Description
Code sample:
#[repr(transparent)]
pub struct K {
b: usize
}
pub fn foo(k: K) {
let x = unsafe { std::mem::transmute::<_, *mut i8>(k) };
std::hint::black_box(unsafe { *x });
}
Expected to generate asm like this(for target aarch64-apple-darwin
):
__ZN7example3foo17hbc2dffde7832bc1dE:
sub sp, sp, #16
ldrb w8, [x0]
strb w8, [sp, #15]
add x8, sp, #15
add sp, sp, #16
ret
However, the generated asm looks like this:
__ZN7example3foo17hbc2dffde7832bc1dE:
brk #0x1
Godbolt: https://rust.godbolt.org/z/s5PY4TPME
I did some research, and found that Rust emits such LLVM IR for the transmute
:
%x = getelementptr i8, ptr null, i64 %k
%dummy = load i8, ptr %x, align 1
However, load (gep ptr null)
is assumed as unreachable
in LLVM's InstCombine
pass, thus brk
is generated.
The transmute
was orignally lowered as inttoptr
rather than getelementptr i8 ptr null..
, which is legal and the result assembly won't crash.
I performed a bisection and discovered that the codegen change was introduced by#121282. I suspect that this PR was intended to address integer-to-pointer conversions, but it inadvertently affects the transmute of integer-sized-struct-to-pointer conversions.
As far as I can tell, integer-sized-struct-to-pointer conversions are not UB 🤔 : https://doc.rust-lang.org/nightly/std/intrinsics/fn.transmute.html.