macro_rules! s {
($ty:ty: $a:expr, [$($idx:literal),* $(,)?]) => {{
#[inline(always)]
fn __do_permutev<R: Register>(a: Storage<R>) -> Storage<R> {
struct Indices<N: generic_array::ArrayLength>(core::marker::PhantomData<N>);
impl<N: generic_array::ArrayLength> SwizzleIndices<N> for Indices<N> {
const INDICES: generic_array::GenericArray<u32, N> = const {
let idxs = [$($idx),*];
assert!(N::USIZE == idxs.len(), "Swizzle mask must be the same length as the register");
unsafe { generic_array::const_transmute::<_, generic_array::GenericArray<u32, N>>(idxs) }
};
}
R::permutev_const::<Indices<R::Lanes>>(a)
}
__do_permutev::<$ty>($a)
}};
($ty:ty: $a:expr, $b:expr, [$($idx:literal),* $(,)?]) => {{
#[inline(always)]
fn __do_swizzle<R: Register>(a: Storage<R>, b: Storage<R>) -> Storage<R> {
struct Indices<N: generic_array::ArrayLength>(core::marker::PhantomData<N>);
impl<N: generic_array::ArrayLength> SwizzleIndices<N> for Indices<N> {
const INDICES: generic_array::GenericArray<u32, N> = const {
let idxs = [$($idx),*];
assert!(N::USIZE == idxs.len(), "Swizzle mask must be the same length as the register");
unsafe { generic_array::const_transmute::<_, generic_array::GenericArray<u32, N>>(idxs) }
};
}
R::swizzle_const::<Indices<R::Lanes>>(a, b)
}
__do_swizzle::<$ty>($a, $b)
}};
}
pub mod array;
pub mod linalg;
pub mod reduced;
pub mod well_formed;
use core::marker::PhantomData;
pub use crate::element::{Element, FloatElement, MaskElement};
pub use linalg::{LinAlg3Register, LinAlg4Register, ValidLinAlg3Length};
use generic_array::{
ArrayLength, GenericArray, IntoArrayLength,
typenum::{self, Unsigned},
};
use crate::{
divider::{BranchfreeDivider, Divider, vector::VectorDivider},
element::{FloatElementWithBits, IntegerElement, float::spec, float::spec::FloatSpec},
isa::InstructionSet,
math::policy::Policy,
vector::{NewConst, ops::MulAddExt},
};
pub use crate::backend::generic::polyfills::StreamGroup;
#[inline(always)]
pub(crate) const fn reg<R: Register, const N: usize>(values: [R::Element; N]) -> Storage<R>
where
typenum::Const<N>: IntoArrayLength<ArrayLength = R::Lanes>,
{
const {
assert!(
size_of::<Storage<R>>() == size_of::<[R::Element; N]>(),
"Size mismatch between register and array of elements"
);
}
unsafe { generic_array::const_transmute(values) }
}
#[inline(always)]
pub(crate) const fn reg_splat<R: Register>(value: R::Element) -> Storage<R> {
let mut dst = R::EMPTY;
unsafe {
let dst = &mut dst as *mut Storage<R> as *mut R::Element;
let mut i = 0;
while i < <R::Lanes as typenum::Unsigned>::USIZE {
dst.add(i).write(value);
i += 1;
}
}
dst
}
#[inline(always)]
pub(crate) const fn empty_reg<R>() -> Storage<R>
where
R: CoreRegister,
{
unsafe { core::mem::zeroed() }
}
#[inline(always)]
pub(crate) const fn alt_sign_reg<R: FloatRegister>(neg_on_even: bool) -> Storage<R> {
let neg = <R as FloatRegister>::NEG_ZERO;
let mut dst = R::EMPTY;
unsafe {
let dstp = &mut dst as *mut Storage<R> as *mut R::Element;
let negp = &neg as *const Storage<R> as *const R::Element;
let mut i = 0;
while i < <R::Lanes as typenum::Unsigned>::USIZE {
if (i % 2 == 0) == neg_on_even {
dstp.add(i).write(negp.add(i).read());
}
i += 1;
}
}
dst
}
pub trait MaskInteroperable<
A: CoreRegister<Lanes = Self::Lanes, Mask: CastMaskRegister<Self::Mask> + CastMaskRegister<B::Mask>>,
B: CoreRegister<Lanes = Self::Lanes, Mask: CastMaskRegister<Self::Mask> + CastMaskRegister<A::Mask>>,
>: CoreRegister<Mask: CastMaskRegister<A::Mask> + CastMaskRegister<B::Mask>>
{
}
impl<R, A, B> MaskInteroperable<A, B> for R
where
R: CoreRegister<Mask: CastMaskRegister<A::Mask> + CastMaskRegister<B::Mask>>,
A: CoreRegister<Lanes = R::Lanes, Mask: CastMaskRegister<Self::Mask> + CastMaskRegister<B::Mask>>,
B: CoreRegister<Lanes = R::Lanes, Mask: CastMaskRegister<Self::Mask> + CastMaskRegister<A::Mask>>,
{
}
pub trait FullyInteroperable<
A: Register<Lanes = Self::Lanes, Mask: CastMaskRegister<Self::Mask> + CastMaskRegister<B::Mask>>,
B: Register<Lanes = Self::Lanes, Mask: CastMaskRegister<Self::Mask> + CastMaskRegister<A::Mask>>,
>: Register<Mask: CastMaskRegister<A::Mask> + CastMaskRegister<B::Mask>>
+ BitCastRegister<Self>
+ BitCastRegister<A>
+ BitCastRegister<B>
+ CastRegister<Self>
+ CastRegister<A>
+ CastRegister<B>
where
A: BitCastRegister<Self> + CastRegister<Self>,
B: BitCastRegister<Self> + CastRegister<Self>
{}
impl<R, A, B> FullyInteroperable<A, B> for R
where
R: Register<Mask: CastMaskRegister<A::Mask> + CastMaskRegister<B::Mask>>
+ BitCastRegister<Self>
+ BitCastRegister<A>
+ BitCastRegister<B>
+ CastRegister<Self>
+ CastRegister<A>
+ CastRegister<B>,
A: Register<Lanes = R::Lanes, Mask: CastMaskRegister<Self::Mask> + CastMaskRegister<B::Mask>>,
B: Register<Lanes = R::Lanes, Mask: CastMaskRegister<Self::Mask> + CastMaskRegister<A::Mask>>,
A: BitCastRegister<R> + CastRegister<R>,
B: BitCastRegister<R> + CastRegister<R>,
{
}
type BitsPerWord = typenum::U32;
type RoundUpConst = typenum::U31;
pub type MaskWordCount<Lanes> = typenum::Quot<typenum::Sum<Lanes, RoundUpConst>, BitsPerWord>;
pub trait Lanes:
ArrayLength + core::ops::Shl<typenum::B1> + core::ops::Add<RoundUpConst> + core::ops::Shr<typenum::B1>
{
type BitmaskLength: ArrayLength;
#[cfg(feature = "bitvec")]
type BitmaskStorage: bitvec::view::BitViewSized<Store = u32>;
const IS_POWER_OF_TWO: bool;
}
#[cfg(feature = "bitvec")]
use bitvec::view::BitViewSized;
#[cfg(not(feature = "bitvec"))]
trait BitViewSized {
type Store;
}
#[cfg(not(feature = "bitvec"))]
impl<N: ArrayLength> BitViewSized for GenericArray<u32, N> {
type Store = u32;
}
impl<T> Lanes for T
where
T: ArrayLength + core::ops::Shl<typenum::B1> + core::ops::Add<RoundUpConst> + core::ops::Shr<typenum::B1>,
typenum::Sum<T, RoundUpConst>: core::ops::Div<BitsPerWord>,
MaskWordCount<T>: ArrayLength,
GenericArray<u32, MaskWordCount<T>>: BitViewSized<Store = u32>,
{
type BitmaskLength = MaskWordCount<T>;
#[cfg(feature = "bitvec")]
type BitmaskStorage = GenericArray<u32, Self::BitmaskLength>;
const IS_POWER_OF_TWO: bool = {
let lanes = <T as Unsigned>::USIZE;
lanes != 0 && (lanes & (lanes - 1)) == 0
};
}
pub type Storage<R> = <R as CoreRegister>::Storage;
pub trait ZeroUpper {
const N: usize;
}
pub(crate) struct OwnLanes<R: CoreRegister>(PhantomData<R>);
impl<R: CoreRegister> ZeroUpper for OwnLanes<R> {
const N: usize = <R::Lanes as Unsigned>::USIZE;
}
pub trait CoreRegister: 'static + Sized {
type NativeIsa: crate::simd::NativeIsa;
type Lanes: Lanes;
type Storage: Sized + Copy + core::fmt::Debug;
type Mask: MaskRegister<Lanes = Self::Lanes>;
#[inline(always)]
fn lanes() -> usize {
<Self::Lanes as Unsigned>::USIZE
}
const IS_EMULATED: bool;
const ISA: InstructionSet = <Self::NativeIsa as crate::simd::HasIsa>::ISA;
const HAS_EQUAL_SIZE_MASK: bool;
fn from_mask(mask: Storage<Self::Mask>) -> Storage<Self>;
fn blendv(mask: Storage<Self::Mask>, on_false: Storage<Self>, on_true: Storage<Self>) -> Storage<Self>;
#[inline(always)]
fn zz(mask: Storage<Self::Mask>, value: Storage<Self>) -> Storage<Self> {
Self::blendv(mask, Self::EMPTY, value)
}
#[inline(always)]
fn nz(mask: Storage<Self::Mask>, value: Storage<Self>) -> Storage<Self> {
if const { Self::HAS_EQUAL_SIZE_MASK } {
Self::zz(<Self::Mask as BitwiseRegister>::not(mask), value)
} else {
Self::blendv(mask, value, Self::EMPTY)
}
}
fn zeroupper_z<Z: ZeroUpper>(value: Storage<Self>) -> Storage<Self>;
#[inline(always)]
fn zeroupper(value: Storage<Self>) -> Storage<Self> {
Self::zeroupper_z::<OwnLanes<Self>>(value)
}
const EMPTY: Storage<Self>;
}
#[rustfmt::skip] #[thermite_macros::register_trait]
pub trait BitwiseRegister: CoreRegister {
#[conditional] fn bitxor(lhs: Storage<Self>, rhs: Storage<Self>) -> Storage<Self>;
#[conditional] fn bitand(lhs: Storage<Self>, rhs: Storage<Self>) -> Storage<Self>;
#[conditional] fn bitor(lhs: Storage<Self>, rhs: Storage<Self>) -> Storage<Self>;
#[conditional] fn not(value: Storage<Self>) -> Storage<Self>;
#[conditional] fn bitandnot(lhs: Storage<Self>, rhs: Storage<Self>) -> Storage<Self> {
Self::bitand(Self::not(lhs), rhs)
}
const HAS_NATIVE_TERNLOG: bool = false;
#[conditional] fn ternlog<const IMM: i32>(a: Storage<Self>, b: Storage<Self>, c: Storage<Self>) -> Storage<Self> {
let mut acc = Self::EMPTY;
if const { IMM == 0xCA } {
return Self::bitor(Self::bitand(a, b), Self::bitandnot(a, c));
}
macro_rules! case {
(0, $expr:expr) => { if const { (IMM & (1 << 0)) != 0 } { acc = $expr; } };
($bit:expr, $expr:expr) => { if const { (IMM & (1 << $bit)) != 0 } { acc = Self::bitor(acc, $expr); } };
}
case!(0, Self::bitandnot(a, Self::bitandnot(b, Self::not(c)))); case!(1, Self::bitandnot(a, Self::bitandnot(b, c))); case!(2, Self::bitandnot(a, Self::bitandnot(c, b))); case!(3, Self::bitandnot(a, Self::bitand(b, c))); case!(4, Self::bitandnot(c, Self::bitandnot(b, a))); case!(5, Self::bitand(a, Self::bitandnot(b, c))); case!(6, Self::bitand(a, Self::bitandnot(c, b))); case!(7, Self::bitand(a, Self::bitand(b, c)));
acc
}
#[conditional] fn bilog<const IMM: i32>(a: Storage<Self>, b: Storage<Self>) -> Storage<Self> {
let mut acc = Self::EMPTY;
if const { (IMM & (1 << 0)) != 0 } { acc = Self::not(Self::bitor(a, b)); } if const { (IMM & (1 << 1)) != 0 } { acc = Self::bitor(acc, Self::bitandnot(a, b)); } if const { (IMM & (1 << 2)) != 0 } { acc = Self::bitor(acc, Self::bitandnot(b, a)); } if const { (IMM & (1 << 3)) != 0 } { acc = Self::bitor(acc, Self::bitand(a, b)); }
acc
}
}
pub trait InterleaveRegister: CoreRegister {
fn interleave(a: Storage<Self>, b: Storage<Self>) -> (Storage<Self>, Storage<Self>);
fn deinterleave(a: Storage<Self>, b: Storage<Self>) -> (Storage<Self>, Storage<Self>);
}
#[inline(always)]
const fn lane_bitmask(lanes: usize) -> u64 {
if lanes >= 64 { u64::MAX } else { (1u64 << lanes) - 1 }
}
#[thermite_macros::register_trait]
pub trait MaskRegister: BitwiseRegister<Mask = Self> + CastMaskRegister<Self> + InterleaveRegister {
const TRUTHY: Storage<Self>;
const FALSY: Storage<Self>;
fn boolean(value: bool) -> Storage<Self> {
if value { Self::TRUTHY } else { Self::FALSY }
}
fn set(mask: Storage<Self>, lane: usize, value: bool) -> Storage<Self>;
fn test(mask: Storage<Self>, lane: usize) -> bool;
fn new_mask(value: GenericArray<bool, Self::Lanes>) -> Storage<Self> {
let mut result = Self::FALSY;
{
for i in 0..Self::lanes() {
if value[i] {
result = Self::set(result, i, true);
}
}
}
result
}
fn all(value: Storage<Self>) -> bool;
fn any(value: Storage<Self>) -> bool;
fn none(value: Storage<Self>) -> bool {
!Self::any(value)
}
fn native_bitmask(value: Storage<Self>) -> Option<u64>;
#[cfg(feature = "bitvec")]
fn fill_bitmask(value: Storage<Self>, view: &mut bitvec::slice::BitSlice<u32>);
#[cfg(feature = "bitvec")]
fn bitmask(value: Storage<Self>) -> bitvec::array::BitArray<<Self::Lanes as Lanes>::BitmaskStorage> {
let mut bitmask = bitvec::array::BitArray::ZERO;
if let Some(native) = Self::native_bitmask(value) {
let bits = unsafe { core::mem::transmute::<u64, [u32; 2]>(native) };
let bits = bitvec::slice::BitSlice::<u32>::from_slice(&bits);
bitmask[..<Self::Lanes as Unsigned>::USIZE].copy_from_bitslice(&bits[..<Self::Lanes as Unsigned>::USIZE]);
} else {
Self::fill_bitmask(value, &mut bitmask[..<Self::Lanes as Unsigned>::USIZE]);
}
bitmask
}
fn from_native_bitmask(bitmask: u64) -> Storage<Self> {
let lanes = <Self::Lanes as Unsigned>::USIZE.min(64);
let mut result = Self::FALSY;
let mut i = 0;
while i < lanes {
if (bitmask >> i) & 1 != 0 {
result = Self::set(result, i, true);
}
i += 1;
}
result
}
#[cfg(feature = "bitvec")]
fn from_bitmask(bits: &bitvec::slice::BitSlice<u32>) -> Storage<Self> {
let lanes = <Self::Lanes as Unsigned>::USIZE.min(bits.len());
if const { <Self::Lanes as Unsigned>::USIZE <= 64 } {
let mut bitmask = 0u64;
for i in bits[..lanes].iter_ones() {
bitmask |= 1 << i;
}
Self::from_native_bitmask(bitmask)
} else {
let mut result = Self::FALSY;
for i in bits[..lanes].iter_ones() {
result = Self::set(result, i, true);
}
result
}
}
fn first_set_one(value: Storage<Self>) -> Option<usize> {
let lanes = <Self::Lanes as Unsigned>::USIZE;
if let Some(bm) = Self::native_bitmask(value) {
let bm = bm & lane_bitmask(lanes);
(bm != 0).then(|| bm.trailing_zeros() as usize)
} else {
#[cfg(feature = "bitvec")]
{
Self::bitmask(value).first_one()
}
#[cfg(not(feature = "bitvec"))]
{
(0..lanes).find(|&i| Self::test(value, i))
}
}
}
fn last_set_one(value: Storage<Self>) -> Option<usize> {
let lanes = <Self::Lanes as Unsigned>::USIZE;
if let Some(bm) = Self::native_bitmask(value) {
let bm = bm & lane_bitmask(lanes);
(bm != 0).then(|| 63 - bm.leading_zeros() as usize)
} else {
#[cfg(feature = "bitvec")]
{
Self::bitmask(value).last_one()
}
#[cfg(not(feature = "bitvec"))]
{
(0..lanes).rev().find(|&i| Self::test(value, i))
}
}
}
fn count_set_one(value: Storage<Self>) -> usize {
let lanes = <Self::Lanes as Unsigned>::USIZE;
if let Some(bm) = Self::native_bitmask(value) {
(bm & lane_bitmask(lanes)).count_ones() as usize
} else {
#[cfg(feature = "bitvec")]
{
Self::bitmask(value).count_ones()
}
#[cfg(not(feature = "bitvec"))]
{
(0..lanes).filter(|&i| Self::test(value, i)).count()
}
}
}
fn first_set<const N: usize>(values: [Storage<Self>; N]) -> Option<usize> {
let lanes = <Self::Lanes as Unsigned>::USIZE;
let mut i = 0;
while i < N {
if let Some(idx) = Self::first_set_one(values[i]) {
return Some(i * lanes + idx);
}
i += 1;
}
None
}
fn last_set<const N: usize>(values: [Storage<Self>; N]) -> Option<usize> {
let lanes = <Self::Lanes as Unsigned>::USIZE;
let mut i = N;
while i > 0 {
i -= 1;
if let Some(idx) = Self::last_set_one(values[i]) {
return Some(i * lanes + idx);
}
}
None
}
fn count_set<const N: usize>(values: [Storage<Self>; N]) -> usize {
let mut total = 0;
let mut i = 0;
while i < N {
total += Self::count_set_one(values[i]);
i += 1;
}
total
}
}
pub trait NewRegister<E, N, S> {
type New<C: NewConst<E, N>>: crate::vector::VectorValue<C, S>;
}
#[rustfmt::skip] #[thermite_macros::register_trait]
pub trait Register:
BitwiseRegister + InterleaveRegister +
NewRegister<Self::Element, Self::Lanes, Storage<Self>> +
CastRegister<Self> + BitCastRegister<Self> + MaskInteroperable<Self::Signed, Self::Unsigned>
{
type Element: Element;
fn into_mask(value: Storage<Self>) -> Storage<Self::Mask>;
fn into_mask_unchecked(value: Storage<Self>) -> Storage<Self::Mask> {
Self::into_mask(value)
}
fn msb_to_mask(value: Storage<Self>) -> Storage<Self::Mask>;
type Unsigned: UnsignedIntegerRegister<
Signed = Self::Signed,
Unsigned = Self::Unsigned,
Lanes = Self::Lanes,
Element = <Self::Element as Element>::Unsigned,
> + CastRegister<Self::Signed>
+ BitCastRegister<Self::Signed>
+ MaskInteroperable<Self, Self::Signed>;
type Signed: SignedIntegerRegister<
Unsigned = Self::Unsigned,
Signed = Self::Signed,
Lanes = Self::Lanes,
Element = <Self::Element as Element>::Signed,
> + CastRegister<Self::Unsigned>
+ BitCastRegister<Self::Unsigned>
+ MaskInteroperable<Self, Self::Unsigned>;
#[masked]
fn new(value: GenericArray<Self::Element, Self::Lanes>) -> Storage<Self>;
fn single(value: Self::Element) -> Storage<Self>;
#[masked] fn splat(value: Self::Element) -> Storage<Self>;
#[conditional]
fn broadcast<const I: usize>(value: Storage<Self>) -> Storage<Self> {
Self::splat(Self::extract::<I>(value))
}
#[conditional]
fn broadcastv(value: Storage<Self>, idx: usize) -> Storage<Self> {
Self::splat(Self::as_slice(&value)[idx])
}
unsafe fn load(ptr: *const Self::Element) -> Storage<Self> {
const {
assert!(
size_of::<Storage<Self>>() == (size_of::<Self::Element>() * <Self::Lanes as Unsigned>::USIZE),
"Size mismatch between register storage and array of elements"
);
}
unsafe { core::ptr::read(ptr as *const Storage<Self>) }
}
unsafe fn load_m(src: Storage<Self>, mask: Storage<Self::Mask>, ptr: *const Self::Element) -> Storage<Self> {
unsafe {
let mut result = src;
let res = Self::as_mut_slice(&mut result);
for i in 0..Self::lanes() {
if !<Self::Mask as MaskRegister>::test(mask, i) {
continue;
}
res[i] = ptr.add(i).read();
}
result
}
}
unsafe fn load_z(mask: Storage<Self::Mask>, ptr: *const Self::Element) -> Storage<Self> {
unsafe { Self::load_m(Self::EMPTY, mask, ptr) }
}
unsafe fn load_unaligned(ptr: *const Self::Element) -> Storage<Self> {
const {
assert!(
size_of::<Storage<Self>>() == (size_of::<Self::Element>() * <Self::Lanes as Unsigned>::USIZE),
"Size mismatch between register storage and array of elements"
);
}
unsafe { core::ptr::read_unaligned(ptr as *const Storage<Self>) }
}
unsafe fn load_stream(ptr: *const Self::Element) -> Storage<Self> {
unsafe { Self::load(ptr) }
}
unsafe fn store(ptr: *mut Self::Element, value: Storage<Self>) {
const {
assert!(
size_of::<Storage<Self>>() == (size_of::<Self::Element>() * <Self::Lanes as Unsigned>::USIZE),
"Size mismatch between register storage and array of elements"
);
}
unsafe { core::ptr::write(ptr as *mut Storage<Self>, value) }
}
unsafe fn store_masked(ptr: *mut Self::Element, mask: Storage<Self::Mask>, value: Storage<Self>) {
unsafe {
let res = Self::as_slice(&value);
for i in 0..Self::lanes() {
if !<Self::Mask as MaskRegister>::test(mask, i) {
continue;
}
ptr.add(i).write(res[i]);
}
}
}
unsafe fn store_unaligned(ptr: *mut Self::Element, value: Storage<Self>) {
const {
assert!(
size_of::<Storage<Self>>() == (size_of::<Self::Element>() * <Self::Lanes as Unsigned>::USIZE),
"Size mismatch between register storage and array of elements"
);
}
unsafe { core::ptr::write_unaligned(ptr as *mut Storage<Self>, value) }
}
unsafe fn store_stream(ptr: *mut Self::Element, value: Storage<Self>) {
unsafe { Self::store(ptr, value) }
}
fn deinterleave_radix<const N: usize>(inputs: [Storage<Self>; N]) -> [Storage<Self>; N] {
crate::backend::generic::polyfills::deinterleave_radix_default::<Self, N>(inputs)
}
fn interleave_radix<const N: usize>(inputs: [Storage<Self>; N]) -> [Storage<Self>; N] {
crate::backend::generic::polyfills::interleave_radix_default::<Self, N>(inputs)
}
fn deinterleave_radix_by<const N: usize, const GROUP: usize>(inputs: [Storage<Self>; N]) -> [Storage<Self>; N] {
crate::backend::generic::polyfills::deinterleave_radix_by_default::<Self, N, GROUP>(inputs)
}
fn interleave_radix_by<const N: usize, const GROUP: usize>(inputs: [Storage<Self>; N]) -> [Storage<Self>; N] {
crate::backend::generic::polyfills::interleave_radix_by_default::<Self, N, GROUP>(inputs)
}
const HAS_STRUCTURAL_MEMOPS: bool = false;
unsafe fn load_deinterleaved<const N: usize>(ptr: *const Self::Element) -> [Storage<Self>; N] {
const { assert!(N >= 1) };
let lanes = Self::lanes();
let mut src = [Self::EMPTY; N];
for (i, s) in src.iter_mut().enumerate() {
*s = unsafe { Self::load_unaligned(ptr.add(i * lanes)) };
}
crate::backend::generic::polyfills::deinterleave_n::<Self, N>(src)
}
unsafe fn store_interleaved<const N: usize>(ptr: *mut Self::Element, values: [Storage<Self>; N]) {
const { assert!(N >= 1) };
let lanes = Self::lanes();
let out = crate::backend::generic::polyfills::interleave_n::<Self, N>(values);
for (i, o) in out.iter().enumerate() {
unsafe { Self::store_unaligned(ptr.add(i * lanes), *o) };
}
}
unsafe fn load_deinterleaved_arrays<const M: usize, const C: usize>(
ptr: *const Self::Element,
) -> [[Storage<Self>; C]; M] {
const { assert!(M >= 1 && C >= 1) };
let lanes = Self::lanes();
if const { Self::HAS_STRUCTURAL_MEMOPS && C <= 4 } {
let mut comp = [[Self::EMPTY; M]; C];
let mut k = 0;
while k < M {
let chunk = unsafe { Self::load_deinterleaved::<C>(ptr.add(k * C * lanes)) };
let mut c = 0;
while c < C {
comp[c][k] = chunk[c];
c += 1;
}
k += 1;
}
let mut out = [[Self::EMPTY; C]; M];
let mut c = 0;
while c < C {
let streams = crate::backend::generic::polyfills::deinterleave_n::<Self, M>(comp[c]);
let mut j = 0;
while j < M {
out[j][c] = streams[j];
j += 1;
}
c += 1;
}
out
} else {
let mut buf = [[Self::EMPTY; C]; M];
{
let flat = crate::backend::generic::polyfills::flat_arrays_mut(&mut buf);
let mut i = 0;
while i < M * C {
flat[i] = unsafe { Self::load_unaligned(ptr.add(i * lanes)) };
i += 1;
}
}
crate::backend::generic::polyfills::deinterleave_arrays::<Self, M, C>(buf)
}
}
unsafe fn store_interleaved_arrays<const M: usize, const C: usize>(
ptr: *mut Self::Element,
values: [[Storage<Self>; C]; M],
) {
const { assert!(M >= 1 && C >= 1) };
let lanes = Self::lanes();
if const { Self::HAS_STRUCTURAL_MEMOPS && C <= 4 } {
let mut comp = [[Self::EMPTY; M]; C];
let mut c = 0;
while c < C {
let mut streams = [Self::EMPTY; M];
let mut j = 0;
while j < M {
streams[j] = values[j][c];
j += 1;
}
comp[c] = crate::backend::generic::polyfills::interleave_n::<Self, M>(streams);
c += 1;
}
let mut k = 0;
while k < M {
let mut chunk = [Self::EMPTY; C];
let mut c = 0;
while c < C {
chunk[c] = comp[c][k];
c += 1;
}
unsafe { Self::store_interleaved::<C>(ptr.add(k * C * lanes), chunk) };
k += 1;
}
} else {
let out = crate::backend::generic::polyfills::interleave_arrays::<Self, M, C>(values);
let flat = crate::backend::generic::polyfills::flat_arrays(&out);
let mut i = 0;
while i < M * C {
unsafe { Self::store_unaligned(ptr.add(i * lanes), flat[i]) };
i += 1;
}
}
}
unsafe fn load_deinterleaved_grouped<const M: usize, const TAIL: usize>(
ptr: *const Self::Element,
) -> [StreamGroup<Storage<Self>, TAIL>; M] {
const { assert!(M >= 1) };
let mut out = [StreamGroup { head: Self::EMPTY, tail: [Self::EMPTY; TAIL] }; M];
macro_rules! reshape {
($c:literal) => {{
let records = unsafe { Self::load_deinterleaved_arrays::<M, $c>(ptr) };
let mut j = 0;
while j < M {
out[j].head = records[j][0];
let mut c = 0;
while c < TAIL {
out[j].tail[c] = records[j][1 + c];
c += 1;
}
j += 1;
}
}};
}
if const { TAIL == 0 } {
reshape!(1);
} else if const { TAIL == 1 } {
reshape!(2);
} else if const { TAIL == 2 } {
reshape!(3);
} else if const { TAIL == 3 } {
reshape!(4);
} else {
let lanes = Self::lanes();
let empty = StreamGroup { head: Self::EMPTY, tail: [Self::EMPTY; TAIL] };
let mut buf = [empty; M];
{
let flat = crate::backend::generic::polyfills::flat_groups_mut(&mut buf);
let mut i = 0;
while i < M * (TAIL + 1) {
flat[i] = unsafe { Self::load_unaligned(ptr.add(i * lanes)) };
i += 1;
}
}
return crate::backend::generic::polyfills::deinterleave_grouped::<Self, M, TAIL>(buf);
}
out
}
unsafe fn store_interleaved_grouped<const M: usize, const TAIL: usize>(
ptr: *mut Self::Element,
values: [StreamGroup<Storage<Self>, TAIL>; M],
) {
const { assert!(M >= 1) };
macro_rules! reshape {
($c:literal) => {{
let mut records = [[Self::EMPTY; $c]; M];
let mut j = 0;
while j < M {
records[j][0] = values[j].head;
let mut c = 0;
while c < TAIL {
records[j][1 + c] = values[j].tail[c];
c += 1;
}
j += 1;
}
unsafe { Self::store_interleaved_arrays::<M, $c>(ptr, records) };
}};
}
if const { TAIL == 0 } {
reshape!(1);
} else if const { TAIL == 1 } {
reshape!(2);
} else if const { TAIL == 2 } {
reshape!(3);
} else if const { TAIL == 3 } {
reshape!(4);
} else {
let lanes = Self::lanes();
let out = crate::backend::generic::polyfills::interleave_grouped::<Self, M, TAIL>(values);
let flat = crate::backend::generic::polyfills::flat_groups(&out);
let mut i = 0;
while i < M * (TAIL + 1) {
unsafe { Self::store_unaligned(ptr.add(i * lanes), flat[i]) };
i += 1;
}
}
}
unsafe fn lookup(values: &[Self::Element], indices: Storage<Self::Unsigned>) -> Storage<Self> {
let indices = <Self::Unsigned as Register>::as_slice(&indices);
let mut res = Self::EMPTY;
let resa = Self::as_mut_slice(&mut res);
for i in 0..Self::lanes() {
let idx: usize = indices[i].try_into().unwrap_or_else(#[cold] |_| panic!("Invalid index given for lookup"));
resa[i] = values[idx];
}
res
}
#[inline(always)]
fn as_slice(storage: &Storage<Self>) -> &[Self::Element] {
const {
assert!(
size_of::<Storage<Self>>() >= (size_of::<Self::Element>() * <Self::Lanes as Unsigned>::USIZE),
"Register storage is smaller than its lane count implies"
);
}
unsafe { core::slice::from_raw_parts(storage as *const Storage<Self> as *const Self::Element, Self::lanes()) }
}
#[inline(always)]
fn as_mut_slice(storage: &mut Storage<Self>) -> &mut [Self::Element] {
const {
assert!(
size_of::<Storage<Self>>() >= (size_of::<Self::Element>() * <Self::Lanes as Unsigned>::USIZE),
"Register storage is smaller than its lane count implies"
);
}
unsafe { core::slice::from_raw_parts_mut(storage as *mut Storage<Self> as *mut Self::Element, Self::lanes()) }
}
fn iter(storage: &Storage<Self>) -> core::slice::Iter<'_, Self::Element> {
Self::as_slice(storage).iter()
}
fn iter_mut(storage: &mut Storage<Self>) -> core::slice::IterMut<'_, Self::Element> {
Self::as_mut_slice(storage).iter_mut()
}
fn extract<const I: usize>(value: Storage<Self>) -> Self::Element {
const {
assert!(
I < <Self::Lanes as Unsigned>::USIZE,
"Index out of bounds for register lane extraction"
);
}
Self::as_slice(&value)[I]
}
fn insert<const I: usize>(mut value: Storage<Self>, element: Self::Element) -> Storage<Self> {
const {
assert!(
I < <Self::Lanes as Unsigned>::USIZE,
"Index out of bounds for register lane insertion"
);
}
Self::as_mut_slice(&mut value)[I] = element;
value
}
fn map<F>(mut value: Storage<Self>, mut f: F) -> Storage<Self>
where
F: FnMut(Self::Element) -> Self::Element,
{
for v in Self::as_mut_slice(&mut value) {
*v = f(*v);
}
value
}
fn zip<F>(mut lhs: Storage<Self>, rhs: Storage<Self>, f: F) -> Storage<Self>
where
F: Fn(Self::Element, Self::Element) -> Self::Element,
{
for (a, b) in Self::as_mut_slice(&mut lhs).iter_mut().zip(Self::as_slice(&rhs)) {
*a = f(*a, *b);
}
lhs
}
fn fold<F>(first: Self::Element, value: Storage<Self>, f: F) -> Self::Element
where
F: Fn(Self::Element, Self::Element) -> Self::Element,
{
Self::as_slice(&value).iter().fold(first, |acc, &v| f(acc, v))
}
fn reduce<F>(value: Storage<Self>, f: F) -> Self::Element
where
F: Fn(Self::Element, Self::Element) -> Self::Element,
{
Self::as_slice(&value)
.iter()
.skip(1)
.fold(Self::extract::<0>(value), |acc, &v| f(acc, v))
}
#[conditional]
fn reverse(mut value: Storage<Self>) -> Storage<Self> {
Self::as_mut_slice(&mut value).reverse();
value
}
#[conditional] fn swap_bytes(value: Storage<Self>) -> Storage<Self>;
fn compress(value: Storage<Self>, mask: Storage<Self::Mask>) -> Storage<Self> {
crate::backend::generic::polyfills::compress_default::<Self>(value, mask)
}
fn compress_z(value: Storage<Self>, mask: Storage<Self::Mask>) -> Storage<Self> {
crate::backend::generic::polyfills::compress_z_default::<Self>(value, mask)
}
fn compress_m(src: Storage<Self>, mask: Storage<Self::Mask>, value: Storage<Self>) -> Storage<Self> {
if const { <Self::Lanes as Unsigned>::USIZE > 64 } {
return crate::backend::generic::polyfills::compress_m_default::<Self>(src, mask, value);
}
let cnt = <Self::Mask as MaskRegister>::count_set_one(mask);
let bits = if cnt >= 64 { u64::MAX } else { (1u64 << cnt) - 1 };
let prefix = <Self::Mask as MaskRegister>::from_native_bitmask(bits);
Self::blendv(prefix, src, Self::compress(value, mask))
}
fn expand(value: Storage<Self>, mask: Storage<Self::Mask>) -> Storage<Self> {
crate::backend::generic::polyfills::expand_default::<Self>(value, mask)
}
fn expand_z(value: Storage<Self>, mask: Storage<Self::Mask>) -> Storage<Self> {
crate::backend::generic::polyfills::expand_z_default::<Self>(value, mask)
}
fn expand_m(src: Storage<Self>, mask: Storage<Self::Mask>, value: Storage<Self>) -> Storage<Self> {
Self::blendv(mask, src, Self::expand(value, mask))
}
const HAS_PERMUTEV: bool;
fn scalar_permutev(value: Storage<Self>, idxs: GenericArray<u32, Self::Lanes>) -> Storage<Self> {
let mut result = Self::EMPTY;
let value_array = Self::as_slice(&value);
let result_array = Self::as_mut_slice(&mut result);
let mask = Self::Lanes::U32 - 1;
for (&idx, dst) in idxs.iter().zip(result_array.iter_mut()) {
let idx = if const { is_power_of_2(Self::Lanes::U32) } {
idx & mask } else {
idx.min(mask) } as usize;
unsafe { core::hint::assert_unchecked(idx < value_array.len()) };
*dst = value_array[idx];
}
result
}
fn interleave_by<const GROUP: usize>(a: Storage<Self>, b: Storage<Self>) -> (Storage<Self>, Storage<Self>) {
crate::backend::generic::polyfills::interleave_by_default::<Self, GROUP>(a, b)
}
fn deinterleave_by<const GROUP: usize>(a: Storage<Self>, b: Storage<Self>) -> (Storage<Self>, Storage<Self>) {
crate::backend::generic::polyfills::deinterleave_by_default::<Self, GROUP>(a, b)
}
#[masked]
fn permutev(value: Storage<Self>, idxs: GenericArray<u32, Self::Lanes>) -> Storage<Self> {
Self::scalar_permutev(value, idxs)
}
fn permutev_const<I: SwizzleIndices<Self::Lanes>>(value: Storage<Self>) -> Storage<Self> {
Self::permutev(value, I::INDICES)
}
fn scalar_swizzle(a: Storage<Self>, b: Storage<Self>, idxs: GenericArray<u32, Self::Lanes>) -> Storage<Self> {
let mut result = Self::EMPTY;
let a_array = Self::as_slice(&a);
let b_array = Self::as_slice(&b);
let result_array = Self::as_mut_slice(&mut result);
let mask = (<Self::Lanes as Unsigned>::U32 << 1) - 1;
for (&idx, dst) in idxs.iter().zip(result_array.iter_mut()) {
let mut idx = if const { is_power_of_2(Self::Lanes::U32) } {
idx & mask } else {
idx.min(mask) } as usize;
*dst = if idx < Self::Lanes::USIZE {
unsafe { core::hint::assert_unchecked(idx < a_array.len()) };
a_array[idx]
} else {
idx -= Self::Lanes::USIZE;
unsafe { core::hint::assert_unchecked(idx < b_array.len()) };
b_array[idx]
};
}
result
}
#[masked]
fn swizzle(a: Storage<Self>, b: Storage<Self>, idxs: GenericArray<u32, Self::Lanes>) -> Storage<Self> {
use typenum::Unsigned;
if const { !Self::HAS_PERMUTEV } {
return Self::scalar_swizzle(a, b, idxs);
}
let mut a_idxs: GenericArray<u32, Self::Lanes> = GenericArray::default();
let mut b_idxs: GenericArray<u32, Self::Lanes> = GenericArray::default();
let mut blend_mask = <Self::Mask as MaskRegister>::FALSY;
for (i, &idx) in idxs.iter().enumerate() {
if idx < Self::Lanes::U32 {
a_idxs[i] = idx;
b_idxs[i] = i as u32;
} else {
a_idxs[i] = i as u32;
b_idxs[i] = idx - Self::Lanes::U32;
blend_mask = <Self::Mask as MaskRegister>::set(blend_mask, i, true);
}
}
let tmp_a = Self::permutev(a, a_idxs);
let tmp_b = Self::permutev(b, b_idxs);
Self::blendv(blend_mask, tmp_a, tmp_b)
}
fn swizzle_const<I: SwizzleIndices<Self::Lanes>>(a: Storage<Self>, b: Storage<Self>) -> Storage<Self> {
Self::swizzle(a, b, I::INDICES)
}
const HAS_NATIVE_ALIGN: bool = false;
fn align<const OFFSET: usize>(a: Storage<Self>, b: Storage<Self>) -> Storage<Self> {
Self::swizzle_const::<crate::swizzle::AlignIndices<OFFSET, Self::Lanes>>(a, b)
}
#[inline(always)]
fn array_permutev<const N: usize>(value: [Storage<Self>; N], idxs: &[u32]) -> [Storage<Self>; N] {
let l = <Self::Lanes as Unsigned>::USIZE;
let total = N * l;
let mut result = [Self::EMPTY; N];
for i in 0..N {
let base = i * l;
let mut local: GenericArray<u32, Self::Lanes> = GenericArray::default();
let mut chunk_ids: GenericArray<<Self::Unsigned as Register>::Element, Self::Lanes> = GenericArray::default();
for lane in 0..l {
let g = idxs[base + lane] as usize;
let g = if const { (N * <Self::Lanes as Unsigned>::USIZE).is_power_of_two() } {
g & (total - 1)
} else {
g.min(total - 1)
};
local[lane] = (g % l) as u32;
chunk_ids[lane] = Element::from_u16((g / l) as u16);
}
let chunk_reg = Self::Unsigned::new(chunk_ids);
let mut out = Self::EMPTY;
for j in 0..N {
let j_splat = Self::Unsigned::splat(Element::from_u16(j as u16));
let eq = Self::Unsigned::eq(chunk_reg, j_splat);
let blend = <Self::Mask as CastMaskRegister<<Self::Unsigned as CoreRegister>::Mask>>::mask_from(eq);
let permuted = Self::permutev(value[j], local.clone());
out = Self::blendv(blend, out, permuted);
}
result[i] = out;
}
result
}
#[inline(always)]
fn array_swizzle<const N: usize>(a: [Storage<Self>; N], b: [Storage<Self>; N], idxs: &[u32]) -> [Storage<Self>; N] {
let l = <Self::Lanes as Unsigned>::USIZE;
let total = N * l;
let span = 2 * total;
let mut result = [Self::EMPTY; N];
for i in 0..N {
let base = i * l;
let mut local: GenericArray<u32, Self::Lanes> = GenericArray::default();
let mut chunk_ids: GenericArray<<Self::Unsigned as Register>::Element, Self::Lanes> = GenericArray::default();
for lane in 0..l {
let g = idxs[base + lane] as usize;
let g = if const { (2 * N * <Self::Lanes as Unsigned>::USIZE).is_power_of_two() } {
g & (span - 1)
} else {
g.min(span - 1)
};
local[lane] = (g % l) as u32;
chunk_ids[lane] = Element::from_u16((g / l) as u16);
}
let chunk_reg = Self::Unsigned::new(chunk_ids);
let mut out = Self::EMPTY;
for j in 0..(2 * N) {
let src = if j < N { a[j] } else { b[j - N] };
let j_splat = Self::Unsigned::splat(Element::from_u16(j as u16));
let eq = Self::Unsigned::eq(chunk_reg, j_splat);
let blend = <Self::Mask as CastMaskRegister<<Self::Unsigned as CoreRegister>::Mask>>::mask_from(eq);
let permuted = Self::permutev(src, local.clone());
out = Self::blendv(blend, out, permuted);
}
result[i] = out;
}
result
}
}
const fn is_power_of_2(n: u32) -> bool {
(n & (n - 1)) == 0
}
pub trait SwizzleIndices<N: ArrayLength> {
const INDICES: GenericArray<u32, N>;
}
pub trait ConcatRegister<HALF: CoreRegister>: ExtendRegister<HALF> {
fn concat(lo: Storage<HALF>, hi: Storage<HALF>) -> Storage<Self>;
fn split(value: Storage<Self>) -> (Storage<HALF>, Storage<HALF>);
}
pub trait SplitRegister<WIDE: ConcatRegister<Self>>: CoreRegister {}
impl<HALF: CoreRegister, WIDE: CoreRegister> SplitRegister<WIDE> for HALF where WIDE: ConcatRegister<HALF> {}
pub trait ExtendRegister<FROM: CoreRegister>: CoreRegister {
fn extend(value: Storage<FROM>) -> Storage<Self>;
fn narrow(value: Storage<Self>) -> Storage<FROM>;
}
pub trait NarrowRegister<TO: ExtendRegister<Self>>: CoreRegister {}
impl<FROM: CoreRegister, TO: CoreRegister> NarrowRegister<TO> for FROM where TO: ExtendRegister<FROM> {}
pub trait WideRegister: Register
where
typenum::Double<Self::Lanes>: Lanes,
{
type Wide: ConcatRegister<Self> + Register<Element = Self::Element, Lanes = typenum::Double<Self::Lanes>>;
}
pub trait IndexableRegister<IDX: UnsignedIntegerRegister<Lanes = Self::Lanes>>: Register {
#[inline(always)]
unsafe fn gather(ptr: *const Self::Element, indices: Storage<IDX>) -> Storage<Self> {
unsafe {
let mut result = Self::EMPTY;
let res = Self::as_mut_slice(&mut result);
let indices = IDX::as_slice(&indices);
for i in 0..Self::lanes() {
res[i] = ptr.add(indices[i].try_into().unwrap_unchecked()).read();
}
result
}
}
#[inline(always)]
unsafe fn gather_m(
src: Storage<Self>,
mask: Storage<Self::Mask>,
ptr: *const Self::Element,
indices: Storage<IDX>,
) -> Storage<Self> {
unsafe {
let mut result = src;
let res = Self::as_mut_slice(&mut result);
let indices = IDX::as_slice(&indices);
for i in 0..Self::lanes() {
if !<Self::Mask as MaskRegister>::test(mask, i) {
continue;
}
res[i] = ptr.add(indices[i].try_into().unwrap_unchecked()).read();
}
result
}
}
#[inline(always)]
unsafe fn gather_z(mask: Storage<Self::Mask>, ptr: *const Self::Element, indices: Storage<IDX>) -> Storage<Self> {
unsafe { Self::gather_m(Self::EMPTY, mask, ptr, indices) }
}
#[inline(always)]
unsafe fn scatter(value: Storage<Self>, ptr: *mut Self::Element, indices: Storage<IDX>) {
unsafe {
let value = Self::as_slice(&value);
let indices = IDX::as_slice(&indices);
for i in 0..Self::lanes() {
ptr.add(indices[i].try_into().unwrap_unchecked()).write(value[i]);
}
}
}
#[inline(always)]
unsafe fn scatter_m(
value: Storage<Self>,
mask: Storage<Self::Mask>,
ptr: *mut Self::Element,
indices: Storage<IDX>,
) {
unsafe {
let value = Self::as_slice(&value);
let indices = IDX::as_slice(&indices);
for i in 0..Self::lanes() {
if !<Self::Mask as MaskRegister>::test(mask, i) {
continue;
}
ptr.add(indices[i].try_into().unwrap_unchecked()).write(value[i]);
}
}
}
}
pub trait WidenIndexRegister: Register {
fn widen_indices(idxs: &GenericArray<u8, generic_array::typenum::U8>) -> GenericArray<u32, Self::Lanes>;
#[inline(always)]
fn permutev_row(value: Storage<Self>, row: &GenericArray<u8, generic_array::typenum::U8>) -> Storage<Self> {
Self::permutev(value, Self::widen_indices(row))
}
}
pub trait ShuffleRegister: Register {
fn shuffle<const IMM8: i32>(lhs: Storage<Self>, rhs: Storage<Self>) -> Storage<Self>;
}
pub trait PermuteRegister: Register {
fn permute<const IMM8: i32>(value: Storage<Self>) -> Storage<Self>;
}
pub trait BlendRegister: Register {
fn blend<const IMM8: i32>(lhs: Storage<Self>, rhs: Storage<Self>) -> Storage<Self>;
}
#[rustfmt::skip]
#[thermite_macros::register_trait]
pub trait BitshiftRegister: Register<Element: IntegerElement> {
#[conditional] fn shr(value: Storage<Self>, shift: u32) -> Storage<Self>;
#[conditional] fn shl(value: Storage<Self>, shift: u32) -> Storage<Self>;
#[conditional] fn shli<const IMM8: i32>(value: Storage<Self>) -> Storage<Self> { Self::shl(value, IMM8 as u32) }
#[conditional] fn shri<const IMM8: i32>(value: Storage<Self>) -> Storage<Self> { Self::shr(value, IMM8 as u32) }
const HAS_WIDE_BYTE_SHIFTS: bool;
#[conditional] fn bshli<const IMM8: i32>(mut value: Storage<Self>) -> Storage<Self> {
let arr = Self::as_mut_slice(&mut value);
let lane_width = core::mem::size_of::<Self::Element>() * 8;
let lanes = <Self::Lanes as Unsigned>::USIZE;
let skip = (8 * IMM8 as usize) / lane_width;
let shift = (8 * IMM8 as u16) % lane_width as u16;
if skip >= lanes {
return Self::EMPTY;
}
if shift == 0 {
for i in (skip..lanes).rev() {
arr[i] = arr[i - skip];
}
for i in 0..skip {
arr[i] = Self::Element::ZERO;
}
return value;
}
let inv_shift = lane_width as u16 - shift;
let shift: Self::Element = Element::from_u16(shift);
let inv_shift: Self::Element = Element::from_u16(inv_shift);
for i in (skip + 1..lanes).rev() {
arr[i] = (arr[i - skip] << shift) | (arr[i - skip - 1] >> inv_shift);
}
arr[skip] = arr[0] << shift;
if skip > 0 {
for i in 0..skip {
arr[i] = Self::Element::ZERO;
}
}
value
}
#[conditional] fn bshri<const IMM8: i32>(mut value: Storage<Self>) -> Storage<Self> {
let arr = Self::as_mut_slice(&mut value);
let lane_width = core::mem::size_of::<Self::Element>() * 8;
let lanes = <Self::Lanes as Unsigned>::USIZE;
let skip = (8 * IMM8 as usize) / lane_width;
let shift = (8 * IMM8 as u16) % lane_width as u16;
if skip >= lanes {
return Self::EMPTY;
}
if shift == 0 {
for i in 0..(lanes - skip) {
arr[i] = arr[i + skip];
}
for i in (lanes - skip)..lanes {
arr[i] = Self::Element::ZERO;
}
return value;
}
let inv_shift = lane_width as u16 - shift;
let shift: Self::Element = Element::from_u16(shift);
let inv_shift: Self::Element = Element::from_u16(inv_shift);
for i in 0..(lanes - skip - 1) {
arr[i] = (arr[i + skip] >> shift) | (arr[i + skip + 1] << inv_shift);
}
arr[lanes - skip - 1] = arr[lanes - 1] >> shift;
if skip > 0 {
for i in (lanes - skip)..lanes {
arr[i] = Self::Element::ZERO;
}
}
value
}
const HAS_TRUE_SHIFTV: bool;
#[conditional] fn shrv(mut value: Storage<Self>, shifts: Storage<Self::Unsigned>) -> Storage<Self> {
for (r, s) in Self::as_mut_slice(&mut value)
.iter_mut()
.zip(<Self::Unsigned as Register>::as_slice(&shifts))
{
*r = r.logical_shr(*s);
}
value
}
#[conditional] fn shlv(mut value: Storage<Self>, shifts: Storage<Self::Unsigned>) -> Storage<Self> {
for (r, s) in Self::as_mut_slice(&mut value)
.iter_mut()
.zip(<Self::Unsigned as Register>::as_slice(&shifts))
{
*r = *r << *s;
}
value
}
#[conditional] fn rol(value: Storage<Self>, shift: u32) -> Storage<Self> {
let width = (core::mem::size_of::<Self::Element>() * 8) as u32;
let shift = shift & (width - 1); Self::bitor(Self::shl(value, shift), Self::shr(value, width - shift))
}
#[conditional] fn ror(value: Storage<Self>, shift: u32) -> Storage<Self> {
let width = (core::mem::size_of::<Self::Element>() * 8) as u32;
let shift = shift & (width - 1);
Self::bitor(Self::shr(value, shift), Self::shl(value, width - shift))
}
#[conditional] fn roli<const IMM8: i32>(value: Storage<Self>) -> Storage<Self> {
Self::rol(value, IMM8 as u32)
}
#[conditional] fn rori<const IMM8: i32>(value: Storage<Self>) -> Storage<Self> {
Self::ror(value, IMM8 as u32)
}
#[masked]
fn rolv(value: Storage<Self>, shifts: Storage<Self::Unsigned>) -> Storage<Self> {
let width = (size_of::<Self::Element>() * 8) as u16;
let width_vec = Self::Unsigned::splat(Element::from_u16(width));
Self::bitor(
Self::shlv(value, shifts),
Self::shrv(value, Self::Unsigned::sub(width_vec, shifts)),
)
}
fn rolv_c(mask: Storage<Self::Mask>, value: Storage<Self>, shifts: Storage<Self::Unsigned>) -> Storage<Self> {
let mask = <<Self::Unsigned as CoreRegister>::Mask as CastMaskRegister<Self::Mask>>::mask_from(mask);
Self::rolv(value, <Self::Unsigned as CoreRegister>::zz(mask, shifts))
}
#[masked]
fn rorv(value: Storage<Self>, shifts: Storage<Self::Unsigned>) -> Storage<Self> {
let width = (size_of::<Self::Element>() * 8) as u16;
let width_vec = Self::Unsigned::splat(Element::from_u16(width));
Self::bitor(
Self::shrv(value, shifts),
Self::shlv(value, Self::Unsigned::sub(width_vec, shifts)),
)
}
fn rorv_c(mask: Storage<Self::Mask>, value: Storage<Self>, shifts: Storage<Self::Unsigned>) -> Storage<Self> {
let mask = <<Self::Unsigned as CoreRegister>::Mask as CastMaskRegister<Self::Mask>>::mask_from(mask);
Self::rorv(value, <Self::Unsigned as CoreRegister>::zz(mask, shifts))
}
#[conditional] fn reverse_bits(mut value: Storage<Self>) -> Storage<Self> {
value = Self::swap_bytes(value);
let mut s = size_of::<Self::Element>() as u32 * 4; let mut mask = Self::not(Self::EMPTY);
while s >= 8 {
mask = Self::bitxor(mask, Self::shl(mask, s));
s >>= 1;
}
while s != 0 {
mask = Self::bitxor(mask, Self::shl(mask, s));
let a = mask;
let b = Self::shr(value, s);
let c = Self::shl(value, s);
value = Self::ternlog::<{ crate::ternlog_imm!((A & B) | (!A & C)) }>(a, b, c);
s >>= 1;
}
value
}
}
pub trait CastRegister<FROM: CoreRegister>: CoreRegister {
#[inline(always)]
fn cast_from(value: Storage<FROM>) -> Storage<Self> {
Self::saturating_cast_from(value)
}
#[inline(always)]
fn saturating_cast_from(value: Storage<FROM>) -> Storage<Self> {
Self::cast_from(value)
}
#[inline(always)]
fn fast_cast_from(value: Storage<FROM>) -> Storage<Self> {
Self::cast_from(value)
}
}
pub trait BitCastRegister<FROM: CoreRegister>: CoreRegister {
fn from_bits(value: Storage<FROM>) -> Storage<Self>;
}
pub trait CastMaskRegister<FROM: CoreRegister>: CoreRegister {
fn mask_from(value: Storage<FROM>) -> Storage<Self>;
}
#[rustfmt::skip]
#[thermite_macros::register_trait]
pub trait PartialOrdRegister: Register {
fn eq(lhs: Storage<Self>, rhs: Storage<Self>) -> Storage<Self::Mask>;
fn gt(lhs: Storage<Self>, rhs: Storage<Self>) -> Storage<Self::Mask>;
fn ge(lhs: Storage<Self>, rhs: Storage<Self>) -> Storage<Self::Mask> {
let gt = Self::gt(lhs, rhs);
let eq = Self::eq(lhs, rhs);
Self::Mask::bitor(gt, eq)
}
fn lt(lhs: Storage<Self>, rhs: Storage<Self>) -> Storage<Self::Mask> { Self::gt(rhs, lhs) }
fn le(lhs: Storage<Self>, rhs: Storage<Self>) -> Storage<Self::Mask> { Self::ge(rhs, lhs) }
fn ne(lhs: Storage<Self>, rhs: Storage<Self>) -> Storage<Self::Mask> { Self::Mask::not(Self::eq(lhs, rhs)) }
}
#[rustfmt::skip]
#[thermite_macros::register_trait]
pub trait NumericRegister:
PartialOrdRegister<Signed: CastRegister<Self>, Unsigned: CastRegister<Self>, Element: num_traits::NumOps>
+ CastRegister<Self::Signed>
+ CastRegister<Self::Unsigned>
{
const ZERO: Storage<Self>;
const ONE: Storage<Self>;
const TWO: Storage<Self>;
const MIN: Storage<Self>;
const MAX: Storage<Self>;
fn is_all_zero(value: Storage<Self>) -> bool {
<Self::Mask as MaskRegister>::all(Self::eq(value, Self::ZERO))
}
#[conditional] fn add(lhs: Storage<Self>, rhs: Storage<Self>) -> Storage<Self>;
#[conditional] fn sub(lhs: Storage<Self>, rhs: Storage<Self>) -> Storage<Self>;
#[conditional] fn mul(lhs: Storage<Self>, rhs: Storage<Self>) -> Storage<Self>;
#[conditional] fn div(lhs: Storage<Self>, rhs: Storage<Self>) -> Storage<Self>;
#[conditional] fn rem(lhs: Storage<Self>, rhs: Storage<Self>) -> Storage<Self>;
#[conditional] fn square(lhs: Storage<Self>) -> Storage<Self> {
Self::mul(lhs, lhs)
}
#[conditional] fn scale(value: Storage<Self>, scalar: Self::Element) -> Storage<Self> {
Self::mul(value, Self::splat(scalar))
}
#[conditional] fn min(lhs: Storage<Self>, rhs: Storage<Self>) -> Storage<Self>;
#[conditional] fn max(lhs: Storage<Self>, rhs: Storage<Self>) -> Storage<Self>;
fn arg_minmax(value: Storage<Self>) -> (usize, usize) {
let (min_val, max_val) = Self::min_max_element(value);
let min = Self::splat(min_val);
let max = Self::splat(max_val);
let min = Self::eq(min, value);
let max = Self::eq(max, value);
let min = match <Self::Mask as MaskRegister>::native_bitmask(min) {
Some(mask) => mask.trailing_zeros() as usize,
#[cfg(feature = "bitvec")]
None => <Self::Mask as MaskRegister>::bitmask(min).trailing_zeros(),
#[cfg(not(feature = "bitvec"))]
None => unreachable!(),
};
let max = match <Self::Mask as MaskRegister>::native_bitmask(max) {
Some(mask) => mask.trailing_zeros() as usize,
#[cfg(feature = "bitvec")]
None => <Self::Mask as MaskRegister>::bitmask(max).trailing_zeros(),
#[cfg(not(feature = "bitvec"))]
None => unreachable!(),
};
(min, max)
}
fn sort_by<O: crate::sort::SortOrder>(value: Storage<Self>) -> Storage<Self> {
crate::backend::generic::polyfills::sort::sort_lanes::<Self, O>(value)
}
fn bitonic_clean_by<O: crate::sort::SortOrder>(value: Storage<Self>) -> Storage<Self> {
crate::backend::generic::polyfills::sort::bitonic_clean_lanes::<Self, O>(value)
}
#[inline(always)]
fn sort(value: Storage<Self>) -> Storage<Self> {
Self::sort_by::<crate::sort::Ascending>(value)
}
#[inline(always)]
fn bitonic_clean(value: Storage<Self>) -> Storage<Self> {
Self::bitonic_clean_by::<crate::sort::Ascending>(value)
}
fn min_element(value: Storage<Self>) -> Self::Element;
fn max_element(value: Storage<Self>) -> Self::Element;
#[inline(always)]
fn min_max_element(value: Storage<Self>) -> (Self::Element, Self::Element) {
(Self::min_element(value), Self::max_element(value))
}
fn sum_elements(value: Storage<Self>) -> Self::Element;
fn prod_elements(value: Storage<Self>) -> Self::Element;
fn prefix_sum(value: Storage<Self>) -> Storage<Self> {
crate::backend::generic::polyfills::scan::prefix_sum::<Self>(value)
}
fn prefix_min(value: Storage<Self>) -> Storage<Self> {
crate::backend::generic::polyfills::scan::prefix_min::<Self>(value)
}
fn prefix_max(value: Storage<Self>) -> Storage<Self> {
crate::backend::generic::polyfills::scan::prefix_max::<Self>(value)
}
fn reverse_prefix_sum(value: Storage<Self>) -> Storage<Self> {
crate::backend::generic::polyfills::scan::reverse_prefix_sum::<Self>(value)
}
fn reverse_prefix_min(value: Storage<Self>) -> Storage<Self> {
crate::backend::generic::polyfills::scan::reverse_prefix_min::<Self>(value)
}
fn reverse_prefix_max(value: Storage<Self>) -> Storage<Self> {
crate::backend::generic::polyfills::scan::reverse_prefix_max::<Self>(value)
}
fn pairwise_sum(lo: Storage<Self>, hi: Storage<Self>) -> Storage<Self> {
let half = const { <Self::Lanes as Unsigned>::USIZE / 2 };
let lo = Self::as_slice(&lo);
let hi = Self::as_slice(&hi);
let mut result = Self::EMPTY;
let out = Self::as_mut_slice(&mut result);
for i in 0..half {
out[i] = lo[2 * i] + lo[2 * i + 1];
out[i + half] = hi[2 * i] + hi[2 * i + 1];
}
result
}
fn relaxed_pairwise_sum(lo: Storage<Self>, hi: Storage<Self>) -> Storage<Self> {
Self::pairwise_sum(lo, hi)
}
fn offset() -> Storage<Self>;
fn indexed() -> Storage<Self>;
}
#[rustfmt::skip] #[thermite_macros::register_trait]
pub trait SignedRegister: NumericRegister<Element: num_traits::Signed> {
#[conditional] fn neg(value: Storage<Self>) -> Storage<Self>;
#[conditional] fn abs(value: Storage<Self>) -> Storage<Self>;
fn signum(value: Storage<Self>) -> Storage<Self> {
let is_neg = Self::is_negative(value);
let is_zero = Self::eq(value, Self::ZERO);
let sign = Self::blendv(is_neg, Self::ONE, Self::NEG_ONE);
if const { Self::HAS_EQUAL_SIZE_MASK } {
let is_zero = Self::from_mask(is_zero);
Self::bitandnot(is_zero, sign)
} else {
Self::blendv(is_zero, sign, Self::ZERO)
}
}
#[conditional]
fn copysign(lhs: Storage<Self>, rhs: Storage<Self>) -> Storage<Self> {
let abs = Self::abs(lhs);
Self::blendv(Self::is_negative(rhs), abs, Self::neg(abs))
}
const NEG_ONE: Storage<Self>;
const MIN_POSITIVE: Storage<Self>;
fn is_negative(value: Storage<Self>) -> Storage<Self::Mask> {
Self::lt(value, Self::ZERO)
}
fn is_positive(value: Storage<Self>) -> Storage<Self::Mask> {
Self::ge(value, Self::ZERO)
}
fn select_negative(value: Storage<Self>, on_neg: Storage<Self>, on_pos: Storage<Self>) -> Storage<Self> {
Self::blendv(Self::msb_to_mask(value), on_pos, on_neg)
}
}
use num_traits::{WrappingAdd, WrappingMul};
#[rustfmt::skip] #[thermite_macros::register_trait]
pub trait IntegerRegister: NumericRegister<Element: IntegerElement> + BitshiftRegister {
fn count_conflicts(value: Storage<Self>) -> Storage<Self> {
crate::backend::generic::polyfills::conflict::count_conflicts_default::<Self>(value)
}
#[conditional] fn mulhi(lhs: Storage<Self>, rhs: Storage<Self>) -> Storage<Self>;
#[conditional] fn mullo(lhs: Storage<Self>, rhs: Storage<Self>) -> Storage<Self>;
#[conditional] fn saturating_add(lhs: Storage<Self>, rhs: Storage<Self>) -> Storage<Self>;
#[conditional] fn saturating_sub(lhs: Storage<Self>, rhs: Storage<Self>) -> Storage<Self>;
fn wrapping_sum(value: Storage<Self>) -> Self::Element {
Self::reduce(value, |a, b| a.wrapping_add(&b))
}
fn wrapping_product(value: Storage<Self>) -> Self::Element {
Self::reduce(value, |a, b| a.wrapping_mul(&b))
}
#[conditional] fn div_branched(value: Storage<Self>, divider: Divider<Self::Element>) -> Storage<Self>;
#[conditional] fn div_branchfree(value: Storage<Self>, divider: BranchfreeDivider<Self::Element>) -> Storage<Self>;
#[conditional] fn divv_branchfree(value: Storage<Self>, dividers: VectorDivider<Self>) -> Storage<Self>;
const HAS_HARDWARE_POPCNT: bool;
#[conditional] fn count_ones(value: Storage<Self>) -> Storage<Self>;
#[conditional] fn count_zeros(value: Storage<Self>) -> Storage<Self> {
Self::count_ones(Self::not(value))
}
#[conditional] fn leading_zeros(value: Storage<Self>) -> Storage<Self>;
#[conditional] fn trailing_zeros(value: Storage<Self>) -> Storage<Self>;
#[conditional] fn leading_ones(value: Storage<Self>) -> Storage<Self> {
Self::leading_zeros(Self::not(value))
}
#[conditional] fn trailing_ones(value: Storage<Self>) -> Storage<Self> {
Self::trailing_zeros(Self::not(value))
}
}
#[thermite_macros::register_trait]
pub trait UnsignedIntegerRegister:
IntegerRegister<Unsigned = Self, Element: crate::element::UnsignedIntegerElement>
{
#[conditional]
fn ilog2p1(value: Storage<Self>) -> Storage<Self> {
Self::count_ones(Self::next_power_of_two_m1(value))
}
#[conditional]
fn next_power_of_two_m1(mut value: Storage<Self>) -> Storage<Self> {
let width = (size_of::<Self::Element>() * 8) as u32;
let mut s = 1;
while s < width {
value = Self::bitor(value, Self::shr(value, s));
s <<= 1;
}
value
}
fn is_power_of_two(value: Storage<Self>) -> Storage<Self::Mask> {
Self::eq(Self::ZERO, Self::bitand(value, Self::sub(value, Self::ONE)))
}
fn in_range(value: Storage<Self>, lo: Storage<Self>, hi: Storage<Self>) -> Storage<Self::Mask> {
Self::le(Self::sub(value, lo), Self::sub(hi, lo))
}
#[conditional]
fn parity(mut value: Storage<Self>) -> Storage<Self> {
let mut shift = size_of::<Self::Element>() as u32 * 4;
if const { Self::HAS_HARDWARE_POPCNT } {
value = Self::count_ones(value);
} else if const { Self::HAS_TRUE_SHIFTV } {
while shift >= 4 {
value = Self::bitxor(value, Self::shr(value, shift));
shift >>= 1;
}
value = Self::shrv(
Self::splat(Element::from_u16(0x6996)),
Self::bitand(value, Self::splat(Element::from_u16(0x0F))),
);
} else {
while shift > 0 {
value = Self::bitxor(value, Self::shr(value, shift));
shift >>= 1;
}
}
Self::bitand(Self::ONE, value)
}
#[conditional]
fn avg(a: Storage<Self>, b: Storage<Self>) -> Storage<Self> {
Self::sub(Self::bitor(a, b), Self::shri::<1>(Self::bitxor(a, b)))
}
#[conditional]
fn abs_diff(a: Storage<Self>, b: Storage<Self>) -> Storage<Self> {
Self::bitor(Self::saturating_sub(a, b), Self::saturating_sub(b, a))
}
fn morton<const N: usize>(values: [Storage<Self>; N]) -> Storage<Self> {
crate::backend::generic::polyfills::morton_cascade::<Self, N>(values)
}
fn reverse_morton<const N: usize>(code: Storage<Self>) -> [Storage<Self>; N] {
crate::backend::generic::polyfills::reverse_morton_cascade::<Self, N>(code)
}
}
#[thermite_macros::register_trait]
pub trait SignedIntegerRegister:
IntegerRegister<Signed = Self, Element: crate::element::SignedIntegerElement> + SignedRegister
{
#[conditional]
fn sra(value: Storage<Self>, shift: u32) -> Storage<Self>;
#[conditional]
fn srai<const IMM8: i32>(value: Storage<Self>) -> Storage<Self> {
Self::sra(value, IMM8 as u32)
}
#[conditional]
fn srav(mut value: Storage<Self>, shifts: Storage<Self::Unsigned>) -> Storage<Self> {
for (r, s) in Self::as_mut_slice(&mut value)
.iter_mut()
.zip(<Self::Unsigned as Register>::as_slice(&shifts))
{
*r = *r >> *s; }
value
}
#[conditional]
fn avg_floor(a: Storage<Self>, b: Storage<Self>) -> Storage<Self> {
Self::add(Self::bitand(a, b), Self::srai::<1>(Self::bitxor(a, b)))
}
#[conditional]
fn avg_ceil(a: Storage<Self>, b: Storage<Self>) -> Storage<Self> {
Self::sub(Self::bitor(a, b), Self::srai::<1>(Self::bitxor(a, b)))
}
#[conditional]
fn mulhrs(a: Storage<Self>, b: Storage<Self>) -> Storage<Self> {
let w = (size_of::<Self::Element>() * 8) as u32;
let lo = Self::mullo(a, b);
let hi = Self::mulhi(a, b);
let shifted = Self::bitor(Self::shli::<1>(hi), Self::shr(lo, w - 1));
let round = Self::bitand(Self::shr(lo, w - 2), Self::ONE);
Self::add(shifted, round)
}
}
#[inline(always)]
fn zip_ternary<R: FloatRegister, F>(mut lhs: Storage<R>, rhs: Storage<R>, acc: Storage<R>, f: F) -> Storage<R>
where
F: Fn(&mut R::Element, R::Element, R::Element),
{
let rhs = R::iter(&rhs);
let acc = R::iter(&acc);
for ((lhs, rhs), acc) in R::iter_mut(&mut lhs).zip(rhs).zip(acc) {
f(lhs, *rhs, *acc);
}
lhs
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct NativeCapability(pub u64);
impl NativeCapability {
pub const fn has(&self, cap: u64) -> bool {
(self.0 & cap) == cap
}
pub const NONE: Self = Self(0);
pub const LDEXP: u64 = 1 << 0;
pub const FREXP: u64 = 1 << 1;
pub const SIN: u64 = 1 << 2;
pub const COS: u64 = 1 << 3;
pub const TAN: u64 = 1 << 4;
pub const EXP2: u64 = 1 << 5;
pub const LOG2: u64 = 1 << 6;
pub const EXP: u64 = 1 << 7;
pub const LN: u64 = 1 << 8;
pub const POWF: u64 = 1 << 9;
}
#[rustfmt::skip] #[thermite_macros::register_trait]
pub trait FloatRegister:
SignedRegister<
Element: FloatElementWithBits,
Signed: CastRegister<Self::SignedBits> + MaskInteroperable<Self::SignedBits, Self::Bits>,
Unsigned: CastRegister<Self::Bits> + MaskInteroperable<Self::SignedBits, Self::Bits>,
>
+ FullyInteroperable<Self::Bits, Self::SignedBits>
+ CastRegister<Self::ExtendedPrecision>
{
type Bits: UnsignedIntegerRegister<Lanes = Self::Lanes, Element = <Self::Element as FloatElementWithBits>::Bits>
+ FullyInteroperable<Self, Self::SignedBits> + CastRegister<Self::Unsigned> + MaskInteroperable<Self::Signed, Self::Unsigned>;
type SignedBits: SignedIntegerRegister<Lanes = Self::Lanes, Element = <Self::Element as FloatElementWithBits>::SignedBits>
+ FullyInteroperable<Self, Self::Bits> + CastRegister<Self::Signed> + MaskInteroperable<Self::Signed, Self::Unsigned>;
type ExtendedPrecision: FloatRegister<Lanes = Self::Lanes> + CastRegister<Self>;
const HAS_TRUE_FMA: bool;
const HALF: Storage<Self>;
const NEG_ZERO: Storage<Self>;
const INFINITY: Storage<Self>;
const NEG_INFINITY: Storage<Self>;
const NAN: Storage<Self>;
const EPSILON: Storage<Self>;
const EXP_MASK: Storage<Self::Bits>;
const ALT_NEG: Storage<Self> = crate::register::alt_sign_reg::<Self>(true);
const ALT_POS: Storage<Self> = crate::register::alt_sign_reg::<Self>(false);
const NATIVE_CAP: NativeCapability;
unsafe fn block_autovectorization(_value: &mut Storage<Self>) {}
unsafe fn native_ldexp(_value: Storage<Self>, _exp: Storage<Self::SignedBits>) -> Storage<Self> {
unreachable!("native_ldexp is not implemented for this FloatRegister");
}
unsafe fn native_frexp(_value: Storage<Self>) -> (Storage<Self>, Storage<Self::SignedBits>) {
unreachable!("native_frexp is not implemented for this FloatRegister");
}
unsafe fn native_sin_cos<P: Policy>(_value: Storage<Self>) -> (Storage<Self>, Storage<Self>) {
unreachable!("native_sin_cos is not implemented for this FloatRegister");
}
unsafe fn native_sin<P: Policy>(_value: Storage<Self>) -> Storage<Self> {
unreachable!("native_sin is not implemented for this FloatRegister");
}
unsafe fn native_cos<P: Policy>(_value: Storage<Self>) -> Storage<Self> {
unreachable!("native_cos is not implemented for this FloatRegister");
}
unsafe fn native_tan<P: Policy>(_value: Storage<Self>) -> Storage<Self> {
unreachable!("native_tan is not implemented for this FloatRegister");
}
unsafe fn native_exp2<P: Policy>(_value: Storage<Self>) -> Storage<Self> {
unreachable!("native_exp2 is not implemented for this FloatRegister");
}
unsafe fn native_log2<P: Policy>(_value: Storage<Self>) -> Storage<Self> {
unreachable!("native_ln2 is not implemented for this FloatRegister");
}
unsafe fn native_exp<P: Policy>(_value: Storage<Self>) -> Storage<Self> {
unreachable!("native_exp is not implemented for this FloatRegister");
}
unsafe fn native_ln<P: Policy>(_value: Storage<Self>) -> Storage<Self> {
unreachable!("native_log is not implemented for this FloatRegister");
}
unsafe fn native_powf<P: Policy>(_base: Storage<Self>, _exp: Storage<Self>) -> Storage<Self> {
unreachable!("native_powf is not implemented for this FloatRegister");
}
fn total_order(value: Storage<Self>) -> Storage<Self::SignedBits> {
let shift = const { size_of::<Self::Element>() as u32 * 8 - 1 };
let signed_bits = <Self::SignedBits as BitCastRegister<Self>>::from_bits(value);
let is_negative = <Self::SignedBits as SignedIntegerRegister>::sra(signed_bits, shift);
let mask = <Self::SignedBits as BitshiftRegister>::shri::<1>(is_negative);
Self::SignedBits::bitxor(signed_bits, mask)
}
fn linear_order(value: Storage<Self>) -> Storage<Self::SignedBits> {
let shift = const { size_of::<Self::Element>() as u32 * 8 - 1 };
let signed_bits = <Self::SignedBits as BitCastRegister<Self>>::from_bits(value);
let is_negative = <Self::SignedBits as SignedIntegerRegister>::sra(signed_bits, shift);
let mask = <Self::SignedBits as BitshiftRegister>::shri::<1>(is_negative);
Self::SignedBits::sub(Self::SignedBits::bitxor(signed_bits, mask), is_negative)
}
fn is_nan(value: Storage<Self>) -> Storage<Self::Mask> {
if let Some(nan_pattern) = <Self::Element as FloatElementWithBits>::NAN_PATTERN {
let nan = <Self as BitCastRegister<Self::Bits>>::from_bits(Self::Bits::splat(nan_pattern));
return Self::eq(value, nan);
}
Self::ne(value, value)
}
fn is_infinite(value: Storage<Self>) -> Storage<Self::Mask> {
if const { !<Self::Element as FloatElement>::HAS_INFINITY } {
return Self::Mask::FALSY;
}
Self::eq(Self::abs(value), Self::INFINITY)
}
fn is_finite(value: Storage<Self>) -> Storage<Self::Mask> {
if const { !<Self::Element as FloatElement>::HAS_INFINITY } {
return Self::Mask::TRUTHY;
}
Self::lt(Self::abs(value), Self::INFINITY)
}
fn is_subnormal(value: Storage<Self>) -> Storage<Self::Mask> {
if const { !<Self::Element as FloatElement>::HAS_SUBNORMALS } {
return Self::Mask::FALSY;
}
let bits: Storage<Self::Bits> = <Self::Bits as BitCastRegister<Self>>::from_bits(value);
let exp = Self::Bits::bitand(Self::EXP_MASK, bits); let rest = Self::Bits::bitandnot(Self::EXP_MASK, bits);
let mantissa = Self::Bits::shli::<1>(rest);
let exp_is_zero = Self::Bits::eq(exp, Self::Bits::ZERO);
let mantissa_is_zero = Self::Bits::eq(mantissa, Self::Bits::ZERO);
let is_subnormal = <Self::Bits as CoreRegister>::Mask::bitandnot(mantissa_is_zero, exp_is_zero);
<Self::Mask as CastMaskRegister<<Self::Bits as CoreRegister>::Mask>>::mask_from(is_subnormal)
}
fn is_zero_or_subnormal(value: Storage<Self>) -> Storage<Self::Mask> {
if const { !<Self::Element as FloatElement>::HAS_SUBNORMALS } {
return Self::eq(value, Self::ZERO);
}
let bits: Storage<Self::Bits> = <Self::Bits as BitCastRegister<Self>>::from_bits(value);
let exp = Self::Bits::bitand(Self::EXP_MASK, bits);
let is_zero_or_subnormal = Self::Bits::eq(exp, Self::Bits::ZERO);
<Self::Mask as CastMaskRegister<<Self::Bits as CoreRegister>::Mask>>::mask_from(is_zero_or_subnormal)
}
fn is_normal(value: Storage<Self>) -> Storage<Self::Mask> {
let bits = <Self::Bits as BitCastRegister<Self>>::from_bits(value);
let exp = Self::Bits::bitand(Self::EXP_MASK, bits);
let exp_is_zero = Self::Bits::eq(exp, Self::Bits::ZERO);
let exp_is_max: Storage<<Self::Bits as CoreRegister>::Mask> =
if const { <Self::Element as FloatElement>::HAS_INFINITY } {
Self::Bits::eq(exp, Self::EXP_MASK) } else if let Some(nan_pattern) = <Self::Element as FloatElementWithBits>::NAN_PATTERN {
let nan = <Self as BitCastRegister<Self::Bits>>::from_bits(Self::Bits::splat(nan_pattern));
<<Self::Bits as CoreRegister>::Mask as CastMaskRegister<Self::Mask>>::mask_from(Self::eq(value, nan))
} else {
<<Self::Bits as CoreRegister>::Mask as MaskRegister>::FALSY
};
let is_not_normal = <Self::Bits as CoreRegister>::Mask::bitor(exp_is_max, exp_is_zero);
let is_normal = <Self::Bits as CoreRegister>::Mask::not(is_not_normal);
<Self::Mask as CastMaskRegister<<Self::Bits as CoreRegister>::Mask>>::mask_from(is_normal)
}
#[conditional] fn mul_adde(lhs: Storage<Self>, rhs: Storage<Self>, acc: Storage<Self>) -> Storage<Self> {
if Self::HAS_TRUE_FMA {
Self::mul_add(lhs, rhs, acc)
} else {
Self::add(Self::mul(lhs, rhs), acc)
}
}
#[conditional] fn mul_sube(lhs: Storage<Self>, rhs: Storage<Self>, acc: Storage<Self>) -> Storage<Self> {
if Self::HAS_TRUE_FMA {
Self::mul_sub(lhs, rhs, acc)
} else {
Self::sub(Self::mul(lhs, rhs), acc)
}
}
#[conditional] fn nmul_adde(lhs: Storage<Self>, rhs: Storage<Self>, acc: Storage<Self>) -> Storage<Self> {
if Self::HAS_TRUE_FMA {
Self::nmul_add(lhs, rhs, acc)
} else {
Self::sub(acc, Self::mul(lhs, rhs))
}
}
#[conditional] fn nmul_sube(lhs: Storage<Self>, rhs: Storage<Self>, acc: Storage<Self>) -> Storage<Self> {
if Self::HAS_TRUE_FMA {
Self::nmul_sub(lhs, rhs, acc)
} else {
Self::mul_sube(Self::neg(lhs), rhs, acc)
}
}
#[conditional] fn mul_add(lhs: Storage<Self>, rhs: Storage<Self>, acc: Storage<Self>) -> Storage<Self> {
zip_ternary::<Self, _>(lhs, rhs, acc, |lhs, rhs, acc| {
*lhs = MulAddExt::mul_add(*lhs, rhs, acc);
})
}
#[conditional] fn mul_sub(lhs: Storage<Self>, rhs: Storage<Self>, acc: Storage<Self>) -> Storage<Self> {
zip_ternary::<Self, _>(lhs, rhs, acc, |lhs, rhs, acc| {
*lhs = MulAddExt::mul_sub(*lhs, rhs, acc);
})
}
#[conditional] fn nmul_add(lhs: Storage<Self>, rhs: Storage<Self>, acc: Storage<Self>) -> Storage<Self> {
zip_ternary::<Self, _>(lhs, rhs, acc, |lhs, rhs, acc| {
*lhs = MulAddExt::nmul_add(*lhs, rhs, acc);
})
}
#[conditional] fn nmul_sub(lhs: Storage<Self>, rhs: Storage<Self>, acc: Storage<Self>) -> Storage<Self> {
zip_ternary::<Self, _>(lhs, rhs, acc, |lhs, rhs, acc| {
*lhs = MulAddExt::nmul_sub(*lhs, rhs, acc);
})
}
#[conditional] fn addsub(a: Storage<Self>, b: Storage<Self>) -> Storage<Self> {
Self::add(a, Self::bitxor(b, Self::ALT_NEG))
}
#[conditional] fn fmaddsub(a: Storage<Self>, b: Storage<Self>, c: Storage<Self>) -> Storage<Self> {
Self::mul_adde(a, b, Self::bitxor(c, Self::ALT_NEG))
}
#[conditional] fn fmsubadd(a: Storage<Self>, b: Storage<Self>, c: Storage<Self>) -> Storage<Self> {
Self::mul_adde(a, b, Self::bitxor(c, Self::ALT_POS))
}
#[conditional] fn sqrt(value: Storage<Self>) -> Storage<Self>;
#[conditional] fn rcp(value: Storage<Self>) -> Storage<Self> {
Self::div(Self::ONE, value)
}
#[conditional] fn rsqrt(value: Storage<Self>) -> Storage<Self> {
Self::rcp(Self::sqrt(value))
}
const HAS_APPROX_RSQRT: bool;
const HAS_APPROX_RCP: bool;
#[conditional] fn floor(value: Storage<Self>) -> Storage<Self>;
#[conditional] fn ceil(value: Storage<Self>) -> Storage<Self>;
#[conditional] fn round(value: Storage<Self>) -> Storage<Self>;
#[conditional] fn trunc(value: Storage<Self>) -> Storage<Self>;
#[conditional] fn fract(value: Storage<Self>) -> Storage<Self> {
Self::sub(value, Self::trunc(value))
}
#[conditional] fn mul_sign(value: Storage<Self>, sign: Storage<Self>) -> Storage<Self> {
Self::bitxor(value, Self::signed_zero(sign))
}
#[conditional] fn signed_zero(value: Storage<Self>) -> Storage<Self> {
Self::bitand(Self::NEG_ZERO, value)
}
#[conditional] fn next_up(value: Storage<Self>) -> Storage<Self> {
let bits = <Self::Bits as BitCastRegister<Self>>::from_bits(value);
let abs = <Self::Bits as BitCastRegister<Self>>::from_bits(Self::abs(value));
let is_nan = Self::is_nan(value);
let is_inf = Self::eq(value, Self::INFINITY);
let unchanged = Self::Mask::bitor(is_nan, is_inf);
let is_positive = Self::Bits::eq(abs, bits);
let is_zero = Self::Bits::eq(abs, <Self::Bits as BitCastRegister<Self>>::from_bits(Self::ZERO));
let add = Self::Bits::add(bits, Self::Bits::ONE);
let sub = Self::Bits::sub(bits, Self::Bits::ONE);
let next_bits = Self::Bits::blendv(is_positive, sub, add);
let next_bits = Self::Bits::blendv(is_zero, next_bits, Self::Bits::ONE);
let unchanged = <<Self::Bits as CoreRegister>::Mask as CastMaskRegister<Self::Mask>>::mask_from(unchanged);
<Self as BitCastRegister<Self::Bits>>::from_bits(Self::Bits::blendv(unchanged, next_bits, bits))
}
#[conditional] fn next_down(value: Storage<Self>) -> Storage<Self> {
let bits = <Self::Bits as BitCastRegister<Self>>::from_bits(value);
let abs = <Self::Bits as BitCastRegister<Self>>::from_bits(Self::abs(value));
let is_nan = Self::is_nan(value);
let is_neg_inf = Self::eq(value, Self::NEG_INFINITY);
let unchanged = Self::Mask::bitor(is_nan, is_neg_inf);
let is_positive = Self::Bits::eq(abs, bits);
let is_zero = Self::Bits::eq(abs, <Self::Bits as BitCastRegister<Self>>::from_bits(Self::ZERO));
let add = Self::Bits::add(bits, Self::Bits::ONE);
let sub = Self::Bits::sub(bits, Self::Bits::ONE);
let next_bits = Self::Bits::blendv(is_positive, add, sub);
let sign_bit = <Self::Bits as BitCastRegister<Self>>::from_bits(Self::NEG_ZERO);
let min_neg = Self::Bits::bitor(Self::Bits::ONE, sign_bit);
let next_bits = Self::Bits::blendv(is_zero, next_bits, min_neg);
let unchanged = <<Self::Bits as CoreRegister>::Mask as CastMaskRegister<Self::Mask>>::mask_from(unchanged);
<Self as BitCastRegister<Self::Bits>>::from_bits(Self::Bits::blendv(unchanged, next_bits, bits))
}
fn mix(a: Storage<Self>, b: Storage<Self>, t: Storage<Self>) -> Storage<Self> {
if const { Self::HAS_TRUE_FMA } {
Self::mul_add(Self::sub(b, a), t, a) } else {
let t0 = Self::sub(Self::ONE, t); Self::add(Self::mul(a, t0), Self::mul(b, t)) }
}
}
#[inline(always)]
fn unpack_packed<S, C, F, B>(values: Storage<C>) -> Storage<F>
where
S: FloatSpec,
C: UnsignedIntegerRegister,
F: FloatRegister<Element = f32, Lanes = C::Lanes, Bits = B> + BitCastRegister<B>,
B: UnsignedIntegerRegister<Lanes = C::Lanes, Unsigned = B, Element = u32>
+ BitshiftRegister
+ CastRegister<C>
+ BitCastRegister<F>,
{
type M<B> = <B as CoreRegister>::Mask;
let h = <B as CastRegister<C>>::cast_from(values);
let e = B::bitand(B::shr(h, S::MANTISSA_BITS), B::splat(S::EXP_FIELD_MAX));
let m = B::bitand(h, B::splat(S::MANTISSA_MASK));
let mant = B::shl(m, S::MANTISSA_SHIFT);
let normal = B::bitor(
B::shl(B::add(e, B::splat(S::EXP_REBIAS as u32)), spec::F32_MANTISSA_BITS),
mant,
);
let k = B::splat(((128 - S::EXP_BIAS) as u32) << spec::F32_MANTISSA_BITS);
let to_f = <F as BitCastRegister<B>>::from_bits;
let sub_f = F::sub(to_f(B::bitor(mant, k)), to_f(k));
let subnormal = <B as BitCastRegister<F>>::from_bits(sub_f);
let mut out = B::blendv(B::eq(e, B::ZERO), normal, subnormal);
if const { matches!(S::SPECIAL, spec::SpecialEncoding::Ieee) } {
let e_is_max = B::eq(e, B::splat(S::EXP_FIELD_MAX));
let quiet = B::blendv(B::eq(m, B::ZERO), B::splat(spec::F32_IMPLICIT >> 1), B::ZERO);
let inf_nan = B::bitor(
B::bitor(B::splat(spec::F32_EXP_FIELD_MAX << spec::F32_MANTISSA_BITS), mant),
quiet,
);
out = B::blendv(e_is_max, out, inf_nan);
} else if const { matches!(S::SPECIAL, spec::SpecialEncoding::FiniteNanOnly) } {
let is_nan = M::<B>::bitand(
B::eq(e, B::splat(S::EXP_FIELD_MAX)),
B::eq(m, B::splat(S::MANTISSA_MASK)),
);
out = B::blendv(is_nan, out, B::splat(spec::F32_QUIET_NAN));
}
if const { S::HAS_SIGN } {
out = B::bitor(out, B::shl(B::shr(h, S::SIGN_SHIFT), 31));
}
<F as BitCastRegister<B>>::from_bits(out)
}
#[inline(always)]
fn pack_packed<S, C, F, B>(values: Storage<F>) -> Storage<C>
where
S: FloatSpec,
C: UnsignedIntegerRegister + CastRegister<B>,
F: FloatRegister<Element = f32, Lanes = C::Lanes, Bits = B> + BitCastRegister<B>,
B: UnsignedIntegerRegister<Lanes = C::Lanes, Unsigned = B, Element = u32>
+ BitshiftRegister
+ CastRegister<C>
+ BitCastRegister<F>,
{
type M<B> = <B as CoreRegister>::Mask;
let fb = <B as BitCastRegister<F>>::from_bits(values);
let abs = B::bitand(fb, B::splat(0x7FFF_FFFF));
let f32_exp = B::shr(abs, spec::F32_MANTISSA_BITS); let f32_mant = B::bitand(abs, B::splat(spec::F32_IMPLICIT - 1));
let significand = B::bitor(B::splat(spec::F32_IMPLICIT), f32_mant);
let one = B::ONE;
let rebias = B::splat(S::EXP_REBIAS as u32);
let sub = B::le(f32_exp, rebias);
let extra = B::sub(B::add(rebias, one), f32_exp); let shift = B::blendv(
sub,
B::splat(S::MANTISSA_SHIFT),
B::add(B::splat(S::MANTISSA_SHIFT), extra),
);
let e = B::blendv(sub, B::sub(f32_exp, rebias), B::ZERO);
let tiny = B::ge(shift, B::splat(32));
let shift = B::min(shift, B::splat(31));
let keep = B::shrv(significand, shift);
let rem = B::bitand(significand, B::sub(B::shlv(one, shift), one));
let halfway = B::shlv(one, B::sub(shift, one));
let tie_to_odd = M::<B>::bitand(B::eq(rem, halfway), B::eq(B::bitand(keep, one), one));
let round_up = M::<B>::bitor(B::gt(rem, halfway), tie_to_odd);
let q = B::add(keep, B::bitand(B::from_mask(round_up), one));
let carry = B::ge(B::shr(q, S::MANTISSA_BITS), B::splat(2));
let e_carried = B::add(e, B::bitand(B::from_mask(carry), one));
let frac = B::nz(carry, B::bitand(q, B::splat(S::MANTISSA_MASK)));
let mag_normal = B::bitor(B::shl(e_carried, S::MANTISSA_BITS), frac);
let mut mag = B::blendv(sub, mag_normal, q);
let overflow = M::<B>::bitandnot(sub, B::gt(e_carried, B::splat(S::MAX_FINITE_EXP_FIELD)));
mag = B::blendv(overflow, mag, B::splat(S::OVERFLOW_BITS));
mag = B::blendv(tiny, mag, B::ZERO);
if const { matches!(S::SPECIAL, spec::SpecialEncoding::FiniteNanOnly) } {
mag = B::blendv(
B::gt(mag, B::splat(S::MAX_FINITE_BITS)),
mag,
B::splat(S::MAX_FINITE_BITS),
);
}
mag = B::blendv(B::eq(f32_exp, B::ZERO), mag, B::ZERO);
let special = B::eq(f32_exp, B::splat(spec::F32_EXP_FIELD_MAX));
let is_nan = M::<B>::bitandnot(B::eq(f32_mant, B::ZERO), special);
let is_inf = M::<B>::bitand(special, B::eq(f32_mant, B::ZERO));
mag = B::blendv(is_inf, mag, B::splat(S::OVERFLOW_BITS));
mag = B::blendv(is_nan, mag, B::splat(S::NAN_OUT_BITS));
if const { S::HAS_SIGN } {
mag = B::bitor(mag, B::shr(B::bitand(fb, B::splat(0x8000_0000)), 31 - S::SIGN_SHIFT));
}
<C as CastRegister<B>>::cast_from(mag)
}
pub trait PackedFloatRegister<
S: FloatSpec,
F: FloatRegister<Element = f32, Lanes = Self::Lanes, Bits: CastRegister<Self>>,
>: UnsignedIntegerRegister<Unsigned = Self>
{
#[inline(always)]
fn pack(values: Storage<F>) -> Storage<Self>
where
Self: CastRegister<F::Bits>,
{
pack_packed::<S, Self, F, F::Bits>(values)
}
#[inline(always)]
fn unpack(values: Storage<Self>) -> Storage<F> {
unpack_packed::<S, Self, F, F::Bits>(values)
}
}
impl<S, C, const N: usize> PackedFloatRegister<S, array::ArrayRegister<f32, N>> for array::ArrayRegister<C, N>
where
S: FloatSpec,
C: CoreRegister,
array::ArrayRegister<C, N>: UnsignedIntegerRegister<Unsigned = Self>,
array::ArrayRegister<f32, N>: FloatRegister<Element = f32, Lanes = Self::Lanes, Bits: CastRegister<Self>>,
{
}
pub trait Sad16Register<W>: UnsignedIntegerRegister<Unsigned = Self>
where
W: UnsignedIntegerRegister<Element = u16>,
{
fn sad16(a: Storage<Self>, b: Storage<Self>) -> Storage<W>;
}
pub trait Sad32Register<W>: UnsignedIntegerRegister<Unsigned = Self>
where
W: UnsignedIntegerRegister<Element = u32>,
{
fn sad32(a: Storage<Self>, b: Storage<Self>) -> Storage<W>;
#[inline(always)]
fn sad32_accum(acc: Storage<W>, a: Storage<Self>, b: Storage<Self>) -> Storage<W> {
W::add(acc, Self::sad32(a, b))
}
}
pub trait Sad64Register<W>: UnsignedIntegerRegister<Unsigned = Self>
where
W: UnsignedIntegerRegister<Element = u64>,
{
fn sad64(a: Storage<Self>, b: Storage<Self>) -> Storage<W>;
#[inline(always)]
fn sad64_accum(acc: Storage<W>, a: Storage<Self>, b: Storage<Self>) -> Storage<W> {
W::add(acc, Self::sad64(a, b))
}
}
macro_rules! decl_sad_scalar {
($name:ident, $ielem:ty, $oelem:ty, $group:expr) => {
#[inline(always)]
pub(crate) fn $name<C, W>(a: Storage<C>, b: Storage<C>) -> Storage<W>
where
C: UnsignedIntegerRegister<Element = $ielem>,
W: UnsignedIntegerRegister<Element = $oelem>,
{
let d = C::abs_diff(a, b);
let ds = C::as_slice(&d);
let n = ds.len();
let mut out = W::EMPTY;
{
let os = W::as_mut_slice(&mut out);
let mut j = 0;
while j < os.len() {
let start = j * $group;
let mut acc: $oelem = 0;
let mut k = 0;
while k < $group && start + k < n {
acc += ds[start + k] as $oelem;
k += 1;
}
os[j] = acc;
j += 1;
}
}
out
}
};
}
decl_sad_scalar!(sad_scalar_u8_16, u8, u16, 2);
decl_sad_scalar!(sad_scalar_u8_32, u8, u32, 4);
decl_sad_scalar!(sad_scalar_u8_64, u8, u64, 8);
decl_sad_scalar!(sad_scalar_u16_32, u16, u32, 2);
decl_sad_scalar!(sad_scalar_u16_64, u16, u64, 4);
decl_sad_scalar!(sad_scalar_u32_64, u32, u64, 2);
#[thermite_macros::inline_always]
impl Sad16Register<u16> for array::ArrayRegister<u8, 2> {
fn sad16(a: Storage<Self>, b: Storage<Self>) -> Storage<u16> {
sad_scalar_u8_16::<Self, u16>(a, b)
}
}
#[thermite_macros::inline_always]
impl Sad32Register<u32> for array::ArrayRegister<u8, 2> {
fn sad32(a: Storage<Self>, b: Storage<Self>) -> Storage<u32> {
sad_scalar_u8_32::<Self, u32>(a, b)
}
}
#[thermite_macros::inline_always]
impl Sad64Register<u64> for array::ArrayRegister<u8, 2> {
fn sad64(a: Storage<Self>, b: Storage<Self>) -> Storage<u64> {
sad_scalar_u8_64::<Self, u64>(a, b)
}
}
#[thermite_macros::inline_always]
impl Sad32Register<u32> for array::ArrayRegister<u16, 2> {
fn sad32(a: Storage<Self>, b: Storage<Self>) -> Storage<u32> {
sad_scalar_u16_32::<Self, u32>(a, b)
}
}
#[thermite_macros::inline_always]
impl Sad64Register<u64> for array::ArrayRegister<u16, 2> {
fn sad64(a: Storage<Self>, b: Storage<Self>) -> Storage<u64> {
sad_scalar_u16_64::<Self, u64>(a, b)
}
}
macro_rules! impl_array_sad {
($trait:ident, $method:ident, $elem:ty) => {
impl<C, W, const N: usize> $trait<array::ArrayRegister<W, N>> for array::ArrayRegister<C, N>
where
C: $trait<W>,
W: UnsignedIntegerRegister<Element = $elem>,
array::ArrayRegister<C, N>: UnsignedIntegerRegister<Unsigned = Self, Storage = array::ArrayRegister<C, N>>,
array::ArrayRegister<W, N>: UnsignedIntegerRegister<Element = $elem, Storage = array::ArrayRegister<W, N>>,
{
#[inline(always)]
fn $method(a: Storage<Self>, b: Storage<Self>) -> Storage<array::ArrayRegister<W, N>> {
let mut out = <array::ArrayRegister<W, N> as CoreRegister>::EMPTY;
let mut i = 0;
while i < N {
out.0[i] = C::$method(a.0[i], b.0[i]);
i += 1;
}
out
}
}
};
}
impl_array_sad!(Sad16Register, sad16, u16);
impl_array_sad!(Sad32Register, sad32, u32);
impl_array_sad!(Sad64Register, sad64, u64);
#[inline(always)]
pub(crate) fn swar_fold<W: UnsignedIntegerRegister>(x: Storage<W>, shift: u32, mask: W::Element) -> Storage<W> {
let mask = W::splat(mask);
W::add(W::bitand(x, mask), W::bitand(W::shr(x, shift), mask))
}
#[inline(always)]
pub(crate) fn sad_cascade_u8_16<C, W>(diffs: Storage<C>) -> Storage<W>
where
C: UnsignedIntegerRegister<Element = u8>,
W: UnsignedIntegerRegister<Element = u16> + BitCastRegister<C>,
{
swar_fold::<W>(<W as BitCastRegister<C>>::from_bits(diffs), 8, 0x00ff)
}
#[inline(always)]
pub(crate) fn sad_cascade_u8_32<C, W>(diffs: Storage<C>) -> Storage<W>
where
C: UnsignedIntegerRegister<Element = u8>,
W: UnsignedIntegerRegister<Element = u32> + BitCastRegister<C>,
{
let x = <W as BitCastRegister<C>>::from_bits(diffs);
let x = swar_fold::<W>(x, 8, 0x00ff_00ff);
swar_fold::<W>(x, 16, 0x0000_ffff)
}
#[inline(always)]
pub(crate) fn sad_cascade_u8_64<C, W>(diffs: Storage<C>) -> Storage<W>
where
C: UnsignedIntegerRegister<Element = u8>,
W: UnsignedIntegerRegister<Element = u64> + BitCastRegister<C>,
{
let x = <W as BitCastRegister<C>>::from_bits(diffs);
let x = swar_fold::<W>(x, 8, 0x00ff_00ff_00ff_00ff);
let x = swar_fold::<W>(x, 16, 0x0000_ffff_0000_ffff);
W::bitand(W::add(x, W::shr(x, 32)), W::splat(0x0000_0000_ffff_ffff))
}
#[inline(always)]
pub(crate) fn sad_cascade_u16_32<C, W>(diffs: Storage<C>) -> Storage<W>
where
C: UnsignedIntegerRegister<Element = u16>,
W: UnsignedIntegerRegister<Element = u32> + BitCastRegister<C>,
{
swar_fold::<W>(<W as BitCastRegister<C>>::from_bits(diffs), 16, 0x0000_ffff)
}
#[inline(always)]
pub(crate) fn sad_cascade_u16_64<C, W>(diffs: Storage<C>) -> Storage<W>
where
C: UnsignedIntegerRegister<Element = u16>,
W: UnsignedIntegerRegister<Element = u64> + BitCastRegister<C>,
{
let x = <W as BitCastRegister<C>>::from_bits(diffs);
let x = swar_fold::<W>(x, 16, 0x0000_ffff_0000_ffff);
W::bitand(W::add(x, W::shr(x, 32)), W::splat(0x0000_0000_ffff_ffff))
}
#[inline(always)]
pub(crate) fn sad_cascade_u32_64<C, W>(diffs: Storage<C>) -> Storage<W>
where
C: UnsignedIntegerRegister<Element = u32>,
W: UnsignedIntegerRegister<Element = u64> + BitCastRegister<C>,
{
swar_fold::<W>(<W as BitCastRegister<C>>::from_bits(diffs), 32, 0x0000_0000_ffff_ffff)
}