diff --git a/crates/core_simd/src/fmt.rs b/crates/core_simd/src/fmt.rs index 78ae5ce3fcea..9ad3a6c100ea 100644 --- a/crates/core_simd/src/fmt.rs +++ b/crates/core_simd/src/fmt.rs @@ -1,88 +1,36 @@ -macro_rules! debug_wrapper { - { $($trait:ident => $name:ident,)* } => { +macro_rules! impl_fmt_trait { + { $($trait:ident,)* } => { $( - pub(crate) fn $name(slice: &[T], f: &mut core::fmt::Formatter) -> core::fmt::Result { - #[repr(transparent)] - struct Wrapper<'a, T: core::fmt::$trait>(&'a T); + impl core::fmt::$trait for crate::Simd + where + crate::LaneCount: crate::SupportedLaneCount, + Element: crate::SimdElement + core::fmt::$trait, + { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + #[repr(transparent)] + struct Wrapper<'a, T: core::fmt::$trait>(&'a T); - impl core::fmt::Debug for Wrapper<'_, T> { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - self.0.fmt(f) + impl core::fmt::Debug for Wrapper<'_, T> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + self.0.fmt(f) + } } - } - f.debug_list() - .entries(slice.iter().map(|x| Wrapper(x))) - .finish() + f.debug_list() + .entries(self.as_array().iter().map(|x| Wrapper(x))) + .finish() + } } )* } } -debug_wrapper! { - Debug => format, - Binary => format_binary, - LowerExp => format_lower_exp, - UpperExp => format_upper_exp, - Octal => format_octal, - LowerHex => format_lower_hex, - UpperHex => format_upper_hex, -} - -macro_rules! impl_fmt_trait { - { $($type:ident => $(($trait:ident, $format:ident)),*;)* } => { - $( // repeat type - $( // repeat trait - impl core::fmt::$trait for crate::$type - where - crate::LaneCount: crate::SupportedLaneCount, - { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - $format(self.as_ref(), f) - } - } - )* - )* - }; - { integers: $($type:ident,)* } => { - impl_fmt_trait! { - $($type => - (Debug, format), - (Binary, format_binary), - (LowerExp, format_lower_exp), - (UpperExp, format_upper_exp), - (Octal, format_octal), - (LowerHex, format_lower_hex), - (UpperHex, format_upper_hex); - )* - } - }; - { floats: $($type:ident,)* } => { - impl_fmt_trait! { - $($type => - (Debug, format), - (LowerExp, format_lower_exp), - (UpperExp, format_upper_exp); - )* - } - }; - { masks: $($type:ident,)* } => { - impl_fmt_trait! { - $($type => - (Debug, format); - )* - } - } -} - impl_fmt_trait! { - integers: - SimdU8, SimdU16, SimdU32, SimdU64, - SimdI8, SimdI16, SimdI32, SimdI64, - SimdUsize, SimdIsize, -} - -impl_fmt_trait! { - floats: - SimdF32, SimdF64, + Debug, + Binary, + LowerExp, + UpperExp, + Octal, + LowerHex, + UpperHex, } diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs index 5f88e3c63b5b..fc0df1813b94 100644 --- a/crates/core_simd/src/lib.rs +++ b/crates/core_simd/src/lib.rs @@ -2,6 +2,7 @@ #![allow(incomplete_features)] #![feature( const_evaluatable_checked, + const_fn_trait_bound, const_generics, platform_intrinsics, repr_simd, diff --git a/crates/core_simd/src/permute.rs b/crates/core_simd/src/permute.rs index 01148a26bad4..4e377d68915b 100644 --- a/crates/core_simd/src/permute.rs +++ b/crates/core_simd/src/permute.rs @@ -1,6 +1,9 @@ macro_rules! impl_shuffle_lane { - { $name:ident, $fn:ident, $n:literal } => { - impl $name<$n> { + { $fn:ident, $n:literal } => { + impl crate::Simd + where + Element: crate::SimdElement, + { /// A const SIMD shuffle that takes 2 SIMD vectors and produces another vector, using /// the indices in the const parameter. The first or "self" vector will have its lanes /// indexed from 0, and the second vector will have its first lane indexed at $n. @@ -138,12 +141,8 @@ macro_rules! impl_shuffle_lane { } } -macro_rules! impl_shuffle_2pow_lanes { - { $name:ident } => { - impl_shuffle_lane!{ $name, simd_shuffle2, 2 } - impl_shuffle_lane!{ $name, simd_shuffle4, 4 } - impl_shuffle_lane!{ $name, simd_shuffle8, 8 } - impl_shuffle_lane!{ $name, simd_shuffle16, 16 } - impl_shuffle_lane!{ $name, simd_shuffle32, 32 } - } -} +impl_shuffle_lane! { simd_shuffle2, 2 } +impl_shuffle_lane! { simd_shuffle4, 4 } +impl_shuffle_lane! { simd_shuffle8, 8 } +impl_shuffle_lane! { simd_shuffle16, 16 } +impl_shuffle_lane! { simd_shuffle32, 32 } diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index ea1a732f2035..2e3855bff3cf 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -1,6 +1,3 @@ -#[macro_use] -mod vector_impl; - mod float; mod int; mod uint; @@ -21,13 +18,325 @@ where Element: SimdElement, LaneCount: SupportedLaneCount; +impl Simd +where + LaneCount: SupportedLaneCount, + Element: SimdElement, +{ + /// Construct a SIMD vector by setting all lanes to the given value. + pub const fn splat(value: Element) -> Self { + Self([value; LANES]) + } + + /// Returns an array reference containing the entire SIMD vector. + pub const fn as_array(&self) -> &[Element; LANES] { + &self.0 + } + + /// Returns a mutable array reference containing the entire SIMD vector. + pub fn as_mut_array(&mut self) -> &mut [Element; LANES] { + &mut self.0 + } + + /// Converts an array to a SIMD vector. + pub const fn from_array(array: [Element; LANES]) -> Self { + Self(array) + } + + /// Converts a SIMD vector to an array. + pub const fn to_array(self) -> [Element; LANES] { + self.0 + } + + /// SIMD gather: construct a SIMD vector by reading from a slice, using potentially discontiguous indices. + /// If an index is out of bounds, that lane instead selects the value from the "or" vector. + /// ``` + /// # #![feature(portable_simd)] + /// # use core_simd::*; + /// let vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; + /// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 5]); + /// let alt = SimdI32::from_array([-5, -4, -3, -2]); + /// + /// let result = SimdI32::<4>::gather_or(&vec, idxs, alt); // Note the lane that is out-of-bounds. + /// assert_eq!(result, SimdI32::from_array([-5, 13, 10, 15])); + /// ``` + #[must_use] + #[inline] + pub fn gather_or(slice: &[Element], idxs: crate::SimdUsize, or: Self) -> Self { + Self::gather_select(slice, crate::MaskSize::splat(true), idxs, or) + } + + /// SIMD gather: construct a SIMD vector by reading from a slice, using potentially discontiguous indices. + /// Out-of-bounds indices instead use the default value for that lane (0). + /// ``` + /// # #![feature(portable_simd)] + /// # use core_simd::*; + /// let vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; + /// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 5]); + /// + /// let result = SimdI32::<4>::gather_or_default(&vec, idxs); // Note the lane that is out-of-bounds. + /// assert_eq!(result, SimdI32::from_array([0, 13, 10, 15])); + /// ``` + #[must_use] + #[inline] + pub fn gather_or_default(slice: &[Element], idxs: crate::SimdUsize) -> Self + where + Element: Default, + { + Self::gather_or(slice, idxs, Self::splat(Element::default())) + } + + /// SIMD gather: construct a SIMD vector by reading from a slice, using potentially discontiguous indices. + /// Out-of-bounds or masked indices instead select the value from the "or" vector. + /// ``` + /// # #![feature(portable_simd)] + /// # use core_simd::*; + /// let vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; + /// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 5]); + /// let alt = SimdI32::from_array([-5, -4, -3, -2]); + /// let mask = MaskSize::from_array([true, true, true, false]); // Note the mask of the last lane. + /// + /// let result = SimdI32::<4>::gather_select(&vec, mask, idxs, alt); // Note the lane that is out-of-bounds. + /// assert_eq!(result, SimdI32::from_array([-5, 13, 10, -2])); + /// ``` + #[must_use] + #[inline] + pub fn gather_select( + slice: &[Element], + mask: crate::MaskSize, + idxs: crate::SimdUsize, + or: Self, + ) -> Self { + let mask = (mask & idxs.lanes_lt(crate::SimdUsize::splat(slice.len()))).to_int(); + let base_ptr = crate::vector::ptr::SimdConstPtr::splat(slice.as_ptr()); + // Ferris forgive me, I have done pointer arithmetic here. + let ptrs = base_ptr.wrapping_add(idxs); + // SAFETY: The ptrs have been bounds-masked to prevent memory-unsafe reads insha'allah + unsafe { crate::intrinsics::simd_gather(or, ptrs, mask) } + } + + /// SIMD scatter: write a SIMD vector's values into a slice, using potentially discontiguous indices. + /// Out-of-bounds indices are not written. + /// `scatter` writes "in order", so if an index receives two writes, only the last is guaranteed. + /// ``` + /// # #![feature(portable_simd)] + /// # use core_simd::*; + /// let mut vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; + /// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 0]); + /// let vals = SimdI32::from_array([-27, 82, -41, 124]); + /// + /// vals.scatter(&mut vec, idxs); // index 0 receives two writes. + /// assert_eq!(vec, vec![124, 11, 12, 82, 14, 15, 16, 17, 18]); + /// ``` + #[inline] + pub fn scatter(self, slice: &mut [Element], idxs: crate::SimdUsize) { + self.scatter_select(slice, crate::MaskSize::splat(true), idxs) + } + + /// SIMD scatter: write a SIMD vector's values into a slice, using potentially discontiguous indices. + /// Out-of-bounds or masked indices are not written. + /// `scatter_select` writes "in order", so if an index receives two writes, only the last is guaranteed. + /// ``` + /// # #![feature(portable_simd)] + /// # use core_simd::*; + /// let mut vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; + /// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 0]); + /// let vals = SimdI32::from_array([-27, 82, -41, 124]); + /// let mask = MaskSize::from_array([true, true, true, false]); // Note the mask of the last lane. + /// + /// vals.scatter_select(&mut vec, mask, idxs); // index 0's second write is masked, thus omitted. + /// assert_eq!(vec, vec![-41, 11, 12, 82, 14, 15, 16, 17, 18]); + /// ``` + #[inline] + pub fn scatter_select( + self, + slice: &mut [Element], + mask: crate::MaskSize, + idxs: crate::SimdUsize, + ) { + // We must construct our scatter mask before we derive a pointer! + let mask = (mask & idxs.lanes_lt(crate::SimdUsize::splat(slice.len()))).to_int(); + // SAFETY: This block works with *mut T derived from &mut 'a [T], + // which means it is delicate in Rust's borrowing model, circa 2021: + // &mut 'a [T] asserts uniqueness, so deriving &'a [T] invalidates live *mut Ts! + // Even though this block is largely safe methods, it must be almost exactly this way + // to prevent invalidating the raw ptrs while they're live. + // Thus, entering this block requires all values to use being already ready: + // 0. idxs we want to write to, which are used to construct the mask. + // 1. mask, which depends on an initial &'a [T] and the idxs. + // 2. actual values to scatter (self). + // 3. &mut [T] which will become our base ptr. + unsafe { + // Now Entering ☢️ *mut T Zone + let base_ptr = crate::vector::ptr::SimdMutPtr::splat(slice.as_mut_ptr()); + // Ferris forgive me, I have done pointer arithmetic here. + let ptrs = base_ptr.wrapping_add(idxs); + // The ptrs have been bounds-masked to prevent memory-unsafe writes insha'allah + crate::intrinsics::simd_scatter(self, ptrs, mask) + // Cleared ☢️ *mut T Zone + } + } +} + +impl Copy for Simd +where + Element: SimdElement, + LaneCount: SupportedLaneCount, +{ +} + +impl Clone for Simd +where + Element: SimdElement, + LaneCount: SupportedLaneCount, +{ + fn clone(&self) -> Self { + *self + } +} + +impl Default for Simd +where + LaneCount: SupportedLaneCount, + Element: SimdElement + Default, +{ + #[inline] + fn default() -> Self { + Self::splat(Element::default()) + } +} + +impl PartialEq for Simd +where + LaneCount: SupportedLaneCount, + Element: SimdElement + PartialEq, +{ + #[inline] + fn eq(&self, other: &Self) -> bool { + // TODO use SIMD equality + self.to_array() == other.to_array() + } +} + +impl PartialOrd for Simd +where + LaneCount: SupportedLaneCount, + Element: SimdElement + PartialOrd, +{ + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + // TODO use SIMD equality + self.to_array().partial_cmp(other.as_ref()) + } +} + +impl Eq for Simd +where + LaneCount: SupportedLaneCount, + Element: SimdElement + Eq, +{ +} + +impl Ord for Simd +where + LaneCount: SupportedLaneCount, + Element: SimdElement + Ord, +{ + #[inline] + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + // TODO use SIMD equality + self.to_array().cmp(other.as_ref()) + } +} + +impl core::hash::Hash for Simd +where + LaneCount: SupportedLaneCount, + Element: SimdElement + core::hash::Hash, +{ + #[inline] + fn hash(&self, state: &mut H) + where + H: core::hash::Hasher, + { + self.as_array().hash(state) + } +} + +// array references +impl AsRef<[Element; LANES]> for Simd +where + LaneCount: SupportedLaneCount, + Element: SimdElement, +{ + #[inline] + fn as_ref(&self) -> &[Element; LANES] { + &self.0 + } +} + +impl AsMut<[Element; LANES]> for Simd +where + LaneCount: SupportedLaneCount, + Element: SimdElement, +{ + #[inline] + fn as_mut(&mut self) -> &mut [Element; LANES] { + &mut self.0 + } +} + +// slice references +impl AsRef<[Element]> for Simd +where + LaneCount: SupportedLaneCount, + Element: SimdElement, +{ + #[inline] + fn as_ref(&self) -> &[Element] { + &self.0 + } +} + +impl AsMut<[Element]> for Simd +where + LaneCount: SupportedLaneCount, + Element: SimdElement, +{ + #[inline] + fn as_mut(&mut self) -> &mut [Element] { + &mut self.0 + } +} + +// vector/array conversion +impl From<[Element; LANES]> for Simd +where + LaneCount: SupportedLaneCount, + Element: SimdElement, +{ + fn from(array: [Element; LANES]) -> Self { + Self(array) + } +} + +impl From> for [Element; LANES] +where + LaneCount: SupportedLaneCount, + Element: SimdElement, +{ + fn from(vector: Simd) -> Self { + vector.to_array() + } +} + mod sealed { pub trait Sealed {} } use sealed::Sealed; /// Marker trait for types that may be used as SIMD vector elements. -pub unsafe trait SimdElement: Sealed { +pub unsafe trait SimdElement: Sealed + Copy { /// The mask element type corresponding to this element type. type Mask: SimdElement; } @@ -106,3 +415,24 @@ pub trait Vector: sealed::Sealed { #[must_use] fn splat(val: Self::Scalar) -> Self; } + +impl Sealed for Simd +where + LaneCount: SupportedLaneCount, + Element: SimdElement, +{ +} + +impl Vector for Simd +where + LaneCount: SupportedLaneCount, + Element: SimdElement, +{ + type Scalar = Element; + const LANES: usize = LANES; + + #[inline] + fn splat(val: Self::Scalar) -> Self { + Self::splat(val) + } +} diff --git a/crates/core_simd/src/vector/float.rs b/crates/core_simd/src/vector/float.rs index 231fe590ada5..40959d66872c 100644 --- a/crates/core_simd/src/vector/float.rs +++ b/crates/core_simd/src/vector/float.rs @@ -7,7 +7,6 @@ use crate::{LaneCount, SupportedLaneCount}; /// representation. Called from `define_float_vector!`. macro_rules! impl_float_vector { { $name:ident, $type:ident, $bits_ty:ident, $mask_ty:ident, $mask_impl_ty:ident } => { - impl_vector! { $name, $type } impl_float_reductions! { $name, $type } impl $name diff --git a/crates/core_simd/src/vector/int.rs b/crates/core_simd/src/vector/int.rs index 88a17daa2f4b..74c4a0f2fb63 100644 --- a/crates/core_simd/src/vector/int.rs +++ b/crates/core_simd/src/vector/int.rs @@ -5,32 +5,8 @@ use crate::{LaneCount, SupportedLaneCount}; /// Implements additional integer traits (Eq, Ord, Hash) on the specified vector `$name`, holding multiple `$lanes` of `$type`. macro_rules! impl_integer_vector { { $name:ident, $type:ty, $mask_ty:ident, $mask_impl_ty:ident } => { - impl_vector! { $name, $type } impl_integer_reductions! { $name, $type } - impl Eq for $name where LaneCount: SupportedLaneCount {} - - impl Ord for $name where LaneCount: SupportedLaneCount { - #[inline] - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - // TODO use SIMD cmp - self.as_array().cmp(other.as_ref()) - } - } - - impl core::hash::Hash for $name - where - LaneCount: SupportedLaneCount, - { - #[inline] - fn hash(&self, state: &mut H) - where - H: core::hash::Hasher - { - self.as_array().hash(state) - } - } - impl $name where LaneCount: SupportedLaneCount, diff --git a/crates/core_simd/src/vector/uint.rs b/crates/core_simd/src/vector/uint.rs index 5bd1a7fd67fd..6dfcbe795939 100644 --- a/crates/core_simd/src/vector/uint.rs +++ b/crates/core_simd/src/vector/uint.rs @@ -1,35 +1,9 @@ #![allow(non_camel_case_types)] -use crate::{LaneCount, SupportedLaneCount}; - /// Implements additional integer traits (Eq, Ord, Hash) on the specified vector `$name`, holding multiple `$lanes` of `$type`. macro_rules! impl_unsigned_vector { { $name:ident, $type:ty } => { - impl_vector! { $name, $type } impl_integer_reductions! { $name, $type } - - impl Eq for $name where LaneCount: SupportedLaneCount {} - - impl Ord for $name where LaneCount: SupportedLaneCount { - #[inline] - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - // TODO use SIMD cmp - self.as_array().cmp(other.as_ref()) - } - } - - impl core::hash::Hash for $name - where - LaneCount: SupportedLaneCount, - { - #[inline] - fn hash(&self, state: &mut H) - where - H: core::hash::Hasher - { - self.as_array().hash(state) - } - } } } diff --git a/crates/core_simd/src/vector/vector_impl.rs b/crates/core_simd/src/vector/vector_impl.rs deleted file mode 100644 index 58ea244adfcb..000000000000 --- a/crates/core_simd/src/vector/vector_impl.rs +++ /dev/null @@ -1,257 +0,0 @@ -/// Implements common traits on the specified vector `$name`, holding multiple `$lanes` of `$type`. -macro_rules! impl_vector { - { $name:ident, $type:ty } => { - impl crate::vector::sealed::Sealed for $name - where - crate::LaneCount: crate::SupportedLaneCount, - {} - - impl crate::vector::Vector for $name - where - crate::LaneCount: crate::SupportedLaneCount, - { - type Scalar = $type; - const LANES: usize = LANES; - - #[inline] - fn splat(val: Self::Scalar) -> Self { - Self::splat(val) - } - } - - impl $name - where - crate::LaneCount: crate::SupportedLaneCount, - { - /// Construct a SIMD vector by setting all lanes to the given value. - pub const fn splat(value: $type) -> Self { - Self([value; LANES]) - } - - /// Returns an array reference containing the entire SIMD vector. - pub const fn as_array(&self) -> &[$type; LANES] { - &self.0 - } - - /// Returns a mutable array reference containing the entire SIMD vector. - pub fn as_mut_array(&mut self) -> &mut [$type; LANES] { - &mut self.0 - } - - /// Converts an array to a SIMD vector. - pub const fn from_array(array: [$type; LANES]) -> Self { - Self(array) - } - - /// Converts a SIMD vector to an array. - pub const fn to_array(self) -> [$type; LANES] { - self.0 - } - - /// SIMD gather: construct a SIMD vector by reading from a slice, using potentially discontiguous indices. - /// If an index is out of bounds, that lane instead selects the value from the "or" vector. - /// ``` - /// # #![feature(portable_simd)] - /// # use core_simd::*; - /// let vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; - /// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 5]); - /// let alt = SimdI32::from_array([-5, -4, -3, -2]); - /// - /// let result = SimdI32::<4>::gather_or(&vec, idxs, alt); // Note the lane that is out-of-bounds. - /// assert_eq!(result, SimdI32::from_array([-5, 13, 10, 15])); - /// ``` - #[must_use] - #[inline] - pub fn gather_or(slice: &[$type], idxs: crate::SimdUsize, or: Self) -> Self { - Self::gather_select(slice, crate::MaskSize::splat(true), idxs, or) - } - - /// SIMD gather: construct a SIMD vector by reading from a slice, using potentially discontiguous indices. - /// Out-of-bounds indices instead use the default value for that lane (0). - /// ``` - /// # #![feature(portable_simd)] - /// # use core_simd::*; - /// let vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; - /// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 5]); - /// - /// let result = SimdI32::<4>::gather_or_default(&vec, idxs); // Note the lane that is out-of-bounds. - /// assert_eq!(result, SimdI32::from_array([0, 13, 10, 15])); - /// ``` - #[must_use] - #[inline] - pub fn gather_or_default(slice: &[$type], idxs: crate::SimdUsize) -> Self { - Self::gather_or(slice, idxs, Self::splat(<$type>::default())) - } - - /// SIMD gather: construct a SIMD vector by reading from a slice, using potentially discontiguous indices. - /// Out-of-bounds or masked indices instead select the value from the "or" vector. - /// ``` - /// # #![feature(portable_simd)] - /// # use core_simd::*; - /// let vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; - /// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 5]); - /// let alt = SimdI32::from_array([-5, -4, -3, -2]); - /// let mask = MaskSize::from_array([true, true, true, false]); // Note the mask of the last lane. - /// - /// let result = SimdI32::<4>::gather_select(&vec, mask, idxs, alt); // Note the lane that is out-of-bounds. - /// assert_eq!(result, SimdI32::from_array([-5, 13, 10, -2])); - /// ``` - #[must_use] - #[inline] - pub fn gather_select( - slice: &[$type], - mask: crate::MaskSize, - idxs: crate::SimdUsize, - or: Self, - ) -> Self - { - let mask = (mask & idxs.lanes_lt(crate::SimdUsize::splat(slice.len()))).to_int(); - let base_ptr = crate::vector::ptr::SimdConstPtr::splat(slice.as_ptr()); - // Ferris forgive me, I have done pointer arithmetic here. - let ptrs = base_ptr.wrapping_add(idxs); - // SAFETY: The ptrs have been bounds-masked to prevent memory-unsafe reads insha'allah - unsafe { crate::intrinsics::simd_gather(or, ptrs, mask) } - } - - /// SIMD scatter: write a SIMD vector's values into a slice, using potentially discontiguous indices. - /// Out-of-bounds indices are not written. - /// `scatter` writes "in order", so if an index receives two writes, only the last is guaranteed. - /// ``` - /// # #![feature(portable_simd)] - /// # use core_simd::*; - /// let mut vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; - /// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 0]); - /// let vals = SimdI32::from_array([-27, 82, -41, 124]); - /// - /// vals.scatter(&mut vec, idxs); // index 0 receives two writes. - /// assert_eq!(vec, vec![124, 11, 12, 82, 14, 15, 16, 17, 18]); - /// ``` - #[inline] - pub fn scatter(self, slice: &mut [$type], idxs: crate::SimdUsize) { - self.scatter_select(slice, crate::MaskSize::splat(true), idxs) - } - - /// SIMD scatter: write a SIMD vector's values into a slice, using potentially discontiguous indices. - /// Out-of-bounds or masked indices are not written. - /// `scatter_select` writes "in order", so if an index receives two writes, only the last is guaranteed. - /// ``` - /// # #![feature(portable_simd)] - /// # use core_simd::*; - /// let mut vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; - /// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 0]); - /// let vals = SimdI32::from_array([-27, 82, -41, 124]); - /// let mask = MaskSize::from_array([true, true, true, false]); // Note the mask of the last lane. - /// - /// vals.scatter_select(&mut vec, mask, idxs); // index 0's second write is masked, thus omitted. - /// assert_eq!(vec, vec![-41, 11, 12, 82, 14, 15, 16, 17, 18]); - /// ``` - #[inline] - pub fn scatter_select( - self, - slice: &mut [$type], - mask: crate::MaskSize, - idxs: crate::SimdUsize, - ) - { - // We must construct our scatter mask before we derive a pointer! - let mask = (mask & idxs.lanes_lt(crate::SimdUsize::splat(slice.len()))).to_int(); - // SAFETY: This block works with *mut T derived from &mut 'a [T], - // which means it is delicate in Rust's borrowing model, circa 2021: - // &mut 'a [T] asserts uniqueness, so deriving &'a [T] invalidates live *mut Ts! - // Even though this block is largely safe methods, it must be almost exactly this way - // to prevent invalidating the raw ptrs while they're live. - // Thus, entering this block requires all values to use being already ready: - // 0. idxs we want to write to, which are used to construct the mask. - // 1. mask, which depends on an initial &'a [T] and the idxs. - // 2. actual values to scatter (self). - // 3. &mut [T] which will become our base ptr. - unsafe { - // Now Entering ☢️ *mut T Zone - let base_ptr = crate::vector::ptr::SimdMutPtr::splat(slice.as_mut_ptr()); - // Ferris forgive me, I have done pointer arithmetic here. - let ptrs = base_ptr.wrapping_add(idxs); - // The ptrs have been bounds-masked to prevent memory-unsafe writes insha'allah - crate::intrinsics::simd_scatter(self, ptrs, mask) - // Cleared ☢️ *mut T Zone - } - } - } - - impl Copy for $name where crate::LaneCount: crate::SupportedLaneCount {} - - impl Clone for $name where crate::LaneCount: crate::SupportedLaneCount { - #[inline] - fn clone(&self) -> Self { - *self - } - } - - impl Default for $name where crate::LaneCount: crate::SupportedLaneCount { - #[inline] - fn default() -> Self { - Self::splat(<$type>::default()) - } - } - - impl PartialEq for $name where crate::LaneCount: crate::SupportedLaneCount { - #[inline] - fn eq(&self, other: &Self) -> bool { - // TODO use SIMD equality - self.to_array() == other.to_array() - } - } - - impl PartialOrd for $name where crate::LaneCount: crate::SupportedLaneCount { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - // TODO use SIMD equalitya - self.to_array().partial_cmp(other.as_ref()) - } - } - - // array references - impl AsRef<[$type; LANES]> for $name where crate::LaneCount: crate::SupportedLaneCount { - #[inline] - fn as_ref(&self) -> &[$type; LANES] { - &self.0 - } - } - - impl AsMut<[$type; LANES]> for $name where crate::LaneCount: crate::SupportedLaneCount { - #[inline] - fn as_mut(&mut self) -> &mut [$type; LANES] { - &mut self.0 - } - } - - // slice references - impl AsRef<[$type]> for $name where crate::LaneCount: crate::SupportedLaneCount { - #[inline] - fn as_ref(&self) -> &[$type] { - &self.0 - } - } - - impl AsMut<[$type]> for $name where crate::LaneCount: crate::SupportedLaneCount { - #[inline] - fn as_mut(&mut self) -> &mut [$type] { - &mut self.0 - } - } - - // vector/array conversion - impl From<[$type; LANES]> for $name where crate::LaneCount: crate::SupportedLaneCount { - fn from(array: [$type; LANES]) -> Self { - Self(array) - } - } - - impl From<$name> for [$type; LANES] where crate::LaneCount: crate::SupportedLaneCount { - fn from(vector: $name) -> Self { - vector.to_array() - } - } - - impl_shuffle_2pow_lanes!{ $name } - } -}