Skip to content

fix bitcast of single-element SIMD vectors #143194

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions compiler/rustc_codegen_ssa/src/mir/rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
// 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 {
if from_scalar == to_scalar && from_backend_ty == to_backend_ty {
Copy link
Member

@workingjubilee workingjubilee Jul 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should add some more comments on why both of these must obtain. That can happen after this merges and the discussion on Zulip settles though.

Copy link
Member

@RalfJung RalfJung Jul 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The entire function is cursed. It talks about transmuting "immediates" but then takes Scalar information as arguments. This needs some refactoring figuring out which class of types it is meant for, and aligning name and signature and comments.

return imm;
}

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

imm = match (from_scalar.primitive(), to_scalar.primitive()) {
(Int(..) | Float(_), Int(..) | Float(_)) => {
if from_backend_ty == to_backend_ty {
imm
} else {
bx.bitcast(imm, to_backend_ty)
}
}
(Int(..) | Float(_), Int(..) | Float(_)) => bx.bitcast(imm, to_backend_ty),
(Pointer(..), Pointer(..)) => bx.pointercast(imm, to_backend_ty),
(Int(..), Pointer(..)) => bx.ptradd(bx.const_null(bx.type_ptr()), imm),
(Pointer(..), Int(..)) => {
Expand Down
55 changes: 45 additions & 10 deletions tests/codegen/transmute-scalar.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
//@ add-core-stubs
//@ compile-flags: -C opt-level=0 -C no-prepopulate-passes

#![crate_type = "lib"]
#![feature(no_core, repr_simd, arm_target_feature, mips_target_feature, s390x_target_feature)]
#![no_core]
extern crate minicore;

use minicore::*;

// With opaque ptrs in LLVM, `transmute` can load/store any `alloca` as any type,
// without needing to pointercast, and SRoA will turn that into a `bitcast`.
Expand All @@ -14,46 +20,46 @@
// CHECK-NEXT: ret i32 %_0
#[no_mangle]
pub fn f32_to_bits(x: f32) -> u32 {
unsafe { std::mem::transmute(x) }
unsafe { mem::transmute(x) }
}

// CHECK-LABEL: define{{.*}}i8 @bool_to_byte(i1 zeroext %b)
// CHECK: %_0 = zext i1 %b to i8
// CHECK-NEXT: ret i8 %_0
#[no_mangle]
pub fn bool_to_byte(b: bool) -> u8 {
unsafe { std::mem::transmute(b) }
unsafe { mem::transmute(b) }
}

// CHECK-LABEL: define{{.*}}zeroext i1 @byte_to_bool(i8{{.*}} %byte)
// CHECK: %_0 = trunc{{( nuw)?}} i8 %byte to i1
// CHECK-NEXT: ret i1 %_0
#[no_mangle]
pub unsafe fn byte_to_bool(byte: u8) -> bool {
std::mem::transmute(byte)
mem::transmute(byte)
}

// CHECK-LABEL: define{{.*}}ptr @ptr_to_ptr(ptr %p)
// CHECK: ret ptr %p
#[no_mangle]
pub fn ptr_to_ptr(p: *mut u16) -> *mut u8 {
unsafe { std::mem::transmute(p) }
unsafe { mem::transmute(p) }
}

// CHECK: define{{.*}}[[USIZE:i[0-9]+]] @ptr_to_int(ptr %p)
// CHECK: %_0 = ptrtoint ptr %p to [[USIZE]]
// CHECK-NEXT: ret [[USIZE]] %_0
#[no_mangle]
pub fn ptr_to_int(p: *mut u16) -> usize {
unsafe { std::mem::transmute(p) }
unsafe { mem::transmute(p) }
}

// CHECK: define{{.*}}ptr @int_to_ptr([[USIZE]] %i)
// CHECK: %_0 = getelementptr i8, ptr null, [[USIZE]] %i
// CHECK-NEXT: ret ptr %_0
#[no_mangle]
pub fn int_to_ptr(i: usize) -> *mut u16 {
unsafe { std::mem::transmute(i) }
unsafe { mem::transmute(i) }
}

// This is the one case where signedness matters to transmuting:
Expand All @@ -70,15 +76,15 @@ pub enum FakeBoolSigned {
// CHECK-NEXT: ret i8 %_0
#[no_mangle]
pub fn bool_to_fake_bool_signed(b: bool) -> FakeBoolSigned {
unsafe { std::mem::transmute(b) }
unsafe { 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) }
unsafe { mem::transmute(b) }
}

#[repr(u8)]
Expand All @@ -91,12 +97,41 @@ pub enum FakeBoolUnsigned {
// CHECK: ret i1 %b
#[no_mangle]
pub fn bool_to_fake_bool_unsigned(b: bool) -> FakeBoolUnsigned {
unsafe { std::mem::transmute(b) }
unsafe { 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) }
unsafe { mem::transmute(b) }
}

#[repr(simd)]
struct S([i64; 1]);

// CHECK-LABEL: define{{.*}}i64 @single_element_simd_to_scalar(<1 x i64> %b)
// CHECK: bitcast <1 x i64> %b to i64
// CHECK: ret i64
#[no_mangle]
#[cfg_attr(target_family = "wasm", target_feature(enable = "simd128"))]
#[cfg_attr(target_arch = "arm", target_feature(enable = "neon"))]
#[cfg_attr(target_arch = "x86", target_feature(enable = "sse"))]
#[cfg_attr(target_arch = "mips", target_feature(enable = "msa"))]
#[cfg_attr(target_arch = "s390x", target_feature(enable = "vector"))]
pub extern "C" fn single_element_simd_to_scalar(b: S) -> i64 {
unsafe { mem::transmute(b) }
}

// CHECK-LABEL: define{{.*}}<1 x i64> @scalar_to_single_element_simd(i64 %b)
// CHECK: bitcast i64 %b to <1 x i64>
// CHECK: ret <1 x i64>
#[no_mangle]
#[cfg_attr(target_family = "wasm", target_feature(enable = "simd128"))]
#[cfg_attr(target_arch = "arm", target_feature(enable = "neon"))]
#[cfg_attr(target_arch = "x86", target_feature(enable = "sse"))]
#[cfg_attr(target_arch = "mips", target_feature(enable = "msa"))]
#[cfg_attr(target_arch = "s390x", target_feature(enable = "vector"))]
pub extern "C" fn scalar_to_single_element_simd(b: i64) -> S {
unsafe { mem::transmute(b) }
}
Loading