Skip to content

Commit a78a62f

Browse files
committed
Auto merge of #77972 - Mark-Simulacrum:side-effect-loop, r=nagisa
Prevent miscompilation in trivial loop {} Ideally, we would want to handle a broader set of cases to fully fix the underlying bug here. That is currently relatively expensive at compile and runtime, so we don't do that for now. Performance results indicate this is not a major regression, if at all, so it should be safe to land. cc #28728
2 parents 8850893 + e2efec8 commit a78a62f

File tree

5 files changed

+42
-9
lines changed

5 files changed

+42
-9
lines changed

Diff for: compiler/rustc_codegen_llvm/src/intrinsic.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -334,8 +334,8 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
334334
self.call(expect, &[cond, self.const_bool(expected)], None)
335335
}
336336

337-
fn sideeffect(&mut self) {
338-
if self.tcx.sess.opts.debugging_opts.insert_sideeffect {
337+
fn sideeffect(&mut self, unconditional: bool) {
338+
if unconditional || self.tcx.sess.opts.debugging_opts.insert_sideeffect {
339339
let fnname = self.get_intrinsic(&("llvm.sideeffect"));
340340
self.call(fnname, &[], None);
341341
}
@@ -390,7 +390,7 @@ fn codegen_msvc_try(
390390
) {
391391
let llfn = get_rust_try_fn(bx, &mut |mut bx| {
392392
bx.set_personality_fn(bx.eh_personality());
393-
bx.sideeffect();
393+
bx.sideeffect(false);
394394

395395
let mut normal = bx.build_sibling_block("normal");
396396
let mut catchswitch = bx.build_sibling_block("catchswitch");
@@ -553,7 +553,7 @@ fn codegen_gnu_try(
553553
// call %catch_func(%data, %ptr)
554554
// ret 1
555555

556-
bx.sideeffect();
556+
bx.sideeffect(false);
557557

558558
let mut then = bx.build_sibling_block("then");
559559
let mut catch = bx.build_sibling_block("catch");
@@ -615,7 +615,7 @@ fn codegen_emcc_try(
615615
// call %catch_func(%data, %catch_data)
616616
// ret 1
617617

618-
bx.sideeffect();
618+
bx.sideeffect(false);
619619

620620
let mut then = bx.build_sibling_block("then");
621621
let mut catch = bx.build_sibling_block("catch");

Diff for: compiler/rustc_codegen_ssa/src/mir/block.rs

+18-2
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
163163
target <= self.bb
164164
&& target.start_location().is_predecessor_of(self.bb.start_location(), mir)
165165
}) {
166-
bx.sideeffect();
166+
bx.sideeffect(false);
167167
}
168168
}
169169
}
@@ -964,7 +964,23 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
964964
}
965965

966966
mir::TerminatorKind::Goto { target } => {
967-
helper.maybe_sideeffect(self.mir, &mut bx, &[target]);
967+
if bb == target {
968+
// This is an unconditional branch back to this same basic
969+
// block. That means we have something like a `loop {}`
970+
// statement. Currently LLVM miscompiles this because it
971+
// assumes forward progress. We want to prevent this in all
972+
// cases, but that has a fairly high cost to compile times
973+
// currently. Instead, try to handle this specific case
974+
// which comes up commonly in practice (e.g., in embedded
975+
// code).
976+
//
977+
// The `true` here means we insert side effects regardless
978+
// of -Zinsert-sideeffect being passed on unconditional
979+
// branching to the same basic block.
980+
bx.sideeffect(true);
981+
} else {
982+
helper.maybe_sideeffect(self.mir, &mut bx, &[target]);
983+
}
968984
helper.funclet_br(self, &mut bx, target);
969985
}
970986

Diff for: compiler/rustc_codegen_ssa/src/mir/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
153153
bx.set_personality_fn(cx.eh_personality());
154154
}
155155

156-
bx.sideeffect();
156+
bx.sideeffect(false);
157157

158158
let cleanup_kinds = analyze::cleanup_kinds(&mir);
159159
// Allocate a `Block` for every basic block, except

Diff for: compiler/rustc_codegen_ssa/src/traits/intrinsic.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ pub trait IntrinsicCallMethods<'tcx>: BackendTypes {
2020
fn abort(&mut self);
2121
fn assume(&mut self, val: Self::Value);
2222
fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value;
23-
fn sideeffect(&mut self);
23+
/// Normally, sideeffect is only emitted if -Zinsert-sideeffect is passed;
24+
/// in some cases though we want to emit it regardless.
25+
fn sideeffect(&mut self, unconditional: bool);
2426
/// Trait method used to inject `va_start` on the "spoofed" `VaListImpl` in
2527
/// Rust defined C-variadic functions.
2628
fn va_start(&mut self, val: Self::Value) -> Self::Value;

Diff for: src/test/codegen/loop.rs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// compile-flags: -C opt-level=3
2+
3+
#![crate_type = "lib"]
4+
5+
// CHECK-LABEL: @check_loop
6+
#[no_mangle]
7+
pub fn check_loop() -> u8 {
8+
// CHECK-NOT: unreachable
9+
call_looper()
10+
}
11+
12+
#[no_mangle]
13+
fn call_looper() -> ! {
14+
loop {}
15+
}

0 commit comments

Comments
 (0)