Auto merge of #137513 - scottmcm:identity-transmute, r=saethlin
Don't re-`assume` in `transmute`s that don't change niches I noticed in nightly 2025-02-21 that `transmute` is emitting way more `assume`s than necessary for newtypes. For example, the three transmutes in <https://rust.godbolt.org/z/fW1KaTc4o> emits ```rust define noundef range(i32 1, 0) i32 `@repeatedly_transparent_transmute(i32` noundef range(i32 1, 0) %_1) unnamed_addr { start: %0 = sub i32 %_1, 1 %1 = icmp ule i32 %0, -2 call void `@llvm.assume(i1` %1) %2 = sub i32 %_1, 1 %3 = icmp ule i32 %2, -2 call void `@llvm.assume(i1` %3) %4 = sub i32 %_1, 1 %5 = icmp ule i32 %4, -2 call void `@llvm.assume(i1` %5) %6 = sub i32 %_1, 1 %7 = icmp ule i32 %6, -2 call void `@llvm.assume(i1` %7) %8 = sub i32 %_1, 1 %9 = icmp ule i32 %8, -2 call void `@llvm.assume(i1` %9) %10 = sub i32 %_1, 1 %11 = icmp ule i32 %10, -2 call void `@llvm.assume(i1` %11) ret i32 %_1 } ``` But those are all just newtypes that don't change size or niches, so none of it's needed. After this PR it's down to just ```rust define noundef range(i32 1, 0) i32 `@repeatedly_transparent_transmute(i32` noundef range(i32 1, 0) %_1) unnamed_addr { start: ret i32 %_1 } ``` because none of those `assume`s in the original actually did anything. (Transmuting to something with a difference niche, though, still has the assumes -- the other tests continue to pass checking that.)
This commit is contained in:
commit
446649d463
2 changed files with 32 additions and 0 deletions
|
@ -364,6 +364,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
) -> Bx::Value {
|
) -> Bx::Value {
|
||||||
assert_eq!(from_scalar.size(self.cx), to_scalar.size(self.cx));
|
assert_eq!(from_scalar.size(self.cx), to_scalar.size(self.cx));
|
||||||
|
|
||||||
|
// While optimizations will remove no-op transmutes, they might still be
|
||||||
|
// there in debug or things that aren't no-op in MIR because they change
|
||||||
|
// the Rust type but not the underlying layout/niche.
|
||||||
|
if from_scalar == to_scalar && from_backend_ty == to_backend_ty {
|
||||||
|
return imm;
|
||||||
|
}
|
||||||
|
|
||||||
use abi::Primitive::*;
|
use abi::Primitive::*;
|
||||||
imm = bx.from_immediate(imm);
|
imm = bx.from_immediate(imm);
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
use std::intrinsics::mir::*;
|
use std::intrinsics::mir::*;
|
||||||
use std::intrinsics::{transmute, transmute_unchecked};
|
use std::intrinsics::{transmute, transmute_unchecked};
|
||||||
use std::mem::MaybeUninit;
|
use std::mem::MaybeUninit;
|
||||||
|
use std::num::NonZero;
|
||||||
|
|
||||||
// FIXME(LLVM18REMOVED): `trunc nuw` doesn't exist in LLVM 18, so once we no
|
// FIXME(LLVM18REMOVED): `trunc nuw` doesn't exist in LLVM 18, so once we no
|
||||||
// longer support it the optional flag checks can be changed to required.
|
// longer support it the optional flag checks can be changed to required.
|
||||||
|
@ -470,3 +471,27 @@ pub unsafe fn check_from_overalign(x: HighAlignScalar) -> u64 {
|
||||||
// CHECK: ret i64 %[[VAL]]
|
// CHECK: ret i64 %[[VAL]]
|
||||||
transmute(x)
|
transmute(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
struct Level1(std::num::NonZero<u32>);
|
||||||
|
#[repr(transparent)]
|
||||||
|
struct Level2(Level1);
|
||||||
|
#[repr(transparent)]
|
||||||
|
struct Level3(Level2);
|
||||||
|
|
||||||
|
// CHECK-LABEL: @repeatedly_transparent_transmute
|
||||||
|
// CHECK-SAME: (i32{{.+}}%[[ARG:[^)]+]])
|
||||||
|
#[no_mangle]
|
||||||
|
#[custom_mir(dialect = "runtime", phase = "optimized")]
|
||||||
|
pub unsafe fn repeatedly_transparent_transmute(x: NonZero<u32>) -> Level3 {
|
||||||
|
// CHECK: start
|
||||||
|
// CHECK-NEXT: ret i32 %[[ARG]]
|
||||||
|
mir! {
|
||||||
|
{
|
||||||
|
let A = CastTransmute::<NonZero<u32>, Level1>(x);
|
||||||
|
let B = CastTransmute::<Level1, Level2>(A);
|
||||||
|
RET = CastTransmute::<Level2, Level3>(B);
|
||||||
|
Return()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue