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.
This commit is contained in:
@@ -1123,7 +1123,7 @@ pub(super) fn transmute_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||||||
// While optimizations will remove no-op transmutes, they might still be
|
// 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
|
// there in debug or things that aren't no-op in MIR because they change
|
||||||
// the Rust type but not the underlying layout/niche.
|
// the Rust type but not the underlying layout/niche.
|
||||||
if from_scalar == to_scalar && from_backend_ty == to_backend_ty {
|
if from_scalar == to_scalar {
|
||||||
return imm;
|
return imm;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1142,7 +1142,13 @@ pub(super) fn transmute_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||||||
assume_scalar_range(bx, imm, from_scalar, from_backend_ty);
|
assume_scalar_range(bx, imm, from_scalar, from_backend_ty);
|
||||||
|
|
||||||
imm = match (from_scalar.primitive(), to_scalar.primitive()) {
|
imm = match (from_scalar.primitive(), to_scalar.primitive()) {
|
||||||
(Int(..) | Float(_), Int(..) | Float(_)) => bx.bitcast(imm, to_backend_ty),
|
(Int(..) | Float(_), Int(..) | Float(_)) => {
|
||||||
|
if from_backend_ty == to_backend_ty {
|
||||||
|
imm
|
||||||
|
} else {
|
||||||
|
bx.bitcast(imm, to_backend_ty)
|
||||||
|
}
|
||||||
|
}
|
||||||
(Pointer(..), Pointer(..)) => bx.pointercast(imm, to_backend_ty),
|
(Pointer(..), Pointer(..)) => bx.pointercast(imm, to_backend_ty),
|
||||||
(Int(..), Pointer(..)) => bx.ptradd(bx.const_null(bx.type_ptr()), imm),
|
(Int(..), Pointer(..)) => bx.ptradd(bx.const_null(bx.type_ptr()), imm),
|
||||||
(Pointer(..), Int(..)) => {
|
(Pointer(..), Int(..)) => {
|
||||||
|
|||||||
@@ -55,3 +55,48 @@ pub fn ptr_to_int(p: *mut u16) -> usize {
|
|||||||
pub fn int_to_ptr(i: usize) -> *mut u16 {
|
pub fn int_to_ptr(i: usize) -> *mut u16 {
|
||||||
unsafe { std::mem::transmute(i) }
|
unsafe { std::mem::transmute(i) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is the one case where signedness matters to transmuting:
|
||||||
|
// the LLVM type is `i8` here because of `repr(i8)`,
|
||||||
|
// whereas below with the `repr(u8)` it's `i1` in LLVM instead.
|
||||||
|
#[repr(i8)]
|
||||||
|
pub enum FakeBoolSigned {
|
||||||
|
False = 0,
|
||||||
|
True = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: define{{.*}}i8 @bool_to_fake_bool_signed(i1 zeroext %b)
|
||||||
|
// CHECK: %_0 = zext i1 %b to i8
|
||||||
|
// CHECK-NEXT: ret i8 %_0
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn bool_to_fake_bool_signed(b: bool) -> FakeBoolSigned {
|
||||||
|
unsafe { std::mem::transmute(b) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: define{{.*}}i1 @fake_bool_signed_to_bool(i8 %b)
|
||||||
|
// CHECK: %_0 = trunc nuw i8 %b to i1
|
||||||
|
// CHECK-NEXT: ret i1 %_0
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn fake_bool_signed_to_bool(b: FakeBoolSigned) -> bool {
|
||||||
|
unsafe { std::mem::transmute(b) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum FakeBoolUnsigned {
|
||||||
|
False = 0,
|
||||||
|
True = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: define{{.*}}i1 @bool_to_fake_bool_unsigned(i1 zeroext %b)
|
||||||
|
// CHECK: ret i1 %b
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn bool_to_fake_bool_unsigned(b: bool) -> FakeBoolUnsigned {
|
||||||
|
unsafe { std::mem::transmute(b) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: define{{.*}}i1 @fake_bool_unsigned_to_bool(i1 zeroext %b)
|
||||||
|
// CHECK: ret i1 %b
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn fake_bool_unsigned_to_bool(b: FakeBoolUnsigned) -> bool {
|
||||||
|
unsafe { std::mem::transmute(b) }
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user