Skip to content

Operand::extract_field: only cast llval if it's a pointer and replace bitcast w/ pointercast. #105583

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 7 commits into from
May 7, 2023
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
37 changes: 33 additions & 4 deletions compiler/rustc_codegen_ssa/src/mir/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use super::place::PlaceRef;
use super::{FunctionCx, LocalRef};

use crate::base;
use crate::common::TypeKind;
use crate::glue;
use crate::traits::*;
use crate::MemFlags;
Expand Down Expand Up @@ -236,19 +237,47 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
};

match (&mut val, field.abi) {
(OperandValue::Immediate(llval), _) => {
(
OperandValue::Immediate(llval),
Abi::Scalar(_) | Abi::ScalarPair(..) | Abi::Vector { .. },
) => {
// Bools in union fields needs to be truncated.
*llval = bx.to_immediate(*llval, field);
// HACK(eddyb) have to bitcast pointers until LLVM removes pointee types.
*llval = bx.bitcast(*llval, bx.cx().immediate_backend_type(field));
let ty = bx.cx().immediate_backend_type(field);
if bx.type_kind(ty) == TypeKind::Pointer {
*llval = bx.pointercast(*llval, ty);
}
Comment on lines +247 to +250
Copy link
Contributor

Choose a reason for hiding this comment

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

This removes the illegal bitcast <4 x i32> to [4 x i32], which is good--but it doesn't replace that illegal conversion with a legal one, so we end up returning a value of type <4 x i32> when extracting a field of type [4 x i32].

The repro example happens to work anyways because the next instruction is a store, and both store <4 x i32> and store [4 x i32] have the same effect. But it won't work if we use that value by value. (This may never happen, because I don't think we ever pass LLVM array types by value, but it's technically incorrect.)

To handle this fully, I think you'd have to special-case this kind of newtype field extraction (self Abi::Vector, field Abi::Aggregate) and roundtrip the vector through memory (alloca, store <4 x i32>, load [4 x i32]).

(I thought there might be endianness concerns, but it seems like that's only a problem for not-a-multiple-of-i8 types like i4)

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point! Split out the case for newtype of simd array

}
(OperandValue::Pair(a, b), Abi::ScalarPair(a_abi, b_abi)) => {
// Bools in union fields needs to be truncated.
*a = bx.to_immediate_scalar(*a, a_abi);
*b = bx.to_immediate_scalar(*b, b_abi);
// HACK(eddyb) have to bitcast pointers until LLVM removes pointee types.
*a = bx.bitcast(*a, bx.cx().scalar_pair_element_backend_type(field, 0, true));
*b = bx.bitcast(*b, bx.cx().scalar_pair_element_backend_type(field, 1, true));
let a_ty = bx.cx().scalar_pair_element_backend_type(field, 0, true);
let b_ty = bx.cx().scalar_pair_element_backend_type(field, 1, true);
if bx.type_kind(a_ty) == TypeKind::Pointer {
*a = bx.pointercast(*a, a_ty);
}
if bx.type_kind(b_ty) == TypeKind::Pointer {
*b = bx.pointercast(*b, b_ty);
}
}
// Newtype vector of array, e.g. #[repr(simd)] struct S([i32; 4]);
(OperandValue::Immediate(llval), Abi::Aggregate { sized: true }) => {
assert!(matches!(self.layout.abi, Abi::Vector { .. }));

let llty = bx.cx().backend_type(self.layout);
let llfield_ty = bx.cx().backend_type(field);

// Can't bitcast an aggregate, so round trip through memory.
let lltemp = bx.alloca(llfield_ty, field.align.abi);
let llptr = bx.pointercast(lltemp, bx.cx().type_ptr_to(llty));
bx.store(*llval, llptr, field.align.abi);
*llval = bx.load(llfield_ty, lltemp, field.align.abi);
}
(OperandValue::Immediate(_), Abi::Uninhabited | Abi::Aggregate { sized: false }) => {
bug!()
}
(OperandValue::Pair(..), _) => bug!(),
(OperandValue::Ref(..), _) => bug!(),
Expand Down
25 changes: 25 additions & 0 deletions tests/ui/simd/issue-105439.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// run-pass
// compile-flags: -O -Zverify-llvm-ir

#![feature(repr_simd)]
#![feature(platform_intrinsics)]

#[allow(non_camel_case_types)]
#[derive(Clone, Copy)]
#[repr(simd)]
struct i32x4([i32; 4]);

extern "platform-intrinsic" {
pub(crate) fn simd_add<T>(x: T, y: T) -> T;
}

#[inline(always)]
fn to_array(a: i32x4) -> [i32; 4] {
a.0
}

fn main() {
let a = i32x4([1, 2, 3, 4]);
let b = unsafe { simd_add(a, a) };
assert_eq!(to_array(b), [2, 4, 6, 8]);
}