Skip to content

Commit 9be2665

Browse files
Rewrite gather/scatter docs
Headings with # Safety and # Examples are more "std style". Use terms like "enable" and "disable", rather than "mask" jargon.
1 parent 01e9816 commit 9be2665

File tree

1 file changed

+69
-40
lines changed

1 file changed

+69
-40
lines changed

crates/core_simd/src/vector.rs

Lines changed: 69 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,10 @@ where
5757
self.0
5858
}
5959

60-
/// SIMD gather: construct a SIMD vector by reading from a slice, using potentially discontiguous indices.
61-
/// If an index is out of bounds, that lane instead selects the value from the "or" vector.
60+
/// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector.
61+
/// Lanes given an out-of-bounds index instead select values from the `or` vector.
62+
///
63+
/// # Examples
6264
/// ```
6365
/// # #![feature(portable_simd)]
6466
/// # #[cfg(feature = "std")] use core_simd::Simd;
@@ -76,8 +78,10 @@ where
7678
Self::gather_select(slice, Mask::splat(true), idxs, or)
7779
}
7880

79-
/// SIMD gather: construct a SIMD vector by reading from a slice, using potentially discontiguous indices.
80-
/// Out-of-bounds indices instead use the default value for that lane (0).
81+
/// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector.
82+
/// Lanes given an out-of-bounds index instead are set the default value for the type.
83+
///
84+
/// # Examples
8185
/// ```
8286
/// # #![feature(portable_simd)]
8387
/// # #[cfg(feature = "std")] use core_simd::Simd;
@@ -97,69 +101,82 @@ where
97101
Self::gather_or(slice, idxs, Self::splat(T::default()))
98102
}
99103

100-
/// SIMD gather: construct a SIMD vector by reading from a slice, using potentially discontiguous indices.
101-
/// Out-of-bounds or masked indices instead select the value from the "or" vector.
104+
/// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector.
105+
/// The mask `enable`s all `true` lanes and disables all `false` lanes.
106+
/// If an index is disabled or is out-of-bounds, the lane is selected from the `or` vector.
107+
///
108+
/// # Examples
102109
/// ```
103110
/// # #![feature(portable_simd)]
104111
/// # #[cfg(feature = "std")] use core_simd::{Simd, Mask};
105112
/// # #[cfg(not(feature = "std"))] use core::simd::{Simd, Mask};
106113
/// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
107114
/// let idxs = Simd::from_array([9, 3, 0, 5]);
108115
/// let alt = Simd::from_array([-5, -4, -3, -2]);
109-
/// let mask = Mask::from_array([true, true, true, false]); // Note the mask of the last lane.
116+
/// let enable = Mask::from_array([true, true, true, false]); // Note the mask of the last lane.
110117
///
111-
/// let result = Simd::gather_select(&vec, mask, idxs, alt); // Note the lane that is out-of-bounds.
118+
/// let result = Simd::gather_select(&vec, enable, idxs, alt); // Note the lane that is out-of-bounds.
112119
/// assert_eq!(result, Simd::from_array([-5, 13, 10, -2]));
113120
/// ```
114121
#[must_use]
115122
#[inline]
116123
pub fn gather_select(
117124
slice: &[T],
118-
mask: Mask<isize, LANES>,
125+
enable: Mask<isize, LANES>,
119126
idxs: Simd<usize, LANES>,
120127
or: Self,
121128
) -> Self {
122-
let mask: Mask<isize, LANES> = mask & idxs.lanes_lt(Simd::splat(slice.len()));
129+
let enable: Mask<isize, LANES> = enable & idxs.lanes_lt(Simd::splat(slice.len()));
123130
// SAFETY: We have masked-off out-of-bounds lanes.
124-
unsafe { Self::gather_select_unchecked(slice, mask, idxs, or) }
131+
unsafe { Self::gather_select_unchecked(slice, enable, idxs, or) }
125132
}
126133

127-
/// Unsafe SIMD gather: construct a SIMD vector by reading from a slice, using potentially discontiguous indices.
128-
/// Masked indices instead select the value from the "or" vector.
129-
/// `gather_select_unchecked` is unsound if any unmasked index is out-of-bounds of the slice.
134+
/// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector.
135+
/// The mask `enable`s all `true` lanes and disables all `false` lanes.
136+
/// If an index is disabled, the lane is selected from the `or` vector.
137+
///
138+
/// # Safety
139+
///
140+
/// Calling this function with an `enable`d out-of-bounds index is *[undefined behavior]*
141+
/// even if the resulting value is not used.
142+
///
143+
/// # Examples
130144
/// ```
131145
/// # #![feature(portable_simd)]
132146
/// # #[cfg(feature = "std")] use core_simd::{Simd, Mask};
133147
/// # #[cfg(not(feature = "std"))] use core::simd::{Simd, Mask};
134148
/// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
135149
/// let idxs = Simd::from_array([9, 3, 0, 5]);
136150
/// let alt = Simd::from_array([-5, -4, -3, -2]);
137-
/// let mask = Mask::from_array([true, true, true, false]); // Note the final mask lane.
151+
/// let enable = Mask::from_array([true, true, true, false]); // Note the final mask lane.
138152
/// // If this mask was used to gather, it would be unsound. Let's fix that.
139-
/// let mask = mask & idxs.lanes_lt(Simd::splat(vec.len()));
153+
/// let enable = enable & idxs.lanes_lt(Simd::splat(vec.len()));
140154
///
141155
/// // We have masked the OOB lane, so it's safe to gather now.
142-
/// let result = unsafe { Simd::gather_select_unchecked(&vec, mask, idxs, alt) };
156+
/// let result = unsafe { Simd::gather_select_unchecked(&vec, enable, idxs, alt) };
143157
/// assert_eq!(result, Simd::from_array([-5, 13, 10, -2]));
144158
/// ```
159+
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
145160
#[must_use]
146161
#[inline]
147162
pub unsafe fn gather_select_unchecked(
148163
slice: &[T],
149-
mask: Mask<isize, LANES>,
164+
enable: Mask<isize, LANES>,
150165
idxs: Simd<usize, LANES>,
151166
or: Self,
152167
) -> Self {
153168
let base_ptr = crate::simd::ptr::SimdConstPtr::splat(slice.as_ptr());
154169
// Ferris forgive me, I have done pointer arithmetic here.
155170
let ptrs = base_ptr.wrapping_add(idxs);
156171
// SAFETY: The ptrs have been bounds-masked to prevent memory-unsafe reads insha'allah
157-
unsafe { intrinsics::simd_gather(or, ptrs, mask.to_int()) }
172+
unsafe { intrinsics::simd_gather(or, ptrs, enable.to_int()) }
158173
}
159174

160-
/// SIMD scatter: write a SIMD vector's values into a slice, using potentially discontiguous indices.
161-
/// Out-of-bounds indices are not written.
162-
/// `scatter` writes "in order", so if an index receives two writes, only the last is guaranteed.
175+
/// Writes the values in a SIMD vector to potentially discontiguous indices in `slice`.
176+
/// If two lanes in the scattered vector would write to the same index
177+
/// only the last lane is guaranteed to actually be written.
178+
///
179+
/// # Examples
163180
/// ```
164181
/// # #![feature(portable_simd)]
165182
/// # #[cfg(feature = "std")] use core_simd::Simd;
@@ -176,58 +193,70 @@ where
176193
self.scatter_select(slice, Mask::splat(true), idxs)
177194
}
178195

179-
/// SIMD scatter: write a SIMD vector's values into a slice, using potentially discontiguous indices.
180-
/// Out-of-bounds or masked indices are not written.
181-
/// `scatter_select` writes "in order", so if an index receives two writes, only the last is guaranteed.
196+
/// Writes the values in a SIMD vector to multiple potentially discontiguous indices in `slice`.
197+
/// The mask `enable`s all `true` lanes and disables all `false` lanes.
198+
/// If an enabled index is out-of-bounds, the lane is not written.
199+
/// If two enabled lanes in the scattered vector would write to the same index,
200+
/// only the last lane is guaranteed to actually be written.
201+
///
202+
/// # Examples
182203
/// ```
183204
/// # #![feature(portable_simd)]
184205
/// # #[cfg(feature = "std")] use core_simd::{Simd, Mask};
185206
/// # #[cfg(not(feature = "std"))] use core::simd::{Simd, Mask};
186207
/// let mut vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
187208
/// let idxs = Simd::from_array([9, 3, 0, 0]);
188209
/// let vals = Simd::from_array([-27, 82, -41, 124]);
189-
/// let mask = Mask::from_array([true, true, true, false]); // Note the mask of the last lane.
210+
/// let enable = Mask::from_array([true, true, true, false]); // Note the mask of the last lane.
190211
///
191-
/// vals.scatter_select(&mut vec, mask, idxs); // index 0's second write is masked, thus omitted.
212+
/// vals.scatter_select(&mut vec, enable, idxs); // index 0's second write is masked, thus omitted.
192213
/// assert_eq!(vec, vec![-41, 11, 12, 82, 14, 15, 16, 17, 18]);
193214
/// ```
194215
#[inline]
195216
pub fn scatter_select(
196217
self,
197218
slice: &mut [T],
198-
mask: Mask<isize, LANES>,
219+
enable: Mask<isize, LANES>,
199220
idxs: Simd<usize, LANES>,
200221
) {
201-
let mask: Mask<isize, LANES> = mask & idxs.lanes_lt(Simd::splat(slice.len()));
222+
let enable: Mask<isize, LANES> = enable & idxs.lanes_lt(Simd::splat(slice.len()));
202223
// SAFETY: We have masked-off out-of-bounds lanes.
203-
unsafe { self.scatter_select_unchecked(slice, mask, idxs) }
224+
unsafe { self.scatter_select_unchecked(slice, enable, idxs) }
204225
}
205226

206-
/// Unsafe SIMD scatter: write a SIMD vector's values into a slice, using potentially discontiguous indices.
207-
/// Out-of-bounds or masked indices are not written.
208-
/// `scatter_select_unchecked` is unsound if any unmasked index is out of bounds of the slice.
209-
/// `scatter_select_unchecked` writes "in order", so if the same index receives two writes, only the last is guaranteed.
227+
/// Writes the values in a SIMD vector to multiple potentially discontiguous indices in `slice`.
228+
/// The mask `enable`s all `true` lanes and disables all `false` lanes.
229+
/// If two enabled lanes in the scattered vector would write to the same index,
230+
/// only the last lane is guaranteed to actually be written.
231+
///
232+
/// # Safety
233+
///
234+
/// Calling this function with an enabled out-of-bounds index is *[undefined behavior]*,
235+
/// and may lead to memory corruption.
236+
///
237+
/// # Examples
210238
/// ```
211239
/// # #![feature(portable_simd)]
212240
/// # #[cfg(feature = "std")] use core_simd::{Simd, Mask};
213241
/// # #[cfg(not(feature = "std"))] use core::simd::{Simd, Mask};
214242
/// let mut vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
215243
/// let idxs = Simd::from_array([9, 3, 0, 0]);
216244
/// let vals = Simd::from_array([-27, 82, -41, 124]);
217-
/// let mask = Mask::from_array([true, true, true, false]); // Note the mask of the last lane.
245+
/// let enable = Mask::from_array([true, true, true, false]); // Note the mask of the last lane.
218246
/// // If this mask was used to scatter, it would be unsound. Let's fix that.
219-
/// let mask = mask & idxs.lanes_lt(Simd::splat(vec.len()));
247+
/// let enable = enable & idxs.lanes_lt(Simd::splat(vec.len()));
220248
///
221249
/// // We have masked the OOB lane, so it's safe to scatter now.
222-
/// unsafe { vals.scatter_select_unchecked(&mut vec, mask, idxs); }
250+
/// unsafe { vals.scatter_select_unchecked(&mut vec, enable, idxs); }
223251
/// // index 0's second write is masked, thus was omitted.
224252
/// assert_eq!(vec, vec![-41, 11, 12, 82, 14, 15, 16, 17, 18]);
225253
/// ```
254+
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
226255
#[inline]
227256
pub unsafe fn scatter_select_unchecked(
228257
self,
229258
slice: &mut [T],
230-
mask: Mask<isize, LANES>,
259+
enable: Mask<isize, LANES>,
231260
idxs: Simd<usize, LANES>,
232261
) {
233262
// SAFETY: This block works with *mut T derived from &mut 'a [T],
@@ -237,7 +266,7 @@ where
237266
// to prevent invalidating the raw ptrs while they're live.
238267
// Thus, entering this block requires all values to use being already ready:
239268
// 0. idxs we want to write to, which are used to construct the mask.
240-
// 1. mask, which depends on an initial &'a [T] and the idxs.
269+
// 1. enable, which depends on an initial &'a [T] and the idxs.
241270
// 2. actual values to scatter (self).
242271
// 3. &mut [T] which will become our base ptr.
243272
unsafe {
@@ -246,7 +275,7 @@ where
246275
// Ferris forgive me, I have done pointer arithmetic here.
247276
let ptrs = base_ptr.wrapping_add(idxs);
248277
// The ptrs have been bounds-masked to prevent memory-unsafe writes insha'allah
249-
intrinsics::simd_scatter(self, ptrs, mask.to_int())
278+
intrinsics::simd_scatter(self, ptrs, enable.to_int())
250279
// Cleared ☢️ *mut T Zone
251280
}
252281
}

0 commit comments

Comments
 (0)