Skip to content

Commit

Permalink
Support repr(simd) on ADTs containing a single array field
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
gnzlbg committed Aug 13, 2019
1 parent d4abb08 commit 257158f
Show file tree
Hide file tree
Showing 10 changed files with 390 additions and 155 deletions.
124 changes: 101 additions & 23 deletions src/librustc/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -687,34 +687,117 @@ 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) as u64;
assert!(count > 0);
let scalar = match element.abi {
Abi::Scalar(ref scalar) => scalar.clone(),
_ => {
tcx.sess.fatal(&format!("monomorphising SIMD type `{}` with \
a non-machine element type `{}`",
ty, element.ty));
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 "machine type", e.g., `f32`, `i64`, `*mut _`.

// SIMD vectors with zero fields are not supported:
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:
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.sty {
// First ADT field is an array:

// SIMD vectors with multiple array fields are not supported:
if def.non_enum_variant().fields.len() != 1 {
tcx.sess.fatal(&format!(
"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(TyLayout{
details: LayoutDetails {
fields: FieldPlacement::Array {
count, ..
}, ..
}, ..
}) = self.layout_of(f0_ty) {
count
} else {
unreachable!();
};

(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)

// SIMD vectors of zero length are not supported:
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 {
tcx.sess.fatal(&format!(
"monomorphising SIMD type `{}` with a non-machine 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 {
FieldPlacement::Arbitrary {
offsets: vec![Size::ZERO],
memory_index: vec![0],
}
} else {
FieldPlacement::Array {
stride: e_ly.size,
count: e_len,
}
};

tcx.intern_layout(LayoutDetails {
variants: Variants::Single { index: VariantIdx::new(0) },
fields: FieldPlacement::Array {
stride: element.size,
count
},
fields,
abi: Abi::Vector {
element: scalar,
count
element: e_abi,
count: e_len,
},
largest_niche: element.largest_niche.clone(),
largest_niche: e_ly.largest_niche.clone(),
size,
align,
})
Expand Down Expand Up @@ -2170,11 +2253,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 {
Expand Down
16 changes: 0 additions & 16 deletions src/librustc/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1823,22 +1823,6 @@ impl<'tcx> TyS<'tcx> {
}
}

pub fn simd_type(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
match self.sty {
Adt(def, substs) => {
def.non_enum_variant().fields[0].ty(tcx, substs)
}
_ => bug!("simd_type called on invalid type")
}
}

pub fn simd_size(&self, _cx: TyCtxt<'_>) -> usize {
match self.sty {
Adt(def, _) => def.non_enum_variant().fields.len(),
_ => bug!("simd_size called on invalid type")
}
}

#[inline]
pub fn is_region_ptr(&self) -> bool {
match self.sty {
Expand Down
Loading

0 comments on commit 257158f

Please sign in to comment.