diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index ff555f4273cbb..95b108b1d334a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -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); diff --git a/tests/codegen/intrinsics/transmute.rs b/tests/codegen/intrinsics/transmute.rs index ff297b2706578..4849b88c09c2e 100644 --- a/tests/codegen/intrinsics/transmute.rs +++ b/tests/codegen/intrinsics/transmute.rs @@ -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. @@ -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); +#[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) -> Level3 { + // CHECK: start + // CHECK-NEXT: ret i32 %[[ARG]] + mir! { + { + let A = CastTransmute::, Level1>(x); + let B = CastTransmute::(A); + RET = CastTransmute::(B); + Return() + } + } +}