Skip to content

Commit fd7a235

Browse files
committed
Lower assume(false) to an unreachable terminator
1 parent 766bdce commit fd7a235

File tree

3 files changed

+63
-3
lines changed

3 files changed

+63
-3
lines changed

compiler/rustc_codegen_ssa/src/mir/block.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use rustc_target::abi::{self, HasDataLayout, WrappingRange};
2323
use rustc_target::spec::abi::Abi;
2424

2525
use std::cmp;
26+
use std::ops::ControlFlow;
2627

2728
// Indicates if we are in the middle of merging a BB's successor into it. This
2829
// can happen when BB jumps directly to its successor and the successor has no
@@ -1213,10 +1214,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
12131214

12141215
debug!("codegen_block({:?}={:?})", bb, data);
12151216

1217+
let mut replaced_terminator = false;
12161218
for statement in &data.statements {
1217-
self.codegen_statement(bx, statement);
1219+
if let ControlFlow::Break(()) = self.codegen_statement(bx, statement) {
1220+
replaced_terminator = true;
1221+
break;
1222+
}
12181223
}
12191224

1225+
if replaced_terminator {
1226+
break;
1227+
}
12201228
let merging_succ = self.codegen_terminator(bx, bb, data.terminator());
12211229
if let MergingSucc::False = merging_succ {
12221230
break;

compiler/rustc_codegen_ssa/src/mir/statement.rs

+26-2
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,22 @@ use super::FunctionCx;
66
use super::LocalRef;
77
use crate::traits::*;
88

9+
use std::ops::ControlFlow;
10+
911
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
12+
/// Lower a single MIR statement, returning [`ControlFlow::Break`] if we must not continue lowering
13+
/// the rest of the statements in the block.
14+
///
15+
/// Since we are lowering a polymorphic MIR body, we can discover only at this point that we
16+
/// are lowering `assume(false)`. If we encounter such a statement, there is no reason to lower
17+
/// the rest of the block; we just emit an unreachable terminator and return
18+
/// [`ControlFlow::Break`].
1019
#[instrument(level = "debug", skip(self, bx))]
11-
pub fn codegen_statement(&mut self, bx: &mut Bx, statement: &mir::Statement<'tcx>) {
20+
pub fn codegen_statement(
21+
&mut self,
22+
bx: &mut Bx,
23+
statement: &mir::Statement<'tcx>,
24+
) -> ControlFlow<()> {
1225
self.set_debug_loc(bx, statement.source_info);
1326
match statement.kind {
1427
mir::StatementKind::Assign(box (ref place, ref rvalue)) => {
@@ -70,7 +83,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
7083
mir::StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(ref op)) => {
7184
if !matches!(bx.tcx().sess.opts.optimize, OptLevel::No | OptLevel::Less) {
7285
let op_val = self.codegen_operand(bx, op);
73-
bx.assume(op_val.immediate());
86+
let imm = op_val.immediate();
87+
if let Some(value) = bx.const_to_opt_uint(imm) {
88+
// If we are lowering assume(false), just produce an unreachable
89+
// terminator. We don't emit anything for assume(true).
90+
if value == 0 {
91+
bx.unreachable();
92+
return ControlFlow::Break(());
93+
}
94+
} else {
95+
bx.assume(imm);
96+
}
7497
}
7598
}
7699
mir::StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(
@@ -97,5 +120,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
97120
| mir::StatementKind::PlaceMention(..)
98121
| mir::StatementKind::Nop => {}
99122
}
123+
ControlFlow::Continue(())
100124
}
101125
}

tests/codegen/discriminant-swap.rs

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// The lowering of the function below initially reads and writes to the entire pointee, even
2+
// though it only needs to do a store to the discriminant.
3+
// This test ensures that std::hint::unreachable_unchecked does not prevent the desired
4+
// optimization.
5+
6+
//@ compile-flags: -O
7+
8+
#![crate_type = "lib"]
9+
10+
use std::hint::unreachable_unchecked;
11+
use std::ptr::{read, write};
12+
13+
type T = [u8; 753];
14+
15+
pub enum State {
16+
A(T),
17+
B(T),
18+
}
19+
20+
// CHECK-LABEL: @init(ptr {{.*}}s)
21+
// CHECK-NEXT: start
22+
// CHECK-NEXT: store i8 1, ptr %s, align 1
23+
// CHECK-NEXT: ret void
24+
#[no_mangle]
25+
unsafe fn init(s: *mut State) {
26+
let State::A(v) = read(s) else { unreachable_unchecked() };
27+
write(s, State::B(v));
28+
}

0 commit comments

Comments
 (0)