macro_rules! impl_packed_fp8 {
($($u8:ty => $f32:ty),* $(,)?) => {$(
impl $crate::register::PackedFloatRegister<$crate::element::float::spec::Fp8E4M3, $f32> for $u8 {}
impl $crate::register::PackedFloatRegister<$crate::element::float::spec::Fp8E5M2, $f32> for $u8 {}
)*};
}
macro_rules! impl_bit_casts_transmute {
($($from:ty as $to:ty),* $(,)?) => {
const _: () = {$(
#[thermite_macros::inline_always]
impl $crate::register::BitCastRegister<$from> for $to {
fn from_bits(value: $crate::register::Storage<$from>) -> $crate::register::Storage<Self> {
unsafe { $crate::generic_array::const_transmute(value) }
}
}
)*};
};
}
macro_rules! impl_sad {
(@swar16 $u8:ty => $u16:ty) => {
#[thermite_macros::inline_always]
impl $crate::register::Sad16Register<$u16> for $u8 {
fn sad16(
a: $crate::register::Storage<Self>,
b: $crate::register::Storage<Self>,
) -> $crate::register::Storage<$u16> {
$crate::register::sad_cascade_u8_16::<Self, $u16>(
<Self as $crate::register::UnsignedIntegerRegister>::abs_diff(a, b),
)
}
}
};
(@swar32 $u8:ty => $u32:ty) => {
#[thermite_macros::inline_always]
impl $crate::register::Sad32Register<$u32> for $u8 {
fn sad32(
a: $crate::register::Storage<Self>,
b: $crate::register::Storage<Self>,
) -> $crate::register::Storage<$u32> {
$crate::register::sad_cascade_u8_32::<Self, $u32>(
<Self as $crate::register::UnsignedIntegerRegister>::abs_diff(a, b),
)
}
}
};
(@swar64 $u8:ty => $u64:ty) => {
#[thermite_macros::inline_always]
impl $crate::register::Sad64Register<$u64> for $u8 {
fn sad64(
a: $crate::register::Storage<Self>,
b: $crate::register::Storage<Self>,
) -> $crate::register::Storage<$u64> {
$crate::register::sad_cascade_u8_64::<Self, $u64>(
<Self as $crate::register::UnsignedIntegerRegister>::abs_diff(a, b),
)
}
}
};
($($u8:ty => ($u16:ty, $u32:ty, $u64:ty)),* $(,)?) => {$(
impl_sad!(@swar16 $u8 => $u16);
impl_sad!(@swar32 $u8 => $u32);
impl_sad!(@swar64 $u8 => $u64);
)*};
}
macro_rules! impl_sad_u16 {
(@swar $($u16:ty => ($u32:ty, $u64:ty)),* $(,)?) => {$(
impl_sad_u16!(@body $u16 => ($u32, $u64),
|a, b| $crate::register::sad_cascade_u16_32::<Self, $u32>(
<Self as $crate::register::UnsignedIntegerRegister>::abs_diff(a, b)),
|a, b| $crate::register::sad_cascade_u16_64::<Self, $u64>(
<Self as $crate::register::UnsignedIntegerRegister>::abs_diff(a, b)));
)*};
(@scalar $($u16:ty => ($u32:ty, $u64:ty)),* $(,)?) => {$(
impl_sad_u16!(@body $u16 => ($u32, $u64),
|a, b| $crate::register::sad_scalar_u16_32::<Self, $u32>(a, b),
|a, b| $crate::register::sad_scalar_u16_64::<Self, $u64>(a, b));
)*};
(@body $u16:ty => ($u32:ty, $u64:ty), |$a32:ident, $b32:ident| $e32:expr, |$a64:ident, $b64:ident| $e64:expr) => {
#[thermite_macros::inline_always]
impl $crate::register::Sad32Register<$u32> for $u16 {
fn sad32(
$a32: $crate::register::Storage<Self>,
$b32: $crate::register::Storage<Self>,
) -> $crate::register::Storage<$u32> {
$e32
}
}
#[thermite_macros::inline_always]
impl $crate::register::Sad64Register<$u64> for $u16 {
fn sad64(
$a64: $crate::register::Storage<Self>,
$b64: $crate::register::Storage<Self>,
) -> $crate::register::Storage<$u64> {
$e64
}
}
};
}
macro_rules! impl_sad_u32 {
(@swar $($u32:ty => $u64:ty),* $(,)?) => {$(
impl_sad_u32!(@body $u32 => $u64,
|a, b| $crate::register::sad_cascade_u32_64::<Self, $u64>(
<Self as $crate::register::UnsignedIntegerRegister>::abs_diff(a, b)));
)*};
(@scalar $($u32:ty => $u64:ty),* $(,)?) => {$(
impl_sad_u32!(@body $u32 => $u64, |a, b| $crate::register::sad_scalar_u32_64::<Self, $u64>(a, b));
)*};
(@body $u32:ty => $u64:ty, |$a:ident, $b:ident| $e:expr) => {
#[thermite_macros::inline_always]
impl $crate::register::Sad64Register<$u64> for $u32 {
fn sad64(
$a: $crate::register::Storage<Self>,
$b: $crate::register::Storage<Self>,
) -> $crate::register::Storage<$u64> {
$e
}
}
};
}
macro_rules! impl_sad_scalar {
($($u8:ty => ($u16:ty, $u32:ty, $u64:ty)),* $(,)?) => {$(
#[thermite_macros::inline_always]
impl $crate::register::Sad16Register<$u16> for $u8 {
fn sad16(
a: $crate::register::Storage<Self>,
b: $crate::register::Storage<Self>,
) -> $crate::register::Storage<$u16> {
$crate::register::sad_scalar_u8_16::<Self, $u16>(a, b)
}
}
#[thermite_macros::inline_always]
impl $crate::register::Sad32Register<$u32> for $u8 {
fn sad32(
a: $crate::register::Storage<Self>,
b: $crate::register::Storage<Self>,
) -> $crate::register::Storage<$u32> {
$crate::register::sad_scalar_u8_32::<Self, $u32>(a, b)
}
}
#[thermite_macros::inline_always]
impl $crate::register::Sad64Register<$u64> for $u8 {
fn sad64(
a: $crate::register::Storage<Self>,
b: $crate::register::Storage<Self>,
) -> $crate::register::Storage<$u64> {
$crate::register::sad_scalar_u8_64::<Self, $u64>(a, b)
}
}
)*};
}
macro_rules! impl_sad_native_u64 {
(@ssse3 $($u8:ty => ($u16:ty, $u32:ty, $u64:ty) via $sad:ident),* $(,)?) => {$(
#[thermite_macros::inline_always]
impl $crate::register::Sad16Register<$u16> for $u8 {
fn sad16(
a: $crate::register::Storage<Self>,
b: $crate::register::Storage<Self>,
) -> $crate::register::Storage<$u16> {
unsafe {
arch::_mm_maddubs_epi16(
<Self as $crate::register::UnsignedIntegerRegister>::abs_diff(a, b),
arch::_mm_set1_epi8(1),
)
}
}
}
#[thermite_macros::inline_always]
impl $crate::register::Sad32Register<$u32> for $u8 {
fn sad32(
a: $crate::register::Storage<Self>,
b: $crate::register::Storage<Self>,
) -> $crate::register::Storage<$u32> {
unsafe {
arch::_mm_madd_epi16(
<Self as $crate::register::Sad16Register<$u16>>::sad16(a, b),
arch::_mm_set1_epi16(1),
)
}
}
}
#[thermite_macros::inline_always]
impl $crate::register::Sad64Register<$u64> for $u8 {
fn sad64(
a: $crate::register::Storage<Self>,
b: $crate::register::Storage<Self>,
) -> $crate::register::Storage<$u64> {
unsafe { arch::$sad(a, b) }
}
}
)*};
($($u8:ty => ($u16:ty, $u32:ty, $u64:ty) via $sad:ident),* $(,)?) => {$(
impl_sad!(@swar16 $u8 => $u16);
impl_sad!(@swar32 $u8 => $u32);
#[thermite_macros::inline_always]
impl $crate::register::Sad64Register<$u64> for $u8 {
fn sad64(
a: $crate::register::Storage<Self>,
b: $crate::register::Storage<Self>,
) -> $crate::register::Storage<$u64> {
unsafe { arch::$sad(a, b) }
}
}
)*};
}
macro_rules! impl_native_extend_from_scalar {
($($reg:ty => $elem:ty),* $(,)?) => {$(
#[thermite_macros::inline_always]
impl $crate::register::ExtendRegister<$elem> for $reg {
fn extend(value: $crate::register::Storage<$elem>) -> $crate::register::Storage<Self> {
<Self as $crate::register::Register>::single(value)
}
fn narrow(value: $crate::register::Storage<Self>) -> $crate::register::Storage<$elem> {
<Self as $crate::register::Register>::extract::<0>(value)
}
}
)*};
}
macro_rules! impl_native_extract {
(@epi64x2) => {
fn extract<const I: usize>(value: $crate::register::Storage<Self>) -> Self::Element {
const {
assert!(I < 2, "Index out of bounds for register lane extraction");
}
unsafe {
match I {
0 => core::arch::x86_64::_mm_extract_epi64::<0>(value) as _,
_ => core::arch::x86_64::_mm_extract_epi64::<1>(value) as _,
}
}
}
};
(@epi32x4) => {
fn extract<const I: usize>(value: $crate::register::Storage<Self>) -> Self::Element {
const {
assert!(I < 4, "Index out of bounds for register lane extraction");
}
unsafe {
match I {
0 => core::arch::x86_64::_mm_extract_epi32::<0>(value) as _,
1 => core::arch::x86_64::_mm_extract_epi32::<1>(value) as _,
2 => core::arch::x86_64::_mm_extract_epi32::<2>(value) as _,
_ => core::arch::x86_64::_mm_extract_epi32::<3>(value) as _,
}
}
}
};
(@epi16x8) => {
fn extract<const I: usize>(value: $crate::register::Storage<Self>) -> Self::Element {
const {
assert!(I < 8, "Index out of bounds for register lane extraction");
}
unsafe {
match I {
0 => core::arch::x86_64::_mm_extract_epi16::<0>(value) as _,
1 => core::arch::x86_64::_mm_extract_epi16::<1>(value) as _,
2 => core::arch::x86_64::_mm_extract_epi16::<2>(value) as _,
3 => core::arch::x86_64::_mm_extract_epi16::<3>(value) as _,
4 => core::arch::x86_64::_mm_extract_epi16::<4>(value) as _,
5 => core::arch::x86_64::_mm_extract_epi16::<5>(value) as _,
6 => core::arch::x86_64::_mm_extract_epi16::<6>(value) as _,
_ => core::arch::x86_64::_mm_extract_epi16::<7>(value) as _,
}
}
}
};
(@epi8x16) => {
fn extract<const I: usize>(value: $crate::register::Storage<Self>) -> Self::Element {
const {
assert!(I < 16, "Index out of bounds for register lane extraction");
}
unsafe {
match I {
0 => core::arch::x86_64::_mm_extract_epi8::<0>(value) as _,
1 => core::arch::x86_64::_mm_extract_epi8::<1>(value) as _,
2 => core::arch::x86_64::_mm_extract_epi8::<2>(value) as _,
3 => core::arch::x86_64::_mm_extract_epi8::<3>(value) as _,
4 => core::arch::x86_64::_mm_extract_epi8::<4>(value) as _,
5 => core::arch::x86_64::_mm_extract_epi8::<5>(value) as _,
6 => core::arch::x86_64::_mm_extract_epi8::<6>(value) as _,
7 => core::arch::x86_64::_mm_extract_epi8::<7>(value) as _,
8 => core::arch::x86_64::_mm_extract_epi8::<8>(value) as _,
9 => core::arch::x86_64::_mm_extract_epi8::<9>(value) as _,
10 => core::arch::x86_64::_mm_extract_epi8::<10>(value) as _,
11 => core::arch::x86_64::_mm_extract_epi8::<11>(value) as _,
12 => core::arch::x86_64::_mm_extract_epi8::<12>(value) as _,
13 => core::arch::x86_64::_mm_extract_epi8::<13>(value) as _,
14 => core::arch::x86_64::_mm_extract_epi8::<14>(value) as _,
_ => core::arch::x86_64::_mm_extract_epi8::<15>(value) as _,
}
}
}
};
(@epi64x2_v1) => {
fn extract<const I: usize>(value: $crate::register::Storage<Self>) -> Self::Element {
const {
assert!(I < 2, "Index out of bounds for register lane extraction");
}
unsafe {
match I {
0 => core::arch::x86_64::_mm_cvtsi128_si64(value) as _,
_ => {
core::arch::x86_64::_mm_cvtsi128_si64(core::arch::x86_64::_mm_unpackhi_epi64(value, value)) as _
}
}
}
}
};
(@epi32x4_v1) => {
fn extract<const I: usize>(value: $crate::register::Storage<Self>) -> Self::Element {
const {
assert!(I < 4, "Index out of bounds for register lane extraction");
}
unsafe {
match I {
0 => core::arch::x86_64::_mm_cvtsi128_si32(value) as _,
1 => core::arch::x86_64::_mm_cvtsi128_si32(core::arch::x86_64::_mm_shuffle_epi32::<0b01_01_01_01>(
value,
)) as _,
2 => {
core::arch::x86_64::_mm_cvtsi128_si32(core::arch::x86_64::_mm_unpackhi_epi64(value, value)) as _
}
_ => core::arch::x86_64::_mm_cvtsi128_si32(core::arch::x86_64::_mm_shuffle_epi32::<0b11_11_11_11>(
value,
)) as _,
}
}
}
};
(@epi8x16_v1) => {
fn extract<const I: usize>(value: $crate::register::Storage<Self>) -> Self::Element {
const {
assert!(I < 16, "Index out of bounds for register lane extraction");
}
unsafe {
let word = match I / 2 {
0 => core::arch::x86_64::_mm_extract_epi16::<0>(value),
1 => core::arch::x86_64::_mm_extract_epi16::<1>(value),
2 => core::arch::x86_64::_mm_extract_epi16::<2>(value),
3 => core::arch::x86_64::_mm_extract_epi16::<3>(value),
4 => core::arch::x86_64::_mm_extract_epi16::<4>(value),
5 => core::arch::x86_64::_mm_extract_epi16::<5>(value),
6 => core::arch::x86_64::_mm_extract_epi16::<6>(value),
_ => core::arch::x86_64::_mm_extract_epi16::<7>(value),
};
((word >> (8 * (I % 2))) as u8) as _
}
}
};
(@ps128_v1) => {
fn extract<const I: usize>(value: $crate::register::Storage<Self>) -> Self::Element {
const {
assert!(I < 4, "Index out of bounds for register lane extraction");
}
unsafe {
match I {
0 => core::arch::x86_64::_mm_cvtss_f32(value),
1 => core::arch::x86_64::_mm_cvtss_f32(core::arch::x86_64::_mm_shuffle_ps::<0b01_01_01_01>(
value, value,
)),
2 => core::arch::x86_64::_mm_cvtss_f32(core::arch::x86_64::_mm_unpackhi_ps(value, value)),
_ => core::arch::x86_64::_mm_cvtss_f32(core::arch::x86_64::_mm_shuffle_ps::<0b11_11_11_11>(
value, value,
)),
}
}
}
};
(@epi64x4) => {
fn extract<const I: usize>(value: $crate::register::Storage<Self>) -> Self::Element {
const {
assert!(I < 4, "Index out of bounds for register lane extraction");
}
unsafe {
match I {
0 => core::arch::x86_64::_mm_extract_epi64::<0>(core::arch::x86_64::_mm256_extracti128_si256::<0>(
value,
)) as _,
1 => core::arch::x86_64::_mm_extract_epi64::<1>(core::arch::x86_64::_mm256_extracti128_si256::<0>(
value,
)) as _,
2 => core::arch::x86_64::_mm_extract_epi64::<0>(core::arch::x86_64::_mm256_extracti128_si256::<1>(
value,
)) as _,
_ => core::arch::x86_64::_mm_extract_epi64::<1>(core::arch::x86_64::_mm256_extracti128_si256::<1>(
value,
)) as _,
}
}
}
};
(@epi32x8) => {
fn extract<const I: usize>(value: $crate::register::Storage<Self>) -> Self::Element {
const {
assert!(I < 8, "Index out of bounds for register lane extraction");
}
unsafe {
let (lo, hi) = (
core::arch::x86_64::_mm256_extracti128_si256::<0>(value),
core::arch::x86_64::_mm256_extracti128_si256::<1>(value),
);
match I {
0 => core::arch::x86_64::_mm_extract_epi32::<0>(lo) as _,
1 => core::arch::x86_64::_mm_extract_epi32::<1>(lo) as _,
2 => core::arch::x86_64::_mm_extract_epi32::<2>(lo) as _,
3 => core::arch::x86_64::_mm_extract_epi32::<3>(lo) as _,
4 => core::arch::x86_64::_mm_extract_epi32::<0>(hi) as _,
5 => core::arch::x86_64::_mm_extract_epi32::<1>(hi) as _,
6 => core::arch::x86_64::_mm_extract_epi32::<2>(hi) as _,
_ => core::arch::x86_64::_mm_extract_epi32::<3>(hi) as _,
}
}
}
};
(@epi16x16) => {
fn extract<const I: usize>(value: $crate::register::Storage<Self>) -> Self::Element {
const {
assert!(I < 16, "Index out of bounds for register lane extraction");
}
unsafe {
let (lo, hi) = (
core::arch::x86_64::_mm256_extracti128_si256::<0>(value),
core::arch::x86_64::_mm256_extracti128_si256::<1>(value),
);
match I {
0 => core::arch::x86_64::_mm_extract_epi16::<0>(lo) as _,
1 => core::arch::x86_64::_mm_extract_epi16::<1>(lo) as _,
2 => core::arch::x86_64::_mm_extract_epi16::<2>(lo) as _,
3 => core::arch::x86_64::_mm_extract_epi16::<3>(lo) as _,
4 => core::arch::x86_64::_mm_extract_epi16::<4>(lo) as _,
5 => core::arch::x86_64::_mm_extract_epi16::<5>(lo) as _,
6 => core::arch::x86_64::_mm_extract_epi16::<6>(lo) as _,
7 => core::arch::x86_64::_mm_extract_epi16::<7>(lo) as _,
8 => core::arch::x86_64::_mm_extract_epi16::<0>(hi) as _,
9 => core::arch::x86_64::_mm_extract_epi16::<1>(hi) as _,
10 => core::arch::x86_64::_mm_extract_epi16::<2>(hi) as _,
11 => core::arch::x86_64::_mm_extract_epi16::<3>(hi) as _,
12 => core::arch::x86_64::_mm_extract_epi16::<4>(hi) as _,
13 => core::arch::x86_64::_mm_extract_epi16::<5>(hi) as _,
14 => core::arch::x86_64::_mm_extract_epi16::<6>(hi) as _,
_ => core::arch::x86_64::_mm_extract_epi16::<7>(hi) as _,
}
}
}
};
(@epi8x32) => {
fn extract<const I: usize>(value: $crate::register::Storage<Self>) -> Self::Element {
const {
assert!(I < 32, "Index out of bounds for register lane extraction");
}
unsafe {
let (lo, hi) = (
core::arch::x86_64::_mm256_extracti128_si256::<0>(value),
core::arch::x86_64::_mm256_extracti128_si256::<1>(value),
);
match I {
0 => core::arch::x86_64::_mm_extract_epi8::<0>(lo) as _,
1 => core::arch::x86_64::_mm_extract_epi8::<1>(lo) as _,
2 => core::arch::x86_64::_mm_extract_epi8::<2>(lo) as _,
3 => core::arch::x86_64::_mm_extract_epi8::<3>(lo) as _,
4 => core::arch::x86_64::_mm_extract_epi8::<4>(lo) as _,
5 => core::arch::x86_64::_mm_extract_epi8::<5>(lo) as _,
6 => core::arch::x86_64::_mm_extract_epi8::<6>(lo) as _,
7 => core::arch::x86_64::_mm_extract_epi8::<7>(lo) as _,
8 => core::arch::x86_64::_mm_extract_epi8::<8>(lo) as _,
9 => core::arch::x86_64::_mm_extract_epi8::<9>(lo) as _,
10 => core::arch::x86_64::_mm_extract_epi8::<10>(lo) as _,
11 => core::arch::x86_64::_mm_extract_epi8::<11>(lo) as _,
12 => core::arch::x86_64::_mm_extract_epi8::<12>(lo) as _,
13 => core::arch::x86_64::_mm_extract_epi8::<13>(lo) as _,
14 => core::arch::x86_64::_mm_extract_epi8::<14>(lo) as _,
15 => core::arch::x86_64::_mm_extract_epi8::<15>(lo) as _,
16 => core::arch::x86_64::_mm_extract_epi8::<0>(hi) as _,
17 => core::arch::x86_64::_mm_extract_epi8::<1>(hi) as _,
18 => core::arch::x86_64::_mm_extract_epi8::<2>(hi) as _,
19 => core::arch::x86_64::_mm_extract_epi8::<3>(hi) as _,
20 => core::arch::x86_64::_mm_extract_epi8::<4>(hi) as _,
21 => core::arch::x86_64::_mm_extract_epi8::<5>(hi) as _,
22 => core::arch::x86_64::_mm_extract_epi8::<6>(hi) as _,
23 => core::arch::x86_64::_mm_extract_epi8::<7>(hi) as _,
24 => core::arch::x86_64::_mm_extract_epi8::<8>(hi) as _,
25 => core::arch::x86_64::_mm_extract_epi8::<9>(hi) as _,
26 => core::arch::x86_64::_mm_extract_epi8::<10>(hi) as _,
27 => core::arch::x86_64::_mm_extract_epi8::<11>(hi) as _,
28 => core::arch::x86_64::_mm_extract_epi8::<12>(hi) as _,
29 => core::arch::x86_64::_mm_extract_epi8::<13>(hi) as _,
30 => core::arch::x86_64::_mm_extract_epi8::<14>(hi) as _,
_ => core::arch::x86_64::_mm_extract_epi8::<15>(hi) as _,
}
}
}
};
(@ps128) => {
fn extract<const I: usize>(value: $crate::register::Storage<Self>) -> Self::Element {
const {
assert!(I < 4, "Index out of bounds for register lane extraction");
}
unsafe {
match I {
0 => core::arch::x86_64::_mm_cvtss_f32(value),
1 => f32::from_bits(core::arch::x86_64::_mm_extract_ps::<1>(value) as u32),
2 => f32::from_bits(core::arch::x86_64::_mm_extract_ps::<2>(value) as u32),
_ => f32::from_bits(core::arch::x86_64::_mm_extract_ps::<3>(value) as u32),
}
}
}
};
(@ps256) => {
fn extract<const I: usize>(value: $crate::register::Storage<Self>) -> Self::Element {
const {
assert!(I < 8, "Index out of bounds for register lane extraction");
}
unsafe {
let (lo, hi) = (
core::arch::x86_64::_mm256_extractf128_ps::<0>(value),
core::arch::x86_64::_mm256_extractf128_ps::<1>(value),
);
match I {
0 => core::arch::x86_64::_mm_cvtss_f32(lo),
1 => f32::from_bits(core::arch::x86_64::_mm_extract_ps::<1>(lo) as u32),
2 => f32::from_bits(core::arch::x86_64::_mm_extract_ps::<2>(lo) as u32),
3 => f32::from_bits(core::arch::x86_64::_mm_extract_ps::<3>(lo) as u32),
4 => core::arch::x86_64::_mm_cvtss_f32(hi),
5 => f32::from_bits(core::arch::x86_64::_mm_extract_ps::<1>(hi) as u32),
6 => f32::from_bits(core::arch::x86_64::_mm_extract_ps::<2>(hi) as u32),
_ => f32::from_bits(core::arch::x86_64::_mm_extract_ps::<3>(hi) as u32),
}
}
}
};
(@pd128) => {
fn extract<const I: usize>(value: $crate::register::Storage<Self>) -> Self::Element {
const {
assert!(I < 2, "Index out of bounds for register lane extraction");
}
unsafe {
match I {
0 => core::arch::x86_64::_mm_cvtsd_f64(value),
_ => core::arch::x86_64::_mm_cvtsd_f64(core::arch::x86_64::_mm_unpackhi_pd(value, value)),
}
}
}
};
(@pd256) => {
fn extract<const I: usize>(value: $crate::register::Storage<Self>) -> Self::Element {
const {
assert!(I < 4, "Index out of bounds for register lane extraction");
}
unsafe {
let (lo, hi) = (
core::arch::x86_64::_mm256_extractf128_pd::<0>(value),
core::arch::x86_64::_mm256_extractf128_pd::<1>(value),
);
match I {
0 => core::arch::x86_64::_mm_cvtsd_f64(lo),
1 => core::arch::x86_64::_mm_cvtsd_f64(core::arch::x86_64::_mm_unpackhi_pd(lo, lo)),
2 => core::arch::x86_64::_mm_cvtsd_f64(hi),
_ => core::arch::x86_64::_mm_cvtsd_f64(core::arch::x86_64::_mm_unpackhi_pd(hi, hi)),
}
}
}
};
}
macro_rules! impl_byte_align_alignr {
() => {
const HAS_NATIVE_ALIGN: bool = true;
fn align<const OFFSET: usize>(a: Storage<Self>, b: Storage<Self>) -> Storage<Self> {
match const { OFFSET * core::mem::size_of::<Self::Element>() } {
0 => unsafe { arch::_mm_alignr_epi8::<0>(b, a) },
1 => unsafe { arch::_mm_alignr_epi8::<1>(b, a) },
2 => unsafe { arch::_mm_alignr_epi8::<2>(b, a) },
3 => unsafe { arch::_mm_alignr_epi8::<3>(b, a) },
4 => unsafe { arch::_mm_alignr_epi8::<4>(b, a) },
5 => unsafe { arch::_mm_alignr_epi8::<5>(b, a) },
6 => unsafe { arch::_mm_alignr_epi8::<6>(b, a) },
7 => unsafe { arch::_mm_alignr_epi8::<7>(b, a) },
8 => unsafe { arch::_mm_alignr_epi8::<8>(b, a) },
9 => unsafe { arch::_mm_alignr_epi8::<9>(b, a) },
10 => unsafe { arch::_mm_alignr_epi8::<10>(b, a) },
11 => unsafe { arch::_mm_alignr_epi8::<11>(b, a) },
12 => unsafe { arch::_mm_alignr_epi8::<12>(b, a) },
13 => unsafe { arch::_mm_alignr_epi8::<13>(b, a) },
14 => unsafe { arch::_mm_alignr_epi8::<14>(b, a) },
15 => unsafe { arch::_mm_alignr_epi8::<15>(b, a) },
16 => unsafe { arch::_mm_alignr_epi8::<16>(b, a) },
_ => Self::swizzle_const::<$crate::swizzle::AlignIndices<OFFSET, Self::Lanes>>(a, b),
}
}
};
}
macro_rules! impl_byte_align_alignr256 {
() => {
const HAS_NATIVE_ALIGN: bool = true;
fn align<const OFFSET: usize>(a: Storage<Self>, b: Storage<Self>) -> Storage<Self> {
let mid = unsafe { arch::_mm256_permute2x128_si256::<0x21>(a, b) };
match OFFSET * core::mem::size_of::<Self::Element>() {
0 => unsafe { arch::_mm256_alignr_epi8::<0>(mid, a) },
1 => unsafe { arch::_mm256_alignr_epi8::<1>(mid, a) },
2 => unsafe { arch::_mm256_alignr_epi8::<2>(mid, a) },
3 => unsafe { arch::_mm256_alignr_epi8::<3>(mid, a) },
4 => unsafe { arch::_mm256_alignr_epi8::<4>(mid, a) },
5 => unsafe { arch::_mm256_alignr_epi8::<5>(mid, a) },
6 => unsafe { arch::_mm256_alignr_epi8::<6>(mid, a) },
7 => unsafe { arch::_mm256_alignr_epi8::<7>(mid, a) },
8 => unsafe { arch::_mm256_alignr_epi8::<8>(mid, a) },
9 => unsafe { arch::_mm256_alignr_epi8::<9>(mid, a) },
10 => unsafe { arch::_mm256_alignr_epi8::<10>(mid, a) },
11 => unsafe { arch::_mm256_alignr_epi8::<11>(mid, a) },
12 => unsafe { arch::_mm256_alignr_epi8::<12>(mid, a) },
13 => unsafe { arch::_mm256_alignr_epi8::<13>(mid, a) },
14 => unsafe { arch::_mm256_alignr_epi8::<14>(mid, a) },
15 => unsafe { arch::_mm256_alignr_epi8::<15>(mid, a) },
16 => unsafe { arch::_mm256_alignr_epi8::<16>(mid, a) },
17 => unsafe { arch::_mm256_alignr_epi8::<1>(b, mid) },
18 => unsafe { arch::_mm256_alignr_epi8::<2>(b, mid) },
19 => unsafe { arch::_mm256_alignr_epi8::<3>(b, mid) },
20 => unsafe { arch::_mm256_alignr_epi8::<4>(b, mid) },
21 => unsafe { arch::_mm256_alignr_epi8::<5>(b, mid) },
22 => unsafe { arch::_mm256_alignr_epi8::<6>(b, mid) },
23 => unsafe { arch::_mm256_alignr_epi8::<7>(b, mid) },
24 => unsafe { arch::_mm256_alignr_epi8::<8>(b, mid) },
25 => unsafe { arch::_mm256_alignr_epi8::<9>(b, mid) },
26 => unsafe { arch::_mm256_alignr_epi8::<10>(b, mid) },
27 => unsafe { arch::_mm256_alignr_epi8::<11>(b, mid) },
28 => unsafe { arch::_mm256_alignr_epi8::<12>(b, mid) },
29 => unsafe { arch::_mm256_alignr_epi8::<13>(b, mid) },
30 => unsafe { arch::_mm256_alignr_epi8::<14>(b, mid) },
31 => unsafe { arch::_mm256_alignr_epi8::<15>(b, mid) },
32 => unsafe { arch::_mm256_alignr_epi8::<16>(b, mid) },
_ => Self::swizzle_const::<$crate::swizzle::AlignIndices<OFFSET, Self::Lanes>>(a, b),
}
}
};
}
macro_rules! impl_byteshift_align {
() => {
const HAS_NATIVE_ALIGN: bool = true;
fn align<const OFFSET: usize>(a: Storage<Self>, b: Storage<Self>) -> Storage<Self> {
match const { OFFSET * core::mem::size_of::<Self::Element>() } {
0 => Self::bitor(Self::bshri::<0>(a), Self::bshli::<16>(b)),
1 => Self::bitor(Self::bshri::<1>(a), Self::bshli::<15>(b)),
2 => Self::bitor(Self::bshri::<2>(a), Self::bshli::<14>(b)),
3 => Self::bitor(Self::bshri::<3>(a), Self::bshli::<13>(b)),
4 => Self::bitor(Self::bshri::<4>(a), Self::bshli::<12>(b)),
5 => Self::bitor(Self::bshri::<5>(a), Self::bshli::<11>(b)),
6 => Self::bitor(Self::bshri::<6>(a), Self::bshli::<10>(b)),
7 => Self::bitor(Self::bshri::<7>(a), Self::bshli::<9>(b)),
8 => Self::bitor(Self::bshri::<8>(a), Self::bshli::<8>(b)),
9 => Self::bitor(Self::bshri::<9>(a), Self::bshli::<7>(b)),
10 => Self::bitor(Self::bshri::<10>(a), Self::bshli::<6>(b)),
11 => Self::bitor(Self::bshri::<11>(a), Self::bshli::<5>(b)),
12 => Self::bitor(Self::bshri::<12>(a), Self::bshli::<4>(b)),
13 => Self::bitor(Self::bshri::<13>(a), Self::bshli::<3>(b)),
14 => Self::bitor(Self::bshri::<14>(a), Self::bshli::<2>(b)),
15 => Self::bitor(Self::bshri::<15>(a), Self::bshli::<1>(b)),
16 => Self::bitor(Self::bshri::<16>(a), Self::bshli::<0>(b)),
_ => Self::swizzle_const::<$crate::swizzle::AlignIndices<OFFSET, Self::Lanes>>(a, b),
}
}
};
}
macro_rules! impl_float_align_via_bits {
($bits:ty) => {
const HAS_NATIVE_ALIGN: bool = <$bits as $crate::register::Register>::HAS_NATIVE_ALIGN;
fn align<const OFFSET: usize>(a: Storage<Self>, b: Storage<Self>) -> Storage<Self> {
<Self as $crate::register::BitCastRegister<$bits>>::from_bits(
<$bits as $crate::register::Register>::align::<OFFSET>(
<$bits as $crate::register::BitCastRegister<Self>>::from_bits(a),
<$bits as $crate::register::BitCastRegister<Self>>::from_bits(b),
),
)
}
};
}
macro_rules! impl_widen_indices_x86 {
($($reg:ty => $shape:ident),* $(,)?) => {
$(
#[thermite_macros::inline_always]
impl $crate::register::WidenIndexRegister for $reg {
fn widen_indices(
idxs: &generic_array::GenericArray<u8, generic_array::typenum::U8>,
) -> generic_array::GenericArray<u32, <Self as $crate::register::CoreRegister>::Lanes> {
unsafe { impl_widen_indices_x86!(@body idxs, $shape) }
}
}
)*
};
(@body $idxs:ident, x2) => {{
let q = arch::_mm_cvtepu8_epi32(arch::_mm_cvtsi32_si128(
core::ptr::read_unaligned($idxs.as_ptr() as *const i32),
));
core::mem::transmute_copy(&q)
}};
(@body $idxs:ident, x4) => {{
let q = arch::_mm_cvtepu8_epi32(arch::_mm_cvtsi32_si128(
core::ptr::read_unaligned($idxs.as_ptr() as *const i32),
));
core::mem::transmute_copy(&q)
}};
(@body $idxs:ident, x8) => {{
let o = arch::_mm256_cvtepu8_epi32(arch::_mm_loadl_epi64($idxs.as_ptr() as *const arch::__m128i));
core::mem::transmute_copy(&o)
}};
(@body $idxs:ident, x8h) => {{
let p = $idxs.as_ptr();
let lo = arch::_mm_cvtepu8_epi32(arch::_mm_cvtsi32_si128(core::ptr::read_unaligned(p as *const i32)));
let hi = arch::_mm_cvtepu8_epi32(arch::_mm_cvtsi32_si128(core::ptr::read_unaligned(
p.add(4) as *const i32,
)));
core::mem::transmute_copy(&[lo, hi])
}};
}
macro_rules! impl_bit_casts {
($($from:ty as $to:ty => $conv:ident),* $(,)?) => {
const _: () = {$(
#[thermite_macros::inline_always]
impl $crate::register::BitCastRegister<$from> for $to {
fn from_bits(value: Storage<$from>) -> Storage<Self> {
unsafe { arch::$conv(value) }
}
}
)*};
};
}
macro_rules! impl_bit_casts_identity {
($($from:ty as $to:ty),* $(,)?) => {
const _: () = {$(
#[thermite_macros::inline_always]
impl $crate::register::BitCastRegister<$from> for $to {
fn from_bits(value: Storage<$from>) -> Storage<Self> {
value
}
}
)*};
};
}
macro_rules! impl_type_casts {
($($from:ty as $to:ty => $conv:ident $(| $fast_conv:ident)?),* $(,)?) => {
const _: () = {$(
#[thermite_macros::inline_always]
impl $crate::register::CastRegister<$from> for $to {
fn cast_from(value: Storage<$from>) -> Storage<Self> {
unsafe { arch::$conv(value) }
}
$(
fn fast_cast_from(value: Storage<$from>) -> Storage<Self> {
unsafe { arch::$fast_conv(value) }
}
)?
}
)*};
};
}
macro_rules! impl_float_to_int_casts {
($($from:ty as $to:ty => $conv:ident sat $sat:ident $(| $fast_conv:ident)?),* $(,)?) => {
const _: () = {$(
#[thermite_macros::inline_always]
impl $crate::register::CastRegister<$from> for $to {
fn cast_from(value: Storage<$from>) -> Storage<Self> {
unsafe { arch::$conv(value) }
}
fn saturating_cast_from(value: Storage<$from>) -> Storage<Self> {
unsafe { arch::$sat(value) }
}
$(
fn fast_cast_from(value: Storage<$from>) -> Storage<Self> {
unsafe { arch::$fast_conv(value) }
}
)?
}
)*};
};
}
macro_rules! impl_cast_via {
($($from:ty as $to:ty => via $mid:ty),* $(,)?) => {
const _: () = {$(
#[thermite_macros::inline_always]
impl $crate::register::CastRegister<$from> for $to {
fn cast_from(value: Storage<$from>) -> Storage<Self> {
<Self as $crate::register::CastRegister<$mid>>::cast_from(
<$mid as $crate::register::CastRegister<$from>>::cast_from(value),
)
}
fn saturating_cast_from(value: Storage<$from>) -> Storage<Self> {
<Self as $crate::register::CastRegister<$mid>>::saturating_cast_from(
<$mid as $crate::register::CastRegister<$from>>::saturating_cast_from(value),
)
}
}
)*};
};
}
macro_rules! impl_cast_via_widen {
($($from:ty as $to:ty => via $mid:ty),* $(,)?) => {
const _: () = {$(
#[thermite_macros::inline_always]
impl $crate::register::CastRegister<$from> for $to {
fn cast_from(value: Storage<$from>) -> Storage<Self> {
<Self as $crate::register::CastRegister<$mid>>::cast_from(
<$mid as $crate::register::CastRegister<$from>>::cast_from(value),
)
}
fn saturating_cast_from(value: Storage<$from>) -> Storage<Self> {
<Self as $crate::register::CastRegister<$mid>>::saturating_cast_from(
<$mid as $crate::register::CastRegister<$from>>::cast_from(value),
)
}
}
)*};
};
}
macro_rules! impl_cast_from_via {
($($from:ty as $to:ty => via $mid:ty),* $(,)?) => {
const _: () = {$(
#[thermite_macros::inline_always]
impl $crate::register::CastRegister<$from> for $to {
fn cast_from(value: Storage<$from>) -> Storage<Self> {
<Self as $crate::register::CastRegister<$mid>>::cast_from(
<$mid as $crate::register::CastRegister<$from>>::cast_from(value),
)
}
}
)*};
};
}
macro_rules! impl_sign_cast_matrix_8_to_32_64 {
($([$i32:ty, $u32:ty, $i64:ty, $u64:ty, $i8:ty, $u8:ty]),* $(,)?) => {$(
impl_cast_from_via! {
$i8 as $u32 => via $i32,
$i8 as $u64 => via $i64,
$u8 as $i32 => via $u32,
$u8 as $i64 => via $u64,
$i32 as $u8 => via $i8,
$u32 as $i8 => via $u8,
$i64 as $u8 => via $i8,
$u64 as $i8 => via $u8,
}
)*};
}
macro_rules! impl_sign_cast_matrix_16_to_32_64 {
($([$i32:ty, $u32:ty, $i64:ty, $u64:ty, $i16:ty, $u16:ty]),* $(,)?) => {$(
impl_cast_from_via! {
$i16 as $u32 => via $i32,
$i16 as $u64 => via $i64,
$u16 as $i32 => via $u32,
$u16 as $i64 => via $u64,
$i32 as $u16 => via $i16,
$u32 as $i16 => via $u16,
$i64 as $u16 => via $i16,
$u64 as $i16 => via $u16,
}
)*};
}
macro_rules! impl_sign_cast_matrix_8_16_to_32_64 {
($([$i32:ty, $u32:ty, $i64:ty, $u64:ty, $i16:ty, $u16:ty, $i8:ty, $u8:ty]),* $(,)?) => {$(
impl_sign_cast_matrix_8_to_32_64! {
[$i32, $u32, $i64, $u64, $i8, $u8]
}
impl_sign_cast_matrix_16_to_32_64! {
[$i32, $u32, $i64, $u64, $i16, $u16]
}
)*};
}
macro_rules! impl_sign_cast_matrix_32_64 {
($([$i32:ty, $u32:ty, $i64:ty, $u64:ty]),* $(,)?) => {$(
impl_cast_from_via! {
$i32 as $u64 => via $i64,
$u32 as $i64 => via $u64,
$i64 as $u32 => via $i32,
$u64 as $i32 => via $u32,
}
)*};
}
macro_rules! impl_sign_cast_matrix {
($([$f32:ty, $f64:ty, $i32:ty, $u32:ty, $i64:ty, $u64:ty, $i16:ty, $u16:ty, $i8:ty, $u8:ty]),* $(,)?) => {$(
impl_sign_cast_matrix_8_16_to_32_64! {
[$i32, $u32, $i64, $u64, $i16, $u16, $i8, $u8]
}
impl_sign_cast_matrix_32_64! {
[$i32, $u32, $i64, $u64]
}
)*};
}
macro_rules! impl_sign_cast_matrix_8_16 {
($([$i16:ty, $u16:ty, $i8:ty, $u8:ty]),* $(,)?) => {$(
impl_cast_from_via! {
$i8 as $u16 => via $i16,
$u8 as $i16 => via $u16,
$i16 as $u8 => via $i8,
$u16 as $i8 => via $u8,
}
)*};
}
macro_rules! impl_float_cast_matrix {
($([$f32:ty, $f64:ty, $i32:ty, $u32:ty, $i64:ty, $u64:ty, $i16:ty, $u16:ty, $i8:ty, $u8:ty]),* $(,)?) => {$(
impl_cast_via! {
$f32 as $i16 => via $i32,
$f32 as $i8 => via $i32,
$f32 as $u16 => via $u32,
$f32 as $u8 => via $u32,
$f64 as $i32 => via $i64,
$f64 as $i16 => via $i64,
$f64 as $i8 => via $i64,
$f64 as $u32 => via $u64,
$f64 as $u16 => via $u64,
$f64 as $u8 => via $u64,
}
impl_cast_via_widen! {
$f32 as $i64 => via $f64,
$f32 as $u64 => via $f64,
}
)*};
}
macro_rules! impl_mask_casts {
($($from:ty as $to:ty => $conv:ident),* $(,)?) => {
const _: () = {$(
#[thermite_macros::inline_always]
impl $crate::register::CastMaskRegister<$from> for $to {
fn mask_from(value: Storage<$from>) -> Storage<Self> {
unsafe { arch::$conv(value) }
}
}
)*};
};
}
macro_rules! impl_concat_bool_register2 {
($e:ty, $r:ty) => {
const _: () = {
use $crate::element::MaskElement;
#[thermite_macros::inline_always]
impl $crate::register::ConcatRegister<bool> for $r {
fn concat(lo: Storage<bool>, hi: Storage<bool>) -> Storage<Self> {
<Self as $crate::register::ConcatRegister<$e>>::concat(<$e>::from_bool(lo), <$e>::from_bool(hi))
}
fn split(value: Storage<Self>) -> (Storage<bool>, Storage<bool>) {
let (lo, hi) = <Self as $crate::register::ConcatRegister<$e>>::split(value);
(lo.to_bool(), hi.to_bool())
}
}
#[thermite_macros::inline_always]
impl $crate::register::ExtendRegister<bool> for $r {
fn extend(value: Storage<bool>) -> Storage<Self> {
<Self as $crate::register::ExtendRegister<$e>>::extend(<$e>::from_bool(value))
}
fn narrow(value: Storage<Self>) -> Storage<bool> {
<Self as $crate::register::ExtendRegister<$e>>::narrow(value).to_bool()
}
}
};
};
}
macro_rules! impl_newregister {
($($r:ty),*) => {$(
impl $crate::register::NewRegister<
<$r as $crate::register::Register>::Element,
<$r as $crate::register::CoreRegister>::Lanes,
<$r as $crate::register::CoreRegister>::Storage
> for $r {
type New<T: $crate::vector::splat::NewConst<
<$r as $crate::register::Register>::Element,
<$r as $crate::register::CoreRegister>::Lanes>
> = Self;
}
impl<T> $crate::vector::splat::VectorValue<T, Storage<Self>> for $r
where
T: $crate::vector::splat::NewConst<
<$r as $crate::register::Register>::Element,
<$r as $crate::register::CoreRegister>::Lanes
>,
{
const VALUE: Storage<Self> = const { unsafe { $crate::generic_array::const_transmute(T::VALUES) } };
}
)*};
}
macro_rules! compress_via_table {
() => {
#[inline(always)]
fn compress(
value: $crate::register::Storage<Self>,
mask: $crate::register::Storage<<Self as $crate::register::CoreRegister>::Mask>,
) -> $crate::register::Storage<Self> {
$crate::backend::generic::polyfills::compress_permute::<Self>(value, mask)
}
#[inline(always)]
fn compress_z(
value: $crate::register::Storage<Self>,
mask: $crate::register::Storage<<Self as $crate::register::CoreRegister>::Mask>,
) -> $crate::register::Storage<Self> {
$crate::backend::generic::polyfills::compress_permute::<Self>(
<Self as $crate::register::CoreRegister>::zz(mask, value),
mask,
)
}
#[inline(always)]
fn expand(
value: $crate::register::Storage<Self>,
mask: $crate::register::Storage<<Self as $crate::register::CoreRegister>::Mask>,
) -> $crate::register::Storage<Self> {
$crate::backend::generic::polyfills::expand_permute::<Self>(value, mask)
}
#[inline(always)]
fn expand_z(
value: $crate::register::Storage<Self>,
mask: $crate::register::Storage<<Self as $crate::register::CoreRegister>::Mask>,
) -> $crate::register::Storage<Self> {
<Self as $crate::register::CoreRegister>::zz(
mask,
$crate::backend::generic::polyfills::expand_permute::<Self>(value, mask),
)
}
};
}
macro_rules! compress_via_wide {
() => {
#[inline(always)]
fn compress(
value: $crate::register::Storage<Self>,
mask: $crate::register::Storage<<Self as $crate::register::CoreRegister>::Mask>,
) -> $crate::register::Storage<Self> {
$crate::backend::generic::polyfills::compress_permute_wide::<Self>(value, mask)
}
#[inline(always)]
fn compress_z(
value: $crate::register::Storage<Self>,
mask: $crate::register::Storage<<Self as $crate::register::CoreRegister>::Mask>,
) -> $crate::register::Storage<Self> {
$crate::backend::generic::polyfills::compress_permute_wide::<Self>(
<Self as $crate::register::CoreRegister>::zz(mask, value),
mask,
)
}
#[inline(always)]
fn expand(
value: $crate::register::Storage<Self>,
mask: $crate::register::Storage<<Self as $crate::register::CoreRegister>::Mask>,
) -> $crate::register::Storage<Self> {
$crate::backend::generic::polyfills::expand_permute_wide::<Self>(value, mask)
}
#[inline(always)]
fn expand_z(
value: $crate::register::Storage<Self>,
mask: $crate::register::Storage<<Self as $crate::register::CoreRegister>::Mask>,
) -> $crate::register::Storage<Self> {
<Self as $crate::register::CoreRegister>::zz(
mask,
$crate::backend::generic::polyfills::expand_permute_wide::<Self>(value, mask),
)
}
};
}
macro_rules! impl_native_radix3 {
($ilv3:path, $dilv3:path) => {
#[inline(always)]
fn interleave_radix<const N: usize>(
inputs: [$crate::register::Storage<Self>; N],
) -> [$crate::register::Storage<Self>; N] {
if const { N == 3 } {
let (x, y, z) = unsafe {
(
*inputs.get_unchecked(0),
*inputs.get_unchecked(1),
*inputs.get_unchecked(2),
)
};
let (a, b, c) = unsafe { $ilv3(x, y, z) };
let mut out = [<Self as $crate::register::CoreRegister>::EMPTY; N];
unsafe {
*out.get_unchecked_mut(0) = a;
*out.get_unchecked_mut(1) = b;
*out.get_unchecked_mut(2) = c;
}
out
} else {
$crate::backend::generic::polyfills::interleave_radix_default::<Self, N>(inputs)
}
}
#[inline(always)]
fn deinterleave_radix<const N: usize>(
inputs: [$crate::register::Storage<Self>; N],
) -> [$crate::register::Storage<Self>; N] {
if const { N == 3 } {
let (a, b, c) = unsafe {
(
*inputs.get_unchecked(0),
*inputs.get_unchecked(1),
*inputs.get_unchecked(2),
)
};
let (x, y, z) = unsafe { $dilv3(a, b, c) };
let mut out = [<Self as $crate::register::CoreRegister>::EMPTY; N];
unsafe {
*out.get_unchecked_mut(0) = x;
*out.get_unchecked_mut(1) = y;
*out.get_unchecked_mut(2) = z;
}
out
} else {
$crate::backend::generic::polyfills::deinterleave_radix_default::<Self, N>(inputs)
}
}
};
}
macro_rules! sort_via_network {
(2) => { sort_via_network!(@emit sort_2, bitonic_clean_2); };
(4) => { sort_via_network!(@emit sort_4, bitonic_clean_4); };
($other:tt) => {};
(@emit $sort:ident, $clean:ident) => {
#[inline(always)]
fn sort_by<O: $crate::sort::SortOrder>(
value: $crate::register::Storage<Self>,
) -> $crate::register::Storage<Self> {
$crate::backend::generic::polyfills::sort::$sort::<Self, O>(value)
}
#[inline(always)]
fn bitonic_clean_by<O: $crate::sort::SortOrder>(
value: $crate::register::Storage<Self>,
) -> $crate::register::Storage<Self> {
$crate::backend::generic::polyfills::sort::$clean::<Self, O>(value)
}
};
}