diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fa9535a3729..764ace91670 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -98,6 +98,7 @@ jobs: run: | ./y.sh build --sysroot ./y.sh test --cargo-tests + CARGO_TEST_FLAGS="-Zmir-preserve-ub" ./y.sh test --cargo-tests -- mir_preserve_ub_empty_switch - name: Run y.sh cargo build run: | diff --git a/src/builder.rs b/src/builder.rs index 33f0f6fc2f8..8d047dcfa5a 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -570,6 +570,18 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { default_block: Block<'gcc>, cases: impl ExactSizeIterator)>, ) { + // A switch with no cases is equivalent to an unconditional jump to the + // default block. Such a `SwitchInt` (one with only an `otherwise` target) + // is normally simplified into a `goto`, but `-Z mir-preserve-ub` keeps it, + // so it can reach here with e.g. the `bool` discriminant produced by a + // range-pattern comparison. `gcc_jit_block_end_with_switch` rejects a + // discriminant that is not of integer type, so emit a plain jump instead + // of a (pointless) switch. + if cases.len() == 0 { + self.block.end_with_jump(self.location, default_block); + return; + } + let mut gcc_cases = vec![]; let typ = self.val_ty(value); // FIXME(FractalFir): This is a workaround for a libgccjit limitation. diff --git a/tests/lang_tests.rs b/tests/lang_tests.rs index 6afd54e1c3f..e3baf1e038f 100644 --- a/tests/lang_tests.rs +++ b/tests/lang_tests.rs @@ -172,6 +172,16 @@ fn build_test_runner( } } + // Extra flags passed at run time (as opposed to the compile-time + // `TEST_FLAGS`). This lets a single test opt into flags like + // `-Zmir-preserve-ub` via an `ignore-if` directive that checks + // whether `CARGO_TEST_FLAGS` is set. + if let Ok(flags) = std::env::var("CARGO_TEST_FLAGS") { + for flag in flags.split_whitespace() { + compiler_args.push(flag.into()); + } + } + if build_mode.is_debug() { compiler_args .extend_from_slice(&["-C".to_string(), "llvm-args=sanitize-undefined".into()]); diff --git a/tests/run/mir_preserve_ub_empty_switch.rs b/tests/run/mir_preserve_ub_empty_switch.rs new file mode 100644 index 00000000000..26056360b92 --- /dev/null +++ b/tests/run/mir_preserve_ub_empty_switch.rs @@ -0,0 +1,35 @@ +// ignore-if: test -z "$CARGO_TEST_FLAGS" +// Compiler: +// +// Run-time: +// status: 0 + +// Regression test for https://github.com/rust-lang/rustc_codegen_gcc/issues/881 +// +// This needs `-Zmir-preserve-ub`, so it is skipped unless that flag is passed +// through `CARGO_TEST_FLAGS` (see the `ignore-if` directive above). Run it with: +// CARGO_TEST_FLAGS="-Zmir-preserve-ub" ./y.sh test --cargo-tests -- mir_preserve_ub_empty_switch + +#![feature(no_core)] +#![no_std] +#![no_core] +#![no_main] + +extern crate mini_core; +use intrinsics::black_box; +use mini_core::*; + +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { + // With `-Zmir-preserve-ub`, the range pattern below is lowered to a pair of + // comparisons and the second one becomes a `SwitchInt` with no cases (only + // an `otherwise` target) whose discriminant is the `bool` comparison + // result. `gcc_jit_block_end_with_switch` rejects a non-integer + // discriminant, so the backend must emit a plain jump for it instead. + let value = black_box(argc); + match value { + 0..=9 => (), + _ => (), + } + 0 +}