use super::super::gather::{
check_gather, gather_remainder, mul_add_gather_gfni, mul_add_gather_impl,
};
use super::super::{Affine8B, Affine8BFactor, Gfni};
use crate::field::gf8b::Elem;
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_gather_affine_8b(
token: archmage::X64V3GfniCryptoToken,
dst: &mut [u8],
factors: &[Affine8BFactor],
srcs: &[&[u8]],
) {
check_gather("mul_add_gather_affine_8b", dst, factors.len(), srcs);
let remainder = dst.len() & 127;
let fused =
dst.len() < 128 && factors.len() > 2 && remainder != 0 && remainder.trailing_zeros() >= 5;
if fused {
mul_add_gather_impl::<Affine8B, true, 4>(token, dst, factors, srcs);
} else {
mul_add_gather_impl::<Affine8B, false, 4>(token, dst, factors, srcs);
}
}
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_gather_gfni_axpy_tail(
token: archmage::X64V3GfniCryptoToken,
dst: &mut [u8],
coeffs: &[Elem],
srcs: &[&[u8]],
) {
check_gather("mul_add_gather_gfni_axpy_tail", dst, coeffs.len(), srcs);
mul_add_gather_impl::<Gfni, false, 4>(token, dst, coeffs, srcs);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_gather_gfni_tile<const TILE_LANES: usize>(
token: archmage::X64V3GfniCryptoToken,
dst: &mut [u8],
coeffs: &[Elem],
srcs: &[&[u8]],
) {
assert!(
(1..=4).contains(&TILE_LANES),
"GFNI gather tile must contain 1–4 lanes"
);
if TILE_LANES == 4 {
mul_add_gather_gfni(token, dst, coeffs, srcs);
return;
}
check_gather("mul_add_gather_gfni_tile", dst, coeffs.len(), srcs);
mul_add_gather_impl::<Gfni, false, TILE_LANES>(token, dst, coeffs, srcs);
}
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_gather_gfni_split(
token: archmage::X64V3GfniCryptoToken,
dst: &mut [u8],
coeffs: &[Elem],
srcs: &[&[u8]],
) {
check_gather("mul_add_gather_gfni_split", dst, coeffs.len(), srcs);
mul_add_gather_gfni_split_impl(token, dst, coeffs, srcs);
}
#[cfg(target_arch = "x86_64")]
#[inline(never)]
fn mul_add_gather_gfni_split_impl(
token: archmage::X64V3GfniCryptoToken,
dst: &mut [u8],
coeffs: &[Elem],
srcs: &[&[u8]],
) {
const TILE: usize = 128;
let (dst_tiles, rest) = dst.as_chunks_mut::<TILE>();
split_gather_tiles(token, dst_tiles, coeffs, srcs);
gather_remainder::<Gfni>(token, rest, coeffs, srcs, dst_tiles.len() * TILE);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
fn split_gather_tiles(
_token: archmage::X64V3GfniCryptoToken,
dst_tiles: &mut [[u8; 128]],
coeffs: &[Elem],
srcs: &[&[u8]],
) {
const TILE: usize = 128;
let mut offset = 0;
for dtile in dst_tiles {
let (dl, _) = dtile.as_chunks_mut::<32>();
let mut even: [__m256i; 4] = core::array::from_fn(|lane| _mm256_loadu_si256(&dl[lane]));
let mut odd = [_mm256_setzero_si256(); 4];
for pair in 0..coeffs.len() / 2 {
let index = pair * 2;
let even_factor = _mm256_set1_epi8(coeffs[index].0.cast_signed());
let odd_factor = _mm256_set1_epi8(coeffs[index + 1].0.cast_signed());
let even_src = &srcs[index][offset..offset + TILE];
let odd_src = &srcs[index + 1][offset..offset + TILE];
let (es, _) = even_src.as_chunks::<32>();
let (os, _) = odd_src.as_chunks::<32>();
for lane in 0..4 {
let even_x = _mm256_loadu_si256(&es[lane]);
let odd_x = _mm256_loadu_si256(&os[lane]);
even[lane] =
_mm256_xor_si256(even[lane], _mm256_gf2p8mul_epi8(even_x, even_factor));
odd[lane] = _mm256_xor_si256(odd[lane], _mm256_gf2p8mul_epi8(odd_x, odd_factor));
}
}
if coeffs.len() & 1 != 0 {
let index = coeffs.len() - 1;
let factor = _mm256_set1_epi8(coeffs[index].0.cast_signed());
let (ss, _) = srcs[index][offset..offset + TILE].as_chunks::<32>();
for (lane, slot) in even.iter_mut().enumerate() {
let x = _mm256_loadu_si256(&ss[lane]);
*slot = _mm256_xor_si256(*slot, _mm256_gf2p8mul_epi8(x, factor));
}
}
for (lane, &value) in even.iter().enumerate() {
_mm256_storeu_si256(&mut dl[lane], _mm256_xor_si256(value, odd[lane]));
}
offset += TILE;
}
}