diff --git a/guide/src/tutorial.md b/guide/src/tutorial.md index baf32a8..87fe04a 100644 --- a/guide/src/tutorial.md +++ b/guide/src/tutorial.md @@ -136,6 +136,27 @@ cargo bisect-rustc --script=./test.sh \ [issue #53157]: https://github.com/rust-lang/rust/issues/53157 [issue #55036]: https://github.com/rust-lang/rust/issues/55036 +## Testing with LLVM FileCheck + +If you want to investigate a regression in codegen, you can use LLVM's FileCheck. You can write a library containing FileCheck annotations: + +```rs +// CHECK-LABEL: @wildcard( +#[no_mangle] +pub fn wildcard(a: u16, b: u16, v: u16) -> u16 { + // CHECK-NOT: br + match (a == v, b == v) { + (true, false) => 0, + (false, true) => u16::MAX, + _ => 1 << 15, // half + } +} +``` + +To investigate when `br` stopped being emitted, you can use `cargo-bisect-rustc --start 1.70.0 --filecheck src/lib.rs --preserve --regress success`. + +By default, this will compile with `cargo rustc -- -Copt-level=3 -Cdebuginfo=0 --emit=llvm-ir=/debug/deps/output.ll`. + ## Custom bisection messages *Available from v0.6.9* diff --git a/src/main.rs b/src/main.rs index 5437208..5200092 100644 --- a/src/main.rs +++ b/src/main.rs @@ -188,6 +188,15 @@ a date (YYYY-MM-DD), git tag name (e.g. 1.58.0) or git commit SHA." #[arg(long, help = "Script replacement for `cargo build` command")] script: Option, + #[arg( + long, + help = "Run in LLVM FileCheck, using the given path for annotations" + )] + filecheck: Option, + + #[arg(long, help = "Path to LLVM FileCheck")] + filecheck_path: Option, + #[arg(long, help = "Do not install cargo [default: install cargo]")] without_cargo: bool, diff --git a/src/toolchains.rs b/src/toolchains.rs index a0a7266..b8d14a3 100644 --- a/src/toolchains.rs +++ b/src/toolchains.rs @@ -248,14 +248,24 @@ impl Toolchain { } }); - let mut cmd = match (script, cfg.args.timeout) { - (Some(script), None) => { + let target_dir = format!("target-{}", self.rustup_name()); + // Used in filecheck mode + let llir_path = PathBuf::from(&target_dir) + .join("debug") + .join("deps") + .join("output.ll"); + + let mut cmd = match (script, cfg.args.timeout, &cfg.args.filecheck) { + (Some(_), _, Some(_)) => { + panic!("incompatbile options `--script` and `--filecheck` used"); + } + (Some(script), None, None) => { let mut cmd = Command::new(script); cmd.env("RUSTUP_TOOLCHAIN", self.rustup_name()); cmd.args(&cfg.args.command_args); cmd } - (None, None) => { + (None, None, None) => { let mut cmd = Command::new("cargo"); cmd.arg(&format!("+{}", self.rustup_name())); if cfg.args.command_args.is_empty() { @@ -265,7 +275,7 @@ impl Toolchain { } cmd } - (Some(script), Some(timeout)) => { + (Some(script), Some(timeout), None) => { let mut cmd = Command::new("timeout"); cmd.arg(timeout.to_string()); cmd.arg(script); @@ -273,7 +283,7 @@ impl Toolchain { cmd.env("RUSTUP_TOOLCHAIN", self.rustup_name()); cmd } - (None, Some(timeout)) => { + (None, Some(timeout), None) => { let mut cmd = Command::new("timeout"); cmd.arg(timeout.to_string()); cmd.arg("cargo"); @@ -285,9 +295,39 @@ impl Toolchain { } cmd } + (None, None, Some(_)) => { + let mut cmd = Command::new("cargo"); + cmd.arg(&format!("+{}", self.rustup_name())); + if cfg.args.command_args.is_empty() { + cmd.arg("rustc") + .arg("--") + .arg("-Copt-level=3") + .arg("-Cdebuginfo=0") + .arg(format!("--emit=llvm-ir={}", llir_path.display())); + } else { + cmd.args(&cfg.args.command_args); + } + cmd + } + (None, Some(timeout), Some(_)) => { + let mut cmd = Command::new("timeout"); + cmd.arg(timeout.to_string()); + cmd.arg("cargo"); + cmd.arg(&format!("+{}", self.rustup_name())); + if cfg.args.command_args.is_empty() { + cmd.arg("rustc") + .arg("--") + .arg("-Copt-level=3") + .arg("-Cdebuginfo=0") + .arg(format!("--emit=llvm-ir={}", llir_path.display())); + } else { + cmd.args(&cfg.args.command_args); + } + cmd + } }; cmd.current_dir(&cfg.args.test_dir); - cmd.env("CARGO_TARGET_DIR", format!("target-{}", self.rustup_name())); + cmd.env("CARGO_TARGET_DIR", &target_dir); if let Some(target) = &cfg.args.target { cmd.env("CARGO_BUILD_TARGET", target); } @@ -319,6 +359,44 @@ impl Toolchain { io::stdout().write_all(&output.stdout).unwrap(); io::stderr().write_all(&output.stderr).unwrap(); } + + let Some(check_file) = &cfg.args.filecheck else { + return output; + }; + + if !output.status.success() { + return output; + } + + let filecheck_path = cfg + .args + .filecheck_path + .clone() + .unwrap_or_else(|| PathBuf::from("FileCheck")); + + let mut cmd = Command::new(filecheck_path); + cmd.arg("--input-file") + .arg( + PathBuf::from(target_dir) + .join("debug") + .join("deps") + .join("output.ll"), + ) + .arg(check_file); + + cmd.stdout(default_stdio()); + cmd.stderr(default_stdio()); + let output = match cmd.output() { + Ok(output) => output, + Err(err) => { + panic!("thiserror::Errored to run {:?}: {:?}", cmd, err); + } + }; + if must_capture_output && emit_output { + io::stdout().write_all(&output.stdout).unwrap(); + io::stderr().write_all(&output.stderr).unwrap(); + } + output } diff --git a/tests/cmd/h.stdout b/tests/cmd/h.stdout index c450078..8412397 100644 --- a/tests/cmd/h.stdout +++ b/tests/cmd/h.stdout @@ -6,36 +6,63 @@ Arguments: [COMMAND_ARGS]... Arguments to pass to cargo or the file specified by --script during tests Options: - -a, --alt Download the alt build instead of normal build - --access How to access Rust git repository [default: github] [possible - values: checkout, github] - --by-commit Bisect via commit artifacts - -c, --component additional components to install - --end Right bound for search (*with* regression). You can use a date - (YYYY-MM-DD), git tag name (e.g. 1.58.0) or git commit SHA. - --force-install Force installation over existing artifacts - -h, --help Print help (see more with '--help') - --host Host triple for the compiler [default: [..]] - --install Install the given artifact - --preserve Preserve the downloaded artifacts - --preserve-target Preserve the target directory used for builds - --prompt Manually evaluate for regression with prompts - --regress Custom regression definition [default: error] [possible values: - error, success, ice, non-ice, non-error] - --script