#[cfg(test)]
mod tests;
pub(crate) mod reduce {
use crate::utils::ArithOps;
use std::ops::Add;
#[inline(always)]
pub(crate) fn sum<T: ArithOps<Scalar: Copy + Add<Output = T::Scalar>>, const N: usize>(
v: T,
) -> T::Scalar {
match N {
1 => {
let [x] = *v.as_array_().first_chunk::<1>().unwrap();
x
}
2 => {
let [x, y] = *v.as_array_().first_chunk::<2>().unwrap();
x + y
}
3 => {
let [x, y, z] = *v.as_array_().first_chunk::<3>().unwrap();
x + y + z
}
4 => {
let [x, y, z, w] = *v.as_array_().first_chunk::<4>().unwrap();
(x + z) + (y + w)
}
_ => unimplemented!(),
}
}
}
pub(crate) mod mask {
use crate::{
simd::utils::{Simd2Ext, Simd4Ext, compute_i32x2, i32x2, swizzle},
utils::CanonicalMask,
};
use wide::{bytemuck::cast, i32x4, i64x2, i64x4};
#[allow(unused_imports)]
#[cfg(target_arch = "x86_64")]
use crate::arch::{x86_64::__m128i, *};
impl CanonicalMask<i32> {
#[inline(always)]
pub(crate) fn cast_i64(self) -> CanonicalMask<i64> {
unsafe { CanonicalMask::new_unchecked(i64::from(self.into_inner())) }
}
}
impl CanonicalMask<i32x2> {
#[inline(always)]
pub(crate) fn cast_i64(self) -> CanonicalMask<i64x2> {
let duplicated = swizzle!(self.load_mask().into_inner(), [0, 0, 1, 1]);
unsafe { CanonicalMask::new_unchecked(cast::<i32x4, i64x2>(duplicated)) }
}
}
impl CanonicalMask<i32x4> {
#[inline(always)]
pub(crate) fn cast_i64(self) -> CanonicalMask<i64x4> {
let inner = self.into_inner();
#[rustfmt::skip]
let widened = cfg_select! {
target_feature = "avx2" => unsafe {
avx2::_mm256_cvtepi32_epi64(inner.into()).into()
},
_ => {{
let halves = [swizzle!(inner, [0, 0, 1, 1]), swizzle!(inner, [2, 2, 3, 3])];
cast::<[i32x4; 2], i64x4>(halves)
}},
};
unsafe { CanonicalMask::new_unchecked(widened) }
}
}
impl CanonicalMask<i64> {
#[inline(always)]
pub(crate) fn cast_i32(self) -> CanonicalMask<i32> {
unsafe { CanonicalMask::new_unchecked(self.into_inner() as i32) }
}
}
impl CanonicalMask<i64x2> {
#[inline(always)]
fn narrow_i32(self) -> CanonicalMask<compute_i32x2> {
let halves = cast::<i64x2, i32x4>(self.into_inner());
unsafe { CanonicalMask::new_unchecked(swizzle!(halves, [0, 2])) }
}
#[inline(always)]
pub(crate) fn cast_i32(self) -> CanonicalMask<i32x2> {
CanonicalMask::store_mask(self.narrow_i32())
}
}
impl CanonicalMask<i64x4> {
#[inline(always)]
pub(crate) fn cast_i32(self) -> CanonicalMask<i32x4> {
let [low, high] = cast::<i64x4, [i32x4; 2]>(self.into_inner());
unsafe { CanonicalMask::new_unchecked(swizzle!(low, high, [0, 2, 4, 6])) }
}
}
macro_rules! impl_matrix_conversion {
($scalar:ty, $vec2:ty, $vec4:ty) => {
#[inline(always)]
pub(crate) fn from_array_1x1([a]: [[bool; 1]; 1]) -> CanonicalMask<$scalar> {
from_array_1(a)
}
#[inline(always)]
pub(crate) fn to_array_1x1(mask: CanonicalMask<$scalar>) -> [[bool; 1]; 1] {
[to_array_1(mask)]
}
#[inline(always)]
pub(crate) fn from_array_2x1([a]: [[bool; 2]; 1]) -> CanonicalMask<$vec2> {
from_array_2(a)
}
#[inline(always)]
pub(crate) fn to_array_2x1(mask: CanonicalMask<$vec2>) -> [[bool; 2]; 1] {
[to_array_2(mask)]
}
#[inline(always)]
pub(crate) fn from_array_3x1([a]: [[bool; 3]; 1]) -> CanonicalMask<$vec4> {
from_array_3(a)
}
#[inline(always)]
pub(crate) fn to_array_3x1(mask: CanonicalMask<$vec4>) -> [[bool; 3]; 1] {
[to_array_3(mask)]
}
#[inline(always)]
pub(crate) fn from_array_4x1([a]: [[bool; 4]; 1]) -> CanonicalMask<$vec4> {
from_array_4(a)
}
#[inline(always)]
pub(crate) fn to_array_4x1(mask: CanonicalMask<$vec4>) -> [[bool; 4]; 1] {
[to_array_4(mask)]
}
#[inline(always)]
pub(crate) fn from_array_1x2([[a], [b]]: [[bool; 1]; 2]) -> CanonicalMask<$vec2> {
from_array_2([a, b])
}
#[inline(always)]
pub(crate) fn to_array_1x2(mask: CanonicalMask<$vec2>) -> [[bool; 1]; 2] {
let [a, b] = to_array_2(mask);
[[a], [b]]
}
#[inline(always)]
pub(crate) fn from_array_2x2([a, b]: [[bool; 2]; 2]) -> CanonicalMask<$vec4> {
from_array_4([a[0], a[1], b[0], b[1]])
}
#[inline(always)]
pub(crate) fn to_array_2x2(mask: CanonicalMask<$vec4>) -> [[bool; 2]; 2] {
let [a, b, c, d] = to_array_4(mask);
[[a, b], [c, d]]
}
#[inline(always)]
pub(crate) fn from_array_3x2([a, b]: [[bool; 3]; 2]) -> CanonicalMask<[$vec4; 2]> {
CanonicalMask::from_parts([from_array_3(a), from_array_3(b)])
}
#[inline(always)]
pub(crate) fn to_array_3x2(mask: CanonicalMask<[$vec4; 2]>) -> [[bool; 3]; 2] {
let [a, b] = mask.into_parts();
[to_array_3(a), to_array_3(b)]
}
#[inline(always)]
pub(crate) fn from_array_4x2([a, b]: [[bool; 4]; 2]) -> CanonicalMask<[$vec4; 2]> {
CanonicalMask::from_parts([from_array_4(a), from_array_4(b)])
}
#[inline(always)]
pub(crate) fn to_array_4x2(mask: CanonicalMask<[$vec4; 2]>) -> [[bool; 4]; 2] {
let [a, b] = mask.into_parts();
[to_array_4(a), to_array_4(b)]
}
#[inline(always)]
pub(crate) fn from_array_1x3([[a], [b], [c]]: [[bool; 1]; 3]) -> CanonicalMask<$vec4> {
from_array_3([a, b, c])
}
#[inline(always)]
pub(crate) fn to_array_1x3(mask: CanonicalMask<$vec4>) -> [[bool; 1]; 3] {
let [a, b, c] = to_array_3(mask);
[[a], [b], [c]]
}
#[allow(dead_code)]
#[inline(always)]
pub(crate) fn from_array_2x3_in_vec4(
array: [[bool; 2]; 3],
) -> CanonicalMask<[$vec4; 2]> {
let [[a, b], [c, d], [e, f]] = array;
CanonicalMask::from_parts([
from_array_4([a, b, c, d]),
from_array_2([e, f]).widen(),
])
}
#[allow(dead_code)]
#[inline(always)]
pub(crate) fn to_array_2x3_in_vec4(mask: CanonicalMask<[$vec4; 2]>) -> [[bool; 2]; 3] {
let [first, last] = mask.into_parts();
let [a, b, c, d] = to_array_4(first);
let [e, f] = to_array_2(last.xy());
[[a, b], [c, d], [e, f]]
}
#[allow(dead_code)]
#[inline(always)]
pub(crate) fn from_array_2x3_in_vec2(
array: [[bool; 2]; 3],
) -> CanonicalMask<[$vec2; 3]> {
CanonicalMask::from_parts(array.map(from_array_2))
}
#[allow(dead_code)]
#[inline(always)]
pub(crate) fn to_array_2x3_in_vec2(mask: CanonicalMask<[$vec2; 3]>) -> [[bool; 2]; 3] {
mask.into_parts().map(to_array_2)
}
#[inline(always)]
pub(crate) fn from_array_3x3([a, b, c]: [[bool; 3]; 3]) -> CanonicalMask<[$vec4; 3]> {
CanonicalMask::from_parts([from_array_3(a), from_array_3(b), from_array_3(c)])
}
#[inline(always)]
pub(crate) fn to_array_3x3(mask: CanonicalMask<[$vec4; 3]>) -> [[bool; 3]; 3] {
let [a, b, c] = mask.into_parts();
[to_array_3(a), to_array_3(b), to_array_3(c)]
}
#[inline(always)]
pub(crate) fn from_array_4x3([a, b, c]: [[bool; 4]; 3]) -> CanonicalMask<[$vec4; 3]> {
CanonicalMask::from_parts([from_array_4(a), from_array_4(b), from_array_4(c)])
}
#[inline(always)]
pub(crate) fn to_array_4x3(mask: CanonicalMask<[$vec4; 3]>) -> [[bool; 4]; 3] {
let [a, b, c] = mask.into_parts();
[to_array_4(a), to_array_4(b), to_array_4(c)]
}
#[inline(always)]
pub(crate) fn from_array_1x4(
[[a], [b], [c], [d]]: [[bool; 1]; 4],
) -> CanonicalMask<$vec4> {
from_array_4([a, b, c, d])
}
#[inline(always)]
pub(crate) fn to_array_1x4(mask: CanonicalMask<$vec4>) -> [[bool; 1]; 4] {
let [a, b, c, d] = to_array_4(mask);
[[a], [b], [c], [d]]
}
#[inline(always)]
pub(crate) fn from_array_2x4(array: [[bool; 2]; 4]) -> CanonicalMask<[$vec4; 2]> {
let [[a, b], [c, d], [e, f], [g, h]] = array;
CanonicalMask::from_parts([from_array_4([a, b, c, d]), from_array_4([e, f, g, h])])
}
#[inline(always)]
pub(crate) fn to_array_2x4(mask: CanonicalMask<[$vec4; 2]>) -> [[bool; 2]; 4] {
let [first, last] = mask.into_parts();
let [a, b, c, d] = to_array_4(first);
let [e, f, g, h] = to_array_4(last);
[[a, b], [c, d], [e, f], [g, h]]
}
#[inline(always)]
pub(crate) fn from_array_3x4(
[a, b, c, d]: [[bool; 3]; 4],
) -> CanonicalMask<[$vec4; 4]> {
CanonicalMask::from_parts([
from_array_3(a),
from_array_3(b),
from_array_3(c),
from_array_3(d),
])
}
#[inline(always)]
pub(crate) fn to_array_3x4(mask: CanonicalMask<[$vec4; 4]>) -> [[bool; 3]; 4] {
let [a, b, c, d] = mask.into_parts();
[to_array_3(a), to_array_3(b), to_array_3(c), to_array_3(d)]
}
#[inline(always)]
pub(crate) fn from_array_4x4(
[a, b, c, d]: [[bool; 4]; 4],
) -> CanonicalMask<[$vec4; 4]> {
CanonicalMask::from_parts([
from_array_4(a),
from_array_4(b),
from_array_4(c),
from_array_4(d),
])
}
#[inline(always)]
pub(crate) fn to_array_4x4(mask: CanonicalMask<[$vec4; 4]>) -> [[bool; 4]; 4] {
let [a, b, c, d] = mask.into_parts();
[to_array_4(a), to_array_4(b), to_array_4(c), to_array_4(d)]
}
};
}
pub(crate) mod i32 {
use super::*;
#[inline(always)]
fn from_array<const N: usize>(array: [bool; 4]) -> CanonicalMask<i32x4> {
std::assert_matches!(N, 2..=4);
#[rustfmt::skip]
let inner = cfg_select! {
all(target_feature = "avx512bw", target_feature = "avx512vl") => unsafe {
let bytes = sse2::_mm_cvtsi32_si128(i32::from_le_bytes(array.map(u8::from)));
let byte_mask =
avx512_bw_vl::_mm_movm_epi8(avx512_bw_vl::_mm_test_epi8_mask(bytes, bytes));
i32x4::from(sse41::_mm_cvtepi8_epi32(byte_mask))
},
all(target_feature = "neon", target_arch = "aarch64") => unsafe {
use core::arch::aarch64::*;
assert_ne!(N, 2);
let v: [i32; 4] = [array[0] as i32, array[1] as i32, array[2] as i32, array[3] as i32];
let v = vld1q_s32(v.as_ptr());
vreinterpretq_s32_u32(vtstq_s32(v, v)).into()
},
_ => {{
use wide::u8x16;
let packed = i32::from_le_bytes(array.map(u8::from));
let bytes: u8x16 = cast(i32x4::new([packed, 0, 0, 0]));
let words = u8x16::unpack_low(bytes, bytes);
let dwords = u8x16::unpack_low(words, words);
cast(dwords.simd_eq(u8x16::splat(0)) ^ u8x16::splat(u8::MAX))
}}
};
unsafe {
CanonicalMask::new_unchecked(inner)
}
}
#[inline(always)]
fn from_array_1([a]: [bool; 1]) -> CanonicalMask<i32> { CanonicalMask::<i32>::new(a) }
#[inline(always)]
pub(super) fn from_array_2(array: [bool; 2]) -> CanonicalMask<compute_i32x2> {
cfg_select! {
all(target_feature = "neon", target_arch = "aarch64") => unsafe {
use core::arch::aarch64::*;
let v: [i32; 2] = [array[0] as i32, array[1] as i32];
let v = vld1_s32(v.as_ptr());
let result = vreinterpret_s32_u32(vtst_s32(v, v));
CanonicalMask::new_unchecked(result.into())
},
_ => from_array::<2>([array[0], array[1], false, false]),
}
}
#[inline(always)]
fn from_array_3(array: [bool; 3]) -> CanonicalMask<i32x4> {
from_array::<3>([array[0], array[1], array[2], false])
}
#[inline(always)]
pub(super) fn from_array_4(array: [bool; 4]) -> CanonicalMask<i32x4> {
from_array::<4>(array)
}
#[inline(always)]
fn to_array<const N: usize>(mask: CanonicalMask<i32x4>) -> [bool; N] {
std::assert_matches!(N, 2..=4);
#[rustfmt::skip]
let bools_u32 = cfg_select! {
target_feature = "ssse3" => {{
use wide::u32x4;
let bytes = super::super::cast::u8_from_i32(mask.into_inner());
wide::bytemuck::cast::<_, u32x4>(bytes).to_array()[0]
}},
all(target_feature = "neon", target_arch = "aarch64") => unsafe {{
use core::arch::aarch64::*;
assert_ne!(N, 2);
let bits = vmovn_s32(mask.into_inner().into());
let bits_u8 = vreinterpret_u8_s16(bits);
let packed = vuzp1_u8(bits_u8, bits_u8);
let packed_u32 = vreinterpret_u32_u8(packed);
vget_lane_u32::<0>(packed_u32)
}},
_ => {{
let bits = mask.into_inner().to_bitmask();
bits.wrapping_mul(0x0020_4081)
}}
} & 0x0101_0101;
let bools =
unsafe { core::mem::transmute::<[u8; 4], [bool; 4]>(bools_u32.to_le_bytes()) };
core::array::from_fn(
#[inline(always)]
|i| bools[i],
)
}
#[inline(always)]
fn to_array_1(mask: CanonicalMask<i32>) -> [bool; 1] { [mask.into_inner() < 0] }
#[inline(always)]
pub(super) fn to_array_2(mask: CanonicalMask<compute_i32x2>) -> [bool; 2] {
cfg_select! {
all(target_feature = "neon", target_arch = "aarch64") => unsafe {
use core::arch::aarch64::*;
let ones = vdup_n_s32(1);
let bits = vand_s32(mask.into_inner().into(), ones);
let bits_u16 = vreinterpret_u16_s32(bits);
let step1 = vuzp1_u16(bits_u16, bits_u16);
let step1_u8 = vreinterpret_u8_u16(step1);
let packed = vuzp1_u8(step1_u8, step1_u8);
let packed_u16 = vreinterpret_u16_u8(packed);
core::mem::transmute::<u16, [bool; 2]>(vget_lane_u16::<0>(packed_u16))
},
_ => to_array(mask),
}
}
#[inline(always)]
fn to_array_3(mask: CanonicalMask<i32x4>) -> [bool; 3] { to_array(mask) }
#[inline(always)]
pub(super) fn to_array_4(mask: CanonicalMask<i32x4>) -> [bool; 4] { to_array(mask) }
pub(crate) use from_array_2x3_in_vec4 as from_array_2x3;
pub(crate) use to_array_2x3_in_vec4 as to_array_2x3;
impl_matrix_conversion!(i32, compute_i32x2, i32x4);
}
pub(crate) mod i64 {
use super::*;
#[inline(always)]
fn from_array_1([x]: [bool; 1]) -> CanonicalMask<i64> { CanonicalMask::<i64>::new(x) }
#[inline(always)]
fn from_array_2(array: [bool; 2]) -> CanonicalMask<i64x2> {
let narrow: CanonicalMask<i32x2> =
CanonicalMask::store_mask(super::i32::from_array_2(array));
narrow.cast_i64()
}
#[inline(always)]
fn from_array_3([x, y, z]: [bool; 3]) -> CanonicalMask<i64x4> {
from_array_4([x, y, z, false])
}
#[inline(always)]
fn from_array_4(array: [bool; 4]) -> CanonicalMask<i64x4> {
super::i32::from_array_4(array).cast_i64()
}
#[inline(always)]
fn to_array_1(mask: CanonicalMask<i64>) -> [bool; 1] { [mask.into_inner() < 0] }
#[inline(always)]
fn to_array_2(mask: CanonicalMask<i64x2>) -> [bool; 2] {
cfg_select! {
all(target_feature = "avx512f", target_feature = "avx512vl") => unsafe {
let bytes = avx512_vl::_mm_cvtepi64_epi8(mask.into_inner().into());
let packed = sse2::_mm_cvtsi128_si32(bytes) as u32 & 0x0000_0101;
core::mem::transmute::<u16, [bool; 2]>(packed as u16)
},
_ => super::i32::to_array_2(mask.narrow_i32()),
}
}
#[inline(always)]
fn to_array_3(mask: CanonicalMask<i64x4>) -> [bool; 3] {
let [x, y, z, _] = to_array_4(mask);
[x, y, z]
}
#[inline(always)]
fn to_array_4(mask: CanonicalMask<i64x4>) -> [bool; 4] {
cfg_select! {
all(target_feature = "avx512f", target_feature = "avx512vl") => unsafe {
let bytes = avx512_vl::_mm256_cvtepi64_epi8(mask.into_inner().into());
let packed = sse2::_mm_cvtsi128_si32(bytes) as u32 & 0x0101_0101;
core::mem::transmute::<[u8; 4], [bool; 4]>(packed.to_le_bytes())
},
_ => super::i32::to_array_4(mask.cast_i32()),
}
}
cfg_select! {
target_feature = "avx2" => {
pub(crate) use from_array_2x3_in_vec4 as from_array_2x3;
pub(crate) use to_array_2x3_in_vec4 as to_array_2x3;
}
_ => {
pub(crate) use from_array_2x3_in_vec2 as from_array_2x3;
pub(crate) use to_array_2x3_in_vec2 as to_array_2x3;
}
}
impl_matrix_conversion!(i64, i64x2, i64x4);
}
}
pub(crate) mod diagonal {
use crate::simd::utils::{ComputeVector4, swizzle};
#[inline(always)]
pub(crate) fn diagonal2x2<Tx4: ComputeVector4>(a: Tx4) -> Tx4::Vector2 { swizzle!(a, [0, 3]) }
#[inline(always)]
pub(crate) fn diagonal3x3<Tx4: ComputeVector4>(a: [Tx4; 3]) -> Tx4 {
cfg_select! {
all(target_feature = "neon", target_arch = "aarch64") => {
let temp = swizzle!(a[0], a[1], [0, 5, 2, 3]);
swizzle!(temp, a[2], [0, 1, 6, 3])
}
_ => {
let temp = swizzle!(a[0], a[1], [0, 4, 1, 5]);
swizzle!(temp, a[2], [0, 3, 6, _])
}
}
}
#[inline(always)]
pub(crate) fn diagonal4x4<Tx4: ComputeVector4>(a: [Tx4; 4]) -> Tx4 {
let xy = swizzle!(a[0], a[1], [0, 5, 2, 3]);
let xyz = swizzle!(xy, a[2], [0, 1, 6, 3]);
swizzle!(xyz, a[3], [0, 1, 2, 7])
}
}
pub(crate) mod transpose {
use crate::simd::utils::{ComputeVector2, ComputeVector4, Simd2Ext, swizzle};
#[inline(always)]
pub(crate) fn transpose1x1<T>(a: T) -> T { a }
#[inline(always)]
pub(crate) fn transpose2x1<Tx2>(a: Tx2) -> Tx2 { a }
#[inline(always)]
pub(crate) fn transpose3x1<Tx4>(a: Tx4) -> Tx4 { a }
#[inline(always)]
pub(crate) fn transpose4x1<Tx4>(a: Tx4) -> Tx4 { a }
#[inline(always)]
pub(crate) fn transpose1x2<Tx2>(a: Tx2) -> Tx2 { a }
#[inline(always)]
pub(crate) fn transpose2x2<Tx4: ComputeVector4>(a: Tx4) -> Tx4 { swizzle!(a, [0, 2, 1, 3]) }
#[inline(always)]
pub(crate) fn transpose3x2_in_vec4<Tx4: ComputeVector4>(a: [Tx4; 2]) -> [Tx4; 2] {
transpose4x2(a)
}
#[allow(dead_code)]
#[inline(always)]
pub(crate) fn transpose3x2_in_vec2<Tx4: ComputeVector4>(a: [Tx4; 2]) -> [Tx4::Vector2; 3] {
let [c0, c1] = a;
[swizzle!(c0, c1, [0, 4]), swizzle!(c0, c1, [1, 5]), swizzle!(c0, c1, [2, 6])]
}
#[inline(always)]
pub(crate) fn transpose4x2<Tx4: ComputeVector4>(a: [Tx4; 2]) -> [Tx4; 2] {
[swizzle!(a[0], a[1], [0, 4, 1, 5]), swizzle!(a[0], a[1], [2, 6, 3, 7])]
}
#[inline(always)]
pub(crate) fn transpose1x3<Tx4>(a: Tx4) -> Tx4 { a }
#[inline(always)]
pub(crate) fn transpose2x3_in_vec4<Tx4: ComputeVector4>(a: [Tx4; 2]) -> [Tx4; 2] {
transpose2x4(a)
}
#[allow(dead_code)]
#[inline(always)]
pub(crate) fn transpose2x3_in_vec2<Tx2: ComputeVector2>(a: [Tx2; 3]) -> [Tx2::Vector4; 2] {
let [c0, c1, c2] = a;
let first_two: Tx2::Vector4 = swizzle!(c0, c1, @concat);
let third = c2.widen();
[swizzle!(first_two, third, [0, 2, 4, _]), swizzle!(first_two, third, [1, 3, 5, _])]
}
#[inline(always)]
pub(crate) fn transpose3x3<Tx4: ComputeVector4>(a: [Tx4; 3]) -> [Tx4; 3] {
let ab_lo = swizzle!(a[0], a[1], [0, 4, 1, 5]);
let ab_hi = swizzle!(a[0], a[1], [2, 6, 3, 7]);
[
swizzle!(ab_lo, a[2], [0, 1, 4, _]),
swizzle!(ab_lo, a[2], [2, 3, 5, _]),
swizzle!(ab_hi, a[2], [0, 1, 6, _]),
]
}
#[inline(always)]
pub(crate) fn transpose4x3<Tx4: ComputeVector4>(a: [Tx4; 3]) -> [Tx4; 4] {
let ab_lo = swizzle!(a[0], a[1], [0, 4, 1, 5]);
let ab_hi = swizzle!(a[0], a[1], [2, 6, 3, 7]);
[
swizzle!(ab_lo, a[2], [0, 1, 4, _]),
swizzle!(ab_lo, a[2], [2, 3, 5, _]),
swizzle!(ab_hi, a[2], [0, 1, 6, _]),
swizzle!(ab_hi, a[2], [2, 3, 7, _]),
]
}
#[inline(always)]
pub(crate) fn transpose1x4<Tx4>(a: Tx4) -> Tx4 { a }
#[inline(always)]
pub(crate) fn transpose2x4<Tx4: ComputeVector4>(a: [Tx4; 2]) -> [Tx4; 2] {
[swizzle!(a[0], a[1], [0, 2, 4, 6]), swizzle!(a[0], a[1], [1, 3, 5, 7])]
}
#[inline(always)]
pub(crate) fn transpose3x4<Tx4: ComputeVector4>(a: [Tx4; 4]) -> [Tx4; 3] {
let ab_lo = swizzle!(a[0], a[1], [0, 4, 1, 5]);
let ab_hi = swizzle!(a[0], a[1], [2, 6, 3, 7]);
let cd_lo = swizzle!(a[2], a[3], [0, 4, 1, 5]);
let cd_hi = swizzle!(a[2], a[3], [2, 6, 3, 7]);
[
swizzle!(ab_lo, cd_lo, [0, 1, 4, 5]),
swizzle!(ab_lo, cd_lo, [2, 3, 6, 7]),
swizzle!(ab_hi, cd_hi, [0, 1, 4, 5]),
]
}
#[inline(always)]
pub(crate) fn transpose4x4<Tx4: ComputeVector4>(a: [Tx4; 4]) -> [Tx4; 4] {
let [col0, col1, col2, col3] = a;
let cols01_even = swizzle!(col0, col1, [0, 4, 2, 6]);
let cols23_even = swizzle!(col2, col3, [0, 4, 2, 6]);
let cols01_odd = swizzle!(col0, col1, [1, 5, 3, 7]);
let cols23_odd = swizzle!(col2, col3, [1, 5, 3, 7]);
[
swizzle!(cols01_even, cols23_even, [0, 1, 4, 5]),
swizzle!(cols01_odd, cols23_odd, [0, 1, 4, 5]),
swizzle!(cols01_even, cols23_even, [2, 3, 6, 7]),
swizzle!(cols01_odd, cols23_odd, [2, 3, 6, 7]),
]
}
pub(crate) mod _32bit {
pub(crate) use super::{
transpose2x3_in_vec4 as transpose2x3,
transpose3x2_in_vec4 as transpose3x2,
*,
};
}
#[cfg(target_feature = "avx2")]
pub(crate) mod _64bit {
pub(crate) use super::{
transpose2x3_in_vec4 as transpose2x3,
transpose3x2_in_vec4 as transpose3x2,
*,
};
}
#[cfg(not(target_feature = "avx2"))]
pub(crate) mod _64bit {
pub(crate) use super::{
transpose2x3_in_vec2 as transpose2x3,
transpose3x2_in_vec2 as transpose3x2,
*,
};
}
}
pub(crate) mod inverse {
#![allow(unused_parens)]
use super::{determinant, transpose::transpose3x3};
use crate::{
simd::utils::{sign, swizzle},
utils::arith,
};
macro_rules! impl_inverse {
($scalar:ident, $vec4:ident) => {
#[inline(always)]
pub(crate) fn _1x1(a: $scalar) -> $scalar { 1. / a }
#[inline(always)]
pub(crate) fn _2x2(a: $vec4) -> $vec4 {
let adj = sign!(swizzle!(a, [3, 1, 2, 0]), [+, -, -, +]);
adj / determinant::$scalar::_2x2(a)
}
#[inline(always)]
pub(crate) fn _3x3(a: [$vec4; 3]) -> [$vec4; 3] {
let [r0, r1, r2] = a;
let r0_yzx = swizzle!(r0, [1, 2, 0, _]);
let r1_yzx = swizzle!(r1, [1, 2, 0, _]);
let r2_yzx = swizzle!(r2, [1, 2, 0, _]);
let c0 = arith!(r1 * r2_yzx - r1_yzx * r2);
let c1 = arith!(r2 * r0_yzx - r2_yzx * r0);
let c2 = arith!(r0 * r1_yzx - r0_yzx * r1);
let det = matmul1x3x1(c0, swizzle!(r0, [2, 0, 1, _]));
let r_det = $vec4::splat(1.) / det;
let [r2, r0, r1] = transpose3x3([c0 * r_det, c1 * r_det, c2 * r_det]);
[r0, r1, r2]
}
#[inline(always)]
pub(crate) fn _4x4(a: [$vec4; 4]) -> [$vec4; 4] {
#[inline(always)]
fn mat2_adj_mul(a: $vec4, b: $vec4) -> $vec4 {
arith!(
(swizzle!(a, [3, 3, 0, 0])) * b
- (swizzle!(a, [1, 1, 2, 2])) * (swizzle!(b, [2, 3, 0, 1]))
)
}
#[inline(always)]
fn mat2_mul_adj(a: $vec4, b: $vec4) -> $vec4 {
arith!(
a * (swizzle!(b, [3, 0, 3, 0]))
- (swizzle!(a, [1, 0, 3, 2])) * (swizzle!(b, [2, 1, 2, 1]))
)
}
let a00 = swizzle!(a[0], a[1], [0, 1, 4, 5]);
let b00 = swizzle!(a[0], a[1], [2, 3, 6, 7]);
let c00 = swizzle!(a[2], a[3], [0, 1, 4, 5]);
let d00 = swizzle!(a[2], a[3], [2, 3, 6, 7]);
let det_sub = arith!(
(swizzle!(a[0], a[2], [0, 2, 4, 6])) * (swizzle!(a[1], a[3], [1, 3, 5, 7]))
- (swizzle!(a[0], a[2], [1, 3, 5, 7])) * (swizzle!(a[1], a[3], [0, 2, 4, 6]))
);
let det_a = swizzle!(det_sub, [0, 0, 0, 0]);
let det_b = swizzle!(det_sub, [1, 1, 1, 1]);
let det_c = swizzle!(det_sub, [2, 2, 2, 2]);
let det_d = swizzle!(det_sub, [3, 3, 3, 3]);
let d_adj_c = mat2_adj_mul(d00, c00);
let a_adj_b = mat2_adj_mul(a00, b00);
let x_ = arith!(det_d * a00 - (matmul2x2x2(d_adj_c, b00)));
let z_ = arith!(det_c * b00 - (mat2_mul_adj(a00, d_adj_c)));
let w_ = arith!(det_a * d00 - (matmul2x2x2(a_adj_b, c00)));
let y_ = arith!(det_b * c00 - (mat2_mul_adj(d00, a_adj_b)));
let tr_terms = a_adj_b * swizzle!(d_adj_c, [0, 2, 1, 3]);
let tr_pair = tr_terms + swizzle!(tr_terms, [1, 0, 3, 2]);
let tr = tr_pair + swizzle!(tr_pair, [2, 3, 0, 1]);
let det_m = arith!(det_a * det_d + (arith!(det_b * det_c - tr)));
let r_det = reciprocal_determinant(det_m);
let x_ = x_ * r_det;
let y_ = y_ * r_det;
let z_ = z_ * r_det;
let w_ = w_ * r_det;
[
swizzle!(x_, y_, [3, 1, 7, 5]),
swizzle!(x_, y_, [2, 0, 6, 4]),
swizzle!(z_, w_, [3, 1, 7, 5]),
swizzle!(z_, w_, [2, 0, 6, 4]),
]
}
};
}
pub(crate) mod f32 {
use super::{
super::matmul::f32::{matmul1x3x1, matmul2x2x2},
*,
};
use wide::f32x4;
#[inline(always)]
fn reciprocal_determinant(det_m: f32x4) -> f32x4 { f32x4::new([1., -1., -1., 1.]) / det_m }
impl_inverse!(f32, f32x4);
}
pub(crate) mod f64 {
use super::{
super::matmul::f64::{matmul1x3x1, matmul2x2x2},
*,
};
use wide::f64x4;
#[inline(always)]
fn reciprocal_determinant(det_m: f64x4) -> f64x4 {
cfg_select! {
target_feature = "avx2" => f64x4::new([1., -1., -1., 1.]) / det_m,
_ => {
let numerator = f64x4::new([1., -1., -1., 1.]);
let half = swizzle!(numerator, [0, 1]) / swizzle!(det_m, [0, 1]);
swizzle!(half, [0, 1, 1, 0])
}
}
}
impl_inverse!(f64, f64x4);
}
}
pub(crate) mod determinant {
#![allow(unused_parens)]
use super::*;
use crate::{simd::utils::swizzle, utils::arith};
macro_rules! impl_determinant {
($scalar:ident, $vec4:ident) => {
#[inline(always)]
pub(crate) fn _2x2(a: $vec4) -> $scalar {
let [a, b, c, d] = a.to_array();
arith!(a * d - b * c)
}
#[inline(always)]
pub(crate) fn _3x3(a: [$vec4; 3]) -> $scalar {
let [r0, r1, r2] = a;
let r1_yzx = swizzle!(r1, [1, 2, 0, _]);
let r2_yzx = swizzle!(r2, [1, 2, 0, _]);
let c0 = arith!(r1 * r2_yzx - r1_yzx * r2);
matmul::$scalar::matmul1x3x1(c0, swizzle!(r0, [2, 0, 1, _]))
}
#[inline(always)]
pub(crate) fn _4x4(a: [$vec4; 4]) -> $scalar {
#[inline(always)]
fn mat2_adj_mul(a: $vec4, b: $vec4) -> $vec4 {
arith!(
(swizzle!(a, [3, 3, 0, 0])) * b
- (swizzle!(a, [1, 1, 2, 2])) * (swizzle!(b, [2, 3, 0, 1]))
)
}
let a00 = swizzle!(a[0], a[1], [0, 1, 4, 5]);
let b00 = swizzle!(a[0], a[1], [2, 3, 6, 7]);
let c00 = swizzle!(a[2], a[3], [0, 1, 4, 5]);
let d00 = swizzle!(a[2], a[3], [2, 3, 6, 7]);
let det_sub = arith!(
(swizzle!(a[0], a[2], [0, 2, 4, 6])) * (swizzle!(a[1], a[3], [1, 3, 5, 7]))
- (swizzle!(a[0], a[2], [1, 3, 5, 7]))
* (swizzle!(a[1], a[3], [0, 2, 4, 6]))
);
let d_adj_c = mat2_adj_mul(d00, c00);
let a_adj_b = mat2_adj_mul(a00, b00);
let det_a = swizzle!(det_sub, [0, 0, 0, 0]);
let det_b = swizzle!(det_sub, [1, 1, 1, 1]);
let det_c = swizzle!(det_sub, [2, 2, 2, 2]);
let det_d = swizzle!(det_sub, [3, 3, 3, 3]);
let tr_terms = a_adj_b * swizzle!(d_adj_c, [0, 2, 1, 3]);
let tr_pair = tr_terms + swizzle!(tr_terms, [1, 0, 3, 2]);
let tr = tr_pair + swizzle!(tr_pair, [2, 3, 0, 1]);
let det_m = arith!(det_a * det_d + (arith!(det_b * det_c - tr)));
det_m.to_array()[0]
}
};
}
pub(crate) mod f32 {
use super::*;
use wide::f32x4;
impl_determinant!(f32, f32x4);
}
pub(crate) mod f64 {
use super::*;
use wide::f64x4;
impl_determinant!(f64, f64x4);
}
}
pub(crate) mod index {
use crate::utils::ArithOps;
macro_rules! impl_index {
($get:ident, $as_array:ident $(, $mut:tt)?) => {
#[inline(always)]
pub(crate) fn _1x1<T: Copy>(a: & $($mut)? T, (i, j): (usize, usize)) -> Option<& $($mut)? T> {
if i == 0 && j == 0 { Some(a) } else { None }
}
#[inline(always)]
pub(crate) fn _2x1<T, Tx4: ArithOps<Scalar = T>>(
a: & $($mut)? Tx4,
(i, j): (usize, usize),
) -> Option<& $($mut)? T> {
if j == 0 && i < 2 { a.$as_array().$get(i) } else { None }
}
#[inline(always)]
pub(crate) fn _3x1<T, Tx4: ArithOps<Scalar = T>>(
a: & $($mut)? Tx4,
(i, j): (usize, usize),
) -> Option<& $($mut)? T> {
if j == 0 && i < 3 { a.$as_array().$get(i) } else { None }
}
#[inline(always)]
pub(crate) fn _4x1<T, Tx4: ArithOps<Scalar = T>>(
a: & $($mut)? Tx4,
(i, j): (usize, usize),
) -> Option<& $($mut)? T> {
if j == 0 { a.$as_array().$get(i) } else { None }
}
#[inline(always)]
pub(crate) fn _1x2<T, Tx4: ArithOps<Scalar = T>>(
a: & $($mut)? Tx4,
(i, j): (usize, usize),
) -> Option<& $($mut)? T> {
if j < 2 && i == 0 { a.$as_array().$get(j) } else { None }
}
#[inline(always)]
pub(crate) fn _2x2<T, Tx4: ArithOps<Scalar = T>>(
a: & $($mut)? Tx4,
(i, j): (usize, usize),
) -> Option<& $($mut)? T> {
if i < 2 && j < 2 { a.$as_array().$get(2 * j + i) } else { None }
}
#[inline(always)]
pub(crate) fn _3x2<T, Tx4: ArithOps<Scalar = T>>(
a: & $($mut)? [Tx4; 2],
(i, j): (usize, usize),
) -> Option<& $($mut)? T> {
match a.$get(j) {
Some(column) if i < 3 => column.$as_array().$get(i),
_ => None,
}
}
#[inline(always)]
pub(crate) fn _4x2<T, Tx4: ArithOps<Scalar = T>>(
a: & $($mut)? [Tx4; 2],
(i, j): (usize, usize),
) -> Option<& $($mut)? T> {
match a.$get(j) {
Some(column) => column.$as_array().$get(i),
_ => None,
}
}
#[inline(always)]
pub(crate) fn _1x3<T, Tx4: ArithOps<Scalar = T>>(
a: & $($mut)? Tx4,
(i, j): (usize, usize),
) -> Option<& $($mut)? T> {
if j < 3 && i == 0 { a.$as_array().$get(j) } else { None }
}
#[inline(always)]
pub(crate) fn _2x3_in_vec4<T, Tx4: ArithOps<Scalar = T>>(
a: & $($mut)? [Tx4; 2],
(i, j): (usize, usize),
) -> Option<& $($mut)? T> {
if j < 3 && i < 2 {
if let Some(chunk) = a.$get(j / 2) {
return chunk.$as_array().$get((2 * j + i) % 4);
}
}
None
}
#[inline(always)]
pub(crate) fn _2x3_in_vec2<T, Tx2: ArithOps<Scalar = T>>(
a: & $($mut)? [Tx2; 3],
(i, j): (usize, usize),
) -> Option<& $($mut)? T> {
match a.$get(j) {
Some(column) => column.$as_array().$get(i),
_ => None,
}
}
#[inline(always)]
pub(crate) fn _3x3<T, Tx4: ArithOps<Scalar = T>>(
a: & $($mut)? [Tx4; 3],
(i, j): (usize, usize),
) -> Option<& $($mut)? T> {
match a.$get(j) {
Some(column) if i < 3 => column.$as_array().$get(i),
_ => None,
}
}
#[inline(always)]
pub(crate) fn _4x3<T, Tx4: ArithOps<Scalar = T>>(
a: & $($mut)? [Tx4; 3],
(i, j): (usize, usize),
) -> Option<& $($mut)? T> {
match a.$get(j) {
Some(column) => column.$as_array().$get(i),
_ => None,
}
}
#[inline(always)]
pub(crate) fn _1x4<T, Tx4: ArithOps<Scalar = T>>(
a: & $($mut)? Tx4,
(i, j): (usize, usize),
) -> Option<& $($mut)? T> {
if i == 0 { a.$as_array().$get(j) } else { None }
}
#[inline(always)]
pub(crate) fn _2x4<T, Tx4: ArithOps<Scalar = T>>(
a: & $($mut)? [Tx4; 2],
(i, j): (usize, usize),
) -> Option<& $($mut)? T> {
if j < 4 && i < 2 {
if let Some(chunk) = a.$get(j / 2) {
return chunk.$as_array().$get((2 * j + i) % 4);
}
}
None
}
#[inline(always)]
pub(crate) fn _3x4<T, Tx4: ArithOps<Scalar = T>>(
a: & $($mut)? [Tx4; 4],
(i, j): (usize, usize),
) -> Option<& $($mut)? T> {
match a.$get(j) {
Some(column) if i < 3 => column.$as_array().$get(i),
_ => None,
}
}
#[inline(always)]
pub(crate) fn _4x4<T, Tx4: ArithOps<Scalar = T>>(
a: & $($mut)? [Tx4; 4],
(i, j): (usize, usize),
) -> Option<& $($mut)? T> {
match a.$get(j) {
Some(column) => column.$as_array().$get(i),
_ => None,
}
}
pub(crate) mod _32bit {
pub(crate) use super::{
_2x3_in_vec4 as _2x3,
*,
};
}
#[cfg(target_feature = "avx2")]
pub(crate) mod _64bit {
pub(crate) use super::{
_2x3_in_vec4 as _2x3,
*,
};
}
#[cfg(not(target_feature = "avx2"))]
pub(crate) mod _64bit {
pub(crate) use super::{
_2x3_in_vec2 as _2x3,
*,
};
}
};
}
pub(super) use impl_index;
impl_index!(get, as_array_);
}
pub(crate) mod index_mut {
use crate::utils::ArithOps;
super::index::impl_index!(get_mut, as_mut_array_, mut);
}
pub(crate) mod to_array {
#[inline(always)]
pub fn __3x1<T, Tx4: Copy + Into<[T; 4]>>(a: Tx4) -> [T; 3] {
let [a, b, c, _] = a.into();
[a, b, c]
}
#[inline(always)]
pub(crate) fn _1x1<T: Copy>(a: T) -> [[T; 1]; 1] { [[a]] }
#[inline(always)]
pub(crate) fn _2x1<T, Tx2: Copy + Into<[T; 2]>>(a: Tx2) -> [[T; 2]; 1] {
let [a, b] = a.into();
[[a, b]]
}
#[inline(always)]
pub(crate) fn _3x1<T, Tx4: Copy + Into<[T; 4]>>(a: Tx4) -> [[T; 3]; 1] { [__3x1(a)] }
#[inline(always)]
pub(crate) fn _4x1<T, Tx4: Copy + Into<[T; 4]>>(a: Tx4) -> [[T; 4]; 1] { [a.into()] }
#[inline(always)]
pub(crate) fn _1x2<T, Tx2: Copy + Into<[T; 2]>>(a: Tx2) -> [[T; 1]; 2] {
let [a, b] = a.into();
[[a], [b]]
}
#[inline(always)]
pub(crate) fn _2x2<T, Tx4: Copy + Into<[T; 4]>>(a: Tx4) -> [[T; 2]; 2] {
let [a, b, c, d] = a.into();
[[a, b], [c, d]]
}
#[inline(always)]
pub(crate) fn _3x2<T, Tx4: Copy + Into<[T; 4]>>(a: [Tx4; 2]) -> [[T; 3]; 2] {
[__3x1(a[0]), __3x1(a[1])]
}
#[inline(always)]
pub(crate) fn _4x2<T, Tx4: Copy + Into<[T; 4]>>(a: [Tx4; 2]) -> [[T; 4]; 2] {
[a[0].into(), a[1].into()]
}
#[inline(always)]
pub(crate) fn _1x3<T, Tx4: Copy + Into<[T; 4]>>(a: Tx4) -> [[T; 1]; 3] {
let [a, b, c, _] = a.into();
[[a], [b], [c]]
}
#[inline(always)]
pub(crate) fn _2x3_in_vec4<T, Tx4: Copy + Into<[T; 4]>>(x: [Tx4; 2]) -> [[T; 2]; 3] {
let [a, b, c, d] = x[0].into();
let [e, f, _, _] = x[1].into();
[[a, b], [c, d], [e, f]]
}
#[inline(always)]
pub(crate) fn _2x3_in_vec2<T, Tx2: Copy + Into<[T; 2]>>(x: [Tx2; 3]) -> [[T; 2]; 3] {
x.map(Into::into)
}
#[inline(always)]
pub(crate) fn _3x3<T, Tx4: Copy + Into<[T; 4]>>(a: [Tx4; 3]) -> [[T; 3]; 3] {
[__3x1(a[0]), __3x1(a[1]), __3x1(a[2])]
}
#[inline(always)]
pub(crate) fn _4x3<T, Tx4: Copy + Into<[T; 4]>>(a: [Tx4; 3]) -> [[T; 4]; 3] {
[a[0].into(), a[1].into(), a[2].into()]
}
#[inline(always)]
pub(crate) fn _1x4<T, Tx4: Copy + Into<[T; 4]>>(a: Tx4) -> [[T; 1]; 4] {
let [a, b, c, d] = a.into();
[[a], [b], [c], [d]]
}
#[inline(always)]
pub(crate) fn _2x4<T, Tx4: Copy + Into<[T; 4]>>(x: [Tx4; 2]) -> [[T; 2]; 4] {
let [a, b, c, d] = x[0].into();
let [e, f, g, h] = x[1].into();
[[a, b], [c, d], [e, f], [g, h]]
}
#[inline(always)]
pub(crate) fn _3x4<T, Tx4: Copy + Into<[T; 4]>>(a: [Tx4; 4]) -> [[T; 3]; 4] {
[__3x1(a[0]), __3x1(a[1]), __3x1(a[2]), __3x1(a[3])]
}
#[inline(always)]
pub(crate) fn _4x4<T, Tx4: Copy + Into<[T; 4]>>(a: [Tx4; 4]) -> [[T; 4]; 4] {
[a[0].into(), a[1].into(), a[2].into(), a[3].into()]
}
pub(crate) mod _32bit {
pub(crate) use super::{_2x3_in_vec4 as _2x3, *};
}
#[cfg(target_feature = "avx2")]
pub(crate) mod _64bit {
pub(crate) use super::{_2x3_in_vec4 as _2x3, *};
}
#[cfg(not(target_feature = "avx2"))]
pub(crate) mod _64bit {
pub(crate) use super::{_2x3_in_vec2 as _2x3, *};
}
}
pub(crate) mod from_array {
macro_rules! impl_fns {
($f32:ty, $x2:ty, $x4:ty) => {
#[inline(always)]
pub(crate) const fn _1x1([a]: [[$f32; 1]; 1]) -> $f32 { a[0] }
#[inline(always)]
pub(crate) const fn _2x1([a]: [[$f32; 2]; 1]) -> $x2 { <$x2>::new([a[0], a[1]]) }
#[inline(always)]
pub(crate) const fn _3x1([a]: [[$f32; 3]; 1]) -> $x4 {
<$x4>::new([a[0], a[1], a[2], 0 as _])
}
#[inline(always)]
pub(crate) const fn _4x1([a]: [[$f32; 4]; 1]) -> $x4 { <$x4>::new(a) }
#[inline(always)]
pub(crate) const fn _1x2([[a], [b]]: [[$f32; 1]; 2]) -> $x2 { <$x2>::new([a, b]) }
#[inline(always)]
pub(crate) const fn _2x2([a, b]: [[$f32; 2]; 2]) -> $x4 {
<$x4>::new([a[0], a[1], b[0], b[1]])
}
#[inline(always)]
pub(crate) const fn _3x2([a, b]: [[$f32; 3]; 2]) -> [$x4; 2] { [_3x1([a]), _3x1([b])] }
#[inline(always)]
pub(crate) const fn _4x2(a: [[$f32; 4]; 2]) -> [$x4; 2] {
[<$x4>::new(a[0]), <$x4>::new(a[1])]
}
#[inline(always)]
pub(crate) const fn _1x3(a: [[$f32; 1]; 3]) -> $x4 {
<$x4>::new([a[0][0], a[1][0], a[2][0], 0 as _])
}
#[inline(always)]
pub(crate) const fn _2x3_in_vec4([a, b, c]: [[$f32; 2]; 3]) -> [$x4; 2] {
[<$x4>::new([a[0], a[1], b[0], b[1]]), <$x4>::new([c[0], c[1], 0 as _, 0 as _])]
}
#[inline(always)]
pub(crate) const fn _2x3_in_vec2([a, b, c]: [[$f32; 2]; 3]) -> [$x2; 3] {
[<$x2>::new(a), <$x2>::new(b), <$x2>::new(c)]
}
#[inline(always)]
pub(crate) const fn _3x3(a: [[$f32; 3]; 3]) -> [$x4; 3] {
[_3x1([a[0]]), _3x1([a[1]]), _3x1([a[2]])]
}
#[inline(always)]
pub(crate) const fn _4x3(a: [[$f32; 4]; 3]) -> [$x4; 3] {
[<$x4>::new(a[0]), <$x4>::new(a[1]), <$x4>::new(a[2])]
}
#[inline(always)]
pub(crate) const fn _1x4(a: [[$f32; 1]; 4]) -> $x4 {
<$x4>::new([a[0][0], a[1][0], a[2][0], a[3][0]])
}
#[inline(always)]
pub(crate) const fn _2x4(x: [[$f32; 2]; 4]) -> [$x4; 2] {
[
<$x4>::new([x[0][0], x[0][1], x[1][0], x[1][1]]),
<$x4>::new([x[2][0], x[2][1], x[3][0], x[3][1]]),
]
}
#[inline(always)]
pub(crate) const fn _3x4(a: [[$f32; 3]; 4]) -> [$x4; 4] {
[_3x1([a[0]]), _3x1([a[1]]), _3x1([a[2]]), _3x1([a[3]])]
}
#[inline(always)]
pub(crate) const fn _4x4(a: [[$f32; 4]; 4]) -> [$x4; 4] {
[<$x4>::new(a[0]), <$x4>::new(a[1]), <$x4>::new(a[2]), <$x4>::new(a[3])]
}
};
}
macro_rules! select_2x3 {
() => {
cfg_select! {
target_feature = "avx2" => {
pub(crate) use _2x3_in_vec4 as _2x3;
}
_ => {
pub(crate) use _2x3_in_vec2 as _2x3;
}
}
};
}
pub(crate) mod f32 {
use crate::simd::utils::f32x2;
pub(crate) use _2x3_in_vec4 as _2x3;
use wide::f32x4;
impl_fns!(f32, f32x2, f32x4);
}
pub(crate) mod f64 {
use wide::{f64x2, f64x4};
select_2x3!();
impl_fns!(f64, f64x2, f64x4);
}
pub(crate) mod i32 {
use crate::simd::utils::i32x2;
pub(crate) use _2x3_in_vec4 as _2x3;
use wide::i32x4;
impl_fns!(i32, i32x2, i32x4);
}
pub(crate) mod i64 {
use wide::{i64x2, i64x4};
select_2x3!();
impl_fns!(i64, i64x2, i64x4);
}
pub(crate) mod u32 {
use crate::simd::utils::u32x2;
pub(crate) use _2x3_in_vec4 as _2x3;
use wide::u32x4;
impl_fns!(u32, u32x2, u32x4);
}
pub(crate) mod u64 {
use wide::{u64x2, u64x4};
select_2x3!();
impl_fns!(u64, u64x2, u64x4);
}
}
pub(crate) mod from_vecs {
macro_rules! impl_fns {
($f32:ty, $x2:ty, $x4:ty) => {
#[inline(always)]
pub(crate) fn _1x1([a]: [Vector<$f32, 1>; 1]) -> $f32 { a.storage }
#[inline(always)]
pub(crate) fn _2x1([a]: [Vector<$f32, 2>; 1]) -> $x2 { a.storage }
#[inline(always)]
pub(crate) fn _3x1([a]: [Vector<$f32, 3>; 1]) -> $x4 { a.storage }
#[inline(always)]
pub(crate) fn _4x1([a]: [Vector<$f32, 4>; 1]) -> $x4 { a.storage }
#[inline(always)]
pub(crate) fn _1x2([a, b]: [Vector<$f32, 1>; 2]) -> $x2 {
<$x2>::new([a.storage, b.storage])
}
#[inline(always)]
pub(crate) fn _2x2([a, b]: [Vector<$f32, 2>; 2]) -> $x4 {
swizzle!(a.storage.load(), b.storage.load(), @concat)
}
#[inline(always)]
pub(crate) fn _3x2([a, b]: [Vector<$f32, 3>; 2]) -> [$x4; 2] { [a.storage, b.storage] }
#[inline(always)]
pub(crate) fn _4x2([a, b]: [Vector<$f32, 4>; 2]) -> [$x4; 2] { [a.storage, b.storage] }
#[inline(always)]
pub(crate) fn _1x3([a, b, c]: [Vector<$f32, 1>; 3]) -> $x4 {
<$x4>::new([a.storage, b.storage, c.storage, 0 as _])
}
#[inline(always)]
pub(crate) fn _2x3_in_vec4([a, b, c]: [Vector<$f32, 2>; 3]) -> [$x4; 2] {
[_2x2([a, b]), c.storage.load().widen()]
}
#[inline(always)]
pub(crate) fn _2x3_in_vec2([a, b, c]: [Vector<$f32, 2>; 3]) -> [$x2; 3] {
[a.storage, b.storage, c.storage]
}
#[inline(always)]
pub(crate) fn _3x3([a, b, c]: [Vector<$f32, 3>; 3]) -> [$x4; 3] {
[a.storage, b.storage, c.storage]
}
#[inline(always)]
pub(crate) fn _4x3([a, b, c]: [Vector<$f32, 4>; 3]) -> [$x4; 3] {
[a.storage, b.storage, c.storage]
}
#[inline(always)]
pub(crate) fn _1x4([a, b, c, d]: [Vector<$f32, 1>; 4]) -> $x4 {
<$x4>::new([a.storage, b.storage, c.storage, d.storage])
}
#[inline(always)]
pub(crate) fn _2x4([a, b, c, d]: [Vector<$f32, 2>; 4]) -> [$x4; 2] {
[_2x2([a, b]), _2x2([c, d])]
}
#[inline(always)]
pub(crate) fn _3x4([a, b, c, d]: [Vector<$f32, 3>; 4]) -> [$x4; 4] {
[a.storage, b.storage, c.storage, d.storage]
}
#[inline(always)]
pub(crate) fn _4x4([a, b, c, d]: [Vector<$f32, 4>; 4]) -> [$x4; 4] {
[a.storage, b.storage, c.storage, d.storage]
}
};
}
macro_rules! select_2x3 {
() => {
cfg_select! {
target_feature = "avx2" => {
pub(crate) use _2x3_in_vec4 as _2x3;
}
_ => {
pub(crate) use _2x3_in_vec2 as _2x3;
}
}
};
}
use crate::{
Vector,
simd::utils::{Simd2Ext, f32x2, i32x2, swizzle, u32x2},
utils::Load,
};
use wide::{f32x4, f64x2, f64x4, i32x4, i64x2, i64x4, u32x4, u64x2, u64x4};
pub(crate) mod f32 {
use super::*;
pub(crate) use _2x3_in_vec4 as _2x3;
impl_fns!(f32, f32x2, f32x4);
}
pub(crate) mod f64 {
use super::*;
select_2x3!();
impl_fns!(f64, f64x2, f64x4);
}
pub(crate) mod i32 {
use super::*;
pub(crate) use _2x3_in_vec4 as _2x3;
impl_fns!(i32, i32x2, i32x4);
}
pub(crate) mod i64 {
use super::*;
select_2x3!();
impl_fns!(i64, i64x2, i64x4);
}
pub(crate) mod u32 {
use super::*;
pub(crate) use _2x3_in_vec4 as _2x3;
impl_fns!(u32, u32x2, u32x4);
}
pub(crate) mod u64 {
use super::*;
select_2x3!();
impl_fns!(u64, u64x2, u64x4);
}
}
pub(crate) mod cast {
#[allow(unused_imports)]
use crate::arch::*;
#[allow(unused_imports)]
use crate::{
simd::utils::{f32x2, i32x2, u32x2},
utils::{Load, Store},
};
#[allow(unused_imports)]
use wide::{bytemuck::cast, f32x4, i16x8, i32x4, u8x16, u32x4};
use wide::{f64x2, f64x4, i64x2, i64x4, u64x2, u64x4};
#[allow(unused_imports)]
use crate::simd::utils::{Simd2Ext, swizzle};
#[cfg(target_feature = "simd128")]
use core::arch::wasm32::{
f32x4_convert_u32x4,
f32x4_demote_f64x2_zero,
f64x2_convert_low_i32x4,
f64x2_convert_low_u32x4,
f64x2_promote_low_f32x4,
i32x4_trunc_sat_f64x2_zero,
i64x2_extend_high_i32x4,
i64x2_extend_low_i32x4,
u32x4_shuffle,
u32x4_trunc_sat_f32x4,
u32x4_trunc_sat_f64x2_zero,
u64x2_extend_high_u32x4,
u64x2_extend_low_u32x4,
v128,
};
#[cfg(all(target_feature = "neon", target_arch = "aarch64"))]
use std::arch::aarch64::*;
#[rustfmt::skip]
#[allow(unused_macros)]
macro_rules! join_64bit {
($n:expr, $low:expr, $high:expr) => {
if $n <= 2 { Simd2Ext::widen($low) } else { swizzle!($low, $high, @concat) }
};
}
#[allow(unused_macros)]
macro_rules! join_32bit {
($n:expr, $low:expr, $high:expr) => {
if $n <= 2 { $low } else { swizzle!($low, $high, [0, 1, 4, 5]) }
};
}
#[allow(unused_macros)]
macro_rules! pack_mask_64_to_32 {
($mask:expr) => {{
let [low, high] = cast::<f64x4, [i32x4; 2]>($mask);
swizzle!(low, high, [0, 2, 4, 6])
}};
}
#[allow(unused_macros)]
macro_rules! zero_extended_to_f64 {
($widened:expr) => {{
const MAGIC: u64 = 0x4330_0000_0000_0000;
cast::<u64x2, f64x2>($widened | u64x2::splat(MAGIC))
- f64x2::splat(f64::from_bits(MAGIC))
}};
}
#[inline(always)]
pub(crate) fn f32x4_from_f32<const N: usize>(v: f32x4) -> f32x4 { v }
#[inline(always)]
pub(crate) fn i32x4_from_i32<const N: usize>(v: i32x4) -> i32x4 { v }
#[inline(always)]
pub(crate) fn u32x4_from_u32<const N: usize>(v: u32x4) -> u32x4 { v }
#[inline(always)]
pub(crate) fn f64x4_from_f64<const N: usize>(v: f64x4) -> f64x4 { v }
#[inline(always)]
pub(crate) fn i64x4_from_i64<const N: usize>(v: i64x4) -> i64x4 { v }
#[inline(always)]
pub(crate) fn u64x4_from_u64<const N: usize>(v: u64x4) -> u64x4 { v }
pub(crate) use core::convert::{
identity as f64x2_from_f64,
identity as i64x2_from_i64,
identity as u64x2_from_u64,
};
#[inline(always)]
pub(crate) fn f32x4_from_f64<const N: usize>(v: f64x4) -> f32x4 {
cfg_select! {
target_feature = "avx" => {
f32x4::from(unsafe { avx::_mm256_cvtpd_ps(v.into()) })
}
all(target_feature = "neon", target_arch = "aarch64") => {
let low: float64x2_t = swizzle!(v, [0, 1]).into();
unsafe {
let narrowed = vcvt_f32_f64(low);
if N <= 2 {
vcombine_f32(narrowed, vdup_n_f32(0.)).into()
} else {
vcvt_high_f32_f64(narrowed, swizzle!(v, [2, 3]).into()).into()
}
}
}
target_feature = "simd128" => {
let low: v128 = swizzle!(v, [0, 1]).into();
let narrowed = f32x4::from(f32x4_demote_f64x2_zero(low));
join_32bit!(N, narrowed, {
let high: v128 = swizzle!(v, [2, 3]).into();
f32x4::from(f32x4_demote_f64x2_zero(high))
})
}
target_feature = "sse2" => {
let narrowed =
f32x4::from(unsafe { sse2::_mm_cvtpd_ps(swizzle!(v, [0, 1]).into()) });
join_32bit!(
N,
narrowed,
f32x4::from(unsafe { sse2::_mm_cvtpd_ps(swizzle!(v, [2, 3]).into()) })
)
}
_ => {
let a = v.to_array();
f32x4::new(core::array::from_fn(
#[inline(always)]
|i| if i < N { a[i] as f32 } else { 0. },
))
}
}
}
#[inline(always)]
pub(crate) fn f32x2_from_f64(v: f64x2) -> f32x2 {
cfg_select! {
all(target_feature = "neon", target_arch = "aarch64") => {
unsafe { vcvt_f32_f64(v.into()) }.into()
}
target_feature = "simd128" => f32x4::from(f32x4_demote_f64x2_zero(v.into())).store(),
target_feature = "sse2" => f32x4::from(unsafe { sse2::_mm_cvtpd_ps(v.into()) }).store(),
_ => {
let [a, b] = v.to_array();
f32x4::new([a as f32, b as f32, 0., 0.]).store()
}
}
}
#[inline(always)]
pub(crate) fn f32x4_from_i32<const N: usize>(v: i32x4) -> f32x4 { f32x4::from_i32x4(v) }
#[inline(always)]
pub(crate) fn f32x2_from_i32(v: i32x2) -> f32x2 {
cfg_select! {
all(target_feature = "neon", target_arch = "aarch64") => {
unsafe { vcvt_f32_s32(v.into()) }.into()
}
_ => f32x4_from_i32::<2>(v.load()).store(),
}
}
#[inline(always)]
pub(crate) fn f32x4_from_i64<const N: usize>(v: i64x4) -> f32x4 {
cfg_select! {
all(target_feature = "avx512dq", target_feature = "avx512vl") => {
f32x4::from(unsafe { avx512_dq_vl::_mm256_cvtepi64_ps(v.into()) })
}
_ => {
let a = v.to_array();
f32x4::new(core::array::from_fn(
#[inline(always)]
|i| if i < N { a[i] as f32 } else { 0. },
))
}
}
}
#[inline(always)]
pub(crate) fn f32x2_from_i64(v: i64x2) -> f32x2 {
cfg_select! {
all(target_feature = "avx512dq", target_feature = "avx512vl") => {
f32x4::from(unsafe { avx512_dq_vl::_mm_cvtepi64_ps(v.into()) }).store()
}
_ => {
let [a, b] = v.to_array();
f32x2::new([a as f32, b as f32])
}
}
}
#[inline(always)]
pub(crate) fn f32x4_from_u32<const N: usize>(v: u32x4) -> f32x4 {
cfg_select! {
target_feature = "avx512f" => unsafe {
let input: x86_64::__m128i = v.into();
let extended = avx512_f::_mm512_zextsi128_si512(input);
let converted = avx512_f::_mm512_cvtepu32_ps(extended);
let low = avx512_f::_mm512_castps512_ps128(converted);
f32x4::from(low)
},
all(target_feature = "neon", target_arch = "aarch64") => {
unsafe { vcvtq_f32_u32(v.into()) }.into()
}
target_feature = "simd128" => f32x4::from(f32x4_convert_u32x4(v.into())),
_ => {
const MASK: i16x8 = i16x8::new([-1, 0, -1, 0, -1, 0, -1, 0]);
let low =
MASK.select(cast::<_, i16x8>(v), cast::<_, i16x8>(u32x4::splat(0x4b00_0000)));
let high = MASK
.select(cast::<_, i16x8>(v >> 16), cast::<_, i16x8>(u32x4::splat(0x5300_0000)));
cast::<_, f32x4>(low) + (cast::<_, f32x4>(high) - 549_764_200_000.0_f32)
}
}
}
#[inline(always)]
pub(crate) fn f32x2_from_u32(v: u32x2) -> f32x2 {
cfg_select! {
all(target_feature = "neon", target_arch = "aarch64") => {
unsafe { vcvt_f32_u32(v.into()) }.into()
}
_ => f32x4_from_u32::<2>(v.load()).store(),
}
}
#[inline(always)]
pub(crate) fn f32x4_from_u64<const N: usize>(v: u64x4) -> f32x4 {
cfg_select! {
all(target_feature = "avx512dq", target_feature = "avx512vl") => {
f32x4::from(unsafe { avx512_dq_vl::_mm256_cvtepu64_ps(v.into()) })
}
_ => {
let a = v.to_array();
f32x4::new(core::array::from_fn(
#[inline(always)]
|i| if i < N { a[i] as f32 } else { 0. },
))
}
}
}
#[inline(always)]
pub(crate) fn f32x2_from_u64(v: u64x2) -> f32x2 {
cfg_select! {
all(target_feature = "avx512dq", target_feature = "avx512vl") => {
f32x4::from(unsafe { avx512_dq_vl::_mm_cvtepu64_ps(v.into()) }).store()
}
_ => {
let [a, b] = v.to_array();
f32x2::new([a as f32, b as f32])
}
}
}
#[inline(always)]
pub(crate) fn i32x4_from_f32<const N: usize>(v: f32x4) -> i32x4 { f32x4::trunc_int(v) }
#[inline(always)]
pub(crate) fn i32x2_from_f32(v: f32x2) -> i32x2 {
cfg_select! {
all(target_feature = "neon", target_arch = "aarch64") => {
unsafe { vcvt_s32_f32(v.into()) }.into()
}
_ => i32x4_from_f32::<2>(v.load()).store(),
}
}
#[inline(always)]
pub(crate) fn i32x4_from_f64<const N: usize>(v: f64x4) -> i32x4 {
cfg_select! {
target_feature = "avx512vl" => {
use core::arch::x86_64::{_CMP_GE_OQ, _CMP_ORD_Q};
let input: x86_64::__m256d = v.into();
unsafe {
let converted = avx::_mm256_cvttpd_epi32(input);
let overflow = avx512_vl::_mm256_cmp_pd_mask::<_CMP_GE_OQ>(
input,
avx::_mm256_set1_pd(2_147_483_648.),
);
let ordered = avx512_vl::_mm256_cmp_pd_mask::<_CMP_ORD_Q>(input, input);
let saturated = avx512_vl::_mm_mask_mov_epi32(
converted,
overflow,
sse2::_mm_set1_epi32(i32::MAX),
);
i32x4::from(avx512_vl::_mm_maskz_mov_epi32(ordered, saturated))
}
}
all(target_feature = "neon", target_arch = "aarch64") => {
unsafe {
let low = vqmovn_s64(vcvtq_s64_f64(swizzle!(v, [0, 1]).into()));
if N <= 2 {
vcombine_s32(low, vdup_n_s32(0)).into()
} else {
vqmovn_high_s64(low, vcvtq_s64_f64(swizzle!(v, [2, 3]).into())).into()
}
}
}
target_feature = "simd128" => {
let low = i32x4::from(i32x4_trunc_sat_f64x2_zero(swizzle!(v, [0, 1]).into()));
join_32bit!(
N,
low,
i32x4::from(i32x4_trunc_sat_f64x2_zero(swizzle!(v, [2, 3]).into()))
)
}
target_feature = "sse2" => {
let two31 = f64x2::splat(2_147_483_648.);
let low: f64x2 = swizzle!(v, [0, 1]);
let converted_low =
i32x4::from(unsafe { sse2::_mm_cvttpd_epi32((low & low.simd_eq(low)).into()) });
let flip_low = cast::<f64x2, i32x4>(low.simd_ge(two31));
if N <= 2 {
converted_low ^ swizzle!(flip_low, [0, 2, 0, 2])
} else {
let high: f64x2 = swizzle!(v, [2, 3]);
let converted_high = i32x4::from(unsafe {
sse2::_mm_cvttpd_epi32((high & high.simd_eq(high)).into())
});
let flip_high = cast::<f64x2, i32x4>(high.simd_ge(two31));
swizzle!(converted_low, converted_high, [0, 1, 4, 5])
^ swizzle!(flip_low, flip_high, [0, 2, 4, 6])
}
}
_ => {
let a = v.to_array();
i32x4::new(core::array::from_fn(
#[inline(always)]
|i| if i < N { a[i] as i32 } else { 0 },
))
}
}
}
#[inline(always)]
pub(crate) fn i32x2_from_f64(v: f64x2) -> i32x2 {
cfg_select! {
all(target_feature = "neon", target_arch = "aarch64") => {
unsafe { vqmovn_s64(vcvtq_s64_f64(v.into())) }.into()
}
target_feature = "simd128" => i32x4::from(i32x4_trunc_sat_f64x2_zero(v.into())).store(),
target_feature = "sse2" => {
let converted =
i32x4::from(unsafe { sse2::_mm_cvttpd_epi32((v & v.simd_eq(v)).into()) });
let flip = cast::<f64x2, i32x4>(v.simd_ge(f64x2::splat(2_147_483_648.)));
(converted ^ swizzle!(flip, [0, 2, 0, 2])).store()
}
_ => {
let [a, b] = v.to_array();
i32x4::new([a as i32, b as i32, 0, 0]).store()
}
}
}
#[inline(always)]
pub(crate) fn i32x4_from_i64<const N: usize>(v: i64x4) -> i32x4 {
let [low, high] = cast::<i64x4, [i32x4; 2]>(v);
swizzle!(low, high, [0, 2, 4, 6])
}
#[inline(always)]
pub(crate) fn i32x2_from_i64(v: i64x2) -> i32x2 {
let halves = cast::<i64x2, i32x4>(v);
swizzle!(halves, [0, 2]).store()
}
#[inline(always)]
pub(crate) fn i32x4_from_u32<const N: usize>(v: u32x4) -> i32x4 { v.cast_signed() }
#[inline(always)]
pub(crate) fn i32x2_from_u32(v: u32x2) -> i32x2 { v.cast_signed() }
#[inline(always)]
pub(crate) fn i32x4_from_u64<const N: usize>(v: u64x4) -> i32x4 {
u32x4_from_u64::<N>(v).cast_signed()
}
#[inline(always)]
pub(crate) fn i32x2_from_u64(v: u64x2) -> i32x2 {
u32x2_from_u64(v).cast_signed()
}
#[inline(always)]
pub(crate) fn u32x4_from_f32<const N: usize>(v: f32x4) -> u32x4 {
cfg_select! {
target_feature = "avx512vl" => unsafe {
use core::arch::x86_64::{_CMP_GE_OQ, _CMP_GT_OQ};
let v: x86_64::__m128 = v.into();
let converted = avx512_vl::_mm_cvttps_epu32(v);
let overflow = avx512_vl::_mm_cmp_ps_mask::<_CMP_GT_OQ>(
v,
sse::_mm_set1_ps(f32::from_bits(0x4f7f_ffff)),
);
let nonnegative =
avx512_vl::_mm_cmp_ps_mask::<_CMP_GE_OQ>(v, sse::_mm_setzero_ps());
let saturated =
avx512_vl::_mm_mask_mov_epi32(converted, overflow, sse2::_mm_set1_epi32(-1));
u32x4::from(avx512_vl::_mm_maskz_mov_epi32(nonnegative, saturated))
},
all(target_feature = "neon", target_arch = "aarch64") => {
unsafe { vcvtq_u32_f32(v.into()) }.into()
}
target_feature = "simd128" => u32x4::from(u32x4_trunc_sat_f32x4(v.into())),
_ => {
let nonnegative = v & v.simd_ge(f32x4::splat(0.));
let two31 = f32x4::splat(2_147_483_648.);
let high = nonnegative.simd_ge(two31);
let adjusted = nonnegative - (high & two31);
let converted = adjusted.fast_trunc_int()
^ (high.to_bits().cast_signed() & i32x4::splat(i32::MIN));
let overflow = nonnegative.simd_ge(f32x4::splat(4_294_967_296.));
converted.cast_unsigned() | overflow.to_bits()
}
}
}
#[inline(always)]
pub(crate) fn u32x2_from_f32(v: f32x2) -> u32x2 {
cfg_select! {
all(target_feature = "neon", target_arch = "aarch64") => {
unsafe { vcvt_u32_f32(v.into()) }.into()
}
_ => u32x4_from_f32::<2>(v.load()).store(),
}
}
#[inline(always)]
pub(crate) fn u32x4_from_f64<const N: usize>(v: f64x4) -> u32x4 {
cfg_select! {
target_feature = "avx512vl" => {
use core::arch::x86_64::{_CMP_GE_OQ, _CMP_GT_OQ};
let input: x86_64::__m256d = v.into();
unsafe {
let converted = avx512_vl::_mm256_cvttpd_epu32(input);
let overflow = avx512_vl::_mm256_cmp_pd_mask::<_CMP_GT_OQ>(
input,
avx::_mm256_set1_pd(u32::MAX as f64),
);
let nonnegative = avx512_vl::_mm256_cmp_pd_mask::<_CMP_GE_OQ>(
input,
avx::_mm256_setzero_pd(),
);
let saturated = avx512_vl::_mm_mask_mov_epi32(
converted,
overflow,
sse2::_mm_set1_epi32(-1),
);
u32x4::from(avx512_vl::_mm_maskz_mov_epi32(nonnegative, saturated))
}
}
all(target_feature = "neon", target_arch = "aarch64") => {
unsafe {
let low = vqmovn_u64(vcvtq_u64_f64(swizzle!(v, [0, 1]).into()));
if N <= 2 {
vcombine_u32(low, vdup_n_u32(0)).into()
} else {
vqmovn_high_u64(low, vcvtq_u64_f64(swizzle!(v, [2, 3]).into())).into()
}
}
}
target_feature = "simd128" => {
let low = u32x4::from(u32x4_trunc_sat_f64x2_zero(swizzle!(v, [0, 1]).into()));
join_32bit!(
N,
low,
u32x4::from(u32x4_trunc_sat_f64x2_zero(swizzle!(v, [2, 3]).into()))
)
}
target_feature = "avx" => {
let nonnegative = v & v.simd_ge(f64x4::splat(0.));
let two31 = f64x4::splat(2_147_483_648.);
let high = nonnegative.simd_ge(two31);
let adjusted = nonnegative - (high & two31);
let converted = i32x4::from(unsafe { avx::_mm256_cvttpd_epi32(adjusted.into()) })
^ (pack_mask_64_to_32!(high) & i32x4::splat(i32::MIN));
let overflow = nonnegative.simd_ge(f64x4::splat(4_294_967_296.));
(converted | pack_mask_64_to_32!(overflow)).cast_unsigned()
}
_ => {
let a = v.to_array();
u32x4::new(core::array::from_fn(
#[inline(always)]
|i| if i < N { a[i] as u32 } else { 0 },
))
}
}
}
#[inline(always)]
pub(crate) fn u32x2_from_f64(v: f64x2) -> u32x2 {
cfg_select! {
all(target_feature = "neon", target_arch = "aarch64") => {
unsafe { vqmovn_u64(vcvtq_u64_f64(v.into())) }.into()
}
target_feature = "simd128" => u32x4::from(u32x4_trunc_sat_f64x2_zero(v.into())).store(),
_ => {
let [a, b] = v.to_array();
u32x4::new([a as u32, b as u32, 0, 0]).store()
}
}
}
#[inline(always)]
pub(crate) fn u32x4_from_i32<const N: usize>(v: i32x4) -> u32x4 { v.cast_unsigned() }
#[inline(always)]
pub(crate) fn u32x2_from_i32(v: i32x2) -> u32x2 { v.cast_unsigned() }
#[inline(always)]
pub(crate) fn u32x4_from_i64<const N: usize>(v: i64x4) -> u32x4 {
i32x4_from_i64::<N>(v).cast_unsigned()
}
#[inline(always)]
pub(crate) fn u32x2_from_i64(v: i64x2) -> u32x2 {
i32x2_from_i64(v).cast_unsigned()
}
#[inline(always)]
pub(crate) fn u32x4_from_u64<const N: usize>(v: u64x4) -> u32x4 {
let [low, high] = cast::<u64x4, [u32x4; 2]>(v);
swizzle!(low, high, [0, 2, 4, 6])
}
#[inline(always)]
pub(crate) fn u32x2_from_u64(v: u64x2) -> u32x2 {
let halves = cast::<u64x2, u32x4>(v);
swizzle!(halves, [0, 2]).store()
}
#[inline(always)]
pub(crate) fn f64x4_from_f32<const N: usize>(v: f32x4) -> f64x4 {
cfg_select! {
target_feature = "avx" => {
f64x4::from(unsafe { avx::_mm256_cvtps_pd(v.into()) })
}
all(target_feature = "neon", target_arch = "aarch64") => {
let reg: float32x4_t = v.into();
unsafe {
let low = f64x2::from(vcvt_f64_f32(vget_low_f32(reg)));
join_64bit!(N, low, f64x2::from(vcvt_high_f64_f32(reg)))
}
}
target_feature = "simd128" => {
let reg: v128 = v.into();
let low = f64x2::from(f64x2_promote_low_f32x4(reg));
join_64bit!(
N,
low,
f64x2::from(f64x2_promote_low_f32x4(u32x4_shuffle::<2, 3, 0, 1>(reg, reg)))
)
}
target_feature = "sse2" => {
let reg: x86_64::__m128 = v.into();
unsafe {
let low = f64x2::from(sse2::_mm_cvtps_pd(reg));
join_64bit!(
N,
low,
f64x2::from(sse2::_mm_cvtps_pd(sse::_mm_movehl_ps(reg, reg)))
)
}
}
_ => {
let a = v.to_array();
f64x4::new(core::array::from_fn(
#[inline(always)]
|i| if i < N { a[i] as f64 } else { 0. },
))
}
}
}
#[inline(always)]
pub(crate) fn f64x2_from_f32(v: f32x2) -> f64x2 {
cfg_select! {
all(target_feature = "neon", target_arch = "aarch64") => {
f64x2::from(unsafe { vcvt_f64_f32(v.into()) })
}
target_feature = "simd128" => f64x2::from(f64x2_promote_low_f32x4(v.load().into())),
target_feature = "sse2" => f64x2::from(unsafe { sse2::_mm_cvtps_pd(v.load().into()) }),
_ => {
let [a, b, ..] = v.load().to_array();
f64x2::new([a as f64, b as f64])
}
}
}
#[inline(always)]
pub(crate) fn f64x4_from_i32<const N: usize>(v: i32x4) -> f64x4 {
cfg_select! {
all(target_feature = "neon", target_arch = "aarch64") => {
let reg: int32x4_t = v.into();
unsafe {
let low = f64x2::from(vcvtq_f64_s64(vmovl_s32(vget_low_s32(reg))));
join_64bit!(N, low, f64x2::from(vcvtq_f64_s64(vmovl_high_s32(reg))))
}
}
target_feature = "simd128" => {
let reg: v128 = v.into();
let low = f64x2::from(f64x2_convert_low_i32x4(reg));
join_64bit!(
N,
low,
f64x2::from(f64x2_convert_low_i32x4(u32x4_shuffle::<2, 3, 0, 1>(reg, reg)))
)
}
target_feature = "avx" => {
f64x4::from(unsafe { avx::_mm256_cvtepi32_pd(v.into()) })
}
target_feature = "sse2" => {
unsafe {
let low = f64x2::from(sse2::_mm_cvtepi32_pd(v.into()));
join_64bit!(
N,
low,
f64x2::from(sse2::_mm_cvtepi32_pd(swizzle!(v, [2, 3, 2, 3]).into()))
)
}
}
_ => {
let a = v.to_array();
f64x4::new(core::array::from_fn(
#[inline(always)]
|i| if i < N { a[i] as f64 } else { 0. },
))
}
}
}
#[inline(always)]
pub(crate) fn f64x2_from_i32(v: i32x2) -> f64x2 {
cfg_select! {
all(target_feature = "neon", target_arch = "aarch64") => {
f64x2::from(unsafe { vcvtq_f64_s64(vmovl_s32(v.into())) })
}
_ => f64x2::from_i32x4_lower2(v.load()),
}
}
#[inline(always)]
pub(crate) fn f64x4_from_i64<const N: usize>(v: i64x4) -> f64x4 {
cfg_select! {
all(target_feature = "avx512dq", target_feature = "avx512vl") => {
f64x4::from(unsafe { avx512_dq_vl::_mm256_cvtepi64_pd(v.into()) })
}
all(target_feature = "neon", target_arch = "aarch64") => unsafe {
let low = f64x2::from(vcvtq_f64_s64(swizzle!(v, [0, 1]).into()));
join_64bit!(N, low, f64x2::from(vcvtq_f64_s64(swizzle!(v, [2, 3]).into())))
},
_ => {
let a = v.to_array();
f64x4::new(core::array::from_fn(
#[inline(always)]
|i| if i < N { a[i] as f64 } else { 0. },
))
}
}
}
#[inline(always)]
pub(crate) fn f64x2_from_i64(v: i64x2) -> f64x2 {
cfg_select! {
all(target_feature = "avx512dq", target_feature = "avx512vl") => {
f64x2::from(unsafe { avx512_dq_vl::_mm_cvtepi64_pd(v.into()) })
}
all(target_feature = "neon", target_arch = "aarch64") => {
f64x2::from(unsafe { vcvtq_f64_s64(v.into()) })
}
_ => {
let [a, b] = v.to_array();
f64x2::new([a as f64, b as f64])
}
}
}
#[inline(always)]
pub(crate) fn f64x4_from_u32<const N: usize>(v: u32x4) -> f64x4 {
cfg_select! {
target_feature = "avx512vl" => {
f64x4::from(unsafe { avx512_vl::_mm256_cvtepu32_pd(v.into()) })
}
all(target_feature = "neon", target_arch = "aarch64") => {
let reg: uint32x4_t = v.into();
unsafe {
let low = f64x2::from(vcvtq_f64_u64(vmovl_u32(vget_low_u32(reg))));
join_64bit!(N, low, f64x2::from(vcvtq_f64_u64(vmovl_high_u32(reg))))
}
}
target_feature = "simd128" => {
let reg: v128 = v.into();
let low = f64x2::from(f64x2_convert_low_u32x4(reg));
join_64bit!(
N,
low,
f64x2::from(f64x2_convert_low_u32x4(u32x4_shuffle::<2, 3, 0, 1>(reg, reg)))
)
}
_ => {
let zero = u32x4::splat(0);
let low =
zero_extended_to_f64!(cast::<u32x4, u64x2>(swizzle!(v, zero, [0, 4, 1, 5])));
join_64bit!(
N,
low,
zero_extended_to_f64!(cast::<u32x4, u64x2>(swizzle!(v, zero, [2, 6, 3, 7])))
)
}
}
}
#[inline(always)]
pub(crate) fn f64x2_from_u32(v: u32x2) -> f64x2 {
cfg_select! {
target_feature = "avx512vl" => {
f64x2::from(unsafe { avx512_vl::_mm_cvtepu32_pd(v.load().into()) })
}
all(target_feature = "neon", target_arch = "aarch64") => {
f64x2::from(unsafe { vcvtq_f64_u64(vmovl_u32(v.into())) })
}
target_feature = "simd128" => f64x2::from(f64x2_convert_low_u32x4(v.load().into())),
_ => zero_extended_to_f64!(u64x2_from_u32(v)),
}
}
#[inline(always)]
pub(crate) fn f64x4_from_u64<const N: usize>(v: u64x4) -> f64x4 {
const {
assert!(matches!(N, 2..=4));
}
cfg_select! {
all(target_feature = "avx512dq", target_feature = "avx512vl") => unsafe {
let input: x86_64::__m256i = v.into();
wide::f64x4::from(avx512_dq_vl::_mm256_cvtepu64_pd(input))
},
all(target_feature = "neon", target_arch = "aarch64") => unsafe {
let low = f64x2::from(vcvtq_f64_u64(swizzle!(v, [0, 1]).into()));
join_64bit!(N, low, f64x2::from(vcvtq_f64_u64(swizzle!(v, [2, 3]).into())))
},
_ => {
let array = v.to_array();
wide::f64x4::new(core::array::from_fn(
#[inline(always)]
|i| if i < N { array[i] as f64 } else { 0. },
))
}
}
}
#[inline(always)]
pub(crate) fn f64x2_from_u64(v: u64x2) -> f64x2 {
cfg_select! {
all(target_feature = "avx512dq", target_feature = "avx512vl") => {
f64x2::from(unsafe { avx512_dq_vl::_mm_cvtepu64_pd(v.into()) })
}
all(target_feature = "neon", target_arch = "aarch64") => {
f64x2::from(unsafe { vcvtq_f64_u64(v.into()) })
}
_ => {
let [a, b] = v.to_array();
f64x2::new([a as f64, b as f64])
}
}
}
#[inline(always)]
pub(crate) fn i64x4_from_f32<const N: usize>(v: f32x4) -> i64x4 {
cfg_select! {
all(target_feature = "avx512dq", target_feature = "avx512vl") => {
use core::arch::x86_64::{_CMP_GE_OQ, _CMP_ORD_Q};
let input: x86_64::__m128 = v.into();
unsafe {
let converted = avx512_dq_vl::_mm256_cvttps_epi64(input);
let overflow = avx512_vl::_mm_cmp_ps_mask::<_CMP_GE_OQ>(
input,
sse::_mm_set1_ps(9_223_372_036_854_775_808.),
);
let ordered = avx512_vl::_mm_cmp_ps_mask::<_CMP_ORD_Q>(input, input);
let saturated = avx512_vl::_mm256_mask_mov_epi64(
converted,
overflow,
avx::_mm256_set1_epi64x(i64::MAX),
);
i64x4::from(avx512_vl::_mm256_maskz_mov_epi64(ordered, saturated))
}
}
_ => {
i64x4_from_f64::<N>(f64x4_from_f32::<N>(v))
}
}
}
#[inline(always)]
pub(crate) fn i64x2_from_f32(v: f32x2) -> i64x2 {
cfg_select! {
all(target_feature = "avx512dq", target_feature = "avx512vl") => {
use core::arch::x86_64::{_CMP_GE_OQ, _CMP_ORD_Q};
let input: x86_64::__m128 = v.load().into();
unsafe {
let converted = avx512_dq_vl::_mm_cvttps_epi64(input);
let overflow = avx512_vl::_mm_cmp_ps_mask::<_CMP_GE_OQ>(
input,
sse::_mm_set1_ps(9_223_372_036_854_775_808.),
);
let ordered = avx512_vl::_mm_cmp_ps_mask::<_CMP_ORD_Q>(input, input);
let saturated = avx512_vl::_mm_mask_mov_epi64(
converted,
overflow,
sse2::_mm_set1_epi64x(i64::MAX),
);
i64x2::from(avx512_vl::_mm_maskz_mov_epi64(ordered, saturated))
}
}
_ => i64x2_from_f64(f64x2_from_f32(v)),
}
}
#[inline(always)]
pub(crate) fn i64x4_from_f64<const N: usize>(v: f64x4) -> i64x4 {
cfg_select! {
all(target_feature = "avx512dq", target_feature = "avx512vl") => {
v.trunc_int()
}
_ => {
let low = f64x2::trunc_int(swizzle!(v, [0, 1]));
join_64bit!(N, low, f64x2::trunc_int(swizzle!(v, [2, 3])))
}
}
}
#[inline(always)]
pub(crate) fn i64x2_from_f64(v: f64x2) -> i64x2 {
v.trunc_int()
}
#[inline(always)]
pub(crate) fn i64x4_from_i32<const N: usize>(v: i32x4) -> i64x4 {
cfg_select! {
target_feature = "avx2" => {
i64x4::from(unsafe { avx2::_mm256_cvtepi32_epi64(v.into()) })
}
all(target_feature = "neon", target_arch = "aarch64") => {
let reg: int32x4_t = v.into();
unsafe {
let low = i64x2::from(vmovl_s32(vget_low_s32(reg)));
join_64bit!(N, low, i64x2::from(vmovl_high_s32(reg)))
}
}
target_feature = "simd128" => {
let reg: v128 = v.into();
let low = i64x2::from(i64x2_extend_low_i32x4(reg));
join_64bit!(N, low, i64x2::from(i64x2_extend_high_i32x4(reg)))
}
_ => {
let sign = v >> 31;
let low = cast::<i32x4, i64x2>(swizzle!(v, sign, [0, 4, 1, 5]));
join_64bit!(N, low, cast::<i32x4, i64x2>(swizzle!(v, sign, [2, 6, 3, 7])))
}
}
}
#[inline(always)]
pub(crate) fn i64x2_from_i32(v: i32x2) -> i64x2 {
cfg_select! {
all(target_feature = "neon", target_arch = "aarch64") => {
i64x2::from(unsafe { vmovl_s32(v.into()) })
}
target_feature = "simd128" => i64x2::from(i64x2_extend_low_i32x4(v.load().into())),
target_feature = "sse4.1" => {
i64x2::from(unsafe { sse41::_mm_cvtepi32_epi64(v.load().into()) })
}
_ => {
let w = v.load();
cast::<i32x4, i64x2>(swizzle!(w, w >> 31, [0, 4, 1, 5]))
}
}
}
#[inline(always)]
pub(crate) fn i64x4_from_u32<const N: usize>(v: u32x4) -> i64x4 {
u64x4_from_u32::<N>(v).cast_signed()
}
#[inline(always)]
pub(crate) fn i64x2_from_u32(v: u32x2) -> i64x2 {
u64x2_from_u32(v).cast_signed()
}
#[inline(always)]
pub(crate) fn i64x4_from_u64<const N: usize>(v: u64x4) -> i64x4 { v.cast_signed() }
#[inline(always)]
pub(crate) fn i64x2_from_u64(v: u64x2) -> i64x2 { v.cast_signed() }
#[inline(always)]
pub(crate) fn u64x4_from_f32<const N: usize>(v: f32x4) -> u64x4 {
cfg_select! {
all(target_feature = "avx512dq", target_feature = "avx512vl") => {
use core::arch::x86_64::_CMP_GE_OQ;
let input: x86_64::__m128 = v.into();
unsafe {
let converted = avx512_dq_vl::_mm256_cvttps_epu64(input);
let nonnegative =
avx512_vl::_mm_cmp_ps_mask::<_CMP_GE_OQ>(input, sse::_mm_setzero_ps());
u64x4::from(avx512_vl::_mm256_maskz_mov_epi64(nonnegative, converted))
}
}
_ => {
u64x4_from_f64::<N>(f64x4_from_f32::<N>(v))
}
}
}
#[inline(always)]
pub(crate) fn u64x2_from_f32(v: f32x2) -> u64x2 {
cfg_select! {
all(target_feature = "avx512dq", target_feature = "avx512vl") => {
use core::arch::x86_64::_CMP_GE_OQ;
let input: x86_64::__m128 = v.load().into();
unsafe {
let converted = avx512_dq_vl::_mm_cvttps_epu64(input);
let nonnegative =
avx512_vl::_mm_cmp_ps_mask::<_CMP_GE_OQ>(input, sse::_mm_setzero_ps());
u64x2::from(avx512_vl::_mm_maskz_mov_epi64(nonnegative, converted))
}
}
_ => u64x2_from_f64(f64x2_from_f32(v)),
}
}
#[inline(always)]
pub(crate) fn u64x4_from_f64<const N: usize>(v: f64x4) -> u64x4 {
cfg_select! {
all(target_feature = "avx512dq", target_feature = "avx512vl") => {
use core::arch::x86_64::_CMP_GE_OQ;
let input: x86_64::__m256d = v.into();
unsafe {
let converted = avx512_dq_vl::_mm256_cvttpd_epu64(input);
let nonnegative = avx512_vl::_mm256_cmp_pd_mask::<_CMP_GE_OQ>(
input,
avx::_mm256_setzero_pd(),
);
u64x4::from(avx512_vl::_mm256_maskz_mov_epi64(nonnegative, converted))
}
}
all(target_feature = "neon", target_arch = "aarch64") => {
unsafe {
let low = u64x2::from(vcvtq_u64_f64(swizzle!(v, [0, 1]).into()));
join_64bit!(N, low, u64x2::from(vcvtq_u64_f64(swizzle!(v, [2, 3]).into())))
}
}
_ => {
let a = v.to_array();
u64x4::new(core::array::from_fn(
#[inline(always)]
|i| if i < N { a[i] as u64 } else { 0 },
))
}
}
}
#[inline(always)]
pub(crate) fn u64x2_from_f64(v: f64x2) -> u64x2 {
cfg_select! {
all(target_feature = "avx512dq", target_feature = "avx512vl") => {
use core::arch::x86_64::_CMP_GE_OQ;
let input: x86_64::__m128d = v.into();
unsafe {
let converted = avx512_dq_vl::_mm_cvttpd_epu64(input);
let nonnegative =
avx512_vl::_mm_cmp_pd_mask::<_CMP_GE_OQ>(input, sse2::_mm_setzero_pd());
u64x2::from(avx512_vl::_mm_maskz_mov_epi64(nonnegative, converted))
}
}
all(target_feature = "neon", target_arch = "aarch64") => {
u64x2::from(unsafe { vcvtq_u64_f64(v.into()) })
}
_ => {
let [a, b] = v.to_array();
u64x2::new([a as u64, b as u64])
}
}
}
#[inline(always)]
pub(crate) fn u64x4_from_i32<const N: usize>(v: i32x4) -> u64x4 {
i64x4_from_i32::<N>(v).cast_unsigned()
}
#[inline(always)]
pub(crate) fn u64x2_from_i32(v: i32x2) -> u64x2 {
i64x2_from_i32(v).cast_unsigned()
}
#[inline(always)]
pub(crate) fn u64x4_from_i64<const N: usize>(v: i64x4) -> u64x4 { v.cast_unsigned() }
#[inline(always)]
pub(crate) fn u64x2_from_i64(v: i64x2) -> u64x2 { v.cast_unsigned() }
#[inline(always)]
pub(crate) fn u64x4_from_u32<const N: usize>(v: u32x4) -> u64x4 {
cfg_select! {
target_feature = "avx2" => {
u64x4::from(unsafe { avx2::_mm256_cvtepu32_epi64(v.into()) })
}
all(target_feature = "neon", target_arch = "aarch64") => {
let reg: uint32x4_t = v.into();
unsafe {
let low = u64x2::from(vmovl_u32(vget_low_u32(reg)));
join_64bit!(N, low, u64x2::from(vmovl_high_u32(reg)))
}
}
target_feature = "simd128" => {
let reg: v128 = v.into();
let low = u64x2::from(u64x2_extend_low_u32x4(reg));
join_64bit!(N, low, u64x2::from(u64x2_extend_high_u32x4(reg)))
}
_ => {
let zero = u32x4::splat(0);
let low = cast::<u32x4, u64x2>(swizzle!(v, zero, [0, 4, 1, 5]));
join_64bit!(N, low, cast::<u32x4, u64x2>(swizzle!(v, zero, [2, 6, 3, 7])))
}
}
}
#[inline(always)]
pub(crate) fn u64x2_from_u32(v: u32x2) -> u64x2 {
cfg_select! {
all(target_feature = "neon", target_arch = "aarch64") => {
u64x2::from(unsafe { vmovl_u32(v.into()) })
}
target_feature = "simd128" => u64x2::from(u64x2_extend_low_u32x4(v.load().into())),
target_feature = "sse4.1" => {
u64x2::from(unsafe { sse41::_mm_cvtepu32_epi64(v.load().into()) })
}
_ => {
cast::<u32x4, u64x2>(swizzle!(v.load(), u32x4::splat(0), [0, 4, 1, 5]))
}
}
}
#[allow(dead_code)]
#[inline(always)]
pub(crate) fn u8_from_i32(mask: i32x4) -> u8x16 {
let low_bytes = cast::<_, i16x8>(mask & i32x4::splat(0xff));
let words = cast::<_, i16x8>(u8x16::narrow_i16x8(low_bytes, low_bytes));
u8x16::narrow_i16x8(words, words)
}
}
pub(crate) mod round {
use wide::{f32x4, f64x2, f64x4};
#[inline(always)]
pub(crate) fn f32x4_round_ties_even(x: f32x4) -> f32x4 {
let rounded = x.round_ties_even();
cfg_select! {
all(target_feature = "sse2", not(target_feature = "sse4.1")) => {
let sign = x & f32x4::splat(f32::from_bits(0x8000_0000));
rounded | sign
}
_ => rounded,
}
}
#[inline(always)]
pub(crate) fn f64x4_round_ties_even(x: f64x4) -> f64x4 {
cfg_select! {
all(target_feature = "neon", target_arch = "aarch64") => {
let [low, high] = unsafe { core::mem::transmute::<f64x4, [f64x2; 2]>(x) };
let halves = [f64x2_round_ties_even(low), f64x2_round_ties_even(high)];
unsafe { core::mem::transmute::<[f64x2; 2], f64x4>(halves) }
}
_ => x.round_ties_even(),
}
}
#[inline(always)]
pub(crate) fn f64x2_round_ties_even(x: f64x2) -> f64x2 {
cfg_select! {
all(target_feature = "neon", target_arch = "aarch64") => {
unsafe { core::arch::aarch64::vrndnq_f64(x.into()) }.into()
}
_ => x.round_ties_even(),
}
}
}
#[cfg(false)]
pub(crate) mod select {
use wide::{f32x4, i32x4, u32x4};
#[expect(dead_code)]
#[inline(always)]
pub fn i32x4_f32x4(mask: i32x4, true_values: f32x4, false_values: f32x4) -> f32x4 {
f32x4::from_bits(mask.cast_unsigned()).select(true_values, false_values)
}
#[expect(dead_code)]
#[inline(always)]
pub fn i32x4_i32x4(mask: i32x4, true_values: i32x4, false_values: i32x4) -> i32x4 {
mask.select(true_values, false_values)
}
#[expect(dead_code)]
#[inline(always)]
pub fn i32x4_u32x4(mask: i32x4, true_values: u32x4, false_values: u32x4) -> u32x4 {
mask.cast_unsigned().select(true_values, false_values)
}
#[inline(always)]
pub fn u64_f32x4(mask: u64, true_values: f32x4, false_values: f32x4) -> f32x4 {
f32x4::from_bits(u64_u32x4(mask, true_values.to_bits(), false_values.to_bits()))
}
#[inline(always)]
pub fn u64_i32x4(mask: u64, true_values: i32x4, false_values: i32x4) -> i32x4 {
const LANE_MASK: i32x4 = i32x4::new([0b0001, 0b0010, 0b0100, 0b1000]);
let mask = (i32x4::splat(mask as i32) & LANE_MASK).simd_eq(LANE_MASK);
mask.select(true_values, false_values)
}
#[inline(always)]
pub fn u64_u32x4(mask: u64, true_values: u32x4, false_values: u32x4) -> u32x4 {
const LANE_MASK: u32x4 = u32x4::new([0b0001, 0b0010, 0b0100, 0b1000]);
let mask = (u32x4::splat(mask as u32) & LANE_MASK).simd_eq(LANE_MASK);
mask.select(true_values, false_values)
}
}
pub(crate) mod matmul {
#![allow(unused_parens)]
use super::{reduce, transpose};
use crate::{
simd::utils::{Simd2Ext, Simd4Ext, swizzle},
utils::arith,
};
macro_rules! select_2x3 {
($layout:ident) => {
paste::paste! {
pub(crate) use {
[<matmul1x2x3_ $layout>] as matmul1x2x3,
[<matmul2x1x3_ $layout>] as matmul2x1x3,
[<matmul2x2x3_ $layout>] as matmul2x2x3,
[<matmul2x3x1_ $layout>] as matmul2x3x1,
[<matmul2x3x2_ $layout>] as matmul2x3x2,
[<matmul2x3x3_ $layout>] as matmul2x3x3,
[<matmul2x3x4_ $layout>] as matmul2x3x4,
[<matmul2x4x3_ $layout>] as matmul2x4x3,
[<matmul3x2x3_ $layout>] as matmul3x2x3,
[<matmul4x2x3_ $layout>] as matmul4x2x3,
};
}
};
}
macro_rules! impl_matmul {
($scalar:ident, $vec2:ident, $vec4:ident) => {
#[inline(always)]
pub(crate) fn matmul1x1x1(a: $scalar, b: $scalar) -> $scalar { a * b }
#[inline(always)]
pub(crate) fn matmul2x1x1(a: $vec2, b: $scalar) -> $vec2 { a * $vec2::splat(b) }
#[inline(always)]
pub(crate) fn matmul3x1x1(a: $vec4, b: $scalar) -> $vec4 { a * b }
#[inline(always)]
pub(crate) fn matmul4x1x1(a: $vec4, b: $scalar) -> $vec4 { a * b }
#[inline(always)]
pub(crate) fn matmul1x2x1(a: $vec2, b: $vec2) -> $scalar {
reduce::sum::<$vec2, 2>(a * b)
}
#[inline(always)]
pub(crate) fn matmul2x2x1(a: $vec4, b: $vec2) -> $vec2 {
let xxyy = swizzle!(b, [0, 0, 1, 1]);
let products = a * xxyy;
let upper_products = swizzle!(products, [2, 3]);
products.xy() + upper_products
}
#[inline(always)]
pub(crate) fn matmul3x2x1(a: [$vec4; 2], b: $vec2) -> $vec4 { matmul4x2x1(a, b) }
#[inline(always)]
pub(crate) fn matmul4x2x1(a: [$vec4; 2], b: $vec2) -> $vec4 {
let xxxx = swizzle!(b, [0, 0, 0, 0]);
let yyyy = swizzle!(b, [1, 1, 1, 1]);
arith!((a[0]) * xxxx + (a[1]) * yyyy)
}
#[inline(always)]
pub(crate) fn matmul1x3x1(a: $vec4, b: $vec4) -> $scalar {
reduce::sum::<$vec4, 3>(a * b)
}
#[inline(always)]
#[allow(dead_code)]
pub(crate) fn matmul2x3x1_in_vec4(a: [$vec4; 2], b: $vec4) -> $vec2 {
let xxyy = swizzle!(b, [0, 0, 1, 1]);
let zz = swizzle!(b, [2, 2]);
let products01 = a[0] * xxyy;
let upper_products01 = swizzle!(products01, [2, 3]);
arith!((products01.xy() + upper_products01) + (a[1].xy()) * zz)
}
#[allow(dead_code)]
#[inline(always)]
pub(crate) fn matmul2x3x1_in_vec2(a: [$vec2; 3], b: $vec4) -> $vec2 {
let [a0, a1, a2] = a;
let b0 = swizzle!(b, [0, 0]);
let b1 = swizzle!(b, [1, 1]);
let b2 = swizzle!(b, [2, 2]);
arith!(a0 * b0 + a1 * b1 + a2 * b2)
}
#[inline(always)]
pub(crate) fn matmul3x3x1(a: [$vec4; 3], b: $vec4) -> $vec4 { matmul4x3x1(a, b) }
#[inline(always)]
pub(crate) fn matmul4x3x1(a: [$vec4; 3], b: $vec4) -> $vec4 {
let xxxx = swizzle!(b, [0, 0, 0, 0]);
let yyyy = swizzle!(b, [1, 1, 1, 1]);
let zzzz = swizzle!(b, [2, 2, 2, 2]);
arith!((a[0]) * xxxx + (a[1]) * yyyy + (a[2]) * zzzz)
}
#[inline(always)]
pub(crate) fn matmul1x4x1(a: $vec4, b: $vec4) -> $scalar {
reduce::sum::<$vec4, 4>(a * b)
}
#[inline(always)]
pub(crate) fn matmul2x4x1(a: [$vec4; 2], b: $vec4) -> $vec2 {
let xxyy = swizzle!(b, [0, 0, 1, 1]);
let zzww = swizzle!(b, [2, 2, 3, 3]);
let pair_sums = arith!((a[0]) * xxyy + (a[1]) * zzww);
let upper_pair_sums = swizzle!(pair_sums, [2, 3]);
pair_sums.xy() + upper_pair_sums
}
#[inline(always)]
pub(crate) fn matmul3x4x1(a: [$vec4; 4], b: $vec4) -> $vec4 { matmul4x4x1(a, b) }
#[inline(always)]
pub(crate) fn matmul4x4x1(a: [$vec4; 4], b: $vec4) -> $vec4 {
let xxxx = swizzle!(b, [0, 0, 0, 0]);
let yyyy = swizzle!(b, [1, 1, 1, 1]);
let zzzz = swizzle!(b, [2, 2, 2, 2]);
let wwww = swizzle!(b, [3, 3, 3, 3]);
arith!((a[0]) * xxxx + (a[1]) * yyyy + (a[2]) * zzzz + (a[3]) * wwww)
}
#[inline(always)]
pub(crate) fn matmul1x1x2(a: $scalar, b: $vec2) -> $vec2 { $vec2::splat(a) * b }
#[inline(always)]
pub(crate) fn matmul2x1x2(a: $vec2, b: $vec2) -> $vec4 {
swizzle!(a, [0, 1, 0, 1]) * swizzle!(b, [0, 0, 1, 1])
}
#[inline(always)]
pub(crate) fn matmul3x1x2(a: $vec4, b: $vec2) -> [$vec4; 2] { matmul4x1x2(a, b) }
#[inline(always)]
pub(crate) fn matmul4x1x2(a: $vec4, b: $vec2) -> [$vec4; 2] {
let col0 = a * swizzle!(b, [0, 0, 0, 0]);
let col1 = a * swizzle!(b, [1, 1, 1, 1]);
[col0, col1]
}
#[inline(always)]
pub(crate) fn matmul1x2x2(a: $vec2, b: $vec4) -> $vec2 {
let products = swizzle!(a, [0, 1, 0, 1]) * b;
swizzle!(products, [0, 2]) + swizzle!(products, [1, 3])
}
#[inline(always)]
pub(crate) fn matmul2x2x2(a: $vec4, b: $vec4) -> $vec4 {
arith!(
(swizzle!(a, [0, 3, 0, 3])) * b
+ (swizzle!(a, [2, 1, 2, 1])) * (swizzle!(b, [1, 0, 3, 2]))
)
}
#[inline(always)]
pub(crate) fn matmul3x2x2(a: [$vec4; 2], b: $vec4) -> [$vec4; 2] { matmul4x2x2(a, b) }
#[inline(always)]
pub(crate) fn matmul4x2x2(a: [$vec4; 2], b: $vec4) -> [$vec4; 2] {
let xxxx = swizzle!(b, [0, 0, 0, 0]);
let yyyy = swizzle!(b, [1, 1, 1, 1]);
let zzzz = swizzle!(b, [2, 2, 2, 2]);
let wwww = swizzle!(b, [3, 3, 3, 3]);
let col0 = arith!((a[0]) * xxxx + (a[1]) * yyyy);
let col1 = arith!((a[0]) * zzzz + (a[1]) * wwww);
[col0, col1]
}
#[inline(always)]
pub(crate) fn matmul1x3x2(a: $vec4, b: [$vec4; 2]) -> $vec2 {
let cols01_lo = swizzle!(b[0], b[1], [0, 4, 1, 5]);
let cols01_hi = swizzle!(b[0], b[1], [2, 6]);
let products01 = cols01_lo * swizzle!(a, [0, 0, 1, 1]);
let sums01 = products01.xy() + swizzle!(products01, [2, 3]);
arith!(sums01 + (swizzle!(a, [2, 2])) * cols01_hi)
}
#[inline(always)]
#[allow(dead_code)]
pub(crate) fn matmul2x3x2_in_vec4(a: [$vec4; 2], b: [$vec4; 2]) -> $vec4 {
let col0 = matmul2x3x1_in_vec4(a, b[0]);
let col1 = matmul2x3x1_in_vec4(a, b[1]);
swizzle!(col0, col1, @concat)
}
#[allow(dead_code)]
#[inline(always)]
pub(crate) fn matmul2x3x2_in_vec2(a: [$vec2; 3], b: [$vec4; 2]) -> $vec4 {
let col0 = matmul2x3x1_in_vec2(a, b[0]);
let col1 = matmul2x3x1_in_vec2(a, b[1]);
swizzle!(col0, col1, @concat)
}
#[inline(always)]
pub(crate) fn matmul3x3x2(a: [$vec4; 3], b: [$vec4; 2]) -> [$vec4; 2] {
matmul4x3x2(a, b)
}
#[inline(always)]
pub(crate) fn matmul4x3x2(a: [$vec4; 3], b: [$vec4; 2]) -> [$vec4; 2] {
let col0 = matmul4x3x1(a, b[0]);
let col1 = matmul4x3x1(a, b[1]);
[col0, col1]
}
#[inline(always)]
pub(crate) fn matmul1x4x2(a: $vec4, b: [$vec4; 2]) -> $vec2 {
let scaled_col0 = a * b[0];
let scaled_col1 = a * b[1];
let pair_sums = swizzle!(scaled_col0, scaled_col1, [0, 4, 1, 5])
+ swizzle!(scaled_col0, scaled_col1, [2, 6, 3, 7]);
pair_sums.xy() + swizzle!(pair_sums, [2, 3])
}
#[inline(always)]
pub(crate) fn matmul2x4x2(a: [$vec4; 2], b: [$vec4; 2]) -> $vec4 {
let col0 = matmul2x4x1(a, b[0]);
let col1 = matmul2x4x1(a, b[1]);
swizzle!(col0, col1, @concat)
}
#[inline(always)]
pub(crate) fn matmul3x4x2(a: [$vec4; 4], b: [$vec4; 2]) -> [$vec4; 2] {
matmul4x4x2(a, b)
}
#[inline(always)]
pub(crate) fn matmul4x4x2(a: [$vec4; 4], b: [$vec4; 2]) -> [$vec4; 2] {
let col0 = matmul4x4x1(a, b[0]);
let col1 = matmul4x4x1(a, b[1]);
[col0, col1]
}
#[inline(always)]
pub(crate) fn matmul1x1x3(a: $scalar, b: $vec4) -> $vec4 { $vec4::splat(a) * b }
#[inline(always)]
#[allow(dead_code)]
pub(crate) fn matmul2x1x3_in_vec4(a: $vec2, b: $vec4) -> [$vec4; 2] {
let xyxy = swizzle!(a, [0, 1, 0, 1]);
let xxyy = swizzle!(b, [0, 0, 1, 1]);
let zz__ = swizzle!(b, [2, 2, _, _]);
[xyxy * xxyy, xyxy * zz__]
}
#[allow(dead_code)]
#[inline(always)]
pub(crate) fn matmul2x1x3_in_vec2(a: $vec2, b: $vec4) -> [$vec2; 3] {
[
a * swizzle!(b, [0, 0]),
a * swizzle!(b, [1, 1]),
a * swizzle!(b, [2, 2]),
]
}
#[inline(always)]
pub(crate) fn matmul3x1x3(a: $vec4, b: $vec4) -> [$vec4; 3] { matmul4x1x3(a, b) }
#[inline(always)]
pub(crate) fn matmul4x1x3(a: $vec4, b: $vec4) -> [$vec4; 3] {
let col0 = a * swizzle!(b, [0, 0, 0, 0]);
let col1 = a * swizzle!(b, [1, 1, 1, 1]);
let col2 = a * swizzle!(b, [2, 2, 2, 2]);
[col0, col1, col2]
}
#[inline(always)]
#[allow(dead_code)]
pub(crate) fn matmul1x2x3_in_vec4(a: $vec2, b: [$vec4; 2]) -> $vec4 {
let col0 = swizzle!(b[0], b[1], [0, 2, 4, _]);
let col1 = swizzle!(b[0], b[1], [1, 3, 5, _]);
let xxx_ = swizzle!(a, [0, 0, 0, _]);
let yyy_ = swizzle!(a, [1, 1, 1, _]);
arith!(xxx_ * col0 + yyy_ * col1)
}
#[allow(dead_code)]
#[inline(always)]
pub(crate) fn matmul1x2x3_in_vec2(a: $vec2, b: [$vec2; 3]) -> $vec4 {
let products = [a * b[0], a * b[1], a * b[2]];
let [low, high] = transpose::transpose2x3_in_vec2::<$vec2>(products);
low + high
}
#[inline(always)]
#[allow(dead_code)]
pub(crate) fn matmul2x2x3_in_vec4(a: $vec4, b: [$vec4; 2]) -> [$vec4; 2] {
let xyxy = swizzle!(a, [0, 1, 0, 1]);
let zwzw = swizzle!(a, [2, 3, 2, 3]);
let xxzz0 = swizzle!(b[0], [0, 0, 2, 2]);
let yyww0 = swizzle!(b[0], [1, 1, 3, 3]);
let cols01 = arith!(xyxy * xxzz0 + zwzw * yyww0);
let b1_xx = swizzle!(b[1], [0, 0, _, _]);
let b1_yy = swizzle!(b[1], [1, 1, _, _]);
let xy__ = swizzle!(a, [0, 1, _, _]);
let zw__ = swizzle!(a, [2, 3, _, _]);
let col2 = arith!(xy__ * b1_xx + zw__ * b1_yy);
[cols01, col2]
}
#[allow(dead_code)]
#[inline(always)]
pub(crate) fn matmul2x2x3_in_vec2(a: $vec4, b: [$vec2; 3]) -> [$vec2; 3] {
let a0 = swizzle!(a, [0, 1]);
let a1 = swizzle!(a, [2, 3]);
b.map(
#[inline(always)]
|col| {
let x = swizzle!(col, [0, 0]);
let y = swizzle!(col, [1, 1]);
arith!(a0 * x + a1 * y)
},
)
}
#[inline(always)]
#[allow(dead_code)]
pub(crate) fn matmul3x2x3_in_vec4(a: [$vec4; 2], b: [$vec4; 2]) -> [$vec4; 3] {
matmul4x2x3_in_vec4(a, b)
}
#[allow(dead_code)]
#[inline(always)]
pub(crate) fn matmul3x2x3_in_vec2(a: [$vec4; 2], b: [$vec2; 3]) -> [$vec4; 3] {
matmul4x2x3_in_vec2(a, b)
}
#[inline(always)]
#[allow(dead_code)]
pub(crate) fn matmul4x2x3_in_vec4(a: [$vec4; 2], b: [$vec4; 2]) -> [$vec4; 3] {
let b0_xxxx = swizzle!(b[0], [0, 0, 0, 0]); let b0_yyyy = swizzle!(b[0], [1, 1, 1, 1]); let b0_zzzz = swizzle!(b[0], [2, 2, 2, 2]); let b0_wwww = swizzle!(b[0], [3, 3, 3, 3]); let col0 = arith!((a[0]) * b0_xxxx + (a[1]) * b0_yyyy);
let col1 = arith!((a[0]) * b0_zzzz + (a[1]) * b0_wwww);
let b1_xxxx = swizzle!(b[1], [0, 0, 0, 0]); let b1_yyyy = swizzle!(b[1], [1, 1, 1, 1]); let col2 = arith!((a[0]) * b1_xxxx + (a[1]) * b1_yyyy);
[col0, col1, col2]
}
#[allow(dead_code)]
#[inline(always)]
pub(crate) fn matmul4x2x3_in_vec2(a: [$vec4; 2], b: [$vec2; 3]) -> [$vec4; 3] {
let [a0, a1] = a;
b.map(
#[inline(always)]
|col| {
let x = swizzle!(col, [0, 0, 0, 0]);
let y = swizzle!(col, [1, 1, 1, 1]);
arith!(a0 * x + a1 * y)
},
)
}
#[inline(always)]
pub(crate) fn matmul1x3x3(a: $vec4, b: [$vec4; 3]) -> $vec4 {
let [coeff_x, coeff_y, coeff_z] = transpose::transpose3x3(b);
let xxx_ = swizzle!(a, [0, 0, 0, _]);
let yyy_ = swizzle!(a, [1, 1, 1, _]);
let zzz_ = swizzle!(a, [2, 2, 2, _]);
arith!(xxx_ * coeff_x + yyy_ * coeff_y + zzz_ * coeff_z)
}
#[inline(always)]
#[allow(dead_code)]
pub(crate) fn matmul2x3x3_in_vec4(a: [$vec4; 2], b: [$vec4; 3]) -> [$vec4; 2] {
let col0 = matmul2x3x1_in_vec4(a, b[0]);
let col1 = matmul2x3x1_in_vec4(a, b[1]);
let col2 = matmul2x3x1_in_vec4(a, b[2]);
[swizzle!(col0, col1, @concat), col2.widen()]
}
#[allow(dead_code)]
#[inline(always)]
pub(crate) fn matmul2x3x3_in_vec2(a: [$vec2; 3], b: [$vec4; 3]) -> [$vec2; 3] {
[
matmul2x3x1_in_vec2(a, b[0]),
matmul2x3x1_in_vec2(a, b[1]),
matmul2x3x1_in_vec2(a, b[2]),
]
}
#[inline(always)]
pub(crate) fn matmul3x3x3(a: [$vec4; 3], b: [$vec4; 3]) -> [$vec4; 3] {
matmul4x3x3(a, b)
}
#[inline(always)]
pub(crate) fn matmul4x3x3(a: [$vec4; 3], b: [$vec4; 3]) -> [$vec4; 3] {
let col0 = matmul4x3x1(a, b[0]);
let col1 = matmul4x3x1(a, b[1]);
let col2 = matmul4x3x1(a, b[2]);
[col0, col1, col2]
}
#[inline(always)]
pub(crate) fn matmul1x4x3(a: $vec4, b: [$vec4; 3]) -> $vec4 {
let transposed = transpose::transpose4x3(b);
matmul3x4x1(transposed, a)
}
#[inline(always)]
#[allow(dead_code)]
pub(crate) fn matmul2x4x3_in_vec4(a: [$vec4; 2], b: [$vec4; 3]) -> [$vec4; 2] {
let col0 = matmul2x4x1(a, b[0]);
let col1 = matmul2x4x1(a, b[1]);
let col2 = matmul2x4x1(a, b[2]);
[swizzle!(col0, col1, @concat), col2.widen()]
}
#[allow(dead_code)]
#[inline(always)]
pub(crate) fn matmul2x4x3_in_vec2(a: [$vec4; 2], b: [$vec4; 3]) -> [$vec2; 3] {
[matmul2x4x1(a, b[0]), matmul2x4x1(a, b[1]), matmul2x4x1(a, b[2])]
}
#[inline(always)]
pub(crate) fn matmul3x4x3(a: [$vec4; 4], b: [$vec4; 3]) -> [$vec4; 3] {
matmul4x4x3(a, b)
}
#[inline(always)]
pub(crate) fn matmul4x4x3(a: [$vec4; 4], b: [$vec4; 3]) -> [$vec4; 3] {
let col0 = matmul4x4x1(a, b[0]);
let col1 = matmul4x4x1(a, b[1]);
let col2 = matmul4x4x1(a, b[2]);
[col0, col1, col2]
}
#[inline(always)]
pub(crate) fn matmul1x1x4(a: $scalar, b: $vec4) -> $vec4 { $vec4::splat(a) * b }
#[inline(always)]
pub(crate) fn matmul2x1x4(a: $vec2, b: $vec4) -> [$vec4; 2] {
let xyxy = swizzle!(a, [0, 1, 0, 1]);
let xxyy = swizzle!(b, [0, 0, 1, 1]);
let zzww = swizzle!(b, [2, 2, 3, 3]);
[xyxy * xxyy, xyxy * zzww]
}
#[inline(always)]
pub(crate) fn matmul3x1x4(a: $vec4, b: $vec4) -> [$vec4; 4] { matmul4x1x4(a, b) }
#[inline(always)]
pub(crate) fn matmul4x1x4(a: $vec4, b: $vec4) -> [$vec4; 4] {
let col0 = a * swizzle!(b, [0, 0, 0, 0]);
let col1 = a * swizzle!(b, [1, 1, 1, 1]);
let col2 = a * swizzle!(b, [2, 2, 2, 2]);
let col3 = a * swizzle!(b, [3, 3, 3, 3]);
[col0, col1, col2, col3]
}
#[inline(always)]
pub(crate) fn matmul1x2x4(a: $vec2, b: [$vec4; 2]) -> $vec4 {
let xyxy = swizzle!(a, [0, 1, 0, 1]);
let scaled_cols01 = b[0] * xyxy;
let scaled_cols23 = b[1] * xyxy;
let even_products = swizzle!(scaled_cols01, scaled_cols23, [0, 2, 4, 6]);
let odd_products = swizzle!(scaled_cols01, scaled_cols23, [1, 3, 5, 7]);
even_products + odd_products
}
#[inline(always)]
pub(crate) fn matmul2x2x4(a: $vec4, b: [$vec4; 2]) -> [$vec4; 2] {
let xyxy = swizzle!(a, [0, 1, 0, 1]);
let zwzw = swizzle!(a, [2, 3, 2, 3]);
let xxzz0 = swizzle!(b[0], [0, 0, 2, 2]);
let yyww0 = swizzle!(b[0], [1, 1, 3, 3]);
let cols01 = arith!(xyxy * xxzz0 + zwzw * yyww0);
let xxzz1 = swizzle!(b[1], [0, 0, 2, 2]);
let yyww1 = swizzle!(b[1], [1, 1, 3, 3]);
let cols23 = arith!(xyxy * xxzz1 + zwzw * yyww1);
[cols01, cols23]
}
#[inline(always)]
pub(crate) fn matmul3x2x4(a: [$vec4; 2], b: [$vec4; 2]) -> [$vec4; 4] {
let b0_xxxx = swizzle!(b[0], [0, 0, 0, 0]);
let b0_yyyy = swizzle!(b[0], [1, 1, 1, 1]);
let b0_zzzz = swizzle!(b[0], [2, 2, 2, 2]);
let b0_wwww = swizzle!(b[0], [3, 3, 3, 3]);
let col0 = arith!((a[0]) * b0_xxxx + (a[1]) * b0_yyyy);
let col1 = arith!((a[0]) * b0_zzzz + (a[1]) * b0_wwww);
let b1_xxxx = swizzle!(b[1], [0, 0, 0, 0]);
let b1_yyyy = swizzle!(b[1], [1, 1, 1, 1]);
let b1_zzzz = swizzle!(b[1], [2, 2, 2, 2]);
let b1_wwww = swizzle!(b[1], [3, 3, 3, 3]);
let col2 = arith!((a[0]) * b1_xxxx + (a[1]) * b1_yyyy);
let col3 = arith!((a[0]) * b1_zzzz + (a[1]) * b1_wwww);
[col0, col1, col2, col3]
}
#[inline(always)]
pub(crate) fn matmul4x2x4(a: [$vec4; 2], b: [$vec4; 2]) -> [$vec4; 4] {
let b0_xxxx = swizzle!(b[0], [0, 0, 0, 0]);
let b0_yyyy = swizzle!(b[0], [1, 1, 1, 1]);
let b0_zzzz = swizzle!(b[0], [2, 2, 2, 2]);
let b0_wwww = swizzle!(b[0], [3, 3, 3, 3]);
let col0 = arith!((a[0]) * b0_xxxx + (a[1]) * b0_yyyy);
let col1 = arith!((a[0]) * b0_zzzz + (a[1]) * b0_wwww);
let b1_xxxx = swizzle!(b[1], [0, 0, 0, 0]);
let b1_yyyy = swizzle!(b[1], [1, 1, 1, 1]);
let b1_zzzz = swizzle!(b[1], [2, 2, 2, 2]);
let b1_wwww = swizzle!(b[1], [3, 3, 3, 3]);
let col2 = arith!((a[0]) * b1_xxxx + (a[1]) * b1_yyyy);
let col3 = arith!((a[0]) * b1_zzzz + (a[1]) * b1_wwww);
[col0, col1, col2, col3]
}
#[inline(always)]
pub(crate) fn matmul1x3x4(a: $vec4, b: [$vec4; 4]) -> $vec4 {
let [coeff_x, coeff_y, coeff_z] = transpose::transpose3x4(b);
let xxx_ = swizzle!(a, [0, 0, 0, _]);
let yyy_ = swizzle!(a, [1, 1, 1, _]);
let zzz_ = swizzle!(a, [2, 2, 2, _]);
arith!(coeff_x * xxx_ + coeff_y * yyy_ + coeff_z * zzz_)
}
#[inline(always)]
#[allow(dead_code)]
pub(crate) fn matmul2x3x4_in_vec4(a: [$vec4; 2], b: [$vec4; 4]) -> [$vec4; 2] {
let col0 = matmul2x3x1_in_vec4(a, b[0]);
let col1 = matmul2x3x1_in_vec4(a, b[1]);
let col2 = matmul2x3x1_in_vec4(a, b[2]);
let col3 = matmul2x3x1_in_vec4(a, b[3]);
[
swizzle!(col0, col1, @concat),
swizzle!(col2, col3, @concat),
]
}
#[allow(dead_code)]
#[inline(always)]
pub(crate) fn matmul2x3x4_in_vec2(a: [$vec2; 3], b: [$vec4; 4]) -> [$vec4; 2] {
let col0 = matmul2x3x1_in_vec2(a, b[0]);
let col1 = matmul2x3x1_in_vec2(a, b[1]);
let col2 = matmul2x3x1_in_vec2(a, b[2]);
let col3 = matmul2x3x1_in_vec2(a, b[3]);
[swizzle!(col0, col1, @concat), swizzle!(col2, col3, @concat)]
}
#[inline(always)]
pub(crate) fn matmul3x3x4(a: [$vec4; 3], b: [$vec4; 4]) -> [$vec4; 4] {
matmul4x3x4(a, b)
}
#[inline(always)]
pub(crate) fn matmul4x3x4(a: [$vec4; 3], b: [$vec4; 4]) -> [$vec4; 4] {
let col0 = matmul4x3x1(a, b[0]);
let col1 = matmul4x3x1(a, b[1]);
let col2 = matmul4x3x1(a, b[2]);
let col3 = matmul4x3x1(a, b[3]);
[col0, col1, col2, col3]
}
#[inline(always)]
pub(crate) fn matmul1x4x4(a: $vec4, b: [$vec4; 4]) -> $vec4 {
let transposed = transpose::transpose4x4(b);
matmul4x4x1(transposed, a)
}
#[inline(always)]
pub(crate) fn matmul2x4x4(a: [$vec4; 2], b: [$vec4; 4]) -> [$vec4; 2] {
let col0 = matmul2x4x1(a, b[0]);
let col1 = matmul2x4x1(a, b[1]);
let col2 = matmul2x4x1(a, b[2]);
let col3 = matmul2x4x1(a, b[3]);
[
swizzle!(col0, col1, @concat),
swizzle!(col2, col3, @concat),
]
}
#[inline(always)]
pub(crate) fn matmul3x4x4(a: [$vec4; 4], b: [$vec4; 4]) -> [$vec4; 4] {
matmul4x4x4(a, b)
}
#[inline(always)]
pub(crate) fn matmul4x4x4(a: [$vec4; 4], b: [$vec4; 4]) -> [$vec4; 4] {
let col0 = matmul4x4x1(a, b[0]);
let col1 = matmul4x4x1(a, b[1]);
let col2 = matmul4x4x1(a, b[2]);
let col3 = matmul4x4x1(a, b[3]);
[col0, col1, col2, col3]
}
};
}
pub(crate) mod f32 {
use super::{super::super::utils::compute_f32x2, *};
use wide::f32x4;
impl_matmul!(f32, compute_f32x2, f32x4);
select_2x3!(in_vec4);
}
pub(crate) mod f64 {
use super::*;
use wide::{f64x2, f64x4};
impl_matmul!(f64, f64x2, f64x4);
cfg_select! {
target_feature = "avx2" => {
select_2x3!(in_vec4);
}
_ => {
select_2x3!(in_vec2);
}
}
}
}