diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/packed_simd/src/api/minimal | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/packed_simd/src/api/minimal')
-rw-r--r-- | third_party/rust/packed_simd/src/api/minimal/iuf.rs | 169 | ||||
-rw-r--r-- | third_party/rust/packed_simd/src/api/minimal/mask.rs | 176 | ||||
-rw-r--r-- | third_party/rust/packed_simd/src/api/minimal/ptr.rs | 1377 |
3 files changed, 1722 insertions, 0 deletions
diff --git a/third_party/rust/packed_simd/src/api/minimal/iuf.rs b/third_party/rust/packed_simd/src/api/minimal/iuf.rs new file mode 100644 index 0000000000..a155ac178a --- /dev/null +++ b/third_party/rust/packed_simd/src/api/minimal/iuf.rs @@ -0,0 +1,169 @@ +//! Minimal API of signed integer, unsigned integer, and floating-point +//! vectors. + +macro_rules! impl_minimal_iuf { + ([$elem_ty:ident; $elem_count:expr]: $id:ident | $ielem_ty:ident | + $test_tt:tt | $($elem_name:ident),+ | $(#[$doc:meta])*) => { + + $(#[$doc])* + pub type $id = Simd<[$elem_ty; $elem_count]>; + + impl sealed::Simd for $id { + type Element = $elem_ty; + const LANES: usize = $elem_count; + type LanesType = [u32; $elem_count]; + } + + impl $id { + /// 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 as $ielem_ty),*)) + } + + /// 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 as $ielem_ty + }),*)) + } + + /// 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; + let e: $ielem_ty = simd_extract(self.0, index as u32); + e as $elem_ty + } + + /// 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" + ] + 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 as $ielem_ty)) + } + } + + test_if!{ + $test_tt: + paste::item! { + // Comparisons use integer casts within mantissa^1 range. + #[allow(clippy::float_cmp)] + 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::lanes()); + + // splat and extract / extract_unchecked: + const VAL: $elem_ty = 7 as $elem_ty; + const VEC: $id = $id::splat(VAL); + for i in 0..$id::lanes() { + assert_eq!(VAL, VEC.extract(i)); + assert_eq!( + VAL, unsafe { VEC.extract_unchecked(i) } + ); + } + + // replace / replace_unchecked + let new_vec = VEC.replace(0, 42 as $elem_ty); + for i in 0..$id::lanes() { + if i == 0 { + assert_eq!(42 as $elem_ty, new_vec.extract(i)); + } else { + assert_eq!(VAL, new_vec.extract(i)); + } + } + let new_vec = unsafe { + VEC.replace_unchecked(0, 42 as $elem_ty) + }; + for i in 0..$id::lanes() { + if i == 0 { + assert_eq!(42 as $elem_ty, new_vec.extract(i)); + } else { + assert_eq!(VAL, new_vec.extract(i)); + } + } + } + + // 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() { + const VAL: $elem_ty = 7 as $elem_ty; + const VEC: $id = $id::splat(VAL); + let _ = VEC.extract($id::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() { + const VAL: $elem_ty = 7 as $elem_ty; + const VEC: $id = $id::splat(VAL); + let _ = VEC.replace($id::lanes(), 42 as $elem_ty); + } + } + } + } + } +} diff --git a/third_party/rust/packed_simd/src/api/minimal/mask.rs b/third_party/rust/packed_simd/src/api/minimal/mask.rs new file mode 100644 index 0000000000..a420060b42 --- /dev/null +++ b/third_party/rust/packed_simd/src/api/minimal/mask.rs @@ -0,0 +1,176 @@ +//! Minimal API of mask vectors. + +macro_rules! impl_minimal_mask { + ([$elem_ty:ident; $elem_count:expr]: $id:ident | $ielem_ty:ident + | $test_tt:tt | $($elem_name:ident),+ | $(#[$doc:meta])*) => { + $(#[$doc])* + pub type $id = Simd<[$elem_ty; $elem_count]>; + + impl sealed::Simd for $id { + type Element = $elem_ty; + const LANES: usize = $elem_count; + type LanesType = [u32; $elem_count]; + } + + impl $id { + /// 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: bool),*) -> Self { + Simd(codegen::$id($(Self::bool_to_internal($elem_name)),*)) + } + + /// Converts a boolean type into the type of the vector lanes. + #[inline] + #[allow(clippy::indexing_slicing)] + const fn bool_to_internal(x: bool) -> $ielem_ty { + [0 as $ielem_ty, !(0 as $ielem_ty)][x as usize] + } + + /// 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: bool) -> Self { + Simd(codegen::$id($({ + #[allow(non_camel_case_types, dead_code)] + struct $elem_name; + Self::bool_to_internal(value) + }),*)) + } + + /// Extracts the value at `index`. + /// + /// # Panics + /// + /// If `index >= Self::lanes()`. + #[inline] + pub fn extract(self, index: usize) -> bool { + 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) -> bool { + use crate::llvm::simd_extract; + let x: $ielem_ty = simd_extract(self.0, index as u32); + x != 0 + } + + /// 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" + ] + pub fn replace(self, index: usize, new_value: bool) -> 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: bool, + ) -> Self { + use crate::llvm::simd_insert; + Simd(simd_insert(self.0, index as u32, + Self::bool_to_internal(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() { + // TODO: test new + + // lanes: + assert_eq!($elem_count, $id::lanes()); + + // splat and extract / extract_unchecked: + let vec = $id::splat(true); + for i in 0..$id::lanes() { + assert_eq!(true, vec.extract(i)); + assert_eq!(true, + unsafe { vec.extract_unchecked(i) } + ); + } + + // replace / replace_unchecked + let new_vec = vec.replace(0, false); + for i in 0..$id::lanes() { + if i == 0 { + assert_eq!(false, new_vec.extract(i)); + } else { + assert_eq!(true, new_vec.extract(i)); + } + } + let new_vec = unsafe { + vec.replace_unchecked(0, false) + }; + for i in 0..$id::lanes() { + if i == 0 { + assert_eq!(false, new_vec.extract(i)); + } else { + assert_eq!(true, new_vec.extract(i)); + } + } + } + + // 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 vec = $id::splat(false); + let _ = vec.extract($id::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 vec = $id::splat(false); + let _ = vec.replace($id::lanes(), true); + } + } + } + } + } +} 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..c3d61fbf6d --- /dev/null +++ b/third_party/rust/packed_simd/src/api/minimal/ptr.rs @@ -0,0 +1,1377 @@ +//! 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.get_unchecked(0) as *const $elem_ty; + 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.get_unchecked(0) as *const $elem_ty as *const Self) + } + + /// 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.get_unchecked(0) as *const $elem_ty as *const u8; + 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.get_unchecked_mut(0) as *mut $elem_ty; + 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.get_unchecked_mut(0) as *mut $elem_ty as *mut Self) = + 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.get_unchecked_mut(0) as *mut $elem_ty as *mut u8; + 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) + ); + } + } + } + } + } + }; +} |