core_simd/
vector.rs

1use crate::simd::{
2    Mask, MaskElement, Swizzle,
3    cmp::SimdPartialOrd,
4    num::SimdUint,
5    ptr::{SimdConstPtr, SimdMutPtr},
6};
7
8/// A SIMD vector with the shape of `[T; N]` but the operations of `T`.
9///
10/// `Simd<T, N>` supports the operators (+, *, etc.) that `T` does in "elementwise" fashion.
11/// These take the element at each index from the left-hand side and right-hand side,
12/// perform the operation, then return the result in the same index in a vector of equal size.
13/// However, `Simd` differs from normal iteration and normal arrays:
14/// - `Simd<T, N>` executes `N` operations in a single step with no `break`s
15/// - `Simd<T, N>` can have an alignment greater than `T`, for better mechanical sympathy
16///
17/// By always imposing these constraints on `Simd`, it is easier to compile elementwise operations
18/// into machine instructions that can themselves be executed in parallel.
19///
20/// ```rust
21/// # #![feature(portable_simd)]
22/// # use core::simd::{Simd};
23/// # use core::array;
24/// let a: [i32; 4] = [-2, 0, 2, 4];
25/// let b = [10, 9, 8, 7];
26/// let sum = array::from_fn(|i| a[i] + b[i]);
27/// let prod = array::from_fn(|i| a[i] * b[i]);
28///
29/// // `Simd<T, N>` implements `From<[T; N]>`
30/// let (v, w) = (Simd::from(a), Simd::from(b));
31/// // Which means arrays implement `Into<Simd<T, N>>`.
32/// assert_eq!(v + w, sum.into());
33/// assert_eq!(v * w, prod.into());
34/// ```
35///
36///
37/// `Simd` with integer elements treats operators as wrapping, as if `T` was [`Wrapping<T>`].
38/// Thus, `Simd` does not implement `wrapping_add`, because that is the default behavior.
39/// This means there is no warning on overflows, even in "debug" builds.
40/// For most applications where `Simd` is appropriate, it is "not a bug" to wrap,
41/// and even "debug builds" are unlikely to tolerate the loss of performance.
42/// You may want to consider using explicitly checked arithmetic if such is required.
43/// Division by zero on integers still causes a panic, so
44/// you may want to consider using `f32` or `f64` if that is unacceptable.
45///
46/// [`Wrapping<T>`]: core::num::Wrapping
47///
48/// # Layout
49/// `Simd<T, N>` has a layout similar to `[T; N]` (identical "shapes"), with a greater alignment.
50/// `[T; N]` is aligned to `T`, but `Simd<T, N>` will have an alignment based on both `T` and `N`.
51/// Thus it is sound to [`transmute`] `Simd<T, N>` to `[T; N]` and should optimize to "zero cost",
52/// but the reverse transmutation may require a copy the compiler cannot simply elide.
53///
54/// `N` cannot be 0 and may be at most 64. This limit may be increased in the future.
55///
56/// # ABI "Features"
57/// Due to Rust's safety guarantees, `Simd<T, N>` is currently passed and returned via memory,
58/// not SIMD registers, except as an optimization. Using `#[inline]` on functions that accept
59/// `Simd<T, N>` or return it is recommended, at the cost of code generation time, as
60/// inlining SIMD-using functions can omit a large function prolog or epilog and thus
61/// improve both speed and code size. The need for this may be corrected in the future.
62///
63/// Using `#[inline(always)]` still requires additional care.
64///
65/// # Safe SIMD with Unsafe Rust
66///
67/// Operations with `Simd` are typically safe, but there are many reasons to want to combine SIMD with `unsafe` code.
68/// Care must be taken to respect differences between `Simd` and other types it may be transformed into or derived from.
69/// In particular, the layout of `Simd<T, N>` may be similar to `[T; N]`, and may allow some transmutations,
70/// but references to `[T; N]` are not interchangeable with those to `Simd<T, N>`.
71/// Thus, when using `unsafe` Rust to read and write `Simd<T, N>` through [raw pointers], it is a good idea to first try with
72/// [`read_unaligned`] and [`write_unaligned`]. This is because:
73/// - [`read`] and [`write`] require full alignment (in this case, `Simd<T, N>`'s alignment)
74/// - `Simd<T, N>` is often read from or written to [`[T]`](slice) and other types aligned to `T`
75/// - combining these actions violates the `unsafe` contract and explodes the program into
76///   a puff of **undefined behavior**
77/// - the compiler can implicitly adjust layouts to make unaligned reads or writes fully aligned
78///   if it sees the optimization
79/// - most contemporary processors with "aligned" and "unaligned" read and write instructions
80///   exhibit no performance difference if the "unaligned" variant is aligned at runtime
81///
82/// Less obligations mean unaligned reads and writes are less likely to make the program unsound,
83/// and may be just as fast as stricter alternatives.
84/// When trying to guarantee alignment, [`[T]::as_simd`][as_simd] is an option for
85/// converting `[T]` to `[Simd<T, N>]`, and allows soundly operating on an aligned SIMD body,
86/// but it may cost more time when handling the scalar head and tail.
87/// If these are not enough, it is most ideal to design data structures to be already aligned
88/// to `align_of::<Simd<T, N>>()` before using `unsafe` Rust to read or write.
89/// Other ways to compensate for these facts, like materializing `Simd` to or from an array first,
90/// are handled by safe methods like [`Simd::from_array`] and [`Simd::from_slice`].
91///
92/// [`transmute`]: core::mem::transmute
93/// [raw pointers]: pointer
94/// [`read_unaligned`]: pointer::read_unaligned
95/// [`write_unaligned`]: pointer::write_unaligned
96/// [`read`]: pointer::read
97/// [`write`]: pointer::write
98/// [as_simd]: slice::as_simd
99//
100// NOTE: Accessing the inner array directly in any way (e.g. by using the `.0` field syntax) or
101// directly constructing an instance of the type (i.e. `let vector = Simd(array)`) should be
102// avoided, as it will likely become illegal on `#[repr(simd)]` structs in the future. It also
103// causes rustc to emit illegal LLVM IR in some cases.
104#[repr(simd, packed)]
105#[rustc_simd_monomorphize_lane_limit = "64"]
106pub struct Simd<T, const N: usize>([T; N])
107where
108    T: SimdElement;
109
110impl<T, const N: usize> Simd<T, N>
111where
112    T: SimdElement,
113{
114    /// Number of elements in this vector.
115    pub const LEN: usize = N;
116
117    /// Returns the number of elements in this SIMD vector.
118    ///
119    /// # Examples
120    ///
121    /// ```
122    /// # #![feature(portable_simd)]
123    /// # #[cfg(feature = "as_crate")] use core_simd::simd;
124    /// # #[cfg(not(feature = "as_crate"))] use core::simd;
125    /// # use simd::u32x4;
126    /// let v = u32x4::splat(0);
127    /// assert_eq!(v.len(), 4);
128    /// ```
129    #[inline]
130    #[allow(clippy::len_without_is_empty)]
131    pub const fn len(&self) -> usize {
132        Self::LEN
133    }
134
135    /// Constructs a new SIMD vector with all elements set to the given value.
136    ///
137    /// # Examples
138    ///
139    /// ```
140    /// # #![feature(portable_simd)]
141    /// # #[cfg(feature = "as_crate")] use core_simd::simd;
142    /// # #[cfg(not(feature = "as_crate"))] use core::simd;
143    /// # use simd::u32x4;
144    /// let v = u32x4::splat(8);
145    /// assert_eq!(v.as_array(), &[8, 8, 8, 8]);
146    /// ```
147    #[inline]
148    #[rustc_const_unstable(feature = "portable_simd", issue = "86656")]
149    pub const fn splat(value: T) -> Self {
150        const fn splat_const<T, const N: usize>(value: T) -> Simd<T, N>
151        where
152            T: SimdElement,
153        {
154            Simd::from_array([value; N])
155        }
156
157        fn splat_rt<T, const N: usize>(value: T) -> Simd<T, N>
158        where
159            T: SimdElement,
160        {
161            // This is preferred over `[value; N]`, since it's explicitly a splat:
162            // https://github.com/rust-lang/rust/issues/97804
163            struct Splat;
164            impl<const N: usize> Swizzle<N> for Splat {
165                const INDEX: [usize; N] = [0; N];
166            }
167
168            Splat::swizzle::<T, 1>(Simd::<T, 1>::from([value]))
169        }
170
171        core::intrinsics::const_eval_select((value,), splat_const, splat_rt)
172    }
173
174    /// Returns an array reference containing the entire SIMD vector.
175    ///
176    /// # Examples
177    ///
178    /// ```
179    /// # #![feature(portable_simd)]
180    /// # use core::simd::{Simd, u64x4};
181    /// let v: u64x4 = Simd::from_array([0, 1, 2, 3]);
182    /// assert_eq!(v.as_array(), &[0, 1, 2, 3]);
183    /// ```
184    #[inline]
185    pub const fn as_array(&self) -> &[T; N] {
186        // SAFETY: `Simd<T, N>` is just an overaligned `[T; N]` with
187        // potential padding at the end, so pointer casting to a
188        // `&[T; N]` is safe.
189        //
190        // NOTE: This deliberately doesn't just use `&self.0`, see the comment
191        // on the struct definition for details.
192        unsafe { &*(self as *const Self as *const [T; N]) }
193    }
194
195    /// Returns a mutable array reference containing the entire SIMD vector.
196    #[inline]
197    pub const fn as_mut_array(&mut self) -> &mut [T; N] {
198        // SAFETY: `Simd<T, N>` is just an overaligned `[T; N]` with
199        // potential padding at the end, so pointer casting to a
200        // `&mut [T; N]` is safe.
201        //
202        // NOTE: This deliberately doesn't just use `&mut self.0`, see the comment
203        // on the struct definition for details.
204        unsafe { &mut *(self as *mut Self as *mut [T; N]) }
205    }
206
207    /// Loads a vector from an array of `T`.
208    ///
209    /// This function is necessary since `repr(simd)` has padding for non-power-of-2 vectors (at the time of writing).
210    /// With padding, `read_unaligned` will read past the end of an array of N elements.
211    ///
212    /// # Safety
213    /// Reading `ptr` must be safe, as if by `<*const [T; N]>::read`.
214    #[inline]
215    const unsafe fn load(ptr: *const [T; N]) -> Self {
216        // There are potentially simpler ways to write this function, but this should result in
217        // LLVM `load <N x T>`
218
219        let mut tmp = core::mem::MaybeUninit::<Self>::uninit();
220        // SAFETY: `Simd<T, N>` always contains `N` elements of type `T`.  It may have padding
221        // which does not need to be initialized.  The safety of reading `ptr` is ensured by the
222        // caller.
223        unsafe {
224            core::ptr::copy_nonoverlapping(ptr, tmp.as_mut_ptr().cast(), 1);
225            tmp.assume_init()
226        }
227    }
228
229    /// Store a vector to an array of `T`.
230    ///
231    /// See `load` as to why this function is necessary.
232    ///
233    /// # Safety
234    /// Writing to `ptr` must be safe, as if by `<*mut [T; N]>::write`.
235    #[inline]
236    const unsafe fn store(self, ptr: *mut [T; N]) {
237        // There are potentially simpler ways to write this function, but this should result in
238        // LLVM `store <N x T>`
239
240        // Creating a temporary helps LLVM turn the memcpy into a store.
241        let tmp = self;
242        // SAFETY: `Simd<T, N>` always contains `N` elements of type `T`.  The safety of writing
243        // `ptr` is ensured by the caller.
244        unsafe { core::ptr::copy_nonoverlapping(tmp.as_array(), ptr, 1) }
245    }
246
247    /// Converts an array to a SIMD vector.
248    #[inline]
249    pub const fn from_array(array: [T; N]) -> Self {
250        // SAFETY: `&array` is safe to read.
251        //
252        // FIXME: We currently use a pointer load instead of `transmute_copy` because `repr(simd)`
253        // results in padding for non-power-of-2 vectors (so vectors are larger than arrays).
254        //
255        // NOTE: This deliberately doesn't just use `Self(array)`, see the comment
256        // on the struct definition for details.
257        unsafe { Self::load(&array) }
258    }
259
260    /// Converts a SIMD vector to an array.
261    #[inline]
262    pub const fn to_array(self) -> [T; N] {
263        let mut tmp = core::mem::MaybeUninit::uninit();
264        // SAFETY: writing to `tmp` is safe and initializes it.
265        //
266        // FIXME: We currently use a pointer store instead of `transmute_copy` because `repr(simd)`
267        // results in padding for non-power-of-2 vectors (so vectors are larger than arrays).
268        //
269        // NOTE: This deliberately doesn't just use `self.0`, see the comment
270        // on the struct definition for details.
271        unsafe {
272            self.store(tmp.as_mut_ptr());
273            tmp.assume_init()
274        }
275    }
276
277    /// Converts a slice to a SIMD vector containing `slice[..N]`.
278    ///
279    /// # Panics
280    ///
281    /// Panics if the slice's length is less than the vector's `Simd::N`.
282    /// Use `load_or_default` for an alternative that does not panic.
283    ///
284    /// # Example
285    ///
286    /// ```
287    /// # #![feature(portable_simd)]
288    /// # use core::simd::u32x4;
289    /// let source = vec![1, 2, 3, 4, 5, 6];
290    /// let v = u32x4::from_slice(&source);
291    /// assert_eq!(v.as_array(), &[1, 2, 3, 4]);
292    /// ```
293    #[must_use]
294    #[inline]
295    #[track_caller]
296    pub const fn from_slice(slice: &[T]) -> Self {
297        assert!(
298            slice.len() >= Self::LEN,
299            "slice length must be at least the number of elements"
300        );
301        // SAFETY: We just checked that the slice contains
302        // at least `N` elements.
303        unsafe { Self::load(slice.as_ptr().cast()) }
304    }
305
306    /// Writes a SIMD vector to the first `N` elements of a slice.
307    ///
308    /// # Panics
309    ///
310    /// Panics if the slice's length is less than the vector's `Simd::N`.
311    ///
312    /// # Example
313    ///
314    /// ```
315    /// # #![feature(portable_simd)]
316    /// # #[cfg(feature = "as_crate")] use core_simd::simd;
317    /// # #[cfg(not(feature = "as_crate"))] use core::simd;
318    /// # use simd::u32x4;
319    /// let mut dest = vec![0; 6];
320    /// let v = u32x4::from_array([1, 2, 3, 4]);
321    /// v.copy_to_slice(&mut dest);
322    /// assert_eq!(&dest, &[1, 2, 3, 4, 0, 0]);
323    /// ```
324    #[inline]
325    #[track_caller]
326    pub const fn copy_to_slice(self, slice: &mut [T]) {
327        assert!(
328            slice.len() >= Self::LEN,
329            "slice length must be at least the number of elements"
330        );
331        // SAFETY: We just checked that the slice contains
332        // at least `N` elements.
333        unsafe { self.store(slice.as_mut_ptr().cast()) }
334    }
335
336    /// Reads contiguous elements from `slice`. Elements are read so long as they're in-bounds for
337    /// the `slice`. Otherwise, the default value for the element type is returned.
338    ///
339    /// # Examples
340    /// ```
341    /// # #![feature(portable_simd)]
342    /// # #[cfg(feature = "as_crate")] use core_simd::simd;
343    /// # #[cfg(not(feature = "as_crate"))] use core::simd;
344    /// # use simd::Simd;
345    /// let vec: Vec<i32> = vec![10, 11];
346    ///
347    /// let result = Simd::<i32, 4>::load_or_default(&vec);
348    /// assert_eq!(result, Simd::from_array([10, 11, 0, 0]));
349    /// ```
350    #[must_use]
351    #[inline]
352    pub fn load_or_default(slice: &[T]) -> Self
353    where
354        T: Default,
355    {
356        Self::load_or(slice, Default::default())
357    }
358
359    /// Reads contiguous elements from `slice`. Elements are read so long as they're in-bounds for
360    /// the `slice`. Otherwise, the corresponding value from `or` is passed through.
361    ///
362    /// # Examples
363    /// ```
364    /// # #![feature(portable_simd)]
365    /// # #[cfg(feature = "as_crate")] use core_simd::simd;
366    /// # #[cfg(not(feature = "as_crate"))] use core::simd;
367    /// # use simd::Simd;
368    /// let vec: Vec<i32> = vec![10, 11];
369    /// let or = Simd::from_array([-5, -4, -3, -2]);
370    ///
371    /// let result = Simd::load_or(&vec, or);
372    /// assert_eq!(result, Simd::from_array([10, 11, -3, -2]));
373    /// ```
374    #[must_use]
375    #[inline]
376    pub fn load_or(slice: &[T], or: Self) -> Self {
377        Self::load_select(slice, Mask::splat(true), or)
378    }
379
380    /// Reads contiguous elements from `slice`. Each element is read from memory if its
381    /// corresponding element in `enable` is `true`.
382    ///
383    /// When the element is disabled or out of bounds for the slice, that memory location
384    /// is not accessed and the corresponding value from `or` is passed through.
385    ///
386    /// # Examples
387    /// ```
388    /// # #![feature(portable_simd)]
389    /// # #[cfg(feature = "as_crate")] use core_simd::simd;
390    /// # #[cfg(not(feature = "as_crate"))] use core::simd;
391    /// # use simd::{Simd, Mask};
392    /// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
393    /// let enable = Mask::from_array([true, true, false, true]);
394    /// let or = Simd::from_array([-5, -4, -3, -2]);
395    ///
396    /// let result = Simd::load_select(&vec, enable, or);
397    /// assert_eq!(result, Simd::from_array([10, 11, -3, 13]));
398    /// ```
399    #[must_use]
400    #[inline]
401    pub fn load_select_or_default(slice: &[T], enable: Mask<<T as SimdElement>::Mask, N>) -> Self
402    where
403        T: Default,
404    {
405        Self::load_select(slice, enable, Default::default())
406    }
407
408    /// Reads contiguous elements from `slice`. Each element is read from memory if its
409    /// corresponding element in `enable` is `true`.
410    ///
411    /// When the element is disabled or out of bounds for the slice, that memory location
412    /// is not accessed and the corresponding value from `or` is passed through.
413    ///
414    /// # Examples
415    /// ```
416    /// # #![feature(portable_simd)]
417    /// # #[cfg(feature = "as_crate")] use core_simd::simd;
418    /// # #[cfg(not(feature = "as_crate"))] use core::simd;
419    /// # use simd::{Simd, Mask};
420    /// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
421    /// let enable = Mask::from_array([true, true, false, true]);
422    /// let or = Simd::from_array([-5, -4, -3, -2]);
423    ///
424    /// let result = Simd::load_select(&vec, enable, or);
425    /// assert_eq!(result, Simd::from_array([10, 11, -3, 13]));
426    /// ```
427    #[must_use]
428    #[inline]
429    pub fn load_select(
430        slice: &[T],
431        mut enable: Mask<<T as SimdElement>::Mask, N>,
432        or: Self,
433    ) -> Self {
434        enable &= mask_up_to(slice.len());
435        // SAFETY: We performed the bounds check by updating the mask. &[T] is properly aligned to
436        // the element.
437        unsafe { Self::load_select_ptr(slice.as_ptr(), enable, or) }
438    }
439
440    /// Reads contiguous elements from `slice`. Each element is read from memory if its
441    /// corresponding element in `enable` is `true`.
442    ///
443    /// When the element is disabled, that memory location is not accessed and the corresponding
444    /// value from `or` is passed through.
445    ///
446    /// # Safety
447    /// Enabled loads must not exceed the length of `slice`.
448    #[must_use]
449    #[inline]
450    pub unsafe fn load_select_unchecked(
451        slice: &[T],
452        enable: Mask<<T as SimdElement>::Mask, N>,
453        or: Self,
454    ) -> Self {
455        let ptr = slice.as_ptr();
456        // SAFETY: The safety of reading elements from `slice` is ensured by the caller.
457        unsafe { Self::load_select_ptr(ptr, enable, or) }
458    }
459
460    /// Reads contiguous elements starting at `ptr`. Each element is read from memory if its
461    /// corresponding element in `enable` is `true`.
462    ///
463    /// When the element is disabled, that memory location is not accessed and the corresponding
464    /// value from `or` is passed through.
465    ///
466    /// # Safety
467    /// Enabled `ptr` elements must be safe to read as if by `std::ptr::read`.
468    #[must_use]
469    #[inline]
470    pub unsafe fn load_select_ptr(
471        ptr: *const T,
472        enable: Mask<<T as SimdElement>::Mask, N>,
473        or: Self,
474    ) -> Self {
475        // SAFETY: The safety of reading elements through `ptr` is ensured by the caller.
476        unsafe { core::intrinsics::simd::simd_masked_load(enable.to_simd(), ptr, or) }
477    }
478
479    /// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector.
480    /// If an index is out-of-bounds, the element is instead selected from the `or` vector.
481    ///
482    /// # Examples
483    /// ```
484    /// # #![feature(portable_simd)]
485    /// # use core::simd::Simd;
486    /// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
487    /// let idxs = Simd::from_array([9, 3, 0, 5]);  // Note the index that is out-of-bounds
488    /// let alt = Simd::from_array([-5, -4, -3, -2]);
489    ///
490    /// let result = Simd::gather_or(&vec, idxs, alt);
491    /// assert_eq!(result, Simd::from_array([-5, 13, 10, 15]));
492    /// ```
493    #[must_use]
494    #[inline]
495    pub fn gather_or(slice: &[T], idxs: Simd<usize, N>, or: Self) -> Self {
496        Self::gather_select(slice, Mask::splat(true), idxs, or)
497    }
498
499    /// Reads from indices in `slice` to construct a SIMD vector.
500    /// If an index is out-of-bounds, the element is set to the default given by `T: Default`.
501    ///
502    /// # Examples
503    /// ```
504    /// # #![feature(portable_simd)]
505    /// # use core::simd::Simd;
506    /// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
507    /// let idxs = Simd::from_array([9, 3, 0, 5]);  // Note the index that is out-of-bounds
508    ///
509    /// let result = Simd::gather_or_default(&vec, idxs);
510    /// assert_eq!(result, Simd::from_array([0, 13, 10, 15]));
511    /// ```
512    #[must_use]
513    #[inline]
514    pub fn gather_or_default(slice: &[T], idxs: Simd<usize, N>) -> Self
515    where
516        T: Default,
517    {
518        Self::gather_or(slice, idxs, Self::splat(T::default()))
519    }
520
521    /// Reads from indices in `slice` to construct a SIMD vector.
522    /// The mask `enable`s all `true` indices and disables all `false` indices.
523    /// If an index is disabled or is out-of-bounds, the element is selected from the `or` vector.
524    ///
525    /// # Examples
526    /// ```
527    /// # #![feature(portable_simd)]
528    /// # use core::simd::{Simd, Mask};
529    /// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
530    /// let idxs = Simd::from_array([9, 3, 0, 5]); // Includes an out-of-bounds index
531    /// let alt = Simd::from_array([-5, -4, -3, -2]);
532    /// let enable = Mask::from_array([true, true, true, false]); // Includes a masked element
533    ///
534    /// let result = Simd::gather_select(&vec, enable, idxs, alt);
535    /// assert_eq!(result, Simd::from_array([-5, 13, 10, -2]));
536    /// ```
537    #[must_use]
538    #[inline]
539    pub fn gather_select(
540        slice: &[T],
541        enable: Mask<isize, N>,
542        idxs: Simd<usize, N>,
543        or: Self,
544    ) -> Self {
545        let enable: Mask<isize, N> = enable & idxs.simd_lt(Simd::splat(slice.len()));
546        // Safety: We have masked-off out-of-bounds indices.
547        unsafe { Self::gather_select_unchecked(slice, enable, idxs, or) }
548    }
549
550    /// Reads from indices in `slice` to construct a SIMD vector.
551    /// The mask `enable`s all `true` indices and disables all `false` indices.
552    /// If an index is disabled, the element is selected from the `or` vector.
553    ///
554    /// # Safety
555    ///
556    /// Calling this function with an `enable`d out-of-bounds index is *[undefined behavior]*
557    /// even if the resulting value is not used.
558    ///
559    /// # Examples
560    /// ```
561    /// # #![feature(portable_simd)]
562    /// # #[cfg(feature = "as_crate")] use core_simd::simd;
563    /// # #[cfg(not(feature = "as_crate"))] use core::simd;
564    /// # use simd::{Simd, cmp::SimdPartialOrd, Mask};
565    /// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
566    /// let idxs = Simd::from_array([9, 3, 0, 5]); // Includes an out-of-bounds index
567    /// let alt = Simd::from_array([-5, -4, -3, -2]);
568    /// let enable = Mask::from_array([true, true, true, false]); // Includes a masked element
569    /// // If this mask was used to gather, it would be unsound. Let's fix that.
570    /// let enable = enable & idxs.simd_lt(Simd::splat(vec.len()));
571    ///
572    /// // The out-of-bounds index has been masked, so it's safe to gather now.
573    /// let result = unsafe { Simd::gather_select_unchecked(&vec, enable, idxs, alt) };
574    /// assert_eq!(result, Simd::from_array([-5, 13, 10, -2]));
575    /// ```
576    /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
577    #[must_use]
578    #[inline]
579    #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
580    pub unsafe fn gather_select_unchecked(
581        slice: &[T],
582        enable: Mask<isize, N>,
583        idxs: Simd<usize, N>,
584        or: Self,
585    ) -> Self {
586        let base_ptr = Simd::<*const T, N>::splat(slice.as_ptr());
587        // Ferris forgive me, I have done pointer arithmetic here.
588        let ptrs = base_ptr.wrapping_add(idxs);
589        // Safety: The caller is responsible for determining the indices are okay to read
590        unsafe { Self::gather_select_ptr(ptrs, enable, or) }
591    }
592
593    /// Reads elementwise from pointers into a SIMD vector.
594    ///
595    /// # Safety
596    ///
597    /// Each read must satisfy the same conditions as [`core::ptr::read`].
598    ///
599    /// # Example
600    /// ```
601    /// # #![feature(portable_simd)]
602    /// # #[cfg(feature = "as_crate")] use core_simd::simd;
603    /// # #[cfg(not(feature = "as_crate"))] use core::simd;
604    /// # use simd::prelude::*;
605    /// let values = [6, 2, 4, 9];
606    /// let offsets = Simd::from_array([1, 0, 0, 3]);
607    /// let source = Simd::splat(values.as_ptr()).wrapping_add(offsets);
608    /// let gathered = unsafe { Simd::gather_ptr(source) };
609    /// assert_eq!(gathered, Simd::from_array([2, 6, 6, 9]));
610    /// ```
611    #[must_use]
612    #[inline]
613    #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
614    pub unsafe fn gather_ptr(source: Simd<*const T, N>) -> Self
615    where
616        T: Default,
617    {
618        // TODO: add an intrinsic that doesn't use a passthru vector, and remove the T: Default bound
619        // Safety: The caller is responsible for upholding all invariants
620        unsafe { Self::gather_select_ptr(source, Mask::splat(true), Self::default()) }
621    }
622
623    /// Conditionally read elementwise from pointers into a SIMD vector.
624    /// The mask `enable`s all `true` pointers and disables all `false` pointers.
625    /// If a pointer is disabled, the element is selected from the `or` vector,
626    /// and no read is performed.
627    ///
628    /// # Safety
629    ///
630    /// Enabled elements must satisfy the same conditions as [`core::ptr::read`].
631    ///
632    /// # Example
633    /// ```
634    /// # #![feature(portable_simd)]
635    /// # #[cfg(feature = "as_crate")] use core_simd::simd;
636    /// # #[cfg(not(feature = "as_crate"))] use core::simd;
637    /// # use simd::prelude::*;
638    /// let values = [6, 2, 4, 9];
639    /// let enable = Mask::from_array([true, true, false, true]);
640    /// let offsets = Simd::from_array([1, 0, 0, 3]);
641    /// let source = Simd::splat(values.as_ptr()).wrapping_add(offsets);
642    /// let gathered = unsafe { Simd::gather_select_ptr(source, enable, Simd::splat(0)) };
643    /// assert_eq!(gathered, Simd::from_array([2, 6, 0, 9]));
644    /// ```
645    #[must_use]
646    #[inline]
647    #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
648    pub unsafe fn gather_select_ptr(
649        source: Simd<*const T, N>,
650        enable: Mask<isize, N>,
651        or: Self,
652    ) -> Self {
653        // Safety: The caller is responsible for upholding all invariants
654        unsafe { core::intrinsics::simd::simd_gather(or, source, enable.to_simd()) }
655    }
656
657    /// Conditionally write contiguous elements to `slice`. The `enable` mask controls
658    /// which elements are written, as long as they're in-bounds of the `slice`.
659    /// If the element is disabled or out of bounds, no memory access to that location
660    /// is made.
661    ///
662    /// # Examples
663    /// ```
664    /// # #![feature(portable_simd)]
665    /// # #[cfg(feature = "as_crate")] use core_simd::simd;
666    /// # #[cfg(not(feature = "as_crate"))] use core::simd;
667    /// # use simd::{Simd, Mask};
668    /// let mut arr = [0i32; 4];
669    /// let write = Simd::from_array([-5, -4, -3, -2]);
670    /// let enable = Mask::from_array([false, true, true, true]);
671    ///
672    /// write.store_select(&mut arr[..3], enable);
673    /// assert_eq!(arr, [0, -4, -3, 0]);
674    /// ```
675    #[inline]
676    pub fn store_select(self, slice: &mut [T], mut enable: Mask<<T as SimdElement>::Mask, N>) {
677        enable &= mask_up_to(slice.len());
678        // SAFETY: We performed the bounds check by updating the mask. &[T] is properly aligned to
679        // the element.
680        unsafe { self.store_select_ptr(slice.as_mut_ptr(), enable) }
681    }
682
683    /// Conditionally write contiguous elements to `slice`. The `enable` mask controls
684    /// which elements are written.
685    ///
686    /// # Safety
687    ///
688    /// Every enabled element must be in bounds for the `slice`.
689    ///
690    /// # Examples
691    /// ```
692    /// # #![feature(portable_simd)]
693    /// # #[cfg(feature = "as_crate")] use core_simd::simd;
694    /// # #[cfg(not(feature = "as_crate"))] use core::simd;
695    /// # use simd::{Simd, Mask};
696    /// let mut arr = [0i32; 4];
697    /// let write = Simd::from_array([-5, -4, -3, -2]);
698    /// let enable = Mask::from_array([false, true, true, true]);
699    ///
700    /// unsafe { write.store_select_unchecked(&mut arr, enable) };
701    /// assert_eq!(arr, [0, -4, -3, -2]);
702    /// ```
703    #[inline]
704    pub unsafe fn store_select_unchecked(
705        self,
706        slice: &mut [T],
707        enable: Mask<<T as SimdElement>::Mask, N>,
708    ) {
709        let ptr = slice.as_mut_ptr();
710        // SAFETY: The safety of writing elements in `slice` is ensured by the caller.
711        unsafe { self.store_select_ptr(ptr, enable) }
712    }
713
714    /// Conditionally write contiguous elements starting from `ptr`.
715    /// The `enable` mask controls which elements are written.
716    /// When disabled, the memory location corresponding to that element is not accessed.
717    ///
718    /// # Safety
719    ///
720    /// Memory addresses for element are calculated [`pointer::wrapping_offset`] and
721    /// each enabled element must satisfy the same conditions as [`core::ptr::write`].
722    #[inline]
723    pub unsafe fn store_select_ptr(self, ptr: *mut T, enable: Mask<<T as SimdElement>::Mask, N>) {
724        // SAFETY: The safety of writing elements through `ptr` is ensured by the caller.
725        unsafe { core::intrinsics::simd::simd_masked_store(enable.to_simd(), ptr, self) }
726    }
727
728    /// Writes the values in a SIMD vector to potentially discontiguous indices in `slice`.
729    /// If an index is out-of-bounds, the write is suppressed without panicking.
730    /// If two elements in the scattered vector would write to the same index
731    /// only the last element is guaranteed to actually be written.
732    ///
733    /// # Examples
734    /// ```
735    /// # #![feature(portable_simd)]
736    /// # use core::simd::Simd;
737    /// let mut vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
738    /// let idxs = Simd::from_array([9, 3, 0, 0]); // Note the duplicate index.
739    /// let vals = Simd::from_array([-27, 82, -41, 124]);
740    ///
741    /// vals.scatter(&mut vec, idxs); // two logical writes means the last wins.
742    /// assert_eq!(vec, vec![124, 11, 12, 82, 14, 15, 16, 17, 18]);
743    /// ```
744    #[inline]
745    pub fn scatter(self, slice: &mut [T], idxs: Simd<usize, N>) {
746        self.scatter_select(slice, Mask::splat(true), idxs)
747    }
748
749    /// Writes values from a SIMD vector to multiple potentially discontiguous indices in `slice`.
750    /// The mask `enable`s all `true` indices and disables all `false` indices.
751    /// If an enabled index is out-of-bounds, the write is suppressed without panicking.
752    /// If two enabled elements in the scattered vector would write to the same index,
753    /// only the last element is guaranteed to actually be written.
754    ///
755    /// # Examples
756    /// ```
757    /// # #![feature(portable_simd)]
758    /// # #[cfg(feature = "as_crate")] use core_simd::simd;
759    /// # #[cfg(not(feature = "as_crate"))] use core::simd;
760    /// # use simd::{Simd, Mask};
761    /// let mut vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
762    /// let idxs = Simd::from_array([9, 3, 0, 0]); // Includes an out-of-bounds index
763    /// let vals = Simd::from_array([-27, 82, -41, 124]);
764    /// let enable = Mask::from_array([true, true, true, false]); // Includes a masked element
765    ///
766    /// vals.scatter_select(&mut vec, enable, idxs); // The last write is masked, thus omitted.
767    /// assert_eq!(vec, vec![-41, 11, 12, 82, 14, 15, 16, 17, 18]);
768    /// ```
769    #[inline]
770    pub fn scatter_select(self, slice: &mut [T], enable: Mask<isize, N>, idxs: Simd<usize, N>) {
771        let enable: Mask<isize, N> = enable & idxs.simd_lt(Simd::splat(slice.len()));
772        // Safety: We have masked-off out-of-bounds indices.
773        unsafe { self.scatter_select_unchecked(slice, enable, idxs) }
774    }
775
776    /// Writes values from a SIMD vector to multiple potentially discontiguous indices in `slice`.
777    /// The mask `enable`s all `true` indices and disables all `false` indices.
778    /// If two enabled elements in the scattered vector would write to the same index,
779    /// only the last element is guaranteed to actually be written.
780    ///
781    /// # Safety
782    ///
783    /// Calling this function with an enabled out-of-bounds index is *[undefined behavior]*,
784    /// and may lead to memory corruption.
785    ///
786    /// # Examples
787    /// ```
788    /// # #![feature(portable_simd)]
789    /// # #[cfg(feature = "as_crate")] use core_simd::simd;
790    /// # #[cfg(not(feature = "as_crate"))] use core::simd;
791    /// # use simd::{Simd, cmp::SimdPartialOrd, Mask};
792    /// let mut vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
793    /// let idxs = Simd::from_array([9, 3, 0, 0]);
794    /// let vals = Simd::from_array([-27, 82, -41, 124]);
795    /// let enable = Mask::from_array([true, true, true, false]); // Masks the final index
796    /// // If this mask was used to scatter, it would be unsound. Let's fix that.
797    /// let enable = enable & idxs.simd_lt(Simd::splat(vec.len()));
798    ///
799    /// // We have masked the OOB index, so it's safe to scatter now.
800    /// unsafe { vals.scatter_select_unchecked(&mut vec, enable, idxs); }
801    /// // The second write to index 0 was masked, thus omitted.
802    /// assert_eq!(vec, vec![-41, 11, 12, 82, 14, 15, 16, 17, 18]);
803    /// ```
804    /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
805    #[inline]
806    #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
807    pub unsafe fn scatter_select_unchecked(
808        self,
809        slice: &mut [T],
810        enable: Mask<isize, N>,
811        idxs: Simd<usize, N>,
812    ) {
813        // Safety: This block works with *mut T derived from &mut 'a [T],
814        // which means it is delicate in Rust's borrowing model, circa 2021:
815        // &mut 'a [T] asserts uniqueness, so deriving &'a [T] invalidates live *mut Ts!
816        // Even though this block is largely safe methods, it must be exactly this way
817        // to prevent invalidating the raw ptrs while they're live.
818        // Thus, entering this block requires all values to use being already ready:
819        // 0. idxs we want to write to, which are used to construct the mask.
820        // 1. enable, which depends on an initial &'a [T] and the idxs.
821        // 2. actual values to scatter (self).
822        // 3. &mut [T] which will become our base ptr.
823        unsafe {
824            // Now Entering ☢️ *mut T Zone
825            let base_ptr = Simd::<*mut T, N>::splat(slice.as_mut_ptr());
826            // Ferris forgive me, I have done pointer arithmetic here.
827            let ptrs = base_ptr.wrapping_add(idxs);
828            // The ptrs have been bounds-masked to prevent memory-unsafe writes insha'allah
829            self.scatter_select_ptr(ptrs, enable);
830            // Cleared ☢️ *mut T Zone
831        }
832    }
833
834    /// Writes pointers elementwise into a SIMD vector.
835    ///
836    /// # Safety
837    ///
838    /// Each write must satisfy the same conditions as [`core::ptr::write`].
839    ///
840    /// # Example
841    /// ```
842    /// # #![feature(portable_simd)]
843    /// # #[cfg(feature = "as_crate")] use core_simd::simd;
844    /// # #[cfg(not(feature = "as_crate"))] use core::simd;
845    /// # use simd::{Simd, ptr::SimdMutPtr};
846    /// let mut values = [0; 4];
847    /// let offset = Simd::from_array([3, 2, 1, 0]);
848    /// let ptrs = Simd::splat(values.as_mut_ptr()).wrapping_add(offset);
849    /// unsafe { Simd::from_array([6, 3, 5, 7]).scatter_ptr(ptrs); }
850    /// assert_eq!(values, [7, 5, 3, 6]);
851    /// ```
852    #[inline]
853    #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
854    pub unsafe fn scatter_ptr(self, dest: Simd<*mut T, N>) {
855        // Safety: The caller is responsible for upholding all invariants
856        unsafe { self.scatter_select_ptr(dest, Mask::splat(true)) }
857    }
858
859    /// Conditionally write pointers elementwise into a SIMD vector.
860    /// The mask `enable`s all `true` pointers and disables all `false` pointers.
861    /// If a pointer is disabled, the write to its pointee is skipped.
862    ///
863    /// # Safety
864    ///
865    /// Enabled pointers must satisfy the same conditions as [`core::ptr::write`].
866    ///
867    /// # Example
868    /// ```
869    /// # #![feature(portable_simd)]
870    /// # #[cfg(feature = "as_crate")] use core_simd::simd;
871    /// # #[cfg(not(feature = "as_crate"))] use core::simd;
872    /// # use simd::{Mask, Simd, ptr::SimdMutPtr};
873    /// let mut values = [0; 4];
874    /// let offset = Simd::from_array([3, 2, 1, 0]);
875    /// let ptrs = Simd::splat(values.as_mut_ptr()).wrapping_add(offset);
876    /// let enable = Mask::from_array([true, true, false, false]);
877    /// unsafe { Simd::from_array([6, 3, 5, 7]).scatter_select_ptr(ptrs, enable); }
878    /// assert_eq!(values, [0, 0, 3, 6]);
879    /// ```
880    #[inline]
881    #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
882    pub unsafe fn scatter_select_ptr(self, dest: Simd<*mut T, N>, enable: Mask<isize, N>) {
883        // Safety: The caller is responsible for upholding all invariants
884        unsafe { core::intrinsics::simd::simd_scatter(self, dest, enable.to_simd()) }
885    }
886}
887
888impl<T, const N: usize> Copy for Simd<T, N> where T: SimdElement {}
889
890impl<T, const N: usize> Clone for Simd<T, N>
891where
892    T: SimdElement,
893{
894    #[inline]
895    fn clone(&self) -> Self {
896        *self
897    }
898}
899
900impl<T, const N: usize> Default for Simd<T, N>
901where
902    T: SimdElement + Default,
903{
904    #[inline]
905    fn default() -> Self {
906        Self::splat(T::default())
907    }
908}
909
910impl<T, const N: usize> PartialEq for Simd<T, N>
911where
912    T: SimdElement + PartialEq,
913{
914    #[inline]
915    fn eq(&self, other: &Self) -> bool {
916        // Safety: All SIMD vectors are SimdPartialEq, and the comparison produces a valid mask.
917        let mask = unsafe {
918            let tfvec: Simd<<T as SimdElement>::Mask, N> =
919                core::intrinsics::simd::simd_eq(*self, *other);
920            Mask::from_simd_unchecked(tfvec)
921        };
922
923        // Two vectors are equal if all elements are equal when compared elementwise
924        mask.all()
925    }
926
927    #[allow(clippy::partialeq_ne_impl)]
928    #[inline]
929    fn ne(&self, other: &Self) -> bool {
930        // Safety: All SIMD vectors are SimdPartialEq, and the comparison produces a valid mask.
931        let mask = unsafe {
932            let tfvec: Simd<<T as SimdElement>::Mask, N> =
933                core::intrinsics::simd::simd_ne(*self, *other);
934            Mask::from_simd_unchecked(tfvec)
935        };
936
937        // Two vectors are non-equal if any elements are non-equal when compared elementwise
938        mask.any()
939    }
940}
941
942/// Lexicographic order. For the SIMD elementwise minimum and maximum, use simd_min and simd_max instead.
943impl<T, const N: usize> PartialOrd for Simd<T, N>
944where
945    T: SimdElement + PartialOrd,
946{
947    #[inline]
948    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
949        // TODO use SIMD equality
950        self.to_array().partial_cmp(other.as_ref())
951    }
952}
953
954impl<T, const N: usize> Eq for Simd<T, N> where T: SimdElement + Eq {}
955
956/// Lexicographic order. For the SIMD elementwise minimum and maximum, use simd_min and simd_max instead.
957impl<T, const N: usize> Ord for Simd<T, N>
958where
959    T: SimdElement + Ord,
960{
961    #[inline]
962    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
963        // TODO use SIMD equality
964        self.to_array().cmp(other.as_ref())
965    }
966}
967
968impl<T, const N: usize> core::hash::Hash for Simd<T, N>
969where
970    T: SimdElement + core::hash::Hash,
971{
972    #[inline]
973    fn hash<H>(&self, state: &mut H)
974    where
975        H: core::hash::Hasher,
976    {
977        self.as_array().hash(state)
978    }
979}
980
981// array references
982impl<T, const N: usize> AsRef<[T; N]> for Simd<T, N>
983where
984    T: SimdElement,
985{
986    #[inline]
987    fn as_ref(&self) -> &[T; N] {
988        self.as_array()
989    }
990}
991
992impl<T, const N: usize> AsMut<[T; N]> for Simd<T, N>
993where
994    T: SimdElement,
995{
996    #[inline]
997    fn as_mut(&mut self) -> &mut [T; N] {
998        self.as_mut_array()
999    }
1000}
1001
1002// slice references
1003impl<T, const N: usize> AsRef<[T]> for Simd<T, N>
1004where
1005    T: SimdElement,
1006{
1007    #[inline]
1008    fn as_ref(&self) -> &[T] {
1009        self.as_array()
1010    }
1011}
1012
1013impl<T, const N: usize> AsMut<[T]> for Simd<T, N>
1014where
1015    T: SimdElement,
1016{
1017    #[inline]
1018    fn as_mut(&mut self) -> &mut [T] {
1019        self.as_mut_array()
1020    }
1021}
1022
1023// vector/array conversion
1024impl<T, const N: usize> From<[T; N]> for Simd<T, N>
1025where
1026    T: SimdElement,
1027{
1028    #[inline]
1029    fn from(array: [T; N]) -> Self {
1030        Self::from_array(array)
1031    }
1032}
1033
1034impl<T, const N: usize> From<Simd<T, N>> for [T; N]
1035where
1036    T: SimdElement,
1037{
1038    #[inline]
1039    fn from(vector: Simd<T, N>) -> Self {
1040        vector.to_array()
1041    }
1042}
1043
1044impl<T, const N: usize> TryFrom<&[T]> for Simd<T, N>
1045where
1046    T: SimdElement,
1047{
1048    type Error = core::array::TryFromSliceError;
1049
1050    #[inline]
1051    fn try_from(slice: &[T]) -> Result<Self, core::array::TryFromSliceError> {
1052        Ok(Self::from_array(slice.try_into()?))
1053    }
1054}
1055
1056impl<T, const N: usize> TryFrom<&mut [T]> for Simd<T, N>
1057where
1058    T: SimdElement,
1059{
1060    type Error = core::array::TryFromSliceError;
1061
1062    #[inline]
1063    fn try_from(slice: &mut [T]) -> Result<Self, core::array::TryFromSliceError> {
1064        Ok(Self::from_array(slice.try_into()?))
1065    }
1066}
1067
1068mod sealed {
1069    pub trait Sealed {}
1070}
1071use sealed::Sealed;
1072
1073/// Marker trait for types that may be used as SIMD vector elements.
1074///
1075/// # Safety
1076/// This trait, when implemented, asserts the compiler can monomorphize
1077/// `#[repr(simd)]` structs with the marked type as an element.
1078/// Strictly, it is valid to impl if the vector will not be miscompiled.
1079/// Practically, it is user-unfriendly to impl it if the vector won't compile,
1080/// even when no soundness guarantees are broken by allowing the user to try.
1081pub unsafe trait SimdElement: Sealed + Copy {
1082    /// The mask element type corresponding to this element type.
1083    type Mask: MaskElement;
1084}
1085
1086impl Sealed for u8 {}
1087
1088// Safety: u8 is a valid SIMD element type, and is supported by this API
1089unsafe impl SimdElement for u8 {
1090    type Mask = i8;
1091}
1092
1093impl Sealed for u16 {}
1094
1095// Safety: u16 is a valid SIMD element type, and is supported by this API
1096unsafe impl SimdElement for u16 {
1097    type Mask = i16;
1098}
1099
1100impl Sealed for u32 {}
1101
1102// Safety: u32 is a valid SIMD element type, and is supported by this API
1103unsafe impl SimdElement for u32 {
1104    type Mask = i32;
1105}
1106
1107impl Sealed for u64 {}
1108
1109// Safety: u64 is a valid SIMD element type, and is supported by this API
1110unsafe impl SimdElement for u64 {
1111    type Mask = i64;
1112}
1113
1114impl Sealed for usize {}
1115
1116// Safety: usize is a valid SIMD element type, and is supported by this API
1117unsafe impl SimdElement for usize {
1118    type Mask = isize;
1119}
1120
1121impl Sealed for i8 {}
1122
1123// Safety: i8 is a valid SIMD element type, and is supported by this API
1124unsafe impl SimdElement for i8 {
1125    type Mask = i8;
1126}
1127
1128impl Sealed for i16 {}
1129
1130// Safety: i16 is a valid SIMD element type, and is supported by this API
1131unsafe impl SimdElement for i16 {
1132    type Mask = i16;
1133}
1134
1135impl Sealed for i32 {}
1136
1137// Safety: i32 is a valid SIMD element type, and is supported by this API
1138unsafe impl SimdElement for i32 {
1139    type Mask = i32;
1140}
1141
1142impl Sealed for i64 {}
1143
1144// Safety: i64 is a valid SIMD element type, and is supported by this API
1145unsafe impl SimdElement for i64 {
1146    type Mask = i64;
1147}
1148
1149impl Sealed for isize {}
1150
1151// Safety: isize is a valid SIMD element type, and is supported by this API
1152unsafe impl SimdElement for isize {
1153    type Mask = isize;
1154}
1155
1156impl Sealed for f32 {}
1157
1158// Safety: f32 is a valid SIMD element type, and is supported by this API
1159unsafe impl SimdElement for f32 {
1160    type Mask = i32;
1161}
1162
1163impl Sealed for f64 {}
1164
1165// Safety: f64 is a valid SIMD element type, and is supported by this API
1166unsafe impl SimdElement for f64 {
1167    type Mask = i64;
1168}
1169
1170impl<T> Sealed for *const T {}
1171
1172// Safety: (thin) const pointers are valid SIMD element types, and are supported by this API
1173//
1174// Fat pointers may be supported in the future.
1175unsafe impl<T> SimdElement for *const T
1176where
1177    T: core::ptr::Pointee<Metadata = ()>,
1178{
1179    type Mask = isize;
1180}
1181
1182impl<T> Sealed for *mut T {}
1183
1184// Safety: (thin) mut pointers are valid SIMD element types, and are supported by this API
1185//
1186// Fat pointers may be supported in the future.
1187unsafe impl<T> SimdElement for *mut T
1188where
1189    T: core::ptr::Pointee<Metadata = ()>,
1190{
1191    type Mask = isize;
1192}
1193
1194#[inline]
1195fn lane_indices<const N: usize>() -> Simd<usize, N> {
1196    #![allow(clippy::needless_range_loop)]
1197    let mut index = [0; N];
1198    for i in 0..N {
1199        index[i] = i;
1200    }
1201    Simd::from_array(index)
1202}
1203
1204#[inline]
1205fn mask_up_to<M, const N: usize>(len: usize) -> Mask<M, N>
1206where
1207    M: MaskElement,
1208{
1209    let index = lane_indices::<N>();
1210    let max_value: u64 = M::max_unsigned();
1211    macro_rules! case {
1212        ($ty:ty) => {
1213            if N < <$ty>::MAX as usize && max_value as $ty as u64 == max_value {
1214                return index.cast().simd_lt(Simd::splat(len.min(N) as $ty)).cast();
1215            }
1216        };
1217    }
1218    case!(u8);
1219    case!(u16);
1220    case!(u32);
1221    case!(u64);
1222    index.simd_lt(Simd::splat(len)).cast()
1223}