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:
commit
760430e6fd
17 changed files with 467 additions and 157 deletions
|
@ -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 {
|
||||
|
|
|
@ -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"),
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue