use p3_binary_field::Poly64;
use super::packed_butterfly;
#[cfg(all(
target_arch = "aarch64",
target_endian = "little",
target_feature = "aes"
))]
mod neon;
#[inline]
pub(super) fn butterfly<const INVERSE: bool>(lo: &mut [Poly64], hi: &mut [Poly64], t: Poly64) {
assert_eq!(lo.len(), hi.len(), "butterfly lengths differ");
let covered = if t.to_bits() == 0 {
0
} else {
register_prefix::<INVERSE>(lo, hi, t)
};
packed_butterfly::<Poly64, INVERSE>(&mut lo[covered..], &mut hi[covered..], t);
}
#[inline]
pub(super) fn radix8<const INVERSE: bool>(rows: &mut [&mut [Poly64]; 8], t: &[Poly64; 7]) {
assert!(
rows.iter().all(|row| row.len() == rows[0].len()),
"radix-8 row lengths differ"
);
#[cfg(all(
target_arch = "aarch64",
target_endian = "little",
target_feature = "aes"
))]
let covered = neon::radix8::<INVERSE>(rows, t);
#[cfg(all(
target_arch = "aarch64",
target_endian = "little",
target_feature = "aes"
))]
{
if covered < rows[0].len() {
radix8_scalar_tail::<INVERSE>(rows, t, covered);
}
}
#[cfg(not(all(
target_arch = "aarch64",
target_endian = "little",
target_feature = "aes"
)))]
{
super::radix8::<Poly64, INVERSE>(rows, t);
}
}
#[cfg(all(
target_arch = "aarch64",
target_endian = "little",
target_feature = "aes"
))]
#[inline]
fn radix8_scalar_tail<const INVERSE: bool>(
rows: &mut [&mut [Poly64]; 8],
t: &[Poly64; 7],
column: usize,
) {
let mut values: [Poly64; 8] = rows.each_ref().map(|row| row[column]);
macro_rules! step {
($lo:literal, $hi:literal, $t:literal) => {{
if INVERSE {
values[$hi] += values[$lo];
}
if t[$t].to_bits() != 0 {
values[$lo] += t[$t] * values[$hi];
}
if !INVERSE {
values[$hi] += values[$lo];
}
}};
}
if INVERSE {
step!(0, 1, 3);
step!(2, 3, 4);
step!(4, 5, 5);
step!(6, 7, 6);
step!(0, 2, 1);
step!(1, 3, 1);
step!(4, 6, 2);
step!(5, 7, 2);
step!(0, 4, 0);
step!(1, 5, 0);
step!(2, 6, 0);
step!(3, 7, 0);
} else {
step!(0, 4, 0);
step!(1, 5, 0);
step!(2, 6, 0);
step!(3, 7, 0);
step!(0, 2, 1);
step!(1, 3, 1);
step!(4, 6, 2);
step!(5, 7, 2);
step!(0, 1, 3);
step!(2, 3, 4);
step!(4, 5, 5);
step!(6, 7, 6);
}
for (row, value) in rows.iter_mut().zip(values) {
row[column] = value;
}
}
#[cfg(all(
target_arch = "x86_64",
target_feature = "avx512f",
target_feature = "vpclmulqdq"
))]
type Wide = core::arch::x86_64::__m512i;
#[cfg(all(
target_arch = "x86_64",
target_feature = "avx2",
target_feature = "vpclmulqdq",
not(target_feature = "avx512f")
))]
type Wide = core::arch::x86_64::__m256i;
#[cfg(all(
target_arch = "x86_64",
target_feature = "avx2",
target_feature = "vpclmulqdq"
))]
#[inline]
fn register_prefix<const INVERSE: bool>(lo: &mut [Poly64], hi: &mut [Poly64], t: Poly64) -> usize {
const LANES: usize = <Wide as ClmulRegister>::LANES;
let t = Wide::splat(t.to_bits());
let (lo, _) = lo.as_chunks_mut::<LANES>();
let (hi, _) = hi.as_chunks_mut::<LANES>();
let covered = lo.len() * LANES;
for (lo, hi) in lo.iter_mut().zip(hi) {
unsafe {
let u = Wide::load(lo.as_ptr().cast());
let v = Wide::load(hi.as_ptr().cast());
if INVERSE {
let v = v.xor(u);
Wide::store(hi.as_mut_ptr().cast(), v);
Wide::store(lo.as_mut_ptr().cast(), u.xor(mul(v, t)));
} else {
let u = u.xor(mul(v, t));
Wide::store(lo.as_mut_ptr().cast(), u);
Wide::store(hi.as_mut_ptr().cast(), v.xor(u));
}
}
}
covered
}
#[cfg(all(
target_arch = "aarch64",
target_endian = "little",
target_feature = "aes"
))]
#[inline]
fn register_prefix<const INVERSE: bool>(lo: &mut [Poly64], hi: &mut [Poly64], t: Poly64) -> usize {
neon::butterfly::<INVERSE>(lo, hi, t)
}
#[cfg(not(any(
all(
target_arch = "x86_64",
target_feature = "avx2",
target_feature = "vpclmulqdq"
),
all(
target_arch = "aarch64",
target_endian = "little",
target_feature = "aes"
)
)))]
#[inline]
const fn register_prefix<const INVERSE: bool>(
_lo: &mut [Poly64],
_hi: &mut [Poly64],
_t: Poly64,
) -> usize {
0
}
#[cfg(all(
target_arch = "x86_64",
target_feature = "avx2",
target_feature = "vpclmulqdq"
))]
#[inline(always)]
fn mul<L: ClmulRegister>(v: L, t: L) -> L {
let (lo, hi) = v.products(t);
let a = hi.xor(hi.double());
let spill = a.shr61();
let c = a.xor3(spill, spill.double());
lo.xor3(c, c.shl3())
}
#[cfg(all(
target_arch = "x86_64",
target_feature = "avx2",
target_feature = "vpclmulqdq"
))]
trait ClmulRegister: Copy {
const LANES: usize;
fn splat(word: u64) -> Self;
unsafe fn load(from: *const u64) -> Self;
unsafe fn store(to: *mut u64, value: Self);
fn xor(self, other: Self) -> Self;
fn xor3(self, b: Self, c: Self) -> Self;
fn double(self) -> Self;
fn shr61(self) -> Self;
fn shl3(self) -> Self;
fn products(self, t: Self) -> (Self, Self);
}
#[cfg(all(
target_arch = "x86_64",
target_feature = "avx512f",
target_feature = "vpclmulqdq"
))]
mod avx512 {
use core::arch::x86_64::{
__m512i, _mm512_add_epi64, _mm512_clmulepi64_epi128, _mm512_loadu_si512, _mm512_set1_epi64,
_mm512_slli_epi64, _mm512_srli_epi64, _mm512_storeu_si512, _mm512_ternarylogic_epi64,
_mm512_unpackhi_epi64, _mm512_unpacklo_epi64, _mm512_xor_si512,
};
use super::ClmulRegister;
impl ClmulRegister for __m512i {
const LANES: usize = 8;
#[inline(always)]
fn splat(word: u64) -> Self {
unsafe { _mm512_set1_epi64(word as i64) }
}
#[inline(always)]
unsafe fn load(from: *const u64) -> Self {
unsafe { _mm512_loadu_si512(from.cast()) }
}
#[inline(always)]
unsafe fn store(to: *mut u64, value: Self) {
unsafe { _mm512_storeu_si512(to.cast(), value) }
}
#[inline(always)]
fn xor(self, other: Self) -> Self {
unsafe { _mm512_xor_si512(self, other) }
}
#[inline(always)]
fn xor3(self, b: Self, c: Self) -> Self {
unsafe { _mm512_ternarylogic_epi64::<0x96>(self, b, c) }
}
#[inline(always)]
fn double(self) -> Self {
unsafe { _mm512_add_epi64(self, self) }
}
#[inline(always)]
fn shr61(self) -> Self {
unsafe { _mm512_srli_epi64::<61>(self) }
}
#[inline(always)]
fn shl3(self) -> Self {
unsafe { _mm512_slli_epi64::<3>(self) }
}
#[inline(always)]
fn products(self, t: Self) -> (Self, Self) {
unsafe {
let even = _mm512_clmulepi64_epi128::<0x00>(self, t);
let odd = _mm512_clmulepi64_epi128::<0x01>(self, t);
(
_mm512_unpacklo_epi64(even, odd),
_mm512_unpackhi_epi64(even, odd),
)
}
}
}
}
#[cfg(all(
target_arch = "x86_64",
target_feature = "avx2",
target_feature = "vpclmulqdq",
not(target_feature = "avx512f")
))]
mod avx2 {
use core::arch::x86_64::{
__m256i, _mm256_add_epi64, _mm256_clmulepi64_epi128, _mm256_loadu_si256,
_mm256_set1_epi64x, _mm256_slli_epi64, _mm256_srli_epi64, _mm256_storeu_si256,
_mm256_unpackhi_epi64, _mm256_unpacklo_epi64, _mm256_xor_si256,
};
use super::ClmulRegister;
impl ClmulRegister for __m256i {
const LANES: usize = 4;
#[inline(always)]
fn splat(word: u64) -> Self {
unsafe { _mm256_set1_epi64x(word as i64) }
}
#[inline(always)]
unsafe fn load(from: *const u64) -> Self {
unsafe { _mm256_loadu_si256(from.cast()) }
}
#[inline(always)]
unsafe fn store(to: *mut u64, value: Self) {
unsafe { _mm256_storeu_si256(to.cast(), value) }
}
#[inline(always)]
fn xor(self, other: Self) -> Self {
unsafe { _mm256_xor_si256(self, other) }
}
#[inline(always)]
fn xor3(self, b: Self, c: Self) -> Self {
unsafe { _mm256_xor_si256(_mm256_xor_si256(self, b), c) }
}
#[inline(always)]
fn double(self) -> Self {
unsafe { _mm256_add_epi64(self, self) }
}
#[inline(always)]
fn shr61(self) -> Self {
unsafe { _mm256_srli_epi64::<61>(self) }
}
#[inline(always)]
fn shl3(self) -> Self {
unsafe { _mm256_slli_epi64::<3>(self) }
}
#[inline(always)]
fn products(self, t: Self) -> (Self, Self) {
unsafe {
let even = _mm256_clmulepi64_epi128::<0x00>(self, t);
let odd = _mm256_clmulepi64_epi128::<0x01>(self, t);
(
_mm256_unpacklo_epi64(even, odd),
_mm256_unpackhi_epi64(even, odd),
)
}
}
}
}