#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
use super::{Affine8D, Blocked, Gfni, bfactor, bfactor128, bmul, bmul128, brem};
use crate::field::gf8b::Elem;
use crate::field::gf8d;
use crate::kernel::gf8::mul_add_nibble;
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_scatter_gfni(
_token: archmage::X64V3GfniCryptoToken,
rows: &mut [u8],
row_len: usize,
coeffs: &[Elem],
src: &[u8],
) {
assert_eq!(row_len, src.len());
assert!(
coeffs
.len()
.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.len()
);
mul_add_scatter_impl::<Gfni>(rows, row_len, coeffs, src);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_scatter_affine(
_token: archmage::X64V3GfniCryptoToken,
rows: &mut [u8],
row_len: usize,
coeffs: &[gf8d::Elem],
src: &[u8],
) {
assert_eq!(row_len, src.len());
assert!(
coeffs
.len()
.checked_mul(row_len)
.is_some_and(|needed| needed <= rows.len()),
"mul_add_scatter_affine: rows buffer does not hold {} rows of {row_len} bytes",
coeffs.len()
);
mul_add_scatter_impl::<Affine8D>(rows, row_len, coeffs, src);
}
#[allow(unsafe_code)]
#[archmage::rite(v3_gfni_crypto)]
fn mul_add_scatter_impl<S: Blocked>(
rows: &mut [u8],
row_len: usize,
coeffs: &[S::Coeff],
src: &[u8],
) {
let base = rows.as_mut_ptr();
let mut ptrs = [base; 4];
let mut group = [S::zero(); 4];
let mut filled = 0;
for (j, &coeff) in coeffs.iter().enumerate() {
if S::byte(coeff) == 0 {
continue;
}
ptrs[filled] = unsafe { base.add(j * row_len) };
group[filled] = coeff;
filled += 1;
if filled == 4 {
scatter_rows4::<S>(ptrs, group, src);
filled = 0;
}
}
if filled >= 2 {
scatter_rows2::<S>([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::<S>(row, group[last], src);
}
}
#[allow(unsafe_code)]
#[archmage::rite(v3_gfni_crypto)]
fn scatter_rows4<S: Blocked>(ptrs: [*mut u8; 4], coeffs: [S::Coeff; 4], src: &[u8]) {
let factors = [
bfactor::<S>(coeffs[0]),
bfactor::<S>(coeffs[1]),
bfactor::<S>(coeffs[2]),
bfactor::<S>(coeffs[3]),
];
let len = src.len();
let src_ptr = src.as_ptr();
let head = super::super::peel_to_align(ptrs[0], len, 1);
scatter_span::<S>(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::<S>(x0, factor));
let r1 = _mm256_xor_si256(d1, bmul::<S>(x1, factor));
let r2 = _mm256_xor_si256(d2, bmul::<S>(x2, factor));
let r3 = _mm256_xor_si256(d3, bmul::<S>(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::<S>(x, factor)));
}
}
offset += 32;
}
scatter_span::<S>(ptrs.iter().zip(&coeffs), offset, len, src);
}
#[allow(unsafe_code)]
#[archmage::rite(v3_gfni_crypto)]
fn scatter_rows2<S: Blocked>(ptrs: [*mut u8; 2], coeffs: [S::Coeff; 2], src: &[u8]) {
let factors = [bfactor::<S>(coeffs[0]), bfactor::<S>(coeffs[1])];
let len = src.len();
let src_ptr = src.as_ptr();
let head = super::super::peel_to_align(ptrs[0], len, 1);
scatter_span::<S>(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::<S>(x0, factor));
let r1 = _mm256_xor_si256(d1, bmul::<S>(x1, factor));
let r2 = _mm256_xor_si256(d2, bmul::<S>(x2, factor));
let r3 = _mm256_xor_si256(d3, bmul::<S>(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::<S>(x, factor)));
}
}
offset += 32;
}
scatter_span::<S>(ptrs.iter().zip(&coeffs), offset, len, src);
}
#[allow(unsafe_code)]
#[archmage::rite(v3_gfni_crypto)]
fn scatter_span<'a, S: Blocked>(
rows: impl Iterator<Item = (&'a *mut u8, &'a S::Coeff)>,
start: usize,
end: usize,
src: &[u8],
) where
S::Coeff: 'a,
{
if start >= end {
return;
}
let vector_end = start + ((end - start) & !15);
for (&row, &coeff) in rows {
let factor = bfactor128::<S>(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, bmul128::<S>(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, S::table(coeff), &src[vector_end..end]);
}
}
}