use super::super::check_gather;
use super::{Affine8D, Blocked, Gfni, bfactor_gfni, bmul_gfni, brem_gfni};
use crate::field::gf8b::Elem;
use crate::field::gf8d;
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_gather_gfni(
token: archmage::X64V3GfniCryptoToken,
dst: &mut [u8],
coeffs: &[Elem],
srcs: &[&[u8]],
) {
check_gather("mul_add_gather_gfni", dst, coeffs.len(), srcs);
let remainder = dst.len() & 127;
let fused =
dst.len() < 128 && coeffs.len() > 2 && remainder != 0 && remainder.trailing_zeros() >= 5;
if fused {
mul_add_gather_impl::<Gfni, true, 4>(token, dst, coeffs, srcs);
} else {
mul_add_gather_impl::<Gfni, false, 4>(token, dst, coeffs, srcs);
}
}
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_gather_gfni_8d(
token: archmage::X64V3GfniCryptoToken,
dst: &mut [u8],
coeffs: &[gf8d::Elem],
srcs: &[&[u8]],
) {
check_gather("mul_add_gather_gfni_8d", dst, coeffs.len(), srcs);
let remainder = dst.len() & 127;
let fused =
dst.len() < 128 && coeffs.len() > 2 && remainder != 0 && remainder.trailing_zeros() >= 5;
if fused {
mul_add_gather_impl::<Affine8D, true, 4>(token, dst, coeffs, srcs);
} else {
mul_add_gather_impl::<Affine8D, false, 4>(token, dst, coeffs, srcs);
}
}
#[cfg(target_arch = "x86_64")]
#[inline(never)]
pub(super) fn mul_add_gather_impl<
S: Blocked,
const FUSED_NATIVE_TAIL: bool,
const TILE_LANES: usize,
>(
token: archmage::X64V3GfniCryptoToken,
dst: &mut [u8],
coeffs: &[S::Coeff],
srcs: &[&[u8]],
) {
assert!((1..=4).contains(&TILE_LANES));
let tile = 32 * TILE_LANES;
let len = dst.len() / tile * tile;
let (tiles, rest) = dst.split_at_mut(len);
gather_tiles::<S, TILE_LANES>(token, tiles, coeffs, srcs);
let consumed = if FUSED_NATIVE_TAIL {
gather_native_tail::<S>(token, rest, coeffs, srcs, len)
} else {
0
};
gather_remainder::<S>(token, &mut rest[consumed..], coeffs, srcs, len + consumed);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
fn gather_tiles<S: Blocked, const TILE_LANES: usize>(
_token: archmage::X64V3GfniCryptoToken,
tiles: &mut [u8],
coeffs: &[S::Coeff],
srcs: &[&[u8]],
) {
let tile = 32 * TILE_LANES;
let mut offset = 0;
for dtile in tiles.chunks_exact_mut(tile) {
let (dl, _) = dtile.as_chunks_mut::<32>();
let mut acc: [__m256i; TILE_LANES] =
core::array::from_fn(|lane| _mm256_loadu_si256(&dl[lane]));
for (&coeff, &src) in coeffs.iter().zip(srcs) {
let factor = bfactor_gfni::<S>(coeff);
let (sl, _) = src[offset..offset + tile].as_chunks::<32>();
for (lane, slot) in acc.iter_mut().enumerate() {
let x = _mm256_loadu_si256(&sl[lane]);
*slot = _mm256_xor_si256(*slot, bmul_gfni::<S>(x, factor));
}
}
for (lane, &value) in acc.iter().enumerate() {
_mm256_storeu_si256(&mut dl[lane], value);
}
offset += tile;
}
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
fn gather_native_tail<S: Blocked>(
_token: archmage::X64V3GfniCryptoToken,
rest: &mut [u8],
coeffs: &[S::Coeff],
srcs: &[&[u8]],
tail: usize,
) -> usize {
let mut consumed = 0;
let rest = if rest.len() >= 64 {
let (pair, after) = rest.split_at_mut(64);
let (dl, _) = pair.as_chunks_mut::<32>();
let (mut a0, mut a1) = (_mm256_loadu_si256(&dl[0]), _mm256_loadu_si256(&dl[1]));
for (&coeff, &src) in coeffs.iter().zip(srcs) {
let factor = bfactor_gfni::<S>(coeff);
let (sl, _) = src[tail..tail + 64].as_chunks::<32>();
let x0 = _mm256_loadu_si256(&sl[0]);
let x1 = _mm256_loadu_si256(&sl[1]);
a0 = _mm256_xor_si256(a0, bmul_gfni::<S>(x0, factor));
a1 = _mm256_xor_si256(a1, bmul_gfni::<S>(x1, factor));
}
_mm256_storeu_si256(&mut dl[0], a0);
_mm256_storeu_si256(&mut dl[1], a1);
consumed += 64;
after
} else {
rest
};
if rest.len() >= 32 {
let (lane, _) = rest.split_at_mut(32);
let (dl, _) = lane.as_chunks_mut::<32>();
let mut acc = _mm256_loadu_si256(&dl[0]);
for (&coeff, &src) in coeffs.iter().zip(srcs) {
let factor = bfactor_gfni::<S>(coeff);
let (sl, _) = src[tail + consumed..tail + consumed + 32].as_chunks::<32>();
let x = _mm256_loadu_si256(&sl[0]);
acc = _mm256_xor_si256(acc, bmul_gfni::<S>(x, factor));
}
_mm256_storeu_si256(&mut dl[0], acc);
consumed += 32;
}
consumed
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
fn gather_remainder<S: Blocked>(
_token: archmage::X64V3GfniCryptoToken,
dst: &mut [u8],
coeffs: &[S::Coeff],
srcs: &[&[u8]],
tail: usize,
) {
for (&coeff, &src) in coeffs.iter().zip(srcs) {
brem_gfni::<S>(dst, coeff, &src[tail..]);
}
}