Skip to content

Commit 81ceda8

Browse files
Add SimdArray::scatter{,_select}
1 parent 128b6f5 commit 81ceda8

File tree

2 files changed

+60
-1
lines changed

2 files changed

+60
-1
lines changed

crates/core_simd/src/array.rs

+59-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::intrinsics;
22
use crate::masks::*;
3-
use crate::vector::ptr::SimdConstPtr;
3+
use crate::vector::ptr::{SimdConstPtr, SimdMutPtr};
44
use crate::vector::*;
55

66
/// A representation of a vector as an "array" with indices, implementing
@@ -83,6 +83,64 @@ where
8383
// SAFETY: The ptrs have been bounds-masked to prevent memory-unsafe reads insha'allah
8484
unsafe { intrinsics::simd_gather(or, ptrs, mask) }
8585
}
86+
87+
/// SIMD scatter: write a SIMD vector's values into a slice, using potentially discontiguous indices.
88+
/// Out-of-bounds indices are not written.
89+
/// ```
90+
/// # use core_simd::*;
91+
/// let mut vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
92+
/// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 5]);
93+
/// let vals = SimdI32::from_array([-5, -4, -3, -2]);
94+
///
95+
/// vals.scatter(&mut vec, idxs);
96+
/// assert_eq!(vec, vec![-3, 11, 12, -4, 14, -2, 16, 17, 18]);
97+
/// ```
98+
#[inline]
99+
fn scatter(self, slice: &mut [Self::Scalar], idxs: SimdUsize<LANES>) {
100+
self.scatter_select(slice, MaskSize::splat(true), idxs)
101+
}
102+
103+
/// SIMD scatter: write a SIMD vector's values into a slice, using potentially discontiguous indices.
104+
/// Out-of-bounds or masked indices are not written.
105+
/// ```
106+
/// # use core_simd::*;
107+
/// let mut vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
108+
/// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 5]);
109+
/// let vals = SimdI32::from_array([-5, -4, -3, -2]);
110+
/// let mask = MaskSize::from_array([true, true, true, false]); // Note the mask of the last lane.
111+
///
112+
/// vals.scatter_select(&mut vec, mask, idxs);
113+
/// assert_eq!(vec, vec![-3, 11, 12, -4, 14, 15, 16, 17, 18]);
114+
/// ```
115+
#[inline]
116+
fn scatter_select(
117+
self,
118+
slice: &mut [Self::Scalar],
119+
mask: MaskSize<LANES>,
120+
idxs: SimdUsize<LANES>,
121+
) {
122+
// We must construct our scatter mask before we derive a pointer!
123+
let mask = (mask & idxs.lanes_lt(SimdUsize::splat(slice.len()))).to_int();
124+
// SAFETY: This block works with *mut T derived from &mut 'a [T],
125+
// which means it is delicate in Rust's borrowing model, circa 2021:
126+
// &mut 'a [T] asserts uniqueness, so deriving &'a [T] invalidates live *mut Ts!
127+
// Even though this block is largely safe methods, it must be almost exactly this way
128+
// to prevent invalidating the raw ptrs while they're live.
129+
// Thus, entering this block requires all values to use being already ready:
130+
// 0. idxs we want to write to, which are used to construct the mask.
131+
// 1. mask, which depends on an initial &'a [T] and the idxs.
132+
// 2. actual values to scatter (self).
133+
// 3. &mut [T] which will become our base ptr.
134+
unsafe {
135+
// Now Entering ☢️ *mut T Zone
136+
let base_ptr = SimdMutPtr::splat(slice.as_mut_ptr());
137+
// Ferris forgive me, I have done pointer arithmetic here.
138+
let ptrs = base_ptr.wrapping_add(idxs);
139+
// The ptrs have been bounds-masked to prevent memory-unsafe writes insha'allah
140+
intrinsics::simd_scatter(self, ptrs, mask)
141+
// Cleared ☢️ *mut T Zone
142+
}
143+
}
86144
}
87145

88146
macro_rules! impl_simdarray_for {

crates/core_simd/src/intrinsics.rs

+1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ extern "platform-intrinsic" {
6464
pub(crate) fn simd_shuffle32<T, U>(x: T, y: T, idx: [u32; 32]) -> U;
6565

6666
pub(crate) fn simd_gather<T, U, V>(val: T, ptr: U, mask: V) -> T;
67+
pub(crate) fn simd_scatter<T, U, V>(val: T, ptr: U, mask: V);
6768

6869
// {s,u}add.sat
6970
pub(crate) fn simd_saturating_add<T>(x: T, y: T) -> T;

0 commit comments

Comments
 (0)