Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/core_simd/examples/nbody.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
8 changes: 5 additions & 3 deletions crates/core_simd/src/masks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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 });
}
}

Expand All @@ -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.
Expand Down
26 changes: 2 additions & 24 deletions crates/core_simd/src/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,6 @@ mod deref;
mod shift_scalar;
mod unary;

impl<I, T, const N: usize> core::ops::Index<I> for Simd<T, N>
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<I, T, const N: usize> core::ops::IndexMut<I> for Simd<T, N>
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
Expand Down Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion crates/core_simd/src/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<M>(default);
let false_values = false_values.resize::<M>(default);

Expand Down
10 changes: 5 additions & 5 deletions crates/core_simd/src/simd/num/float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<N>(x[0])
x.resize::<N>(x.get(0))
} else if N < 8 {
let x = self.resize::<8>(Default::default()).cast();
x.resize::<N>(x[0])
x.resize::<N>(x.get(0))
} else if N < 16 {
let x = self.resize::<16>(Default::default()).cast();
x.resize::<N>(x[0])
x.resize::<N>(x.get(0))
} else if N < 32 {
let x = self.resize::<32>(Default::default()).cast();
x.resize::<N>(x[0])
x.resize::<N>(x.get(0))
} else {
let x = self.resize::<64>(Default::default()).cast();
x.resize::<N>(x[0])
x.resize::<N>(x.get(0))
}
}

Expand Down
6 changes: 3 additions & 3 deletions crates/core_simd/src/swizzle_dyn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@ impl<const N: usize> Simd<u8, N> {
_ => {
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()
}
Expand Down
151 changes: 151 additions & 0 deletions crates/core_simd/src/vector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
///
/// ```
Expand All @@ -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<T, N>` is just an overaligned `[T; N]` with
Expand All @@ -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
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No idea what the problem was, but LLVM was very unhappy for some reason

rustc-LLVM ERROR: Cannot select: 0x7ff0c871e2a0: v16i8 = setcc 0x7ff0c871ee00, 0x7ff0ca14b000, setne:ch
  0x7ff0c871ee00: v8i16,ch = CopyFromReg 0x7ff0c871e8c0:1, Register:v8i16 %21
  0x7ff0ca14b000: v8i16 = xor 0x7ff0c8720a80, 0x7ff0c871db60
    0x7ff0c8720a80: v8i16,ch = CopyFromReg 0x7ff0c871e070:1, Register:v8i16 %11
    0x7ff0c871db60: v8i16 = splat_vector Constant:i32<-1>

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<T> {
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<Self> {
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).
Expand Down
2 changes: 1 addition & 1 deletion crates/core_simd/tests/ops_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ macro_rules! impl_signed_tests {
fn div_by_one_zero_panics<const LANES: usize>() {
let a = Vector::<LANES>::splat(42);
let mut b = Vector::<LANES>::splat(21);
b[0] = 0 as _;
b = b.set(0, 0 as _);
let _ = a / b;
}

Expand Down
12 changes: 6 additions & 6 deletions crates/core_simd/tests/to_bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<u32, 2>::from_ne_bytes(ne_bytes), int);
assert_eq!(Simd::<u32, 2>::from_be_bytes(be_bytes), int);
assert_eq!(Simd::<u32, 2>::from_le_bytes(le_bytes), int);
Expand Down
Loading