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.
1371 ///
1372 /// Lane count is preserved; only the element type changes. The cast may
1373 /// be widening, narrowing, signed/unsigned, or float/int. Out-of-range
1374 /// float-to-int conversions follow the same saturating behavior as
1375 /// scalar `as` on the host backend.
1376 #[inline(always)] fn cast<INTO>(self) -> INTO
1377 where
1378 INTO: CastVector<Self>,
1379 {
1380 INTO::cast_from(self)
1381 }
1382
1383 /// Fast numeric cast to another vector type.
1384 ///
1385 /// Equivalent to [`cast`](Self::cast) when the backend has no faster path,
1386 /// but may relax IEEE corner cases (NaN propagation, out-of-range
1387 /// float-to-int handling) in exchange for fewer instructions.
1388 ///
1389 /// Use [`cast`](Self::cast) when you need the documented `as` semantics
1390 /// exactly; use this when you have already ruled out problematic inputs.
1391 #[inline(always)] fn fast_cast<INTO>(self) -> INTO
1392 where
1393 INTO: CastVector<Self>,
1394 {
1395 INTO::fast_cast_from(self)
1396 }
1397
1398 /// Reinterpret the bits of this vector as another vector type of the same
1399 /// size and lane count.
1400 ///
1401 /// This is a zero-cost transmute; no conversion is performed. Typical use
1402 /// is moving between a float vector and its integer "bits" vector for
1403 /// bit-level manipulation.
1404 #[inline(always)] fn into_bits<INTO>(self) -> INTO
1405 where
1406 INTO: BitCastVector<Self>,
1407 {
1408 INTO::from_bits(self)
1409 }
1410
1411 /// Narrowing cast that saturates (clamps) out-of-range values to the
1412 /// destination element range, rather than wrapping like [`cast`](Self::cast).
1413 ///
1414 /// Only resolves for narrowing, same-signedness integer conversions
1415 /// (`i64 -> ... -> i8`, `u64 -> ... -> u8`); widening or sign-changing
1416 /// casts have no `SaturatingCastVector` impl and must use [`cast`](Self::cast).
1417 /// See [`SaturatingCastVector`].
1418 #[inline(always)] fn saturating_cast<INTO>(self) -> INTO
1419 where
1420 INTO: SaturatingCastVector<Self>,
1421 {
1422 INTO::saturating_cast_from(self)
1423 }
1424}
1425
1426/// Bitwise operations over the lanes of a vector: `&`, `|`, `^`, `!`,
1427/// `bitandnot`, and the arbitrary three-input [`ternlog`](Self::ternlog).
1428///
1429/// Implemented by integer and mask vectors. Float vectors have no direct bitwise
1430/// ops, so reach their bits through [`FloatVectorWithBits`] first.
1431///
1432/// Note that `a.bitandnot(b)` is `a & !b` at this layer.
1433#[rustfmt::skip] #[thermite_macros::vector_trait]
1434#[diagnostic::on_unimplemented(
1435 message = "`{Self}` does not support bitwise vector operations",
1436 label = "no `&`, `|`, `^`, `!`, andnot, or ternlog",
1437 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."
1438)]
1439pub trait BitwiseVector:
1440 GenericVector
1441 + ops::BitAndMasked<Self::Mask, Self, Output = Self>
1442 + ops::BitAndAssignMasked<Self::Mask, Self>
1443 + ops::BitAndNotMasked<Self::Mask, Self, Output = Self>
1444 + ops::BitAndNotAssignMasked<Self::Mask, Self>
1445 + ops::BitOrMasked<Self::Mask, Self, Output = Self>
1446 + ops::BitOrAssignMasked<Self::Mask, Self>
1447 + ops::BitXorMasked<Self::Mask, Self, Output = Self>
1448 + ops::BitXorAssignMasked<Self::Mask, Self>
1449 + ops::NotMasked<Self::Mask, Output = Self>
1450{
1451 /// Computes an arbitrary bitwise boolean function of three inputs (`a`, `b`, `c`)
1452 /// based on the truth table specified by `IMM`.
1453 ///
1454 /// This function is a "programmable logic gate". It applies the logic defined in `IMM`
1455 /// to every bit of the inputs in parallel.
1456 ///
1457 /// # How to Calculate `IMM`
1458 /// The easiest way to find the correct `IMM` value is to perform your desired boolean
1459 /// logic on these three specific "Magic Constants":
1460 ///
1461 /// * **A** = `0xF0` (Binary `11110000`)
1462 /// * **B** = `0xCC` (Binary `11001100`)
1463 /// * **C** = `0xAA` (Binary `10101010`)
1464 ///
1465 /// ## Example: `(A OR B) XOR C`
1466 /// 1. `A | B` = `0xF0 | 0xCC` = `0xFC`
1467 /// 2. `Result ^ C` = `0xFC ^ 0xAA` = `0x56`
1468 /// 3. Therefore, `IMM = 0x56`.
1469 ///
1470 /// You can also use the [`ternlog_imm!`](crate::ternlog_imm) macro to compute
1471 /// this at compile time.
1472 ///
1473 /// # Visualization using Disjunction Normal Form (DNF)
1474 /// The constants `0xF0`, `0xCC`, and `0xAA` simply form a parallel truth table
1475 /// for all 8 possible combinations of 3 bits:
1476 ///
1477 /// | A | B | C | Bit Index | Term Logic (Minterm) |
1478 /// |:---:|:---:|:---:|:-----------:|:---------------------:|
1479 /// | 0 | 0 | 0 | 0 | ~A & ~B & ~C |
1480 /// | 0 | 0 | 1 | 1 | ~A & ~B & C |
1481 /// | 0 | 1 | 0 | 2 | ~A & B & ~C |
1482 /// | 0 | 1 | 1 | 3 | ~A & B & C |
1483 /// | 1 | 0 | 0 | 4 | A & ~B & ~C |
1484 /// | 1 | 0 | 1 | 5 | A & ~B & C |
1485 /// | 1 | 1 | 0 | 6 | A & B & ~C |
1486 /// | 1 | 1 | 1 | 7 | A & B & C |
1487 ///
1488 /// If `IMM = 0x88` (Bit 3 and 7 set), the logic is:
1489 /// - Bit 3 (0, 1, 1): `~A & B & C`
1490 /// - Bit 7 (1, 1, 1): `A & B & C`
1491 ///
1492 /// As raw DNF, this becomes: `(~A & B & C) | (A & B & C)`.\
1493 /// `~A` and `A` cancel out, simplifying to `B & C`.
1494 ///
1495 /// For each bit set in IMM, we effectively bitwise-OR each corresponding minterm.
1496 ///
1497 /// # Common Immediate Values
1498 /// | Logic | Immediate | Description |
1499 /// | :--- | :--- | :--- |
1500 /// | `A ^ B ^ C` | `0x96` | **3-Way XOR** (Parity) |
1501 /// | `(A & B) OR (~A & C)` | `0xCA` | **Bitwise Select** (If A=1 use B, else use C) |
1502 /// | `(A & B) OR (A & C) OR (B & C)` | `0xE8` | **Majority** (True if 2+ inputs are 1) |
1503 /// | `A OR B OR C` | `0xFE` | **3-Way OR** |
1504 /// | `A ? B : 0` | `0xA0` | **Mask** (A & B) |
1505 ///
1506 /// # Performance Note
1507 /// Since `IMM` is a compile-time constant, the compiler will optimize this function
1508 /// into the most efficient sequence of native instructions (AND, OR, XOR, NOT)
1509 /// for your specific architecture. If using AVX512, there actually exists a single
1510 /// instruction for this.
1511 #[conditional] fn ternlog<const IMM: i32>(a: Self, b: Self, c: Self) -> Self;
1512
1513 /// Two-input version of [`ternlog`](Self::ternlog).
1514 ///
1515 /// Computes an arbitrary bitwise boolean function of two inputs (`a`, `b`)
1516 /// based on the 4-bit truth table specified by the low nibble of `IMM`.
1517 /// Bit `i` of `IMM` selects the output when `(a, b)` equals the binary
1518 /// representation of `i`. As with `ternlog`, the magic constants are
1519 /// `A = 0xC` (`1100`) and `B = 0xA` (`1010`); evaluate your desired logic
1520 /// against them to obtain `IMM`. For example, `A & B == 0x8`, `A | B == 0xE`,
1521 /// `A ^ B == 0x6`, `!A == 0x3`.
1522 ///
1523 /// Since `IMM` is a compile-time constant, the compiler lowers this to
1524 /// the most efficient native instruction sequence for the target ISA.
1525 #[conditional] fn bilog<const IMM: i32>(a: Self, b: Self) -> Self;
1526}
1527
1528/// Shifts and rotates over the lanes of an integer vector, by an immediate, by a
1529/// runtime scalar, or by a per-lane count.
1530///
1531/// `<<` and `>>` are the operator forms. **`>>` is a logical shift even on a
1532/// signed vector**, since the operator traits are shared with the unsigned
1533/// vectors. Sign-filling shifts live on [`SignedIntegerVector`] as
1534/// [`srai`](SignedIntegerVector::srai) / [`sra`](SignedIntegerVector::sra) /
1535/// [`srav`](SignedIntegerVector::srav).
1536///
1537/// Per-lane variable shifts ([`shlv`](Self::shlv) and friends) are one
1538/// instruction where the ISA has them (AVX2 `vpsllvd`) and a lane walk where it
1539/// does not, which [`HAS_TRUE_SHIFTV`](Self::HAS_TRUE_SHIFTV) reports.
1540#[rustfmt::skip] #[thermite_macros::vector_trait]
1541#[diagnostic::on_unimplemented(
1542 message = "`{Self}` does not support bit-shift vector operations",
1543 label = "no `<<`, `>>`, rotate, or byte-shift",
1544 note = "`BitshiftVector` is implemented by integer vectors (`Vector<i32>`, `Vector<u8>`, `i32xN`, ...). Float and mask vectors do not have shifts."
1545)]
1546pub trait BitshiftVector:
1547 BitwiseVector
1548 + ops::ShrMasked<Self::Mask, Self::Unsigned, Output = Self>
1549 + ops::ShrAssignMasked<Self::Mask, Self::Unsigned>
1550 + ops::ShlMasked<Self::Mask, Self::Unsigned, Output = Self>
1551 + ops::ShlAssignMasked<Self::Mask, Self::Unsigned>
1552 + ops::ShrMasked<Self::Mask, u32, Output = Self>
1553 + ops::ShrAssignMasked<Self::Mask, u32>
1554 + ops::ShlMasked<Self::Mask, u32, Output = Self>
1555 + ops::ShlAssignMasked<Self::Mask, u32>
1556{
1557 /// `true` if the backend has a true per-lane variable shift instruction
1558 /// (e.g. AVX2 `vpsllvd`). When `false`, [`shlv`](Self::shlv) /
1559 /// [`shrv`](Self::shrv) are emulated and may be slower than splatting a
1560 /// scalar shift count through [`shli`](Self::shli) / [`shri`](Self::shri).
1561 const HAS_TRUE_SHIFTV: bool;
1562
1563 /// `true` if the backend can byte-shift the entire vector as a single
1564 /// large integer at register widths above 128 bits without lane-boundary
1565 /// stitching. When `false`, [`bshli`](Self::bshli) / [`bshri`](Self::bshri)
1566 /// on wider vectors are emulated via shuffles.
1567 const HAS_WIDE_BYTE_SHIFTS: bool;
1568
1569 /// Treats the entire vector as a single large integer and shifts left by the immediate value
1570 /// number of BYTES. Not bits, bytes.
1571 ///
1572 /// Bits shifted out at the high end are discarded; the low end is zero-filled.
1573 #[conditional] fn bshli<const I: i32>(self) -> Self;
1574
1575 /// Treats the entire vector as a single large integer and shifts right by the immediate value
1576 /// number of BYTES. Not bits, bytes.
1577 ///
1578 /// Bits shifted out at the low end are discarded; the high end is zero-filled.
1579 #[conditional] fn bshri<const I: i32>(self) -> Self;
1580
1581 /// For each lane in the vector, shift left by the immediate value.
1582 #[conditional] fn shli<const I: i32>(self) -> Self;
1583
1584 /// For each lane in the vector, shift right by the immediate value.
1585 #[conditional] fn shri<const I: i32>(self) -> Self;
1586
1587 /// For each lane in the vector, shift left by the given value.
1588 #[conditional] fn shlv(self, counts: Self::Unsigned) -> Self;
1589
1590 /// For each lane in the vector, shift right by the given value.
1591 #[conditional] fn shrv(self, counts: Self::Unsigned) -> Self;
1592
1593 /// For each element in the vector, rotate the bits to the left by the given
1594 /// number of bits.
1595 #[conditional] fn rol(self, shift: u32) -> Self;
1596 /// For each element in the vector, rotate the bits to the right by the given
1597 /// number of bits.
1598 #[conditional] fn ror(self, shift: u32) -> Self;
1599 /// For each element in the vector, rotate the bits to the left by the immediate
1600 /// value number of bits.
1601 #[conditional] fn roli<const I: i32>(self) -> Self;
1602 /// For each element in the vector, rotate the bits to the right by the immediate
1603 /// value number of bits.
1604 #[conditional] fn rori<const I: i32>(self) -> Self;
1605
1606 /// For each element in the vector, rotate the bits to the left by the given
1607 /// number of bits in the corresponding lane of `counts`.
1608 #[conditional] fn rolv(self, counts: Self::Unsigned) -> Self;
1609
1610 /// For each element in the vector, rotate the bits to the right by the given
1611 /// number of bits in the corresponding lane of `counts`.
1612 #[conditional] fn rorv(self, counts: Self::Unsigned) -> Self;
1613
1614 /// For each element in the vector, reverse the bits of that element.
1615 #[conditional] fn reverse_bits(self) -> Self;
1616}
1617
1618/// Per-lane numeric conversion between vector types.
1619///
1620/// Implementing `CastVector<FROM>` for `Self` means a `FROM` value can be
1621/// converted into `Self` with the same semantics as Rust's `as` operator on
1622/// the underlying scalar elements. Most users should call
1623/// [`GenericVector::cast`] rather than these methods directly.
1624pub trait CastVector<FROM: Sized>: Sized {
1625 /// Convert a vector of type `FROM` into `Self`, lane-by-lane, using `as`
1626 /// semantics on each element.
1627 fn cast_from(from: FROM) -> Self;
1628
1629 /// Convert this vector into a vector of type `FROM`, lane-by-lane.
1630 fn cast_into(self) -> FROM;
1631
1632 /// Like [`cast_from`](Self::cast_from), but may take a faster path that
1633 /// relaxes IEEE corner cases. See [`GenericVector::fast_cast`].
1634 #[inline(always)]
1635 fn fast_cast_from(from: FROM) -> Self {
1636 Self::cast_from(from)
1637 }
1638
1639 /// Like [`cast_into`](Self::cast_into), but may take a faster path that
1640 /// relaxes IEEE corner cases. See [`GenericVector::fast_cast`].
1641 #[inline(always)]
1642 fn fast_cast_into(self) -> FROM {
1643 Self::cast_into(self)
1644 }
1645}
1646
1647/// Zero-cost bit-level reinterpretation between vector types of the same
1648/// size and lane count.
1649///
1650/// Unlike [`CastVector`], no numeric conversion is performed: the underlying
1651/// bits are reinterpreted as the destination element type. Typical use is
1652/// moving between a float vector and its integer "bits" vector.
1653pub trait BitCastVector<FROM: Sized>: Sized {
1654 /// Reinterpret the bit pattern of `bits` as a value of `Self`.
1655 fn from_bits(bits: FROM) -> Self;
1656}
1657
1658/// Vector-layer mirror of [`SaturatingCastRegister`](crate::register::SaturatingCastRegister):
1659/// a narrowing, same-signedness cast that clamps out-of-range values to the
1660/// destination element range instead of wrapping like [`CastVector`].
1661///
1662/// Implemented only for narrowing same-sign integer pairs (`i64 -> ... -> i8`,
1663/// `u64 -> ... -> u8`, including skip-level pairs). Widening and sign-changing
1664/// conversions are not saturating and go through [`CastVector`]. Most users
1665/// reach this through [`GenericVector::saturating_cast`] rather than naming the
1666/// trait directly.
1667///
1668/// Blanket-implemented for every `Vector<INTO>` whose register implements
1669/// [`SaturatingCastRegister<FROM>`](crate::register::SaturatingCastRegister).
1670pub trait SaturatingCastVector<FROM: Sized>: Sized {
1671 /// Narrow `from` into `Self`, clamping each lane to `Self`'s element range.
1672 fn saturating_cast_from(from: FROM) -> Self;
1673}
1674
1675/// A `u16`/`u8` integer vector reinterpreted as a vector of *packed floats* (format `S`: fp16,
1676/// bfloat16, the fp8 variants, ...), transcodable to and from the wider `f32` vector `F` of the
1677/// same lane count.
1678///
1679/// This is the vector-layer mirror of
1680/// [`PackedFloatRegister`](crate::register::PackedFloatRegister): `Self` is the `Vector<u16/u8
1681/// register>` and `F` is the matching `Vector<f32 register>`. Both directions are exact for the
1682/// decode (every value of these sub-`f32` formats is representable in `f32`) and round-to-nearest
1683/// for the encode; backends use hardware (F16C `vcvtph2ps`) where available and a generic
1684/// branchless fallback otherwise.
1685///
1686/// Blanket-implemented for every `Vector<R>` whose register implements `PackedFloatRegister<S,
1687/// FR>`, so e.g. `u16x8<S>: PackedFloatVector<Fp16, f32x8<S>>` holds wherever the register does.
1688///
1689/// ```
1690/// # use thermite::prelude::*;
1691/// # use thermite::element::float::spec::Fp16;
1692/// # use thermite::vector::PackedFloatVector;
1693/// fn widen<U, F>(halves: U) -> F
1694/// where
1695/// U: PackedFloatVector<Fp16, F>,
1696/// {
1697/// halves.unpack()
1698/// }
1699/// ```
1700pub trait PackedFloatVector<S: crate::element::float::spec::FloatSpec, F>: GenericVector {
1701 /// Encode the `f32` vector `values` into this packed format (round to nearest, ties to even;
1702 /// overflow / non-finite handled per the format `S`).
1703 fn pack(values: F) -> Self;
1704
1705 /// Decode this packed-float vector into the `f32` vector it represents (exact).
1706 fn unpack(self) -> F;
1707}
1708
1709/// A `u8` vector whose absolute differences can be summed in groups of 2 byte-lanes into
1710/// the `u16` vector `W` (same total width, `LANES / 2` output lanes).
1711///
1712/// Vector-layer mirror of [`Sad16Register`](crate::register::Sad16Register);
1713/// blanket-implemented for every `Vector<R>` whose register implements it. Each output
1714/// lane is at most `510`. There is no accumulating form - a `u16` lane saturates after
1715/// ~128 accumulations; use [`Sad32Vector`] / [`Sad64Vector`] to reduce over a long run.
1716pub trait Sad16Vector<W>: GenericVector {
1717 /// Sum of absolute differences over each aligned pair of byte lanes.
1718 fn sad16(self, other: Self) -> W;
1719}
1720
1721/// A `u8` vector whose absolute differences can be summed in groups of 4 byte-lanes into
1722/// the `u32` vector `W` (same total width, `LANES / 4` output lanes).
1723///
1724/// Vector-layer mirror of [`Sad32Register`](crate::register::Sad32Register). Each output
1725/// lane is at most `1020`, so [`sad32_accum`](Self::sad32_accum) absorbs ~4.2e6
1726/// accumulations before overflow.
1727pub trait Sad32Vector<W>: GenericVector {
1728 /// Sum of absolute differences over each aligned group of 4 byte lanes.
1729 fn sad32(self, other: Self) -> W;
1730
1731 /// `acc + self.sad32(other)` - the accumulate step of a blocked SAD loop.
1732 fn sad32_accum(self, acc: W, other: Self) -> W;
1733}
1734
1735/// A `u8` vector whose absolute differences can be summed in groups of 8 byte-lanes into
1736/// the `u64` vector `W` (same total width, `LANES / 8` output lanes) - x86 `PSADBW`
1737/// semantics.
1738///
1739/// Vector-layer mirror of [`Sad64Register`](crate::register::Sad64Register). The `u64`
1740/// lanes are accumulation headroom (each result is at most `2040`), so the intended shape
1741/// of a byte-buffer reduction is to [`sad64_accum`](Self::sad64_accum) through the loop
1742/// and reduce horizontally exactly once at the end:
1743///
1744/// ```ignore
1745/// let mut acc = W::ZERO;
1746/// for (a, b) in blocks { acc = a.sad64_accum(acc, b); }
1747/// let total = acc.sum_elements();
1748/// ```
1749pub trait Sad64Vector<W>: GenericVector {
1750 /// Sum of absolute differences over each aligned group of 8 byte lanes.
1751 fn sad64(self, other: Self) -> W;
1752
1753 /// `acc + self.sad64(other)` - the accumulate step of a blocked SAD loop.
1754 fn sad64_accum(self, acc: W, other: Self) -> W;
1755}
1756
1757/// Lanes of a vector partitioned into groups of equal value, produced by
1758/// [`group_by_value`](PartialOrdVector::group_by_value).
1759///
1760/// Each call to [`next_group`](Self::next_group) yields one distinct value and
1761/// the mask of lanes holding it; groups come out in order of first occurrence,
1762/// and every selected lane is yielded exactly once. That turns a divergent
1763/// packet, whose lanes want different work, into a short sequence of uniform
1764/// sub-packets.
1765///
1766/// The inherent [`next_group`](Self::next_group) is the primary interface: a
1767/// plain `while let` loop needs no trait in scope and inlines predictably inside
1768/// `#[target_feature]` bodies. [`Iterator`] is implemented on top of it, so
1769/// `for` loops work too.
1770///
1771/// ```ignore
1772/// // Shade a ray packet one geometry at a time.
1773/// let mut groups = geom_ids.group_by_value(active);
1774/// while let Some((geom_id, lanes)) = groups.next_group() {
1775/// shade(geom_id, lanes);
1776/// }
1777/// ```
1778///
1779/// Cost is proportional to the number of *distinct* values, not the lane count:
1780/// roughly a broadcast, a compare, and two mask ops per group. A uniform packet
1781/// costs one iteration.
1782#[derive(Debug, Clone, Copy)]
1783pub struct ValueGroups<V: PartialOrdVector> {
1784 value: V,
1785 remaining: V::Mask,
1786}
1787
1788impl<V: PartialOrdVector> ValueGroups<V> {
1789 /// The next distinct value and the mask of remaining lanes holding it, or
1790 /// `None` once every selected lane has been yielded.
1791 #[inline(always)]
1792 pub fn next_group(&mut self) -> Option<(V::Element, V::Mask)> {
1793 let lane = self.remaining.first_set()?;
1794
1795 // `broadcastv` rather than `splat(extractv(..))`: one register op that
1796 // backends already specialize, instead of a lane -> scalar -> lane
1797 // round trip through memory.
1798 let group = self.remaining & self.value.cmp_eq(self.value.broadcastv(lane));
1799 let value = self.value.extractv(lane);
1800
1801 self.remaining = crate::vector::ops::BitAndNot::bitandnot(self.remaining, group);
1802
1803 Some((value, group))
1804 }
1805
1806 /// Lanes not yet yielded, so a caller can stop part-way and keep the rest.
1807 #[inline(always)]
1808 pub fn remaining(&self) -> V::Mask {
1809 self.remaining
1810 }
1811
1812 /// Whether every selected lane has been yielded.
1813 #[inline(always)]
1814 pub fn is_empty(&self) -> bool {
1815 self.remaining.none()
1816 }
1817}
1818
1819impl<V: PartialOrdVector> Iterator for ValueGroups<V> {
1820 type Item = (V::Element, V::Mask);
1821
1822 #[inline(always)]
1823 fn next(&mut self) -> Option<Self::Item> {
1824 self.next_group()
1825 }
1826}
1827
1828/// Per-lane comparison producing a [`Mask`](GenericVector::Mask).
1829///
1830/// Each comparison returns a mask whose lanes are `true` where the predicate
1831/// held for the corresponding lane pair and `false` otherwise. The mask can
1832/// then be used with [`select`](crate::mask::GenericMask::select),
1833/// `_c`/`_m`/`_z` masked variants, or reduced via
1834/// [`all`](crate::mask::GenericMask::all) /
1835/// [`any`](crate::mask::GenericMask::any).
1836///
1837/// For floating-point vectors, NaN compares unequal to everything, so e.g.
1838/// `cmp_lt(x, NaN)` is always `false`, matching the `<` operator on `f32`/`f64`.
1839#[diagnostic::on_unimplemented(
1840 message = "`{Self}` does not support lane-wise comparisons",
1841 label = "no `cmp_lt` / `cmp_le` / `cmp_gt` / `cmp_ge` / `cmp_eq` / `cmp_ne`",
1842 note = "`PartialOrdVector` turns lane-wise comparisons into a `Mask`; it is implemented by all numeric vectors (integer and float)."
1843)]
1844pub trait PartialOrdVector: GenericVector + PartialEq {
1845 /// Partition the lanes selected by `valid` into groups of equal value.
1846 ///
1847 /// See [`ValueGroups`] for the loop shape and cost. Pass
1848 /// `Self::Mask::TRUTHY` to group every lane.
1849 #[inline(always)]
1850 fn group_by_value(self, valid: Self::Mask) -> ValueGroups<Self> {
1851 ValueGroups {
1852 value: self,
1853 remaining: valid,
1854 }
1855 }
1856
1857 /// Lane-wise `self < other`.
1858 fn cmp_lt(self, other: Self) -> Self::Mask;
1859 /// Lane-wise `self <= other`.
1860 fn cmp_le(self, other: Self) -> Self::Mask;
1861 /// Lane-wise `self > other`.
1862 fn cmp_gt(self, other: Self) -> Self::Mask;
1863 /// Lane-wise `self >= other`.
1864 fn cmp_ge(self, other: Self) -> Self::Mask;
1865 /// Lane-wise `self == other`.
1866 fn cmp_eq(self, other: Self) -> Self::Mask;
1867 /// Lane-wise `self != other`.
1868 fn cmp_ne(self, other: Self) -> Self::Mask;
1869}
1870
1871/// Vectors that support arithmetic and comparison operations on their elements.
1872///
1873/// This trait sits between [`PartialOrdVector`] and the more specific
1874/// [`SignedVector`] / [`IntegerVector`] / [`FloatVector`] traits, and provides
1875/// the operator overloads (`+`, `-`, `*`, `/`, `%`, their `*Assign` variants,
1876/// and the masked `_c`/`_m`/`_z` forms via [`ops`]).
1877///
1878/// # Overflow semantics
1879///
1880/// **For integer element types, the basic arithmetic operators (`+`, `-`, `*`,
1881/// `/`, `%`) are wrapping on overflow.** This matches the behavior of every
1882/// SIMD ISA (`paddd`, `pmulld`, etc. all wrap silently) and avoids per-lane
1883/// panics inside vectorized loops. Concretely, on every backend including the
1884/// scalar reference backend, `Vector::<i32x4>::splat(i32::MAX) + Vector::ONE`
1885/// produces `i32::MIN` in every lane rather than panicking.
1886///
1887/// This is intentional and is **not affected by debug vs release builds**: the
1888/// scalar backend uses `wrapping_add` / `wrapping_sub` / `wrapping_mul`
1889/// internally, so the wrapping behavior is consistent across all build
1890/// configurations. If you need saturation or explicit wrapping naming, use
1891/// [`saturating_add`](IntegerVector::saturating_add) /
1892/// [`saturating_sub`](IntegerVector::saturating_sub), or the
1893/// `num_traits::WrappingAdd` / `WrappingSub` / `WrappingMul` impls.
1894///
1895/// Integer division (`/`, `%`) panics on division by zero, matching scalar
1896/// Rust. Float division by zero produces an infinity or NaN per IEEE 754.
1897///
1898/// For float element types, overflow simply produces an infinity per IEEE 754;
1899/// there is nothing to wrap.
1900#[rustfmt::skip] #[thermite_macros::vector_trait]
1901#[diagnostic::on_unimplemented(
1902 message = "`{Self}` does not support arithmetic vector operations",
1903 label = "no `+`, `-`, `*`, `/`, `%`, min/max, or FMA",
1904 note = "`NumericVector` is implemented by numeric vectors - integer (`Vector<i32>`, `i32xN`, ...) and float (`Vector<f32>`, `f32xN`, ...). Masks and bare scalars do not qualify.",
1905 note = "A bare `f32`/`f64` is not a vector: wrap it in `Vector::<f32>::splat(x)` first."
1906)]
1907pub trait NumericVector:
1908 PartialOrdVector<Element: num_traits::NumOps>
1909 + ops::AddMasked<Self::Mask, Self, Output = Self>
1910 + ops::AddAssignMasked<Self::Mask, Self>
1911 + ops::SubMasked<Self::Mask, Self, Output = Self>
1912 + ops::SubAssignMasked<Self::Mask, Self>
1913 + ops::MulMasked<Self::Mask, Self, Output = Self>
1914 + ops::MulAssignMasked<Self::Mask, Self>
1915 + ops::DivMasked<Self::Mask, Self, Output = Self>
1916 + ops::DivAssignMasked<Self::Mask, Self>
1917 + ops::RemMasked<Self::Mask, Self, Output = Self>
1918 + ops::RemAssignMasked<Self::Mask, Self>
1919 + ops::SquareMasked<Self::Mask, Output = Self>
1920 + num_traits::NumOps<Self>
1921 + num_traits::NumAssignOps<Self>
1922 + core::iter::Sum
1923 + core::iter::Product
1924{
1925 /// A vector of the value "0" in the element type.
1926 const ZERO: Self;
1927 /// A vector of the value "1" in the element type.
1928 const ONE: Self;
1929 /// A vector of the value "2" in the element type.
1930 const TWO: Self;
1931
1932 /// A vector of the minimum value the element type of this vector can represent.
1933 const MIN: Self;
1934 /// A vector of the maximum value the element type of this vector can represent.
1935 const MAX: Self;
1936
1937 /// Convert each lane to the companion signed integer type, with `as` semantics -
1938 /// round toward zero, saturating at the bounds, NaN to zero.
1939 ///
1940 /// This is the numeric conversion, *not* a bit reinterpretation; for the bit
1941 /// pattern of a float see [`GenericVector::into_bits`].
1942 ///
1943 /// # Why a method and not a `CastVector` bound
1944 ///
1945 /// A bound would have to be written either as `Self::Signed: CastVector<Self>`,
1946 /// whose impl `Self` type is an associated-type projection and so cannot be
1947 /// written at all, or as `Self: CastVector<Self::Signed>`, which collides with the
1948 /// blanket self-casts the composite types already carry. A method has no coherence
1949 /// surface and every implementor can simply provide it.
1950 fn to_signed_integer(self) -> Self::Signed;
1951
1952 /// Convert each lane from the companion signed integer type, with `as` semantics.
1953 ///
1954 /// For composite element types this produces a value with no imaginary part, no
1955 /// derivative and no error term: an integer carries none of those.
1956 fn from_signed_integer(v: Self::Signed) -> Self;
1957
1958 /// Convert each lane to the companion unsigned integer type, with `as` semantics.
1959 /// See [`to_signed_integer`](Self::to_signed_integer).
1960 fn to_unsigned_integer(self) -> Self::Unsigned;
1961
1962 /// Convert each lane from the companion unsigned integer type, with `as` semantics.
1963 /// See [`from_signed_integer`](Self::from_signed_integer).
1964 fn from_unsigned_integer(v: Self::Unsigned) -> Self;
1965
1966 /// Like [`to_signed_integer`](Self::to_signed_integer), but may relax IEEE corner
1967 /// cases (out-of-range and NaN inputs) for speed. Defaults to the exact form.
1968 #[inline(always)]
1969 fn fast_to_signed_integer(self) -> Self::Signed {
1970 self.to_signed_integer()
1971 }
1972
1973 /// Like [`to_unsigned_integer`](Self::to_unsigned_integer), but may relax IEEE
1974 /// corner cases. Defaults to the exact form.
1975 #[inline(always)]
1976 fn fast_to_unsigned_integer(self) -> Self::Unsigned {
1977 self.to_unsigned_integer()
1978 }
1979
1980 /// For each element in the vector, return a mask indicating whether that element is zero.
1981 fn is_zero(self) -> Self::Mask;
1982
1983 /// Returns `true` if all elements in the vector are zero, `false` otherwise.
1984 ///
1985 /// This can often be more performant than naive comparisons or even `is_zero().all()`
1986 fn is_all_zero(self) -> bool;
1987
1988 /// Return the minimum of two vectors, element-wise.
1989 #[conditional] fn min(self, other: Self) -> Self;
1990
1991 /// Return the maximum of two vectors, element-wise.
1992 #[conditional] fn max(self, other: Self) -> Self;
1993
1994 /// Sort the lanes of this vector in `O` order.
1995 ///
1996 /// Backed by a sorting network where one exists for the lane count, and by
1997 /// a scalar compare-and-swap walk otherwise - see
1998 /// [`NumericRegister::sort_by`](crate::register::NumericRegister::sort_by),
1999 /// which this delegates to so a backend override is picked up here too.
2000 /// The direction is free; see [`crate::sort`].
2001 fn sort_by<O: crate::sort::SortOrder>(self) -> Self;
2002
2003 /// Sort the lanes of a **bitonic** vector in `O` order - one that rises then
2004 /// falls, or a rotation of one.
2005 ///
2006 /// Garbage in, garbage out on non-bitonic input. See
2007 /// [`NumericRegister::bitonic_clean_by`](crate::register::NumericRegister::bitonic_clean_by).
2008 fn bitonic_clean_by<O: crate::sort::SortOrder>(self) -> Self;
2009
2010 /// Sort the lanes ascending. Shorthand for
2011 /// [`sort_by::<Ascending>`](Self::sort_by).
2012 #[inline(always)]
2013 fn sort(self) -> Self {
2014 self.sort_by::<crate::sort::Ascending>()
2015 }
2016
2017 /// Sort the lanes of a **bitonic** vector ascending. Shorthand for
2018 /// [`bitonic_clean_by::<Ascending>`](Self::bitonic_clean_by).
2019 #[inline(always)]
2020 fn bitonic_clean(self) -> Self {
2021 self.bitonic_clean_by::<crate::sort::Ascending>()
2022 }
2023
2024 /// Clamps the elements of the vector between the given minimum and maximum values.
2025 fn clamp(self, min: Self, max: Self) -> Self;
2026
2027 /// Returns the minimum value in the vector.
2028 ///
2029 /// This operation has an `O(log2 n)` complexity to reduce.
2030 fn min_element(self) -> Self::Element;
2031 /// Returns the maximum value in the vector.
2032 ///
2033 /// This operation has an `O(log2 n)` complexity to reduce.
2034 fn max_element(self) -> Self::Element;
2035 /// Returns both the minimum and maximum values in the vector simultaneously.
2036 ///
2037 /// More efficient than calling [`min_element`](NumericVector::min_element) and
2038 /// [`max_element`](NumericVector::max_element) separately when both are needed.
2039 fn min_max_element(self) -> (Self::Element, Self::Element);
2040
2041 /// Returns the indices of the minimum and maximum elements in the vector, respectively.
2042 fn arg_minmax(self) -> (usize, usize);
2043
2044 /// Scales each element in the vector by the given factor.
2045 ///
2046 /// While semantically equivalent to `self * Self::splat(factor)`, this method may be optimized
2047 /// better on certain architectures, such as GPUs.
2048 #[conditional] fn scale(self, factor: Self::Element) -> Self;
2049
2050 /// Sums adjacent lane pairs from `lo` and `hi`, returning a vector of the same width.
2051 ///
2052 /// Output: `[lo[0]+lo[1], lo[2]+lo[3], ..., hi[0]+hi[1], hi[2]+hi[3], ...]`
2053 ///
2054 /// The result is always in strict order: all pair sums from `lo` followed by all pair sums from `hi`.
2055 fn pairwise_sum(lo: Self, hi: Self) -> Self;
2056
2057 /// Like [`pairwise_sum`](NumericVector::pairwise_sum), but may return a relaxed (implementation-defined)
2058 /// lane ordering for performance. Treat this as if randomly shuffling the result of
2059 /// [`pairwise_sum`](NumericVector::pairwise_sum), with better performance than `pairwise_sum`.
2060 ///
2061 /// Prefer this if you are simply summing any adjacent pairs from `lo` and `hi`, and don't
2062 /// care about the exact ordering of the resulting sums.
2063 fn relaxed_pairwise_sum(lo: Self, hi: Self) -> Self;
2064
2065 /// Returns the sum of all elements in the vector.
2066 ///
2067 /// This operation has an `O(log2 n)` complexity to reduce.
2068 fn sum_elements(self) -> Self::Element;
2069
2070 /// Returns the product of all elements in the vector.
2071 ///
2072 /// This operation has an `O(log2 n)` complexity to reduce.
2073 fn prod_elements(self) -> Self::Element;
2074
2075 /// Inclusive forward prefix sum ("running total"): `out[i] = self[0] + .. + self[i]`.
2076 ///
2077 /// Unlike [`sum_elements`](Self::sum_elements), which collapses the register to one
2078 /// scalar, this keeps every partial sum in its own lane - the primitive behind
2079 /// bin offsets and stream-compaction write indices.
2080 ///
2081 /// `O(log2 LANES)` vector ops where the backend has a native cross-register
2082 /// [`align`](GenericVector::align), a sequential lane walk where it does not,
2083 /// chosen at compile time. For a scan over only some lanes, neutralise the rest
2084 /// first: `v.zz(mask).prefix_sum()`.
2085 ///
2086 /// ```
2087 /// use thermite::prelude::*;
2088 /// use thermite::backend::scalar::Scalar;
2089 ///
2090 /// let v = <thermite::simd::i32x4<Scalar>>::new([1, 2, 3, 4]);
2091 /// assert_eq!(v.prefix_sum().into_array(), [1, 3, 6, 10].into());
2092 /// assert_eq!(v.reverse_prefix_sum().into_array(), [10, 9, 7, 4].into());
2093 /// ```
2094 fn prefix_sum(self) -> Self;
2095
2096 /// Inclusive forward prefix minimum: `out[i] = min(self[0], .., self[i])`.
2097 ///
2098 /// See [`prefix_sum`](Self::prefix_sum) for the cost model. With NaN lanes, which
2099 /// operand wins is unspecified (as for [`min`](Self::min) itself); exact and
2100 /// backend-identical otherwise, infinities included.
2101 fn prefix_min(self) -> Self;
2102
2103 /// Inclusive forward prefix maximum: `out[i] = max(self[0], .., self[i])`.
2104 ///
2105 /// See [`prefix_min`](Self::prefix_min) for the NaN caveat.
2106 fn prefix_max(self) -> Self;
2107
2108 /// Inclusive reverse (suffix) sum: `out[i] = self[i] + .. + self[LANES-1]`.
2109 fn reverse_prefix_sum(self) -> Self;
2110
2111 /// Inclusive reverse (suffix) minimum: `out[i] = min(self[i], .., self[LANES-1])`.
2112 ///
2113 /// See [`prefix_min`](Self::prefix_min) for the NaN caveat.
2114 fn reverse_prefix_min(self) -> Self;
2115
2116 /// Inclusive reverse (suffix) maximum: `out[i] = max(self[i], .., self[LANES-1])`.
2117 ///
2118 /// See [`prefix_min`](Self::prefix_min) for the NaN caveat.
2119 fn reverse_prefix_max(self) -> Self;
2120
2121 /// Returns a vector whose every lane equals [`LANES`](GenericVector::LANES),
2122 /// converted into the element type.
2123 ///
2124 /// Equivalent to `Self::splat(Self::LANES as Self::Element)`. Useful for
2125 /// stepping an [`indexed`](Self::indexed) counter forward by one full
2126 /// vector's worth of lanes in tight loops.
2127 fn offset() -> Self;
2128
2129 /// Returns a vector where each lane holds its own index, cast to the
2130 /// element type: `[0, 1, 2, ..., LANES-1]`.
2131 ///
2132 /// This is the typical starting point for index-based vector loops. The
2133 /// counter can be advanced by adding [`offset`](Self::offset).
2134 fn indexed() -> Self;
2135}
2136
2137/// Vectors whose elements can represent negative values.
2138///
2139/// Adds negation, absolute value, sign extraction, and sign-conditional
2140/// selection on top of [`NumericVector`]. Implemented for signed integer and
2141/// floating-point vectors; not for unsigned integer vectors.
2142///
2143/// As with the base [`NumericVector`] operators, unary `-` on a signed integer
2144/// vector is **wrapping**: `-Vector::<i32x4>::splat(i32::MIN)` returns
2145/// `i32::MIN` in every lane rather than panicking.
2146// TODO: Add back in some kind of `Signed` trait requirement for Element?
2147#[rustfmt::skip] #[thermite_macros::vector_trait]
2148#[diagnostic::on_unimplemented(
2149 message = "`{Self}` is not a signed SIMD vector",
2150 label = "no `abs`, `signum`, `copysign`, or unary `-`",
2151 note = "`SignedVector` is implemented by signed integer and floating-point vectors. Unsigned integer vectors (`Vector<u32>`, `u8xN`, ...) are not signed."
2152)]
2153pub trait SignedVector: NumericVector + ops::NegMasked<Self::Mask, Output = Self> {
2154 /// A vector of the value "-1" in the element type.
2155 const NEG_ONE: Self;
2156
2157 /// A vector of the smallest positive (non-zero) value in the element type.
2158 const MIN_POSITIVE: Self;
2159
2160 /// Take the absolute value of the vector, element-wise.
2161 #[conditional] fn abs(self) -> Self;
2162
2163 /// For each element in the vector, return a new vector
2164 /// where each element is either -1 or +1 depending
2165 /// on the sign of the element.
2166 ///
2167 /// For integers, this will also return zero (0) if the
2168 /// element is zero. This matches Rust's behavior for integer
2169 /// `signum`. Floats remain only -1 or +1.
2170 fn signum(self) -> Self;
2171
2172 /// For each element in the vector, set the sign of that
2173 /// element to the sign of the corresponding element in the other vector.
2174 #[conditional] fn copysign(self, sign: Self) -> Self;
2175
2176 /// For each element in the vector, return a mask indicating
2177 /// whether that element is negative.
2178 fn is_positive(self) -> Self::Mask;
2179
2180 /// For each element in the vector, return a mask indicating
2181 /// whether that element is positive.
2182 fn is_negative(self) -> Self::Mask;
2183
2184 /// Based on if self is negative, select between `if_neg` and `if_pos`.
2185 fn select_negative(self, if_neg: Self, if_pos: Self) -> Self;
2186}
2187
2188/// Vectors of integer elements.
2189///
2190/// Adds bitwise shifts (via [`BitshiftVector`]), bit-counting, saturating
2191/// arithmetic, branchfree integer division helpers, and exposes the type of
2192/// the per-divider precomputed structures used for vectorized division.
2193///
2194/// # Wrapping arithmetic
2195///
2196/// The basic operators (`+`, `-`, `*`, their assigning forms, and unary `-`
2197/// for signed integer vectors) **wrap on overflow** on every backend, in both
2198/// debug and release builds. See [`NumericVector`] for the rationale. The
2199/// `num_traits::WrappingAdd` / `WrappingSub` / `WrappingMul` impls are simply
2200/// renames of the operator forms; for explicit saturation use
2201/// [`saturating_add`](Self::saturating_add) /
2202/// [`saturating_sub`](Self::saturating_sub).
2203///
2204/// # Reductions
2205///
2206/// Horizontal reductions ([`sum_elements`](NumericVector::sum_elements),
2207/// [`prod_elements`](NumericVector::prod_elements), [`wrapping_sum`](Self::wrapping_sum),
2208/// [`wrapping_prod`](Self::wrapping_prod)) all wrap on overflow.
2209#[rustfmt::skip] #[thermite_macros::vector_trait]
2210#[diagnostic::on_unimplemented(
2211 message = "`{Self}` is not an integer SIMD vector",
2212 label = "not an integer vector",
2213 note = "`IntegerVector` is implemented by integer vectors (`Vector<i32>`, `Vector<u8>`, `i32xN`, ...). Float vectors implement `FloatVector` instead; convert with `.to_int()` or a cast."
2214)]
2215pub trait IntegerVector:
2216 NumericVector<Element: Denominator>
2217 + BitshiftVector
2218 + ops::DivMasked<Self::Mask, Self::Divider, Output = Self>
2219 + ops::DivMasked<Self::Mask, Self::BranchfreeDivider, Output = Self>
2220 // TODO: Some of these might interfere with the methods of this trait,
2221 // adding ambiguity. See what we can do about that.
2222 + num_traits::Saturating + num_traits::SaturatingAdd
2223 + num_traits::SaturatingSub + num_traits::WrappingMul
2224 + num_traits::WrappingAdd + num_traits::WrappingSub
2225{
2226 /// Precomputed scalar divider used by per-lane division against a
2227 /// runtime-known but loop-invariant divisor. See [`crate::Divider`].
2228 type Divider: Copy;
2229 /// Branchfree variant of [`Divider`](Self::Divider). Slightly slower for
2230 /// some divisors but always emits straight-line code with no conditional
2231 /// branches, which is what you want inside a hot SIMD loop.
2232 type BranchfreeDivider: Copy;
2233 /// Precomputed per-lane divider produced by [`to_divider`](Self::to_divider).
2234 /// Used when each lane needs a different (but loop-invariant) divisor.
2235 type VectorizedDivider: Copy;
2236
2237 /// Multiply two vectors lane-wise and return the *high* half of each
2238 /// double-width product.
2239 ///
2240 /// For signed `i32` lanes the result is `(a as i64 * b as i64) >> 32`;
2241 /// for unsigned `u32` it is the same with `u64`. Together with
2242 /// [`mullo`](Self::mullo) this gives the full double-width product
2243 /// without widening the vector type.
2244 #[conditional] fn mulhi(self, other: Self) -> Self;
2245
2246 /// Multiply two vectors lane-wise and return the *low* half of each
2247 /// product, with wrapping on overflow.
2248 ///
2249 /// This is bit-identical to the `*` operator on integer vectors; the
2250 /// dedicated method exists because some ISAs have specialized
2251 /// low-half-only multiply instructions worth emitting directly.
2252 #[conditional] fn mullo(self, other: Self) -> Self;
2253
2254 // fn wrapping_add(self, other: Self) -> Self;
2255 // fn wrapping_sub(self, other: Self) -> Self;
2256 // fn wrapping_mul(self, other: Self) -> Self;
2257
2258 /// Per-lane saturating addition: instead of wrapping, the result is
2259 /// clamped to the element type's range (`MIN`..=`MAX`) on overflow.
2260 #[conditional] fn saturating_add(self, other: Self) -> Self;
2261
2262 /// Per-lane saturating subtraction: instead of wrapping, the result is
2263 /// clamped to the element type's range (`MIN`..=`MAX`) on overflow.
2264 #[conditional] fn saturating_sub(self, other: Self) -> Self;
2265
2266 /// Horizontal sum of all lanes, wrapping on overflow.
2267 ///
2268 /// Equivalent to [`sum_elements`](NumericVector::sum_elements) on integer
2269 /// vectors; the explicit name documents the wrapping behavior at the
2270 /// callsite.
2271 #[conditional] fn wrapping_sum(self) -> Self::Element;
2272
2273 /// Horizontal product of all lanes, wrapping on overflow.
2274 ///
2275 /// Equivalent to [`prod_elements`](NumericVector::prod_elements); the
2276 /// explicit name documents the wrapping behavior at the callsite.
2277 #[conditional] fn wrapping_prod(self) -> Self::Element;
2278
2279 /// Build a [`Divider`](Self::Divider) for a single scalar divisor `d`,
2280 /// suitable for repeatedly dividing many vectors by the same `d`.
2281 ///
2282 /// Construction is `O(1)` but non-trivial; build once outside the hot
2283 /// loop, then use `vec / divider` inside.
2284 fn create_divider(d: Self::Element) -> Self::Divider;
2285
2286 /// Build a [`BranchfreeDivider`](Self::BranchfreeDivider) for a single
2287 /// scalar divisor `d`. Prefer this over [`create_divider`](Self::create_divider)
2288 /// inside tight SIMD loops where conditional branches would hurt
2289 /// throughput.
2290 fn create_branchfree_divider(d: Self::Element) -> Self::BranchfreeDivider;
2291
2292 /// Use this vector as the denominators for a vectorized division operation.
2293 ///
2294 /// This creates a `VectorDivider` which can then be used to perform
2295 /// vectorized integer division with the `Div` trait. Note that for unsigned
2296 /// integer types, `1` is not a valid denominator and will cause a panic.
2297 ///
2298 /// This operation itself is NOT vectorized and is `O(n)` in the number of lanes.
2299 /// It is designed to be calculated once and then reused for multiple division operations.
2300 ///
2301 /// # Panics
2302 ///
2303 /// If unsigned, integer values of `1` present in the vector
2304 /// denominators will cause a panic.
2305 fn to_divider(self) -> Self::VectorizedDivider;
2306
2307 /// For each element in the vector, count the number of bits that are set to 1.
2308 #[conditional] fn count_ones(self) -> Self;
2309 /// For each element in the vector, count the number of bits that are set to 0.
2310 #[conditional] fn count_zeros(self) -> Self;
2311 /// For each element in the vector, count the number of leading ones.
2312 #[conditional] fn leading_ones(self) -> Self;
2313 /// For each element in the vector, count the number of leading zeros.
2314 #[conditional] fn leading_zeros(self) -> Self;
2315 /// For each element in the vector, count the number of trailing ones.
2316 #[conditional] fn trailing_ones(self) -> Self;
2317 /// For each element in the vector, count the number of trailing zeros.
2318 #[conditional] fn trailing_zeros(self) -> Self;
2319
2320 /// For each lane, how many *earlier* lanes hold the same value:
2321 /// `out[i] == |{ j < i : self[j] == self[i] }|`.
2322 ///
2323 /// Equivalent to AVX-512CD's `conflict(self).count_ones()`. Two things fall
2324 /// out of it:
2325 ///
2326 /// - `count_conflicts().cmp_eq(Self::ZERO)` is the **first-occurrence** mask.
2327 /// - The count is the round number for a conflicting read-modify-write. A
2328 /// lane of rank `r` is safe to process in round `r`, since every earlier
2329 /// duplicate has a strictly smaller rank and goes first. That is what
2330 /// makes a vectorized histogram / SAH-bin increment correct where a plain
2331 /// scatter would silently drop duplicate writes.
2332 ///
2333 /// Backed by [`IntegerRegister::count_conflicts`](crate::register::IntegerRegister::count_conflicts),
2334 /// so a backend with hardware conflict detection overrides it in one place.
2335 fn count_conflicts(self) -> Self;
2336}
2337
2338/// The operations that need both a sign and integer lanes: arithmetic
2339/// (sign-filling) right shifts, the overflow-free averages, and the rounded
2340/// high-half multiply.
2341///
2342/// The meeting point of [`SignedVector`] and [`IntegerVector`], implemented only
2343/// by vectors of signed integer elements (`Vector<i32>`, `i16xN`, ...).
2344#[rustfmt::skip] #[thermite_macros::vector_trait]
2345#[diagnostic::on_unimplemented(
2346 message = "`{Self}` is not a signed integer SIMD vector",
2347 label = "not a signed integer vector",
2348 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."
2349)]
2350pub trait SignedIntegerVector: SignedVector + IntegerVector<Element: crate::element::SignedIntegerElement> {
2351 /// For each lane in the vector, right shift in sign bits by the immediate value.
2352 #[conditional] fn srai<const I: i32>(self) -> Self;
2353 /// For each lane in the vector, right shift in sign bits by the given value.
2354 #[conditional] fn sra(self, count: u32) -> Self;
2355 /// For each lane in the vector, right shift in sign bits by the corresponding lane in the shifts vector.
2356 #[conditional] fn srav(self, counts: Self::Unsigned) -> Self;
2357
2358 /// Floor average: `(a + b) >> 1` rounded toward -∞, computed without overflow.
2359 #[conditional] fn avg_floor(self, other: Self) -> Self;
2360 /// Ceiling average: `(a + b + 1) >> 1` rounded toward +∞, computed without overflow.
2361 #[conditional] fn avg_ceil(self, other: Self) -> Self;
2362
2363 /// Rounded high-half signed multiply: the fixed-point `Q(W-1)` product
2364 /// `(self * other + 2^(W-2)) >> (W-1)`, where `W` is the element bit width.
2365 ///
2366 /// For `i16` this is the Q15 rounded multiply (x86 `PMULHRSW`), the
2367 /// fixed-point DSP primitive for gain/volume, fades, and window functions.
2368 /// Unlike [`mulhi`](IntegerVector::mulhi) it rounds to nearest instead of
2369 /// truncating, avoiding a DC bias.
2370 #[conditional] fn mulhrs(self, other: Self) -> Self;
2371}
2372
2373/// The operations that read better on unsigned lanes: the power-of-two and
2374/// inclusive-range predicates, and the unsigned averages.
2375///
2376/// Implemented only by vectors of unsigned integer elements (`Vector<u32>`,
2377/// `u8xN`, ...). Several of these exist here specifically because the unsigned
2378/// form is cheaper: [`in_range`](Self::in_range) is one wrapping subtract and
2379/// one compare, against the two compares an explicit `lo <= x && x <= hi` costs.
2380#[rustfmt::skip] #[thermite_macros::vector_trait]
2381#[diagnostic::on_unimplemented(
2382 message = "`{Self}` is not an unsigned integer SIMD vector",
2383 label = "not an unsigned integer vector",
2384 note = "`UnsignedIntegerVector` is implemented only by vectors of unsigned integer elements (`Vector<u32>`, `u8xN`, ...). Signed integer and float vectors do not qualify."
2385)]
2386pub trait UnsignedIntegerVector: IntegerVector<Element: crate::element::UnsignedIntegerElement> {
2387 /// Determines if each unsigned integer element in the vector is a
2388 /// power of two, returning a mask indicating whether or not it is.
2389 fn is_power_of_two(self) -> Self::Mask;
2390
2391 /// Per-lane inclusive unsigned range test: a mask of `lo <= self <= hi`,
2392 /// assuming `lo <= hi`.
2393 ///
2394 /// Computed branchlessly as `(self - lo) <= (hi - lo)` with wrapping
2395 /// subtraction: a single unsigned compare instead of the two an explicit
2396 /// `self >= lo & self <= hi` would need. The workhorse of byte
2397 /// classification - testing digit/alpha/whitespace ranges.
2398 fn in_range(self, lo: Self, hi: Self) -> Self::Mask;
2399
2400 /// Returns the next power of two minus one for each unsigned integer
2401 /// element in the vector.
2402 #[conditional] fn next_power_of_two_m1(self) -> Self;
2403 /// Computes log2(x) + 1 for each unsigned integer element in the vector.
2404 #[conditional] fn ilog2p1(self) -> Self;
2405
2406 /// Compute the parity of each unsigned integer lane in the vector.
2407 #[conditional] fn parity(self) -> Self;
2408
2409 /// Ceiling average: `(a + b + 1) >> 1`, computed without overflow.
2410 ///
2411 /// Matches x86 `PAVGB`/`PAVGW` and ARM `vrhadd` semantics.
2412 #[conditional] fn avg(self, other: Self) -> Self;
2413
2414 /// Per-lane unsigned absolute difference `|self - other|`, without overflow.
2415 ///
2416 /// Computed branchlessly as `(self -| other) | (other -| self)` with
2417 /// saturating subtraction. The per-lane building block of sum-of-absolute-
2418 /// differences (block matching, motion estimation).
2419 #[conditional] fn abs_diff(self, other: Self) -> Self;
2420
2421 /// Per-lane `N`-dimensional Morton code (Z-order curve index): interleave the
2422 /// low `floor(W / N)` bits of each of the `N` coordinate vectors into one,
2423 /// placing bit `i` of `values[d]` at output position `i * N + d`. `N = 2` is
2424 /// the classic 2D code, `N = 3` the 3D (voxel/octree) code.
2425 ///
2426 /// The workhorse for spatial sorting (BVH/octree builds, grid binning,
2427 /// nearest-neighbour broad-phase): compute a whole vector of codes at once,
2428 /// then sort. [`reverse_morton`](Self::reverse_morton) inverts it.
2429 fn morton<const N: usize>(values: [Self; N]) -> Self;
2430
2431 /// Inverse of [`morton`](Self::morton): de-interleave a Morton code back into
2432 /// its `N` coordinate vectors, where `out[d]` gathers output bits
2433 /// `d, d + N, d + 2N, ...` into the low `floor(W / N)` bits.
2434 fn reverse_morton<const N: usize>(self) -> [Self; N];
2435}
2436
2437/// Escape hatch tying a [`Vector`] to its specific underlying
2438/// [`Register`](crate::register::Register) type.
2439///
2440/// Provides round-trip conversion between the user-facing [`Vector`] and the
2441/// raw register storage. Most generic code should bound on
2442/// [`GenericVector`] (or a more specific vector trait) and never need this;
2443/// it exists so that code which deliberately specializes on a particular
2444/// backend can drop down to the register layer without losing the trait
2445/// hierarchy on the way back up.
2446pub trait VectorWithRegister<R: crate::register::Register>: GenericVector {
2447 /// Consume the vector and yield its raw register storage.
2448 fn into_register(self) -> crate::register::Storage<R>;
2449
2450 /// Wrap a raw register storage value back into a `Vector`.
2451 fn from_register(reg: crate::register::Storage<R>) -> Self;
2452
2453 /// Borrow the vector's elements as a slice.
2454 ///
2455 /// The lane count travels as the slice length (always
2456 /// [`lanes()`](GenericVector::lanes)) rather than in the type, so this is the
2457 /// preferred read accessor over array-typed borrows.
2458 fn as_slice(&self) -> &[Self::Element];
2459
2460 /// Mutably borrow the vector's elements as a slice.
2461 ///
2462 /// See [`as_slice`](Self::as_slice).
2463 fn as_mut_slice(&mut self) -> &mut [Self::Element];
2464}
2465
2466/// Float vector types which have an associated hardware register type.
2467pub trait FloatVectorWithRegister:
2468 FloatVectorWithBits<Mask = crate::Mask<Self::Register>> + VectorWithRegister<Self::Register>
2469{
2470 /// The backing hardware register this vector is a thin wrapper over.
2471 type Register: crate::register::FloatRegister<Element = Self::Element, Lanes = Self::Lanes>;
2472}
2473
2474/// SignedBits integer vector types which have an associated hardware register type.
2475pub trait SignedIntegerVectorWithRegister:
2476 SignedIntegerVector<Mask = crate::Mask<Self::Register>> + VectorWithRegister<Self::Register>
2477{
2478 /// The backing hardware register this vector is a thin wrapper over.
2479 type Register: crate::register::SignedIntegerRegister<Element = Self::Element, Lanes = Self::Lanes>;
2480}
2481
2482/// Unsigned integer vector types which have an associated hardware register type.
2483pub trait UnsignedIntegerVectorWithRegister:
2484 UnsignedIntegerVector<Mask = crate::Mask<Self::Register>> + VectorWithRegister<Self::Register>
2485{
2486 /// The backing hardware register this vector is a thin wrapper over.
2487 type Register: crate::register::UnsignedIntegerRegister<Element = Self::Element, Lanes = Self::Lanes>;
2488}
2489
2490/// Floating-point vectors: the bound most user code should be written against.
2491///
2492/// Carries the float arithmetic, rounding, the FMA family, the predicates
2493/// (`is_finite`, `is_nan`, ...) and the [`FloatConsts`] values, on top of
2494/// everything [`SignedVector`] provides. The policy math library
2495/// ([`CoreMath`](crate::math::CoreMath),
2496/// [`TranscendentalMath`](crate::math::TranscendentalMath) and the rest) attaches
2497/// to this bound, so `V: FloatVector + TranscendentalMath` is the usual signature
2498/// for a numeric kernel.
2499///
2500/// Implemented by the 1-lane `Vector<f32>` / `Vector<f64>`, the native-width
2501/// `f32xN` / `f64xN`, and the composite float types (`Dual`, `Complex`,
2502/// `Compensated`), which is what lets one generic function run as plain SIMD, as
2503/// autodiff, or in double-double precision without being edited.
2504///
2505/// Bare `f32` and `f64` do **not** implement it. Wrap the scalar first with
2506/// `Vector::<f32>::splat(x)`, or use [`ScalarMath`](crate::math::ScalarMath) for
2507/// one-off scalar math.
2508#[rustfmt::skip] #[thermite_macros::vector_trait]
2509#[diagnostic::on_unimplemented(
2510 message = "`{Self}` is not a floating-point SIMD vector",
2511 label = "not a float vector",
2512 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`).",
2513 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()`, ...).",
2514 note = "Integer vectors are not float vectors either; convert with `.to_float()` or a cast before calling float operations."
2515)]
2516pub trait FloatVector: SignedVector<Element: FloatElement>
2517 + FloatConsts
2518 + CastVector<Self::ExtendedPrecision>
2519 + ops::MulAddExtMasked<Self::Mask, Self, Self, Output = Self>
2520 + ops::MulAddAssignExtMasked<Self::Mask, Self, Self>
2521 + ops::AddSubExtMasked<Self::Mask, Output = Self>
2522{
2523 /// The value `0.5` represented in this vector type.
2524 const HALF: Self;
2525 /// The value `-0.0` represented in this vector type.
2526 const NEG_ZERO: Self;
2527 /// The value `infinity` represented in this vector type.
2528 const INFINITY: Self;
2529 /// The value `-infinity` represented in this vector type.
2530 const NEG_INFINITY: Self;
2531 /// The value `NaN` represented in this vector type.
2532 const NAN: Self;
2533 /// Hardware epsilon value in this vector type.
2534 const EPSILON: Self;
2535
2536 /// If available, an extended precision floating point vector type
2537 /// corresponding to this vector type. E.g., for `f32` vectors, this
2538 /// would be an `f64` vector type.
2539 ///
2540 /// If no such type exists, this will be the same as `Self`.
2541 type ExtendedPrecision: FloatVector<Lanes = Self::Lanes> + CastVector<Self>;
2542
2543 /// Check if each element in the vector is infinite, returning a mask.
2544 fn is_infinite(self) -> Self::Mask;
2545
2546 /// Check if each element in the vector is finite, returning a mask.
2547 fn is_finite(self) -> Self::Mask;
2548
2549 /// Check if each element in the vector is NaN, returning a mask.
2550 fn is_nan(self) -> Self::Mask;
2551
2552 /// Check if each element in the vector is zero or subnormal, returning a mask.
2553 fn is_zero_or_subnormal(self) -> Self::Mask;
2554
2555 /// Check if each element in the vector is normal, returning a mask.
2556 fn is_normal(self) -> Self::Mask;
2557
2558 /// Check if each element in the vector is subnormal, returning a mask.
2559 fn is_subnormal(self) -> Self::Mask;
2560
2561 /// `true` if the backend has a hardware approximate-reciprocal
2562 /// instruction (e.g. `rcpps` on x86). When `false`, [`rcp`](Self::rcp)
2563 /// falls back to a full IEEE division and provides no speed advantage
2564 /// over `Self::ONE / self`.
2565 const HAS_APPROX_RCP: bool;
2566
2567 /// `true` if the backend has a hardware approximate-reciprocal-square-root
2568 /// instruction (e.g. `rsqrtps` on x86). When `false`, [`rsqrt`](Self::rsqrt)
2569 /// falls back to `Self::ONE / self.sqrt()`.
2570 const HAS_APPROX_RSQRT: bool;
2571
2572 /// Lane-wise IEEE 754 square root.
2573 ///
2574 /// Negative inputs (other than `-0.0`) produce NaN. `sqrt(-0.0)` is `-0.0`.
2575 #[conditional] fn sqrt(self) -> Self;
2576
2577 /// Lane-wise approximate reciprocal square root.
2578 ///
2579 /// Accuracy is hardware-dependent (typically 12 bits on x86 `rsqrtps`,
2580 /// closer to full precision on newer ISAs). For full-precision results
2581 /// or backends without hardware support, see [`HAS_APPROX_RSQRT`](Self::HAS_APPROX_RSQRT).
2582 #[conditional] fn rsqrt(self) -> Self;
2583
2584 /// Lane-wise approximate reciprocal: `1 / self`.
2585 ///
2586 /// Accuracy is hardware-dependent (typically 12 bits on x86 `rcpps`).
2587 /// For full-precision results or backends without hardware support,
2588 /// see [`HAS_APPROX_RCP`](Self::HAS_APPROX_RCP), or use `Self::ONE / self`.
2589 #[conditional] fn rcp(self) -> Self;
2590
2591 /// Lane-wise floor: largest integer less than or equal to each element.
2592 ///
2593 /// Result type stays the same; the value is the integer rounded toward
2594 /// negative infinity, kept in the float representation.
2595 #[conditional] fn floor(self) -> Self;
2596
2597 /// Lane-wise ceiling: smallest integer greater than or equal to each
2598 /// element, kept in the float representation.
2599 #[conditional] fn ceil(self) -> Self;
2600
2601 /// Lane-wise round-to-nearest.
2602 ///
2603 /// Halfway cases follow the current rounding mode of the hardware. On
2604 /// x86 this is round-half-to-even (banker's rounding), which differs
2605 /// from the scalar `f32::round` / `f64::round` half-away-from-zero
2606 /// convention. If you need a specific tie-breaking rule, do it explicitly.
2607 #[conditional] fn round(self) -> Self;
2608
2609 /// Lane-wise truncation toward zero (drops the fractional part), kept
2610 /// in the float representation.
2611 #[conditional] fn trunc(self) -> Self;
2612
2613 /// Lane-wise fractional part: `self - self.trunc()`.
2614 ///
2615 /// Result has the same sign as the input. For very large magnitudes the
2616 /// fractional part is exactly zero because the float has no fractional bits.
2617 #[conditional] fn fract(self) -> Self;
2618
2619 /// Effectively `self * sign.signum()`, multiplying the sign bits.
2620 #[conditional] fn mul_sign(self, sign: Self) -> Self;
2621
2622 /// Returns zero with the sign of `self`, i.e.: only the sign bit is set.
2623 #[conditional] fn signed_zero(self) -> Self;
2624
2625 /// Returns the next representable value greater than the current value, towards positive infinity.
2626 #[conditional] fn next_up(self) -> Self;
2627
2628 /// Returns the next representable value less than the current value, towards negative infinity.
2629 #[conditional] fn next_down(self) -> Self;
2630
2631 /// Linearly interpolates between `a` and `b` by `self`, where `self` is typically in the range `[0, 1]`.
2632 ///
2633 /// Follows the formula: `a * (1 - self) + b * self`, but the underlying implementation
2634 /// may optimize into certain other formulations.
2635 fn mix(self, a: Self, b: Self) -> Self;
2636
2637 /// Computes `$1 - x^2$` accurately, avoiding the cancellation a naive `1 - self * self`
2638 /// suffers as `self` approaches `±1` (where the result is small but `self * self` is near 1).
2639 ///
2640 /// With hardware FMA this is `nmul_add(self, self, 1)`: the exact product `$x^2$` is formed
2641 /// and subtracted from one with a single rounding. Without FMA it falls back to the factored
2642 /// `$(1 - x)(1 + x)$`, also cancellation-free (`1 - self` is exact for `self` near 1 by
2643 /// Sterbenz's lemma). Both keep full relative accuracy in the small result.
2644 #[inline(always)]
2645 fn one_minus_sq(self) -> Self {
2646 if const { Self::HAS_TRUE_FMA } {
2647 // FMA: 1 - self*self formed from the exact product with a single rounding.
2648 self.nmul_add(self, Self::ONE)
2649 } else {
2650 // No FMA: factored difference of squares, cancellation-free near |self| = 1.
2651 (Self::ONE - self) * (Self::ONE + self)
2652 }
2653 }
2654
2655 /// Inhibit further LLVM auto-vectorization of code surrounding this call.
2656 ///
2657 /// LLVM sometimes tries to "vectorize the vectors" -- repacking
2658 /// already-SIMD code into a wider form that ends up slower. Inserting
2659 /// this call inside a hot loop blocks that pass at the use site. The
2660 /// call itself emits no instructions; only the optimizer barrier remains.
2661 ///
2662 /// # Safety
2663 ///
2664 /// Memory-safe to call, but the side effect on code generation is
2665 /// significant. Only reach for this when you have measured a regression
2666 /// caused by over-aggressive auto-vectorization.
2667 unsafe fn block_autovectorization(&mut self);
2668
2669 /// Attempt to upcast this FloatVector to a FloatVectorWithBits,
2670 /// using the provided kernel. If not possible, returns None.
2671 fn with_bits<const N: usize, K: AsFloatVectorWithBitsKernel<Self, N>>(
2672 _values: [Self; N],
2673 _kernel: K,
2674 ) -> Option<<K as AsFloatVectorWithBitsKernel<Self, N>>::Output> {
2675 None // Default implementation returns None
2676 }
2677}
2678
2679/// Inclusive scan ladder at the **vector** layer, for composite vector types.
2680///
2681/// The register-layer ladder in
2682/// [`polyfills::scan`](crate::backend::generic::polyfills::scan) is written in
2683/// `Register` ops and so cannot be reused by `Dual`/`Compensated`/`Complex`, whose
2684/// scans have to run on their own `Self` operations (a dual carries the winning
2685/// lane's derivative; a compensated sum has to renormalise its error term). This is
2686/// the same ladder spelled in [`GenericVector`] methods, exported so those crates
2687/// share one copy.
2688///
2689/// Invoke inside an `impl` block for the composite -- it resolves `Self`:
2690///
2691/// ```ignore
2692/// fn prefix_min(self) -> Self {
2693/// thermite::scan_ladder!(forward, self, self.broadcast::<0>(), Self::min)
2694/// }
2695/// fn reverse_prefix_sum(self) -> Self {
2696/// thermite::scan_ladder!(reverse, self, Self::ZERO, core::ops::Add::add)
2697/// }
2698/// ```
2699///
2700/// `$op` must be associative (a doubling ladder reassociates freely), and `$fill`
2701/// must leave the already-final lanes alone: `ZERO` for a sum, and a broadcast of
2702/// the *edge* lane for `min`/`max` -- lane 0 forward, lane `LANES - 1` reverse.
2703/// `MIN`/`MAX` are finite bounds and would clamp an infinite lane, which is the same
2704/// trap the register ladder documents.
2705///
2706/// `align`'s offset is a const-generic argument and must be a literal. The reverse
2707/// direction is fine (the shift *is* the offset), but the forward direction needs
2708/// `LANES - s`, hence the match on the compile-time lane count with a per-width
2709/// offset list -- exactly one arm survives monomorphization. Widths outside
2710/// power-of-two `<= 64` have no arm and fall back to reversing, running the reverse
2711/// ladder, and reversing back, which needs only literal shifts and is correct at any
2712/// width.
2713///
2714/// Every stage is an `align`, so on a vector whose
2715/// [`HAS_NATIVE_ALIGN`](GenericVector::HAS_NATIVE_ALIGN) is false each one expands to
2716/// a shuffle-and-blend and the ladder gets correspondingly more expensive. It is
2717/// still `ceil(log2(LANES))` stages against a per-lane walk's `LANES` extract/insert
2718/// pairs, which is why this does not switch lowering the way the register-layer
2719/// ladder does -- there the fallback walks a slice in place and is genuinely cheaper.
2720/// Gate on the const at the call site if a specific composite says otherwise.
2721#[doc(hidden)]
2722#[macro_export]
2723macro_rules! scan_ladder {
2724 (reverse, $v:expr, $fill:expr, $op:path) => {{
2725 let mut v = $v;
2726 let f = $fill;
2727 #[rustfmt::skip]
2728 let () = {
2729 if const { Self::LANES > 1 } { v = $op(v, v.align::<1>(f)); }
2730 if const { Self::LANES > 2 } { v = $op(v, v.align::<2>(f)); }
2731 if const { Self::LANES > 4 } { v = $op(v, v.align::<4>(f)); }
2732 if const { Self::LANES > 8 } { v = $op(v, v.align::<8>(f)); }
2733 if const { Self::LANES > 16 } { v = $op(v, v.align::<16>(f)); }
2734 if const { Self::LANES > 32 } { v = $op(v, v.align::<32>(f)); }
2735 };
2736 v
2737 }};
2738
2739 (forward, $v:expr, $fill:expr, $op:path) => {{
2740 let mut v = $v;
2741 let f = $fill;
2742
2743 if const { Self::LANES.is_power_of_two() && Self::LANES <= 64 } {
2744 // `a.align::<OFFSET>(b)[i] == concat(a, b)[OFFSET + i]`, so with `a = fill`
2745 // and `b = v` the stage that wants `v[i - s]` is `OFFSET == LANES - s`.
2746 #[rustfmt::skip]
2747 let () = match const { Self::LANES } {
2748 0 | 1 => {}
2749 2 => { v = $op(v, f.align::<1>(v)); }
2750 4 => { v = $op(v, f.align::<3>(v));
2751 v = $op(v, f.align::<2>(v)); }
2752 8 => { v = $op(v, f.align::<7>(v));
2753 v = $op(v, f.align::<6>(v));
2754 v = $op(v, f.align::<4>(v)); }
2755 16 => { v = $op(v, f.align::<15>(v));
2756 v = $op(v, f.align::<14>(v));
2757 v = $op(v, f.align::<12>(v));
2758 v = $op(v, f.align::<8>(v)); }
2759 32 => { v = $op(v, f.align::<31>(v));
2760 v = $op(v, f.align::<30>(v));
2761 v = $op(v, f.align::<28>(v));
2762 v = $op(v, f.align::<24>(v));
2763 v = $op(v, f.align::<16>(v)); }
2764 64 => { v = $op(v, f.align::<63>(v));
2765 v = $op(v, f.align::<62>(v));
2766 v = $op(v, f.align::<60>(v));
2767 v = $op(v, f.align::<56>(v));
2768 v = $op(v, f.align::<48>(v));
2769 v = $op(v, f.align::<32>(v)); }
2770 // unreachable: guarded by the `if const` above. Panicking is the right
2771 // failure mode if a width ever slips past that guard.
2772 _ => unreachable!(),
2773 };
2774 v
2775 } else {
2776 // `fill` is the lane-0 broadcast either way: reversing makes it the last
2777 // lane, which is exactly what the reverse ladder wants.
2778 $crate::scan_ladder!(reverse, v.reverse(), f, $op).reverse()
2779 }
2780 }};
2781}
2782
2783/// Run a closure-like block with a generic [`FloatVector`] temporarily upcast
2784/// to a [`FloatVectorWithBits`], when the backend supports it.
2785///
2786/// Given an array of `FloatVector` values and a body parameterized over a
2787/// `FloatVectorWithBits` type, this expands to a call to
2788/// [`FloatVector::with_bits`] with an anonymous kernel implementing
2789/// [`AsFloatVectorWithBitsKernel`]. The body only runs when bit access is
2790/// available for the concrete backend; otherwise the whole expression evaluates
2791/// to `None` (the return type is therefore `Option<_>`).
2792///
2793/// This is the ergonomic front-end to the [`AsFloatVectorWithBitsKernel`]
2794/// pattern; reach for it inside generic code bounded only on `FloatVector` that
2795/// wants an optional fast path requiring bit-level access.
2796#[macro_export]
2797macro_rules! with_bits {
2798 // (($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)* }) => {{
2799 // $crate::with_bits!(($first_value): $ty as fn($first_decl: $first_alias) -> impl $ret $(where $($c: $constraint),*)? {
2800 // $crate::with_bits!(($($value),+): $ty as fn($($decl: $alias),*) -> $ret $(where $($c: $constraint),*)? {
2801 // $($body)*
2802 // })
2803 // })
2804 // }};
2805
2806 ([$($values:expr),+]: [$ty:ty; $len:literal] as fn($decl:ident: [$alias:ident; _]) -> $ret:ty $(where $($c:ty: $constraint:ident),*)? { $($body:tt)* }) => {{
2807 struct AnonymousAsFloatVectorWithBitsKernel<V>(core::marker::PhantomData<V>);
2808
2809 impl<V: FloatVector> $crate::vector::AsFloatVectorWithBitsKernel<V, $len> for AnonymousAsFloatVectorWithBitsKernel<V>
2810 $(where $($c: $constraint),*)?
2811 {
2812 type Output = $ret;
2813
2814 fn with_bits<
2815 $alias: FloatVectorWithBits<
2816 Element = V::Element,
2817 Lanes = V::Lanes,
2818 Mask = V::Mask,
2819 Signed = V::Signed,
2820 Unsigned = V::Unsigned,
2821 ExtendedPrecision = V::ExtendedPrecision,
2822 > + CastVector<V>,
2823 >(
2824 self,
2825 $decl: [$alias; $len],
2826 ) -> Self::Output {
2827 $($body)*
2828 }
2829 }
2830
2831 <V as FloatVector>::with_bits(
2832 [$($values),+],
2833 AnonymousAsFloatVectorWithBitsKernel::<V>(core::marker::PhantomData),
2834 )
2835 }};
2836}
2837
2838/// Some algorithms may benefit from being able to access the bitwise
2839/// representation of floating point vectors. However, not all vectors
2840/// support this functionality, and those that do may be passed as generic
2841/// FloatVector. Therefore, this is a way of upcasting a FloatVector
2842/// to a FloatVectorWithBits, if possible. If not possible, returns None.
2843pub trait AsFloatVectorWithBitsKernel<O: FloatVector, const N: usize> {
2844 /// Whatever the kernel body returns, threaded back out through the upcast.
2845 type Output;
2846
2847 /// Runs the kernel with `v` re-typed as a [`FloatVectorWithBits`].
2848 ///
2849 /// The bound is written on the method rather than the trait so the caller
2850 /// stays generic over plain [`FloatVector`]. The bit-level type only exists
2851 /// inside this call.
2852 fn with_bits<
2853 V: FloatVectorWithBits<
2854 Element = O::Element,
2855 Lanes = O::Lanes,
2856 Mask = O::Mask,
2857 Signed = O::Signed,
2858 Unsigned = O::Unsigned,
2859 ExtendedPrecision = O::ExtendedPrecision,
2860 > + CastVector<O>,
2861 >(
2862 self,
2863 v: [V; N],
2864 ) -> Self::Output;
2865}
2866
2867/// A [`FloatVector`] that additionally exposes its raw bit representation as
2868/// companion integer vectors, enabling bit-level float algorithms.
2869///
2870/// On top of [`FloatVector`] this provides:
2871/// - the [`Bits`](Self::Bits) (unsigned) and [`SignedBits`](Self::SignedBits)
2872/// integer vector types matching this float's bit width and lane count, with
2873/// full [`FullyInteroperable`] cast/bitcast interop between all three views;
2874/// - hardware-accelerated `native_*` transcendentals gated by
2875/// [`NATIVE_CAP`](Self::NATIVE_CAP);
2876/// - bit-level helpers like [`total_order`](Self::total_order) /
2877/// [`linear_order`](Self::linear_order) for sorting and ULP math.
2878///
2879/// Not every float vector implements this (it requires the element to be a
2880/// [`FloatElementWithBits`]); generic code that only sometimes needs bit access
2881/// can attempt to obtain it via [`FloatVector::with_bits`].
2882///
2883/// The methods on this trait do **not** have masked (`_c`/`_m`/`_z`) variants.
2884#[diagnostic::on_unimplemented(
2885 message = "`{Self}` does not expose float bit-manipulation operations",
2886 label = "no `ldexp` / `frexp` or raw bit access",
2887 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."
2888)]
2889pub trait FloatVectorWithBits:
2890 BitwiseVector
2891 + FloatVector<Element: FloatElementWithBits, Signed: CastVector<Self::SignedBits>, Unsigned: CastVector<Self::Bits>>
2892 + GenericVector<
2893 Signed: GenericVector<Mask: CastMask<<Self::SignedBits as GenericVector>::Mask>>,
2894 Unsigned: GenericVector<Mask: CastMask<<Self::Unsigned as GenericVector>::Mask>>,
2895 > + FullyInteroperable<Self::Bits, Self::SignedBits>
2896{
2897 /// This vector's bit pattern viewed as *signed* integer lanes of the same
2898 /// width, for exponent arithmetic and the sign-aware bit tricks.
2899 type SignedBits: SignedIntegerVector<
2900 Mask: CastMask<<Self::Signed as GenericVector>::Mask>,
2901 Lanes = Self::Lanes,
2902 Divider = Divider<<Self::Element as FloatElementWithBits>::SignedBits>,
2903 BranchfreeDivider = BranchfreeDivider<<Self::Element as FloatElementWithBits>::SignedBits>,
2904 Element = <Self::Element as FloatElementWithBits>::SignedBits,
2905 > + FullyInteroperable<Self, Self::Bits>
2906 + CastVector<Self::Signed>;
2907
2908 /// This vector's bit pattern viewed as *unsigned* integer lanes of the same
2909 /// width, which is what masking and shifting the raw bits wants.
2910 type Bits: UnsignedIntegerVector<
2911 Mask: CastMask<<Self::Unsigned as GenericVector>::Mask>,
2912 Lanes = Self::Lanes,
2913 Divider = Divider<<Self::Element as FloatElementWithBits>::Bits>,
2914 BranchfreeDivider = BranchfreeDivider<<Self::Element as FloatElementWithBits>::Bits>,
2915 Element = <Self::Element as FloatElementWithBits>::Bits,
2916 > + FullyInteroperable<Self, Self::SignedBits>
2917 + CastVector<Self::Unsigned>;
2918
2919 /// Bit-flag set describing which `native_*` methods on this trait have a
2920 /// real hardware implementation on the current backend.
2921 ///
2922 /// Test with `NATIVE_CAP.has(NativeCapability::SIN)` etc. before calling
2923 /// the corresponding `native_*` method directly; otherwise the default
2924 /// implementation will panic.
2925 const NATIVE_CAP: NativeCapability;
2926
2927 /// Hardware-accelerated `ldexp`: `self * 2^exp`, lane-wise.
2928 ///
2929 /// # Safety
2930 /// Only callable when [`NATIVE_CAP`](Self::NATIVE_CAP) advertises `LDEXP`.
2931 /// Calling on a backend without hardware support is undefined behavior
2932 /// (the default impl panics via `unreachable!` at the register layer).
2933 unsafe fn native_ldexp(self, exp: Self::SignedBits) -> Self;
2934
2935 /// Hardware-accelerated `frexp`: split each lane into a normalized
2936 /// mantissa in `[0.5, 1.0)` and an integer exponent.
2937 ///
2938 /// # Safety
2939 /// Only callable when [`NATIVE_CAP`](Self::NATIVE_CAP) advertises `FREXP`.
2940 unsafe fn native_frexp(self) -> (Self, Self::SignedBits);
2941
2942 /// Hardware-accelerated combined sine and cosine, lane-wise.
2943 ///
2944 /// # Safety
2945 /// Only callable when [`NATIVE_CAP`](Self::NATIVE_CAP) advertises `SIN_COS`.
2946 unsafe fn native_sin_cos<P: Policy>(self) -> (Self, Self);
2947
2948 /// Hardware-accelerated sine, lane-wise.
2949 ///
2950 /// # Safety
2951 /// Only callable when [`NATIVE_CAP`](Self::NATIVE_CAP) advertises `SIN`.
2952 unsafe fn native_sin<P: Policy>(self) -> Self;
2953
2954 /// Hardware-accelerated cosine, lane-wise.
2955 ///
2956 /// # Safety
2957 /// Only callable when [`NATIVE_CAP`](Self::NATIVE_CAP) advertises `COS`.
2958 unsafe fn native_cos<P: Policy>(self) -> Self;
2959
2960 /// Hardware-accelerated tangent, lane-wise.
2961 ///
2962 /// # Safety
2963 /// Only callable when [`NATIVE_CAP`](Self::NATIVE_CAP) advertises `TAN`.
2964 unsafe fn native_tan<P: Policy>(self) -> Self;
2965
2966 /// Hardware-accelerated `2^self`, lane-wise.
2967 ///
2968 /// # Safety
2969 /// Only callable when [`NATIVE_CAP`](Self::NATIVE_CAP) advertises `EXP2`.
2970 unsafe fn native_exp2<P: Policy>(self) -> Self;
2971
2972 /// Hardware-accelerated `log2(self)`, lane-wise.
2973 ///
2974 /// # Safety
2975 /// Only callable when [`NATIVE_CAP`](Self::NATIVE_CAP) advertises `LOG2`.
2976 unsafe fn native_log2<P: Policy>(self) -> Self;
2977
2978 /// Hardware-accelerated `e^self`, lane-wise.
2979 ///
2980 /// # Safety
2981 /// Only callable when [`NATIVE_CAP`](Self::NATIVE_CAP) advertises `EXP`.
2982 unsafe fn native_exp<P: Policy>(self) -> Self;
2983
2984 /// Hardware-accelerated natural logarithm, lane-wise.
2985 ///
2986 /// # Safety
2987 /// Only callable when [`NATIVE_CAP`](Self::NATIVE_CAP) advertises `LN`.
2988 unsafe fn native_ln<P: Policy>(self) -> Self;
2989
2990 /// Hardware-accelerated `self^exp`, lane-wise.
2991 ///
2992 /// # Safety
2993 /// Only callable when [`NATIVE_CAP`](Self::NATIVE_CAP) advertises `POWF`.
2994 unsafe fn native_powf<P: Policy>(self, exp: Self) -> Self;
2995
2996 /// Return a signed integer vector that is capable of encapsulating
2997 /// the "total order" of the floating point values in this vector,
2998 /// such that when compared as integers, the ordering is the same
2999 /// as the floating point ordering, including NaNs, in the following order:
3000 ///
3001 /// - negative quiet NaN
3002 /// - negative signaling NaN
3003 /// - negative infinity
3004 /// - negative numbers
3005 /// - negative subnormal numbers
3006 /// - negative zero
3007 /// - positive zero
3008 /// - positive subnormal numbers
3009 /// - positive numbers
3010 /// - positive infinity
3011 /// - positive signaling NaN
3012 /// - positive quiet NaN.
3013 ///
3014 /// This is useful for sorting floating point numbers in a way that
3015 /// is consistent and well-defined. However, it may differ from
3016 /// the default floating point comparison behavior of the platform.
3017 ///
3018 /// # Example
3019 /// ```rust
3020 /// # use thermite::backend::scalar::prelude::*;
3021 /// let x = f32x4::NAN;
3022 /// let y = f32x4::ONE;
3023 /// let total_lt = x.total_order().cmp_lt(y.total_order());
3024 /// assert!(total_lt.none()); // NaN is not less than 1.0 in total order
3025 /// ```
3026 fn total_order(self) -> Self::SignedBits;
3027
3028 /// Similar to [`total_order`](FloatVectorWithBits::total_order), but positive zero and negative zero are
3029 /// the same value. This can be used for calculating ULP differences by simply subtracting one from another.
3030 fn linear_order(self) -> Self::SignedBits;
3031}
3032
3033/// Convenience accessors `x()` / `y()` automatically available on any
3034/// 2-lane [`GenericVector`].
3035///
3036/// Each method is just shorthand for [`extract`](GenericVector::extract) at
3037/// the corresponding compile-time index.
3038#[rustfmt::skip]
3039pub trait GenericVector2: GenericVector {
3040 /// Returns the value of lane 0.
3041 #[inline(always)] fn x(&self) -> Self::Element { self.extract::<0>() }
3042 /// Returns the value of lane 1.
3043 #[inline(always)] fn y(&self) -> Self::Element { self.extract::<1>() }
3044}
3045
3046/// Convenience accessors `x()` / `y()` / `z()` automatically available on any
3047/// 3-lane [`GenericVector`].
3048///
3049/// Each method is just shorthand for [`extract`](GenericVector::extract) at
3050/// the corresponding compile-time index.
3051#[rustfmt::skip]
3052pub trait GenericVector3: GenericVector {
3053 /// Returns the value of lane 0.
3054 #[inline(always)] fn x(&self) -> Self::Element { self.extract::<0>() }
3055 /// Returns the value of lane 1.
3056 #[inline(always)] fn y(&self) -> Self::Element { self.extract::<1>() }
3057 /// Returns the value of lane 2.
3058 #[inline(always)] fn z(&self) -> Self::Element { self.extract::<2>() }
3059}
3060
3061/// Convenience accessors `x()` / `y()` / `z()` / `w()` automatically available
3062/// on any 4-lane [`GenericVector`].
3063///
3064/// Each method is just shorthand for [`extract`](GenericVector::extract) at
3065/// the corresponding compile-time index.
3066#[rustfmt::skip]
3067pub trait GenericVector4: GenericVector {
3068 /// Returns the value of lane 0.
3069 #[inline(always)] fn x(&self) -> Self::Element { self.extract::<0>() }
3070 /// Returns the value of lane 1.
3071 #[inline(always)] fn y(&self) -> Self::Element { self.extract::<1>() }
3072 /// Returns the value of lane 2.
3073 #[inline(always)] fn z(&self) -> Self::Element { self.extract::<2>() }
3074 /// Returns the value of lane 3.
3075 #[inline(always)] fn w(&self) -> Self::Element { self.extract::<3>() }
3076}
3077
3078impl<V: GenericVector<Lanes = typenum::U2>> GenericVector2 for V {}
3079impl<V: GenericVector<Lanes = typenum::U3>> GenericVector3 for V {}
3080impl<V: GenericVector<Lanes = typenum::U4>> GenericVector4 for V {}
3081
3082#[rustfmt::skip]
3083macro_rules! impl_swizzle4 {
3084 (@ x) => { 0 };
3085 (@ y) => { 1 };
3086 (@ z) => { 2 };
3087 (@ w) => { 3 };
3088
3089 (IMPL x x x x) => { #[inline(always)] fn xxxx(self) -> Self { self.broadcast::<0>() } };
3090 (IMPL y y y y) => { #[inline(always)] fn yyyy(self) -> Self { self.broadcast::<1>() } };
3091 (IMPL z z z z) => { #[inline(always)] fn zzzz(self) -> Self { self.broadcast::<2>() } };
3092 (IMPL w w w w) => { #[inline(always)] fn wwww(self) -> Self { self.broadcast::<3>() } };
3093
3094 (IMPL $a:ident $b:ident $c:ident $d:ident) => {paste::paste! {
3095 #[inline(always)]
3096 fn [<$a $b $c $d>](self) -> Self {
3097 struct Indices;
3098
3099 impl crate::swizzle::SwizzleIndices<typenum::U4> for Indices {
3100 const INDICES: GenericArray<u32, typenum::U4> = {
3101 unsafe { $crate::generic_array::const_transmute::<_, GenericArray<u32, typenum::U4>>([
3102 impl_swizzle4!(@ $a),
3103 impl_swizzle4!(@ $b),
3104 impl_swizzle4!(@ $c),
3105 impl_swizzle4!(@ $d)
3106 ]) }
3107 };
3108 }
3109
3110 self.permute_const::<Indices>()
3111 }
3112 }};
3113
3114 (DECL $(#[$meta:meta])* $a:ident $b:ident $c:ident $d:ident) => {paste::paste! {
3115 #[allow(missing_docs)]
3116 $(#[$meta])* fn [<$a $b $c $d>](self) -> Self;
3117 }};
3118
3119 ($( $(#[$meta:meta])* [$a:ident $b:ident $c:ident $d:ident]),*) => {
3120 /// Only available for 4-lane vectors, this allows human-readable swizzle/permutations
3121 /// of the vector.
3122 pub trait Swizzle4: SwizzleVector<Lanes = typenum::U4> { $(impl_swizzle4!(DECL $(#[$meta])* $a $b $c $d);)* }
3123
3124 /// Implements 4-lane swizzling for vectors.
3125 impl<V: SwizzleVector<Lanes = typenum::U4>> Swizzle4 for V {
3126 $(impl_swizzle4!(IMPL $a $b $c $d);)*
3127 }
3128 }
3129}
3130
3131#[rustfmt::skip]
3132macro_rules! impl_swizzle3 {
3133 (IMPL x x x) => { #[inline(always)] fn xxx(self) -> Self { self.broadcast::<0>() } };
3134 (IMPL y y y) => { #[inline(always)] fn yyy(self) -> Self { self.broadcast::<1>() } };
3135 (IMPL z z z) => { #[inline(always)] fn zzz(self) -> Self { self.broadcast::<2>() } };
3136
3137 (IMPL $a:ident $b:ident $c:ident) => {paste::paste! {
3138 #[inline(always)]
3139 fn [<$a $b $c>](self) -> Self {
3140 struct Indices;
3141
3142 impl crate::swizzle::SwizzleIndices<typenum::U3> for Indices {
3143 const INDICES: GenericArray<u32, typenum::U3> = {
3144 unsafe { $crate::generic_array::const_transmute::<_, GenericArray<u32, typenum::U3>>([
3145 impl_swizzle4!(@ $a),
3146 impl_swizzle4!(@ $b),
3147 impl_swizzle4!(@ $c)
3148 ]) }
3149 };
3150 }
3151
3152 self.permute_const::<Indices>()
3153 }
3154 }};
3155
3156 (DECL $(#[$meta:meta])* $a:ident $b:ident $c:ident) => {paste::paste! {
3157 #[allow(missing_docs)]
3158 $(#[$meta])* fn [<$a $b $c>](self) -> Self;
3159 }};
3160
3161 ($( $(#[$meta:meta])* [$a:ident $b:ident $c:ident]),*) => {
3162 /// Only available for "3-lane" (ignoring 4th lane) [`LinAlg3Register`](crate::register::LinAlg3Register) vectors,
3163 /// this allows human-readable swizzle/permutations of the vector. Permutations
3164 /// will ignore the 4th lane of the register, leaving it unchanged.
3165 pub trait Swizzle3: SwizzleVector<Lanes = typenum::U3> { $(impl_swizzle3!(DECL $(#[$meta])* $a $b $c);)* }
3166
3167 /// Implements 3-lane swizzling for vectors support 3-lane linear algebra operations.
3168 impl<V: SwizzleVector<Lanes = typenum::U3>> Swizzle3 for V {
3169 $(impl_swizzle3!(IMPL $a $b $c);)*
3170 }
3171 }
3172}
3173
3174impl_swizzle3! {
3175 [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],
3176 [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],
3177 [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]
3178}
3179
3180impl_swizzle4! {
3181 [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],
3182 [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],
3183 [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],
3184 [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],
3185 [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],
3186 [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],
3187 [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],
3188 [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],
3189 [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],
3190 [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],
3191 [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],
3192 [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],
3193 [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],
3194 [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],
3195 [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],
3196 [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],
3197 [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],
3198 [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],
3199 [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],
3200 [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],
3201 [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],
3202 [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],
3203 [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],
3204 [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],
3205 [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],
3206 [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],
3207 [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],
3208 [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],
3209 [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],
3210 [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],
3211 [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],
3212 [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]
3213}
3214
3215/// Vector suitable for 3D linear algebra operations.
3216///
3217/// The length of this vector must be either 3 or 4 lanes.
3218///
3219/// Methods in this are specifically optimized to either ignore the fourth lane (if it exists),
3220/// or to use algorithms that map especially well when there are truly only three "lanes",
3221/// such as on GPUs.
3222pub trait LinAlg3Vector: FloatVector {
3223 /// Scalar Product using only the first three lanes of the register as a 3D vector.
3224 ///
3225 /// This is more efficient than a raw scalar product, as there is no need to
3226 /// zero out the last lane of the register.
3227 fn dot3(self, other: Self) -> Self::Element;
3228
3229 /// Cross Product using only the first three lanes of the register as a 3D vector.
3230 ///
3231 /// This is more efficient than a raw cross product, as there is no need to
3232 /// zero out the last lane of the register.
3233 ///
3234 /// The `DOP` generic parameter indicates whether to use the
3235 /// "Difference of Products" method for computing the cross product,
3236 /// which can be more accurate in some cases, but _requires_
3237 /// hardware fused multiply-add instructions to be efficient.
3238 ///
3239 /// If you want the best performance, set `DOP` to `false`.\
3240 /// If you want the best accuracy or have FMA support, set `DOP` to `true`.
3241 fn cross3<const DOP: bool>(self, other: Self) -> Self;
3242
3243 /// Refraction of incident vector `self` through a surface with normal `n`
3244 /// and relative index of refraction `eta` (`$\eta = \eta_i/\eta_t$`). `self` and `n` are
3245 /// assumed unit length.
3246 ///
3247 /// Total internal reflection (`1 - eta^2*(1 - dot(n,self)^2) < 0`) returns the
3248 /// zero vector; otherwise `eta*self - (eta*dot(n,self) + sqrt(k))*n`
3249 fn refract(self, n: Self, eta: Self::Element) -> Self;
3250
3251 /// Efficiently set the 4th (last) lane of the register to 0.0.
3252 ///
3253 /// Useful for sanitizing 3D Homogeneous vectors.
3254 ///
3255 /// See [`LinAlg3Vector::one4`] for similar functionality for 3D points.
3256 fn zero4(self) -> Self;
3257
3258 /// Efficiently set the 4th (last) lane of the register to 1.0.
3259 ///
3260 /// Useful for sanitizing 3D Homogeneous points.
3261 ///
3262 /// See [`LinAlg3Vector::zero4`] for similar functionality for 3D vectors.
3263 fn one4(self) -> Self;
3264
3265 /// Returns the minimum value in the first three lanes of the register.
3266 fn min_element3(self) -> Self::Element;
3267
3268 /// Returns the maximum value in the first three lanes of the register.
3269 fn max_element3(self) -> Self::Element;
3270
3271 /// Returns the sum of the first three elements of the register.
3272 fn sum_elements3(self) -> Self::Element;
3273
3274 /// Returns the product of the first three elements of the register.
3275 fn prod_elements3(self) -> Self::Element;
3276
3277 /// 3x3 Matrix Transpose.
3278 ///
3279 /// Only the first three lanes of each output column are meaningful; the 4th
3280 /// lane (on 4-lane vectors) is unspecified.
3281 fn mat3_transpose(m: &[Self; 3]) -> [Self; 3];
3282
3283 /// 3x3 Matrix-Vector multiplication, assuming `self` as the vector.
3284 fn mat3_vec3_product<const COLUMN_MAJOR: bool>(self, m: &[Self; 3]) -> Self;
3285
3286 /// 3x3 matrix times `N` 3D vectors (small-`N` batch; see
3287 /// [`mat4_vec4_product_array`](LinAlg4Vector::mat4_vec4_product_array)).
3288 fn mat3_vec3_product_array<const COLUMN_MAJOR: bool, const N: usize>(
3289 m: &[Self; 3],
3290 vectors: &[Self; N],
3291 ) -> [Self; N];
3292
3293 /// 3x3 Matrix-Matrix multiplication.
3294 ///
3295 /// If `COLUMN_MAJOR` is `false`, the matrices are treated as row-major and
3296 /// the multiplication order becomes `rhs * lhs`, mirroring
3297 /// [`mat4_product`](LinAlg4Vector::mat4_product).
3298 fn mat3_product<const COLUMN_MAJOR: bool>(lhs: &[Self; 3], rhs: &[Self; 3]) -> [Self; 3];
3299
3300 /// Determinant of a column-major 3x3 matrix.
3301 fn mat3_det(m: &[Self; 3]) -> Self::Element;
3302
3303 /// In-place 3x3 Matrix inversion; **returns the determinant**.
3304 ///
3305 /// An exactly-zero determinant leaves the matrix untouched; a near-zero
3306 /// (ill-conditioned) determinant gives a finite but unreliable result, so
3307 /// inspect the returned determinant before trusting the matrix.
3308 fn mat3_inverse_inplace(m: &mut [Self; 3]) -> Self::Element;
3309
3310 /// 3x3 Matrix inversion.
3311 ///
3312 /// Returns `Some(inverse)`, or `None` if the matrix is exactly singular.
3313 /// Consider [`mat3_inverse_inplace`](Self::mat3_inverse_inplace) (which hands
3314 /// back the determinant) to avoid the copy and to use a custom tolerance.
3315 #[inline(always)]
3316 fn mat3_inverse(m: &[Self; 3]) -> Option<[Self; 3]> {
3317 let mut mat = *m;
3318 if Self::mat3_inverse_inplace(&mut mat) == Self::Element::ZERO {
3319 None
3320 } else {
3321 Some(mat)
3322 }
3323 }
3324
3325 /// "Normal matrix" for transforming normals under non-uniform scale, from
3326 /// the cofactor cross-products of a column-major 3x3.
3327 ///
3328 /// `DIVIDE = true` gives the true inverse-transpose `$(M^{-1})^{T}$` (non-finite if
3329 /// singular); `DIVIDE = false` gives the un-divided cofactor matrix, which is
3330 /// cheaper, never singular, and points normals the same direction (use it
3331 /// when you re-normalize the result). Cheaper than a full inverse either way.
3332 fn mat3_normal<const DIVIDE: bool>(m: &[Self; 3]) -> [Self; 3];
3333}
3334
3335/// Vector suitable for 4D linear algebra operations.
3336///
3337/// Must have exactly 4 lanes.
3338pub trait LinAlg4Vector: LinAlg3Vector {
3339 /// Scalar Product using all four lanes of the register as a 4D vector.
3340 fn dot4(self, other: Self) -> Self::Element;
3341
3342 /// Quaternion multiplication.
3343 ///
3344 /// Method:
3345 /// ```text
3346 /// T1 = (lhs.w * rhs)
3347 /// T2 = (lhs.x * rhs.wzyx) * {+,-,+,-}
3348 /// T3 = (lhs.y * rhs.zwxy) * {+,+,-,-}
3349 /// T4 = (lhs.z * rhs.yxwz) * {-,+,+,-}
3350 /// T1 + T2 + T3 + T4
3351 /// ```
3352 fn quat4_product(self, other: Self) -> Self;
3353
3354 /// Quaternion-vector multiplication.
3355 ///
3356 /// This is optimized to work best on various SIMD architectures. On
3357 /// architectures with permute/shuffle instructions, it uses the
3358 /// Double-Cross (Giesen) method. On architectures without such instructions,
3359 /// it falls back to the standard method of two dot products
3360 /// and a single cross product. This is because cross products require
3361 /// several shuffles/permutations to compute efficiently with SIMD.
3362 ///
3363 /// The `DOP` generic parameter indicates whether to use the
3364 /// "Difference of Products" method for computing the cross product(s),
3365 /// which can be more accurate in some cases, but _requires_
3366 /// hardware fused multiply-add instructions to be efficient.
3367 ///
3368 /// If you want the best performance, set `DOP` to `false`.\
3369 /// If you want the best accuracy or have FMA support, set `DOP` to `true`.
3370 fn quat4_vec3_product<const DOP: bool>(self, vec: Self) -> Self;
3371
3372 /// Rotation matrix of a **unit** quaternion as 3 registers; the 4th lane of
3373 /// each is unspecified.
3374 ///
3375 /// `COLUMN_MAJOR` picks the storage: the rotation's columns when `true`, its
3376 /// rows when `false` (i.e. the transpose). The choice is free - only the
3377 /// compile-time sign masks differ.
3378 ///
3379 /// Trig-free - the entries are pairwise products of `{x, y, z, w}`, no
3380 /// `sin`/`cos`/`sqrt`. The quaternion is assumed normalized.
3381 ///
3382 /// To rotate many vectors by one quaternion, convert once here and batch via
3383 /// [`mat3_vec3_product`](LinAlg3Vector::mat3_vec3_product) with the matching
3384 /// `COLUMN_MAJOR` - cheaper than a per-vector
3385 /// [`quat4_vec3_product`](Self::quat4_vec3_product) for large `N`.
3386 fn quat_to_mat3<const COLUMN_MAJOR: bool>(self) -> [Self; 3];
3387
3388 /// Homogeneous 4x4 rotation matrix of a **unit** quaternion: the
3389 /// [`quat_to_mat3`](Self::quat_to_mat3) rotation with each rotation
3390 /// register's 4th lane zeroed and a `[0, 0, 0, 1]` 4th register.
3391 /// `COLUMN_MAJOR` is forwarded to `quat_to_mat3`.
3392 fn quat_to_mat4<const COLUMN_MAJOR: bool>(self) -> [Self; 4];
3393
3394 /// 4x4 Matrix Transpose.
3395 fn mat4_transpose(m: &[Self; 4]) -> [Self; 4];
3396
3397 /// 4x4 Matrix-Vector multiplication, assuming `self` as the vector.
3398 ///
3399 /// The `COLUMN_MAJOR` generic parameter indicates whether the matrix
3400 /// is stored in column-major order (`true`) or row-major order (`false`).
3401 ///
3402 /// If the matrix is **NOT** in column-major order, it will need to be
3403 /// transposed before the actual multiplication, which will incur a performance penalty.
3404 ///
3405 /// NOTE: If you want to multiply 4 vectors by the same **column-major** matrix, consider using
3406 /// [`LinAlg4Vector::mat4_product`] instead. It is conceptually the same as multiplying each
3407 /// vector individually, but can take advantage of SIMD optimizations better.
3408 fn mat4_vec4_product<const COLUMN_MAJOR: bool>(self, m: &[Self; 4]) -> Self;
3409
3410 /// 4x4 Matrix-Vector3 multiplication, optimized for the case where the vector is a 3D coordinate
3411 /// (i.e., the 4th lane is ignored).
3412 ///
3413 /// The `COLUMN_MAJOR` generic parameter indicates whether the matrix
3414 /// is stored in column-major order (`true`) or row-major order (`false`).
3415 ///
3416 /// If the matrix is **NOT** in column-major order, it will need to be
3417 /// transposed before the actual multiplication, which will incur a performance penalty.
3418 fn mat4_vec3_product<const COLUMN_MAJOR: bool>(self, m: &[Self; 4]) -> Self;
3419
3420 /// 4x4 matrix multiplied with `N` 3D vectors (small-`N` batch; see
3421 /// [`mat4_vec4_product_array`](Self::mat4_vec4_product_array)).
3422 fn mat4_vec3_product_array<const COLUMN_MAJOR: bool, const N: usize>(
3423 m: &[Self; 4],
3424 vectors: &[Self; N],
3425 ) -> [Self; N];
3426
3427 /// 4x4 Matrix-Point3 multiplication, optimized for the case where the input 3D value is a
3428 /// point of homogenous coordinates (i.e. 4th lane is 1.0).
3429 ///
3430 /// The `COLUMN_MAJOR` generic parameter indicates whether the matrix
3431 /// is stored in column-major order (`true`) or row-major order (`false`).
3432 ///
3433 /// If the matrix is **NOT** in column-major order, it will need to be
3434 /// transposed before the actual multiplication, which will incur a performance penalty.
3435 fn mat4_point3_product<const COLUMN_MAJOR: bool>(self, m: &[Self; 4]) -> Self;
3436
3437 /// 4x4 Matrix multiplied with `N` 3D points (small-`N` batch).
3438 fn mat4_point3_product_array<const COLUMN_MAJOR: bool, const N: usize>(
3439 m: &[Self; 4],
3440 points: &[Self; N],
3441 ) -> [Self; N];
3442
3443 /// 4x4 Matrix-Matrix multiplication.
3444 ///
3445 /// If `COLUMN_MAJOR` is `false`, the matrices are assumed to be in row-major order,
3446 /// and the order of the multiplication will become `rhs * lhs` to account for that.
3447 /// This is mathematically equivalent to transposing both matrices, performing
3448 /// the multiplication, and then transposing the result, but is obviously more efficient.
3449 ///
3450 /// NOTE: When operating in column-major mode (`COLUMN_MAJOR = true`), the multiplication
3451 /// is effectively:
3452 /// ```text
3453 /// C0 = mat4_vec4_product(lhs, R0)
3454 /// C1 = mat4_vec4_product(lhs, R1)
3455 /// C2 = mat4_vec4_product(lhs, R2)
3456 /// C3 = mat4_vec4_product(lhs, R3)
3457 /// ```
3458 ///
3459 /// and is therefore, in **column-major order**, useful for transforming 4 vectors
3460 /// by the same matrix, but capable of being optimized better than doing
3461 /// 4 individual matrix-vector multiplications.
3462 fn mat4_product<const COLUMN_MAJOR: bool>(lhs: &[Self; 4], rhs: &[Self; 4]) -> [Self; 4];
3463
3464 /// Transform `N` vectors by a single 4x4 matrix, returning the transformed array.
3465 ///
3466 /// Intended for **small** `N` (a handful of points): the array is taken and
3467 /// returned **by value** and the loop fully unrolls, so a large `N` will
3468 /// bloat code size and stack usage. For large or dynamic counts, loop
3469 /// [`mat4_vec4_product`](Self::mat4_vec4_product) over a slice instead.
3470 ///
3471 /// Row-major matrices are transposed once up front (amortized over `N`).
3472 /// Backends with a true double-width register transform two vectors per pass.
3473 fn mat4_vec4_product_array<const COLUMN_MAJOR: bool, const N: usize>(
3474 m: &[Self; 4],
3475 vectors: &[Self; N],
3476 ) -> [Self; N];
3477
3478 /// In-place 4x4 Matrix inversion; **returns the determinant**.
3479 ///
3480 /// An exactly-zero determinant leaves the matrix untouched; a near-zero
3481 /// (ill-conditioned) determinant gives a finite but unreliable result, so
3482 /// inspect the returned determinant before trusting the matrix.
3483 fn mat4_inverse_inplace(m: &mut [Self; 4]) -> Self::Element;
3484
3485 /// Compute the determinant of a 4x4 matrix without inverting it.
3486 fn mat4_det(m: &[Self; 4]) -> Self::Element;
3487
3488 /// 4x4 Matrix inversion.
3489 ///
3490 /// Returns `Some(inverted_matrix)`, or `None` if the matrix is exactly
3491 /// singular. Consider [`Vector::mat4_inverse_inplace`] (which hands back the
3492 /// determinant) to avoid the copy and to use a custom tolerance.
3493 #[inline(always)]
3494 fn mat4_inverse(m: &[Self; 4]) -> Option<[Self; 4]> {
3495 let mut mat = *m;
3496
3497 if crate::likely(Self::mat4_inverse_inplace(&mut mat) != Self::Element::ZERO) {
3498 Some(mat)
3499 } else {
3500 None
3501 }
3502 }
3503}