Skip to content

Commit 04d1404

Browse files
committed
Auto merge of rust-lang#122610 - saethlin:assume-const, r=<try>
Lower assume(false) to an unreachable terminator This turns the IR pattern: ``` call void `@llvm.assume(i1` false) call void `@core::hint::unreachable_unchecked::precondition_check()` rust-lang#7 br label %bb3 ``` Into just: ``` unreachable ``` I'm not sure why LLVM needs its hand held like this, but also this is a chance to emit less IR so even if the optimization is fixed in LLVM we still come out ahead. The only part of this PR I'm not happy with is the complexity in the loop that iterates over the MIR blocks. The structure of it was a bit subtle before, and now it's just out of control. Please help?
2 parents 4c1b9c3 + 9cc67c5 commit 04d1404

File tree

3 files changed

+52
-5
lines changed

3 files changed

+52
-5
lines changed

compiler/rustc_codegen_ssa/src/mir/block.rs

+12-3
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,13 +1214,21 @@ 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

1220-
let merging_succ = self.codegen_terminator(bx, bb, data.terminator());
1221-
if let MergingSucc::False = merging_succ {
1225+
if replaced_terminator {
12221226
break;
1227+
} else {
1228+
let merging_succ = self.codegen_terminator(bx, bb, data.terminator());
1229+
if let MergingSucc::False = merging_succ {
1230+
break;
1231+
}
12231232
}
12241233

12251234
// We are merging the successor into the produced backend basic

compiler/rustc_codegen_ssa/src/mir/statement.rs

+17-2
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,15 @@ 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> {
1012
#[instrument(level = "debug", skip(self, bx))]
11-
pub fn codegen_statement(&mut self, bx: &mut Bx, statement: &mir::Statement<'tcx>) {
13+
pub fn codegen_statement(
14+
&mut self,
15+
bx: &mut Bx,
16+
statement: &mir::Statement<'tcx>,
17+
) -> ControlFlow<()> {
1218
self.set_debug_loc(bx, statement.source_info);
1319
match statement.kind {
1420
mir::StatementKind::Assign(box (ref place, ref rvalue)) => {
@@ -70,7 +76,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
7076
mir::StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(ref op)) => {
7177
if !matches!(bx.tcx().sess.opts.optimize, OptLevel::No | OptLevel::Less) {
7278
let op_val = self.codegen_operand(bx, op);
73-
bx.assume(op_val.immediate());
79+
let imm = op_val.immediate();
80+
if let Some(value) = bx.const_to_opt_uint(imm) {
81+
if value == 0 {
82+
bx.unreachable();
83+
return ControlFlow::Break(());
84+
}
85+
} else {
86+
bx.assume(imm);
87+
}
7488
}
7589
}
7690
mir::StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(
@@ -97,5 +111,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
97111
| mir::StatementKind::PlaceMention(..)
98112
| mir::StatementKind::Nop => {}
99113
}
114+
ControlFlow::Continue(())
100115
}
101116
}

tests/codegen/discriminant-swap.rs

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//@ compile-flags: -O
2+
3+
#![crate_type = "lib"]
4+
5+
use std::hint::unreachable_unchecked;
6+
use std::ptr::{read, write};
7+
8+
type T = [u8; 753];
9+
10+
pub enum State {
11+
A(T),
12+
B(T),
13+
}
14+
15+
// CHECK-LABEL: @init(ptr {{.*}}s)
16+
// CHECK-NEXT: start
17+
// CHECK-NEXT: store i8 1, ptr %s, align 1
18+
// CHECK-NEXT: ret void
19+
#[no_mangle]
20+
unsafe fn init(s: *mut State) {
21+
let State::A(v) = read(s) else { unreachable_unchecked() };
22+
write(s, State::B(v));
23+
}

0 commit comments

Comments
 (0)