1
Fork 0

Auto merge of #78863 - KodrAus:feat/simd-array, r=oli-obk

Support repr(simd) on ADTs containing a single array field

This is a squash and rebase of `@gnzlbg's` #63531

I've never actually written code in the compiler before so just fumbled my way around until it would build 😅

I imagine there'll be some work we need to do in `rustc_codegen_cranelift` too for this now, but might need some input from `@bjorn3` to know what that is.

cc `@rust-lang/project-portable-simd`

-----

This PR allows using `#[repr(simd)]` on ADTs containing a single array field:

```rust
 #[repr(simd)] struct S0([f32; 4]);
 #[repr(simd)] struct S1<const N: usize>([f32; N]);
 #[repr(simd)] struct S2<T, const N: usize>([T; N]);
```

This should allow experimenting with portable packed SIMD abstractions on nightly that make use of const generics.
This commit is contained in:
bors 2020-11-29 09:28:09 +00:00
commit 760430e6fd
17 changed files with 467 additions and 157 deletions

View file

@ -631,30 +631,106 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
}
// SIMD vector types.
ty::Adt(def, ..) if def.repr.simd() => {
let element = self.layout_of(ty.simd_type(tcx))?;
let count = ty.simd_size(tcx);
assert!(count > 0);
let scalar = match element.abi {
Abi::Scalar(ref scalar) => scalar.clone(),
_ => {
ty::Adt(def, substs) if def.repr.simd() => {
// Supported SIMD vectors are homogeneous ADTs with at least one field:
//
// * #[repr(simd)] struct S(T, T, T, T);
// * #[repr(simd)] struct S { x: T, y: T, z: T, w: T }
// * #[repr(simd)] struct S([T; 4])
//
// where T is a primitive scalar (integer/float/pointer).
// SIMD vectors with zero fields are not supported.
// (should be caught by typeck)
if def.non_enum_variant().fields.is_empty() {
tcx.sess.fatal(&format!("monomorphising SIMD type `{}` of zero length", ty));
}
// Type of the first ADT field:
let f0_ty = def.non_enum_variant().fields[0].ty(tcx, substs);
// Heterogeneous SIMD vectors are not supported:
// (should be caught by typeck)
for fi in &def.non_enum_variant().fields {
if fi.ty(tcx, substs) != f0_ty {
tcx.sess.fatal(&format!("monomorphising heterogeneous SIMD type `{}`", ty));
}
}
// The element type and number of elements of the SIMD vector
// are obtained from:
//
// * the element type and length of the single array field, if
// the first field is of array type, or
//
// * the homogenous field type and the number of fields.
let (e_ty, e_len, is_array) = if let ty::Array(e_ty, _) = f0_ty.kind() {
// First ADT field is an array:
// SIMD vectors with multiple array fields are not supported:
// (should be caught by typeck)
if def.non_enum_variant().fields.len() != 1 {
tcx.sess.fatal(&format!(
"monomorphising SIMD type `{}` with \
a non-machine element type `{}`",
ty, element.ty
"monomorphising SIMD type `{}` with more than one array field",
ty
));
}
// Extract the number of elements from the layout of the array field:
let len = if let Ok(TyAndLayout {
layout: Layout { fields: FieldsShape::Array { count, .. }, .. },
..
}) = self.layout_of(f0_ty)
{
count
} else {
return Err(LayoutError::Unknown(ty));
};
(*e_ty, *len, true)
} else {
// First ADT field is not an array:
(f0_ty, def.non_enum_variant().fields.len() as _, false)
};
let size =
element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow(ty))?;
// SIMD vectors of zero length are not supported.
//
// Can't be caught in typeck if the array length is generic.
if e_len == 0 {
tcx.sess.fatal(&format!("monomorphising SIMD type `{}` of zero length", ty));
}
// Compute the ABI of the element type:
let e_ly = self.layout_of(e_ty)?;
let e_abi = if let Abi::Scalar(ref scalar) = e_ly.abi {
scalar.clone()
} else {
// This error isn't caught in typeck, e.g., if
// the element type of the vector is generic.
tcx.sess.fatal(&format!(
"monomorphising SIMD type `{}` with a non-primitive-scalar \
(integer/float/pointer) element type `{}`",
ty, e_ty
))
};
// Compute the size and alignment of the vector:
let size = e_ly.size.checked_mul(e_len, dl).ok_or(LayoutError::SizeOverflow(ty))?;
let align = dl.vector_align(size);
let size = size.align_to(align.abi);
// Compute the placement of the vector fields:
let fields = if is_array {
FieldsShape::Arbitrary { offsets: vec![Size::ZERO], memory_index: vec![0] }
} else {
FieldsShape::Array { stride: e_ly.size, count: e_len }
};
tcx.intern_layout(Layout {
variants: Variants::Single { index: VariantIdx::new(0) },
fields: FieldsShape::Array { stride: element.size, count },
abi: Abi::Vector { element: scalar, count },
largest_niche: element.largest_niche.clone(),
fields,
abi: Abi::Vector { element: e_abi, count: e_len },
largest_niche: e_ly.largest_niche.clone(),
size,
align,
})
@ -2121,9 +2197,6 @@ where
ty::Tuple(tys) => tys[i].expect_ty(),
// SIMD vector types.
ty::Adt(def, ..) if def.repr.simd() => this.ty.simd_type(tcx),
// ADTs.
ty::Adt(def, substs) => {
match this.variants {

View file

@ -1956,27 +1956,22 @@ impl<'tcx> TyS<'tcx> {
}
}
pub fn simd_type(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
match self.kind() {
Adt(def, substs) => def.non_enum_variant().fields[0].ty(tcx, substs),
_ => bug!("`simd_type` called on invalid type"),
}
}
pub fn simd_size(&self, _tcx: TyCtxt<'tcx>) -> u64 {
// Parameter currently unused, but probably needed in the future to
// allow `#[repr(simd)] struct Simd<T, const N: usize>([T; N]);`.
match self.kind() {
Adt(def, _) => def.non_enum_variant().fields.len() as u64,
_ => bug!("`simd_size` called on invalid type"),
}
}
pub fn simd_size_and_type(&self, tcx: TyCtxt<'tcx>) -> (u64, Ty<'tcx>) {
match self.kind() {
Adt(def, substs) => {
let variant = def.non_enum_variant();
(variant.fields.len() as u64, variant.fields[0].ty(tcx, substs))
let f0_ty = variant.fields[0].ty(tcx, substs);
match f0_ty.kind() {
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_usize(tcx, ParamEnv::empty()) as u64, f0_elem_ty)
}
_ => (variant.fields.len() as u64, f0_ty),
}
}
_ => bug!("`simd_size_and_type` called on invalid type"),
}