Skip to content

Commit

Permalink
Auto merge of rust-lang#122610 - saethlin:assume-const, r=<try>
Browse files Browse the repository at this point in the history
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?
  • Loading branch information
bors committed Mar 17, 2024
2 parents 4c1b9c3 + 9cc67c5 commit 04d1404
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 5 deletions.
15 changes: 12 additions & 3 deletions compiler/rustc_codegen_ssa/src/mir/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use rustc_target::abi::{self, HasDataLayout, WrappingRange};
use rustc_target::spec::abi::Abi;

use std::cmp;
use std::ops::ControlFlow;

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

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

let mut replaced_terminator = false;
for statement in &data.statements {
self.codegen_statement(bx, statement);
if let ControlFlow::Break(()) = self.codegen_statement(bx, statement) {
replaced_terminator = true;
break;
}
}

let merging_succ = self.codegen_terminator(bx, bb, data.terminator());
if let MergingSucc::False = merging_succ {
if replaced_terminator {
break;
} else {
let merging_succ = self.codegen_terminator(bx, bb, data.terminator());
if let MergingSucc::False = merging_succ {
break;
}
}

// We are merging the successor into the produced backend basic
Expand Down
19 changes: 17 additions & 2 deletions compiler/rustc_codegen_ssa/src/mir/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@ use super::FunctionCx;
use super::LocalRef;
use crate::traits::*;

use std::ops::ControlFlow;

impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
#[instrument(level = "debug", skip(self, bx))]
pub fn codegen_statement(&mut self, bx: &mut Bx, statement: &mir::Statement<'tcx>) {
pub fn codegen_statement(
&mut self,
bx: &mut Bx,
statement: &mir::Statement<'tcx>,
) -> ControlFlow<()> {
self.set_debug_loc(bx, statement.source_info);
match statement.kind {
mir::StatementKind::Assign(box (ref place, ref rvalue)) => {
Expand Down Expand Up @@ -70,7 +76,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
mir::StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(ref op)) => {
if !matches!(bx.tcx().sess.opts.optimize, OptLevel::No | OptLevel::Less) {
let op_val = self.codegen_operand(bx, op);
bx.assume(op_val.immediate());
let imm = op_val.immediate();
if let Some(value) = bx.const_to_opt_uint(imm) {
if value == 0 {
bx.unreachable();
return ControlFlow::Break(());
}
} else {
bx.assume(imm);
}
}
}
mir::StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(
Expand All @@ -97,5 +111,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
| mir::StatementKind::PlaceMention(..)
| mir::StatementKind::Nop => {}
}
ControlFlow::Continue(())
}
}
23 changes: 23 additions & 0 deletions tests/codegen/discriminant-swap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//@ compile-flags: -O

#![crate_type = "lib"]

use std::hint::unreachable_unchecked;
use std::ptr::{read, write};

type T = [u8; 753];

pub enum State {
A(T),
B(T),
}

// CHECK-LABEL: @init(ptr {{.*}}s)
// CHECK-NEXT: start
// CHECK-NEXT: store i8 1, ptr %s, align 1
// CHECK-NEXT: ret void
#[no_mangle]
unsafe fn init(s: *mut State) {
let State::A(v) = read(s) else { unreachable_unchecked() };
write(s, State::B(v));
}

0 comments on commit 04d1404

Please sign in to comment.