diff --git a/.github/workflows/demo.yml b/.github/workflows/demo.yml new file mode 100644 index 0000000..86249c5 --- /dev/null +++ b/.github/workflows/demo.yml @@ -0,0 +1,20 @@ +# Run a job to ensure formatting is OK +name: Run demo +on: + pull_request: + paths: + - demo/** + + push: + paths: + - demo/** + +jobs: + check_demo: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Run Demo + run: ./demo/run_demo.sh diff --git a/Cargo.toml b/Cargo.toml index e90ec9c..8f02265 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,5 @@ members = [ exclude = [ "build", "target", + "demo", ] diff --git a/demo/Cargo.lock b/demo/Cargo.lock new file mode 100644 index 0000000..05729f1 --- /dev/null +++ b/demo/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "smir-demo" +version = "0.0.0" diff --git a/demo/Cargo.toml b/demo/Cargo.toml new file mode 100644 index 0000000..7ab12d7 --- /dev/null +++ b/demo/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "smir-demo" +description = "A little demo tool on how to use StableMIR to analyze crates" +version = "0.0.0" +edition = "2021" + +[dependencies] + +[package.metadata.rust-analyzer] +# This crate uses #[feature(rustc_private)]. +# See https://github.com/rust-analyzer/rust-analyzer/pull/7891 +rustc_private = true diff --git a/demo/build.rs b/demo/build.rs new file mode 100644 index 0000000..e3fdffb --- /dev/null +++ b/demo/build.rs @@ -0,0 +1,15 @@ +use std::env; +use std::path::PathBuf; + +pub fn main() { + // Add rustup to the rpath in order to properly link with the correct rustc version. + let rustup_home = env::var("RUSTUP_HOME").unwrap(); + let toolchain = env::var("RUSTUP_TOOLCHAIN").unwrap(); + let rustc_lib: PathBuf = [&rustup_home, "toolchains", &toolchain, "lib"] + .iter() + .collect(); + println!( + "cargo:rustc-link-arg-bin=smir-demo=-Wl,-rpath,{}", + rustc_lib.display() + ); +} diff --git a/demo/run_demo.sh b/demo/run_demo.sh new file mode 100755 index 0000000..b761a22 --- /dev/null +++ b/demo/run_demo.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# Builds and run the demo driver against itself. +# I.e.: This bootstrap itself. + +REPO_DIR=$(git rev-parse --show-toplevel) +DEMO_DIR="${REPO_DIR}/demo" + +cd "${DEMO_DIR}" +cargo run -- src/main.rs --crate-name demo --edition 2021 -C panic=abort + diff --git a/demo/rust-toolchain.toml b/demo/rust-toolchain.toml new file mode 100644 index 0000000..698da75 --- /dev/null +++ b/demo/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "nightly-2024-04-02" +components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/demo/src/main.rs b/demo/src/main.rs new file mode 100644 index 0000000..a1d889a --- /dev/null +++ b/demo/src/main.rs @@ -0,0 +1,88 @@ +//! Small utility that print some information about a crate. + +#![feature(rustc_private)] +#![feature(assert_matches)] + +extern crate rustc_driver; +extern crate rustc_interface; +#[macro_use] +extern crate rustc_smir; +extern crate stable_mir; + +use std::collections::HashSet; +use std::io::stdout; +use rustc_smir::{run, rustc_internal}; +use stable_mir::{CompilerError, CrateDef}; +use std::ops::ControlFlow; +use std::process::ExitCode; +use stable_mir::mir::{LocalDecl, MirVisitor, Terminator, TerminatorKind}; +use stable_mir::mir::mono::Instance; +use stable_mir::mir::visit::Location; +use stable_mir::ty::{RigidTy, Ty, TyKind}; + + +/// This is a wrapper that can be used to replace rustc. +fn main() -> ExitCode { + let rustc_args = std::env::args().into_iter().collect(); + let result = run!(rustc_args, start_demo); + match result { + Ok(_) | Err(CompilerError::Skipped | CompilerError::Interrupted(_)) => ExitCode::SUCCESS, + _ => ExitCode::FAILURE, + } +} + +fn start_demo() -> ControlFlow<()> { + let crate_name = stable_mir::local_crate().name; + eprintln!("--- Analyzing crate: {crate_name}"); + + let crate_items = stable_mir::all_local_items(); + for item in crate_items { + eprintln!(" - {} @{:?}", item.name(), item.span()) + } + + let entry_fn = stable_mir::entry_fn().unwrap(); + let entry_instance = Instance::try_from(entry_fn).unwrap(); + analyze_instance(entry_instance); + ControlFlow::Break(()) +} + +fn analyze_instance(instance: Instance) { + eprintln!("--- Analyzing instance: {}", instance.name()); + eprintln!(" - Mangled name: {}", instance.mangled_name()); + eprintln!(" - FnABI: {:?}", instance.fn_abi().unwrap()); + + let body = instance.body().unwrap(); + let mut visitor = Visitor { + locals: body.locals(), + tys: Default::default(), + fn_calls: Default::default(), + }; + visitor.visit_body(&body); + visitor.tys.iter().for_each(|ty| eprintln!(" - Visited: {ty}")); + visitor.fn_calls.iter().for_each(|instance| eprintln!(" - Call: {}", instance.name())); + + body.dump(&mut stdout().lock(), &instance.name()).unwrap(); +} + +struct Visitor<'a> { + locals: &'a [LocalDecl], + tys: HashSet, + fn_calls: HashSet, +} + +impl<'a> MirVisitor for Visitor<'a> { + fn visit_terminator(&mut self, term: &Terminator, _location: Location) { + match term.kind { + TerminatorKind::Call { ref func, .. } => { + let op_ty = func.ty(self.locals).unwrap(); + let TyKind::RigidTy(RigidTy::FnDef(def, args)) = op_ty.kind() else { return; }; + self.fn_calls.insert(Instance::resolve(def, &args).unwrap()); + } + _ => {} + } + } + + fn visit_ty(&mut self, ty: &Ty, _location: Location) { + self.tys.insert(*ty); + } +} diff --git a/tools/test-drive/Cargo.toml b/tools/test-drive/Cargo.toml index 12b88aa..8fc1468 100644 --- a/tools/test-drive/Cargo.toml +++ b/tools/test-drive/Cargo.toml @@ -4,8 +4,6 @@ description = "A rustc wrapper that can be used to test stable-mir on a crate" version = "0.0.0" edition = "2021" -[dependencies] - [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)]. # See https://github.com/rust-analyzer/rust-analyzer/pull/7891