Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Support repr(simd) on ADTs containing a single array field
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 authored and KodrAus committed Nov 8, 2020
1 parent 9d78d1d commit 6e88e96
Show file tree
Hide file tree
Showing 15 changed files with 427 additions and 160 deletions.
122 changes: 72 additions & 50 deletions compiler/rustc_codegen_llvm/src/intrinsic.rs
Expand Up @@ -740,6 +740,23 @@ fn generic_simd_intrinsic(
llret_ty: &'ll Type,
span: Span,
) -> Result<&'ll Value, ()> {
// Given a SIMD vector type `x` return the element type and the number of
// elements in the vector.
fn simd_ty_and_len(bx: &Builder<'a, 'll, 'tcx>, simd_ty: Ty<'tcx>) -> (Ty<'tcx>, u64) {
let ty = if let ty::Adt(_def, _substs) = simd_ty.kind() {
let f0_ty = bx.layout_of(simd_ty).field(bx, 0).ty;
if let ty::Array(element_ty, _) = f0_ty.kind() { element_ty } else { f0_ty }
} else {
bug!("should only be called with a SIMD type")
};
let count = if let abi::Abi::Vector { count, .. } = bx.layout_of(simd_ty).abi {
count
} else {
bug!("should only be called with a SIMD type")
};
(ty, count)
}

// macros for error handling:
macro_rules! emit_error {
($msg: tt) => {
Expand Down Expand Up @@ -792,7 +809,7 @@ fn generic_simd_intrinsic(
_ => return_error!("`{}` is not an integral type", in_ty),
};
require_simd!(arg_tys[1], "argument");
let v_len = arg_tys[1].simd_size(tcx);
let (_, v_len) = simd_ty_and_len(bx, arg_tys[1]);
require!(
// Allow masks for vectors with fewer than 8 elements to be
// represented with a u8 or i8.
Expand All @@ -812,8 +829,6 @@ fn generic_simd_intrinsic(
// every intrinsic below takes a SIMD vector as its first argument
require_simd!(arg_tys[0], "input");
let in_ty = arg_tys[0];
let in_elem = arg_tys[0].simd_type(tcx);
let in_len = arg_tys[0].simd_size(tcx);

let comparison = match name {
sym::simd_eq => Some(hir::BinOpKind::Eq),
Expand All @@ -825,14 +840,15 @@ fn generic_simd_intrinsic(
_ => None,
};

let (in_elem, in_len) = simd_ty_and_len(bx, arg_tys[0]);
if let Some(cmp_op) = comparison {
require_simd!(ret_ty, "return");

let out_len = ret_ty.simd_size(tcx);
let (out_ty, out_len) = simd_ty_and_len(bx, ret_ty);
require!(
in_len == out_len,
"expected return type with length {} (same as input type `{}`), \
found `{}` with length {}",
found `{}` with length {}",
in_len,
in_ty,
ret_ty,
Expand All @@ -842,7 +858,7 @@ fn generic_simd_intrinsic(
bx.type_kind(bx.element_type(llret_ty)) == TypeKind::Integer,
"expected return type with integer elements, found `{}` with non-integer `{}`",
ret_ty,
ret_ty.simd_type(tcx)
out_ty
);

return Ok(compare_simd_types(
Expand All @@ -862,7 +878,7 @@ fn generic_simd_intrinsic(

require_simd!(ret_ty, "return");

let out_len = ret_ty.simd_size(tcx);
let (out_ty, out_len) = simd_ty_and_len(bx, ret_ty);
require!(
out_len == n,
"expected return type of length {}, found `{}` with length {}",
Expand All @@ -871,13 +887,13 @@ fn generic_simd_intrinsic(
out_len
);
require!(
in_elem == ret_ty.simd_type(tcx),
in_elem == out_ty,
"expected return element type `{}` (element of input `{}`), \
found `{}` with element type `{}`",
found `{}` with element type `{}`",
in_elem,
in_ty,
ret_ty,
ret_ty.simd_type(tcx)
out_ty
);

let total_len = u128::from(in_len) * 2;
Expand Down Expand Up @@ -946,7 +962,7 @@ fn generic_simd_intrinsic(
let m_elem_ty = in_elem;
let m_len = in_len;
require_simd!(arg_tys[1], "argument");
let v_len = arg_tys[1].simd_size(tcx);
let (_, v_len) = simd_ty_and_len(bx, arg_tys[1]);
require!(
m_len == v_len,
"mismatched lengths: mask length `{}` != other vector length `{}`",
Expand Down Expand Up @@ -1171,25 +1187,27 @@ fn generic_simd_intrinsic(
require_simd!(ret_ty, "return");

// Of the same length:
let (_, out_len) = simd_ty_and_len(bx, arg_tys[1]);
let (_, out_len2) = simd_ty_and_len(bx, arg_tys[2]);
require!(
in_len == arg_tys[1].simd_size(tcx),
in_len == out_len,
"expected {} argument with length {} (same as input type `{}`), \
found `{}` with length {}",
found `{}` with length {}",
"second",
in_len,
in_ty,
arg_tys[1],
arg_tys[1].simd_size(tcx)
out_len
);
require!(
in_len == arg_tys[2].simd_size(tcx),
in_len == out_len2,
"expected {} argument with length {} (same as input type `{}`), \
found `{}` with length {}",
found `{}` with length {}",
"third",
in_len,
in_ty,
arg_tys[2],
arg_tys[2].simd_size(tcx)
out_len2
);

// The return type must match the first argument type
Expand All @@ -1213,39 +1231,40 @@ fn generic_simd_intrinsic(

// The second argument must be a simd vector with an element type that's a pointer
// to the element type of the first argument
let (pointer_count, underlying_ty) = match arg_tys[1].simd_type(tcx).kind() {
ty::RawPtr(p) if p.ty == in_elem => {
(ptr_count(arg_tys[1].simd_type(tcx)), non_ptr(arg_tys[1].simd_type(tcx)))
}
let (element_ty0, _) = simd_ty_and_len(bx, arg_tys[0]);
let (element_ty1, _) = simd_ty_and_len(bx, arg_tys[1]);
let (pointer_count, underlying_ty) = match element_ty1.kind() {
ty::RawPtr(p) if p.ty == in_elem => (ptr_count(element_ty1), non_ptr(element_ty1)),
_ => {
require!(
false,
"expected element type `{}` of second argument `{}` \
to be a pointer to the element type `{}` of the first \
argument `{}`, found `{}` != `*_ {}`",
arg_tys[1].simd_type(tcx),
to be a pointer to the element type `{}` of the first \
argument `{}`, found `{}` != `*_ {}`",
element_ty1,
arg_tys[1],
in_elem,
in_ty,
arg_tys[1].simd_type(tcx),
element_ty1,
in_elem
);
unreachable!();
}
};
assert!(pointer_count > 0);
assert_eq!(pointer_count - 1, ptr_count(arg_tys[0].simd_type(tcx)));
assert_eq!(underlying_ty, non_ptr(arg_tys[0].simd_type(tcx)));
assert_eq!(pointer_count - 1, ptr_count(element_ty0));
assert_eq!(underlying_ty, non_ptr(element_ty0));

// The element type of the third argument must be a signed integer type of any width:
match arg_tys[2].simd_type(tcx).kind() {
let (element_ty2, _) = simd_ty_and_len(bx, arg_tys[2]);
match element_ty2.kind() {
ty::Int(_) => (),
_ => {
require!(
false,
"expected element type `{}` of third argument `{}` \
to be a signed integer type",
arg_tys[2].simd_type(tcx),
element_ty2,
arg_tys[2]
);
}
Expand Down Expand Up @@ -1297,25 +1316,27 @@ fn generic_simd_intrinsic(
require_simd!(arg_tys[2], "third");

// Of the same length:
let (_, element_len1) = simd_ty_and_len(bx, arg_tys[1]);
let (_, element_len2) = simd_ty_and_len(bx, arg_tys[2]);
require!(
in_len == arg_tys[1].simd_size(tcx),
in_len == element_len1,
"expected {} argument with length {} (same as input type `{}`), \
found `{}` with length {}",
found `{}` with length {}",
"second",
in_len,
in_ty,
arg_tys[1],
arg_tys[1].simd_size(tcx)
element_len1
);
require!(
in_len == arg_tys[2].simd_size(tcx),
in_len == element_len2,
"expected {} argument with length {} (same as input type `{}`), \
found `{}` with length {}",
found `{}` with length {}",
"third",
in_len,
in_ty,
arg_tys[2],
arg_tys[2].simd_size(tcx)
element_len2
);

// This counts how many pointers
Expand All @@ -1336,39 +1357,42 @@ fn generic_simd_intrinsic(

// The second argument must be a simd vector with an element type that's a pointer
// to the element type of the first argument
let (pointer_count, underlying_ty) = match arg_tys[1].simd_type(tcx).kind() {
let (element_ty0, _element_len0) = simd_ty_and_len(bx, arg_tys[0]);
let (element_ty1, _element_len1) = simd_ty_and_len(bx, arg_tys[1]);
let (element_ty2, _element_len2) = simd_ty_and_len(bx, arg_tys[2]);
let (pointer_count, underlying_ty) = match element_ty1.kind() {
ty::RawPtr(p) if p.ty == in_elem && p.mutbl == hir::Mutability::Mut => {
(ptr_count(arg_tys[1].simd_type(tcx)), non_ptr(arg_tys[1].simd_type(tcx)))
(ptr_count(element_ty1), non_ptr(element_ty1))
}
_ => {
require!(
false,
"expected element type `{}` of second argument `{}` \
to be a pointer to the element type `{}` of the first \
argument `{}`, found `{}` != `*mut {}`",
arg_tys[1].simd_type(tcx),
to be a pointer to the element type `{}` of the first \
argument `{}`, found `{}` != `*mut {}`",
element_ty1,
arg_tys[1],
in_elem,
in_ty,
arg_tys[1].simd_type(tcx),
element_ty1,
in_elem
);
unreachable!();
}
};
assert!(pointer_count > 0);
assert_eq!(pointer_count - 1, ptr_count(arg_tys[0].simd_type(tcx)));
assert_eq!(underlying_ty, non_ptr(arg_tys[0].simd_type(tcx)));
assert_eq!(pointer_count - 1, ptr_count(element_ty0));
assert_eq!(underlying_ty, non_ptr(element_ty0));

// The element type of the third argument must be a signed integer type of any width:
match arg_tys[2].simd_type(tcx).kind() {
match element_ty2.kind() {
ty::Int(_) => (),
_ => {
require!(
false,
"expected element type `{}` of third argument `{}` \
to be a signed integer type",
arg_tys[2].simd_type(tcx),
be a signed integer type",
element_ty2,
arg_tys[2]
);
}
Expand Down Expand Up @@ -1565,7 +1589,7 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#,

if name == sym::simd_cast {
require_simd!(ret_ty, "return");
let out_len = ret_ty.simd_size(tcx);
let (out_elem, out_len) = simd_ty_and_len(bx, ret_ty);
require!(
in_len == out_len,
"expected return type with length {} (same as input type `{}`), \
Expand All @@ -1576,8 +1600,6 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#,
out_len
);
// casting cares about nominal type, not just structural type
let out_elem = ret_ty.simd_type(tcx);

if in_elem == out_elem {
return Ok(args[0].immediate());
}
Expand Down Expand Up @@ -1693,7 +1715,7 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#,
return_error!(
"expected element type `{}` of vector type `{}` \
to be a signed or unsigned integer type",
arg_tys[0].simd_type(tcx),
simd_ty_and_len(bx, arg_tys[0]).0,
arg_tys[0]
);
}
Expand Down

0 comments on commit 6e88e96

Please sign in to comment.