diff --git a/crates/core_simd/examples/nbody.rs b/crates/core_simd/examples/nbody.rs index 65820d1340b..66ec725357d 100644 --- a/crates/core_simd/examples/nbody.rs +++ b/crates/core_simd/examples/nbody.rs @@ -139,8 +139,8 @@ mod nbody { (r[i + 1] * r[i + 1]).reduce_sum(), ]); let dmags = f64x2::splat(dt) / (d2s * d2s.sqrt()); - mag[i] = dmags[0]; - mag[i + 1] = dmags[1]; + mag[i] = dmags.get(0); + mag[i + 1] = dmags.get(1); } let mut i = 0; diff --git a/crates/core_simd/src/masks.rs b/crates/core_simd/src/masks.rs index 3e2209556b6..af050affa53 100644 --- a/crates/core_simd/src/masks.rs +++ b/crates/core_simd/src/masks.rs @@ -250,7 +250,7 @@ where #[must_use = "method returns a new bool and does not mutate the original value"] #[track_caller] pub fn test(&self, index: usize) -> bool { - T::eq(self.0[index], T::TRUE) + T::eq(self.0.get(index), T::TRUE) } /// Sets the value of the specified element. @@ -261,7 +261,9 @@ where pub unsafe fn set_unchecked(&mut self, index: usize, value: bool) { // Safety: the caller must confirm this invariant unsafe { - *self.0.as_mut_array().get_unchecked_mut(index) = if value { T::TRUE } else { T::FALSE } + self.0 = self + .0 + .set_unchecked(index, if value { T::TRUE } else { T::FALSE }); } } @@ -272,7 +274,7 @@ where #[inline] #[track_caller] pub fn set(&mut self, index: usize, value: bool) { - self.0[index] = if value { T::TRUE } else { T::FALSE } + self.0 = self.0.set(index, if value { T::TRUE } else { T::FALSE }); } /// Returns true if any element is set, or false otherwise. diff --git a/crates/core_simd/src/ops.rs b/crates/core_simd/src/ops.rs index eb6601f7348..ff1601d4ae8 100644 --- a/crates/core_simd/src/ops.rs +++ b/crates/core_simd/src/ops.rs @@ -9,29 +9,6 @@ mod deref; mod shift_scalar; mod unary; -impl core::ops::Index for Simd -where - T: SimdElement, - I: core::slice::SliceIndex<[T]>, -{ - type Output = I::Output; - #[inline] - fn index(&self, index: I) -> &Self::Output { - &self.as_array()[index] - } -} - -impl core::ops::IndexMut for Simd -where - T: SimdElement, - I: core::slice::SliceIndex<[T]>, -{ - #[inline] - fn index_mut(&mut self, index: I) -> &mut Self::Output { - &mut self.as_mut_array()[index] - } -} - macro_rules! unsafe_base { ($lhs:ident, $rhs:ident, {$simd_call:ident}, $($_:tt)*) => { // Safety: $lhs and $rhs are vectors @@ -101,7 +78,8 @@ macro_rules! int_divrem_guard { { let mut out = Simd::splat(0 as _); for i in 0..Self::LEN { - out[i] = $lhs[i] $op rhs[i]; + let val = $lhs.get(i) $op rhs.get(i); + out = out.set(i, val); } out } diff --git a/crates/core_simd/src/select.rs b/crates/core_simd/src/select.rs index 404f54d8f38..eb710e9981b 100644 --- a/crates/core_simd/src/select.rs +++ b/crates/core_simd/src/select.rs @@ -89,7 +89,7 @@ where where T: SimdElement, { - let default = true_values[0]; + let default = true_values.get(0); let true_values = true_values.resize::(default); let false_values = false_values.resize::(default); diff --git a/crates/core_simd/src/simd/num/float.rs b/crates/core_simd/src/simd/num/float.rs index efd7c246951..d9369330b78 100644 --- a/crates/core_simd/src/simd/num/float.rs +++ b/crates/core_simd/src/simd/num/float.rs @@ -269,19 +269,19 @@ macro_rules! impl_trait { unsafe { core::intrinsics::simd::simd_as(self) } } else if N < 4 { let x = self.resize::<4>(Default::default()).cast(); - x.resize::(x[0]) + x.resize::(x.get(0)) } else if N < 8 { let x = self.resize::<8>(Default::default()).cast(); - x.resize::(x[0]) + x.resize::(x.get(0)) } else if N < 16 { let x = self.resize::<16>(Default::default()).cast(); - x.resize::(x[0]) + x.resize::(x.get(0)) } else if N < 32 { let x = self.resize::<32>(Default::default()).cast(); - x.resize::(x[0]) + x.resize::(x.get(0)) } else { let x = self.resize::<64>(Default::default()).cast(); - x.resize::(x[0]) + x.resize::(x.get(0)) } } diff --git a/crates/core_simd/src/swizzle_dyn.rs b/crates/core_simd/src/swizzle_dyn.rs index ae0b174973d..cfb14bb52c0 100644 --- a/crates/core_simd/src/swizzle_dyn.rs +++ b/crates/core_simd/src/swizzle_dyn.rs @@ -93,9 +93,9 @@ impl Simd { _ => { let mut array = [0; N]; for (i, k) in idxs.to_array().into_iter().enumerate() { - if (k as usize) < N { - array[i] = self[k as usize]; - }; + if let Some(val) = self.get_checked(usize::from(k)) { + array[i] = val; + } } array.into() } diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index 2dba5c83e11..022ca251d48 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -173,6 +173,11 @@ where /// Returns an array reference containing the entire SIMD vector. /// + /// While this exists and *can* be used to read an arbitrary element from a + /// vector, like `v.as_array()[i]`, that's discouraged because it forces the + /// vector to memory which tends to perform poorly. Instead, use + /// [`Self::get`]. (This is also why `Index` is not implemented.) + /// /// # Examples /// /// ``` @@ -193,6 +198,11 @@ where } /// Returns a mutable array reference containing the entire SIMD vector. + /// + /// While this exists and *can* be used to write an arbitrary element into a + /// vector, like `v.as_mut_array()[i] = x`, that's discouraged because it + /// forces the vector to memory which tends to perform poorly. Instead, use + /// [`Self::set`]. (This is also why `IndexMut` is not implemented.) #[inline] pub const fn as_mut_array(&mut self) -> &mut [T; N] { // SAFETY: `Simd` is just an overaligned `[T; N]` with @@ -204,6 +214,147 @@ where unsafe { &mut *(self as *mut Self as *mut [T; N]) } } + /// # Safety + /// `idx` must be in-bounds (`idx < N`) + #[inline] + unsafe fn get_inner(self, idx: usize) -> T { + // FIXME: This is a workaround for a CI failure in #498 + if cfg!(target_family = "wasm") { + return self.as_array()[idx]; + } + + // SAFETY: our precondition is also that the value is in-bounds + // and this type is a simd type of the correct element type. + unsafe { core::intrinsics::simd::simd_extract_dyn(self, idx as u32) } + } + + /// Gets the value at `idx` to `val`. + /// + /// This typically faster than reading via `as_array`. + /// + /// # Panics + /// + /// If `idx` is out of bounds (aka if `idx >= N`). + /// + /// # Examples + /// + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::{Simd, u64x4}; + /// let v: u64x4 = Simd::from_array([3, 5, 7, 11]); + /// assert_eq!(v.get(2), 7); + /// ``` + #[inline] + pub fn get(self, idx: usize) -> T { + assert!(idx < N); + // SAFETY: just checked in-bounds with the assert + unsafe { self.get_inner(idx) } + } + + /// Gets the value at `idx` to `val`, or `None` if `idx >= N`. + /// + /// This typically faster than reading via `as_array`. + #[inline] + pub fn get_checked(self, idx: usize) -> Option { + if idx < N { + // SAFETY: just checked in-bounds with the if + Some(unsafe { self.get_inner(idx) }) + } else { + None + } + } + + /// Gets the value at `idx` to `val`. + /// + /// This typically faster than reading via `as_array`. + /// + /// # Safety + /// `idx` must be in-bounds (`idx < N`) + #[inline] + pub unsafe fn get_unchecked(self, idx: usize) -> T { + // SAFETY: our precondition is also that the value is in-bounds + unsafe { + core::hint::assert_unchecked(idx < N); + self.get_inner(idx) + } + } + + /// # Safety + /// `idx` must be in-bounds (`idx < N`) + #[inline] + #[must_use = "This returns a new vector, rather than updating in-place"] + unsafe fn set_inner(self, idx: usize, val: T) -> Self { + // FIXME: This is a workaround for a CI failure in #498 + if cfg!(target_family = "wasm") { + let mut temp = self; + temp.as_mut_array()[idx] = val; + return temp; + } + + // SAFETY: our precondition is also that the value is in-bounds + // and this type is a simd type of the correct element type. + unsafe { core::intrinsics::simd::simd_insert_dyn(self, idx as u32, val) } + } + + /// Sets the value at `idx` to `val`, returning the updated vector. + /// + /// This typically faster than updating via `as_mut_array`. + /// + /// # Panics + /// + /// If `idx` is out of bounds (aka if `idx >= N`). + /// + /// # Examples + /// + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::{Simd, u64x4}; + /// let v: u64x4 = Simd::from_array([3, 5, 7, 11]); + /// assert_eq!(v.set(2, 99).as_array(), &[3, 5, 99, 11]); + /// ``` + #[inline] + #[must_use = "This returns a new vector, rather than updating in-place"] + pub fn set(self, idx: usize, val: T) -> Self { + assert!(idx < N); + // SAFETY: just checked in-bounds with the assert + unsafe { self.set_inner(idx, val) } + } + + /// Sets the value at `idx` to `val` and returns the updated vector + /// or returns `None` if `idx >= N`. + /// + /// This typically faster than updating via `as_mut_array`. + #[inline] + #[must_use = "This returns a new vector, rather than updating in-place"] + pub fn set_checked(self, idx: usize, val: T) -> Option { + if idx < N { + // SAFETY: just checked in-bounds with the if + Some(unsafe { self.set_inner(idx, val) }) + } else { + None + } + } + + /// Sets the value at `idx` to `val`, returning the updated vector. + /// + /// This typically faster than updating via `as_mut_array`. + /// + /// # Safety + /// `idx` must be in-bounds (`idx < N`) + #[inline] + #[must_use = "This returns a new vector, rather than updating in-place"] + pub unsafe fn set_unchecked(self, idx: usize, val: T) -> Self { + // SAFETY: our precondition is also that the value is in-bounds + unsafe { + core::hint::assert_unchecked(idx < N); + self.set_inner(idx, val) + } + } + /// Loads a vector from an array of `T`. /// /// This function is necessary since `repr(simd)` has padding for non-power-of-2 vectors (at the time of writing). diff --git a/crates/core_simd/tests/ops_macros.rs b/crates/core_simd/tests/ops_macros.rs index 6de78f51e59..57ba40197c1 100644 --- a/crates/core_simd/tests/ops_macros.rs +++ b/crates/core_simd/tests/ops_macros.rs @@ -375,7 +375,7 @@ macro_rules! impl_signed_tests { fn div_by_one_zero_panics() { let a = Vector::::splat(42); let mut b = Vector::::splat(21); - b[0] = 0 as _; + b = b.set(0, 0 as _); let _ = a / b; } diff --git a/crates/core_simd/tests/to_bytes.rs b/crates/core_simd/tests/to_bytes.rs index 66a7981cdc3..5e8744834f8 100644 --- a/crates/core_simd/tests/to_bytes.rs +++ b/crates/core_simd/tests/to_bytes.rs @@ -8,12 +8,12 @@ fn byte_convert() { let ne_bytes = int.to_ne_bytes(); let be_bytes = int.to_be_bytes(); let le_bytes = int.to_le_bytes(); - assert_eq!(int[0].to_ne_bytes(), ne_bytes[..4]); - assert_eq!(int[1].to_ne_bytes(), ne_bytes[4..]); - assert_eq!(int[0].to_be_bytes(), be_bytes[..4]); - assert_eq!(int[1].to_be_bytes(), be_bytes[4..]); - assert_eq!(int[0].to_le_bytes(), le_bytes[..4]); - assert_eq!(int[1].to_le_bytes(), le_bytes[4..]); + assert_eq!(int.get(0).to_ne_bytes(), ne_bytes.as_array()[..4]); + assert_eq!(int.get(1).to_ne_bytes(), ne_bytes.as_array()[4..]); + assert_eq!(int.get(0).to_be_bytes(), be_bytes.as_array()[..4]); + assert_eq!(int.get(1).to_be_bytes(), be_bytes.as_array()[4..]); + assert_eq!(int.get(0).to_le_bytes(), le_bytes.as_array()[..4]); + assert_eq!(int.get(1).to_le_bytes(), le_bytes.as_array()[4..]); assert_eq!(Simd::::from_ne_bytes(ne_bytes), int); assert_eq!(Simd::::from_be_bytes(be_bytes), int); assert_eq!(Simd::::from_le_bytes(le_bytes), int);