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.
This commit is contained in:
parent
7f58716810
commit
e2efec8976
@ -334,8 +334,8 @@ fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value {
|
||||
self.call(expect, &[cond, self.const_bool(expected)], None)
|
||||
}
|
||||
|
||||
fn sideeffect(&mut self) {
|
||||
if self.tcx.sess.opts.debugging_opts.insert_sideeffect {
|
||||
fn sideeffect(&mut self, unconditional: bool) {
|
||||
if unconditional || self.tcx.sess.opts.debugging_opts.insert_sideeffect {
|
||||
let fnname = self.get_intrinsic(&("llvm.sideeffect"));
|
||||
self.call(fnname, &[], None);
|
||||
}
|
||||
@ -390,7 +390,7 @@ fn codegen_msvc_try(
|
||||
) {
|
||||
let llfn = get_rust_try_fn(bx, &mut |mut bx| {
|
||||
bx.set_personality_fn(bx.eh_personality());
|
||||
bx.sideeffect();
|
||||
bx.sideeffect(false);
|
||||
|
||||
let mut normal = bx.build_sibling_block("normal");
|
||||
let mut catchswitch = bx.build_sibling_block("catchswitch");
|
||||
@ -553,7 +553,7 @@ fn codegen_gnu_try(
|
||||
// call %catch_func(%data, %ptr)
|
||||
// ret 1
|
||||
|
||||
bx.sideeffect();
|
||||
bx.sideeffect(false);
|
||||
|
||||
let mut then = bx.build_sibling_block("then");
|
||||
let mut catch = bx.build_sibling_block("catch");
|
||||
@ -615,7 +615,7 @@ fn codegen_emcc_try(
|
||||
// call %catch_func(%data, %catch_data)
|
||||
// ret 1
|
||||
|
||||
bx.sideeffect();
|
||||
bx.sideeffect(false);
|
||||
|
||||
let mut then = bx.build_sibling_block("then");
|
||||
let mut catch = bx.build_sibling_block("catch");
|
||||
|
@ -163,7 +163,7 @@ fn maybe_sideeffect<Bx: BuilderMethods<'a, 'tcx>>(
|
||||
target <= self.bb
|
||||
&& target.start_location().is_predecessor_of(self.bb.start_location(), mir)
|
||||
}) {
|
||||
bx.sideeffect();
|
||||
bx.sideeffect(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -964,7 +964,23 @@ fn codegen_terminator(
|
||||
}
|
||||
|
||||
mir::TerminatorKind::Goto { target } => {
|
||||
if bb == target {
|
||||
// This is an unconditional branch back to this same basic
|
||||
// block. That means we have something like a `loop {}`
|
||||
// statement. Currently LLVM miscompiles this because it
|
||||
// assumes forward progress. We want to prevent this in all
|
||||
// cases, but that has a fairly high cost to compile times
|
||||
// currently. Instead, try to handle this specific case
|
||||
// which comes up commonly in practice (e.g., in embedded
|
||||
// code).
|
||||
//
|
||||
// The `true` here means we insert side effects regardless
|
||||
// of -Zinsert-sideeffect being passed on unconditional
|
||||
// branching to the same basic block.
|
||||
bx.sideeffect(true);
|
||||
} else {
|
||||
helper.maybe_sideeffect(self.mir, &mut bx, &[target]);
|
||||
}
|
||||
helper.funclet_br(self, &mut bx, target);
|
||||
}
|
||||
|
||||
|
@ -153,7 +153,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||
bx.set_personality_fn(cx.eh_personality());
|
||||
}
|
||||
|
||||
bx.sideeffect();
|
||||
bx.sideeffect(false);
|
||||
|
||||
let cleanup_kinds = analyze::cleanup_kinds(&mir);
|
||||
// Allocate a `Block` for every basic block, except
|
||||
|
@ -20,7 +20,9 @@ fn codegen_intrinsic_call(
|
||||
fn abort(&mut self);
|
||||
fn assume(&mut self, val: Self::Value);
|
||||
fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value;
|
||||
fn sideeffect(&mut self);
|
||||
/// Normally, sideeffect is only emitted if -Zinsert-sideeffect is passed;
|
||||
/// in some cases though we want to emit it regardless.
|
||||
fn sideeffect(&mut self, unconditional: bool);
|
||||
/// Trait method used to inject `va_start` on the "spoofed" `VaListImpl` in
|
||||
/// Rust defined C-variadic functions.
|
||||
fn va_start(&mut self, val: Self::Value) -> Self::Value;
|
||||
|
15
src/test/codegen/loop.rs
Normal file
15
src/test/codegen/loop.rs
Normal file
@ -0,0 +1,15 @@
|
||||
// compile-flags: -C opt-level=3
|
||||
|
||||
#![crate_type = "lib"]
|
||||
|
||||
// CHECK-LABEL: @check_loop
|
||||
#[no_mangle]
|
||||
pub fn check_loop() -> u8 {
|
||||
// CHECK-NOT: unreachable
|
||||
call_looper()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn call_looper() -> ! {
|
||||
loop {}
|
||||
}
|
Loading…
Reference in New Issue
Block a user