Skip to content

Commit e2efec8

Browse files
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.
1 parent 7f58716 commit e2efec8

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)