use core::arch::x86_64::*;
use p3_field::interleave::{interleave_u64, interleave_u128, interleave_u256};
use crate::clmul::wide::{Lanes64, TOP_NIBBLE_FOLD};
pub(crate) type Reg = __m512i;
pub(crate) const WIDTH_64: usize = 8;
#[inline(always)]
pub(crate) fn interleave_64(a: Reg, b: Reg, block_len: usize) -> (Reg, Reg) {
match block_len {
1 => interleave_u64(a, b),
2 => interleave_u128(a, b),
4 => interleave_u256(a, b),
8 => (a, b),
_ => panic!("unsupported block_len"),
}
}
#[inline(always)]
pub(crate) unsafe fn gather_3(from: *const u64) -> [Reg; 3] {
unsafe {
let rows = core::array::from_fn::<_, 3, _>(|r| _mm512_loadu_si512(from.add(8 * r).cast()));
core::array::from_fn(|d| {
let mask = |r| {
(0..8).fold(0u8, |mask, k| {
if (3 * k + d) / 8 == r {
mask | 1 << ((3 * k + d) % 8)
} else {
mask
}
})
};
let indices: [i64; 8] = core::array::from_fn(|k| ((3 * k + d) % 8) as i64);
let blended = _mm512_mask_blend_epi64(
mask(2),
_mm512_mask_blend_epi64(mask(1), rows[0], rows[1]),
rows[2],
);
_mm512_permutexvar_epi64(_mm512_loadu_si512(indices.as_ptr().cast()), blended)
})
}
}
#[inline(always)]
pub(crate) unsafe fn scatter_3(to: *mut u64, coordinates: [Reg; 3]) {
unsafe {
for r in 0..3 {
let indices: [i64; 8] = core::array::from_fn(|p| ((8 * r + p) / 3) as i64);
let index = _mm512_loadu_si512(indices.as_ptr().cast());
let values = coordinates.map(|c| _mm512_permutexvar_epi64(index, c));
let mask = |d| {
(0..8).fold(0u8, |mask, p| {
if (8 * r + p) % 3 == d {
mask | 1 << p
} else {
mask
}
})
};
let row = _mm512_mask_blend_epi64(
mask(2),
_mm512_mask_blend_epi64(mask(1), values[0], values[1]),
values[2],
);
_mm512_storeu_si512(to.add(8 * r).cast(), row);
}
}
}
impl Lanes64 for Reg {
#[inline(always)]
fn zero() -> Self {
unsafe { _mm512_setzero_si512() }
}
#[inline(always)]
fn xor(self, b: Self) -> Self {
unsafe { _mm512_xor_si512(self, b) }
}
#[inline(always)]
fn xor3(self, b: Self, c: Self) -> Self {
unsafe { _mm512_ternarylogic_epi64::<0x96>(self, b, c) }
}
#[inline(always)]
fn clmul<const IMM: i32>(self, b: Self) -> Self {
unsafe { _mm512_clmulepi64_epi128::<IMM>(self, b) }
}
#[inline(always)]
fn unpack_low(self, b: Self) -> Self {
unsafe { _mm512_unpacklo_epi64(self, b) }
}
#[inline(always)]
fn unpack_high(self, b: Self) -> Self {
unsafe { _mm512_unpackhi_epi64(self, b) }
}
#[inline(always)]
fn shl<const N: i32>(self) -> Self {
unsafe { _mm512_sll_epi64(self, _mm_cvtsi32_si128(N)) }
}
#[inline(always)]
fn shr<const N: i32>(self) -> Self {
unsafe { _mm512_srl_epi64(self, _mm_cvtsi32_si128(N)) }
}
#[inline(always)]
fn fold_top_nibble(self) -> Self {
unsafe {
let table = _mm512_broadcast_i32x4(_mm_loadu_si128(TOP_NIBBLE_FOLD.as_ptr().cast()));
_mm512_shuffle_epi8(table, _mm512_srli_epi64::<60>(self))
}
}
}