Skip to content

Commit

Permalink
Auto merge of #137513 - scottmcm:identity-transmute, r=saethlin
Browse files Browse the repository at this point in the history
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.)
  • Loading branch information
bors committed Mar 9, 2025
2 parents dea1661 + 23c6b93 commit 446649d
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 0 deletions.
7 changes: 7 additions & 0 deletions compiler/rustc_codegen_ssa/src/mir/rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
) -> Bx::Value {
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::*;
imm = bx.from_immediate(imm);

Expand Down
25 changes: 25 additions & 0 deletions tests/codegen/intrinsics/transmute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use std::intrinsics::mir::*;
use std::intrinsics::{transmute, transmute_unchecked};
use std::mem::MaybeUninit;
use std::num::NonZero;

// 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.
Expand Down Expand Up @@ -470,3 +471,27 @@ pub unsafe fn check_from_overalign(x: HighAlignScalar) -> u64 {
// CHECK: ret i64 %[[VAL]]
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()
}
}
}

0 comments on commit 446649d

Please sign in to comment.