Skip to content

Commit 446649d

Browse files
committed
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.)
2 parents dea1661 + 23c6b93 commit 446649d

File tree

2 files changed

+32
-0
lines changed

2 files changed

+32
-0
lines changed

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

+7
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
364364
) -> Bx::Value {
365365
assert_eq!(from_scalar.size(self.cx), to_scalar.size(self.cx));
366366

367+
// While optimizations will remove no-op transmutes, they might still be
368+
// there in debug or things that aren't no-op in MIR because they change
369+
// the Rust type but not the underlying layout/niche.
370+
if from_scalar == to_scalar && from_backend_ty == to_backend_ty {
371+
return imm;
372+
}
373+
367374
use abi::Primitive::*;
368375
imm = bx.from_immediate(imm);
369376

tests/codegen/intrinsics/transmute.rs

+25
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use std::intrinsics::mir::*;
1111
use std::intrinsics::{transmute, transmute_unchecked};
1212
use std::mem::MaybeUninit;
13+
use std::num::NonZero;
1314

1415
// FIXME(LLVM18REMOVED): `trunc nuw` doesn't exist in LLVM 18, so once we no
1516
// 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 {
470471
// CHECK: ret i64 %[[VAL]]
471472
transmute(x)
472473
}
474+
475+
#[repr(transparent)]
476+
struct Level1(std::num::NonZero<u32>);
477+
#[repr(transparent)]
478+
struct Level2(Level1);
479+
#[repr(transparent)]
480+
struct Level3(Level2);
481+
482+
// CHECK-LABEL: @repeatedly_transparent_transmute
483+
// CHECK-SAME: (i32{{.+}}%[[ARG:[^)]+]])
484+
#[no_mangle]
485+
#[custom_mir(dialect = "runtime", phase = "optimized")]
486+
pub unsafe fn repeatedly_transparent_transmute(x: NonZero<u32>) -> Level3 {
487+
// CHECK: start
488+
// CHECK-NEXT: ret i32 %[[ARG]]
489+
mir! {
490+
{
491+
let A = CastTransmute::<NonZero<u32>, Level1>(x);
492+
let B = CastTransmute::<Level1, Level2>(A);
493+
RET = CastTransmute::<Level2, Level3>(B);
494+
Return()
495+
}
496+
}
497+
}

0 commit comments

Comments
 (0)