Skip to content

Commit db35b39

Browse files
Add SimdArray::{gather, gather_or}
1 parent 048b547 commit db35b39

File tree

2 files changed

+47
-1
lines changed

2 files changed

+47
-1
lines changed

crates/core_simd/src/array.rs

+44
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
use crate::intrinsics;
12
use crate::masks::*;
3+
use crate::vector::ptr::SimdConst;
24
use crate::vector::*;
35

46
/// A representation of a vector as an "array" with indices,
@@ -17,6 +19,48 @@ where
1719
/// Generates a SIMD vector with the same value in every lane.
1820
#[must_use]
1921
fn splat(val: Self::Scalar) -> Self;
22+
23+
/// SIMD gather: construct a SIMD vector by reading from a slice, using potentially discontiguous indices.
24+
/// Out-of-bounds indices instead use the default value (zero, generally).
25+
/// ```
26+
/// # use core_simd::*;
27+
/// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
28+
/// let indices = SimdUsize::<4>::from_array([7, 3, 0, 5]);
29+
///
30+
/// let result = SimdI32::<4>::gather(&vec, indices);
31+
/// assert!(result == SimdI32::from_array([14, 13, 11, 12]));
32+
/// ```
33+
#[must_use]
34+
#[inline]
35+
fn gather(slice: &[Self::Scalar], idxs: SimdUsize<LANES>) -> Self
36+
where
37+
Self::Scalar: Default,
38+
{
39+
Self::gather_or(slice, idxs, Self::splat(Self::Scalar::default()))
40+
}
41+
42+
/// SIMD gather: construct a SIMD vector by reading from a slice, using potentially discontiguous indices.
43+
/// If an index is out of bounds, it instead selects from the values in the "or" vector.
44+
/// ```
45+
/// # use core_simd::*;
46+
/// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
47+
/// let indices = SimdUsize::<4>::from_array([4, 3, 1, 2]);
48+
///
49+
/// let result = SimdI32::<4>::gather(&vec, indices);
50+
/// assert!(result == SimdI32::from_array([14, 13, 11, 12]));
51+
/// ```
52+
#[must_use]
53+
#[inline]
54+
fn gather_or(slice: &[Self::Scalar], idxs: SimdUsize<LANES>, or: Self) -> Self {
55+
// IMPORTANT: We MUST construct our gather mask before we derive a pointer!
56+
let mask: MaskSize<LANES> = idxs.lanes_lt(SimdUsize::splat(slice.len()));
57+
// IMPORTANT: We MUST obtain a pointer via as_ptr or an equivalent method!
58+
let base_ptr = SimdConst::splat(slice.as_ptr());
59+
// Ferris forgive me, I have done pointer arithmetic here.
60+
let ptrs = base_ptr.wrapping_add(idxs);
61+
// SAFETY: We bounds-mask the pointers so nothing fishy can emerge from this insha'allah
62+
unsafe { intrinsics::simd_gather(or, ptrs, mask) }
63+
}
2064
}
2165

2266
macro_rules! impl_simdarray_for {

crates/core_simd/src/intrinsics.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ extern "platform-intrinsic" {
4545

4646
/// fabs
4747
pub(crate) fn simd_fabs<T>(x: T) -> T;
48-
48+
4949
/// fsqrt
5050
pub(crate) fn simd_fsqrt<T>(x: T) -> T;
5151

@@ -63,6 +63,8 @@ extern "platform-intrinsic" {
6363
pub(crate) fn simd_shuffle16<T, U>(x: T, y: T, idx: [u32; 16]) -> U;
6464
pub(crate) fn simd_shuffle32<T, U>(x: T, y: T, idx: [u32; 32]) -> U;
6565

66+
pub(crate) fn simd_gather<T, U, V>(val: T, ptr: U, mask: V) -> T;
67+
6668
// {s,u}add.sat
6769
pub(crate) fn simd_saturating_add<T>(x: T, y: T) -> T;
6870

0 commit comments

Comments
 (0)