Skip to content

Commit 5d16a7b

Browse files
committed
Avoid a bitcast FFI call in transmuting
For things that only change the valid ranges, we can just skip the `LLVMBuildBitCast` call. I tried to tweak this a bit more and broke stuff, so I also added some extra tests for that as we apparently didn't have coverage.
1 parent d1d8e38 commit 5d16a7b

File tree

2 files changed

+53
-2
lines changed

2 files changed

+53
-2
lines changed

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,7 +1123,7 @@ pub(super) fn transmute_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
11231123
// While optimizations will remove no-op transmutes, they might still be
11241124
// there in debug or things that aren't no-op in MIR because they change
11251125
// the Rust type but not the underlying layout/niche.
1126-
if from_scalar == to_scalar && from_backend_ty == to_backend_ty {
1126+
if from_scalar == to_scalar {
11271127
return imm;
11281128
}
11291129

@@ -1142,7 +1142,13 @@ pub(super) fn transmute_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
11421142
assume_scalar_range(bx, imm, from_scalar, from_backend_ty);
11431143

11441144
imm = match (from_scalar.primitive(), to_scalar.primitive()) {
1145-
(Int(..) | Float(_), Int(..) | Float(_)) => bx.bitcast(imm, to_backend_ty),
1145+
(Int(..) | Float(_), Int(..) | Float(_)) => {
1146+
if from_backend_ty == to_backend_ty {
1147+
imm
1148+
} else {
1149+
bx.bitcast(imm, to_backend_ty)
1150+
}
1151+
}
11461152
(Pointer(..), Pointer(..)) => bx.pointercast(imm, to_backend_ty),
11471153
(Int(..), Pointer(..)) => bx.ptradd(bx.const_null(bx.type_ptr()), imm),
11481154
(Pointer(..), Int(..)) => {

tests/codegen/transmute-scalar.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,48 @@ pub fn ptr_to_int(p: *mut u16) -> usize {
5555
pub fn int_to_ptr(i: usize) -> *mut u16 {
5656
unsafe { std::mem::transmute(i) }
5757
}
58+
59+
// This is the one case where signedness matters to transmuting:
60+
// the LLVM type is `i8` here because of `repr(i8)`,
61+
// whereas below with the `repr(u8)` it's `i1` in LLVM instead.
62+
#[repr(i8)]
63+
pub enum FakeBoolSigned {
64+
False = 0,
65+
True = 1,
66+
}
67+
68+
// CHECK-LABEL: define{{.*}}i8 @bool_to_fake_bool_signed(i1 zeroext %b)
69+
// CHECK: %_0 = zext i1 %b to i8
70+
// CHECK-NEXT: ret i8 %_0
71+
#[no_mangle]
72+
pub fn bool_to_fake_bool_signed(b: bool) -> FakeBoolSigned {
73+
unsafe { std::mem::transmute(b) }
74+
}
75+
76+
// CHECK-LABEL: define{{.*}}i1 @fake_bool_signed_to_bool(i8 %b)
77+
// CHECK: %_0 = trunc nuw i8 %b to i1
78+
// CHECK-NEXT: ret i1 %_0
79+
#[no_mangle]
80+
pub fn fake_bool_signed_to_bool(b: FakeBoolSigned) -> bool {
81+
unsafe { std::mem::transmute(b) }
82+
}
83+
84+
#[repr(u8)]
85+
pub enum FakeBoolUnsigned {
86+
False = 0,
87+
True = 1,
88+
}
89+
90+
// CHECK-LABEL: define{{.*}}i1 @bool_to_fake_bool_unsigned(i1 zeroext %b)
91+
// CHECK: ret i1 %b
92+
#[no_mangle]
93+
pub fn bool_to_fake_bool_unsigned(b: bool) -> FakeBoolUnsigned {
94+
unsafe { std::mem::transmute(b) }
95+
}
96+
97+
// CHECK-LABEL: define{{.*}}i1 @fake_bool_unsigned_to_bool(i1 zeroext %b)
98+
// CHECK: ret i1 %b
99+
#[no_mangle]
100+
pub fn fake_bool_unsigned_to_bool(b: FakeBoolUnsigned) -> bool {
101+
unsafe { std::mem::transmute(b) }
102+
}

0 commit comments

Comments
 (0)