thermite/vector/mod.rs
1#![warn(missing_docs)]
2#![allow(clippy::missing_safety_doc)]
3#![deny(unconditional_recursion)] // just in case we miss one
4
5//! User-facing vector types and the trait hierarchy that defines them.
6//!
7//! This module is the top of Thermite's public API. It provides the
8//! [`Vector<R>`] newtype - the value you actually compute with - and the tower
9//! of traits ([`GenericVector`] and its descendants) that describe what a
10//! vector can do.
11//!
12//! # Generic over *behavior*, not over a backend
13//!
14//! The central idea of Thermite is that you write code against
15//! [`GenericVector`] (or a more specific trait like [`NumericVector`],
16//! [`FloatVector`], or [`IntegerVector`]) and let the caller pick the concrete
17//! type. That concrete type decides the ISA, the lane count, and the element
18//! type - your code does not name any of them:
19//!
20//! ```
21//! use thermite::prelude::*;
22//! use thermite::math::TranscendentalMath;
23//!
24//! // Works on any backend, any width, any float element type.
25//! fn gaussian<V: FloatVector + TranscendentalMath>(v: V) -> V {
26//! (-v * v).exp()
27//! }
28//! ```
29//!
30//! Crucially, "generic" here is stronger than "generic over the hardware
31//! backend". A [`GenericVector`] is not required to be a dense array of scalars
32//! sitting in a hardware register at all. The trait describes an *algebra of
33//! lanes*, and anything that satisfies that algebra is a first-class vector.
34//!
35//! # Composable abstractions all the way up
36//!
37//! Because the trait bounds are the only contract, wrapper types that are not
38//! SIMD registers in any conventional sense can still implement the hierarchy
39//! and flow through the very same generic functions:
40//!
41//! - **Complex numbers** - a `Complex<V>` pairing two real vectors implements
42//! the [`GenericVector`]/[`FloatVector`] traits, so a function written for
43//! real `FloatVector`s operates transparently on complex data.
44//! - **Compensated arithmetic** - a double-double `Compensated<V>` that tracks
45//! rounding error implements the same traits; existing generic code gains
46//! extended precision just by being instantiated with it.
47//! - **Dual / hyperdual numbers** - automatic differentiation via the same
48//! trait composition, so a generic numeric routine differentiates itself when
49//! handed a dual type.
50//!
51//! And these compose: `Complex<Compensated<f32x8>>` is a perfectly valid vector
52//! type where every complex operation is carried out in compensated real
53//! arithmetic, all still SIMD-accelerated underneath. The function you wrote
54//! once against `FloatVector` does not change.
55//!
56//! # The trait hierarchy
57//!
58//! Each trait adds capability on top of the previous one; bound on the least
59//! specific trait that supplies the operations you need.
60//!
61//! ```text
62//! GenericVector construction, lane access, memory I/O, gather/scatter,
63//! | reinterpretation, map/fold/reduce, interleave
64//! |- BitwiseVector &, |, ^, !, andnot, ternlog
65//! | \- BitshiftVector shifts, rotations, byte-shifts
66//! \- PartialOrdVector cmp_lt/le/gt/ge/eq/ne -> Mask
67//! \- NumericVector +, -, *, /, %, min/max/clamp, reductions, FMA
68//! |- SignedVector abs, signum, copysign, neg
69//! | \- FloatVector sqrt, rcp/rsqrt, rounding, mix, consts
70//! | \- FloatVectorWithBits ldexp/frexp, bit-level ops
71//! \- IntegerVector saturating/wrapping, popcount, dividers
72//! | (also requires BitshiftVector)
73//! |- SignedIntegerVector arithmetic shift, avg
74//! | (also requires SignedVector)
75//! \- UnsignedIntegerVector is_power_of_two, parity, avg
76//! ```
77//!
78//! `FloatVector` and `SignedIntegerVector` both sit under [`SignedVector`];
79//! `SignedIntegerVector` additionally requires [`IntegerVector`], so it is the
80//! meeting point of the signed and integer branches. `IntegerVector` itself
81//! does **not** require [`SignedVector`] - unsigned integer vectors are
82//! integers without being signed.
83//!
84//! Alongside these, [`LinAlg3Vector`]/[`LinAlg4Vector`] add 3D/4D linear-algebra
85//! operations, and the `Swizzle`/[`Swizzle3`]/[`Swizzle4`] traits add lane
86//! permutation. Masked (`_c`/`_m`/`_z`) variants of most operations live in the
87//! [`ops`] submodule.
88//!
89//! Three layers cooperate to make all of this work: an `Element` (the scalar),
90//! a [`Register`](crate::register::Register) (the functional hardware layer),
91//! and [`Vector<R>`] (this module's ergonomic wrapper). Most users only ever
92//! touch the [`Vector`] layer and its traits.
93
94use generic_array::{GenericArray, typenum};
95
96use crate::{
97 BranchfreeDivider, Divider,
98 divider::Denominator,
99 element::FloatElementWithBits,
100 isa::InstructionSet,
101 mask::{CastMask, GenericMask, GenericSelectable},
102 math::{FloatConsts, policy::Policy},
103 register::{Element, FloatElement, Lanes, NativeCapability},
104};
105
106mod num;
107
108#[doc(hidden)]
109pub mod splat;
110
111#[allow(clippy::module_inception)]
112mod vector;
113
114/// Operator traits behind the vector arithmetic, plus the masked `_c` / `_m` / `_z`
115/// forms of each.
116///
117/// The vector traits in this module's parent list these as supertraits, so a
118/// `V: NumericVector` bound already carries `+`, `-`, `*` and their masked
119/// variants. Import from here only to name one directly, such as writing a
120/// generic bound on [`ops::Square`] or [`ops::BitAndNot`] alone.
121pub mod ops;
122pub mod streaming;
123pub mod unaligned;
124
125pub use self::num::NumVector;
126pub use self::splat::{NewConst, NewVector, SplatConst, SplatVector, VectorValue, const_new, const_splat};
127pub use self::vector::Vector;
128pub use crate::register::StreamGroup;
129
130/// Three vector types (`Self`, `A`, `B`) whose masks can all be freely cast to
131/// one another.
132///
133/// All three must share the same [`Lanes`](GenericVector::Lanes) count, and
134/// each one's [`Mask`](GenericVector::Mask) must implement [`CastMask`] into
135/// the other two. This is a convenience bound for generic code that selects or
136/// blends across vectors of different element types but identical width - e.g.
137/// using a mask produced from a float comparison to select lanes of an integer
138/// vector.
139///
140/// It is blanket-implemented for every triple of types satisfying the cast
141/// requirements, so it never needs to be implemented manually.
142pub trait MaskInteroperable<A, B>: GenericVector<Mask: CastMask<A::Mask> + CastMask<B::Mask>>
143where
144 A: GenericVector<Lanes = Self::Lanes, Mask: CastMask<Self::Mask> + CastMask<B::Mask>>,
145 B: GenericVector<Lanes = Self::Lanes, Mask: CastMask<Self::Mask> + CastMask<A::Mask>>,
146{
147}
148
149impl<T, A, B> MaskInteroperable<A, B> for T
150where
151 T: GenericVector<Mask: CastMask<A::Mask> + CastMask<B::Mask>>,
152 A: GenericVector<Lanes = T::Lanes, Mask: CastMask<T::Mask> + CastMask<B::Mask>>,
153 B: GenericVector<Lanes = T::Lanes, Mask: CastMask<T::Mask> + CastMask<A::Mask>>,
154{
155}
156
157/// [`MaskInteroperable`] plus bidirectional numeric ([`CastVector`]) conversion
158/// among `Self`, `A`, and `B`.
159///
160/// In addition to interoperable masks, this guarantees `Self`, `A`, and `B` can
161/// all be numerically cast into one another in either direction (`A`/`B` into
162/// `Self` *and* `Self` into `A`/`B`), so generic code can freely move operands
163/// of differing element types into whichever common type it needs before
164/// combining them. It does **not** require bit-level reinterpretation; for that
165/// see [`FullyInteroperable`].
166///
167/// Blanket-implemented for every triple satisfying the bounds.
168pub trait PartiallyInteroperable<A, B>:
169 GenericVector<Mask: CastMask<A::Mask> + CastMask<B::Mask>>
170 // casts
171 + CastVector<Self>
172 + CastVector<A>
173 + CastVector<B>
174where
175 A: CastVector<Self> + GenericVector<Lanes = Self::Lanes, Mask: CastMask<Self::Mask> + CastMask<B::Mask>>,
176 B: CastVector<Self> + GenericVector<Lanes = Self::Lanes, Mask: CastMask<Self::Mask> + CastMask<A::Mask>>,
177{
178}
179
180impl<V, A, B> PartiallyInteroperable<A, B> for V
181where
182 V: GenericVector<Mask: CastMask<A::Mask> + CastMask<B::Mask>>
183 // casts
184 + CastVector<V>
185 + CastVector<A>
186 + CastVector<B>,
187 A: CastVector<V> + GenericVector<Lanes = V::Lanes, Mask: CastMask<V::Mask> + CastMask<B::Mask>>,
188 B: CastVector<V> + GenericVector<Lanes = V::Lanes, Mask: CastMask<V::Mask> + CastMask<A::Mask>>,
189{
190}
191
192/// [`PartiallyInteroperable`] plus zero-cost bit-level reinterpretation
193/// ([`BitCastVector`]) among `Self`, `A`, and `B`.
194///
195/// The strongest of the three interoperability bounds: masks are mutually
196/// castable, the three element types convert numerically, *and* their bit
197/// patterns can be reinterpreted into one another. This is what a float vector
198/// needs against its own bits/signed-bits integer vectors (see
199/// [`FloatVectorWithBits`]) so that bit-twiddling algorithms can hop between the
200/// float view and the integer view with no instructions emitted.
201///
202/// Blanket-implemented for every triple satisfying the bounds.
203pub trait FullyInteroperable<A, B>:
204 GenericVector<Mask: CastMask<A::Mask> + CastMask<B::Mask>>
205 // bits
206 + BitCastVector<Self>
207 + BitCastVector<A>
208 + BitCastVector<B>
209 // casts
210 + CastVector<Self>
211 + CastVector<A>
212 + CastVector<B>
213where
214 A: BitCastVector<Self> + CastVector<Self> + GenericVector<Lanes = Self::Lanes, Mask: CastMask<Self::Mask> + CastMask<B::Mask>>,
215 B: BitCastVector<Self> + CastVector<Self> + GenericVector<Lanes = Self::Lanes, Mask: CastMask<Self::Mask> + CastMask<A::Mask>>,
216{
217}
218
219impl<V, A, B> FullyInteroperable<A, B> for V
220where
221 V: GenericVector<Mask: CastMask<A::Mask> + CastMask<B::Mask>>
222 // bits
223 + BitCastVector<V>
224 + BitCastVector<A>
225 + BitCastVector<B>
226 // casts
227 + CastVector<V>
228 + CastVector<A>
229 + CastVector<B>,
230 A: BitCastVector<V> + CastVector<V> + GenericVector<Lanes = V::Lanes, Mask: CastMask<V::Mask> + CastMask<B::Mask>>,
231 B: BitCastVector<V> + CastVector<V> + GenericVector<Lanes = V::Lanes, Mask: CastMask<V::Mask> + CastMask<A::Mask>>,
232{
233}
234
235/// Internal helpers for generic vectors.
236trait GenericVectorExt: GenericVector {
237 #[inline(always)]
238 fn len_to_indices<I: UnsignedIntegerVector>(len: usize) -> I {
239 let Ok(len) = <<I as GenericVector>::Element as TryFrom<usize>>::try_from(len) else {
240 #[cfg(feature = "std")]
241 panic!("Length {} exceeds maximum supported index for this vector type", len);
242
243 #[cfg(not(feature = "std"))]
244 panic!("Length exceeds maximum supported index for this vector type");
245 };
246
247 I::splat(len)
248 }
249}
250
251impl<V: GenericVector> GenericVectorExt for V {}
252
253/// An unsigned integer vector that can be used as the index operand for
254/// gather/scatter operations producing/consuming a vector of type `V`.
255///
256/// This is the inverse-facing companion to [`IndexableVector`]: where
257/// `IndexableVector<I>` is implemented on the gathered vector type, this is
258/// implemented on the index type. It is blanket-implemented for every index
259/// type `I` such that `V: IndexableVector<I>`, simply forwarding to `V`'s
260/// methods. The index lanes are element offsets (not byte offsets) and must
261/// match `V`'s lane count.
262///
263/// The methods here are the raw pointer primitives; prefer the safe,
264/// bounds-checked wrappers on [`GenericVector`] ([`gather`](GenericVector::gather),
265/// [`scatter`](GenericVector::scatter), etc.) instead of calling these directly.
266pub trait VectorIndices<V: GenericVector>: UnsignedIntegerVector<Lanes = V::Lanes> {
267 /// Gather one element of `V` per lane from `ptr[indices[lane]]`.
268 ///
269 /// # Safety
270 /// `ptr` must be valid for reads, and for every lane the offset
271 /// `indices[lane]` must land within the allocation `ptr` points into
272 /// (i.e. `ptr.add(indices[lane])` must be readable). Indices are not
273 /// bounds-checked.
274 unsafe fn gather_ptr(ptr: *const V::Element, indices: Self) -> V;
275
276 /// Like [`gather_ptr`](Self::gather_ptr), but only lanes where `mask` is
277 /// `true` are loaded; the rest are taken from `src`.
278 ///
279 /// # Safety
280 /// Same as [`gather_ptr`](Self::gather_ptr), but only the offsets for lanes
281 /// where `mask` is `true` need to be in bounds; masked-off lanes are not
282 /// accessed.
283 unsafe fn gather_ptr_m(src: V, mask: V::Mask, ptr: *const V::Element, indices: Self) -> V;
284
285 /// Like [`gather_ptr_m`](Self::gather_ptr_m), but masked-off lanes are
286 /// zeroed instead of taken from a source vector.
287 ///
288 /// # Safety
289 /// Same as [`gather_ptr_m`](Self::gather_ptr_m).
290 unsafe fn gather_ptr_z(mask: V::Mask, ptr: *const V::Element, indices: Self) -> V;
291
292 /// Scatter each lane of `value` to `ptr[indices[lane]]`.
293 ///
294 /// # Safety
295 /// `ptr` must be valid for writes, and for every lane the offset
296 /// `indices[lane]` must land within the allocation `ptr` points into.
297 /// Indices are not bounds-checked, and overlapping (duplicate) indices
298 /// produce an unspecified winning lane.
299 unsafe fn scatter_ptr(value: V, ptr: *mut V::Element, indices: Self);
300
301 /// Like [`scatter_ptr`](Self::scatter_ptr), but only lanes where `mask` is
302 /// `true` are written.
303 ///
304 /// # Safety
305 /// Same as [`scatter_ptr`](Self::scatter_ptr), but only the offsets for
306 /// lanes where `mask` is `true` need to be in bounds; masked-off lanes are
307 /// not written.
308 unsafe fn scatter_ptr_m(value: V, mask: V::Mask, ptr: *mut V::Element, indices: Self);
309}
310
311/// A vector type that supports gather/scatter using index vectors of type `I`.
312///
313/// Implemented on the gathered/scattered vector type (`Self`), parameterized by
314/// the unsigned integer index vector type `I` (which must share `Self`'s lane
315/// count). Backends with hardware gather/scatter (e.g. AVX2's `vpgatherdd`)
316/// provide an accelerated implementation; others fall back to scalar loops.
317///
318/// These are the raw pointer primitives; index lanes are element offsets, not
319/// byte offsets, and are not bounds-checked. Prefer the safe, bounds-checked
320/// [`GenericVector`] wrappers ([`gather`](GenericVector::gather),
321/// [`scatter`](GenericVector::scatter), etc.) in normal code.
322pub trait IndexableVector<I: UnsignedIntegerVector<Lanes = Self::Lanes>>: GenericVector {
323 /// Gather one element per lane from `ptr[indices[lane]]`.
324 ///
325 /// # Safety
326 /// `ptr` must be valid for reads, and every offset `indices[lane]` must
327 /// land within the allocation `ptr` points into. Indices are not
328 /// bounds-checked.
329 unsafe fn gather_ptr(ptr: *const Self::Element, indices: I) -> Self;
330
331 /// Like [`gather_ptr`](Self::gather_ptr), but only lanes where `mask` is
332 /// `true` are loaded; the rest are taken from `src`.
333 ///
334 /// # Safety
335 /// Same as [`gather_ptr`](Self::gather_ptr), but only the offsets for lanes
336 /// where `mask` is `true` need to be in bounds.
337 unsafe fn gather_ptr_m(src: Self, mask: Self::Mask, ptr: *const Self::Element, indices: I) -> Self;
338
339 /// Like [`gather_ptr_m`](Self::gather_ptr_m), but masked-off lanes are
340 /// zeroed instead of taken from a source vector.
341 ///
342 /// # Safety
343 /// Same as [`gather_ptr_m`](Self::gather_ptr_m).
344 unsafe fn gather_ptr_z(mask: Self::Mask, ptr: *const Self::Element, indices: I) -> Self;
345
346 /// Scatter each lane of `value` to `ptr[indices[lane]]`.
347 ///
348 /// # Safety
349 /// `ptr` must be valid for writes, and every offset `indices[lane]` must
350 /// land within the allocation `ptr` points into. Indices are not
351 /// bounds-checked; duplicate indices produce an unspecified winning lane.
352 unsafe fn scatter_ptr(value: Self, ptr: *mut Self::Element, indices: I);
353
354 /// Like [`scatter_ptr`](Self::scatter_ptr), but only lanes where `mask` is
355 /// `true` are written.
356 ///
357 /// # Safety
358 /// Same as [`scatter_ptr`](Self::scatter_ptr), but only the offsets for
359 /// lanes where `mask` is `true` need to be in bounds.
360 unsafe fn scatter_ptr_m(value: Self, mask: Self::Mask, ptr: *mut Self::Element, indices: I);
361}
362
363impl<I, V> VectorIndices<V> for I
364where
365 I: UnsignedIntegerVector<Lanes = V::Lanes>,
366 V: IndexableVector<I>,
367{
368 #[inline(always)]
369 unsafe fn gather_ptr(ptr: *const <V as GenericVector>::Element, indices: Self) -> V {
370 unsafe { V::gather_ptr(ptr, indices) }
371 }
372
373 #[inline(always)]
374 unsafe fn gather_ptr_m(
375 src: V,
376 mask: <V as GenericVector>::Mask,
377 ptr: *const <V as GenericVector>::Element,
378 indices: Self,
379 ) -> V {
380 unsafe { V::gather_ptr_m(src, mask, ptr, indices) }
381 }
382
383 #[inline(always)]
384 unsafe fn gather_ptr_z(
385 mask: <V as GenericVector>::Mask,
386 ptr: *const <V as GenericVector>::Element,
387 indices: Self,
388 ) -> V {
389 unsafe { V::gather_ptr_z(mask, ptr, indices) }
390 }
391
392 #[inline(always)]
393 unsafe fn scatter_ptr(value: V, ptr: *mut <V as GenericVector>::Element, indices: Self) {
394 unsafe { V::scatter_ptr(value, ptr, indices) }
395 }
396
397 #[inline(always)]
398 unsafe fn scatter_ptr_m(
399 value: V,
400 mask: <V as GenericVector>::Mask,
401 ptr: *mut <V as GenericVector>::Element,
402 indices: Self,
403 ) {
404 unsafe { V::scatter_ptr_m(value, mask, ptr, indices) }
405 }
406}
407
408/// Joining two `HALF`-width values into one double-width `Self`, and splitting
409/// back apart.
410///
411/// Implemented for both vectors and masks. `Self` has exactly twice the lane
412/// count of `HALF`. Most users should go through
413/// [`GenericVector::concat`] / [`GenericVector::split`] rather than naming this
414/// trait directly. Because the wide type can always be narrowed back to a half,
415/// `Concat` requires [`Extend`].
416pub trait Concat<HALF>: Extend<HALF> {
417 /// Build the double-width value from a `lo` and `hi` half, with `lo`'s lanes
418 /// occupying the lower half of the result and `hi`'s the upper half.
419 fn concat(lo: HALF, hi: HALF) -> Self;
420
421 /// Split into `(lo, hi)` halves, the inverse of [`concat`](Self::concat).
422 fn split(self) -> (HALF, HALF);
423}
424
425/// Zero-extend a narrower `FROM` value into a wider `Self`, and narrow back.
426///
427/// Implemented for both vectors and masks. Most users should go through
428/// [`GenericVector::extend`] / [`GenericVector::narrow`].
429pub trait Extend<FROM> {
430 /// Widen `v` into `Self`, placing `v`'s lanes in the lower half and filling
431 /// the upper half with zeros.
432 fn extend(v: FROM) -> Self;
433
434 /// Narrow back to `FROM` by keeping the lower lanes and discarding the
435 /// upper lanes.
436 fn narrow(self) -> FROM;
437}
438
439/// [`Concat`] specialized to vector types: `Self` is a [`GenericVector`] that is
440/// the concatenation of two `HALF` vectors of the same element type, and whose
441/// mask is likewise the concatenation of two `HALF` masks.
442///
443/// Blanket-implemented; this is the bound used by [`GenericVector::concat`] /
444/// [`GenericVector::split`].
445pub trait ConcatVector<HALF: GenericVector<Element = Self::Element>>:
446 Concat<HALF> + GenericVector<Mask: Concat<HALF::Mask>>
447{
448}
449
450/// Zero-extend vectors
451pub trait ExtendVector<FROM: GenericVector<Element = Self::Element>>:
452 Extend<FROM> + GenericVector<Mask: Extend<FROM::Mask>>
453{
454}
455
456impl<V: GenericVector, H: GenericVector<Element = V::Element>> ConcatVector<H> for V
457where
458 V: Concat<H>,
459 V::Mask: Concat<H::Mask>,
460{
461}
462impl<V: GenericVector, F: GenericVector<Element = V::Element>> ExtendVector<F> for V
463where
464 V: Extend<F>,
465 V::Mask: Extend<F::Mask>,
466{
467}
468
469/// A [`GenericVector`] whose lanes can be permuted by the
470/// [`Swizzle`](crate::swizzle::Swizzle) machinery for its lane count.
471///
472/// Blanket-implemented for every vector that satisfies the swizzle bound; it is
473/// the prerequisite for the human-readable swizzle traits ([`Swizzle3`],
474/// [`Swizzle4`]) and the [`swizzle!`](crate::swizzle) macro.
475pub trait SwizzleVector: GenericVector + crate::swizzle::Swizzle<Self::Lanes> {}
476impl<V> SwizzleVector for V where V: GenericVector + crate::swizzle::Swizzle<V::Lanes> {}
477
478/// Pairwise lane interleaving and its exact inverse, the building block for
479/// moving between array-of-structs and struct-of-arrays layouts.
480///
481/// [`interleave`](Self::interleave) zips two vectors into a low half and a high
482/// half, and [`deinterleave`](Self::deinterleave) undoes it. Both lower to one
483/// instruction per output on most backends (x86 `unpcklps` / `unpckhps`, NEON
484/// `zip1` / `zip2`).
485///
486/// This trait carries only that pair, so it can bound code that is not generic
487/// over a full vector. The radix-`N` generalizations for wider strides and
488/// group granularity live on [`GenericVector`] instead.
489pub trait Interleave: Sized {
490 /// Unpack and interleave elements from two vectors.
491 ///
492 /// The resulting two vectors contain the interleaved elements from the input vectors. e.g.,
493 /// for vectors `a = [a0, a1, a2, a3]` and `b = [b0, b1, b2, b3]`, the result will be
494 /// `([a0, b0, a1, b1], [a2, b2, a3, b3])`.
495 ///
496 /// # Note
497 ///
498 /// Unlike the native unpacklo/unpackhi instructions, at higher register widths
499 /// this will preserve the order of all elements, not just 128-bit chunks.
500 fn interleave(self, other: Self) -> (Self, Self);
501
502 /// Pack and deinterleave elements from two vectors. This is the inverse operation of `interleave`.
503 ///
504 /// The resulting vector contains the deinterleaved elements from the input vectors. e.g.,
505 /// for vectors `a = [a0, b0, a1, b1]` and `b = [a2, b2, a3, b3]`, the result will be
506 /// `[a0, a1, a2, a3]` and `[b0, b1, b2, b3]`.
507 fn deinterleave(self, other: Self) -> (Self, Self);
508}
509
510/// Core trait for generic vector types.
511///
512/// Provides the basis for further specialized vector traits. Every other vector
513/// trait in the hierarchy (`NumericVector`, `FloatVector`, `IntegerVector`, etc.)
514/// is built on top of this one.
515///
516/// A `GenericVector` is a fixed-length, immutable, copyable array of `Element`s
517/// laid out contiguously and aligned to its register's native alignment. The
518/// number of lanes is known at compile time via the [`LANES`](Self::LANES)
519/// constant and the [`Lanes`](Self::Lanes) associated type (a `typenum`).
520///
521/// All construction, lane access, memory I/O, gather/scatter, reinterpretation,
522/// and scalar-fallback (`map`/`fold`/`reduce`) operations live on this trait.
523/// Arithmetic, bitwise and float operations are added by the sub-traits.
524#[rustfmt::skip] #[thermite_macros::vector_trait]
525#[diagnostic::on_unimplemented(
526 message = "`{Self}` is not a Thermite vector type",
527 label = "not a SIMD vector",
528 note = "`GenericVector` is the root of Thermite's vector trait tower. It is implemented by `Vector<R>` (including the 1-lane scalar `Vector<f32>` / `Vector<f64>`) and by composite vector types such as `Dual`, `Complex`, and `Compensated`.",
529 note = "A bare scalar such as `f32` or `f64` is NOT a vector. Wrap it with `Vector::<f32>::splat(x)` (or `Vector(x)`) to get a 1-lane vector, or use the `ScalarMath` methods (`x.scalar_sin()`, ...) for one-off scalar math."
530)]
531pub trait GenericVector: 'static + Sized + Default + Copy + core::fmt::Debug
532 + const_default::ConstDefault
533 + SplatVector<Self::Element> + NewVector<Self::Element, Self::Lanes>
534 + GenericSelectable<SelectableMask = Self::Mask>
535 + crate::simd::HasIsa
536 + CastVector<Self>
537 + Interleave
538{
539 /// Scalar element type of the vector.
540 type Element: Element;
541
542 /// A vector with all elements zeroed.
543 const EMPTY: Self;
544
545 /// Number of lanes in the vector.
546 const LANES: usize;
547
548 /// Number of lanes in the vector, as a runtime value.
549 ///
550 /// Today this is always [`LANES`](Self::LANES), but prefer it over the constant in
551 /// loop bounds and address arithmetic: a future scalable-vector backend (SVE /
552 /// RISC-V V) can only report its lane count at runtime, and code written against
553 /// `lanes()` will carry over unchanged.
554 #[inline(always)]
555 fn lanes() -> usize {
556 Self::LANES
557 }
558
559 /// Number of lanes in the vector, as a typenum.
560 type Lanes: Lanes;
561
562 /// Unsigned Integer Type suitable for use with this vector.
563 type Unsigned: UnsignedIntegerVector<
564 Signed = Self::Signed,
565 Unsigned = Self::Unsigned,
566 Lanes = Self::Lanes,
567 Element = <Self::Element as Element>::Unsigned,
568 Mask: CastMask<Self::Mask> + CastMask<<Self::Signed as GenericVector>::Mask>,
569 > + CastVector<Self::Signed>
570 + BitCastVector<Self::Signed>;
571
572 /// SignedBits Integer Type suitable for use with this vector.
573 type Signed: SignedIntegerVector<
574 Signed = Self::Signed,
575 Unsigned = Self::Unsigned,
576 Lanes = Self::Lanes,
577 Element = <Self::Element as Element>::Signed,
578 Mask: CastMask<Self::Mask> + CastMask<<Self::Unsigned as GenericVector>::Mask>,
579 > + CastVector<Self::Unsigned>
580 + BitCastVector<Self::Unsigned>;
581
582 /// Mask type for this vector. Masks are semantically boolean vectors indicating
583 /// true or false for each lane. They may or may not be represented as actual bits.
584 type Mask: GenericMask
585 + CastMask<<Self::Unsigned as GenericVector>::Mask>
586 + CastMask<<Self::Signed as GenericVector>::Mask>;
587
588 /// Create a new vector from an array of elements.
589 ///
590 /// The array length `N` must equal [`LANES`](Self::LANES); this is enforced
591 /// at compile time by the `Const<N> == Lanes` bound.
592 fn new<const N: usize>(value: [Self::Element; N]) -> Self
593 where generic_array::typenum::Const<N>: generic_array::IntoArrayLength<ArrayLength = Self::Lanes>;
594
595 /// Consume the vector and return its elements as a `GenericArray`.
596 ///
597 /// This is the inverse of [`new`](Self::new); it copies lane-by-lane and
598 /// has no runtime cost beyond a register-to-memory store on backends where
599 /// the storage and array layouts are bit-identical (the common case).
600 fn into_array(self) -> GenericArray<Self::Element, Self::Lanes>;
601
602 /// Create a new vector from a single element by splatting it across all lanes.
603 #[masked] fn splat(value: Self::Element) -> Self;
604
605 /// Create a new vector with the first lane set to the given value, and all other lanes set to zero.
606 fn single(value: Self::Element) -> Self;
607
608 /// Combine two vectors of the same type into one wider vector,
609 /// with `self` as the lower half and `hi` as the upper half.
610 fn concat<INTO>(self, hi: Self) -> INTO
611 where
612 INTO: ConcatVector<Self, Element = Self::Element>,
613 {
614 <INTO as Concat<Self>>::concat(self, hi)
615 }
616
617 /// Split this vector into two narrower vectors of the same type, with the lower
618 /// lanes in the first vector and the upper lanes in the second vector.
619 fn split<INTO: GenericVector>(self) -> (INTO, INTO)
620 where
621 Self: ConcatVector<INTO, Element = INTO::Element>,
622 {
623 <Self as Concat<INTO>>::split(self)
624 }
625
626 /// Zero-extend a narrower vector into this wider vector type, placing the
627 /// original values in the lower lanes and filling the upper lanes with zeros.
628 fn extend<INTO>(self) -> INTO
629 where
630 INTO: ExtendVector<Self, Element = Self::Element>,
631 {
632 <INTO as Extend<Self>>::extend(self)
633 }
634
635 /// Narrow this wider vector into a narrower vector by taking the lower lanes.
636 ///
637 /// The upper lanes are discarded.
638 fn narrow<INTO: GenericVector>(self) -> INTO
639 where
640 Self: ExtendVector<INTO, Element = INTO::Element>,
641 {
642 <Self as Extend<INTO>>::narrow(self)
643 }
644
645 /// Align a slice of elements to the vector's lane count, returning the aligned portion and any unaligned head or tail.
646 ///
647 /// If the vector's size in bytes does not match the size of its elements times the lane count, this will
648 /// return the entire slice as unaligned and empty aligned/remaining parts. This is rare, but may occur
649 /// if using a generic vector type that doesn't correspond to an actual hardware vector (for example, a 3-lane vector).
650 #[inline(always)]
651 fn align_slice(slice: &[Self::Element]) -> (&[Self::Element], &[Self], &[Self::Element]) {
652 if const { size_of::<Self>() != (size_of::<Self::Element>() * Self::LANES) } {
653 return (slice, &[], &[]);
654 };
655
656 unsafe { slice.align_to() }
657 }
658
659 /// Align a mutable slice of elements to the vector's lane count, returning the aligned portion and any unaligned head or tail.
660 ///
661 /// If the vector's size in bytes does not match the size of its elements times the lane count, this will
662 /// return the entire slice as unaligned and empty aligned/remaining parts. This is rare, but may occur
663 /// if using a generic vector type that doesn't correspond to an actual hardware vector (for example, a 3-lane vector).
664 #[inline(always)]
665 fn align_slice_mut(slice: &mut [Self::Element]) -> (&mut [Self::Element], &mut [Self], &mut [Self::Element]) {
666 if const { size_of::<Self>() != (size_of::<Self::Element>() * Self::LANES) } {
667 return (slice, &mut [], &mut []);
668 };
669
670 unsafe { slice.align_to_mut() }
671 }
672
673 /// Create a new vector from a slice of elements. The slice must have at least as many elements as the vector's lanes.
674 ///
675 /// This will emit an unaligned load.
676 ///
677 /// If you're looking for masked variants of this, those typically only exist for aligned inputs,
678 /// so you'll need an aligned pointer and use [`load_m`](Self::load_m) or [`load_z`](Self::load_z).
679 fn from_slice(slice: &[Self::Element]) -> Self {
680 assert!(slice.len() >= Self::lanes(), "Slice must have at least {} elements to create a vector", Self::lanes());
681
682 unsafe { Self::load_unaligned(slice.as_ptr()) }
683 }
684
685 /// Copy the elements of the vector into a slice. The slice must have at least as many elements as the vector's lanes.
686 ///
687 /// This will emit an unaligned store.
688 fn copy_to_slice(self, slice: &mut [Self::Element]) {
689 assert!(slice.len() >= Self::lanes(), "Slice must have at least {} elements to copy from a vector", Self::lanes());
690
691 unsafe { self.store_unaligned(slice.as_mut_ptr()) }
692 }
693
694 /// Transform a slice of element values into an unaligned iterator of vectors,
695 /// returning any remaining elements as a suffix slice.
696 fn iter_unaligned<'a>(values: &'a [Self::Element]) -> (unaligned::Unaligned<'a, Self>, &'a [Self::Element]) {
697 let num_vectors = values.len() / Self::lanes();
698 let offset = num_vectors * Self::lanes();
699
700 let head = &values[..offset];
701 let tail = &values[offset..];
702
703 (unaligned::Unaligned(head), tail)
704 }
705
706 /// Transform a mutable slice of element values into an unaligned iterator of vectors,
707 /// returning any remaining elements as a suffix slice.
708 fn iter_mut_unaligned<'a>(values: &'a mut [Self::Element]) -> (unaligned::UnalignedMut<'a, Self>, &'a mut [Self::Element]) {
709 let num_vectors = values.len() / Self::lanes();
710 let offset = num_vectors * Self::lanes();
711
712 let (head, tail) = values.split_at_mut(offset);
713
714 (unaligned::UnalignedMut(head), tail)
715 }
716
717 /// Iterate over a slice of element values as Vectors using non-temporal (streaming) loads.
718 ///
719 /// # Panics
720 ///
721 /// If the slice is not aligned to the register type of the vector, or has remaining elements.
722 fn stream_aligned_slice<'a>(values: &'a [Self::Element]) -> impl DoubleEndedIterator<Item = streaming::StreamingVector<'a, Self>> {
723 let (&[], values, &[]) = Self::align_slice(values) else {
724 panic!("Slice is not aligned to the vector type, or has remaining elements");
725 };
726
727 values.iter().map(|v| streaming::StreamingVector(v))
728 }
729
730 /// Iterate over a mutable slice of element values as Vectors using non-temporal (streaming) loads and stores.
731 ///
732 /// # Panics
733 ///
734 /// If the slice is not aligned to the register type of the vector, or has remaining elements.
735 fn stream_aligned_slice_mut<'a>(values: &'a mut [Self::Element]) -> impl DoubleEndedIterator<Item = streaming::StreamingVectorMut<'a, Self>> {
736 let (&mut [], values, &mut []) = Self::align_slice_mut(values) else {
737 panic!("Slice is not aligned to the vector type, or has remaining elements");
738 };
739
740 values.iter_mut().map(|v| streaming::StreamingVectorMut(v))
741 }
742
743 /// Gather elements from memory at the specified indices and return a new vector with those elements.
744 ///
745 /// The provided indices are in number of elements, not bytes.
746 ///
747 /// # Panics
748 /// If any index is out of bounds for the slice length, or the slice length exceeds
749 /// the maximum supported index for this vector type.
750 fn gather<I: VectorIndices<Self>>(slice: &[Self::Element], indices: I) -> Self {
751 if indices.cmp_lt(Self::len_to_indices::<I>(slice.len())).all() {
752 unsafe { I::gather_ptr(slice.as_ptr(), indices) }
753 } else {
754 #[cfg(feature = "std")]
755 panic!("One or more indices are out of bounds for the slice length {}", slice.len());
756
757 #[cfg(not(feature = "std"))] // avoid fmt
758 panic!("One or more indices are out of bounds for the slice length");
759 }
760 }
761
762 /// Gather elements from memory at the specified indices, or return `or` if the index is out of bounds.
763 ///
764 /// The provided indices are in number of elements, not bytes.
765 ///
766 /// # Panics
767 /// If the slice length exceeds the maximum supported index for this vector type.
768 fn gather_or<I: VectorIndices<Self>>(slice: &[Self::Element], indices: I, or: Self) -> Self
769 where Self::Mask: CastMask<I::Mask>,
770 {
771 let in_bounds = indices.cmp_lt(Self::len_to_indices::<I>(slice.len()));
772
773 unsafe { I::gather_ptr_m(or, in_bounds.cast(), slice.as_ptr(), indices) }
774 }
775
776 /// Gather elements from memory at the specified indices, or set the lane to zero
777 /// if the index is out of bounds.
778 ///
779 /// The provided indices are in number of elements, not bytes.
780 ///
781 /// # Panics
782 /// If the slice length exceeds the maximum supported index for this vector type.
783 fn gather_or_zero<I: VectorIndices<Self>>(slice: &[Self::Element], indices: I) -> Self
784 where Self::Mask: CastMask<I::Mask>,
785 {
786 let in_bounds = indices.cmp_lt(Self::len_to_indices::<I>(slice.len()));
787
788 unsafe { I::gather_ptr_z(in_bounds.cast(), slice.as_ptr(), indices) }
789 }
790
791 /// Gather elements from memory at the specified indices, or return `or` if the `enable` mask is
792 /// `false` OR if any index is out of bounds.
793 ///
794 /// The provided indices are in number of elements, not bytes.
795 ///
796 /// # Panics
797 /// If the slice length exceeds the maximum supported index for this vector type.
798 fn gather_if<I: VectorIndices<Self>>(slice: &[Self::Element], enable: Self::Mask, indices: I, or: Self) -> Self
799 where
800 Self::Mask: CastMask<I::Mask>,
801 Self::Element: Default,
802 {
803 let in_bounds = indices.cmp_lt(Self::len_to_indices::<I>(slice.len()));
804
805 unsafe { I::gather_ptr_m(or, enable & in_bounds.cast(), slice.as_ptr(), indices) }
806 }
807
808 /// Scatter elements from the given vector into memory at the specified indices. If the index is outside of the
809 /// bounds of the provided slice, the write is suppressed without panicking.
810 fn scatter<I: VectorIndices<Self>>(self, slice: &mut [Self::Element], indices: I)
811 where Self::Mask: CastMask<I::Mask>,
812 {
813 let in_bounds = indices.cmp_lt(Self::len_to_indices::<I>(slice.len()));
814
815 unsafe { I::scatter_ptr_m(self, in_bounds.cast(), slice.as_mut_ptr(), indices) }
816 }
817
818 /// Scatter elements from the given vector into memory at the specified indices, but only for lanes where the `enable` mask is `true`.
819 /// If the index is outside of the bounds of the provided slice, the write is suppressed without panicking.
820 fn scatter_if<I: VectorIndices<Self>>(self, slice: &mut [Self::Element], enable: Self::Mask, indices: I)
821 where Self::Mask: CastMask<I::Mask>,
822 {
823 let in_bounds = indices.cmp_lt(Self::len_to_indices::<I>(slice.len()));
824
825 unsafe { I::scatter_ptr_m(self, enable & in_bounds.cast(), slice.as_mut_ptr(), indices) }
826 }
827
828 /// Load a vector from an **aligned** pointer to its elements.
829 ///
830 /// # SAFETY
831 /// The caller must ensure that the pointer is valid, aligned, and points to a memory region
832 /// that is at least `Self::Lanes` elements long.
833 #[masked] unsafe fn load(ptr: *const Self::Element) -> Self;
834
835 /// Load a vector from an **unaligned** pointer to its elements.
836 ///
837 /// # SAFETY
838 /// The caller must ensure that the pointer is valid and points to a memory region
839 /// that is at least `Self::Lanes` elements long.
840 ///
841 /// Unaligned access may be slower on some older architectures.
842 unsafe fn load_unaligned(ptr: *const Self::Element) -> Self;
843
844 /// Load a vector from a pointer to its elements using non-temporal (streaming) loads.
845 ///
846 /// The memory region should not be accessed frequently by the CPU,
847 /// as non-temporal loads are intended for data that will not be reused soon.
848 ///
849 /// # SAFETY
850 /// The caller must ensure that the pointer is valid, aligned, and points to a memory region
851 /// that is at least `Self::Lanes` elements long.
852 unsafe fn load_streaming(ptr: *const Self::Element) -> Self;
853
854 /// Store the vector to an **aligned** pointer to its elements.
855 ///
856 /// # SAFETY
857 /// The caller must ensure that the pointer is valid, aligned, and points to a memory region
858 /// that is at least `Self::Lanes` elements long.
859 unsafe fn store(self, ptr: *mut Self::Element);
860
861 /// Store the vector to an **aligned** pointer to its elements, but only for lanes where the corresponding mask lane is `true`.
862 /// For lanes where the mask is `false`, the store is suppressed without panicking.
863 ///
864 /// # SAFETY
865 /// The caller must ensure that the pointer is valid, aligned, and points to a memory region
866 /// that is at least `Self::Lanes` elements long (or at least as long as the number of `true` lanes in the mask).
867 unsafe fn store_masked(self, mask: Self::Mask, ptr: *mut Self::Element);
868
869 /// Store the vector to an **unaligned** pointer to its elements.
870 ///
871 /// # SAFETY
872 /// The caller must ensure that the pointer is valid and points to a memory region
873 /// that is at least `Self::Lanes` elements long. Unaligned access may be slower on some architectures.
874 ///
875 /// Unaligned access may be slower on some older architectures.
876 unsafe fn store_unaligned(self, ptr: *mut Self::Element);
877
878 /// Store the vector to a pointer to its elements using non-temporal (streaming) stores.
879 ///
880 /// The memory region should not be accessed frequently by the CPU,
881 /// as non-temporal stores are intended for data that will not be reused soon.
882 ///
883 /// # SAFETY
884 /// The caller must ensure that the pointer is valid, aligned, and points to a memory region
885 /// that is at least `Self::Lanes` elements long.
886 unsafe fn store_streaming(self, ptr: *mut Self::Element);
887
888 /// Interleave two vectors at **group granularity**: blocks of `GROUP` consecutive elements move
889 /// as a unit and are never split. `GROUP == 1` is [`interleave`](Interleave::interleave); `GROUP == 2`
890 /// is the complex interleave - `lo == [a.c0, b.c0, a.c1, b.c1, ...]` over the low half of the
891 /// groups, `hi` over the high half - which lowers to the doubled-element unpack (`unpacklo_pd` +
892 /// `permute2f128` on AVX2, `zip` on NEON) rather than a general permute. The primitive for
893 /// complex FFT transposes and any group-structured SIMD. `GROUP` must divide `LANES`.
894 ///
895 /// The register-level default forwards `GROUP == 1` to [`interleave`](Interleave::interleave) and uses
896 /// a lane-wise fallback otherwise; backends override the group sizes they do natively.
897 fn interleave_by<const GROUP: usize>(self, other: Self) -> (Self, Self);
898
899 /// The inverse of [`interleave_by`](Self::interleave_by) - group-granularity de-interleave.
900 fn deinterleave_by<const GROUP: usize>(self, other: Self) -> (Self, Self);
901
902 /// Radix-`N` interleave: the generic sibling of [`interleave`](Interleave::interleave)
903 /// (`N == 2`). Treats the `N` inputs as one contiguous `N * LANES` span and
904 /// gives `out` with `concat(out)[q * N + r] == inputs[r].extract(q)`.
905 ///
906 /// `N` is inferred from the array length, so no turbofish is needed:
907 /// `V::interleave_radix([a, b])` is the 2-way interleave. `N == 2` reuses the
908 /// native `interleave`, `N == 3` a native radix-3 register sequence; any other
909 /// `N` uses a single permute+blend gather. For the AoS<->SoA memory form over
910 /// arbitrary `N`, use [`load_deinterleaved`](Self::load_deinterleaved) /
911 /// [`store_interleaved`](Self::store_interleaved) instead.
912 fn interleave_radix<const N: usize>(inputs: [Self; N]) -> [Self; N];
913
914 /// The inverse of [`interleave_radix`](Self::interleave_radix) - radix-`N`
915 /// de-interleave: `out[r].extract(q) == concat(inputs)[q * N + r]`.
916 fn deinterleave_radix<const N: usize>(inputs: [Self; N]) -> [Self; N];
917
918 /// Group-granularity radix-`N` de-interleave: the two-axis unification of
919 /// [`deinterleave_radix`](Self::deinterleave_radix) (`GROUP == 1`) and
920 /// [`deinterleave_by`](Self::deinterleave_by) (`N == 2`). Each vector is viewed
921 /// as `LANES / GROUP` groups of `GROUP` consecutive elements; `out[r]` group `q`
922 /// is the `(q * N + r)`-th group of the concatenated input sequence, each group
923 /// moving as a unit.
924 ///
925 /// The square case `N == LANES / GROUP` is a register-array transpose of
926 /// `GROUP`-wide elements: `deinterleave_radix_by::<4, 2>` on 8-lane f32 is the
927 /// 4x4 interleaved-complex transpose (8 ops on AVX2), and
928 /// `deinterleave_radix_by::<4, 1>` on f64x4 is the 4x4 `f64` transpose - the
929 /// primitives for FFT codelets and small matrices. `GROUP` must divide `LANES`.
930 fn deinterleave_radix_by<const N: usize, const GROUP: usize>(inputs: [Self; N]) -> [Self; N];
931
932 /// The inverse of [`deinterleave_radix_by`](Self::deinterleave_radix_by) -
933 /// group-granularity radix-`N` interleave. For the square case it is the same
934 /// (self-inverse) register-array transpose.
935 fn interleave_radix_by<const N: usize, const GROUP: usize>(inputs: [Self; N]) -> [Self; N];
936
937 /// Load `N` interleaved (array-of-structures) streams and de-interleave them
938 /// into `N` vectors: reads `N * LANES` contiguous elements from `ptr` and
939 /// returns `out` with `out[j].extract(lane) == ptr[lane * N + j]`.
940 ///
941 /// The AoS -> SoA load. `N == 3` over `f32` is the classic case: a
942 /// `[[f32; 3]]` of `xyzxyzxyz...` becomes one vector each of `xxx`, `yyy`,
943 /// `zzz`. ARM lowers this to a single `LD2`/`LD3`/`LD4` (the de-interleave
944 /// happens in the load unit); elsewhere it is contiguous loads plus a
945 /// cross-register permute.
946 ///
947 /// No alignment is required beyond that of `Element`.
948 ///
949 /// # SAFETY
950 /// `ptr` must be valid for reads of `N * LANES` elements.
951 unsafe fn load_deinterleaved<const N: usize>(ptr: *const Self::Element) -> [Self; N];
952
953 /// Interleave `N` vectors and store them contiguously as an
954 /// array-of-structures: writes `N * LANES` elements such that
955 /// `ptr[lane * N + j] == values[j].extract(lane)`.
956 ///
957 /// The SoA -> AoS store, and the exact inverse of
958 /// [`load_deinterleaved`](Self::load_deinterleaved). Lowers to `ST2`/`ST3`/`ST4`
959 /// on ARM. No alignment is required beyond that of `Element`.
960 ///
961 /// # SAFETY
962 /// `ptr` must be valid for writes of `N * LANES` elements.
963 unsafe fn store_interleaved<const N: usize>(ptr: *mut Self::Element, values: [Self; N]);
964
965 /// Load `M` interleaved AoS records of `C` components each and de-interleave
966 /// them: reads `M * C * LANES` contiguous elements, and `out[j][c]` holds
967 /// component `c` of record `j`
968 /// (`out[j][c].extract(lane) == ptr[lane * M * C + j * C + c]`).
969 ///
970 /// This is the AoS -> SoA load for structured data: an array of 3D points is
971 /// `M = 1, C = 3`; an array of rays (origin + direction) is `M = 2, C = 3`.
972 /// See
973 /// [`Register::load_deinterleaved_arrays`](crate::register::Register::load_deinterleaved_arrays)
974 /// for how a backend serves it (NEON: an `LD3` per chunk).
975 ///
976 /// The default is a lane-wise gather - correct for ANY vector type, but
977 /// scalar. [`Vector`] overrides it with the register engine.
978 ///
979 /// # SAFETY
980 /// `ptr` must be valid for reads of `M * C * LANES` elements.
981 unsafe fn load_deinterleaved_arrays<const M: usize, const C: usize>(
982 ptr: *const Self::Element,
983 ) -> [[Self; C]; M] {
984 const { assert!(M >= 1 && C >= 1) };
985
986 let mut out = [[Self::EMPTY; C]; M];
987
988 let mut j = 0;
989 while j < M {
990 let mut c = 0;
991 while c < C {
992 let mut v = Self::EMPTY;
993
994 let mut lane = 0;
995 while lane < Self::LANES {
996 v = v.insertv(lane, unsafe { ptr.add(lane * (M * C) + j * C + c).read_unaligned() });
997 lane += 1;
998 }
999
1000 out[j][c] = v;
1001 c += 1;
1002 }
1003 j += 1;
1004 }
1005
1006 out
1007 }
1008
1009 /// Interleave `M` records of `C` components and store them contiguously - the
1010 /// exact inverse of
1011 /// [`load_deinterleaved_arrays`](Self::load_deinterleaved_arrays), with the
1012 /// same lane-wise default.
1013 ///
1014 /// # SAFETY
1015 /// `ptr` must be valid for writes of `M * C * LANES` elements.
1016 unsafe fn store_interleaved_arrays<const M: usize, const C: usize>(ptr: *mut Self::Element, values: [[Self; C]; M]) {
1017 const { assert!(M >= 1 && C >= 1) };
1018
1019 let mut j = 0;
1020 while j < M {
1021 let mut c = 0;
1022 while c < C {
1023 let v = values[j][c];
1024
1025 let mut lane = 0;
1026 while lane < Self::LANES {
1027 unsafe { ptr.add(lane * (M * C) + j * C + c).write_unaligned(v.extractv(lane)) };
1028 lane += 1;
1029 }
1030 c += 1;
1031 }
1032 j += 1;
1033 }
1034 }
1035
1036 /// Load `M` interleaved composite streams of `1 + TAIL` components each and
1037 /// de-interleave them into `M` [`StreamGroup`]s: reads
1038 /// `M * (TAIL + 1) * LANES` contiguous elements, and group `j`'s
1039 /// `head`/`tail[c - 1]` hold the de-interleaved components of composite
1040 /// stream `j`. See [`StreamGroup`] for why the component count is a
1041 /// separate const generic, and
1042 /// [`Register::load_deinterleaved_grouped`](crate::register::Register::load_deinterleaved_grouped)
1043 /// for the register-level strategy.
1044 ///
1045 /// The default is a lane-wise gather: correct for ANY vector type, but
1046 /// scalar. [`Vector`] overrides it with the register engine; a composite
1047 /// vector (dual numbers, compensated floats) instead implements its plain
1048 /// [`load_deinterleaved`](Self::load_deinterleaved) by calling *its inner
1049 /// vector's* grouped op with the composite's component count folded into
1050 /// `TAIL`. Only a composite nested inside another composite ever reaches
1051 /// this default - at that point layout-aware shuffling has run out of road,
1052 /// and correctness is all that is on offer.
1053 ///
1054 /// # SAFETY
1055 /// `ptr` must be valid for reads of `M * (TAIL + 1) * LANES` elements.
1056 unsafe fn load_deinterleaved_grouped<const M: usize, const TAIL: usize>(
1057 ptr: *const Self::Element,
1058 ) -> [StreamGroup<Self, TAIL>; M] {
1059 const { assert!(M >= 1) };
1060
1061 let c = TAIL + 1;
1062
1063 let mut out = [StreamGroup { head: Self::EMPTY, tail: [Self::EMPTY; TAIL] }; M];
1064
1065 let mut j = 0;
1066 while j < M {
1067 let mut comp = 0;
1068 while comp < c {
1069 let mut v = Self::EMPTY;
1070
1071 let mut lane = 0;
1072 while lane < Self::LANES {
1073 v = v.insertv(lane, unsafe { ptr.add(lane * (M * c) + j * c + comp).read_unaligned() });
1074 lane += 1;
1075 }
1076
1077 if comp == 0 {
1078 out[j].head = v;
1079 } else {
1080 out[j].tail[comp - 1] = v;
1081 }
1082 comp += 1;
1083 }
1084 j += 1;
1085 }
1086
1087 out
1088 }
1089
1090 /// Interleave `M` [`StreamGroup`]s and store them as a contiguous
1091 /// array-of-structures - the exact inverse of
1092 /// [`load_deinterleaved_grouped`](Self::load_deinterleaved_grouped), with
1093 /// the same lane-wise default and the same override expectations.
1094 ///
1095 /// # SAFETY
1096 /// `ptr` must be valid for writes of `M * (TAIL + 1) * LANES` elements.
1097 unsafe fn store_interleaved_grouped<const M: usize, const TAIL: usize>(
1098 ptr: *mut Self::Element,
1099 values: [StreamGroup<Self, TAIL>; M],
1100 ) {
1101 const { assert!(M >= 1) };
1102
1103 let c = TAIL + 1;
1104
1105 let mut j = 0;
1106 while j < M {
1107 let mut comp = 0;
1108 while comp < c {
1109 let v = if comp == 0 { values[j].head } else { values[j].tail[comp - 1] };
1110
1111 let mut lane = 0;
1112 while lane < Self::LANES {
1113 unsafe { ptr.add(lane * (M * c) + j * c + comp).write_unaligned(v.extractv(lane)) };
1114 lane += 1;
1115 }
1116 comp += 1;
1117 }
1118 j += 1;
1119 }
1120 }
1121
1122 /// Assemble a vector from a slice of elements and a vector of indices
1123 /// into that slice. If an index is outside the bounds of the given slice,
1124 /// the resulting lane will be the first element of the input slice.
1125 ///
1126 /// This is semantically equivalent to `gather`, but specialized for small lookup tables approximately
1127 /// the same size as the vector itself. If the lookup table is too large, it will fall back to `gather`.
1128 fn lookup(values: &[Self::Element], indices: Self::Unsigned) -> Self {
1129 let in_bounds = indices.cmp_lt(Self::len_to_indices::<Self::Unsigned>(values.len()));
1130
1131 unsafe { Self::lookup_unchecked(values, indices.zz(in_bounds)) }
1132 }
1133
1134 /// Assemble a vector from a slice of elements and a vector of indices
1135 /// into that slice. The indices are NOT checked to be within bounds.
1136 ///
1137 /// # Safety
1138 /// The caller must ensure that the indices are within bounds for the given values slice,
1139 /// otherwise this may panic or result in undefined behavior.
1140 unsafe fn lookup_unchecked(values: &[Self::Element], indices: Self::Unsigned) -> Self;
1141
1142 /// Broadcast the value of a single lane across all lanes of the vector.
1143 #[conditional] fn broadcast<const I: usize>(self) -> Self;
1144
1145 /// Broadcast the value of a single lane across all lanes of the vector.
1146 ///
1147 /// # Panics
1148 /// If `idx` is out of bounds for the vector's lanes.
1149 #[conditional] fn broadcastv(self, idx: usize) -> Self;
1150
1151 /// Extract a single element from the vector at the const-generic index `I`.
1152 ///
1153 /// Because `I` is known at compile time, the backend can lower this to a
1154 /// single instruction (e.g. `pextrd`) with no runtime branch.
1155 ///
1156 /// # Compile-time errors
1157 /// `I` must be less than [`LANES`](Self::LANES).
1158 fn extract<const I: usize>(self) -> Self::Element;
1159
1160 /// Extract lane 0 -- the scalar counterpart to [`single`](Self::single).
1161 ///
1162 /// `single` is the way *into* a vector from a bare scalar; this is the way back
1163 /// out. Together they are the whole scalar/SIMD boundary, and neither is free:
1164 /// lane 0 lives in a vector register, so reading it costs a cross-domain move
1165 /// (and a shuffle on backends without a lane-0 extract). Mixing scalar and SIMD
1166 /// code pays that on every crossing -- prefer staying in vector form.
1167 ///
1168 /// Equivalent to `self.extract::<0>()`, and lowered identically.
1169 #[inline(always)]
1170 fn first(self) -> Self::Element {
1171 self.extract::<0>()
1172 }
1173
1174 /// Extract a single element from the vector at the runtime index `idx`.
1175 ///
1176 /// Prefer [`extract`](Self::extract) when the index is known at compile
1177 /// time; this variant typically lowers to a small jump table or per-lane
1178 /// blend and is slower.
1179 ///
1180 /// # Panics
1181 /// If `idx` is out of bounds for the vector's lanes.
1182 fn extractv(self, idx: usize) -> Self::Element;
1183
1184 /// Replace a single element in the vector at the const-generic index `I`.
1185 ///
1186 /// Returns a new vector; the original is unmodified. The lane index is
1187 /// resolved at compile time.
1188 ///
1189 /// # Compile-time errors
1190 /// `I` must be less than [`LANES`](Self::LANES).
1191 fn insert<const I: usize>(self, value: Self::Element) -> Self;
1192
1193 /// Replace a single element in the vector at the runtime index `idx`.
1194 ///
1195 /// Prefer [`insert`](Self::insert) when the index is known at compile time.
1196 ///
1197 /// # Panics
1198 /// If `idx` is out of bounds for the vector's lanes.
1199 fn insertv(self, idx: usize, value: Self::Element) -> Self;
1200
1201 /// Reverse the order of the elements in the vector.
1202 ///
1203 /// For a vector `[a, b, c, d]` this returns `[d, c, b, a]`.
1204 #[conditional] fn reverse(self) -> Self;
1205
1206 /// Swap the byte order of each element in the vector, converting between
1207 /// little-endian and big-endian representations lane-by-lane.
1208 ///
1209 /// Only the bytes within each element are reordered; lane order is
1210 /// preserved. For a `u32` vector `[0x11223344]` this returns `[0x44332211]`.
1211 #[conditional] fn swap_bytes(self) -> Self;
1212
1213 /// (Zero If False) Zero elements if the corresponding mask lane is false; otherwise, leave unchanged.
1214 ///
1215 /// Similar to a `mask & self` operation.
1216 fn zz(self, mask: Self::Mask) -> Self;
1217
1218 /// (Zero If True) Zero elements if the corresponding mask lane is true; otherwise, leave unchanged.
1219 ///
1220 /// Similar to a `!mask & self` operation.
1221 fn nz(self, mask: Self::Mask) -> Self;
1222
1223 /// Construct a mask whose first `n` lanes are `true` and the remaining
1224 /// lanes `false`.
1225 ///
1226 /// `n` is clamped to [`LANES`](Self::LANES): `n >= LANES` yields an
1227 /// all-`true` mask and `n == 0` an all-`false` mask. This is the canonical
1228 /// tail-handling helper - given a remainder of `k < LANES` elements,
1229 /// `Self::prefix_mask(k)` selects exactly those lanes for a masked store,
1230 /// [`select`](crate::mask::GenericMask::select), or `_c`/`_m`/`_z`
1231 /// operation.
1232 ///
1233 /// Semantically `Self::indexed() < n` lifted into the mask domain, but
1234 /// available on any [`GenericVector`] (the predicate is built on
1235 /// [`Unsigned`](Self::Unsigned), so it does not require `Self: NumericVector`).
1236 #[inline(always)]
1237 fn prefix_mask(n: usize) -> Self::Mask {
1238 let n = if n > Self::lanes() { Self::lanes() } else { n };
1239 let limit = Self::len_to_indices::<Self::Unsigned>(n);
1240 Self::Unsigned::indexed().cmp_lt(limit).cast::<Self::Mask>()
1241 }
1242
1243 /// Construct a mask whose last `n` lanes are `true` and the remaining
1244 /// lanes `false`.
1245 ///
1246 /// `n` is clamped to [`LANES`](Self::LANES). This is the high-lane
1247 /// companion to [`prefix_mask`](Self::prefix_mask); for example
1248 /// `Self::suffix_mask(2)` on a 4-lane vector selects lanes 2 and 3.
1249 #[inline(always)]
1250 fn suffix_mask(n: usize) -> Self::Mask {
1251 let n = if n > Self::lanes() { Self::lanes() } else { n };
1252 let start = Self::len_to_indices::<Self::Unsigned>(Self::lanes() - n);
1253 Self::Unsigned::indexed().cmp_ge(start).cast::<Self::Mask>()
1254 }
1255
1256 /// Left-pack (a.k.a. `compress`): gather the lanes where `mask` is `true`
1257 /// into the low lanes, preserving their relative order. The unselected lanes
1258 /// are *kept* (not zeroed) and packed into the high lanes, also in order - a
1259 /// stable partition of the vector by `mask`.
1260 ///
1261 /// For `[a, b, c, d]` with `mask = [true, false, true, false]` this returns
1262 /// `[a, c, b, d]`. Combined with a masked store of the leading `mask`-count
1263 /// lanes, this is the building block for stream compaction - whitespace
1264 /// stripping, filtering, JSON minification, and similar. For the zero-filled
1265 /// tail variant, see [`compress_z`](Self::compress_z).
1266 ///
1267 /// Lowers to AVX-512 `vpcompress*` where available; otherwise a portable
1268 /// scalar partition (some backends accelerate it with a permute table).
1269 fn compress(self, mask: Self::Mask) -> Self;
1270
1271 /// Zero-filling left-pack: like [`compress`](Self::compress), but the lanes
1272 /// beyond the `mask` population count are zeroed instead of holding the
1273 /// unselected elements. Matches AVX-512 zero-masking `vpcompress*`.
1274 ///
1275 /// For `[a, b, c, d]` with `mask = [true, false, true, false]` this returns
1276 /// `[a, c, 0, 0]`.
1277 fn compress_z(self, mask: Self::Mask) -> Self;
1278
1279 /// Merge-masked left-pack: like [`compress`](Self::compress), but the lanes
1280 /// at and beyond the `mask` population count take their values from `src`
1281 /// (at their own positions). Matches AVX-512 merge-masked `vpcompress*`.
1282 ///
1283 /// This is the accumulator step of a buffered stream compactor: pack
1284 /// `self`'s selected lanes to the front while retaining `src`'s tail, then
1285 /// [`align`](Self::align) by the running count.
1286 ///
1287 /// For `self = [a, b, c, d]`, `src = [w, x, y, z]`,
1288 /// `mask = [true, false, true, false]` this returns `[a, c, y, z]`.
1289 fn compress_m(self, src: Self, mask: Self::Mask) -> Self;
1290
1291 /// Inverse left-pack (`expand`): scatter this vector's packed low lanes back
1292 /// out to the lanes where `mask` is set, preserving order; the unselected
1293 /// lanes read the tail. The **exact inverse permutation** of
1294 /// [`compress`](Self::compress):
1295 /// `v.compress(m).expand(m) == v` and `v.expand(m).compress(m) == v` for
1296 /// every `v` and `m`.
1297 ///
1298 /// For `[a, c, b, d]` with `mask = [true, false, true, false]` this returns
1299 /// `[a, b, c, d]` - the return trip of stream compaction (compact the
1300 /// active lanes, operate on the packed front, expand the results back to
1301 /// their home lanes).
1302 fn expand(self, mask: Self::Mask) -> Self;
1303
1304 /// Zero-filling inverse left-pack: like [`expand`](Self::expand), but the
1305 /// unselected lanes are zeroed. Matches AVX-512 zero-masking `vpexpand*`.
1306 ///
1307 /// For `[a, c, _, _]` with `mask = [true, false, true, false]` this returns
1308 /// `[a, 0, c, 0]`.
1309 fn expand_z(self, mask: Self::Mask) -> Self;
1310
1311 /// Merge-masked inverse left-pack: like [`expand`](Self::expand), but the
1312 /// unselected lanes take their values from `src`. Matches AVX-512
1313 /// merge-masked `vpexpand*`.
1314 ///
1315 /// For `self = [a, c, _, _]`, `src = [w, x, y, z]`,
1316 /// `mask = [true, false, true, false]` this returns `[a, x, c, z]` -
1317 /// equivalent to `mask.select(self.expand(mask), src)`.
1318 fn expand_m(self, src: Self, mask: Self::Mask) -> Self;
1319
1320 /// Two-register element align (the `palignr` family): the window of `LANES`
1321 /// lanes starting at lane `OFFSET` of the concatenation `[self, other]`
1322 /// (`self`'s lanes first, then `other`'s). `OFFSET == 0` returns `self`,
1323 /// `OFFSET == LANES` returns `other`; in between, lanes spill from the tail
1324 /// of `self` into the head of `other`.
1325 ///
1326 /// The cross-register sliding window used for scanning multi-byte
1327 /// delimiters / substrings across a load boundary. Works for any element
1328 /// type (it is pure lane movement); integer backends accelerate it with
1329 /// native byte aligns.
1330 fn align<const OFFSET: usize>(self, other: Self) -> Self;
1331
1332 /// Whether [`align`](Self::align) is a native cross-register instruction rather
1333 /// than the generic shuffle-and-blend fallback, forwarded from
1334 /// [`Register::HAS_NATIVE_ALIGN`](crate::register::Register::HAS_NATIVE_ALIGN).
1335 ///
1336 /// Both paths give the same results, so this only ever selects between
1337 /// lowerings: one instruction where the backend has a real align (`palignr`,
1338 /// `vext`, `i8x16.shuffle`), two permutes plus a blend where it has only
1339 /// variable permutes, and a scalar memory round-trip where it has neither.
1340 ///
1341 /// Gate on it when an algorithm is built from a *ladder* of aligns - the
1342 /// prefix-scan family is the one in-tree - since a ladder of emulated aligns can
1343 /// lose to walking the lanes outright. A composite vector forwards the flag from
1344 /// the vector it wraps, so `Dual`/`Compensated`/`Complex` report whatever their
1345 /// inner vector does.
1346 const HAS_NATIVE_ALIGN: bool;
1347
1348 /// Apply a function to each element in the vector, returning a new vector with the results.
1349 ///
1350 /// This is not explicitly SIMD-optimized, so may be slower than using native vector operations.
1351 fn map<F>(self, f: F) -> Self
1352 where
1353 F: Fn(Self::Element) -> Self::Element;
1354
1355 /// Fold the elements of the vector using the provided function and initial value.
1356 ///
1357 /// This is not explicitly SIMD-optimized, so may be slower than using native vector operations.
1358 fn fold<F>(self, init: Self::Element, f: F) -> Self::Element
1359 where
1360 F: Fn(Self::Element, Self::Element) -> Self::Element;
1361
1362 /// Reduce the elements of the vector using the provided function.
1363 ///
1364 /// This is not explicitly SIMD-optimized, so may be slower than using native vector operations.
1365 fn reduce<F>(self, f: F) -> Self::Element
1366 where
1367 F: Fn(Self::Element, Self::Element) -> Self::Element;
1368
1369 /// Numeric cast to another vector type, matching the semantics of Rust's
1370 /// `as` operator on the underlying scalar elements **for in-range, finite
1371 /// inputs**.
1372 ///
1373 /// Lane count is preserved; only the element type changes. The cast may
1374 /// be widening, narrowing, signed/unsigned, or float/int.
1375 ///
1376 /// Float-to-int lanes that are NaN or out of the destination's range
1377 /// produce a backend-defined value (x86 hardware conversions return the
1378 /// "indefinite" integer, `INT::MIN`, where scalar `as` would saturate).
1379 /// For exact `as` semantics on every input (NaN gives 0, out-of-range
1380 /// clamps) use [`saturating_cast`](Self::saturating_cast).
1381 ///
1382 /// The `strict_ieee754` feature points every **float-source** cast at the
1383 /// saturating implementation, so the two agree under it. Integer sources
1384 /// are left alone, deliberately: `as` wraps for int-to-int and so does
1385 /// `cast`, so redirecting them would clamp where the language wraps.
1386 #[inline(always)] fn cast<INTO>(self) -> INTO
1387 where
1388 INTO: CastVector<Self>,
1389 {
1390 INTO::cast_from(self)
1391 }
1392
1393 /// Fast numeric cast to another vector type.
1394 ///
1395 /// Equivalent to [`cast`](Self::cast) when the backend has no faster path,
1396 /// but may relax IEEE corner cases (NaN propagation, out-of-range
1397 /// float-to-int handling) in exchange for fewer instructions.
1398 ///
1399 /// Use [`cast`](Self::cast) when you need the documented `as` semantics
1400 /// exactly; use this when you have already ruled out problematic inputs.
1401 #[inline(always)] fn fast_cast<INTO>(self) -> INTO
1402 where
1403 INTO: CastVector<Self>,
1404 {
1405 INTO::fast_cast_from(self)
1406 }
1407
1408 /// Reinterpret the bits of this vector as another vector type of the same
1409 /// size and lane count.
1410 ///
1411 /// This is a zero-cost transmute; no conversion is performed. Typical use
1412 /// is moving between a float vector and its integer "bits" vector for
1413 /// bit-level manipulation.
1414 #[inline(always)] fn into_bits<INTO>(self) -> INTO
1415 where
1416 INTO: BitCastVector<Self>,
1417 {
1418 INTO::from_bits(self)
1419 }
1420
1421 /// Cast that saturates (clamps) out-of-range values to the destination
1422 /// element range, rather than wrapping (integers) or producing a
1423 /// backend-defined value (float-to-int) like [`cast`](Self::cast).
1424 ///
1425 /// Distinct from [`cast`](Self::cast) for narrowing, same-signedness integer
1426 /// conversions (`i64 -> ... -> i8`, `u64 -> ... -> u8`) and for every
1427 /// float-to-int pair at a given lane count (`f32`/`f64` into any of
1428 /// `i8`/`i16`/`i32`/`i64` and their unsigned forms), where it has exact Rust
1429 /// `as` semantics. NaN gives 0, out-of-range clamps to MIN/MAX.
1430 ///
1431 /// Every other conversion resolves too, falling through to
1432 /// [`cast`](Self::cast) because it has no separate saturating lowering.
1433 /// That is exact for widening conversions, which lose nothing. It is *not*
1434 /// what the name suggests for sign-changing integer casts, which wrap:
1435 /// there is no saturating `i32 -> u32`, so ask for [`cast`](Self::cast)
1436 /// and say what you meant.
1437 #[inline(always)] fn saturating_cast<INTO>(self) -> INTO
1438 where
1439 INTO: CastVector<Self>,
1440 {
1441 INTO::saturating_cast_from(self)
1442 }
1443}
1444
1445/// Bitwise operations over the lanes of a vector: `&`, `|`, `^`, `!`,
1446/// `bitandnot`, and the arbitrary three-input [`ternlog`](Self::ternlog).
1447///
1448/// Implemented by integer and mask vectors. Float vectors have no direct bitwise
1449/// ops, so reach their bits through [`FloatVectorWithBits`] first.
1450///
1451/// Note that `a.bitandnot(b)` is `a & !b` at this layer.
1452#[rustfmt::skip] #[thermite_macros::vector_trait]
1453#[diagnostic::on_unimplemented(
1454 message = "`{Self}` does not support bitwise vector operations",
1455 label = "no `&`, `|`, `^`, `!`, andnot, or ternlog",
1456 note = "`BitwiseVector` is implemented by integer and mask vectors. Floating-point vectors have no direct bitwise ops; reach their bits via `FloatVectorWithBits` (`.to_bits()` / reinterpret) first."
1457)]
1458pub trait BitwiseVector:
1459 GenericVector
1460 + ops::BitAndMasked<Self::Mask, Self, Output = Self>
1461 + ops::BitAndAssignMasked<Self::Mask, Self>
1462 + ops::BitAndNotMasked<Self::Mask, Self, Output = Self>
1463 + ops::BitAndNotAssignMasked<Self::Mask, Self>
1464 + ops::BitOrMasked<Self::Mask, Self, Output = Self>
1465 + ops::BitOrAssignMasked<Self::Mask, Self>
1466 + ops::BitXorMasked<Self::Mask, Self, Output = Self>
1467 + ops::BitXorAssignMasked<Self::Mask, Self>
1468 + ops::NotMasked<Self::Mask, Output = Self>
1469{
1470 /// Computes an arbitrary bitwise boolean function of three inputs (`a`, `b`, `c`)
1471 /// based on the truth table specified by `IMM`.
1472 ///
1473 /// This function is a "programmable logic gate". It applies the logic defined in `IMM`
1474 /// to every bit of the inputs in parallel.
1475 ///
1476 /// # How to Calculate `IMM`
1477 /// The easiest way to find the correct `IMM` value is to perform your desired boolean
1478 /// logic on these three specific "Magic Constants":
1479 ///
1480 /// * **A** = `0xF0` (Binary `11110000`)
1481 /// * **B** = `0xCC` (Binary `11001100`)
1482 /// * **C** = `0xAA` (Binary `10101010`)
1483 ///
1484 /// ## Example: `(A OR B) XOR C`
1485 /// 1. `A | B` = `0xF0 | 0xCC` = `0xFC`
1486 /// 2. `Result ^ C` = `0xFC ^ 0xAA` = `0x56`
1487 /// 3. Therefore, `IMM = 0x56`.
1488 ///
1489 /// You can also use the [`ternlog_imm!`](crate::ternlog_imm) macro to compute
1490 /// this at compile time.
1491 ///
1492 /// # Visualization using Disjunction Normal Form (DNF)
1493 /// The constants `0xF0`, `0xCC`, and `0xAA` simply form a parallel truth table
1494 /// for all 8 possible combinations of 3 bits:
1495 ///
1496 /// | A | B | C | Bit Index | Term Logic (Minterm) |
1497 /// |:---:|:---:|:---:|:-----------:|:---------------------:|
1498 /// | 0 | 0 | 0 | 0 | ~A & ~B & ~C |
1499 /// | 0 | 0 | 1 | 1 | ~A & ~B & C |
1500 /// | 0 | 1 | 0 | 2 | ~A & B & ~C |
1501 /// | 0 | 1 | 1 | 3 | ~A & B & C |
1502 /// | 1 | 0 | 0 | 4 | A & ~B & ~C |
1503 /// | 1 | 0 | 1 | 5 | A & ~B & C |
1504 /// | 1 | 1 | 0 | 6 | A & B & ~C |
1505 /// | 1 | 1 | 1 | 7 | A & B & C |
1506 ///
1507 /// If `IMM = 0x88` (Bit 3 and 7 set), the logic is:
1508 /// - Bit 3 (0, 1, 1): `~A & B & C`
1509 /// - Bit 7 (1, 1, 1): `A & B & C`
1510 ///
1511 /// As raw DNF, this becomes: `(~A & B & C) | (A & B & C)`.\
1512 /// `~A` and `A` cancel out, simplifying to `B & C`.
1513 ///
1514 /// For each bit set in IMM, we effectively bitwise-OR each corresponding minterm.
1515 ///
1516 /// # Common Immediate Values
1517 /// | Logic | Immediate | Description |
1518 /// | :--- | :--- | :--- |
1519 /// | `A ^ B ^ C` | `0x96` | **3-Way XOR** (Parity) |
1520 /// | `(A & B) OR (~A & C)` | `0xCA` | **Bitwise Select** (If A=1 use B, else use C) |
1521 /// | `(A & B) OR (A & C) OR (B & C)` | `0xE8` | **Majority** (True if 2+ inputs are 1) |
1522 /// | `A OR B OR C` | `0xFE` | **3-Way OR** |
1523 /// | `A ? B : 0` | `0xA0` | **Mask** (A & B) |
1524 ///
1525 /// # Performance Note
1526 /// Since `IMM` is a compile-time constant, the compiler will optimize this function
1527 /// into the most efficient sequence of native instructions (AND, OR, XOR, NOT)
1528 /// for your specific architecture. If using AVX512, there actually exists a single
1529 /// instruction for this.
1530 ///
1531 /// Gate on [`HAS_NATIVE_TERNLOG`](Self::HAS_NATIVE_TERNLOG) when choosing
1532 /// between a ternlog bit assembly and a `select`/blend chain.
1533 #[conditional] fn ternlog<const IMM: i32>(a: Self, b: Self, c: Self) -> Self;
1534
1535 /// Whether [`ternlog`](Self::ternlog) is a single native instruction
1536 /// (AVX-512 `vpternlog{d,q}`), forwarded from
1537 /// [`BitwiseRegister::HAS_NATIVE_TERNLOG`](crate::register::BitwiseRegister::HAS_NATIVE_TERNLOG).
1538 ///
1539 /// Both paths compute the same function, so this only selects a lowering:
1540 /// one instruction where the hardware has ternary logic, up to eight DNF
1541 /// terms of AND/ANDNOT/OR where it does not. Fork on it when the
1542 /// alternative to a ternlog assembly is a `blendv`-style select chain -
1543 /// below AVX-512 the blends win (measured on znver3 in `ldexp`'s checked
1544 /// tail: 3.8 cyc/iter for blends against 5.8 for ternlogs), while a native
1545 /// ternlog makes the bit assembly strictly cheaper.
1546 const HAS_NATIVE_TERNLOG: bool;
1547
1548 /// Two-input version of [`ternlog`](Self::ternlog).
1549 ///
1550 /// Computes an arbitrary bitwise boolean function of two inputs (`a`, `b`)
1551 /// based on the 4-bit truth table specified by the low nibble of `IMM`.
1552 /// Bit `i` of `IMM` selects the output when `(a, b)` equals the binary
1553 /// representation of `i`. As with `ternlog`, the magic constants are
1554 /// `A = 0xC` (`1100`) and `B = 0xA` (`1010`); evaluate your desired logic
1555 /// against them to obtain `IMM`. For example, `A & B == 0x8`, `A | B == 0xE`,
1556 /// `A ^ B == 0x6`, `!A == 0x3`.
1557 ///
1558 /// Since `IMM` is a compile-time constant, the compiler lowers this to
1559 /// the most efficient native instruction sequence for the target ISA.
1560 #[conditional] fn bilog<const IMM: i32>(a: Self, b: Self) -> Self;
1561}
1562
1563/// Shifts and rotates over the lanes of an integer vector, by an immediate, by a
1564/// runtime scalar, or by a per-lane count.
1565///
1566/// `<<` and `>>` are the operator forms. **`>>` is a logical shift even on a
1567/// signed vector**, since the operator traits are shared with the unsigned
1568/// vectors. Sign-filling shifts live on [`SignedIntegerVector`] as
1569/// [`srai`](SignedIntegerVector::srai) / [`sra`](SignedIntegerVector::sra) /
1570/// [`srav`](SignedIntegerVector::srav).
1571///
1572/// Per-lane variable shifts ([`shlv`](Self::shlv) and friends) are one
1573/// instruction where the ISA has them (AVX2 `vpsllvd`) and a lane walk where it
1574/// does not, which [`HAS_TRUE_SHIFTV`](Self::HAS_TRUE_SHIFTV) reports.
1575#[rustfmt::skip] #[thermite_macros::vector_trait]
1576#[diagnostic::on_unimplemented(
1577 message = "`{Self}` does not support bit-shift vector operations",
1578 label = "no `<<`, `>>`, rotate, or byte-shift",
1579 note = "`BitshiftVector` is implemented by integer vectors (`Vector<i32>`, `Vector<u8>`, `i32xN`, ...). Float and mask vectors do not have shifts."
1580)]
1581pub trait BitshiftVector:
1582 BitwiseVector
1583 + ops::ShrMasked<Self::Mask, Self::Unsigned, Output = Self>
1584 + ops::ShrAssignMasked<Self::Mask, Self::Unsigned>
1585 + ops::ShlMasked<Self::Mask, Self::Unsigned, Output = Self>
1586 + ops::ShlAssignMasked<Self::Mask, Self::Unsigned>
1587 + ops::ShrMasked<Self::Mask, u32, Output = Self>
1588 + ops::ShrAssignMasked<Self::Mask, u32>
1589 + ops::ShlMasked<Self::Mask, u32, Output = Self>
1590 + ops::ShlAssignMasked<Self::Mask, u32>
1591{
1592 /// `true` if the backend has a true per-lane variable shift instruction
1593 /// (e.g. AVX2 `vpsllvd`). When `false`, [`shlv`](Self::shlv) /
1594 /// [`shrv`](Self::shrv) are emulated and may be slower than splatting a
1595 /// scalar shift count through [`shli`](Self::shli) / [`shri`](Self::shri).
1596 const HAS_TRUE_SHIFTV: bool;
1597
1598 /// `true` if the backend can byte-shift the entire vector as a single
1599 /// large integer at register widths above 128 bits without lane-boundary
1600 /// stitching. When `false`, [`bshli`](Self::bshli) / [`bshri`](Self::bshri)
1601 /// on wider vectors are emulated via shuffles.
1602 const HAS_WIDE_BYTE_SHIFTS: bool;
1603
1604 /// Treats the entire vector as a single large integer and shifts left by the immediate value
1605 /// number of BYTES. Not bits, bytes.
1606 ///
1607 /// Bits shifted out at the high end are discarded; the low end is zero-filled.
1608 #[conditional] fn bshli<const I: i32>(self) -> Self;
1609
1610 /// Treats the entire vector as a single large integer and shifts right by the immediate value
1611 /// number of BYTES. Not bits, bytes.
1612 ///
1613 /// Bits shifted out at the low end are discarded; the high end is zero-filled.
1614 #[conditional] fn bshri<const I: i32>(self) -> Self;
1615
1616 /// For each lane in the vector, shift left by the immediate value.
1617 #[conditional] fn shli<const I: i32>(self) -> Self;
1618
1619 /// For each lane in the vector, shift right by the immediate value.
1620 #[conditional] fn shri<const I: i32>(self) -> Self;
1621
1622 /// For each lane in the vector, shift left by the given value.
1623 #[conditional] fn shlv(self, counts: Self::Unsigned) -> Self;
1624
1625 /// For each lane in the vector, shift right by the given value.
1626 #[conditional] fn shrv(self, counts: Self::Unsigned) -> Self;
1627
1628 /// For each element in the vector, rotate the bits to the left by the given
1629 /// number of bits.
1630 #[conditional] fn rol(self, shift: u32) -> Self;
1631 /// For each element in the vector, rotate the bits to the right by the given
1632 /// number of bits.
1633 #[conditional] fn ror(self, shift: u32) -> Self;
1634 /// For each element in the vector, rotate the bits to the left by the immediate
1635 /// value number of bits.
1636 #[conditional] fn roli<const I: i32>(self) -> Self;
1637 /// For each element in the vector, rotate the bits to the right by the immediate
1638 /// value number of bits.
1639 #[conditional] fn rori<const I: i32>(self) -> Self;
1640
1641 /// For each element in the vector, rotate the bits to the left by the given
1642 /// number of bits in the corresponding lane of `counts`.
1643 #[conditional] fn rolv(self, counts: Self::Unsigned) -> Self;
1644
1645 /// For each element in the vector, rotate the bits to the right by the given
1646 /// number of bits in the corresponding lane of `counts`.
1647 #[conditional] fn rorv(self, counts: Self::Unsigned) -> Self;
1648
1649 /// For each element in the vector, reverse the bits of that element.
1650 #[conditional] fn reverse_bits(self) -> Self;
1651}
1652
1653/// Per-lane numeric conversion between vector types.
1654///
1655/// Implementing `CastVector<FROM>` for `Self` means a `FROM` value can be
1656/// converted into `Self` with the same semantics as Rust's `as` operator on
1657/// the underlying scalar elements. Most users should call
1658/// [`GenericVector::cast`] rather than these methods directly.
1659///
1660/// # Float to int
1661///
1662/// Float to int matches `as` for in-range finite lanes, truncating toward zero.
1663/// A NaN or out-of-range lane gets a **backend-defined** value instead. x86's
1664/// hardware conversions hand back the "indefinite" integer (`INT::MIN`) for
1665/// every such lane, where scalar `as` gives 0 for NaN and clamps the rest.
1666///
1667/// The fixup is not free. `f32x4 -> i32x4` is one `cvttps2dq`, and the exact
1668/// form is 5 instructions (a compare against 2^31, an unordered compare, an XOR
1669/// and an ANDNOT on top of it). A kernel that has already bounded its inputs
1670/// would pay that on every cast, so `cast` keeps the bare conversion.
1671///
1672/// [`saturating_cast_from`](Self::saturating_cast_from), reached through
1673/// [`GenericVector::saturating_cast`], is the exact form, and covers every
1674/// float-to-int pair at a given lane count. `strict_ieee754` points
1675/// float-source `cast_from` at it too, so the two agree under that feature and
1676/// every such cast pays the 5 instructions.
1677///
1678/// Integer sources are left alone by that feature, deliberately. `as` wraps for
1679/// int-to-int, which is already what `cast_from` does, so redirecting them would
1680/// clamp where the language wraps.
1681pub trait CastVector<FROM: Sized>: Sized {
1682 /// Convert a vector of type `FROM` into `Self`, lane-by-lane, using `as`
1683 /// semantics on each element. See the trait docs for what float-to-int
1684 /// does with NaN and out-of-range lanes.
1685 fn cast_from(from: FROM) -> Self;
1686
1687 /// Convert this vector into a vector of type `FROM`, lane-by-lane.
1688 fn cast_into(self) -> FROM;
1689
1690 /// Convert lane-by-lane, clamping out-of-range values to `Self`'s element
1691 /// range rather than wrapping (integers) or producing a backend-defined
1692 /// value (float to int).
1693 ///
1694 /// Float to int is exactly Rust's `as` here. NaN gives 0, and anything out
1695 /// of range clamps to the destination MIN/MAX. Meaningful for narrowing
1696 /// same-sign integer pairs and for every float-to-int pair; conversions with
1697 /// no distinct saturating lowering (widening, sign-changing, float to float)
1698 /// fall through to [`cast_from`](Self::cast_from), which for the widening
1699 /// cases is already exact.
1700 #[inline(always)]
1701 fn saturating_cast_from(from: FROM) -> Self {
1702 Self::cast_from(from)
1703 }
1704
1705 /// Like [`cast_from`](Self::cast_from), but may take a faster path that
1706 /// relaxes IEEE corner cases. See [`GenericVector::fast_cast`].
1707 ///
1708 /// Float-to-int keeps its narrow domain in every configuration,
1709 /// `strict_ieee754` included. This is the operation whose out-of-range
1710 /// behavior is unspecified by definition, so that feature has nothing to
1711 /// tighten here.
1712 #[inline(always)]
1713 fn fast_cast_from(from: FROM) -> Self {
1714 Self::cast_from(from)
1715 }
1716
1717 /// Like [`cast_into`](Self::cast_into), but may take a faster path that
1718 /// relaxes IEEE corner cases. See [`GenericVector::fast_cast`].
1719 #[inline(always)]
1720 fn fast_cast_into(self) -> FROM {
1721 Self::cast_into(self)
1722 }
1723}
1724
1725/// Zero-cost bit-level reinterpretation between vector types of the same
1726/// size and lane count.
1727///
1728/// Unlike [`CastVector`], no numeric conversion is performed: the underlying
1729/// bits are reinterpreted as the destination element type. Typical use is
1730/// moving between a float vector and its integer "bits" vector.
1731pub trait BitCastVector<FROM: Sized>: Sized {
1732 /// Reinterpret the bit pattern of `bits` as a value of `Self`.
1733 fn from_bits(bits: FROM) -> Self;
1734}
1735
1736/// A `u16`/`u8` integer vector reinterpreted as a vector of *packed floats* (format `S`: fp16,
1737/// bfloat16, the fp8 variants, ...), transcodable to and from the wider `f32` vector `F` of the
1738/// same lane count.
1739///
1740/// This is the vector-layer mirror of
1741/// [`PackedFloatRegister`](crate::register::PackedFloatRegister): `Self` is the `Vector<u16/u8
1742/// register>` and `F` is the matching `Vector<f32 register>`. Both directions are exact for the
1743/// decode (every value of these sub-`f32` formats is representable in `f32`) and round-to-nearest
1744/// for the encode; backends use hardware (F16C `vcvtph2ps`) where available and a generic
1745/// branchless fallback otherwise.
1746///
1747/// Blanket-implemented for every `Vector<R>` whose register implements `PackedFloatRegister<S,
1748/// FR>`, so e.g. `u16x8<S>: PackedFloatVector<Fp16, f32x8<S>>` holds wherever the register does.
1749///
1750/// ```
1751/// # use thermite::prelude::*;
1752/// # use thermite::element::float::spec::Fp16;
1753/// # use thermite::vector::PackedFloatVector;
1754/// fn widen<U, F>(halves: U) -> F
1755/// where
1756/// U: PackedFloatVector<Fp16, F>,
1757/// {
1758/// halves.unpack()
1759/// }
1760/// ```
1761pub trait PackedFloatVector<S: crate::element::float::spec::FloatSpec, F>: GenericVector {
1762 /// Encode the `f32` vector `values` into this packed format (round to nearest, ties to even;
1763 /// overflow / non-finite handled per the format `S`).
1764 fn pack(values: F) -> Self;
1765
1766 /// Decode this packed-float vector into the `f32` vector it represents (exact).
1767 fn unpack(self) -> F;
1768}
1769
1770/// A `u8` vector whose absolute differences can be summed in groups of 2 byte-lanes into
1771/// the `u16` vector `W` (same total width, `LANES / 2` output lanes).
1772///
1773/// Vector-layer mirror of [`Sad16Register`](crate::register::Sad16Register);
1774/// blanket-implemented for every `Vector<R>` whose register implements it. Each output
1775/// lane is at most `510`. There is no accumulating form - a `u16` lane saturates after
1776/// ~128 accumulations; use [`Sad32Vector`] / [`Sad64Vector`] to reduce over a long run.
1777pub trait Sad16Vector<W>: GenericVector {
1778 /// Sum of absolute differences over each aligned pair of byte lanes.
1779 fn sad16(self, other: Self) -> W;
1780}
1781
1782/// A `u8` vector whose absolute differences can be summed in groups of 4 byte-lanes into
1783/// the `u32` vector `W` (same total width, `LANES / 4` output lanes).
1784///
1785/// Vector-layer mirror of [`Sad32Register`](crate::register::Sad32Register). Each output
1786/// lane is at most `1020`, so [`sad32_accum`](Self::sad32_accum) absorbs ~4.2e6
1787/// accumulations before overflow.
1788pub trait Sad32Vector<W>: GenericVector {
1789 /// Sum of absolute differences over each aligned group of 4 byte lanes.
1790 fn sad32(self, other: Self) -> W;
1791
1792 /// `acc + self.sad32(other)` - the accumulate step of a blocked SAD loop.
1793 fn sad32_accum(self, acc: W, other: Self) -> W;
1794}
1795
1796/// A `u8` vector whose absolute differences can be summed in groups of 8 byte-lanes into
1797/// the `u64` vector `W` (same total width, `LANES / 8` output lanes) - x86 `PSADBW`
1798/// semantics.
1799///
1800/// Vector-layer mirror of [`Sad64Register`](crate::register::Sad64Register). The `u64`
1801/// lanes are accumulation headroom (each result is at most `2040`), so the intended shape
1802/// of a byte-buffer reduction is to [`sad64_accum`](Self::sad64_accum) through the loop
1803/// and reduce horizontally exactly once at the end:
1804///
1805/// ```ignore
1806/// let mut acc = W::ZERO;
1807/// for (a, b) in blocks { acc = a.sad64_accum(acc, b); }
1808/// let total = acc.sum_elements();
1809/// ```
1810pub trait Sad64Vector<W>: GenericVector {
1811 /// Sum of absolute differences over each aligned group of 8 byte lanes.
1812 fn sad64(self, other: Self) -> W;
1813
1814 /// `acc + self.sad64(other)` - the accumulate step of a blocked SAD loop.
1815 fn sad64_accum(self, acc: W, other: Self) -> W;
1816}
1817
1818/// Lanes of a vector partitioned into groups of equal value, produced by
1819/// [`group_by_value`](PartialOrdVector::group_by_value).
1820///
1821/// Each call to [`next_group`](Self::next_group) yields one distinct value and
1822/// the mask of lanes holding it; groups come out in order of first occurrence,
1823/// and every selected lane is yielded exactly once. That turns a divergent
1824/// packet, whose lanes want different work, into a short sequence of uniform
1825/// sub-packets.
1826///
1827/// The inherent [`next_group`](Self::next_group) is the primary interface: a
1828/// plain `while let` loop needs no trait in scope and inlines predictably inside
1829/// `#[target_feature]` bodies. [`Iterator`] is implemented on top of it, so
1830/// `for` loops work too.
1831///
1832/// ```ignore
1833/// // Shade a ray packet one geometry at a time.
1834/// let mut groups = geom_ids.group_by_value(active);
1835/// while let Some((geom_id, lanes)) = groups.next_group() {
1836/// shade(geom_id, lanes);
1837/// }
1838/// ```
1839///
1840/// Cost is proportional to the number of *distinct* values, not the lane count:
1841/// roughly a broadcast, a compare, and two mask ops per group. A uniform packet
1842/// costs one iteration.
1843#[derive(Debug, Clone, Copy)]
1844pub struct ValueGroups<V: PartialOrdVector> {
1845 value: V,
1846 remaining: V::Mask,
1847}
1848
1849impl<V: PartialOrdVector> ValueGroups<V> {
1850 /// The next distinct value and the mask of remaining lanes holding it, or
1851 /// `None` once every selected lane has been yielded.
1852 #[inline(always)]
1853 pub fn next_group(&mut self) -> Option<(V::Element, V::Mask)> {
1854 let lane = self.remaining.first_set()?;
1855
1856 // `broadcastv` rather than `splat(extractv(..))`: one register op that
1857 // backends already specialize, instead of a lane -> scalar -> lane
1858 // round trip through memory.
1859 let group = self.remaining & self.value.cmp_eq(self.value.broadcastv(lane));
1860 let value = self.value.extractv(lane);
1861
1862 self.remaining = crate::vector::ops::BitAndNot::bitandnot(self.remaining, group);
1863
1864 Some((value, group))
1865 }
1866
1867 /// Lanes not yet yielded, so a caller can stop part-way and keep the rest.
1868 #[inline(always)]
1869 pub fn remaining(&self) -> V::Mask {
1870 self.remaining
1871 }
1872
1873 /// Whether every selected lane has been yielded.
1874 #[inline(always)]
1875 pub fn is_empty(&self) -> bool {
1876 self.remaining.none()
1877 }
1878}
1879
1880impl<V: PartialOrdVector> Iterator for ValueGroups<V> {
1881 type Item = (V::Element, V::Mask);
1882
1883 #[inline(always)]
1884 fn next(&mut self) -> Option<Self::Item> {
1885 self.next_group()
1886 }
1887}
1888
1889/// Per-lane comparison producing a [`Mask`](GenericVector::Mask).
1890///
1891/// Each comparison returns a mask whose lanes are `true` where the predicate
1892/// held for the corresponding lane pair and `false` otherwise. The mask can
1893/// then be used with [`select`](crate::mask::GenericMask::select),
1894/// `_c`/`_m`/`_z` masked variants, or reduced via
1895/// [`all`](crate::mask::GenericMask::all) /
1896/// [`any`](crate::mask::GenericMask::any).
1897///
1898/// For floating-point vectors, NaN compares unequal to everything, so e.g.
1899/// `cmp_lt(x, NaN)` is always `false`, matching the `<` operator on `f32`/`f64`.
1900#[diagnostic::on_unimplemented(
1901 message = "`{Self}` does not support lane-wise comparisons",
1902 label = "no `cmp_lt` / `cmp_le` / `cmp_gt` / `cmp_ge` / `cmp_eq` / `cmp_ne`",
1903 note = "`PartialOrdVector` turns lane-wise comparisons into a `Mask`; it is implemented by all numeric vectors (integer and float)."
1904)]
1905pub trait PartialOrdVector: GenericVector + PartialEq {
1906 /// Partition the lanes selected by `valid` into groups of equal value.
1907 ///
1908 /// See [`ValueGroups`] for the loop shape and cost. Pass
1909 /// `Self::Mask::TRUTHY` to group every lane.
1910 #[inline(always)]
1911 fn group_by_value(self, valid: Self::Mask) -> ValueGroups<Self> {
1912 ValueGroups {
1913 value: self,
1914 remaining: valid,
1915 }
1916 }
1917
1918 /// Lane-wise `self < other`.
1919 fn cmp_lt(self, other: Self) -> Self::Mask;
1920 /// Lane-wise `self <= other`.
1921 fn cmp_le(self, other: Self) -> Self::Mask;
1922 /// Lane-wise `self > other`.
1923 fn cmp_gt(self, other: Self) -> Self::Mask;
1924 /// Lane-wise `self >= other`.
1925 fn cmp_ge(self, other: Self) -> Self::Mask;
1926 /// Lane-wise `self == other`.
1927 fn cmp_eq(self, other: Self) -> Self::Mask;
1928 /// Lane-wise `self != other`.
1929 fn cmp_ne(self, other: Self) -> Self::Mask;
1930}
1931
1932/// Vectors that support arithmetic and comparison operations on their elements.
1933///
1934/// This trait sits between [`PartialOrdVector`] and the more specific
1935/// [`SignedVector`] / [`IntegerVector`] / [`FloatVector`] traits, and provides
1936/// the operator overloads (`+`, `-`, `*`, `/`, `%`, their `*Assign` variants,
1937/// and the masked `_c`/`_m`/`_z` forms via [`ops`]).
1938///
1939/// # Overflow semantics
1940///
1941/// **For integer element types, the basic arithmetic operators (`+`, `-`, `*`,
1942/// `/`, `%`) are wrapping on overflow.** This matches the behavior of every
1943/// SIMD ISA (`paddd`, `pmulld`, etc. all wrap silently) and avoids per-lane
1944/// panics inside vectorized loops. Concretely, on every backend including the
1945/// scalar reference backend, `Vector::<i32x4>::splat(i32::MAX) + Vector::ONE`
1946/// produces `i32::MIN` in every lane rather than panicking.
1947///
1948/// This is intentional and is **not affected by debug vs release builds**: the
1949/// scalar backend uses `wrapping_add` / `wrapping_sub` / `wrapping_mul`
1950/// internally, so the wrapping behavior is consistent across all build
1951/// configurations. If you need saturation or explicit wrapping naming, use
1952/// [`saturating_add`](IntegerVector::saturating_add) /
1953/// [`saturating_sub`](IntegerVector::saturating_sub), or the
1954/// `num_traits::WrappingAdd` / `WrappingSub` / `WrappingMul` impls.
1955///
1956/// Integer division (`/`, `%`) panics on division by zero, matching scalar
1957/// Rust. Float division by zero produces an infinity or NaN per IEEE 754.
1958///
1959/// For float element types, overflow simply produces an infinity per IEEE 754;
1960/// there is nothing to wrap.
1961#[rustfmt::skip] #[thermite_macros::vector_trait]
1962#[diagnostic::on_unimplemented(
1963 message = "`{Self}` does not support arithmetic vector operations",
1964 label = "no `+`, `-`, `*`, `/`, `%`, min/max, or FMA",
1965 note = "`NumericVector` is implemented by numeric vectors - integer (`Vector<i32>`, `i32xN`, ...) and float (`Vector<f32>`, `f32xN`, ...). Masks and bare scalars do not qualify.",
1966 note = "A bare `f32`/`f64` is not a vector: wrap it in `Vector::<f32>::splat(x)` first."
1967)]
1968pub trait NumericVector:
1969 PartialOrdVector<Element: num_traits::NumOps>
1970 + ops::AddMasked<Self::Mask, Self, Output = Self>
1971 + ops::AddAssignMasked<Self::Mask, Self>
1972 + ops::SubMasked<Self::Mask, Self, Output = Self>
1973 + ops::SubAssignMasked<Self::Mask, Self>
1974 + ops::MulMasked<Self::Mask, Self, Output = Self>
1975 + ops::MulAssignMasked<Self::Mask, Self>
1976 + ops::DivMasked<Self::Mask, Self, Output = Self>
1977 + ops::DivAssignMasked<Self::Mask, Self>
1978 + ops::RemMasked<Self::Mask, Self, Output = Self>
1979 + ops::RemAssignMasked<Self::Mask, Self>
1980 + ops::SquareMasked<Self::Mask, Output = Self>
1981 + num_traits::NumOps<Self>
1982 + num_traits::NumAssignOps<Self>
1983 + core::iter::Sum
1984 + core::iter::Product
1985{
1986 /// A vector of the value "0" in the element type.
1987 const ZERO: Self;
1988 /// A vector of the value "1" in the element type.
1989 const ONE: Self;
1990 /// A vector of the value "2" in the element type.
1991 const TWO: Self;
1992
1993 /// A vector of the minimum value the element type of this vector can represent.
1994 const MIN: Self;
1995 /// A vector of the maximum value the element type of this vector can represent.
1996 const MAX: Self;
1997
1998 /// Convert each lane to the companion signed integer type, with `as` semantics -
1999 /// round toward zero, saturating at the bounds, NaN to zero.
2000 ///
2001 /// This is the numeric conversion, *not* a bit reinterpretation; for the bit
2002 /// pattern of a float see [`GenericVector::into_bits`].
2003 ///
2004 /// # Why a method and not a `CastVector` bound
2005 ///
2006 /// A bound would have to be written either as `Self::Signed: CastVector<Self>`,
2007 /// whose impl `Self` type is an associated-type projection and so cannot be
2008 /// written at all, or as `Self: CastVector<Self::Signed>`, which collides with the
2009 /// blanket self-casts the composite types already carry. A method has no coherence
2010 /// surface and every implementor can simply provide it.
2011 fn to_signed_integer(self) -> Self::Signed;
2012
2013 /// Convert each lane from the companion signed integer type, with `as` semantics.
2014 ///
2015 /// For composite element types this produces a value with no imaginary part, no
2016 /// derivative and no error term: an integer carries none of those.
2017 fn from_signed_integer(v: Self::Signed) -> Self;
2018
2019 /// Convert each lane to the companion unsigned integer type, with `as` semantics.
2020 /// See [`to_signed_integer`](Self::to_signed_integer).
2021 fn to_unsigned_integer(self) -> Self::Unsigned;
2022
2023 /// Convert each lane from the companion unsigned integer type, with `as` semantics.
2024 /// See [`from_signed_integer`](Self::from_signed_integer).
2025 fn from_unsigned_integer(v: Self::Unsigned) -> Self;
2026
2027 /// Like [`to_signed_integer`](Self::to_signed_integer), but may relax IEEE corner
2028 /// cases (out-of-range and NaN inputs) for speed. Defaults to the exact form.
2029 #[inline(always)]
2030 fn fast_to_signed_integer(self) -> Self::Signed {
2031 self.to_signed_integer()
2032 }
2033
2034 /// Like [`to_unsigned_integer`](Self::to_unsigned_integer), but may relax IEEE
2035 /// corner cases. Defaults to the exact form.
2036 #[inline(always)]
2037 fn fast_to_unsigned_integer(self) -> Self::Unsigned {
2038 self.to_unsigned_integer()
2039 }
2040
2041 /// For each element in the vector, return a mask indicating whether that element is zero.
2042 fn is_zero(self) -> Self::Mask;
2043
2044 /// Returns `true` if all elements in the vector are zero, `false` otherwise.
2045 ///
2046 /// This can often be more performant than naive comparisons or even `is_zero().all()`
2047 fn is_all_zero(self) -> bool;
2048
2049 /// Return the minimum of two vectors, element-wise.
2050 #[conditional] fn min(self, other: Self) -> Self;
2051
2052 /// Return the maximum of two vectors, element-wise.
2053 #[conditional] fn max(self, other: Self) -> Self;
2054
2055 /// Sort the lanes of this vector in `O` order.
2056 ///
2057 /// Backed by a sorting network where one exists for the lane count, and by
2058 /// a scalar compare-and-swap walk otherwise - see
2059 /// [`NumericRegister::sort_by`](crate::register::NumericRegister::sort_by),
2060 /// which this delegates to so a backend override is picked up here too.
2061 /// The direction is free; see [`crate::sort`].
2062 fn sort_by<O: crate::sort::SortOrder>(self) -> Self;
2063
2064 /// Sort the lanes of a **bitonic** vector in `O` order - one that rises then
2065 /// falls, or a rotation of one.
2066 ///
2067 /// Garbage in, garbage out on non-bitonic input. See
2068 /// [`NumericRegister::bitonic_clean_by`](crate::register::NumericRegister::bitonic_clean_by).
2069 fn bitonic_clean_by<O: crate::sort::SortOrder>(self) -> Self;
2070
2071 /// Sort the lanes ascending. Shorthand for
2072 /// [`sort_by::<Ascending>`](Self::sort_by).
2073 #[inline(always)]
2074 fn sort(self) -> Self {
2075 self.sort_by::<crate::sort::Ascending>()
2076 }
2077
2078 /// Sort the lanes of a **bitonic** vector ascending. Shorthand for
2079 /// [`bitonic_clean_by::<Ascending>`](Self::bitonic_clean_by).
2080 #[inline(always)]
2081 fn bitonic_clean(self) -> Self {
2082 self.bitonic_clean_by::<crate::sort::Ascending>()
2083 }
2084
2085 /// Clamps the elements of the vector between the given minimum and maximum values.
2086 fn clamp(self, min: Self, max: Self) -> Self;
2087
2088 /// Returns the minimum value in the vector.
2089 ///
2090 /// This operation has an `O(log2 n)` complexity to reduce.
2091 fn min_element(self) -> Self::Element;
2092 /// Returns the maximum value in the vector.
2093 ///
2094 /// This operation has an `O(log2 n)` complexity to reduce.
2095 fn max_element(self) -> Self::Element;
2096 /// Returns both the minimum and maximum values in the vector simultaneously.
2097 ///
2098 /// More efficient than calling [`min_element`](NumericVector::min_element) and
2099 /// [`max_element`](NumericVector::max_element) separately when both are needed.
2100 fn min_max_element(self) -> (Self::Element, Self::Element);
2101
2102 /// Returns the indices of the minimum and maximum elements in the vector, respectively.
2103 fn arg_minmax(self) -> (usize, usize);
2104
2105 /// Scales each element in the vector by the given factor.
2106 ///
2107 /// While semantically equivalent to `self * Self::splat(factor)`, this method may be optimized
2108 /// better on certain architectures, such as GPUs.
2109 #[conditional] fn scale(self, factor: Self::Element) -> Self;
2110
2111 /// Sums adjacent lane pairs from `lo` and `hi`, returning a vector of the same width.
2112 ///
2113 /// Output: `[lo[0]+lo[1], lo[2]+lo[3], ..., hi[0]+hi[1], hi[2]+hi[3], ...]`
2114 ///
2115 /// The result is always in strict order: all pair sums from `lo` followed by all pair sums from `hi`.
2116 fn pairwise_sum(lo: Self, hi: Self) -> Self;
2117
2118 /// Like [`pairwise_sum`](NumericVector::pairwise_sum), but may return a relaxed (implementation-defined)
2119 /// lane ordering for performance. Treat this as if randomly shuffling the result of
2120 /// [`pairwise_sum`](NumericVector::pairwise_sum), with better performance than `pairwise_sum`.
2121 ///
2122 /// Prefer this if you are simply summing any adjacent pairs from `lo` and `hi`, and don't
2123 /// care about the exact ordering of the resulting sums.
2124 fn relaxed_pairwise_sum(lo: Self, hi: Self) -> Self;
2125
2126 /// Returns the sum of all elements in the vector.
2127 ///
2128 /// This operation has an `O(log2 n)` complexity to reduce.
2129 fn sum_elements(self) -> Self::Element;
2130
2131 /// Returns the product of all elements in the vector.
2132 ///
2133 /// This operation has an `O(log2 n)` complexity to reduce.
2134 fn prod_elements(self) -> Self::Element;
2135
2136 /// Inclusive forward prefix sum ("running total"): `out[i] = self[0] + .. + self[i]`.
2137 ///
2138 /// Unlike [`sum_elements`](Self::sum_elements), which collapses the register to one
2139 /// scalar, this keeps every partial sum in its own lane - the primitive behind
2140 /// bin offsets and stream-compaction write indices.
2141 ///
2142 /// `O(log2 LANES)` vector ops where the backend has a native cross-register
2143 /// [`align`](GenericVector::align), a sequential lane walk where it does not,
2144 /// chosen at compile time. For a scan over only some lanes, neutralise the rest
2145 /// first: `v.zz(mask).prefix_sum()`.
2146 ///
2147 /// ```
2148 /// use thermite::prelude::*;
2149 /// use thermite::backend::scalar::Scalar;
2150 ///
2151 /// let v = <thermite::simd::i32x4<Scalar>>::new([1, 2, 3, 4]);
2152 /// assert_eq!(v.prefix_sum().into_array(), [1, 3, 6, 10].into());
2153 /// assert_eq!(v.reverse_prefix_sum().into_array(), [10, 9, 7, 4].into());
2154 /// ```
2155 fn prefix_sum(self) -> Self;
2156
2157 /// Inclusive forward prefix minimum: `out[i] = min(self[0], .., self[i])`.
2158 ///
2159 /// See [`prefix_sum`](Self::prefix_sum) for the cost model. With NaN lanes, which
2160 /// operand wins is unspecified (as for [`min`](Self::min) itself); exact and
2161 /// backend-identical otherwise, infinities included.
2162 fn prefix_min(self) -> Self;
2163
2164 /// Inclusive forward prefix maximum: `out[i] = max(self[0], .., self[i])`.
2165 ///
2166 /// See [`prefix_min`](Self::prefix_min) for the NaN caveat.
2167 fn prefix_max(self) -> Self;
2168
2169 /// Inclusive reverse (suffix) sum: `out[i] = self[i] + .. + self[LANES-1]`.
2170 fn reverse_prefix_sum(self) -> Self;
2171
2172 /// Inclusive reverse (suffix) minimum: `out[i] = min(self[i], .., self[LANES-1])`.
2173 ///
2174 /// See [`prefix_min`](Self::prefix_min) for the NaN caveat.
2175 fn reverse_prefix_min(self) -> Self;
2176
2177 /// Inclusive reverse (suffix) maximum: `out[i] = max(self[i], .., self[LANES-1])`.
2178 ///
2179 /// See [`prefix_min`](Self::prefix_min) for the NaN caveat.
2180 fn reverse_prefix_max(self) -> Self;
2181
2182 /// Returns a vector whose every lane equals [`LANES`](GenericVector::LANES),
2183 /// converted into the element type.
2184 ///
2185 /// Equivalent to `Self::splat(Self::LANES as Self::Element)`. Useful for
2186 /// stepping an [`indexed`](Self::indexed) counter forward by one full
2187 /// vector's worth of lanes in tight loops.
2188 fn offset() -> Self;
2189
2190 /// Returns a vector where each lane holds its own index, cast to the
2191 /// element type: `[0, 1, 2, ..., LANES-1]`.
2192 ///
2193 /// This is the typical starting point for index-based vector loops. The
2194 /// counter can be advanced by adding [`offset`](Self::offset).
2195 fn indexed() -> Self;
2196}
2197
2198/// Vectors whose elements can represent negative values.
2199///
2200/// Adds negation, absolute value, sign extraction, and sign-conditional
2201/// selection on top of [`NumericVector`]. Implemented for signed integer and
2202/// floating-point vectors; not for unsigned integer vectors.
2203///
2204/// As with the base [`NumericVector`] operators, unary `-` on a signed integer
2205/// vector is **wrapping**: `-Vector::<i32x4>::splat(i32::MIN)` returns
2206/// `i32::MIN` in every lane rather than panicking.
2207// TODO: Add back in some kind of `Signed` trait requirement for Element?
2208#[rustfmt::skip] #[thermite_macros::vector_trait]
2209#[diagnostic::on_unimplemented(
2210 message = "`{Self}` is not a signed SIMD vector",
2211 label = "no `abs`, `signum`, `copysign`, or unary `-`",
2212 note = "`SignedVector` is implemented by signed integer and floating-point vectors. Unsigned integer vectors (`Vector<u32>`, `u8xN`, ...) are not signed."
2213)]
2214pub trait SignedVector: NumericVector + ops::NegMasked<Self::Mask, Output = Self> {
2215 /// A vector of the value "-1" in the element type.
2216 const NEG_ONE: Self;
2217
2218 /// A vector of the smallest positive (non-zero) value in the element type.
2219 const MIN_POSITIVE: Self;
2220
2221 /// Take the absolute value of the vector, element-wise.
2222 #[conditional] fn abs(self) -> Self;
2223
2224 /// For each element in the vector, return a new vector
2225 /// where each element is either -1 or +1 depending
2226 /// on the sign of the element.
2227 ///
2228 /// For integers, this will also return zero (0) if the
2229 /// element is zero. This matches Rust's behavior for integer
2230 /// `signum`. Floats remain only -1 or +1.
2231 fn signum(self) -> Self;
2232
2233 /// For each element in the vector, set the sign of that
2234 /// element to the sign of the corresponding element in the other vector.
2235 #[conditional] fn copysign(self, sign: Self) -> Self;
2236
2237 /// For each element in the vector, return a mask indicating
2238 /// whether that element is negative.
2239 fn is_positive(self) -> Self::Mask;
2240
2241 /// For each element in the vector, return a mask indicating
2242 /// whether that element is positive.
2243 fn is_negative(self) -> Self::Mask;
2244
2245 /// Based on if self is negative, select between `if_neg` and `if_pos`.
2246 fn select_negative(self, if_neg: Self, if_pos: Self) -> Self;
2247}
2248
2249/// Vectors of integer elements.
2250///
2251/// Adds bitwise shifts (via [`BitshiftVector`]), bit-counting, saturating
2252/// arithmetic, branchfree integer division helpers, and exposes the type of
2253/// the per-divider precomputed structures used for vectorized division.
2254///
2255/// # Wrapping arithmetic
2256///
2257/// The basic operators (`+`, `-`, `*`, their assigning forms, and unary `-`
2258/// for signed integer vectors) **wrap on overflow** on every backend, in both
2259/// debug and release builds. See [`NumericVector`] for the rationale. The
2260/// `num_traits::WrappingAdd` / `WrappingSub` / `WrappingMul` impls are simply
2261/// renames of the operator forms; for explicit saturation use
2262/// [`saturating_add`](Self::saturating_add) /
2263/// [`saturating_sub`](Self::saturating_sub).
2264///
2265/// # Reductions
2266///
2267/// Horizontal reductions ([`sum_elements`](NumericVector::sum_elements),
2268/// [`prod_elements`](NumericVector::prod_elements), [`wrapping_sum`](Self::wrapping_sum),
2269/// [`wrapping_prod`](Self::wrapping_prod)) all wrap on overflow.
2270#[rustfmt::skip] #[thermite_macros::vector_trait]
2271#[diagnostic::on_unimplemented(
2272 message = "`{Self}` is not an integer SIMD vector",
2273 label = "not an integer vector",
2274 note = "`IntegerVector` is implemented by integer vectors (`Vector<i32>`, `Vector<u8>`, `i32xN`, ...). Float vectors implement `FloatVector` instead; convert with `.to_int()` or a cast."
2275)]
2276pub trait IntegerVector:
2277 NumericVector<Element: Denominator>
2278 + BitshiftVector
2279 + ops::DivMasked<Self::Mask, Self::Divider, Output = Self>
2280 + ops::DivMasked<Self::Mask, Self::BranchfreeDivider, Output = Self>
2281 // TODO: Some of these might interfere with the methods of this trait,
2282 // adding ambiguity. See what we can do about that.
2283 + num_traits::Saturating + num_traits::SaturatingAdd
2284 + num_traits::SaturatingSub + num_traits::WrappingMul
2285 + num_traits::WrappingAdd + num_traits::WrappingSub
2286{
2287 /// Precomputed scalar divider used by per-lane division against a
2288 /// runtime-known but loop-invariant divisor. See [`crate::Divider`].
2289 type Divider: Copy;
2290 /// Branchfree variant of [`Divider`](Self::Divider). Slightly slower for
2291 /// some divisors but always emits straight-line code with no conditional
2292 /// branches, which is what you want inside a hot SIMD loop.
2293 type BranchfreeDivider: Copy;
2294 /// Precomputed per-lane divider produced by [`to_divider`](Self::to_divider).
2295 /// Used when each lane needs a different (but loop-invariant) divisor.
2296 type VectorizedDivider: Copy;
2297
2298 /// Multiply two vectors lane-wise and return the *high* half of each
2299 /// double-width product.
2300 ///
2301 /// For signed `i32` lanes the result is `(a as i64 * b as i64) >> 32`;
2302 /// for unsigned `u32` it is the same with `u64`. Together with
2303 /// [`mullo`](Self::mullo) this gives the full double-width product
2304 /// without widening the vector type.
2305 #[conditional] fn mulhi(self, other: Self) -> Self;
2306
2307 /// Multiply two vectors lane-wise and return the *low* half of each
2308 /// product, with wrapping on overflow.
2309 ///
2310 /// This is bit-identical to the `*` operator on integer vectors; the
2311 /// dedicated method exists because some ISAs have specialized
2312 /// low-half-only multiply instructions worth emitting directly.
2313 #[conditional] fn mullo(self, other: Self) -> Self;
2314
2315 // fn wrapping_add(self, other: Self) -> Self;
2316 // fn wrapping_sub(self, other: Self) -> Self;
2317 // fn wrapping_mul(self, other: Self) -> Self;
2318
2319 /// Per-lane saturating addition: instead of wrapping, the result is
2320 /// clamped to the element type's range (`MIN`..=`MAX`) on overflow.
2321 #[conditional] fn saturating_add(self, other: Self) -> Self;
2322
2323 /// Per-lane saturating subtraction: instead of wrapping, the result is
2324 /// clamped to the element type's range (`MIN`..=`MAX`) on overflow.
2325 #[conditional] fn saturating_sub(self, other: Self) -> Self;
2326
2327 /// Horizontal sum of all lanes, wrapping on overflow.
2328 ///
2329 /// Equivalent to [`sum_elements`](NumericVector::sum_elements) on integer
2330 /// vectors; the explicit name documents the wrapping behavior at the
2331 /// callsite.
2332 #[conditional] fn wrapping_sum(self) -> Self::Element;
2333
2334 /// Horizontal product of all lanes, wrapping on overflow.
2335 ///
2336 /// Equivalent to [`prod_elements`](NumericVector::prod_elements); the
2337 /// explicit name documents the wrapping behavior at the callsite.
2338 #[conditional] fn wrapping_prod(self) -> Self::Element;
2339
2340 /// Build a [`Divider`](Self::Divider) for a single scalar divisor `d`,
2341 /// suitable for repeatedly dividing many vectors by the same `d`.
2342 ///
2343 /// Construction is `O(1)` but non-trivial; build once outside the hot
2344 /// loop, then use `vec / divider` inside.
2345 fn create_divider(d: Self::Element) -> Self::Divider;
2346
2347 /// Build a [`BranchfreeDivider`](Self::BranchfreeDivider) for a single
2348 /// scalar divisor `d`. Prefer this over [`create_divider`](Self::create_divider)
2349 /// inside tight SIMD loops where conditional branches would hurt
2350 /// throughput.
2351 fn create_branchfree_divider(d: Self::Element) -> Self::BranchfreeDivider;
2352
2353 /// Use this vector as the denominators for a vectorized division operation.
2354 ///
2355 /// This creates a `VectorDivider` which can then be used to perform
2356 /// vectorized integer division with the `Div` trait. Note that for unsigned
2357 /// integer types, `1` is not a valid denominator and will cause a panic.
2358 ///
2359 /// This operation itself is NOT vectorized and is `O(n)` in the number of lanes.
2360 /// It is designed to be calculated once and then reused for multiple division operations.
2361 ///
2362 /// # Panics
2363 ///
2364 /// If unsigned, integer values of `1` present in the vector
2365 /// denominators will cause a panic.
2366 fn to_divider(self) -> Self::VectorizedDivider;
2367
2368 /// For each element in the vector, count the number of bits that are set to 1.
2369 #[conditional] fn count_ones(self) -> Self;
2370 /// For each element in the vector, count the number of bits that are set to 0.
2371 #[conditional] fn count_zeros(self) -> Self;
2372 /// For each element in the vector, count the number of leading ones.
2373 #[conditional] fn leading_ones(self) -> Self;
2374 /// For each element in the vector, count the number of leading zeros.
2375 #[conditional] fn leading_zeros(self) -> Self;
2376 /// For each element in the vector, count the number of trailing ones.
2377 #[conditional] fn trailing_ones(self) -> Self;
2378 /// For each element in the vector, count the number of trailing zeros.
2379 #[conditional] fn trailing_zeros(self) -> Self;
2380
2381 /// For each lane, how many *earlier* lanes hold the same value:
2382 /// `out[i] == |{ j < i : self[j] == self[i] }|`.
2383 ///
2384 /// Equivalent to AVX-512CD's `conflict(self).count_ones()`. Two things fall
2385 /// out of it:
2386 ///
2387 /// - `count_conflicts().cmp_eq(Self::ZERO)` is the **first-occurrence** mask.
2388 /// - The count is the round number for a conflicting read-modify-write. A
2389 /// lane of rank `r` is safe to process in round `r`, since every earlier
2390 /// duplicate has a strictly smaller rank and goes first. That is what
2391 /// makes a vectorized histogram / SAH-bin increment correct where a plain
2392 /// scatter would silently drop duplicate writes.
2393 ///
2394 /// Backed by [`IntegerRegister::count_conflicts`](crate::register::IntegerRegister::count_conflicts),
2395 /// so a backend with hardware conflict detection overrides it in one place.
2396 fn count_conflicts(self) -> Self;
2397}
2398
2399/// The operations that need both a sign and integer lanes: arithmetic
2400/// (sign-filling) right shifts, the overflow-free averages, and the rounded
2401/// high-half multiply.
2402///
2403/// The meeting point of [`SignedVector`] and [`IntegerVector`], implemented only
2404/// by vectors of signed integer elements (`Vector<i32>`, `i16xN`, ...).
2405#[rustfmt::skip] #[thermite_macros::vector_trait]
2406#[diagnostic::on_unimplemented(
2407 message = "`{Self}` is not a signed integer SIMD vector",
2408 label = "not a signed integer vector",
2409 note = "`SignedIntegerVector` is the meeting point of `SignedVector` and `IntegerVector`: it is implemented only by vectors of signed integer elements (`Vector<i32>`, `i16xN`, ...). Unsigned integer and float vectors do not qualify."
2410)]
2411pub trait SignedIntegerVector: SignedVector + IntegerVector<Element: crate::element::SignedIntegerElement> {
2412 /// For each lane in the vector, right shift in sign bits by the immediate value.
2413 #[conditional] fn srai<const I: i32>(self) -> Self;
2414 /// For each lane in the vector, right shift in sign bits by the given value.
2415 #[conditional] fn sra(self, count: u32) -> Self;
2416 /// For each lane in the vector, right shift in sign bits by the corresponding lane in the shifts vector.
2417 #[conditional] fn srav(self, counts: Self::Unsigned) -> Self;
2418
2419 /// Floor average: `(a + b) >> 1` rounded toward -∞, computed without overflow.
2420 #[conditional] fn avg_floor(self, other: Self) -> Self;
2421 /// Ceiling average: `(a + b + 1) >> 1` rounded toward +∞, computed without overflow.
2422 #[conditional] fn avg_ceil(self, other: Self) -> Self;
2423
2424 /// Rounded high-half signed multiply: the fixed-point `Q(W-1)` product
2425 /// `(self * other + 2^(W-2)) >> (W-1)`, where `W` is the element bit width.
2426 ///
2427 /// For `i16` this is the Q15 rounded multiply (x86 `PMULHRSW`), the
2428 /// fixed-point DSP primitive for gain/volume, fades, and window functions.
2429 /// Unlike [`mulhi`](IntegerVector::mulhi) it rounds to nearest instead of
2430 /// truncating, avoiding a DC bias.
2431 #[conditional] fn mulhrs(self, other: Self) -> Self;
2432}
2433
2434/// The operations that read better on unsigned lanes: the power-of-two and
2435/// inclusive-range predicates, and the unsigned averages.
2436///
2437/// Implemented only by vectors of unsigned integer elements (`Vector<u32>`,
2438/// `u8xN`, ...). Several of these exist here specifically because the unsigned
2439/// form is cheaper: [`in_range`](Self::in_range) is one wrapping subtract and
2440/// one compare, against the two compares an explicit `lo <= x && x <= hi` costs.
2441#[rustfmt::skip] #[thermite_macros::vector_trait]
2442#[diagnostic::on_unimplemented(
2443 message = "`{Self}` is not an unsigned integer SIMD vector",
2444 label = "not an unsigned integer vector",
2445 note = "`UnsignedIntegerVector` is implemented only by vectors of unsigned integer elements (`Vector<u32>`, `u8xN`, ...). Signed integer and float vectors do not qualify."
2446)]
2447pub trait UnsignedIntegerVector: IntegerVector<Element: crate::element::UnsignedIntegerElement> {
2448 /// Determines if each unsigned integer element in the vector is a
2449 /// power of two, returning a mask indicating whether or not it is.
2450 fn is_power_of_two(self) -> Self::Mask;
2451
2452 /// Per-lane inclusive unsigned range test: a mask of `lo <= self <= hi`,
2453 /// assuming `lo <= hi`.
2454 ///
2455 /// Computed branchlessly as `(self - lo) <= (hi - lo)` with wrapping
2456 /// subtraction: a single unsigned compare instead of the two an explicit
2457 /// `self >= lo & self <= hi` would need. The workhorse of byte
2458 /// classification - testing digit/alpha/whitespace ranges.
2459 fn in_range(self, lo: Self, hi: Self) -> Self::Mask;
2460
2461 /// Returns the next power of two minus one for each unsigned integer
2462 /// element in the vector.
2463 #[conditional] fn next_power_of_two_m1(self) -> Self;
2464 /// Computes log2(x) + 1 for each unsigned integer element in the vector.
2465 #[conditional] fn ilog2p1(self) -> Self;
2466
2467 /// Compute the parity of each unsigned integer lane in the vector.
2468 #[conditional] fn parity(self) -> Self;
2469
2470 /// Ceiling average: `(a + b + 1) >> 1`, computed without overflow.
2471 ///
2472 /// Matches x86 `PAVGB`/`PAVGW` and ARM `vrhadd` semantics.
2473 #[conditional] fn avg(self, other: Self) -> Self;
2474
2475 /// Per-lane unsigned absolute difference `|self - other|`, without overflow.
2476 ///
2477 /// Computed branchlessly as `(self -| other) | (other -| self)` with
2478 /// saturating subtraction. The per-lane building block of sum-of-absolute-
2479 /// differences (block matching, motion estimation).
2480 #[conditional] fn abs_diff(self, other: Self) -> Self;
2481
2482 /// Per-lane `N`-dimensional Morton code (Z-order curve index): interleave the
2483 /// low `floor(W / N)` bits of each of the `N` coordinate vectors into one,
2484 /// placing bit `i` of `values[d]` at output position `i * N + d`. `N = 2` is
2485 /// the classic 2D code, `N = 3` the 3D (voxel/octree) code.
2486 ///
2487 /// The workhorse for spatial sorting (BVH/octree builds, grid binning,
2488 /// nearest-neighbour broad-phase): compute a whole vector of codes at once,
2489 /// then sort. [`reverse_morton`](Self::reverse_morton) inverts it.
2490 fn morton<const N: usize>(values: [Self; N]) -> Self;
2491
2492 /// Inverse of [`morton`](Self::morton): de-interleave a Morton code back into
2493 /// its `N` coordinate vectors, where `out[d]` gathers output bits
2494 /// `d, d + N, d + 2N, ...` into the low `floor(W / N)` bits.
2495 fn reverse_morton<const N: usize>(self) -> [Self; N];
2496}
2497
2498/// Escape hatch tying a [`Vector`] to its specific underlying
2499/// [`Register`](crate::register::Register) type.
2500///
2501/// Provides round-trip conversion between the user-facing [`Vector`] and the
2502/// raw register storage. Most generic code should bound on
2503/// [`GenericVector`] (or a more specific vector trait) and never need this;
2504/// it exists so that code which deliberately specializes on a particular
2505/// backend can drop down to the register layer without losing the trait
2506/// hierarchy on the way back up.
2507pub trait VectorWithRegister<R: crate::register::Register>: GenericVector {
2508 /// Consume the vector and yield its raw register storage.
2509 fn into_register(self) -> crate::register::Storage<R>;
2510
2511 /// Wrap a raw register storage value back into a `Vector`.
2512 fn from_register(reg: crate::register::Storage<R>) -> Self;
2513
2514 /// Borrow the vector's elements as a slice.
2515 ///
2516 /// The lane count travels as the slice length (always
2517 /// [`lanes()`](GenericVector::lanes)) rather than in the type, so this is the
2518 /// preferred read accessor over array-typed borrows.
2519 fn as_slice(&self) -> &[Self::Element];
2520
2521 /// Mutably borrow the vector's elements as a slice.
2522 ///
2523 /// See [`as_slice`](Self::as_slice).
2524 fn as_mut_slice(&mut self) -> &mut [Self::Element];
2525}
2526
2527/// Float vector types which have an associated hardware register type.
2528pub trait FloatVectorWithRegister:
2529 FloatVectorWithBits<Mask = crate::Mask<Self::Register>> + VectorWithRegister<Self::Register>
2530{
2531 /// The backing hardware register this vector is a thin wrapper over.
2532 type Register: crate::register::FloatRegister<Element = Self::Element, Lanes = Self::Lanes>;
2533}
2534
2535/// SignedBits integer vector types which have an associated hardware register type.
2536pub trait SignedIntegerVectorWithRegister:
2537 SignedIntegerVector<Mask = crate::Mask<Self::Register>> + VectorWithRegister<Self::Register>
2538{
2539 /// The backing hardware register this vector is a thin wrapper over.
2540 type Register: crate::register::SignedIntegerRegister<Element = Self::Element, Lanes = Self::Lanes>;
2541}
2542
2543/// Unsigned integer vector types which have an associated hardware register type.
2544pub trait UnsignedIntegerVectorWithRegister:
2545 UnsignedIntegerVector<Mask = crate::Mask<Self::Register>> + VectorWithRegister<Self::Register>
2546{
2547 /// The backing hardware register this vector is a thin wrapper over.
2548 type Register: crate::register::UnsignedIntegerRegister<Element = Self::Element, Lanes = Self::Lanes>;
2549}
2550
2551/// Floating-point vectors: the bound most user code should be written against.
2552///
2553/// Carries the float arithmetic, rounding, the FMA family, the predicates
2554/// (`is_finite`, `is_nan`, ...) and the [`FloatConsts`] values, on top of
2555/// everything [`SignedVector`] provides. The policy math library
2556/// ([`CoreMath`](crate::math::CoreMath),
2557/// [`TranscendentalMath`](crate::math::TranscendentalMath) and the rest) attaches
2558/// to this bound, so `V: FloatVector + TranscendentalMath` is the usual signature
2559/// for a numeric kernel.
2560///
2561/// Implemented by the 1-lane `Vector<f32>` / `Vector<f64>`, the native-width
2562/// `f32xN` / `f64xN`, and the composite float types (`Dual`, `Complex`,
2563/// `Compensated`), which is what lets one generic function run as plain SIMD, as
2564/// autodiff, or in double-double precision without being edited.
2565///
2566/// Bare `f32` and `f64` do **not** implement it. Wrap the scalar first with
2567/// `Vector::<f32>::splat(x)`, or use [`ScalarMath`](crate::math::ScalarMath) for
2568/// one-off scalar math.
2569#[rustfmt::skip] #[thermite_macros::vector_trait]
2570#[diagnostic::on_unimplemented(
2571 message = "`{Self}` is not a floating-point SIMD vector",
2572 label = "not a float vector",
2573 note = "`FloatVector` is implemented by float vectors: the 1-lane `Vector<f32>` / `Vector<f64>`, the native-width `f32xN` / `f64xN`, and composite float types (`Dual`, `Complex`, `Compensated`).",
2574 note = "Bare `f32` / `f64` do NOT implement `FloatVector`. Wrap the scalar first - `Vector::<f32>::splat(x)` or `Vector(x)` - or, for one-off scalar math, use `ScalarMath` (`x.scalar_sqrt()`, `x.scalar_exp()`, ...).",
2575 note = "Integer vectors are not float vectors either; convert with `.to_float()` or a cast before calling float operations."
2576)]
2577pub trait FloatVector: SignedVector<Element: FloatElement>
2578 + FloatConsts
2579 + CastVector<Self::ExtendedPrecision>
2580 + ops::MulAddExtMasked<Self::Mask, Self, Self, Output = Self>
2581 + ops::MulAddAssignExtMasked<Self::Mask, Self, Self>
2582 + ops::AddSubExtMasked<Self::Mask, Output = Self>
2583{
2584 /// The value `0.5` represented in this vector type.
2585 const HALF: Self;
2586 /// The value `-0.0` represented in this vector type.
2587 const NEG_ZERO: Self;
2588 /// The value `infinity` represented in this vector type.
2589 const INFINITY: Self;
2590 /// The value `-infinity` represented in this vector type.
2591 const NEG_INFINITY: Self;
2592 /// The value `NaN` represented in this vector type.
2593 const NAN: Self;
2594 /// Hardware epsilon value in this vector type.
2595 const EPSILON: Self;
2596
2597 /// If available, an extended precision floating point vector type
2598 /// corresponding to this vector type. E.g., for `f32` vectors, this
2599 /// would be an `f64` vector type.
2600 ///
2601 /// If no such type exists, this will be the same as `Self`.
2602 type ExtendedPrecision: FloatVector<Lanes = Self::Lanes> + CastVector<Self>;
2603
2604 /// Check if each element in the vector is infinite, returning a mask.
2605 fn is_infinite(self) -> Self::Mask;
2606
2607 /// Check if each element in the vector is finite, returning a mask.
2608 fn is_finite(self) -> Self::Mask;
2609
2610 /// Check if each element in the vector is NaN, returning a mask.
2611 fn is_nan(self) -> Self::Mask;
2612
2613 /// Check if each element in the vector is zero or subnormal, returning a mask.
2614 fn is_zero_or_subnormal(self) -> Self::Mask;
2615
2616 /// Check if each element in the vector is normal, returning a mask.
2617 fn is_normal(self) -> Self::Mask;
2618
2619 /// Check if each element in the vector is subnormal, returning a mask.
2620 fn is_subnormal(self) -> Self::Mask;
2621
2622 /// `true` if the backend has a hardware approximate-reciprocal
2623 /// instruction (e.g. `rcpps` on x86). When `false`, [`rcp`](Self::rcp)
2624 /// falls back to a full IEEE division and provides no speed advantage
2625 /// over `Self::ONE / self`.
2626 const HAS_APPROX_RCP: bool;
2627
2628 /// `true` if the backend has a hardware approximate-reciprocal-square-root
2629 /// instruction (e.g. `rsqrtps` on x86). When `false`, [`rsqrt`](Self::rsqrt)
2630 /// falls back to `Self::ONE / self.sqrt()`.
2631 const HAS_APPROX_RSQRT: bool;
2632
2633 /// Lane-wise IEEE 754 square root.
2634 ///
2635 /// Negative inputs (other than `-0.0`) produce NaN. `sqrt(-0.0)` is `-0.0`.
2636 #[conditional] fn sqrt(self) -> Self;
2637
2638 /// Lane-wise approximate reciprocal square root.
2639 ///
2640 /// Accuracy is hardware-dependent (typically 12 bits on x86 `rsqrtps`,
2641 /// closer to full precision on newer ISAs). For full-precision results
2642 /// or backends without hardware support, see [`HAS_APPROX_RSQRT`](Self::HAS_APPROX_RSQRT).
2643 #[conditional] fn rsqrt(self) -> Self;
2644
2645 /// Lane-wise approximate reciprocal: `1 / self`.
2646 ///
2647 /// Accuracy is hardware-dependent (typically 12 bits on x86 `rcpps`).
2648 /// For full-precision results or backends without hardware support,
2649 /// see [`HAS_APPROX_RCP`](Self::HAS_APPROX_RCP), or use `Self::ONE / self`.
2650 #[conditional] fn rcp(self) -> Self;
2651
2652 /// Lane-wise floor: largest integer less than or equal to each element.
2653 ///
2654 /// Result type stays the same; the value is the integer rounded toward
2655 /// negative infinity, kept in the float representation.
2656 #[conditional] fn floor(self) -> Self;
2657
2658 /// Lane-wise ceiling: smallest integer greater than or equal to each
2659 /// element, kept in the float representation.
2660 #[conditional] fn ceil(self) -> Self;
2661
2662 /// Lane-wise round-to-nearest.
2663 ///
2664 /// Halfway cases follow the current rounding mode of the hardware. On
2665 /// x86 this is round-half-to-even (banker's rounding), which differs
2666 /// from the scalar `f32::round` / `f64::round` half-away-from-zero
2667 /// convention. If you need a specific tie-breaking rule, do it explicitly.
2668 #[conditional] fn round(self) -> Self;
2669
2670 /// Lane-wise truncation toward zero (drops the fractional part), kept
2671 /// in the float representation.
2672 #[conditional] fn trunc(self) -> Self;
2673
2674 /// Lane-wise fractional part: `self - self.trunc()`.
2675 ///
2676 /// Result has the same sign as the input. For very large magnitudes the
2677 /// fractional part is exactly zero because the float has no fractional bits.
2678 #[conditional] fn fract(self) -> Self;
2679
2680 /// Effectively `self * sign.signum()`, multiplying the sign bits.
2681 #[conditional] fn mul_sign(self, sign: Self) -> Self;
2682
2683 /// Returns zero with the sign of `self`, i.e.: only the sign bit is set.
2684 #[conditional] fn signed_zero(self) -> Self;
2685
2686 /// Returns the next representable value greater than the current value, towards positive infinity.
2687 #[conditional] fn next_up(self) -> Self;
2688
2689 /// Returns the next representable value less than the current value, towards negative infinity.
2690 #[conditional] fn next_down(self) -> Self;
2691
2692 /// Linearly interpolates between `a` and `b` by `self`, where `self` is typically in the range `[0, 1]`.
2693 ///
2694 /// Follows the formula: `a * (1 - self) + b * self`, but the underlying implementation
2695 /// may optimize into certain other formulations.
2696 fn mix(self, a: Self, b: Self) -> Self;
2697
2698 /// Computes `$1 - x^2$` accurately, avoiding the cancellation a naive `1 - self * self`
2699 /// suffers as `self` approaches `±1` (where the result is small but `self * self` is near 1).
2700 ///
2701 /// With hardware FMA this is `nmul_add(self, self, 1)`: the exact product `$x^2$` is formed
2702 /// and subtracted from one with a single rounding. Without FMA it falls back to the factored
2703 /// `$(1 - x)(1 + x)$`, also cancellation-free (`1 - self` is exact for `self` near 1 by
2704 /// Sterbenz's lemma). Both keep full relative accuracy in the small result.
2705 #[inline(always)]
2706 fn one_minus_sq(self) -> Self {
2707 if const { Self::HAS_TRUE_FMA } {
2708 // FMA: 1 - self*self formed from the exact product with a single rounding.
2709 self.nmul_add(self, Self::ONE)
2710 } else {
2711 // No FMA: factored difference of squares, cancellation-free near |self| = 1.
2712 (Self::ONE - self) * (Self::ONE + self)
2713 }
2714 }
2715
2716 /// Inhibit further LLVM auto-vectorization of code surrounding this call.
2717 ///
2718 /// LLVM sometimes tries to "vectorize the vectors" -- repacking
2719 /// already-SIMD code into a wider form that ends up slower. Inserting
2720 /// this call inside a hot loop blocks that pass at the use site. The
2721 /// call itself emits no instructions; only the optimizer barrier remains.
2722 ///
2723 /// # Safety
2724 ///
2725 /// Memory-safe to call, but the side effect on code generation is
2726 /// significant. Only reach for this when you have measured a regression
2727 /// caused by over-aggressive auto-vectorization.
2728 unsafe fn block_autovectorization(&mut self);
2729
2730 /// Attempt to upcast this FloatVector to a FloatVectorWithBits,
2731 /// using the provided kernel. If not possible, returns None.
2732 fn with_bits<const N: usize, K: AsFloatVectorWithBitsKernel<Self, N>>(
2733 _values: [Self; N],
2734 _kernel: K,
2735 ) -> Option<<K as AsFloatVectorWithBitsKernel<Self, N>>::Output> {
2736 None // Default implementation returns None
2737 }
2738}
2739
2740/// Inclusive scan ladder at the **vector** layer, for composite vector types.
2741///
2742/// The register-layer ladder in
2743/// [`polyfills::scan`](crate::backend::generic::polyfills::scan) is written in
2744/// `Register` ops and so cannot be reused by `Dual`/`Compensated`/`Complex`, whose
2745/// scans have to run on their own `Self` operations (a dual carries the winning
2746/// lane's derivative; a compensated sum has to renormalise its error term). This is
2747/// the same ladder spelled in [`GenericVector`] methods, exported so those crates
2748/// share one copy.
2749///
2750/// Invoke inside an `impl` block for the composite -- it resolves `Self`:
2751///
2752/// ```ignore
2753/// fn prefix_min(self) -> Self {
2754/// thermite::scan_ladder!(forward, self, self.broadcast::<0>(), Self::min)
2755/// }
2756/// fn reverse_prefix_sum(self) -> Self {
2757/// thermite::scan_ladder!(reverse, self, Self::ZERO, core::ops::Add::add)
2758/// }
2759/// ```
2760///
2761/// `$op` must be associative (a doubling ladder reassociates freely), and `$fill`
2762/// must leave the already-final lanes alone: `ZERO` for a sum, and a broadcast of
2763/// the *edge* lane for `min`/`max` -- lane 0 forward, lane `LANES - 1` reverse.
2764/// `MIN`/`MAX` are finite bounds and would clamp an infinite lane, which is the same
2765/// trap the register ladder documents.
2766///
2767/// `align`'s offset is a const-generic argument and must be a literal. The reverse
2768/// direction is fine (the shift *is* the offset), but the forward direction needs
2769/// `LANES - s`, hence the match on the compile-time lane count with a per-width
2770/// offset list -- exactly one arm survives monomorphization. Widths outside
2771/// power-of-two `<= 64` have no arm and fall back to reversing, running the reverse
2772/// ladder, and reversing back, which needs only literal shifts and is correct at any
2773/// width.
2774///
2775/// Every stage is an `align`, so on a vector whose
2776/// [`HAS_NATIVE_ALIGN`](GenericVector::HAS_NATIVE_ALIGN) is false each one expands to
2777/// a shuffle-and-blend and the ladder gets correspondingly more expensive. It is
2778/// still `ceil(log2(LANES))` stages against a per-lane walk's `LANES` extract/insert
2779/// pairs, which is why this does not switch lowering the way the register-layer
2780/// ladder does -- there the fallback walks a slice in place and is genuinely cheaper.
2781/// Gate on the const at the call site if a specific composite says otherwise.
2782#[rustfmt::skip]
2783#[doc(hidden)]
2784#[macro_export]
2785macro_rules! scan_ladder {
2786 (reverse, $v:expr, $fill:expr, $op:path) => {{
2787 let mut v = $v;
2788 let f = $fill;
2789 let () = {
2790 if const { Self::LANES > 1 } { v = $op(v, v.align::<1>(f)); }
2791 if const { Self::LANES > 2 } { v = $op(v, v.align::<2>(f)); }
2792 if const { Self::LANES > 4 } { v = $op(v, v.align::<4>(f)); }
2793 if const { Self::LANES > 8 } { v = $op(v, v.align::<8>(f)); }
2794 if const { Self::LANES > 16 } { v = $op(v, v.align::<16>(f)); }
2795 if const { Self::LANES > 32 } { v = $op(v, v.align::<32>(f)); }
2796 };
2797 v
2798 }};
2799
2800 (forward, $v:expr, $fill:expr, $op:path) => {{
2801 let mut v = $v;
2802 let f = $fill;
2803
2804 if const { Self::LANES.is_power_of_two() && Self::LANES <= 64 } {
2805 // `a.align::<OFFSET>(b)[i] == concat(a, b)[OFFSET + i]`, so with `a = fill`
2806 // and `b = v` the stage that wants `v[i - s]` is `OFFSET == LANES - s`.
2807 let () = match const { Self::LANES } {
2808 0 | 1 => {}
2809 2 => { v = $op(v, f.align::<1>(v)); }
2810 4 => { v = $op(v, f.align::<3>(v));
2811 v = $op(v, f.align::<2>(v)); }
2812 8 => { v = $op(v, f.align::<7>(v));
2813 v = $op(v, f.align::<6>(v));
2814 v = $op(v, f.align::<4>(v)); }
2815 16 => { v = $op(v, f.align::<15>(v));
2816 v = $op(v, f.align::<14>(v));
2817 v = $op(v, f.align::<12>(v));
2818 v = $op(v, f.align::<8>(v)); }
2819 32 => { v = $op(v, f.align::<31>(v));
2820 v = $op(v, f.align::<30>(v));
2821 v = $op(v, f.align::<28>(v));
2822 v = $op(v, f.align::<24>(v));
2823 v = $op(v, f.align::<16>(v)); }
2824 64 => { v = $op(v, f.align::<63>(v));
2825 v = $op(v, f.align::<62>(v));
2826 v = $op(v, f.align::<60>(v));
2827 v = $op(v, f.align::<56>(v));
2828 v = $op(v, f.align::<48>(v));
2829 v = $op(v, f.align::<32>(v)); }
2830 // unreachable: guarded by the `if const` above. Panicking is the right
2831 // failure mode if a width ever slips past that guard.
2832 _ => unreachable!(),
2833 };
2834 v
2835 } else {
2836 // `fill` is the lane-0 broadcast either way: reversing makes it the last
2837 // lane, which is exactly what the reverse ladder wants.
2838 $crate::scan_ladder!(reverse, v.reverse(), f, $op).reverse()
2839 }
2840 }};
2841}
2842
2843/// Run a closure-like block with a generic [`FloatVector`] temporarily upcast
2844/// to a [`FloatVectorWithBits`], when the backend supports it.
2845///
2846/// Given an array of `FloatVector` values and a body parameterized over a
2847/// `FloatVectorWithBits` type, this expands to a call to
2848/// [`FloatVector::with_bits`] with an anonymous kernel implementing
2849/// [`AsFloatVectorWithBitsKernel`]. The body only runs when bit access is
2850/// available for the concrete backend; otherwise the whole expression evaluates
2851/// to `None` (the return type is therefore `Option<_>`).
2852///
2853/// This is the ergonomic front-end to the [`AsFloatVectorWithBitsKernel`]
2854/// pattern; reach for it inside generic code bounded only on `FloatVector` that
2855/// wants an optional fast path requiring bit-level access.
2856#[macro_export]
2857macro_rules! with_bits {
2858 // (($first_value:expr $(, $value:expr)+): $ty:ty as fn($first_decl:ident: $first_alias:ident $(,$decl:ident: $alias:ident)* ) -> $ret:ty $(where $($c:ty: $constraint:ident),*)? { $($body:tt)* }) => {{
2859 // $crate::with_bits!(($first_value): $ty as fn($first_decl: $first_alias) -> impl $ret $(where $($c: $constraint),*)? {
2860 // $crate::with_bits!(($($value),+): $ty as fn($($decl: $alias),*) -> $ret $(where $($c: $constraint),*)? {
2861 // $($body)*
2862 // })
2863 // })
2864 // }};
2865
2866 ([$($values:expr),+]: [$ty:ty; $len:literal] as fn($decl:ident: [$alias:ident; _]) -> $ret:ty $(where $($c:ty: $constraint:ident),*)? { $($body:tt)* }) => {{
2867 struct AnonymousAsFloatVectorWithBitsKernel<V>(core::marker::PhantomData<V>);
2868
2869 impl<V: FloatVector> $crate::vector::AsFloatVectorWithBitsKernel<V, $len> for AnonymousAsFloatVectorWithBitsKernel<V>
2870 $(where $($c: $constraint),*)?
2871 {
2872 type Output = $ret;
2873
2874 fn with_bits<
2875 $alias: FloatVectorWithBits<
2876 Element = V::Element,
2877 Lanes = V::Lanes,
2878 Mask = V::Mask,
2879 Signed = V::Signed,
2880 Unsigned = V::Unsigned,
2881 ExtendedPrecision = V::ExtendedPrecision,
2882 > + CastVector<V>,
2883 >(
2884 self,
2885 $decl: [$alias; $len],
2886 ) -> Self::Output {
2887 $($body)*
2888 }
2889 }
2890
2891 <V as FloatVector>::with_bits(
2892 [$($values),+],
2893 AnonymousAsFloatVectorWithBitsKernel::<V>(core::marker::PhantomData),
2894 )
2895 }};
2896}
2897
2898/// Some algorithms may benefit from being able to access the bitwise
2899/// representation of floating point vectors. However, not all vectors
2900/// support this functionality, and those that do may be passed as generic
2901/// FloatVector. Therefore, this is a way of upcasting a FloatVector
2902/// to a FloatVectorWithBits, if possible. If not possible, returns None.
2903pub trait AsFloatVectorWithBitsKernel<O: FloatVector, const N: usize> {
2904 /// Whatever the kernel body returns, threaded back out through the upcast.
2905 type Output;
2906
2907 /// Runs the kernel with `v` re-typed as a [`FloatVectorWithBits`].
2908 ///
2909 /// The bound is written on the method rather than the trait so the caller
2910 /// stays generic over plain [`FloatVector`]. The bit-level type only exists
2911 /// inside this call.
2912 fn with_bits<
2913 V: FloatVectorWithBits<
2914 Element = O::Element,
2915 Lanes = O::Lanes,
2916 Mask = O::Mask,
2917 Signed = O::Signed,
2918 Unsigned = O::Unsigned,
2919 ExtendedPrecision = O::ExtendedPrecision,
2920 > + CastVector<O>,
2921 >(
2922 self,
2923 v: [V; N],
2924 ) -> Self::Output;
2925}
2926
2927/// A [`FloatVector`] that additionally exposes its raw bit representation as
2928/// companion integer vectors, enabling bit-level float algorithms.
2929///
2930/// On top of [`FloatVector`] this provides:
2931/// - the [`Bits`](Self::Bits) (unsigned) and [`SignedBits`](Self::SignedBits)
2932/// integer vector types matching this float's bit width and lane count, with
2933/// full [`FullyInteroperable`] cast/bitcast interop between all three views;
2934/// - hardware-accelerated `native_*` transcendentals gated by
2935/// [`NATIVE_CAP`](Self::NATIVE_CAP);
2936/// - bit-level helpers like [`total_order`](Self::total_order) /
2937/// [`linear_order`](Self::linear_order) for sorting and ULP math.
2938///
2939/// Not every float vector implements this (it requires the element to be a
2940/// [`FloatElementWithBits`]); generic code that only sometimes needs bit access
2941/// can attempt to obtain it via [`FloatVector::with_bits`].
2942///
2943/// The methods on this trait do **not** have masked (`_c`/`_m`/`_z`) variants.
2944#[diagnostic::on_unimplemented(
2945 message = "`{Self}` does not expose float bit-manipulation operations",
2946 label = "no `ldexp` / `frexp` or raw bit access",
2947 note = "`FloatVectorWithBits` is implemented by concrete float vectors (`Vector<f32>`, `f32xN`, ...). Composite float types such as `Dual` / `Compensated` may not expose raw bit access, so bound on `FloatVector` instead unless you specifically need bit-level ops."
2948)]
2949pub trait FloatVectorWithBits:
2950 BitwiseVector
2951 + FloatVector<Element: FloatElementWithBits, Signed: CastVector<Self::SignedBits>, Unsigned: CastVector<Self::Bits>>
2952 + GenericVector<
2953 Signed: GenericVector<Mask: CastMask<<Self::SignedBits as GenericVector>::Mask>>,
2954 Unsigned: GenericVector<Mask: CastMask<<Self::Unsigned as GenericVector>::Mask>>,
2955 > + FullyInteroperable<Self::Bits, Self::SignedBits>
2956{
2957 /// This vector's bit pattern viewed as *signed* integer lanes of the same
2958 /// width, for exponent arithmetic and the sign-aware bit tricks.
2959 type SignedBits: SignedIntegerVector<
2960 Mask: CastMask<<Self::Signed as GenericVector>::Mask>,
2961 Lanes = Self::Lanes,
2962 Divider = Divider<<Self::Element as FloatElementWithBits>::SignedBits>,
2963 BranchfreeDivider = BranchfreeDivider<<Self::Element as FloatElementWithBits>::SignedBits>,
2964 Element = <Self::Element as FloatElementWithBits>::SignedBits,
2965 > + FullyInteroperable<Self, Self::Bits>
2966 + CastVector<Self::Signed>;
2967
2968 /// This vector's bit pattern viewed as *unsigned* integer lanes of the same
2969 /// width, which is what masking and shifting the raw bits wants.
2970 type Bits: UnsignedIntegerVector<
2971 Mask: CastMask<<Self::Unsigned as GenericVector>::Mask>,
2972 Lanes = Self::Lanes,
2973 Divider = Divider<<Self::Element as FloatElementWithBits>::Bits>,
2974 BranchfreeDivider = BranchfreeDivider<<Self::Element as FloatElementWithBits>::Bits>,
2975 Element = <Self::Element as FloatElementWithBits>::Bits,
2976 > + FullyInteroperable<Self, Self::SignedBits>
2977 + CastVector<Self::Unsigned>;
2978
2979 /// Bit-flag set describing which `native_*` methods on this trait have a
2980 /// real hardware implementation on the current backend.
2981 ///
2982 /// Test with `NATIVE_CAP.has(NativeCapability::SIN)` etc. before calling
2983 /// the corresponding `native_*` method directly; otherwise the default
2984 /// implementation will panic.
2985 const NATIVE_CAP: NativeCapability;
2986
2987 /// Hardware-accelerated `ldexp`: `self * 2^exp`, lane-wise.
2988 ///
2989 /// # Safety
2990 /// Only callable when [`NATIVE_CAP`](Self::NATIVE_CAP) advertises `LDEXP`.
2991 /// Calling on a backend without hardware support is undefined behavior
2992 /// (the default impl panics via `unreachable!` at the register layer).
2993 unsafe fn native_ldexp(self, exp: Self::SignedBits) -> Self;
2994
2995 /// Hardware-accelerated `frexp`: split each lane into a normalized
2996 /// mantissa in `[0.5, 1.0)` and an integer exponent.
2997 ///
2998 /// # Safety
2999 /// Only callable when [`NATIVE_CAP`](Self::NATIVE_CAP) advertises `FREXP`.
3000 unsafe fn native_frexp(self) -> (Self, Self::SignedBits);
3001
3002 /// Hardware-accelerated combined sine and cosine, lane-wise.
3003 ///
3004 /// # Safety
3005 /// Only callable when [`NATIVE_CAP`](Self::NATIVE_CAP) advertises `SIN_COS`.
3006 unsafe fn native_sin_cos<P: Policy>(self) -> (Self, Self);
3007
3008 /// Hardware-accelerated sine, lane-wise.
3009 ///
3010 /// # Safety
3011 /// Only callable when [`NATIVE_CAP`](Self::NATIVE_CAP) advertises `SIN`.
3012 unsafe fn native_sin<P: Policy>(self) -> Self;
3013
3014 /// Hardware-accelerated cosine, lane-wise.
3015 ///
3016 /// # Safety
3017 /// Only callable when [`NATIVE_CAP`](Self::NATIVE_CAP) advertises `COS`.
3018 unsafe fn native_cos<P: Policy>(self) -> Self;
3019
3020 /// Hardware-accelerated tangent, lane-wise.
3021 ///
3022 /// # Safety
3023 /// Only callable when [`NATIVE_CAP`](Self::NATIVE_CAP) advertises `TAN`.
3024 unsafe fn native_tan<P: Policy>(self) -> Self;
3025
3026 /// Hardware-accelerated `2^self`, lane-wise.
3027 ///
3028 /// # Safety
3029 /// Only callable when [`NATIVE_CAP`](Self::NATIVE_CAP) advertises `EXP2`.
3030 unsafe fn native_exp2<P: Policy>(self) -> Self;
3031
3032 /// Hardware-accelerated `log2(self)`, lane-wise.
3033 ///
3034 /// # Safety
3035 /// Only callable when [`NATIVE_CAP`](Self::NATIVE_CAP) advertises `LOG2`.
3036 unsafe fn native_log2<P: Policy>(self) -> Self;
3037
3038 /// Hardware-accelerated `e^self`, lane-wise.
3039 ///
3040 /// # Safety
3041 /// Only callable when [`NATIVE_CAP`](Self::NATIVE_CAP) advertises `EXP`.
3042 unsafe fn native_exp<P: Policy>(self) -> Self;
3043
3044 /// Hardware-accelerated natural logarithm, lane-wise.
3045 ///
3046 /// # Safety
3047 /// Only callable when [`NATIVE_CAP`](Self::NATIVE_CAP) advertises `LN`.
3048 unsafe fn native_ln<P: Policy>(self) -> Self;
3049
3050 /// Hardware-accelerated `self^exp`, lane-wise.
3051 ///
3052 /// # Safety
3053 /// Only callable when [`NATIVE_CAP`](Self::NATIVE_CAP) advertises `POWF`.
3054 unsafe fn native_powf<P: Policy>(self, exp: Self) -> Self;
3055
3056 /// Return a signed integer vector that is capable of encapsulating
3057 /// the "total order" of the floating point values in this vector, /// such that when compared as integers, the ordering is the same
3058 /// as the floating point ordering, including NaNs, in the following order:
3059 ///
3060 /// - negative quiet NaN
3061 /// - negative signaling NaN
3062 /// - negative infinity
3063 /// - negative numbers
3064 /// - negative subnormal numbers
3065 /// - negative zero
3066 /// - positive zero
3067 /// - positive subnormal numbers
3068 /// - positive numbers
3069 /// - positive infinity
3070 /// - positive signaling NaN
3071 /// - positive quiet NaN.
3072 ///
3073 /// This is useful for sorting floating point numbers in a way that
3074 /// is consistent and well-defined. However, it may differ from
3075 /// the default floating point comparison behavior of the platform.
3076 ///
3077 /// # Example
3078 /// ```rust
3079 /// # use thermite::backend::scalar::prelude::*;
3080 /// let x = f32x4::NAN;
3081 /// let y = f32x4::ONE;
3082 /// let total_lt = x.total_order().cmp_lt(y.total_order());
3083 /// assert!(total_lt.none()); // NaN is not less than 1.0 in total order
3084 /// ```
3085 fn total_order(self) -> Self::SignedBits;
3086
3087 /// Similar to [`total_order`](FloatVectorWithBits::total_order), but positive zero and negative zero are
3088 /// the same value. This can be used for calculating ULP differences by simply subtracting one from another.
3089 fn linear_order(self) -> Self::SignedBits;
3090}
3091
3092/// Convenience accessors `x()` / `y()` automatically available on any
3093/// 2-lane [`GenericVector`].
3094///
3095/// Each method is just shorthand for [`extract`](GenericVector::extract) at
3096/// the corresponding compile-time index.
3097#[rustfmt::skip]
3098pub trait GenericVector2: GenericVector {
3099 /// Returns the value of lane 0.
3100 #[inline(always)] fn x(&self) -> Self::Element { self.extract::<0>() }
3101 /// Returns the value of lane 1.
3102 #[inline(always)] fn y(&self) -> Self::Element { self.extract::<1>() }
3103}
3104
3105/// Convenience accessors `x()` / `y()` / `z()` automatically available on any
3106/// 3-lane [`GenericVector`].
3107///
3108/// Each method is just shorthand for [`extract`](GenericVector::extract) at
3109/// the corresponding compile-time index.
3110#[rustfmt::skip]
3111pub trait GenericVector3: GenericVector {
3112 /// Returns the value of lane 0.
3113 #[inline(always)] fn x(&self) -> Self::Element { self.extract::<0>() }
3114 /// Returns the value of lane 1.
3115 #[inline(always)] fn y(&self) -> Self::Element { self.extract::<1>() }
3116 /// Returns the value of lane 2.
3117 #[inline(always)] fn z(&self) -> Self::Element { self.extract::<2>() }
3118}
3119
3120/// Convenience accessors `x()` / `y()` / `z()` / `w()` automatically available
3121/// on any 4-lane [`GenericVector`].
3122///
3123/// Each method is just shorthand for [`extract`](GenericVector::extract) at
3124/// the corresponding compile-time index.
3125#[rustfmt::skip]
3126pub trait GenericVector4: GenericVector {
3127 /// Returns the value of lane 0.
3128 #[inline(always)] fn x(&self) -> Self::Element { self.extract::<0>() }
3129 /// Returns the value of lane 1.
3130 #[inline(always)] fn y(&self) -> Self::Element { self.extract::<1>() }
3131 /// Returns the value of lane 2.
3132 #[inline(always)] fn z(&self) -> Self::Element { self.extract::<2>() }
3133 /// Returns the value of lane 3.
3134 #[inline(always)] fn w(&self) -> Self::Element { self.extract::<3>() }
3135}
3136
3137impl<V: GenericVector<Lanes = typenum::U2>> GenericVector2 for V {}
3138impl<V: GenericVector<Lanes = typenum::U3>> GenericVector3 for V {}
3139impl<V: GenericVector<Lanes = typenum::U4>> GenericVector4 for V {}
3140
3141#[rustfmt::skip]
3142macro_rules! impl_swizzle4 {
3143 (@ x) => { 0 };
3144 (@ y) => { 1 };
3145 (@ z) => { 2 };
3146 (@ w) => { 3 };
3147
3148 (IMPL x x x x) => { #[inline(always)] fn xxxx(self) -> Self { self.broadcast::<0>() } };
3149 (IMPL y y y y) => { #[inline(always)] fn yyyy(self) -> Self { self.broadcast::<1>() } };
3150 (IMPL z z z z) => { #[inline(always)] fn zzzz(self) -> Self { self.broadcast::<2>() } };
3151 (IMPL w w w w) => { #[inline(always)] fn wwww(self) -> Self { self.broadcast::<3>() } };
3152
3153 (IMPL $a:ident $b:ident $c:ident $d:ident) => {paste::paste! {
3154 #[inline(always)]
3155 fn [<$a $b $c $d>](self) -> Self {
3156 struct Indices;
3157
3158 impl crate::swizzle::SwizzleIndices<typenum::U4> for Indices {
3159 const INDICES: GenericArray<u32, typenum::U4> = {
3160 unsafe { $crate::generic_array::const_transmute::<_, GenericArray<u32, typenum::U4>>([
3161 impl_swizzle4!(@ $a),
3162 impl_swizzle4!(@ $b),
3163 impl_swizzle4!(@ $c),
3164 impl_swizzle4!(@ $d)
3165 ]) }
3166 };
3167 }
3168
3169 self.permute_const::<Indices>()
3170 }
3171 }};
3172
3173 (DECL $(#[$meta:meta])* $a:ident $b:ident $c:ident $d:ident) => {paste::paste! {
3174 #[allow(missing_docs)]
3175 $(#[$meta])* fn [<$a $b $c $d>](self) -> Self;
3176 }};
3177
3178 ($( $(#[$meta:meta])* [$a:ident $b:ident $c:ident $d:ident]),*) => {
3179 /// Only available for 4-lane vectors, this allows human-readable swizzle/permutations
3180 /// of the vector.
3181 pub trait Swizzle4: SwizzleVector<Lanes = typenum::U4> { $(impl_swizzle4!(DECL $(#[$meta])* $a $b $c $d);)* }
3182
3183 /// Implements 4-lane swizzling for vectors.
3184 impl<V: SwizzleVector<Lanes = typenum::U4>> Swizzle4 for V {
3185 $(impl_swizzle4!(IMPL $a $b $c $d);)*
3186 }
3187 }
3188}
3189
3190#[rustfmt::skip]
3191macro_rules! impl_swizzle3 {
3192 (IMPL x x x) => { #[inline(always)] fn xxx(self) -> Self { self.broadcast::<0>() } };
3193 (IMPL y y y) => { #[inline(always)] fn yyy(self) -> Self { self.broadcast::<1>() } };
3194 (IMPL z z z) => { #[inline(always)] fn zzz(self) -> Self { self.broadcast::<2>() } };
3195
3196 (IMPL $a:ident $b:ident $c:ident) => {paste::paste! {
3197 #[inline(always)]
3198 fn [<$a $b $c>](self) -> Self {
3199 struct Indices;
3200
3201 impl crate::swizzle::SwizzleIndices<typenum::U3> for Indices {
3202 const INDICES: GenericArray<u32, typenum::U3> = {
3203 unsafe { $crate::generic_array::const_transmute::<_, GenericArray<u32, typenum::U3>>([
3204 impl_swizzle4!(@ $a),
3205 impl_swizzle4!(@ $b),
3206 impl_swizzle4!(@ $c)
3207 ]) }
3208 };
3209 }
3210
3211 self.permute_const::<Indices>()
3212 }
3213 }};
3214
3215 (DECL $(#[$meta:meta])* $a:ident $b:ident $c:ident) => {paste::paste! {
3216 #[allow(missing_docs)]
3217 $(#[$meta])* fn [<$a $b $c>](self) -> Self;
3218 }};
3219
3220 ($( $(#[$meta:meta])* [$a:ident $b:ident $c:ident]),*) => {
3221 /// Only available for "3-lane" (ignoring 4th lane) [`LinAlg3Register`](crate::register::LinAlg3Register) vectors,
3222 /// this allows human-readable swizzle/permutations of the vector. Permutations
3223 /// will ignore the 4th lane of the register, leaving it unchanged.
3224 pub trait Swizzle3: SwizzleVector<Lanes = typenum::U3> { $(impl_swizzle3!(DECL $(#[$meta])* $a $b $c);)* }
3225
3226 /// Implements 3-lane swizzling for vectors support 3-lane linear algebra operations.
3227 impl<V: SwizzleVector<Lanes = typenum::U3>> Swizzle3 for V {
3228 $(impl_swizzle3!(IMPL $a $b $c);)*
3229 }
3230 }
3231}
3232
3233impl_swizzle3! {
3234 [x y z], [x x x], [x x y], [x x z], [x y x], [x y y], [x z x], [x z y], [x z z],
3235 [y x x], [y x y], [y x z], [y y x], [y y y], [y y z], [y z x], [y z y], [y z z],
3236 [z x x], [z x y], [z x z], [z y x], [z y y], [z y z], [z z x], [z z y], [z z z]
3237}
3238
3239impl_swizzle4! {
3240 [x y z w], [x x x x], [x x x y], [x x x z], [x x x w], [x x y x], [x x y y], [x x y z],
3241 [x x y w], [x x z x], [x x z y], [x x z z], [x x z w], [x x w x], [x x w y], [x x w z],
3242 [x x w w], [x y x x], [x y x y], [x y x z], [x y x w], [x y y x], [x y y y], [x y y z],
3243 [x y y w], [x y z x], [x y z y], [x y z z], [x y w x], [x y w y], [x y w z], [x y w w],
3244 [x z x x], [x z x y], [x z x z], [x z x w], [x z y x], [x z y y], [x z y z], [x z y w],
3245 [x z z x], [x z z y], [x z z z], [x z z w], [x z w x], [x z w y], [x z w z], [x z w w],
3246 [x w x x], [x w x y], [x w x z], [x w x w], [x w y x], [x w y y], [x w y z], [x w y w],
3247 [x w z x], [x w z y], [x w z z], [x w z w], [x w w x], [x w w y], [x w w z], [x w w w],
3248 [y x x x], [y x x y], [y x x z], [y x x w], [y x y x], [y x y y], [y x y z], [y x y w],
3249 [y x z x], [y x z y], [y x z z], [y x z w], [y x w x], [y x w y], [y x w z], [y x w w],
3250 [y y x x], [y y x y], [y y x z], [y y x w], [y y y x], [y y y y], [y y y z], [y y y w],
3251 [y y z x], [y y z y], [y y z z], [y y z w], [y y w x], [y y w y], [y y w z], [y y w w],
3252 [y z x x], [y z x y], [y z x z], [y z x w], [y z y x], [y z y y], [y z y z], [y z y w],
3253 [y z z x], [y z z y], [y z z z], [y z z w], [y z w x], [y z w y], [y z w z], [y z w w],
3254 [y w x x], [y w x y], [y w x z], [y w x w], [y w y x], [y w y y], [y w y z], [y w y w],
3255 [y w z x], [y w z y], [y w z z], [y w z w], [y w w x], [y w w y], [y w w z], [y w w w],
3256 [z x x x], [z x x y], [z x x z], [z x x w], [z x y x], [z x y y], [z x y z], [z x y w],
3257 [z x z x], [z x z y], [z x z z], [z x z w], [z x w x], [z x w y], [z x w z], [z x w w],
3258 [z y x x], [z y x y], [z y x z], [z y x w], [z y y x], [z y y y], [z y y z], [z y y w],
3259 [z y z x], [z y z y], [z y z z], [z y z w], [z y w x], [z y w y], [z y w z], [z y w w],
3260 [z z x x], [z z x y], [z z x z], [z z x w], [z z y x], [z z y y], [z z y z], [z z y w],
3261 [z z z x], [z z z y], [z z z z], [z z z w], [z z w x], [z z w y], [z z w z], [z z w w],
3262 [z w x x], [z w x y], [z w x z], [z w x w], [z w y x], [z w y y], [z w y z], [z w y w],
3263 [z w z x], [z w z y], [z w z z], [z w z w], [z w w x], [z w w y], [z w w z], [z w w w],
3264 [w x x x], [w x x y], [w x x z], [w x x w], [w x y x], [w x y y], [w x y z], [w x y w],
3265 [w x z x], [w x z y], [w x z z], [w x z w], [w x w x], [w x w y], [w x w z], [w x w w],
3266 [w y x x], [w y x y], [w y x z], [w y x w], [w y y x], [w y y y], [w y y z], [w y y w],
3267 [w y z x], [w y z y], [w y z z], [w y z w], [w y w x], [w y w y], [w y w z], [w y w w],
3268 [w z x x], [w z x y], [w z x z], [w z x w], [w z y x], [w z y y], [w z y z], [w z y w],
3269 [w z z x], [w z z y], [w z z z], [w z z w], [w z w x], [w z w y], [w z w z], [w z w w],
3270 [w w x x], [w w x y], [w w x z], [w w x w], [w w y x], [w w y y], [w w y z], [w w y w],
3271 [w w z x], [w w z y], [w w z z], [w w z w], [w w w x], [w w w y], [w w w z], [w w w w]
3272}
3273
3274/// Vector suitable for 3D linear algebra operations.
3275///
3276/// The length of this vector must be either 3 or 4 lanes.
3277///
3278/// Methods in this are specifically optimized to either ignore the fourth lane (if it exists),
3279/// or to use algorithms that map especially well when there are truly only three "lanes",
3280/// such as on GPUs.
3281pub trait LinAlg3Vector: FloatVector {
3282 /// Scalar Product using only the first three lanes of the register as a 3D vector.
3283 ///
3284 /// This is more efficient than a raw scalar product, as there is no need to
3285 /// zero out the last lane of the register.
3286 fn dot3(self, other: Self) -> Self::Element;
3287
3288 /// Cross Product using only the first three lanes of the register as a 3D vector.
3289 ///
3290 /// This is more efficient than a raw cross product, as there is no need to
3291 /// zero out the last lane of the register.
3292 ///
3293 /// The `DOP` generic parameter indicates whether to use the
3294 /// "Difference of Products" method for computing the cross product,
3295 /// which can be more accurate in some cases, but _requires_
3296 /// hardware fused multiply-add instructions to be efficient.
3297 ///
3298 /// If you want the best performance, set `DOP` to `false`.\
3299 /// If you want the best accuracy or have FMA support, set `DOP` to `true`.
3300 fn cross3<const DOP: bool>(self, other: Self) -> Self;
3301
3302 /// Refraction of incident vector `self` through a surface with normal `n`
3303 /// and relative index of refraction `eta` (`$\eta = \eta_i/\eta_t$`). `self` and `n` are
3304 /// assumed unit length.
3305 ///
3306 /// Total internal reflection (`1 - eta^2*(1 - dot(n,self)^2) < 0`) returns the
3307 /// zero vector; otherwise `eta*self - (eta*dot(n,self) + sqrt(k))*n`
3308 fn refract(self, n: Self, eta: Self::Element) -> Self;
3309
3310 /// Efficiently set the 4th (last) lane of the register to 0.0.
3311 ///
3312 /// Useful for sanitizing 3D Homogeneous vectors.
3313 ///
3314 /// See [`LinAlg3Vector::one4`] for similar functionality for 3D points.
3315 fn zero4(self) -> Self;
3316
3317 /// Efficiently set the 4th (last) lane of the register to 1.0.
3318 ///
3319 /// Useful for sanitizing 3D Homogeneous points.
3320 ///
3321 /// See [`LinAlg3Vector::zero4`] for similar functionality for 3D vectors.
3322 fn one4(self) -> Self;
3323
3324 /// Returns the minimum value in the first three lanes of the register.
3325 fn min_element3(self) -> Self::Element;
3326
3327 /// Returns the maximum value in the first three lanes of the register.
3328 fn max_element3(self) -> Self::Element;
3329
3330 /// Returns the sum of the first three elements of the register.
3331 fn sum_elements3(self) -> Self::Element;
3332
3333 /// Returns the product of the first three elements of the register.
3334 fn prod_elements3(self) -> Self::Element;
3335
3336 /// 3x3 Matrix Transpose.
3337 ///
3338 /// Only the first three lanes of each output column are meaningful; the 4th
3339 /// lane (on 4-lane vectors) is unspecified.
3340 fn mat3_transpose(m: &[Self; 3]) -> [Self; 3];
3341
3342 /// 3x3 Matrix-Vector multiplication, assuming `self` as the vector.
3343 fn mat3_vec3_product<const COLUMN_MAJOR: bool>(self, m: &[Self; 3]) -> Self;
3344
3345 /// 3x3 matrix times `N` 3D vectors (small-`N` batch; see
3346 /// [`mat4_vec4_product_array`](LinAlg4Vector::mat4_vec4_product_array)).
3347 fn mat3_vec3_product_array<const COLUMN_MAJOR: bool, const N: usize>(
3348 m: &[Self; 3],
3349 vectors: &[Self; N],
3350 ) -> [Self; N];
3351
3352 /// 3x3 Matrix-Matrix multiplication.
3353 ///
3354 /// If `COLUMN_MAJOR` is `false`, the matrices are treated as row-major and
3355 /// the multiplication order becomes `rhs * lhs`, mirroring
3356 /// [`mat4_product`](LinAlg4Vector::mat4_product).
3357 fn mat3_product<const COLUMN_MAJOR: bool>(lhs: &[Self; 3], rhs: &[Self; 3]) -> [Self; 3];
3358
3359 /// Determinant of a column-major 3x3 matrix.
3360 fn mat3_det(m: &[Self; 3]) -> Self::Element;
3361
3362 /// In-place 3x3 Matrix inversion; **returns the determinant**.
3363 ///
3364 /// An exactly-zero determinant leaves the matrix untouched; a near-zero
3365 /// (ill-conditioned) determinant gives a finite but unreliable result, so
3366 /// inspect the returned determinant before trusting the matrix.
3367 fn mat3_inverse_inplace(m: &mut [Self; 3]) -> Self::Element;
3368
3369 /// 3x3 Matrix inversion.
3370 ///
3371 /// Returns `Some(inverse)`, or `None` if the matrix is exactly singular.
3372 /// Consider [`mat3_inverse_inplace`](Self::mat3_inverse_inplace) (which hands
3373 /// back the determinant) to avoid the copy and to use a custom tolerance.
3374 #[inline(always)]
3375 fn mat3_inverse(m: &[Self; 3]) -> Option<[Self; 3]> {
3376 let mut mat = *m;
3377 if Self::mat3_inverse_inplace(&mut mat) == Self::Element::ZERO {
3378 None
3379 } else {
3380 Some(mat)
3381 }
3382 }
3383
3384 /// "Normal matrix" for transforming normals under non-uniform scale, from
3385 /// the cofactor cross-products of a column-major 3x3.
3386 ///
3387 /// `DIVIDE = true` gives the true inverse-transpose `$(M^{-1})^{T}$` (non-finite if
3388 /// singular); `DIVIDE = false` gives the un-divided cofactor matrix, which is
3389 /// cheaper, never singular, and points normals the same direction (use it
3390 /// when you re-normalize the result). Cheaper than a full inverse either way.
3391 fn mat3_normal<const DIVIDE: bool>(m: &[Self; 3]) -> [Self; 3];
3392}
3393
3394/// Vector suitable for 4D linear algebra operations.
3395///
3396/// Must have exactly 4 lanes.
3397pub trait LinAlg4Vector: LinAlg3Vector {
3398 /// Scalar Product using all four lanes of the register as a 4D vector.
3399 fn dot4(self, other: Self) -> Self::Element;
3400
3401 /// Quaternion multiplication.
3402 ///
3403 /// Method:
3404 /// ```text
3405 /// T1 = (lhs.w * rhs)
3406 /// T2 = (lhs.x * rhs.wzyx) * {+,-,+,-}
3407 /// T3 = (lhs.y * rhs.zwxy) * {+,+,-,-}
3408 /// T4 = (lhs.z * rhs.yxwz) * {-,+,+,-}
3409 /// T1 + T2 + T3 + T4
3410 /// ```
3411 fn quat4_product(self, other: Self) -> Self;
3412
3413 /// Quaternion-vector multiplication.
3414 ///
3415 /// This is optimized to work best on various SIMD architectures. On
3416 /// architectures with permute/shuffle instructions, it uses the
3417 /// Double-Cross (Giesen) method. On architectures without such instructions,
3418 /// it falls back to the standard method of two dot products
3419 /// and a single cross product. This is because cross products require
3420 /// several shuffles/permutations to compute efficiently with SIMD.
3421 ///
3422 /// The `DOP` generic parameter indicates whether to use the
3423 /// "Difference of Products" method for computing the cross product(s),
3424 /// which can be more accurate in some cases, but _requires_
3425 /// hardware fused multiply-add instructions to be efficient.
3426 ///
3427 /// If you want the best performance, set `DOP` to `false`.\
3428 /// If you want the best accuracy or have FMA support, set `DOP` to `true`.
3429 fn quat4_vec3_product<const DOP: bool>(self, vec: Self) -> Self;
3430
3431 /// Rotation matrix of a **unit** quaternion as 3 registers; the 4th lane of
3432 /// each is unspecified.
3433 ///
3434 /// `COLUMN_MAJOR` picks the storage: the rotation's columns when `true`, its
3435 /// rows when `false` (i.e. the transpose). The choice is free - only the
3436 /// compile-time sign masks differ.
3437 ///
3438 /// Trig-free - the entries are pairwise products of `{x, y, z, w}`, no
3439 /// `sin`/`cos`/`sqrt`. The quaternion is assumed normalized.
3440 ///
3441 /// To rotate many vectors by one quaternion, convert once here and batch via
3442 /// [`mat3_vec3_product`](LinAlg3Vector::mat3_vec3_product) with the matching
3443 /// `COLUMN_MAJOR` - cheaper than a per-vector
3444 /// [`quat4_vec3_product`](Self::quat4_vec3_product) for large `N`.
3445 fn quat_to_mat3<const COLUMN_MAJOR: bool>(self) -> [Self; 3];
3446
3447 /// Homogeneous 4x4 rotation matrix of a **unit** quaternion: the
3448 /// [`quat_to_mat3`](Self::quat_to_mat3) rotation with each rotation
3449 /// register's 4th lane zeroed and a `[0, 0, 0, 1]` 4th register.
3450 /// `COLUMN_MAJOR` is forwarded to `quat_to_mat3`.
3451 fn quat_to_mat4<const COLUMN_MAJOR: bool>(self) -> [Self; 4];
3452
3453 /// 4x4 Matrix Transpose.
3454 fn mat4_transpose(m: &[Self; 4]) -> [Self; 4];
3455
3456 /// 4x4 Matrix-Vector multiplication, assuming `self` as the vector.
3457 ///
3458 /// The `COLUMN_MAJOR` generic parameter indicates whether the matrix
3459 /// is stored in column-major order (`true`) or row-major order (`false`).
3460 ///
3461 /// If the matrix is **NOT** in column-major order, it will need to be
3462 /// transposed before the actual multiplication, which will incur a performance penalty.
3463 ///
3464 /// NOTE: If you want to multiply 4 vectors by the same **column-major** matrix, consider using
3465 /// [`LinAlg4Vector::mat4_product`] instead. It is conceptually the same as multiplying each
3466 /// vector individually, but can take advantage of SIMD optimizations better.
3467 fn mat4_vec4_product<const COLUMN_MAJOR: bool>(self, m: &[Self; 4]) -> Self;
3468
3469 /// 4x4 Matrix-Vector3 multiplication, optimized for the case where the vector is a 3D coordinate
3470 /// (i.e., the 4th lane is ignored).
3471 ///
3472 /// The `COLUMN_MAJOR` generic parameter indicates whether the matrix
3473 /// is stored in column-major order (`true`) or row-major order (`false`).
3474 ///
3475 /// If the matrix is **NOT** in column-major order, it will need to be
3476 /// transposed before the actual multiplication, which will incur a performance penalty.
3477 fn mat4_vec3_product<const COLUMN_MAJOR: bool>(self, m: &[Self; 4]) -> Self;
3478
3479 /// 4x4 matrix multiplied with `N` 3D vectors (small-`N` batch; see
3480 /// [`mat4_vec4_product_array`](Self::mat4_vec4_product_array)).
3481 fn mat4_vec3_product_array<const COLUMN_MAJOR: bool, const N: usize>(
3482 m: &[Self; 4],
3483 vectors: &[Self; N],
3484 ) -> [Self; N];
3485
3486 /// 4x4 Matrix-Point3 multiplication, optimized for the case where the input 3D value is a
3487 /// point of homogenous coordinates (i.e. 4th lane is 1.0).
3488 ///
3489 /// The `COLUMN_MAJOR` generic parameter indicates whether the matrix
3490 /// is stored in column-major order (`true`) or row-major order (`false`).
3491 ///
3492 /// If the matrix is **NOT** in column-major order, it will need to be
3493 /// transposed before the actual multiplication, which will incur a performance penalty.
3494 fn mat4_point3_product<const COLUMN_MAJOR: bool>(self, m: &[Self; 4]) -> Self;
3495
3496 /// 4x4 Matrix multiplied with `N` 3D points (small-`N` batch).
3497 fn mat4_point3_product_array<const COLUMN_MAJOR: bool, const N: usize>(
3498 m: &[Self; 4],
3499 points: &[Self; N],
3500 ) -> [Self; N];
3501
3502 /// 4x4 Matrix-Matrix multiplication.
3503 ///
3504 /// If `COLUMN_MAJOR` is `false`, the matrices are assumed to be in row-major order,
3505 /// and the order of the multiplication will become `rhs * lhs` to account for that.
3506 /// This is mathematically equivalent to transposing both matrices, performing
3507 /// the multiplication, and then transposing the result, but is obviously more efficient.
3508 ///
3509 /// NOTE: When operating in column-major mode (`COLUMN_MAJOR = true`), the multiplication
3510 /// is effectively:
3511 /// ```text
3512 /// C0 = mat4_vec4_product(lhs, R0)
3513 /// C1 = mat4_vec4_product(lhs, R1)
3514 /// C2 = mat4_vec4_product(lhs, R2)
3515 /// C3 = mat4_vec4_product(lhs, R3)
3516 /// ```
3517 ///
3518 /// and is therefore, in **column-major order**, useful for transforming 4 vectors
3519 /// by the same matrix, but capable of being optimized better than doing
3520 /// 4 individual matrix-vector multiplications.
3521 fn mat4_product<const COLUMN_MAJOR: bool>(lhs: &[Self; 4], rhs: &[Self; 4]) -> [Self; 4];
3522
3523 /// Transform `N` vectors by a single 4x4 matrix, returning the transformed array.
3524 ///
3525 /// Intended for **small** `N` (a handful of points): the array is taken and
3526 /// returned **by value** and the loop fully unrolls, so a large `N` will
3527 /// bloat code size and stack usage. For large or dynamic counts, loop
3528 /// [`mat4_vec4_product`](Self::mat4_vec4_product) over a slice instead.
3529 ///
3530 /// Row-major matrices are transposed once up front (amortized over `N`).
3531 /// Backends with a true double-width register transform two vectors per pass.
3532 fn mat4_vec4_product_array<const COLUMN_MAJOR: bool, const N: usize>(
3533 m: &[Self; 4],
3534 vectors: &[Self; N],
3535 ) -> [Self; N];
3536
3537 /// In-place 4x4 Matrix inversion; **returns the determinant**.
3538 ///
3539 /// An exactly-zero determinant leaves the matrix untouched; a near-zero
3540 /// (ill-conditioned) determinant gives a finite but unreliable result, so
3541 /// inspect the returned determinant before trusting the matrix.
3542 fn mat4_inverse_inplace(m: &mut [Self; 4]) -> Self::Element;
3543
3544 /// Compute the determinant of a 4x4 matrix without inverting it.
3545 fn mat4_det(m: &[Self; 4]) -> Self::Element;
3546
3547 /// 4x4 Matrix inversion.
3548 ///
3549 /// Returns `Some(inverted_matrix)`, or `None` if the matrix is exactly
3550 /// singular. Consider [`Vector::mat4_inverse_inplace`] (which hands back the
3551 /// determinant) to avoid the copy and to use a custom tolerance.
3552 #[inline(always)]
3553 fn mat4_inverse(m: &[Self; 4]) -> Option<[Self; 4]> {
3554 let mut mat = *m;
3555
3556 if crate::likely(Self::mat4_inverse_inplace(&mut mat) != Self::Element::ZERO) {
3557 Some(mat)
3558 } else {
3559 None
3560 }
3561 }
3562}