diff --git a/src/bootstrap/src/core/build_steps/synthetic_targets.rs b/src/bootstrap/src/core/build_steps/synthetic_targets.rs index 2b5039214f62c..2c35b39287d70 100644 --- a/src/bootstrap/src/core/build_steps/synthetic_targets.rs +++ b/src/bootstrap/src/core/build_steps/synthetic_targets.rs @@ -11,18 +11,40 @@ use crate::core::builder::{Builder, Step}; use crate::core::compiler::Compiler; use crate::core::config::TargetSelection; +/// Note that this currently only contains panic strategies that we somehow use in bootstrap, not +/// all possible strategires supported by rustc. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub(crate) enum PanicStrategy { + Unwind, + Abort, +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub(crate) struct MirOptPanicAbortSyntheticTarget { +pub(crate) struct SyntheticTargetWithPanicStrategy { pub(crate) compiler: Compiler, pub(crate) base: TargetSelection, + pub(crate) strategy: PanicStrategy, +} + +impl SyntheticTargetWithPanicStrategy { + pub(crate) fn panic_abort(compiler: Compiler, base: TargetSelection) -> Self { + Self { compiler, base, strategy: PanicStrategy::Abort } + } + pub(crate) fn panic_unwind(compiler: Compiler, base: TargetSelection) -> Self { + Self { compiler, base, strategy: PanicStrategy::Unwind } + } } -impl Step for MirOptPanicAbortSyntheticTarget { +impl Step for SyntheticTargetWithPanicStrategy { type Output = TargetSelection; fn run(self, builder: &Builder<'_>) -> Self::Output { + let strategy = match self.strategy { + PanicStrategy::Unwind => "unwind", + PanicStrategy::Abort => "abort", + }; create_synthetic_target(builder, self.compiler, "miropt-abort", self.base, |spec| { - spec.insert("panic-strategy".into(), "abort".into()); + spec.insert("panic-strategy".into(), strategy.into()); }) } } @@ -49,16 +71,7 @@ fn create_synthetic_target( return TargetSelection::create_synthetic(&name, path.to_str().unwrap()); } - let mut cmd = builder.rustc_cmd(compiler); - cmd.arg("--target").arg(base.rustc_target_arg()); - cmd.args(["-Zunstable-options", "--print", "target-spec-json"]); - - // If `rust.channel` is set to either beta or stable, rustc will complain that - // we cannot use nightly features. So `RUSTC_BOOTSTRAP` is needed here. - cmd.env("RUSTC_BOOTSTRAP", "1"); - - let output = cmd.run_capture(builder).stdout(); - let mut spec: serde_json::Value = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut spec = get_target_specs(builder, compiler, base); let spec_map = spec.as_object_mut().unwrap(); // The `is-builtin` attribute of a spec needs to be removed, otherwise rustc will complain. @@ -69,3 +82,23 @@ fn create_synthetic_target( std::fs::write(&path, serde_json::to_vec_pretty(&spec).unwrap()).unwrap(); TargetSelection::create_synthetic(&name, path.to_str().unwrap()) } + +/// Get the JSON target specs from the given compiler. +/// Note that the set of targets will differ between the stage0 and stage1+ (in-tree) compiler! +pub fn get_target_specs( + builder: &Builder<'_>, + compiler: Compiler, + target: TargetSelection, +) -> serde_json::Value { + let mut cmd = builder.rustc_cmd(compiler); + cmd.arg("--target").arg(target.rustc_target_arg()); + cmd.args(["-Zunstable-options", "--print", "target-spec-json"]); + + // If `rust.channel` is set to either beta or stable, rustc will complain that + // we cannot use nightly features. So `RUSTC_BOOTSTRAP` is needed here. + cmd.env("RUSTC_BOOTSTRAP", "1"); + + let output = cmd.cached().run_capture(builder).stdout(); + let spec: serde_json::Value = serde_json::from_slice(output.as_bytes()).unwrap(); + spec +} diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 47318d3c086f5..aba8d52959c88 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -22,7 +22,9 @@ use crate::core::build_steps::format::InternalRustfmt; use crate::core::build_steps::gcc::{Gcc, GccTargetPair, add_cg_gcc_cargo_flags}; use crate::core::build_steps::llvm::get_llvm_version; use crate::core::build_steps::run::{get_completion_paths, get_help_path}; -use crate::core::build_steps::synthetic_targets::MirOptPanicAbortSyntheticTarget; +use crate::core::build_steps::synthetic_targets::{ + PanicStrategy, SyntheticTargetWithPanicStrategy, get_target_specs, +}; use crate::core::build_steps::test::compiletest::CompiletestMode; use crate::core::build_steps::test::failed_tests::{RecordFailedTests, SetupFailedTestsFile}; use crate::core::build_steps::tool::{ @@ -2168,8 +2170,8 @@ test!(CoverageRunRustdoc { // For the mir-opt suite we do not use macros, as we need custom behavior when blessing. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct MirOpt { - pub compiler: Compiler, - pub target: TargetSelection, + compiler: Compiler, + target: TargetSelection, } impl CommandLineStep for MirOpt { @@ -2185,45 +2187,110 @@ impl CommandLineStep for MirOpt { fn make_run(run: RunConfig<'_>) { let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); - run.builder.ensure(MirOpt { compiler, target: run.target }); - } - fn run(self, builder: &Builder<'_>) { - let run = |target| { - builder.ensure(Compiletest { - test_compiler: self.compiler, - target, - mode: CompiletestMode::MirOpt, - suite: "mir-opt", - path: "tests/mir-opt", - compare_mode: None, - }) + // The mir-opt tests check four distinct configurations, the cross-product of the + // following two axes: + // - Bit-width: 32-bit and 64-bit + // - Panic strategy: unwind and abort + + // Return the bitwidth and panic strategy of the default (usually host) target + let get_bitwidth_and_panic_strategy = || -> (u64, PanicStrategy) { + if run.builder.config.dry_run() { + return (64, PanicStrategy::Unwind); + } + + let specs = get_target_specs(run.builder, compiler, run.target); + let specs = specs.as_object(); + let bitwidth = specs + .and_then(|obj| obj.get("target-pointer-width")) + .and_then(|v| v.as_i64()) + .map(|v| v as u64) + .unwrap_or(64); + let panic_strategy = specs + .and_then(|obj| obj.get("panic-strategy")) + .and_then(|v| v.as_str()) + .map(|v| match v { + "unwind" => PanicStrategy::Unwind, + _ => PanicStrategy::Abort, + }) + // The default panic strategy is unwind + .unwrap_or(PanicStrategy::Unwind); + (bitwidth, panic_strategy) }; - run(self.target); + // Here we generate several configurations of this step to evaluate multiple targets. + let targets = if run.builder.config.cmd.bless() { + // When blessing, we generate a fixed set of 4 targets that cover all the + // possible combinations. This selection covers all our tier 1 operating systems and + // architectures using only tier 1 targets. - // Run more targets with `--bless`. But we always run the host target first, since some - // tests use very specific `only` clauses that are not covered by the target set below. - if builder.config.cmd.bless() { - // All that we really need to do is cover all combinations of 32/64-bit and unwind/abort, - // but while we're at it we might as well flex our cross-compilation support. This - // selection covers all our tier 1 operating systems and architectures using only tier - // 1 targets. + // We also include the host target, since some tests use very specific `only` clauses + // that are not covered by the target set below. + + let (bitwidth, strategy) = get_bitwidth_and_panic_strategy(); + let mut targets = vec![(bitwidth, strategy, run.target)]; - for target in ["aarch64-unknown-linux-gnu", "i686-pc-windows-msvc"] { - run(TargetSelection::from_user(target)); + // 64-bit and 32-bit panic=unwind + for (bitwidth, target) in + [(64, "aarch64-unknown-linux-gnu"), (32, "i686-pc-windows-msvc")] + { + targets.push((bitwidth, PanicStrategy::Unwind, TargetSelection::from_user(target))); } - for target in ["x86_64-apple-darwin", "i686-unknown-linux-musl"] { + // 64-bit and 32-bit panic=abort + for (bitwidth, target) in [(64, "x86_64-apple-darwin"), (32, "i686-unknown-linux-musl")] + { let target = TargetSelection::from_user(target); - let panic_abort_target = builder.ensure(MirOptPanicAbortSyntheticTarget { - compiler: self.compiler, - base: target, - }); - run(panic_abort_target); + let panic_abort_target = run + .builder + .ensure(SyntheticTargetWithPanicStrategy::panic_abort(compiler, target)); + targets.push((bitwidth, PanicStrategy::Abort, panic_abort_target)); } + // This is a small optimization for local blessing. + // If we figure out that the host target already has a given bitwidth/panic strategy + // combination, we do not add the fixed targets to the list. + let mut unique = HashSet::new(); + targets.retain(|(bitwidth, strategy, _)| unique.insert((*bitwidth, *strategy))); + + targets.into_iter().map(|(_, _, target)| target).collect() + } else { + // When not blessing, we could also test all four configurations. But that would make + // local tests quite slow. So instead, we check the current target, and then the + // current target with switched panic strategy. + // On CI, we should be running this test for both 32-bit and 64-bit targets, so together + // this should check all possible configurations on CI. + + // The complicated thing here is how to figure out the panic strategy of the current + // target. In theory, we could just assume that in most situations, the target is + // panic=unwind, and force generation of panic=abort. But to ensure that we do this + // properly, we actually query the compiler to figure out the panic strategy, and then + // generate a synthetic target with the opposite strategy. + let panic_strategy = get_bitwidth_and_panic_strategy().1; + let synthetic_target = if panic_strategy == PanicStrategy::Unwind { + run.builder + .ensure(SyntheticTargetWithPanicStrategy::panic_abort(compiler, run.target)) + } else { + run.builder + .ensure(SyntheticTargetWithPanicStrategy::panic_unwind(compiler, run.target)) + }; + vec![run.target, synthetic_target] + }; + + for target in targets { + run.builder.ensure(MirOpt { compiler, target }); } } + + fn run(self, builder: &Builder<'_>) { + builder.ensure(Compiletest { + test_compiler: self.compiler, + target: self.target, + mode: CompiletestMode::MirOpt, + suite: "mir-opt", + path: "tests/mir-opt", + compare_mode: None, + }); + } } /// Executes the `compiletest` tool to run a suite of tests. diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index b04f61eafee32..5b63dc4a0f7ae 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -178,7 +178,11 @@ impl Cargo { // No need to configure the target linker for these command types. Kind::Clean | Kind::Check | Kind::Format | Kind::Setup => {} _ => { - cargo.configure_linker(builder); + // Do not configure the linker for synthetic targets, as we won't have cc set up + // for them. + if !target.is_synthetic() { + cargo.configure_linker(builder); + } } } diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 1f08ee9c11864..97c8d532edfb8 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1941,6 +1941,8 @@ mod snapshot { [test] compiletest-coverage 1 [build] rustc 1 -> std 1 [test] compiletest-mir-opt 1 + [build] rustc 1 -> std 1 + [test] compiletest-mir-opt 1 [test] compiletest-codegen-llvm 1 [test] compiletest-codegen-units 1 [test] compiletest-assembly-llvm 1 @@ -2122,6 +2124,9 @@ mod snapshot { [test] compiletest-coverage 2 [build] rustc 2 -> std 2 [test] compiletest-mir-opt 2 + [build] rustc 1 -> std 1 + [build] rustc 2 -> std 2 + [test] compiletest-mir-opt 2 [test] compiletest-codegen-llvm 2 [test] compiletest-codegen-units 2 [test] compiletest-assembly-llvm 2 @@ -2389,6 +2394,52 @@ mod snapshot { "); } + #[test] + fn test_mir_opt() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + prepare_test_config(&ctx) + .path("tests/mir-opt") + .render_steps(), @" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustc 0 -> Compiletest 1 + [test] compiletest-mir-opt 1 + [build] rustc 1 -> std 1 + [test] compiletest-mir-opt 1 + "); + } + + #[test] + fn test_mir_opt_bless() { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + prepare_test_config(&ctx) + .arg("--bless") + .hosts(&[TEST_TRIPLE_1]) + .arg("--build") + .arg(TEST_TRIPLE_1) + .targets(&[TEST_TRIPLE_1]) + .path("tests/mir-opt") + .get_steps() + .render_with(RenderConfig { + normalize_host: false + }), @" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + [build] rustc 0 -> Compiletest 1 + [test] compiletest-mir-opt 1 + [build] rustc 1 -> std 1 + [test] compiletest-mir-opt 1 + [build] rustc 1 -> std 1 + [test] compiletest-mir-opt 1 + [build] rustc 1 -> std 1 + [test] compiletest-mir-opt 1 + "); + } + #[test] fn doc_all() { let ctx = TestCtx::new(); @@ -3184,7 +3235,7 @@ fn render_metadata(metadata: &StepMetadata, config: &RenderConfig) -> String { } fn normalize_target(target: TargetSelection, config: &RenderConfig) -> String { - let mut target = target.to_string(); + let mut target = target.triple.to_string(); if config.normalize_host { target = target.replace(&host_target(), "host"); } diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.rs b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.rs index 5534a45f19d64..8593a322ad363 100644 --- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.rs +++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.rs @@ -1,4 +1,6 @@ //@ test-mir-pass: GVN +// layout randomization affects the alloc output +//@ needs-deterministic-layouts //@ compile-flags: -Zinline-mir --crate-type lib // EMIT_MIR_FOR_EACH_BIT_WIDTH // EMIT_MIR_FOR_EACH_PANIC_STRATEGY diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.runtime-optimized.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.runtime-optimized.after.panic-abort.mir index 549af7af4d888..b42087b5c822d 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.runtime-optimized.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.runtime-optimized.after.panic-abort.mir @@ -21,7 +21,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { debug x => _34; } scope 18 (inlined > as Iterator>::next) { - let mut _22: std::option::Option; + let mut _22: std::option::Option; let mut _27: std::option::Option<&T>; let mut _30: (usize, bool); let mut _31: (usize, &T); @@ -32,7 +32,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } scope 20 { scope 21 { - scope 27 (inlined as FromResidual>>::from_residual) { + scope 27 (inlined as FromResidual>>::from_residual) { let mut _21: isize; let mut _23: bool; }