Skip to content

Commit

Permalink
Ban non-array SIMD
Browse files Browse the repository at this point in the history
  • Loading branch information
scottmcm committed Aug 23, 2024
1 parent a32d4a0 commit 7056f90
Show file tree
Hide file tree
Showing 99 changed files with 787 additions and 1,080 deletions.
42 changes: 22 additions & 20 deletions compiler/rustc_hir_analysis/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1063,20 +1063,29 @@ pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) {
struct_span_code_err!(tcx.dcx(), sp, E0075, "SIMD vector cannot be empty").emit();
return;
}
let e = fields[FieldIdx::ZERO].ty(tcx, args);
if !fields.iter().all(|f| f.ty(tcx, args) == e) {
struct_span_code_err!(tcx.dcx(), sp, E0076, "SIMD vector should be homogeneous")
.with_span_label(sp, "SIMD elements must have the same type")

let array_field = &fields[FieldIdx::ZERO];
let array_ty = array_field.ty(tcx, args);
let ty::Array(element_ty, len_const) = array_ty.kind() else {
struct_span_code_err!(
tcx.dcx(),
sp,
E0076,
"SIMD vector's only field must be an array"
)
.with_span_label(tcx.def_span(array_field.did), "not an array")
.emit();
return;
};

if let Some(second_field) = fields.get(FieldIdx::from_u32(1)) {
struct_span_code_err!(tcx.dcx(), sp, E0075, "SIMD vector cannot have multiple fields")
.with_span_label(tcx.def_span(second_field.did), "excess field")
.emit();
return;
}

let len = if let ty::Array(_ty, c) = e.kind() {
c.try_eval_target_usize(tcx, tcx.param_env(def.did()))
} else {
Some(fields.len() as u64)
};
if let Some(len) = len {
if let Some(len) = len_const.try_eval_target_usize(tcx, tcx.param_env(def.did())) {
if len == 0 {
struct_span_code_err!(tcx.dcx(), sp, E0075, "SIMD vector cannot be empty").emit();
return;
Expand All @@ -1096,16 +1105,9 @@ pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) {
// These are scalar types which directly match a "machine" type
// Yes: Integers, floats, "thin" pointers
// No: char, "fat" pointers, compound types
match e.kind() {
ty::Param(_) => (), // pass struct<T>(T, T, T, T) through, let monomorphization catch errors
ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_, _) => (), // struct(u8, u8, u8, u8) is ok
ty::Array(t, _) if matches!(t.kind(), ty::Param(_)) => (), // pass struct<T>([T; N]) through, let monomorphization catch errors
ty::Array(t, _clen)
if matches!(
t.kind(),
ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_, _)
) =>
{ /* struct([f32; 4]) is ok */ }
match element_ty.kind() {
ty::Param(_) => (), // pass struct<T>([T; 4]) through, let monomorphization catch errors
ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_, _) => (), // struct([u8; 4]) is ok
_ => {
struct_span_code_err!(
tcx.dcx(),
Expand Down
38 changes: 15 additions & 23 deletions compiler/rustc_middle/src/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1091,29 +1091,21 @@ impl<'tcx> Ty<'tcx> {
}

pub fn simd_size_and_type(self, tcx: TyCtxt<'tcx>) -> (u64, Ty<'tcx>) {
match self.kind() {
Adt(def, args) => {
assert!(def.repr().simd(), "`simd_size_and_type` called on non-SIMD type");
let variant = def.non_enum_variant();
let f0_ty = variant.fields[FieldIdx::ZERO].ty(tcx, args);

match f0_ty.kind() {
// If the first field is an array, we assume it is the only field and its
// elements are the SIMD components.
Array(f0_elem_ty, f0_len) => {
// FIXME(repr_simd): https://github.com/rust-lang/rust/pull/78863#discussion_r522784112
// The way we evaluate the `N` in `[T; N]` here only works since we use
// `simd_size_and_type` post-monomorphization. It will probably start to ICE
// if we use it in generic code. See the `simd-array-trait` ui test.
(f0_len.eval_target_usize(tcx, ParamEnv::empty()), *f0_elem_ty)
}
// Otherwise, the fields of this Adt are the SIMD components (and we assume they
// all have the same type).
_ => (variant.fields.len() as u64, f0_ty),
}
}
_ => bug!("`simd_size_and_type` called on invalid type"),
}
let Adt(def, args) = self.kind() else {
bug!("`simd_size_and_type` called on invalid type")
};
assert!(def.repr().simd(), "`simd_size_and_type` called on non-SIMD type");
let variant = def.non_enum_variant();
assert_eq!(variant.fields.len(), 1);
let field_ty = variant.fields[FieldIdx::ZERO].ty(tcx, args);
let Array(f0_elem_ty, f0_len) = field_ty.kind() else {
bug!("Simd type has non-array field type {field_ty:?}")
};
// FIXME(repr_simd): https://github.com/rust-lang/rust/pull/78863#discussion_r522784112
// The way we evaluate the `N` in `[T; N]` here only works since we use
// `simd_size_and_type` post-monomorphization. It will probably start to ICE
// if we use it in generic code. See the `simd-array-trait` ui test.
(f0_len.eval_target_usize(tcx, ParamEnv::empty()), *f0_elem_ty)
}

#[inline]
Expand Down
27 changes: 15 additions & 12 deletions tests/assembly/asm/aarch64-types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,32 +28,35 @@ trait Sized {}
#[lang = "copy"]
trait Copy {}

// Do we really need to use no_core for this?!?
impl<T: Copy, const N: usize> Copy for [T; N] {}

type ptr = *mut u8;

#[repr(simd)]
pub struct i8x8(i8, i8, i8, i8, i8, i8, i8, i8);
pub struct i8x8([i8; 8]);
#[repr(simd)]
pub struct i16x4(i16, i16, i16, i16);
pub struct i16x4([i16; 4]);
#[repr(simd)]
pub struct i32x2(i32, i32);
pub struct i32x2([i32; 2]);
#[repr(simd)]
pub struct i64x1(i64);
pub struct i64x1([i64; 1]);
#[repr(simd)]
pub struct f32x2(f32, f32);
pub struct f32x2([f32; 2]);
#[repr(simd)]
pub struct f64x1(f64);
pub struct f64x1([f64; 1]);
#[repr(simd)]
pub struct i8x16(i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8);
pub struct i8x16([i8; 16]);
#[repr(simd)]
pub struct i16x8(i16, i16, i16, i16, i16, i16, i16, i16);
pub struct i16x8([i16; 8]);
#[repr(simd)]
pub struct i32x4(i32, i32, i32, i32);
pub struct i32x4([i32; 4]);
#[repr(simd)]
pub struct i64x2(i64, i64);
pub struct i64x2([i64; 2]);
#[repr(simd)]
pub struct f32x4(f32, f32, f32, f32);
pub struct f32x4([f32; 4]);
#[repr(simd)]
pub struct f64x2(f64, f64);
pub struct f64x2([f64; 2]);

impl Copy for i8 {}
impl Copy for i16 {}
Expand Down
5 changes: 4 additions & 1 deletion tests/assembly/asm/arm-modifiers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@ trait Sized {}
#[lang = "copy"]
trait Copy {}

// Do we really need to use no_core for this?!?
impl<T: Copy, const N: usize> Copy for [T; N] {}

#[repr(simd)]
pub struct f32x4(f32, f32, f32, f32);
pub struct f32x4([f32; 4]);

impl Copy for i32 {}
impl Copy for f32 {}
Expand Down
27 changes: 15 additions & 12 deletions tests/assembly/asm/arm-types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,32 +30,35 @@ trait Sized {}
#[lang = "copy"]
trait Copy {}

// Do we really need to use no_core for this?!?
impl<T: Copy, const N: usize> Copy for [T; N] {}

type ptr = *mut u8;

#[repr(simd)]
pub struct i8x8(i8, i8, i8, i8, i8, i8, i8, i8);
pub struct i8x8([i8; 8]);
#[repr(simd)]
pub struct i16x4(i16, i16, i16, i16);
pub struct i16x4([i16; 4]);
#[repr(simd)]
pub struct i32x2(i32, i32);
pub struct i32x2([i32; 2]);
#[repr(simd)]
pub struct i64x1(i64);
pub struct i64x1([i64; 1]);
#[repr(simd)]
pub struct f16x4(f16, f16, f16, f16);
pub struct f16x4([f16; 4]);
#[repr(simd)]
pub struct f32x2(f32, f32);
pub struct f32x2([f32; 2]);
#[repr(simd)]
pub struct i8x16(i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8);
pub struct i8x16([i8; 16]);
#[repr(simd)]
pub struct i16x8(i16, i16, i16, i16, i16, i16, i16, i16);
pub struct i16x8([i16; 8]);
#[repr(simd)]
pub struct i32x4(i32, i32, i32, i32);
pub struct i32x4([i32; 4]);
#[repr(simd)]
pub struct i64x2(i64, i64);
pub struct i64x2([i64; 2]);
#[repr(simd)]
pub struct f16x8(f16, f16, f16, f16, f16, f16, f16, f16);
pub struct f16x8([f16; 8]);
#[repr(simd)]
pub struct f32x4(f32, f32, f32, f32);
pub struct f32x4([f32; 4]);

impl Copy for i8 {}
impl Copy for i16 {}
Expand Down
Loading

0 comments on commit 7056f90

Please sign in to comment.