summaryrefslogtreecommitdiffstats
path: root/third_party/rust/packed_simd/src/api/minimal/ptr.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/packed_simd/src/api/minimal/ptr.rs')
-rw-r--r--third_party/rust/packed_simd/src/api/minimal/ptr.rs1373
1 files changed, 1373 insertions, 0 deletions
diff --git a/third_party/rust/packed_simd/src/api/minimal/ptr.rs b/third_party/rust/packed_simd/src/api/minimal/ptr.rs
new file mode 100644
index 0000000000..d9e47c9ccb
--- /dev/null
+++ b/third_party/rust/packed_simd/src/api/minimal/ptr.rs
@@ -0,0 +1,1373 @@
+//! Minimal API of pointer vectors.
+
+macro_rules! impl_minimal_p {
+ ([$elem_ty:ty; $elem_count:expr]: $id:ident, $mask_ty:ident,
+ $usize_ty:ident, $isize_ty:ident | $ref:ident | $test_tt:tt
+ | $($elem_name:ident),+ | ($true:expr, $false:expr) |
+ $(#[$doc:meta])*) => {
+
+ $(#[$doc])*
+ pub type $id<T> = Simd<[$elem_ty; $elem_count]>;
+
+ impl<T> sealed::Simd for $id<T> {
+ type Element = $elem_ty;
+ const LANES: usize = $elem_count;
+ type LanesType = [u32; $elem_count];
+ }
+
+ impl<T> $id<T> {
+ /// Creates a new instance with each vector elements initialized
+ /// with the provided values.
+ #[inline]
+ #[allow(clippy::too_many_arguments)]
+ pub const fn new($($elem_name: $elem_ty),*) -> Self {
+ Simd(codegen::$id($($elem_name),*))
+ }
+
+ /// Returns the number of vector lanes.
+ #[inline]
+ pub const fn lanes() -> usize {
+ $elem_count
+ }
+
+ /// Constructs a new instance with each element initialized to
+ /// `value`.
+ #[inline]
+ pub const fn splat(value: $elem_ty) -> Self {
+ Simd(codegen::$id($({
+ #[allow(non_camel_case_types, dead_code)]
+ struct $elem_name;
+ value
+ }),*))
+ }
+
+ /// Constructs a new instance with each element initialized to
+ /// `null`.
+ #[inline]
+ pub const fn null() -> Self {
+ Self::splat(crate::ptr::null_mut() as $elem_ty)
+ }
+
+ /// Returns a mask that selects those lanes that contain `null`
+ /// pointers.
+ #[inline]
+ pub fn is_null(self) -> $mask_ty {
+ self.eq(Self::null())
+ }
+
+ /// Extracts the value at `index`.
+ ///
+ /// # Panics
+ ///
+ /// If `index >= Self::lanes()`.
+ #[inline]
+ pub fn extract(self, index: usize) -> $elem_ty {
+ assert!(index < $elem_count);
+ unsafe { self.extract_unchecked(index) }
+ }
+
+ /// Extracts the value at `index`.
+ ///
+ /// # Safety
+ ///
+ /// If `index >= Self::lanes()` the behavior is undefined.
+ #[inline]
+ pub unsafe fn extract_unchecked(self, index: usize) -> $elem_ty {
+ use crate::llvm::simd_extract;
+ simd_extract(self.0, index as u32)
+ }
+
+ /// Returns a new vector where the value at `index` is replaced by
+ /// `new_value`.
+ ///
+ /// # Panics
+ ///
+ /// If `index >= Self::lanes()`.
+ #[inline]
+ #[must_use = "replace does not modify the original value - \
+ it returns a new vector with the value at `index` \
+ replaced by `new_value`d"
+ ]
+ #[allow(clippy::not_unsafe_ptr_arg_deref)]
+ pub fn replace(self, index: usize, new_value: $elem_ty) -> Self {
+ assert!(index < $elem_count);
+ unsafe { self.replace_unchecked(index, new_value) }
+ }
+
+ /// Returns a new vector where the value at `index` is replaced by `new_value`.
+ ///
+ /// # Safety
+ ///
+ /// If `index >= Self::lanes()` the behavior is undefined.
+ #[inline]
+ #[must_use = "replace_unchecked does not modify the original value - \
+ it returns a new vector with the value at `index` \
+ replaced by `new_value`d"
+ ]
+ pub unsafe fn replace_unchecked(
+ self,
+ index: usize,
+ new_value: $elem_ty,
+ ) -> Self {
+ use crate::llvm::simd_insert;
+ Simd(simd_insert(self.0, index as u32, new_value))
+ }
+ }
+
+
+ test_if!{
+ $test_tt:
+ paste::item! {
+ pub mod [<$id _minimal>] {
+ use super::*;
+ #[cfg_attr(not(target_arch = "wasm32"), test)]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn minimal() {
+ // lanes:
+ assert_eq!($elem_count, $id::<i32>::lanes());
+
+ // splat and extract / extract_unchecked:
+ let VAL7: <$id<i32> as sealed::Simd>::Element
+ = $ref!(7);
+ let VAL42: <$id<i32> as sealed::Simd>::Element
+ = $ref!(42);
+ let VEC: $id<i32> = $id::splat(VAL7);
+ for i in 0..$id::<i32>::lanes() {
+ assert_eq!(VAL7, VEC.extract(i));
+ assert_eq!(
+ VAL7, unsafe { VEC.extract_unchecked(i) }
+ );
+ }
+
+ // replace / replace_unchecked
+ let new_vec = VEC.replace(0, VAL42);
+ for i in 0..$id::<i32>::lanes() {
+ if i == 0 {
+ assert_eq!(VAL42, new_vec.extract(i));
+ } else {
+ assert_eq!(VAL7, new_vec.extract(i));
+ }
+ }
+ let new_vec = unsafe {
+ VEC.replace_unchecked(0, VAL42)
+ };
+ for i in 0..$id::<i32>::lanes() {
+ if i == 0 {
+ assert_eq!(VAL42, new_vec.extract(i));
+ } else {
+ assert_eq!(VAL7, new_vec.extract(i));
+ }
+ }
+
+ let mut n = $id::<i32>::null();
+ assert_eq!(
+ n,
+ $id::<i32>::splat(unsafe { crate::mem::zeroed() })
+ );
+ assert!(n.is_null().all());
+ n = n.replace(
+ 0, unsafe { crate::mem::transmute(1_isize) }
+ );
+ assert!(!n.is_null().all());
+ if $id::<i32>::lanes() > 1 {
+ assert!(n.is_null().any());
+ } else {
+ assert!(!n.is_null().any());
+ }
+ }
+
+ // FIXME: wasm-bindgen-test does not support #[should_panic]
+ // #[cfg_attr(not(target_arch = "wasm32"), test)]
+ // #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ #[cfg(not(target_arch = "wasm32"))]
+ #[test]
+ #[should_panic]
+ fn extract_panic_oob() {
+ let VAL: <$id<i32> as sealed::Simd>::Element
+ = $ref!(7);
+ let VEC: $id<i32> = $id::splat(VAL);
+ let _ = VEC.extract($id::<i32>::lanes());
+ }
+
+ // FIXME: wasm-bindgen-test does not support #[should_panic]
+ // #[cfg_attr(not(target_arch = "wasm32"), test)]
+ // #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ #[cfg(not(target_arch = "wasm32"))]
+ #[test]
+ #[should_panic]
+ fn replace_panic_oob() {
+ let VAL: <$id<i32> as sealed::Simd>::Element
+ = $ref!(7);
+ let VAL42: <$id<i32> as sealed::Simd>::Element
+ = $ref!(42);
+ let VEC: $id<i32> = $id::splat(VAL);
+ let _ = VEC.replace($id::<i32>::lanes(), VAL42);
+ }
+ }
+ }
+ }
+
+ impl<T> crate::fmt::Debug for $id<T> {
+ #[allow(clippy::missing_inline_in_public_items)]
+ fn fmt(&self, f: &mut crate::fmt::Formatter<'_>)
+ -> crate::fmt::Result {
+ write!(
+ f,
+ "{}<{}>(",
+ stringify!($id),
+ crate::intrinsics::type_name::<T>()
+ )?;
+ for i in 0..$elem_count {
+ if i > 0 {
+ write!(f, ", ")?;
+ }
+ self.extract(i).fmt(f)?;
+ }
+ write!(f, ")")
+ }
+ }
+
+ test_if!{
+ $test_tt:
+ paste::item! {
+ pub mod [<$id _fmt_debug>] {
+ use super::*;
+ #[cfg_attr(not(target_arch = "wasm32"), test)]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn debug() {
+ use arrayvec::{ArrayString,ArrayVec};
+ type TinyString = ArrayString<[u8; 512]>;
+
+ use crate::fmt::Write;
+ let v = $id::<i32>::default();
+ let mut s = TinyString::new();
+ write!(&mut s, "{:?}", v).unwrap();
+
+ let mut beg = TinyString::new();
+ write!(&mut beg, "{}<i32>(", stringify!($id)).unwrap();
+ assert!(
+ s.starts_with(beg.as_str()),
+ "s = {} (should start with = {})", s, beg
+ );
+ assert!(s.ends_with(")"));
+ let s: ArrayVec<[TinyString; 64]>
+ = s.replace(beg.as_str(), "")
+ .replace(")", "").split(",")
+ .map(|v| TinyString::from(v.trim()).unwrap())
+ .collect();
+ assert_eq!(s.len(), $id::<i32>::lanes());
+ for (index, ss) in s.into_iter().enumerate() {
+ let mut e = TinyString::new();
+ write!(&mut e, "{:?}", v.extract(index)).unwrap();
+ assert_eq!(ss, e);
+ }
+ }
+ }
+ }
+ }
+
+ impl<T> Default for $id<T> {
+ #[inline]
+ fn default() -> Self {
+ // FIXME: ptrs do not implement default
+ Self::null()
+ }
+ }
+
+ test_if!{
+ $test_tt:
+ paste::item! {
+ pub mod [<$id _default>] {
+ use super::*;
+ #[cfg_attr(not(target_arch = "wasm32"), test)]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn default() {
+ let a = $id::<i32>::default();
+ for i in 0..$id::<i32>::lanes() {
+ assert_eq!(
+ a.extract(i), unsafe { crate::mem::zeroed() }
+ );
+ }
+ }
+ }
+ }
+ }
+
+ impl<T> $id<T> {
+ /// Lane-wise equality comparison.
+ #[inline]
+ pub fn eq(self, other: Self) -> $mask_ty {
+ unsafe {
+ use crate::llvm::simd_eq;
+ let a: $usize_ty = crate::mem::transmute(self);
+ let b: $usize_ty = crate::mem::transmute(other);
+ Simd(simd_eq(a.0, b.0))
+ }
+ }
+
+ /// Lane-wise inequality comparison.
+ #[inline]
+ pub fn ne(self, other: Self) -> $mask_ty {
+ unsafe {
+ use crate::llvm::simd_ne;
+ let a: $usize_ty = crate::mem::transmute(self);
+ let b: $usize_ty = crate::mem::transmute(other);
+ Simd(simd_ne(a.0, b.0))
+ }
+ }
+
+ /// Lane-wise less-than comparison.
+ #[inline]
+ pub fn lt(self, other: Self) -> $mask_ty {
+ unsafe {
+ use crate::llvm::simd_lt;
+ let a: $usize_ty = crate::mem::transmute(self);
+ let b: $usize_ty = crate::mem::transmute(other);
+ Simd(simd_lt(a.0, b.0))
+ }
+ }
+
+ /// Lane-wise less-than-or-equals comparison.
+ #[inline]
+ pub fn le(self, other: Self) -> $mask_ty {
+ unsafe {
+ use crate::llvm::simd_le;
+ let a: $usize_ty = crate::mem::transmute(self);
+ let b: $usize_ty = crate::mem::transmute(other);
+ Simd(simd_le(a.0, b.0))
+ }
+ }
+
+ /// Lane-wise greater-than comparison.
+ #[inline]
+ pub fn gt(self, other: Self) -> $mask_ty {
+ unsafe {
+ use crate::llvm::simd_gt;
+ let a: $usize_ty = crate::mem::transmute(self);
+ let b: $usize_ty = crate::mem::transmute(other);
+ Simd(simd_gt(a.0, b.0))
+ }
+ }
+
+ /// Lane-wise greater-than-or-equals comparison.
+ #[inline]
+ pub fn ge(self, other: Self) -> $mask_ty {
+ unsafe {
+ use crate::llvm::simd_ge;
+ let a: $usize_ty = crate::mem::transmute(self);
+ let b: $usize_ty = crate::mem::transmute(other);
+ Simd(simd_ge(a.0, b.0))
+ }
+ }
+ }
+
+ test_if!{
+ $test_tt:
+ paste::item! {
+ pub mod [<$id _cmp_vertical>] {
+ use super::*;
+ #[cfg_attr(not(target_arch = "wasm32"), test)]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn cmp() {
+ let a = $id::<i32>::null();
+ let b = $id::<i32>::splat(unsafe {
+ crate::mem::transmute(1_isize)
+ });
+
+ let r = a.lt(b);
+ let e = $mask_ty::splat(true);
+ assert!(r == e);
+ let r = a.le(b);
+ assert!(r == e);
+
+ let e = $mask_ty::splat(false);
+ let r = a.gt(b);
+ assert!(r == e);
+ let r = a.ge(b);
+ assert!(r == e);
+ let r = a.eq(b);
+ assert!(r == e);
+
+ let mut a = a;
+ let mut b = b;
+ let mut e = e;
+ for i in 0..$id::<i32>::lanes() {
+ if i % 2 == 0 {
+ a = a.replace(
+ i,
+ unsafe { crate::mem::transmute(0_isize) }
+ );
+ b = b.replace(
+ i,
+ unsafe { crate::mem::transmute(1_isize) }
+ );
+ e = e.replace(i, true);
+ } else {
+ a = a.replace(
+ i,
+ unsafe { crate::mem::transmute(1_isize) }
+ );
+ b = b.replace(
+ i,
+ unsafe { crate::mem::transmute(0_isize) }
+ );
+ e = e.replace(i, false);
+ }
+ }
+ let r = a.lt(b);
+ assert!(r == e);
+ }
+ }
+ }
+ }
+
+ #[allow(clippy::partialeq_ne_impl)]
+ impl<T> crate::cmp::PartialEq<$id<T>> for $id<T> {
+ #[inline]
+ fn eq(&self, other: &Self) -> bool {
+ $id::<T>::eq(*self, *other).all()
+ }
+ #[inline]
+ fn ne(&self, other: &Self) -> bool {
+ $id::<T>::ne(*self, *other).any()
+ }
+ }
+
+ // FIXME: https://github.com/rust-lang-nursery/rust-clippy/issues/2892
+ #[allow(clippy::partialeq_ne_impl)]
+ impl<T> crate::cmp::PartialEq<LexicographicallyOrdered<$id<T>>>
+ for LexicographicallyOrdered<$id<T>>
+ {
+ #[inline]
+ fn eq(&self, other: &Self) -> bool {
+ self.0 == other.0
+ }
+ #[inline]
+ fn ne(&self, other: &Self) -> bool {
+ self.0 != other.0
+ }
+ }
+
+ test_if!{
+ $test_tt:
+ paste::item! {
+ pub mod [<$id _cmp_PartialEq>] {
+ use super::*;
+ #[cfg_attr(not(target_arch = "wasm32"), test)]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn partial_eq() {
+ let a = $id::<i32>::null();
+ let b = $id::<i32>::splat(unsafe {
+ crate::mem::transmute(1_isize)
+ });
+
+ assert!(a != b);
+ assert!(!(a == b));
+ assert!(a == a);
+ assert!(!(a != a));
+
+ if $id::<i32>::lanes() > 1 {
+ let a = $id::<i32>::null().replace(0, unsafe {
+ crate::mem::transmute(1_isize)
+ });
+ let b = $id::<i32>::splat(unsafe {
+ crate::mem::transmute(1_isize)
+ });
+
+ assert!(a != b);
+ assert!(!(a == b));
+ assert!(a == a);
+ assert!(!(a != a));
+ }
+ }
+ }
+ }
+ }
+
+ impl<T> crate::cmp::Eq for $id<T> {}
+ impl<T> crate::cmp::Eq for LexicographicallyOrdered<$id<T>> {}
+
+ test_if!{
+ $test_tt:
+ paste::item! {
+ pub mod [<$id _cmp_eq>] {
+ use super::*;
+ #[cfg_attr(not(target_arch = "wasm32"), test)]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn eq() {
+ fn foo<E: crate::cmp::Eq>(_: E) {}
+ let a = $id::<i32>::null();
+ foo(a);
+ }
+ }
+ }
+ }
+
+ impl<T> From<[$elem_ty; $elem_count]> for $id<T> {
+ #[inline]
+ fn from(array: [$elem_ty; $elem_count]) -> Self {
+ unsafe {
+ // FIXME: unnecessary zeroing; better than UB.
+ let mut u: Self = crate::mem::zeroed();
+ crate::ptr::copy_nonoverlapping(
+ &array as *const [$elem_ty; $elem_count] as *const u8,
+ &mut u as *mut Self as *mut u8,
+ crate::mem::size_of::<Self>()
+ );
+ u
+ }
+ }
+ }
+ impl<T> Into<[$elem_ty; $elem_count]> for $id<T> {
+ #[inline]
+ fn into(self) -> [$elem_ty; $elem_count] {
+ unsafe {
+ // FIXME: unnecessary zeroing; better than UB.
+ let mut u: [$elem_ty; $elem_count] = crate::mem::zeroed();
+ crate::ptr::copy_nonoverlapping(
+ &self as *const $id<T> as *const u8,
+ &mut u as *mut [$elem_ty; $elem_count] as *mut u8,
+ crate::mem::size_of::<Self>()
+ );
+ u
+ }
+ }
+ }
+
+ test_if!{
+ $test_tt:
+ paste::item! {
+ pub mod [<$id _from>] {
+ use super::*;
+ #[cfg_attr(not(target_arch = "wasm32"), test)]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn array() {
+ let values = [1_i32; $elem_count];
+
+ let mut vec: $id<i32> = Default::default();
+ let mut array = [
+ $id::<i32>::null().extract(0); $elem_count
+ ];
+
+ for i in 0..$elem_count {
+ let ptr = &values[i] as *const i32 as *mut i32;
+ vec = vec.replace(i, ptr);
+ array[i] = ptr;
+ }
+
+ // FIXME: there is no impl of From<$id<T>> for [$elem_ty; N]
+ // let a0 = From::from(vec);
+ // assert_eq!(a0, array);
+ #[allow(unused_assignments)]
+ let mut a1 = array;
+ a1 = vec.into();
+ assert_eq!(a1, array);
+
+ let v0: $id<i32> = From::from(array);
+ assert_eq!(v0, vec);
+ let v1: $id<i32> = array.into();
+ assert_eq!(v1, vec);
+ }
+ }
+ }
+ }
+
+ impl<T> $id<T> {
+ /// Instantiates a new vector with the values of the `slice`.
+ ///
+ /// # Panics
+ ///
+ /// If `slice.len() < Self::lanes()` or `&slice[0]` is not aligned
+ /// to an `align_of::<Self>()` boundary.
+ #[inline]
+ pub fn from_slice_aligned(slice: &[$elem_ty]) -> Self {
+ unsafe {
+ assert!(slice.len() >= $elem_count);
+ let target_ptr = slice.as_ptr();
+ assert!(
+ target_ptr.align_offset(crate::mem::align_of::<Self>())
+ == 0
+ );
+ Self::from_slice_aligned_unchecked(slice)
+ }
+ }
+
+ /// Instantiates a new vector with the values of the `slice`.
+ ///
+ /// # Panics
+ ///
+ /// If `slice.len() < Self::lanes()`.
+ #[inline]
+ pub fn from_slice_unaligned(slice: &[$elem_ty]) -> Self {
+ unsafe {
+ assert!(slice.len() >= $elem_count);
+ Self::from_slice_unaligned_unchecked(slice)
+ }
+ }
+
+ /// Instantiates a new vector with the values of the `slice`.
+ ///
+ /// # Safety
+ ///
+ /// If `slice.len() < Self::lanes()` or `&slice[0]` is not aligned
+ /// to an `align_of::<Self>()` boundary, the behavior is undefined.
+ #[inline]
+ pub unsafe fn from_slice_aligned_unchecked(slice: &[$elem_ty])
+ -> Self {
+ #[allow(clippy::cast_ptr_alignment)]
+ *(slice.as_ptr().cast())
+ }
+
+ /// Instantiates a new vector with the values of the `slice`.
+ ///
+ /// # Safety
+ ///
+ /// If `slice.len() < Self::lanes()` the behavior is undefined.
+ #[inline]
+ pub unsafe fn from_slice_unaligned_unchecked(
+ slice: &[$elem_ty],
+ ) -> Self {
+ use crate::mem::size_of;
+ let target_ptr = slice.as_ptr().cast();
+ let mut x = Self::splat(crate::ptr::null_mut() as $elem_ty);
+ let self_ptr = &mut x as *mut Self as *mut u8;
+ crate::ptr::copy_nonoverlapping(
+ target_ptr,
+ self_ptr,
+ size_of::<Self>(),
+ );
+ x
+ }
+ }
+
+ test_if!{
+ $test_tt:
+ paste::item! {
+ pub mod [<$id _slice_from_slice>] {
+ use super::*;
+ use crate::iter::Iterator;
+
+ #[cfg_attr(not(target_arch = "wasm32"), test)]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn from_slice_unaligned() {
+ let (null, non_null) = ptr_vals!($id<i32>);
+
+ let mut unaligned = [
+ non_null; $id::<i32>::lanes() + 1
+ ];
+ unaligned[0] = null;
+ let vec = $id::<i32>::from_slice_unaligned(
+ &unaligned[1..]
+ );
+ for (index, &b) in unaligned.iter().enumerate() {
+ if index == 0 {
+ assert_eq!(b, null);
+ } else {
+ assert_eq!(b, non_null);
+ assert_eq!(b, vec.extract(index - 1));
+ }
+ }
+ }
+
+ // FIXME: wasm-bindgen-test does not support #[should_panic]
+ // #[cfg_attr(not(target_arch = "wasm32"), test)]
+ // #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ #[cfg(not(target_arch = "wasm32"))]
+ #[test]
+ #[should_panic]
+ fn from_slice_unaligned_fail() {
+ let (_null, non_null) = ptr_vals!($id<i32>);
+ let unaligned = [non_null; $id::<i32>::lanes() + 1];
+ // the slice is not large enough => panic
+ let _vec = $id::<i32>::from_slice_unaligned(
+ &unaligned[2..]
+ );
+ }
+
+ union A {
+ data: [<$id<i32> as sealed::Simd>::Element;
+ 2 * $id::<i32>::lanes()],
+ _vec: $id<i32>,
+ }
+
+ #[cfg_attr(not(target_arch = "wasm32"), test)]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn from_slice_aligned() {
+ let (null, non_null) = ptr_vals!($id<i32>);
+ let mut aligned = A {
+ data: [null; 2 * $id::<i32>::lanes()],
+ };
+ for i in
+ $id::<i32>::lanes()..(2 * $id::<i32>::lanes()) {
+ unsafe {
+ aligned.data[i] = non_null;
+ }
+ }
+
+ let vec = unsafe {
+ $id::<i32>::from_slice_aligned(
+ &aligned.data[$id::<i32>::lanes()..]
+ )
+ };
+ for (index, &b) in unsafe {
+ aligned.data.iter().enumerate()
+ } {
+ if index < $id::<i32>::lanes() {
+ assert_eq!(b, null);
+ } else {
+ assert_eq!(b, non_null);
+ assert_eq!(
+ b, vec.extract(index - $id::<i32>::lanes())
+ );
+ }
+ }
+ }
+
+ // FIXME: wasm-bindgen-test does not support #[should_panic]
+ // #[cfg_attr(not(target_arch = "wasm32"), test)]
+ // #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ #[cfg(not(target_arch = "wasm32"))]
+ #[test]
+ #[should_panic]
+ fn from_slice_aligned_fail_lanes() {
+ let (_null, non_null) = ptr_vals!($id<i32>);
+ let aligned = A {
+ data: [non_null; 2 * $id::<i32>::lanes()],
+ };
+ // the slice is not large enough => panic
+ let _vec = unsafe {
+ $id::<i32>::from_slice_aligned(
+ &aligned.data[2 * $id::<i32>::lanes()..]
+ )
+ };
+ }
+
+ // FIXME: wasm-bindgen-test does not support #[should_panic]
+ // #[cfg_attr(not(target_arch = "wasm32"), test)]
+ // #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ #[cfg(not(target_arch = "wasm32"))]
+ #[test]
+ #[should_panic]
+ fn from_slice_aligned_fail_align() {
+ unsafe {
+ let (null, _non_null) = ptr_vals!($id<i32>);
+ let aligned = A {
+ data: [null; 2 * $id::<i32>::lanes()],
+ };
+
+ // get a pointer to the front of data
+ let ptr = aligned.data.as_ptr();
+ // offset pointer by one element
+ let ptr = ptr.wrapping_add(1);
+
+ if ptr.align_offset(
+ crate::mem::align_of::<$id<i32>>()
+ ) == 0 {
+ // the pointer is properly aligned, so
+ // from_slice_aligned won't fail here (e.g. this
+ // can happen for i128x1). So we panic to make
+ // the "should_fail" test pass:
+ panic!("ok");
+ }
+
+ // create a slice - this is safe, because the
+ // elements of the slice exist, are properly
+ // initialized, and properly aligned:
+ let s = slice::from_raw_parts(
+ ptr, $id::<i32>::lanes()
+ );
+ // this should always panic because the slice
+ // alignment does not match the alignment
+ // requirements for the vector type:
+ let _vec = $id::<i32>::from_slice_aligned(s);
+ }
+ }
+ }
+ }
+ }
+
+ impl<T> $id<T> {
+ /// Writes the values of the vector to the `slice`.
+ ///
+ /// # Panics
+ ///
+ /// If `slice.len() < Self::lanes()` or `&slice[0]` is not
+ /// aligned to an `align_of::<Self>()` boundary.
+ #[inline]
+ pub fn write_to_slice_aligned(self, slice: &mut [$elem_ty]) {
+ unsafe {
+ assert!(slice.len() >= $elem_count);
+ let target_ptr = slice.as_mut_ptr();
+ assert!(
+ target_ptr.align_offset(crate::mem::align_of::<Self>())
+ == 0
+ );
+ self.write_to_slice_aligned_unchecked(slice);
+ }
+ }
+
+ /// Writes the values of the vector to the `slice`.
+ ///
+ /// # Panics
+ ///
+ /// If `slice.len() < Self::lanes()`.
+ #[inline]
+ pub fn write_to_slice_unaligned(self, slice: &mut [$elem_ty]) {
+ unsafe {
+ assert!(slice.len() >= $elem_count);
+ self.write_to_slice_unaligned_unchecked(slice);
+ }
+ }
+
+ /// Writes the values of the vector to the `slice`.
+ ///
+ /// # Safety
+ ///
+ /// If `slice.len() < Self::lanes()` or `&slice[0]` is not
+ /// aligned to an `align_of::<Self>()` boundary, the behavior is
+ /// undefined.
+ #[inline]
+ pub unsafe fn write_to_slice_aligned_unchecked(
+ self, slice: &mut [$elem_ty],
+ ) {
+ #[allow(clippy::cast_ptr_alignment)]
+ *(slice.as_mut_ptr().cast()) = self;
+ }
+
+ /// Writes the values of the vector to the `slice`.
+ ///
+ /// # Safety
+ ///
+ /// If `slice.len() < Self::lanes()` the behavior is undefined.
+ #[inline]
+ pub unsafe fn write_to_slice_unaligned_unchecked(
+ self, slice: &mut [$elem_ty],
+ ) {
+ let target_ptr = slice.as_mut_ptr().cast();
+ let self_ptr = &self as *const Self as *const u8;
+ crate::ptr::copy_nonoverlapping(
+ self_ptr,
+ target_ptr,
+ crate::mem::size_of::<Self>(),
+ );
+ }
+ }
+
+ test_if!{
+ $test_tt:
+ paste::item! {
+ pub mod [<$id _slice_write_to_slice>] {
+ use super::*;
+ use crate::iter::Iterator;
+
+ #[cfg_attr(not(target_arch = "wasm32"), test)]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn write_to_slice_unaligned() {
+ let (null, non_null) = ptr_vals!($id<i32>);
+ let mut unaligned = [null; $id::<i32>::lanes() + 1];
+ let vec = $id::<i32>::splat(non_null);
+ vec.write_to_slice_unaligned(&mut unaligned[1..]);
+ for (index, &b) in unaligned.iter().enumerate() {
+ if index == 0 {
+ assert_eq!(b, null);
+ } else {
+ assert_eq!(b, non_null);
+ assert_eq!(b, vec.extract(index - 1));
+ }
+ }
+ }
+
+ // FIXME: wasm-bindgen-test does not support #[should_panic]
+ // #[cfg_attr(not(target_arch = "wasm32"), test)]
+ // #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ #[cfg(not(target_arch = "wasm32"))]
+ #[test]
+ #[should_panic]
+ fn write_to_slice_unaligned_fail() {
+ let (null, non_null) = ptr_vals!($id<i32>);
+ let mut unaligned = [null; $id::<i32>::lanes() + 1];
+ let vec = $id::<i32>::splat(non_null);
+ // the slice is not large enough => panic
+ vec.write_to_slice_unaligned(&mut unaligned[2..]);
+ }
+
+ union A {
+ data: [<$id<i32> as sealed::Simd>::Element;
+ 2 * $id::<i32>::lanes()],
+ _vec: $id<i32>,
+ }
+
+ #[cfg_attr(not(target_arch = "wasm32"), test)]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn write_to_slice_aligned() {
+ let (null, non_null) = ptr_vals!($id<i32>);
+ let mut aligned = A {
+ data: [null; 2 * $id::<i32>::lanes()],
+ };
+ let vec = $id::<i32>::splat(non_null);
+ unsafe {
+ vec.write_to_slice_aligned(
+ &mut aligned.data[$id::<i32>::lanes()..]
+ )
+ };
+ for (index, &b) in
+ unsafe { aligned.data.iter().enumerate() } {
+ if index < $id::<i32>::lanes() {
+ assert_eq!(b, null);
+ } else {
+ assert_eq!(b, non_null);
+ assert_eq!(
+ b, vec.extract(index - $id::<i32>::lanes())
+ );
+ }
+ }
+ }
+
+ // FIXME: wasm-bindgen-test does not support #[should_panic]
+ // #[cfg_attr(not(target_arch = "wasm32"), test)]
+ // #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ #[cfg(not(target_arch = "wasm32"))]
+ #[test]
+ #[should_panic]
+ fn write_to_slice_aligned_fail_lanes() {
+ let (null, non_null) = ptr_vals!($id<i32>);
+ let mut aligned = A {
+ data: [null; 2 * $id::<i32>::lanes()],
+ };
+ let vec = $id::<i32>::splat(non_null);
+ // the slice is not large enough => panic
+ unsafe {
+ vec.write_to_slice_aligned(
+ &mut aligned.data[2 * $id::<i32>::lanes()..]
+ )
+ };
+ }
+
+ // FIXME: wasm-bindgen-test does not support #[should_panic]
+ // #[cfg_attr(not(target_arch = "wasm32"), test)]
+ // #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ #[cfg(not(target_arch = "wasm32"))]
+ #[test]
+ #[should_panic]
+ fn write_to_slice_aligned_fail_align() {
+ let (null, non_null) = ptr_vals!($id<i32>);
+ unsafe {
+ let mut aligned = A {
+ data: [null; 2 * $id::<i32>::lanes()],
+ };
+
+ // get a pointer to the front of data
+ let ptr = aligned.data.as_mut_ptr();
+ // offset pointer by one element
+ let ptr = ptr.wrapping_add(1);
+
+ if ptr.align_offset(
+ crate::mem::align_of::<$id<i32>>()
+ ) == 0 {
+ // the pointer is properly aligned, so
+ // write_to_slice_aligned won't fail here (e.g.
+ // this can happen for i128x1). So we panic to
+ // make the "should_fail" test pass:
+ panic!("ok");
+ }
+
+ // create a slice - this is safe, because the
+ // elements of the slice exist, are properly
+ // initialized, and properly aligned:
+ let s = slice::from_raw_parts_mut(
+ ptr, $id::<i32>::lanes()
+ );
+ // this should always panic because the slice
+ // alignment does not match the alignment
+ // requirements for the vector type:
+ let vec = $id::<i32>::splat(non_null);
+ vec.write_to_slice_aligned(s);
+ }
+ }
+ }
+ }
+ }
+
+ impl<T> crate::hash::Hash for $id<T> {
+ #[inline]
+ fn hash<H: crate::hash::Hasher>(&self, state: &mut H) {
+ let s: $usize_ty = unsafe { crate::mem::transmute(*self) };
+ s.hash(state)
+ }
+ }
+
+ test_if! {
+ $test_tt:
+ paste::item! {
+ pub mod [<$id _hash>] {
+ use super::*;
+ #[cfg_attr(not(target_arch = "wasm32"), test)]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn hash() {
+ use crate::hash::{Hash, Hasher};
+ #[allow(deprecated)]
+ use crate::hash::{SipHasher13};
+
+ let values = [1_i32; $elem_count];
+
+ let mut vec: $id<i32> = Default::default();
+ let mut array = [
+ $id::<i32>::null().extract(0);
+ $elem_count
+ ];
+
+ for i in 0..$elem_count {
+ let ptr = &values[i] as *const i32 as *mut i32;
+ vec = vec.replace(i, ptr);
+ array[i] = ptr;
+ }
+
+ #[allow(deprecated)]
+ let mut a_hash = SipHasher13::new();
+ let mut v_hash = a_hash.clone();
+ array.hash(&mut a_hash);
+ vec.hash(&mut v_hash);
+ assert_eq!(a_hash.finish(), v_hash.finish());
+ }
+ }
+ }
+ }
+
+ impl<T> $id<T> {
+ /// Calculates the offset from a pointer.
+ ///
+ /// `count` is in units of `T`; e.g. a count of `3` represents a
+ /// pointer offset of `3 * size_of::<T>()` bytes.
+ ///
+ /// # Safety
+ ///
+ /// If any of the following conditions are violated, the result is
+ /// Undefined Behavior:
+ ///
+ /// * Both the starting and resulting pointer must be either in
+ /// bounds or one byte past the end of an allocated object.
+ ///
+ /// * The computed offset, in bytes, cannot overflow an `isize`.
+ ///
+ /// * The offset being in bounds cannot rely on "wrapping around"
+ /// the address space. That is, the infinite-precision sum, in bytes
+ /// must fit in a `usize`.
+ ///
+ /// The compiler and standard library generally tries to ensure
+ /// allocations never reach a size where an offset is a concern. For
+ /// instance, `Vec` and `Box` ensure they never allocate more than
+ /// `isize::MAX` bytes, so `vec.as_ptr().offset(vec.len() as isize)`
+ /// is always safe.
+ ///
+ /// Most platforms fundamentally can't even construct such an
+ /// allocation. For instance, no known 64-bit platform can ever
+ /// serve a request for 263 bytes due to page-table limitations or
+ /// splitting the address space. However, some 32-bit and 16-bit
+ /// platforms may successfully serve a request for more than
+ /// `isize::MAX` bytes with things like Physical Address Extension.
+ /// As such, memory acquired directly from allocators or memory
+ /// mapped files may be too large to handle with this function.
+ ///
+ /// Consider using `wrapping_offset` instead if these constraints
+ /// are difficult to satisfy. The only advantage of this method is
+ /// that it enables more aggressive compiler optimizations.
+ #[inline]
+ pub unsafe fn offset(self, count: $isize_ty) -> Self {
+ // FIXME: should use LLVM's `add nsw nuw`
+ self.wrapping_offset(count)
+ }
+
+ /// Calculates the offset from a pointer using wrapping arithmetic.
+ ///
+ /// `count` is in units of `T`; e.g. a count of `3` represents a
+ /// pointer offset of `3 * size_of::<T>()` bytes.
+ ///
+ /// # Safety
+ ///
+ /// The resulting pointer does not need to be in bounds, but it is
+ /// potentially hazardous to dereference (which requires unsafe).
+ ///
+ /// Always use `.offset(count)` instead when possible, because
+ /// offset allows the compiler to optimize better.
+ #[inline]
+ pub fn wrapping_offset(self, count: $isize_ty) -> Self {
+ unsafe {
+ let x: $isize_ty = crate::mem::transmute(self);
+ // note: {+,*} currently performs a `wrapping_{add, mul}`
+ crate::mem::transmute(
+ x + (count * crate::mem::size_of::<T>() as isize)
+ )
+ }
+ }
+
+ /// Calculates the distance between two pointers.
+ ///
+ /// The returned value is in units of `T`: the distance in bytes is
+ /// divided by `mem::size_of::<T>()`.
+ ///
+ /// This function is the inverse of offset.
+ ///
+ /// # Safety
+ ///
+ /// If any of the following conditions are violated, the result is
+ /// Undefined Behavior:
+ ///
+ /// * Both the starting and other pointer must be either in bounds
+ /// or one byte past the end of the same allocated object.
+ ///
+ /// * The distance between the pointers, in bytes, cannot overflow
+ /// an `isize`.
+ ///
+ /// * The distance between the pointers, in bytes, must be an exact
+ /// multiple of the size of `T`.
+ ///
+ /// * The distance being in bounds cannot rely on "wrapping around"
+ /// the address space.
+ ///
+ /// The compiler and standard library generally try to ensure
+ /// allocations never reach a size where an offset is a concern. For
+ /// instance, `Vec` and `Box` ensure they never allocate more than
+ /// `isize::MAX` bytes, so `ptr_into_vec.offset_from(vec.as_ptr())`
+ /// is always safe.
+ ///
+ /// Most platforms fundamentally can't even construct such an
+ /// allocation. For instance, no known 64-bit platform can ever
+ /// serve a request for 263 bytes due to page-table limitations or
+ /// splitting the address space. However, some 32-bit and 16-bit
+ /// platforms may successfully serve a request for more than
+ /// `isize::MAX` bytes with things like Physical Address Extension.
+ /// As such, memory acquired directly from allocators or memory
+ /// mapped files may be too large to handle with this function.
+ ///
+ /// Consider using `wrapping_offset_from` instead if these constraints
+ /// are difficult to satisfy. The only advantage of this method is
+ /// that it enables more aggressive compiler optimizations.
+ #[inline]
+ pub unsafe fn offset_from(self, origin: Self) -> $isize_ty {
+ // FIXME: should use LLVM's `sub nsw nuw`.
+ self.wrapping_offset_from(origin)
+ }
+
+ /// Calculates the distance between two pointers.
+ ///
+ /// The returned value is in units of `T`: the distance in bytes is
+ /// divided by `mem::size_of::<T>()`.
+ ///
+ /// If the address different between the two pointers is not a
+ /// multiple of `mem::size_of::<T>()` then the result of the
+ /// division is rounded towards zero.
+ ///
+ /// Though this method is safe for any two pointers, note that its
+ /// result will be mostly useless if the two pointers aren't into
+ /// the same allocated object, for example if they point to two
+ /// different local variables.
+ #[inline]
+ pub fn wrapping_offset_from(self, origin: Self) -> $isize_ty {
+ let x: $isize_ty = unsafe { crate::mem::transmute(self) };
+ let y: $isize_ty = unsafe { crate::mem::transmute(origin) };
+ // note: {-,/} currently perform wrapping_{sub, div}
+ (y - x) / (crate::mem::size_of::<T>() as isize)
+ }
+
+ /// Calculates the offset from a pointer (convenience for
+ /// `.offset(count as isize)`).
+ ///
+ /// `count` is in units of `T`; e.g. a count of 3 represents a
+ /// pointer offset of `3 * size_of::<T>()` bytes.
+ ///
+ /// # Safety
+ ///
+ /// If any of the following conditions are violated, the result is
+ /// Undefined Behavior:
+ ///
+ /// * Both the starting and resulting pointer must be either in
+ /// bounds or one byte past the end of an allocated object.
+ ///
+ /// * The computed offset, in bytes, cannot overflow an `isize`.
+ ///
+ /// * The offset being in bounds cannot rely on "wrapping around"
+ /// the address space. That is, the infinite-precision sum must fit
+ /// in a `usize`.
+ ///
+ /// The compiler and standard library generally tries to ensure
+ /// allocations never reach a size where an offset is a concern. For
+ /// instance, `Vec` and `Box` ensure they never allocate more than
+ /// `isize::MAX` bytes, so `vec.as_ptr().add(vec.len())` is always
+ /// safe.
+ ///
+ /// Most platforms fundamentally can't even construct such an
+ /// allocation. For instance, no known 64-bit platform can ever
+ /// serve a request for 263 bytes due to page-table limitations or
+ /// splitting the address space. However, some 32-bit and 16-bit
+ /// platforms may successfully serve a request for more than
+ /// `isize::MAX` bytes with things like Physical Address Extension.
+ /// As such, memory acquired directly from allocators or memory
+ /// mapped files may be too large to handle with this function.
+ ///
+ /// Consider using `wrapping_offset` instead if these constraints
+ /// are difficult to satisfy. The only advantage of this method is
+ /// that it enables more aggressive compiler optimizations.
+ #[inline]
+ #[allow(clippy::should_implement_trait)]
+ pub unsafe fn add(self, count: $usize_ty) -> Self {
+ self.offset(count.cast())
+ }
+
+ /// Calculates the offset from a pointer (convenience for
+ /// `.offset((count as isize).wrapping_neg())`).
+ ///
+ /// `count` is in units of T; e.g. a `count` of 3 represents a
+ /// pointer offset of `3 * size_of::<T>()` bytes.
+ ///
+ /// # Safety
+ ///
+ /// If any of the following conditions are violated, the result is
+ /// Undefined Behavior:
+ ///
+ /// * Both the starting and resulting pointer must be either in
+ /// bounds or one byte past the end of an allocated object.
+ ///
+ /// * The computed offset cannot exceed `isize::MAX` **bytes**.
+ ///
+ /// * The offset being in bounds cannot rely on "wrapping around"
+ /// the address space. That is, the infinite-precision sum must fit
+ /// in a usize.
+ ///
+ /// The compiler and standard library generally tries to ensure
+ /// allocations never reach a size where an offset is a concern. For
+ /// instance, `Vec` and `Box` ensure they never allocate more than
+ /// `isize::MAX` bytes, so
+ /// `vec.as_ptr().add(vec.len()).sub(vec.len())` is always safe.
+ ///
+ /// Most platforms fundamentally can't even construct such an
+ /// allocation. For instance, no known 64-bit platform can ever
+ /// serve a request for 2<sup>63</sup> bytes due to page-table
+ /// limitations or splitting the address space. However, some 32-bit
+ /// and 16-bit platforms may successfully serve a request for more
+ /// than `isize::MAX` bytes with things like Physical Address
+ /// Extension. As such, memory acquired directly from allocators or
+ /// memory mapped files *may* be too large to handle with this
+ /// function.
+ ///
+ /// Consider using `wrapping_offset` instead if these constraints
+ /// are difficult to satisfy. The only advantage of this method is
+ /// that it enables more aggressive compiler optimizations.
+ #[inline]
+ #[allow(clippy::should_implement_trait)]
+ pub unsafe fn sub(self, count: $usize_ty) -> Self {
+ let x: $isize_ty = count.cast();
+ // note: - is currently wrapping_neg
+ self.offset(-x)
+ }
+
+ /// Calculates the offset from a pointer using wrapping arithmetic.
+ /// (convenience for `.wrapping_offset(count as isize)`)
+ ///
+ /// `count` is in units of T; e.g. a `count` of 3 represents a
+ /// pointer offset of `3 * size_of::<T>()` bytes.
+ ///
+ /// # Safety
+ ///
+ /// The resulting pointer does not need to be in bounds, but it is
+ /// potentially hazardous to dereference (which requires `unsafe`).
+ ///
+ /// Always use `.add(count)` instead when possible, because `add`
+ /// allows the compiler to optimize better.
+ #[inline]
+ pub fn wrapping_add(self, count: $usize_ty) -> Self {
+ self.wrapping_offset(count.cast())
+ }
+
+ /// Calculates the offset from a pointer using wrapping arithmetic.
+ /// (convenience for `.wrapping_offset((count as
+ /// isize).wrapping_sub())`)
+ ///
+ /// `count` is in units of T; e.g. a `count` of 3 represents a
+ /// pointer offset of `3 * size_of::<T>()` bytes.
+ ///
+ /// # Safety
+ ///
+ /// The resulting pointer does not need to be in bounds, but it is
+ /// potentially hazardous to dereference (which requires `unsafe`).
+ ///
+ /// Always use `.sub(count)` instead when possible, because `sub`
+ /// allows the compiler to optimize better.
+ #[inline]
+ pub fn wrapping_sub(self, count: $usize_ty) -> Self {
+ let x: $isize_ty = count.cast();
+ self.wrapping_offset(-1 * x)
+ }
+ }
+
+ impl<T> $id<T> {
+ /// Shuffle vector elements according to `indices`.
+ #[inline]
+ pub fn shuffle1_dyn<I>(self, indices: I) -> Self
+ where
+ Self: codegen::shuffle1_dyn::Shuffle1Dyn<Indices = I>,
+ {
+ codegen::shuffle1_dyn::Shuffle1Dyn::shuffle1_dyn(self, indices)
+ }
+ }
+
+ test_if! {
+ $test_tt:
+ paste::item! {
+ pub mod [<$id _shuffle1_dyn>] {
+ use super::*;
+ #[cfg_attr(not(target_arch = "wasm32"), test)]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+ fn shuffle1_dyn() {
+ let (null, non_null) = ptr_vals!($id<i32>);
+
+ // alternating = [non_null, null, non_null, null, ...]
+ let mut alternating = $id::<i32>::splat(null);
+ for i in 0..$id::<i32>::lanes() {
+ if i % 2 == 0 {
+ alternating = alternating.replace(i, non_null);
+ }
+ }
+
+ type Indices = <$id<i32>
+ as codegen::shuffle1_dyn::Shuffle1Dyn>::Indices;
+ // even = [0, 0, 2, 2, 4, 4, ..]
+ let even = {
+ let mut v = Indices::splat(0);
+ for i in 0..$id::<i32>::lanes() {
+ if i % 2 == 0 {
+ v = v.replace(i, (i as u8).into());
+ } else {
+ v = v.replace(i, (i as u8 - 1).into());
+ }
+ }
+ v
+ };
+ // odd = [1, 1, 3, 3, 5, 5, ...]
+ let odd = {
+ let mut v = Indices::splat(0);
+ for i in 0..$id::<i32>::lanes() {
+ if i % 2 != 0 {
+ v = v.replace(i, (i as u8).into());
+ } else {
+ v = v.replace(i, (i as u8 + 1).into());
+ }
+ }
+ v
+ };
+
+ assert_eq!(
+ alternating.shuffle1_dyn(even),
+ $id::<i32>::splat(non_null)
+ );
+ if $id::<i32>::lanes() > 1 {
+ assert_eq!(
+ alternating.shuffle1_dyn(odd),
+ $id::<i32>::splat(null)
+ );
+ }
+ }
+ }
+ }
+ }
+ };
+}