Add BuilderMethods::unreachable_nonterminator

So places that need `unreachable` but in the middle of a basic block can call that instead of figuring out the best way to do it.
This commit is contained in:
Scott McMurray
2025-07-10 09:17:28 -07:00
parent 58d7c2d5a7
commit f5fc8727db
3 changed files with 32 additions and 27 deletions

View File

@@ -207,9 +207,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
{ {
// These cases are all UB to actually hit, so don't emit code for them. // These cases are all UB to actually hit, so don't emit code for them.
// (The size mismatches are reachable via `transmute_unchecked`.) // (The size mismatches are reachable via `transmute_unchecked`.)
// We can't use unreachable because that's a terminator, and we bx.unreachable_nonterminator();
// need something that can be in the middle of a basic block.
bx.assume(bx.cx().const_bool(false))
} else { } else {
// Since in this path we have a place anyway, we can store or copy to it, // Since in this path we have a place anyway, we can store or copy to it,
// making sure we use the destination place's alignment even if the // making sure we use the destination place's alignment even if the
@@ -236,9 +234,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|| operand.layout.is_uninhabited() || operand.layout.is_uninhabited()
|| cast.is_uninhabited() || cast.is_uninhabited()
{ {
// We can't use unreachable because that's a terminator, and we bx.unreachable_nonterminator();
// need something that can be in the middle of a basic block.
bx.assume(bx.cx().const_bool(false));
// We still need to return a value of the appropriate type, but // We still need to return a value of the appropriate type, but
// it's already UB so do the easiest thing available. // it's already UB so do the easiest thing available.

View File

@@ -136,6 +136,16 @@ pub trait BuilderMethods<'a, 'tcx>:
) -> Self::Value; ) -> Self::Value;
fn unreachable(&mut self); fn unreachable(&mut self);
/// Like [`Self::unreachable`], but for use in the middle of a basic block.
fn unreachable_nonterminator(&mut self) {
// This is the preferred LLVM incantation for this per
// https://llvm.org/docs/Frontend/PerformanceTips.html#other-things-to-consider
// Other backends may override if they have a better way.
let const_true = self.cx().const_bool(true);
let poison_ptr = self.const_poison(self.cx().type_ptr());
self.store(const_true, poison_ptr, Align::ONE);
}
fn add(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn add(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
fn fadd(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn fadd(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
fn fadd_fast(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn fadd_fast(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;

View File

@@ -29,28 +29,28 @@ pub struct Aggregate8(u8);
// CHECK-LABEL: @check_bigger_size( // CHECK-LABEL: @check_bigger_size(
#[no_mangle] #[no_mangle]
pub unsafe fn check_bigger_size(x: u16) -> u32 { pub unsafe fn check_bigger_size(x: u16) -> u32 {
// CHECK: call void @llvm.assume(i1 false) // CHECK: store i1 true, ptr poison, align 1
transmute_unchecked(x) transmute_unchecked(x)
} }
// CHECK-LABEL: @check_smaller_size( // CHECK-LABEL: @check_smaller_size(
#[no_mangle] #[no_mangle]
pub unsafe fn check_smaller_size(x: u32) -> u16 { pub unsafe fn check_smaller_size(x: u32) -> u16 {
// CHECK: call void @llvm.assume(i1 false) // CHECK: store i1 true, ptr poison, align 1
transmute_unchecked(x) transmute_unchecked(x)
} }
// CHECK-LABEL: @check_smaller_array( // CHECK-LABEL: @check_smaller_array(
#[no_mangle] #[no_mangle]
pub unsafe fn check_smaller_array(x: [u32; 7]) -> [u32; 3] { pub unsafe fn check_smaller_array(x: [u32; 7]) -> [u32; 3] {
// CHECK: call void @llvm.assume(i1 false) // CHECK: store i1 true, ptr poison, align 1
transmute_unchecked(x) transmute_unchecked(x)
} }
// CHECK-LABEL: @check_bigger_array( // CHECK-LABEL: @check_bigger_array(
#[no_mangle] #[no_mangle]
pub unsafe fn check_bigger_array(x: [u32; 3]) -> [u32; 7] { pub unsafe fn check_bigger_array(x: [u32; 3]) -> [u32; 7] {
// CHECK: call void @llvm.assume(i1 false) // CHECK: store i1 true, ptr poison, align 1
transmute_unchecked(x) transmute_unchecked(x)
} }
@@ -58,9 +58,9 @@ pub unsafe fn check_bigger_array(x: [u32; 3]) -> [u32; 7] {
#[no_mangle] #[no_mangle]
#[custom_mir(dialect = "runtime", phase = "optimized")] #[custom_mir(dialect = "runtime", phase = "optimized")]
pub unsafe fn check_to_empty_array(x: [u32; 5]) -> [u32; 0] { pub unsafe fn check_to_empty_array(x: [u32; 5]) -> [u32; 0] {
// CHECK-NOT: call // CHECK: start
// CHECK: call void @llvm.assume(i1 false) // CHECK-NEXT: store i1 true, ptr poison, align 1
// CHECK-NOT: call // CHECK-NEXT: ret void
mir! { mir! {
{ {
RET = CastTransmute(x); RET = CastTransmute(x);
@@ -73,9 +73,9 @@ pub unsafe fn check_to_empty_array(x: [u32; 5]) -> [u32; 0] {
#[no_mangle] #[no_mangle]
#[custom_mir(dialect = "runtime", phase = "optimized")] #[custom_mir(dialect = "runtime", phase = "optimized")]
pub unsafe fn check_from_empty_array(x: [u32; 0]) -> [u32; 5] { pub unsafe fn check_from_empty_array(x: [u32; 0]) -> [u32; 5] {
// CHECK-NOT: call // CHECK: start
// CHECK: call void @llvm.assume(i1 false) // CHECK-NEXT: store i1 true, ptr poison, align 1
// CHECK-NOT: call // CHECK-NEXT: ret void
mir! { mir! {
{ {
RET = CastTransmute(x); RET = CastTransmute(x);
@@ -88,9 +88,9 @@ pub unsafe fn check_from_empty_array(x: [u32; 0]) -> [u32; 5] {
#[no_mangle] #[no_mangle]
#[custom_mir(dialect = "runtime", phase = "optimized")] #[custom_mir(dialect = "runtime", phase = "optimized")]
pub unsafe fn check_to_uninhabited(x: u16) { pub unsafe fn check_to_uninhabited(x: u16) {
// CHECK-NOT: call // CHECK: start
// CHECK: call void @llvm.assume(i1 false) // CHECK-NEXT: store i1 true, ptr poison, align 1
// CHECK-NOT: call // CHECK-NEXT: ret void
mir! { mir! {
let temp: BigNever; let temp: BigNever;
{ {
@@ -104,10 +104,9 @@ pub unsafe fn check_to_uninhabited(x: u16) {
#[no_mangle] #[no_mangle]
#[custom_mir(dialect = "runtime", phase = "optimized")] #[custom_mir(dialect = "runtime", phase = "optimized")]
pub unsafe fn check_from_uninhabited(x: BigNever) -> u16 { pub unsafe fn check_from_uninhabited(x: BigNever) -> u16 {
// CHECK-NOT: call // CHECK: start
// CHECK: call void @llvm.assume(i1 false) // CHECK-NEXT: store i1 true, ptr poison, align 1
// CHECK-NOT: call // CHECK-NEXT: ret i16 poison
// CHECK: ret i16 poison
mir! { mir! {
{ {
RET = CastTransmute(x); RET = CastTransmute(x);
@@ -404,9 +403,9 @@ pub unsafe fn check_issue_109992(x: ()) -> [(); 1] {
pub unsafe fn check_unit_to_never(x: ()) { pub unsafe fn check_unit_to_never(x: ()) {
// This uses custom MIR to avoid MIR optimizations having removed ZST ops. // This uses custom MIR to avoid MIR optimizations having removed ZST ops.
// CHECK-NOT: call // CHECK: start
// CHECK: call void @llvm.assume(i1 false) // CHECK-NEXT: store i1 true, ptr poison, align 1
// CHECK-NOT: call // CHECK-NEXT: ret void
mir! { mir! {
let temp: ZstNever; let temp: ZstNever;
{ {
@@ -423,7 +422,7 @@ pub unsafe fn check_unit_from_never(x: ZstNever) -> () {
// This uses custom MIR to avoid MIR optimizations having removed ZST ops. // This uses custom MIR to avoid MIR optimizations having removed ZST ops.
// CHECK: start // CHECK: start
// CHECK-NEXT: call void @llvm.assume(i1 false) // CHECK-NEXT: store i1 true, ptr poison, align 1
// CHECK-NEXT: ret void // CHECK-NEXT: ret void
mir! { mir! {
{ {