use super::{bfactor_gfni, bfactor_half_gfni, bmul_gfni, bmul_half_gfni, brem_gfni};
use crate::kernel::gf8::{Coeffs, Prepared, mul_add_nibble};
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_scatter_gfni<const NATIVE: bool>(
_token: archmage::X64V3GfniCryptoToken,
rows: &mut [u8],
row_len: usize,
coeffs: &(impl Coeffs + ?Sized),
src: &[u8],
) {
assert_eq!(row_len, src.len());
assert!(
coeffs
.count()
.checked_mul(row_len)
.is_some_and(|needed| needed <= rows.len()),
"mul_add_scatter_gfni: rows buffer does not hold {} rows of {row_len} bytes",
coeffs.count()
);
mul_add_scatter_impl::<NATIVE>(rows, row_len, coeffs, src);
}
#[allow(unsafe_code)]
#[archmage::rite(v3_gfni_crypto)]
fn mul_add_scatter_impl<const NATIVE: bool>(
rows: &mut [u8],
row_len: usize,
coeffs: &(impl Coeffs + ?Sized),
src: &[u8],
) {
let base = rows.as_mut_ptr();
let mut ptrs = [base; 4];
let mut group = [Prepared::zero(); 4];
let mut filled = 0;
for j in 0..coeffs.count() {
let coeff = coeffs.resolved(j);
if coeff.is_zero() {
continue;
}
ptrs[filled] = unsafe { base.add(j * row_len) };
group[filled] = coeff;
filled += 1;
if filled == 4 {
scatter_rows4::<NATIVE>(ptrs, group, src);
filled = 0;
}
}
if filled >= 2 {
scatter_rows2::<NATIVE>([ptrs[0], ptrs[1]], [group[0], group[1]], src);
}
if filled == 1 || filled == 3 {
let last = filled - 1;
let row = unsafe { core::slice::from_raw_parts_mut(ptrs[last], src.len()) };
brem_gfni::<NATIVE>(row, group[last], src);
}
}
#[allow(unsafe_code)]
#[archmage::rite(v3_gfni_crypto)]
fn scatter_rows4<const NATIVE: bool>(ptrs: [*mut u8; 4], coeffs: [Prepared; 4], src: &[u8]) {
let len = src.len();
if (512..2048).contains(&len) && len % 32 == 16 && (ptrs[0] as usize).is_multiple_of(16) {
scatter_rows2::<NATIVE>([ptrs[0], ptrs[2]], [coeffs[0], coeffs[2]], src);
scatter_rows2::<NATIVE>([ptrs[1], ptrs[3]], [coeffs[1], coeffs[3]], src);
return;
}
let factors = [
bfactor_gfni::<NATIVE>(coeffs[0]),
bfactor_gfni::<NATIVE>(coeffs[1]),
bfactor_gfni::<NATIVE>(coeffs[2]),
bfactor_gfni::<NATIVE>(coeffs[3]),
];
let src_ptr = src.as_ptr();
let head = if (512..2048).contains(&len) && (ptrs[0] as usize) % 32 == 16 {
(ptrs[0] as usize).wrapping_neg() & 31
} else {
crate::kernel::x86::peel_to_align(ptrs[0], len, 1)
};
scatter_span::<NATIVE>(ptrs.iter().zip(&coeffs), 0, head, src);
let mut offset = head;
while offset + 128 <= len {
unsafe {
let sp = src_ptr.add(offset);
let x0 = _mm256_loadu_si256(sp.cast());
let x1 = _mm256_loadu_si256(sp.add(32).cast());
let x2 = _mm256_loadu_si256(sp.add(64).cast());
let x3 = _mm256_loadu_si256(sp.add(96).cast());
for (&row, &factor) in ptrs.iter().zip(&factors) {
let rp = row.add(offset);
let d0 = _mm256_loadu_si256(rp.cast());
let d1 = _mm256_loadu_si256(rp.add(32).cast());
let d2 = _mm256_loadu_si256(rp.add(64).cast());
let d3 = _mm256_loadu_si256(rp.add(96).cast());
let r0 = _mm256_xor_si256(d0, bmul_gfni::<NATIVE>(x0, factor));
let r1 = _mm256_xor_si256(d1, bmul_gfni::<NATIVE>(x1, factor));
let r2 = _mm256_xor_si256(d2, bmul_gfni::<NATIVE>(x2, factor));
let r3 = _mm256_xor_si256(d3, bmul_gfni::<NATIVE>(x3, factor));
_mm256_storeu_si256(rp.cast(), r0);
_mm256_storeu_si256(rp.add(32).cast(), r1);
_mm256_storeu_si256(rp.add(64).cast(), r2);
_mm256_storeu_si256(rp.add(96).cast(), r3);
}
}
offset += 128;
}
while offset + 32 <= len {
unsafe {
let x = _mm256_loadu_si256(src_ptr.add(offset).cast());
for (&row, &factor) in ptrs.iter().zip(&factors) {
let rp = row.add(offset);
let d = _mm256_loadu_si256(rp.cast());
_mm256_storeu_si256(
rp.cast(),
_mm256_xor_si256(d, bmul_gfni::<NATIVE>(x, factor)),
);
}
}
offset += 32;
}
scatter_span::<NATIVE>(ptrs.iter().zip(&coeffs), offset, len, src);
}
#[allow(unsafe_code)]
#[archmage::rite(v3_gfni_crypto)]
fn scatter_rows2<const NATIVE: bool>(ptrs: [*mut u8; 2], coeffs: [Prepared; 2], src: &[u8]) {
let factors = [
bfactor_gfni::<NATIVE>(coeffs[0]),
bfactor_gfni::<NATIVE>(coeffs[1]),
];
let len = src.len();
let src_ptr = src.as_ptr();
let head = if (512..2048).contains(&len) && (ptrs[0] as usize) % 32 == 16 {
(ptrs[0] as usize).wrapping_neg() & 31
} else {
crate::kernel::x86::peel_to_align(ptrs[0], len, 1)
};
scatter_span::<NATIVE>(ptrs.iter().zip(&coeffs), 0, head, src);
let mut offset = head;
while offset + 128 <= len {
unsafe {
let sp = src_ptr.add(offset);
let x0 = _mm256_loadu_si256(sp.cast());
let x1 = _mm256_loadu_si256(sp.add(32).cast());
let x2 = _mm256_loadu_si256(sp.add(64).cast());
let x3 = _mm256_loadu_si256(sp.add(96).cast());
for (&row, &factor) in ptrs.iter().zip(&factors) {
let rp = row.add(offset);
let d0 = _mm256_loadu_si256(rp.cast());
let d1 = _mm256_loadu_si256(rp.add(32).cast());
let d2 = _mm256_loadu_si256(rp.add(64).cast());
let d3 = _mm256_loadu_si256(rp.add(96).cast());
let r0 = _mm256_xor_si256(d0, bmul_gfni::<NATIVE>(x0, factor));
let r1 = _mm256_xor_si256(d1, bmul_gfni::<NATIVE>(x1, factor));
let r2 = _mm256_xor_si256(d2, bmul_gfni::<NATIVE>(x2, factor));
let r3 = _mm256_xor_si256(d3, bmul_gfni::<NATIVE>(x3, factor));
_mm256_storeu_si256(rp.cast(), r0);
_mm256_storeu_si256(rp.add(32).cast(), r1);
_mm256_storeu_si256(rp.add(64).cast(), r2);
_mm256_storeu_si256(rp.add(96).cast(), r3);
}
}
offset += 128;
}
while offset + 32 <= len {
unsafe {
let x = _mm256_loadu_si256(src_ptr.add(offset).cast());
for (&row, &factor) in ptrs.iter().zip(&factors) {
let rp = row.add(offset);
let d = _mm256_loadu_si256(rp.cast());
_mm256_storeu_si256(
rp.cast(),
_mm256_xor_si256(d, bmul_gfni::<NATIVE>(x, factor)),
);
}
}
offset += 32;
}
scatter_span::<NATIVE>(ptrs.iter().zip(&coeffs), offset, len, src);
}
#[allow(unsafe_code)]
#[archmage::rite(v3_gfni_crypto)]
fn scatter_span<'a, const NATIVE: bool>(
rows: impl Iterator<Item = (&'a *mut u8, &'a Prepared)>,
start: usize,
end: usize,
src: &[u8],
) {
if start >= end {
return;
}
let vector_end = start + ((end - start) & !15);
for (&row, &coeff) in rows {
let factor = bfactor_half_gfni::<NATIVE>(coeff);
let mut offset = start;
while offset < vector_end {
unsafe {
let x = _mm_loadu_si128(src.as_ptr().add(offset).cast());
let ptr = row.add(offset);
let d = _mm_loadu_si128(ptr.cast());
_mm_storeu_si128(
ptr.cast(),
_mm_xor_si128(d, bmul_half_gfni::<NATIVE>(x, factor)),
);
}
offset += 16;
}
if vector_end < end {
let dst =
unsafe { core::slice::from_raw_parts_mut(row.add(vector_end), end - vector_end) };
mul_add_nibble(dst, coeff.table, &src[vector_end..end]);
}
}
}