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.
This commit is contained in:
parent
9d78d1d027
commit
6e88e96ccf
15 changed files with 427 additions and 160 deletions
|
@ -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) => {
|
||||
|
@ -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.
|
||||
|
@ -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),
|
||||
|
@ -825,10 +840,11 @@ 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 `{}`), \
|
||||
|
@ -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(
|
||||
|
@ -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 {}",
|
||||
|
@ -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 `{}`",
|
||||
in_elem,
|
||||
in_ty,
|
||||
ret_ty,
|
||||
ret_ty.simd_type(tcx)
|
||||
out_ty
|
||||
);
|
||||
|
||||
let total_len = u128::from(in_len) * 2;
|
||||
|
@ -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 `{}`",
|
||||
|
@ -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 {}",
|
||||
"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 {}",
|
||||
"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
|
||||
|
@ -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),
|
||||
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]
|
||||
);
|
||||
}
|
||||
|
@ -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 {}",
|
||||
"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 {}",
|
||||
"third",
|
||||
in_len,
|
||||
in_ty,
|
||||
arg_tys[2],
|
||||
arg_tys[2].simd_size(tcx)
|
||||
element_len2
|
||||
);
|
||||
|
||||
// This counts how many pointers
|
||||
|
@ -1336,9 +1357,12 @@ 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!(
|
||||
|
@ -1346,29 +1370,29 @@ fn generic_simd_intrinsic(
|
|||
"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),
|
||||
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]
|
||||
);
|
||||
}
|
||||
|
@ -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 `{}`), \
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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]
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
};
|
||||
let size =
|
||||
element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow(ty))?;
|
||||
|
||||
(*e_ty, *len, true)
|
||||
} else {
|
||||
// First ADT field is not an array:
|
||||
(f0_ty, def.non_enum_variant().fields.len() as _, false)
|
||||
};
|
||||
|
||||
// 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 {
|
||||
|
|
|
@ -1959,22 +1959,6 @@ 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) => {
|
||||
|
|
|
@ -1065,12 +1065,14 @@ pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) {
|
|||
match e.kind() {
|
||||
ty::Param(_) => { /* struct<T>(T, T, T, T) is ok */ }
|
||||
_ if e.is_machine() => { /* struct(u8, u8, u8, u8) is ok */ }
|
||||
ty::Array(ty, _c) if ty.is_machine() => { /* struct([f32; 4]) */ }
|
||||
_ => {
|
||||
struct_span_err!(
|
||||
tcx.sess,
|
||||
sp,
|
||||
E0077,
|
||||
"SIMD vector element type should be machine type"
|
||||
"SIMD vector element type should be a \
|
||||
primitive scalar (integer/float/pointer) type"
|
||||
)
|
||||
.emit();
|
||||
return;
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
// compile-flags: -C no-prepopulate-passes
|
||||
|
||||
#![crate_type = "lib"]
|
||||
|
||||
#![feature(repr_simd, platform_intrinsics, const_generics)]
|
||||
#![allow(non_camel_case_types, incomplete_features)]
|
||||
|
||||
#[repr(simd)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct M(pub f32, pub f32, pub f32, pub f32);
|
||||
|
||||
#[repr(simd)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct S<const N: usize>([f32; N]);
|
||||
|
||||
extern "platform-intrinsic" {
|
||||
fn simd_extract<T, U>(x: T, idx: u32) -> U;
|
||||
fn simd_insert<T, U>(x: T, idx: u32, b: U) -> T;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @extract_m
|
||||
#[no_mangle]
|
||||
pub unsafe fn extract_m(v: M, i: u32) -> f32 {
|
||||
// CHECK: extractelement <4 x float> %0, i32 %i
|
||||
simd_extract(v, i)
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @extract_s
|
||||
#[no_mangle]
|
||||
pub unsafe fn extract_s(v: S<4>, i: u32) -> f32 {
|
||||
// CHECK: extractelement <4 x float> %0, i32 %i
|
||||
simd_extract(v, i)
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @insert_m
|
||||
#[no_mangle]
|
||||
pub unsafe fn insert_m(v: M, i: u32, j: f32) -> M {
|
||||
// CHECK: insertelement <4 x float> %1, float %j, i32 %i
|
||||
simd_insert(v, i, j)
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @insert_s
|
||||
#[no_mangle]
|
||||
pub unsafe fn insert_s(v: S<4>, i: u32, j: f32) -> S<4> {
|
||||
// CHECK: insertelement <4 x float> %1, float %j, i32 %i
|
||||
simd_insert(v, i, j)
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
// ignore-tidy-linelength
|
||||
// compile-flags: -C no-prepopulate-passes
|
||||
// min-llvm-version 8.0
|
||||
|
||||
#![crate_type = "lib"]
|
||||
|
||||
#![allow(non_camel_case_types, incomplete_features)]
|
||||
#![feature(repr_simd, platform_intrinsics, const_generics)]
|
||||
|
||||
#[repr(simd)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct S<const N: usize>([f32; N]);
|
||||
|
||||
#[repr(simd)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct T([f32; 4]);
|
||||
|
||||
#[repr(simd)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct U(f32, f32, f32, f32);
|
||||
|
||||
// CHECK-LABEL: @build_array_s
|
||||
#[no_mangle]
|
||||
pub fn build_array_s(x: [f32; 4]) -> S<4> {
|
||||
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* {{.*}} %{{[0-9]+}}, i8* {{.*}} %3, i64 16, i1 false)
|
||||
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* {{.*}} %{{[0-9]+}}, i8* {{.*}} %6, i64 16, i1 false)
|
||||
S::<4>(x)
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @build_array_t
|
||||
#[no_mangle]
|
||||
pub fn build_array_t(x: [f32; 4]) -> T {
|
||||
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* {{.*}} %{{[0-9]+}}, i8* {{.*}} %3, i64 16, i1 false)
|
||||
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* {{.*}} %{{[0-9]+}}, i8* {{.*}} %6, i64 16, i1 false)
|
||||
T(x)
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @build_array_u
|
||||
#[no_mangle]
|
||||
pub fn build_array_u(x: [f32; 4]) -> U {
|
||||
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* {{.*}} %{{[0-9]+}}, i8* {{.*}} %3, i64 16, i1 false)
|
||||
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* {{.*}} %{{[0-9]+}}, i8* {{.*}} %6, i64 16, i1 false)
|
||||
unsafe { std::mem::transmute(x) }
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
error[E0077]: SIMD vector element type should be machine type
|
||||
error[E0077]: SIMD vector element type should be a primitive scalar (integer/float/pointer) type
|
||||
--> $DIR/E0077.rs:4:1
|
||||
|
|
||||
LL | struct Bad(String);
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
#![feature(repr_simd, platform_intrinsics)]
|
||||
|
||||
// error-pattern:monomorphising SIMD type `Simd2<X>` with a non-machine element type `X`
|
||||
// ignore-tidy-linelength
|
||||
|
||||
// error-pattern:monomorphising SIMD type `Simd2<X>` with a non-primitive-scalar (integer/float/pointer) element type `X`
|
||||
|
||||
struct X(Vec<i32>);
|
||||
#[repr(simd)]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: monomorphising SIMD type `Simd2<X>` with a non-machine element type `X`
|
||||
error: monomorphising SIMD type `Simd2<X>` with a non-primitive-scalar (integer/float/pointer) element type `X`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
|
@ -1,10 +1,20 @@
|
|||
#![feature(repr_simd)]
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
// ignore-tidy-linelength
|
||||
|
||||
#[repr(simd)]
|
||||
struct empty; //~ ERROR SIMD vector cannot be empty
|
||||
|
||||
#[repr(simd)]
|
||||
struct i64f64(i64, f64); //~ ERROR SIMD vector should be homogeneous
|
||||
|
||||
struct Foo;
|
||||
|
||||
#[repr(simd)]
|
||||
struct FooV(Foo, Foo); //~ ERROR SIMD vector element type should be a primitive scalar (integer/float/pointer) type
|
||||
|
||||
#[repr(simd)]
|
||||
struct FooV2([Foo; 2]); //~ ERROR SIMD vector element type should be a primitive scalar (integer/float/pointer) type
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -1,16 +1,28 @@
|
|||
error[E0075]: SIMD vector cannot be empty
|
||||
--> $DIR/simd-type.rs:5:1
|
||||
--> $DIR/simd-type.rs:7:1
|
||||
|
|
||||
LL | struct empty;
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error[E0076]: SIMD vector should be homogeneous
|
||||
--> $DIR/simd-type.rs:8:1
|
||||
--> $DIR/simd-type.rs:10:1
|
||||
|
|
||||
LL | struct i64f64(i64, f64);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ SIMD elements must have the same type
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error[E0077]: SIMD vector element type should be a primitive scalar (integer/float/pointer) type
|
||||
--> $DIR/simd-type.rs:15:1
|
||||
|
|
||||
LL | struct FooV(Foo, Foo);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Some errors have detailed explanations: E0075, E0076.
|
||||
error[E0077]: SIMD vector element type should be a primitive scalar (integer/float/pointer) type
|
||||
--> $DIR/simd-type.rs:18:1
|
||||
|
|
||||
LL | struct FooV2([Foo; 2]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0075, E0076, E0077.
|
||||
For more information about an error, try `rustc --explain E0075`.
|
||||
|
|
44
src/test/ui/simd/simd-array-type.rs
Normal file
44
src/test/ui/simd/simd-array-type.rs
Normal file
|
@ -0,0 +1,44 @@
|
|||
// run-pass
|
||||
#![allow(dead_code, incomplete_features)]
|
||||
|
||||
// pretty-expanded FIXME #23616
|
||||
|
||||
#![feature(repr_simd)]
|
||||
#![feature(platform_intrinsics)]
|
||||
#![feature(const_generics)]
|
||||
|
||||
#[repr(simd)]
|
||||
#[derive(Copy, Clone)]
|
||||
struct S([i32; 4]);
|
||||
|
||||
#[repr(simd)]
|
||||
#[derive(Copy, Clone)]
|
||||
struct T<const N: usize>([i32; N]);
|
||||
|
||||
extern "platform-intrinsic" {
|
||||
fn simd_insert<T, E>(x: T, idx: u32, y: E) -> T;
|
||||
fn simd_extract<T, E>(x: T, idx: u32) -> E;
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let mut s = S([0; 4]);
|
||||
|
||||
unsafe {
|
||||
for i in 0_i32..4 {
|
||||
s = simd_insert(s, i as u32, i);
|
||||
}
|
||||
for i in 0_i32..4 {
|
||||
assert_eq!(i, simd_extract(s, i as u32));
|
||||
}
|
||||
}
|
||||
|
||||
let mut t = T::<4>([0; 4]);
|
||||
unsafe {
|
||||
for i in 0_i32..4 {
|
||||
t = simd_insert(t, i as u32, i);
|
||||
}
|
||||
for i in 0_i32..4 {
|
||||
assert_eq!(i, simd_extract(t, i as u32));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,6 @@
|
|||
// run-pass
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
|
||||
|
||||
#![feature(repr_simd, platform_intrinsics)]
|
||||
#![allow(non_camel_case_types, incomplete_features)]
|
||||
#![feature(repr_simd, platform_intrinsics, const_generics)]
|
||||
|
||||
use std::ops;
|
||||
|
||||
|
@ -11,6 +8,11 @@ use std::ops;
|
|||
#[derive(Copy, Clone)]
|
||||
struct f32x4(f32, f32, f32, f32);
|
||||
|
||||
#[repr(simd)]
|
||||
#[derive(Copy, Clone)]
|
||||
struct S<const N: usize>([f32; N]);
|
||||
|
||||
|
||||
extern "platform-intrinsic" {
|
||||
fn simd_add<T>(x: T, y: T) -> T;
|
||||
}
|
||||
|
@ -27,7 +29,16 @@ impl ops::Add for f32x4 {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
impl ops::Add for S<4> {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self {
|
||||
unsafe {simd_add(self, rhs)}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn main() { unsafe {
|
||||
let lr = f32x4(1.0f32, 2.0f32, 3.0f32, 4.0f32);
|
||||
|
||||
// lame-o
|
||||
|
@ -36,4 +47,11 @@ pub fn main() {
|
|||
assert_eq!(y, 4.0f32);
|
||||
assert_eq!(z, 6.0f32);
|
||||
assert_eq!(w, 8.0f32);
|
||||
}
|
||||
|
||||
let lr2 = S::<4>([1.0f32, 2.0f32, 3.0f32, 4.0f32]);
|
||||
let [x, y, z, w] = add(lr2, lr2).0;
|
||||
assert_eq!(x, 2.0f32);
|
||||
assert_eq!(y, 4.0f32);
|
||||
assert_eq!(z, 6.0f32);
|
||||
assert_eq!(w, 8.0f32);
|
||||
}}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
// run-pass
|
||||
// ignore-emscripten
|
||||
|
||||
#![allow(non_camel_case_types)]
|
||||
#![feature(repr_simd, platform_intrinsics)]
|
||||
#![allow(non_camel_case_types, incomplete_features)]
|
||||
#![feature(repr_simd, platform_intrinsics, const_generics)]
|
||||
|
||||
#[repr(simd)]
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
struct u32x4(pub u32, pub u32, pub u32, pub u32);
|
||||
|
||||
#[repr(simd)]
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
struct i32x4(pub i32, pub i32, pub i32, pub i32);
|
||||
#[derive(Copy, Clone)]
|
||||
struct I32<const N: usize>([i32; N]);
|
||||
|
||||
extern "platform-intrinsic" {
|
||||
fn simd_saturating_add<T>(x: T, y: T) -> T;
|
||||
|
@ -51,41 +51,41 @@ fn main() {
|
|||
const MIN: i32 = i32::MIN;
|
||||
const MAX: i32 = i32::MAX;
|
||||
|
||||
let a = i32x4(1, 2, 3, 4);
|
||||
let b = i32x4(2, 4, 6, 8);
|
||||
let c = i32x4(-1, -2, -3, -4);
|
||||
let d = i32x4(-2, -4, -6, -8);
|
||||
let a = I32::<4>([1, 2, 3, 4]);
|
||||
let b = I32::<4>([2, 4, 6, 8]);
|
||||
let c = I32::<4>([-1, -2, -3, -4]);
|
||||
let d = I32::<4>([-2, -4, -6, -8]);
|
||||
|
||||
let max = i32x4(MAX, MAX, MAX, MAX);
|
||||
let max1 = i32x4(MAX - 1, MAX - 1, MAX - 1, MAX - 1);
|
||||
let min = i32x4(MIN, MIN, MIN, MIN);
|
||||
let min1 = i32x4(MIN + 1, MIN + 1, MIN + 1, MIN + 1);
|
||||
let max = I32::<4>([MAX, MAX, MAX, MAX]);
|
||||
let max1 = I32::<4>([MAX - 1, MAX - 1, MAX - 1, MAX - 1]);
|
||||
let min = I32::<4>([MIN, MIN, MIN, MIN]);
|
||||
let min1 = I32::<4>([MIN + 1, MIN + 1, MIN + 1, MIN + 1]);
|
||||
|
||||
let z = i32x4(0, 0, 0, 0);
|
||||
let z = I32::<4>([0, 0, 0, 0]);
|
||||
|
||||
unsafe {
|
||||
assert_eq!(simd_saturating_add(z, z), z);
|
||||
assert_eq!(simd_saturating_add(z, a), a);
|
||||
assert_eq!(simd_saturating_add(b, z), b);
|
||||
assert_eq!(simd_saturating_add(a, a), b);
|
||||
assert_eq!(simd_saturating_add(a, max), max);
|
||||
assert_eq!(simd_saturating_add(max, b), max);
|
||||
assert_eq!(simd_saturating_add(max1, a), max);
|
||||
assert_eq!(simd_saturating_add(min1, z), min1);
|
||||
assert_eq!(simd_saturating_add(min, z), min);
|
||||
assert_eq!(simd_saturating_add(min1, c), min);
|
||||
assert_eq!(simd_saturating_add(min, c), min);
|
||||
assert_eq!(simd_saturating_add(min1, d), min);
|
||||
assert_eq!(simd_saturating_add(min, d), min);
|
||||
assert_eq!(simd_saturating_add(z, z).0, z.0);
|
||||
assert_eq!(simd_saturating_add(z, a).0, a.0);
|
||||
assert_eq!(simd_saturating_add(b, z).0, b.0);
|
||||
assert_eq!(simd_saturating_add(a, a).0, b.0);
|
||||
assert_eq!(simd_saturating_add(a, max).0, max.0);
|
||||
assert_eq!(simd_saturating_add(max, b).0, max.0);
|
||||
assert_eq!(simd_saturating_add(max1, a).0, max.0);
|
||||
assert_eq!(simd_saturating_add(min1, z).0, min1.0);
|
||||
assert_eq!(simd_saturating_add(min, z).0, min.0);
|
||||
assert_eq!(simd_saturating_add(min1, c).0, min.0);
|
||||
assert_eq!(simd_saturating_add(min, c).0, min.0);
|
||||
assert_eq!(simd_saturating_add(min1, d).0, min.0);
|
||||
assert_eq!(simd_saturating_add(min, d).0, min.0);
|
||||
|
||||
assert_eq!(simd_saturating_sub(b, z), b);
|
||||
assert_eq!(simd_saturating_sub(b, a), a);
|
||||
assert_eq!(simd_saturating_sub(a, a), z);
|
||||
assert_eq!(simd_saturating_sub(a, b), c);
|
||||
assert_eq!(simd_saturating_sub(z, max), min1);
|
||||
assert_eq!(simd_saturating_sub(min1, z), min1);
|
||||
assert_eq!(simd_saturating_sub(min1, a), min);
|
||||
assert_eq!(simd_saturating_sub(min1, b), min);
|
||||
assert_eq!(simd_saturating_sub(b, z).0, b.0);
|
||||
assert_eq!(simd_saturating_sub(b, a).0, a.0);
|
||||
assert_eq!(simd_saturating_sub(a, a).0, z.0);
|
||||
assert_eq!(simd_saturating_sub(a, b).0, c.0);
|
||||
assert_eq!(simd_saturating_sub(z, max).0, min1.0);
|
||||
assert_eq!(simd_saturating_sub(min1, z).0, min1.0);
|
||||
assert_eq!(simd_saturating_sub(min1, a).0, min.0);
|
||||
assert_eq!(simd_saturating_sub(min1, b).0, min.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// run-pass
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_camel_case_types, incomplete_features)]
|
||||
|
||||
// ignore-emscripten FIXME(#45351) hits an LLVM assert
|
||||
|
||||
#![feature(repr_simd, platform_intrinsics)]
|
||||
#![feature(repr_simd, platform_intrinsics, const_generics)]
|
||||
|
||||
#[repr(simd)]
|
||||
#[derive(Copy, Clone)]
|
||||
|
@ -11,7 +11,7 @@ struct i32x4(pub i32, pub i32, pub i32, pub i32);
|
|||
|
||||
#[repr(simd)]
|
||||
#[derive(Copy, Clone)]
|
||||
struct u32x4(pub u32, pub u32, pub u32, pub u32);
|
||||
struct U32<const N: usize>([u32; N]);
|
||||
|
||||
#[repr(simd)]
|
||||
#[derive(Copy, Clone)]
|
||||
|
@ -25,6 +25,15 @@ macro_rules! all_eq {
|
|||
}}
|
||||
}
|
||||
|
||||
macro_rules! all_eq_ {
|
||||
($a: expr, $b: expr) => {{
|
||||
let a = $a;
|
||||
let b = $b;
|
||||
assert!(a.0 == b.0);
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
extern "platform-intrinsic" {
|
||||
fn simd_add<T>(x: T, y: T) -> T;
|
||||
fn simd_sub<T>(x: T, y: T) -> T;
|
||||
|
@ -40,81 +49,81 @@ extern "platform-intrinsic" {
|
|||
|
||||
fn main() {
|
||||
let x1 = i32x4(1, 2, 3, 4);
|
||||
let y1 = u32x4(1, 2, 3, 4);
|
||||
let y1 = U32::<4>([1, 2, 3, 4]);
|
||||
let z1 = f32x4(1.0, 2.0, 3.0, 4.0);
|
||||
let x2 = i32x4(2, 3, 4, 5);
|
||||
let y2 = u32x4(2, 3, 4, 5);
|
||||
let y2 = U32::<4>([2, 3, 4, 5]);
|
||||
let z2 = f32x4(2.0, 3.0, 4.0, 5.0);
|
||||
|
||||
unsafe {
|
||||
all_eq!(simd_add(x1, x2), i32x4(3, 5, 7, 9));
|
||||
all_eq!(simd_add(x2, x1), i32x4(3, 5, 7, 9));
|
||||
all_eq!(simd_add(y1, y2), u32x4(3, 5, 7, 9));
|
||||
all_eq!(simd_add(y2, y1), u32x4(3, 5, 7, 9));
|
||||
all_eq_!(simd_add(y1, y2), U32::<4>([3, 5, 7, 9]));
|
||||
all_eq_!(simd_add(y2, y1), U32::<4>([3, 5, 7, 9]));
|
||||
all_eq!(simd_add(z1, z2), f32x4(3.0, 5.0, 7.0, 9.0));
|
||||
all_eq!(simd_add(z2, z1), f32x4(3.0, 5.0, 7.0, 9.0));
|
||||
|
||||
all_eq!(simd_mul(x1, x2), i32x4(2, 6, 12, 20));
|
||||
all_eq!(simd_mul(x2, x1), i32x4(2, 6, 12, 20));
|
||||
all_eq!(simd_mul(y1, y2), u32x4(2, 6, 12, 20));
|
||||
all_eq!(simd_mul(y2, y1), u32x4(2, 6, 12, 20));
|
||||
all_eq_!(simd_mul(y1, y2), U32::<4>([2, 6, 12, 20]));
|
||||
all_eq_!(simd_mul(y2, y1), U32::<4>([2, 6, 12, 20]));
|
||||
all_eq!(simd_mul(z1, z2), f32x4(2.0, 6.0, 12.0, 20.0));
|
||||
all_eq!(simd_mul(z2, z1), f32x4(2.0, 6.0, 12.0, 20.0));
|
||||
|
||||
all_eq!(simd_sub(x2, x1), i32x4(1, 1, 1, 1));
|
||||
all_eq!(simd_sub(x1, x2), i32x4(-1, -1, -1, -1));
|
||||
all_eq!(simd_sub(y2, y1), u32x4(1, 1, 1, 1));
|
||||
all_eq!(simd_sub(y1, y2), u32x4(!0, !0, !0, !0));
|
||||
all_eq_!(simd_sub(y2, y1), U32::<4>([1, 1, 1, 1]));
|
||||
all_eq_!(simd_sub(y1, y2), U32::<4>([!0, !0, !0, !0]));
|
||||
all_eq!(simd_sub(z2, z1), f32x4(1.0, 1.0, 1.0, 1.0));
|
||||
all_eq!(simd_sub(z1, z2), f32x4(-1.0, -1.0, -1.0, -1.0));
|
||||
|
||||
all_eq!(simd_div(x1, x1), i32x4(1, 1, 1, 1));
|
||||
all_eq!(simd_div(i32x4(2, 4, 6, 8), i32x4(2, 2, 2, 2)), x1);
|
||||
all_eq!(simd_div(y1, y1), u32x4(1, 1, 1, 1));
|
||||
all_eq!(simd_div(u32x4(2, 4, 6, 8), u32x4(2, 2, 2, 2)), y1);
|
||||
all_eq_!(simd_div(y1, y1), U32::<4>([1, 1, 1, 1]));
|
||||
all_eq_!(simd_div(U32::<4>([2, 4, 6, 8]), U32::<4>([2, 2, 2, 2])), y1);
|
||||
all_eq!(simd_div(z1, z1), f32x4(1.0, 1.0, 1.0, 1.0));
|
||||
all_eq!(simd_div(z1, z2), f32x4(1.0/2.0, 2.0/3.0, 3.0/4.0, 4.0/5.0));
|
||||
all_eq!(simd_div(z2, z1), f32x4(2.0/1.0, 3.0/2.0, 4.0/3.0, 5.0/4.0));
|
||||
|
||||
all_eq!(simd_rem(x1, x1), i32x4(0, 0, 0, 0));
|
||||
all_eq!(simd_rem(x2, x1), i32x4(0, 1, 1, 1));
|
||||
all_eq!(simd_rem(y1, y1), u32x4(0, 0, 0, 0));
|
||||
all_eq!(simd_rem(y2, y1), u32x4(0, 1, 1, 1));
|
||||
all_eq_!(simd_rem(y1, y1), U32::<4>([0, 0, 0, 0]));
|
||||
all_eq_!(simd_rem(y2, y1), U32::<4>([0, 1, 1, 1]));
|
||||
all_eq!(simd_rem(z1, z1), f32x4(0.0, 0.0, 0.0, 0.0));
|
||||
all_eq!(simd_rem(z1, z2), z1);
|
||||
all_eq!(simd_rem(z2, z1), f32x4(0.0, 1.0, 1.0, 1.0));
|
||||
|
||||
all_eq!(simd_shl(x1, x2), i32x4(1 << 2, 2 << 3, 3 << 4, 4 << 5));
|
||||
all_eq!(simd_shl(x2, x1), i32x4(2 << 1, 3 << 2, 4 << 3, 5 << 4));
|
||||
all_eq!(simd_shl(y1, y2), u32x4(1 << 2, 2 << 3, 3 << 4, 4 << 5));
|
||||
all_eq!(simd_shl(y2, y1), u32x4(2 << 1, 3 << 2, 4 << 3, 5 << 4));
|
||||
all_eq_!(simd_shl(y1, y2), U32::<4>([1 << 2, 2 << 3, 3 << 4, 4 << 5]));
|
||||
all_eq_!(simd_shl(y2, y1), U32::<4>([2 << 1, 3 << 2, 4 << 3, 5 << 4]));
|
||||
|
||||
// test right-shift by assuming left-shift is correct
|
||||
all_eq!(simd_shr(simd_shl(x1, x2), x2), x1);
|
||||
all_eq!(simd_shr(simd_shl(x2, x1), x1), x2);
|
||||
all_eq!(simd_shr(simd_shl(y1, y2), y2), y1);
|
||||
all_eq!(simd_shr(simd_shl(y2, y1), y1), y2);
|
||||
all_eq_!(simd_shr(simd_shl(y1, y2), y2), y1);
|
||||
all_eq_!(simd_shr(simd_shl(y2, y1), y1), y2);
|
||||
|
||||
// ensure we get logical vs. arithmetic shifts correct
|
||||
let (a, b, c, d) = (-12, -123, -1234, -12345);
|
||||
all_eq!(simd_shr(i32x4(a, b, c, d), x1), i32x4(a >> 1, b >> 2, c >> 3, d >> 4));
|
||||
all_eq!(simd_shr(u32x4(a as u32, b as u32, c as u32, d as u32), y1),
|
||||
u32x4((a as u32) >> 1, (b as u32) >> 2, (c as u32) >> 3, (d as u32) >> 4));
|
||||
all_eq_!(simd_shr(U32::<4>([a as u32, b as u32, c as u32, d as u32]), y1),
|
||||
U32::<4>([(a as u32) >> 1, (b as u32) >> 2, (c as u32) >> 3, (d as u32) >> 4]));
|
||||
|
||||
all_eq!(simd_and(x1, x2), i32x4(0, 2, 0, 4));
|
||||
all_eq!(simd_and(x2, x1), i32x4(0, 2, 0, 4));
|
||||
all_eq!(simd_and(y1, y2), u32x4(0, 2, 0, 4));
|
||||
all_eq!(simd_and(y2, y1), u32x4(0, 2, 0, 4));
|
||||
all_eq_!(simd_and(y1, y2), U32::<4>([0, 2, 0, 4]));
|
||||
all_eq_!(simd_and(y2, y1), U32::<4>([0, 2, 0, 4]));
|
||||
|
||||
all_eq!(simd_or(x1, x2), i32x4(3, 3, 7, 5));
|
||||
all_eq!(simd_or(x2, x1), i32x4(3, 3, 7, 5));
|
||||
all_eq!(simd_or(y1, y2), u32x4(3, 3, 7, 5));
|
||||
all_eq!(simd_or(y2, y1), u32x4(3, 3, 7, 5));
|
||||
all_eq_!(simd_or(y1, y2), U32::<4>([3, 3, 7, 5]));
|
||||
all_eq_!(simd_or(y2, y1), U32::<4>([3, 3, 7, 5]));
|
||||
|
||||
all_eq!(simd_xor(x1, x2), i32x4(3, 1, 7, 1));
|
||||
all_eq!(simd_xor(x2, x1), i32x4(3, 1, 7, 1));
|
||||
all_eq!(simd_xor(y1, y2), u32x4(3, 1, 7, 1));
|
||||
all_eq!(simd_xor(y2, y1), u32x4(3, 1, 7, 1));
|
||||
all_eq_!(simd_xor(y1, y2), U32::<4>([3, 1, 7, 1]));
|
||||
all_eq_!(simd_xor(y2, y1), U32::<4>([3, 1, 7, 1]));
|
||||
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue