Skip to content
Open
59 changes: 46 additions & 13 deletions src/bootstrap/src/core/build_steps/synthetic_targets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Comment thread
Kobzol marked this conversation as resolved.

#[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",
};
Comment thread
jieyouxu marked this conversation as resolved.
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());
})
}
}
Expand All @@ -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.
Expand All @@ -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.
Comment thread
jieyouxu marked this conversation as resolved.
/// 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
}
129 changes: 98 additions & 31 deletions src/bootstrap/src/core/build_steps/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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 {
Expand All @@ -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,
})
Comment thread
jieyouxu marked this conversation as resolved.
// 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.
Comment thread
Kobzol marked this conversation as resolved.

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.
Comment thread
Kobzol marked this conversation as resolved.
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.
Expand Down
6 changes: 5 additions & 1 deletion src/bootstrap/src/core/builder/cargo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}

Expand Down
53 changes: 52 additions & 1 deletion src/bootstrap/src/core/builder/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1941,6 +1941,8 @@ mod snapshot {
[test] compiletest-coverage 1 <host>
[build] rustc 1 <host> -> std 1 <host>
[test] compiletest-mir-opt 1 <host>
[build] rustc 1 <host> -> std 1 <host-synthetic-miropt-abort>
[test] compiletest-mir-opt 1 <host-synthetic-miropt-abort>
[test] compiletest-codegen-llvm 1 <host>
[test] compiletest-codegen-units 1 <host>
[test] compiletest-assembly-llvm 1 <host>
Expand Down Expand Up @@ -2122,6 +2124,9 @@ mod snapshot {
[test] compiletest-coverage 2 <host>
[build] rustc 2 <host> -> std 2 <host>
[test] compiletest-mir-opt 2 <host>
[build] rustc 1 <host> -> std 1 <host-synthetic-miropt-abort>
[build] rustc 2 <host> -> std 2 <host-synthetic-miropt-abort>
[test] compiletest-mir-opt 2 <host-synthetic-miropt-abort>
[test] compiletest-codegen-llvm 2 <host>
[test] compiletest-codegen-units 2 <host>
[test] compiletest-assembly-llvm 2 <host>
Expand Down Expand Up @@ -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 <host>
[build] rustc 0 <host> -> rustc 1 <host>
[build] rustc 1 <host> -> std 1 <host>
[build] rustc 0 <host> -> Compiletest 1 <host>
[test] compiletest-mir-opt 1 <host>
[build] rustc 1 <host> -> std 1 <host-synthetic-miropt-abort>
[test] compiletest-mir-opt 1 <host-synthetic-miropt-abort>
");
}

#[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 <target1>
[build] rustc 0 <target1> -> rustc 1 <target1>
[build] rustc 1 <target1> -> std 1 <target1>
[build] rustc 0 <target1> -> Compiletest 1 <target1>
[test] compiletest-mir-opt 1 <target1>
[build] rustc 1 <target1> -> std 1 <i686-pc-windows-msvc>
[test] compiletest-mir-opt 1 <i686-pc-windows-msvc>
[build] rustc 1 <target1> -> std 1 <x86_64-apple-darwin-synthetic-miropt-abort>
[test] compiletest-mir-opt 1 <x86_64-apple-darwin-synthetic-miropt-abort>
[build] rustc 1 <target1> -> std 1 <i686-unknown-linux-musl-synthetic-miropt-abort>
[test] compiletest-mir-opt 1 <i686-unknown-linux-musl-synthetic-miropt-abort>
");
}

#[test]
fn doc_all() {
let ctx = TestCtx::new();
Expand Down Expand Up @@ -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");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () {
debug x => _34;
}
scope 18 (inlined <Enumerate<std::slice::Iter<'_, T>> as Iterator>::next) {
let mut _22: std::option::Option<std::convert::Infallible>;
let mut _22: std::option::Option<!>;
let mut _27: std::option::Option<&T>;
let mut _30: (usize, bool);
let mut _31: (usize, &T);
Expand All @@ -32,7 +32,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () {
}
scope 20 {
scope 21 {
scope 27 (inlined <Option<(usize, &T)> as FromResidual<Option<Infallible>>>::from_residual) {
scope 27 (inlined <Option<(usize, &T)> as FromResidual<Option<!>>>::from_residual) {
let mut _21: isize;
let mut _23: bool;
}
Expand Down
Loading