use super::super::{broadcast_words, check_elements, swap_mask_avx2};
use crate::field::gf16::Elem;
use crate::kernel::Matrix;
use crate::kernel::gf16::mul_add_scalar;
use crate::kernel::tables::TowerCoeff;
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
use super::scale_gfni;
const TERM_BLOCK: usize = 16;
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_matrix_gfni(
token: archmage::X64V3GfniCryptoToken,
rows: &mut [u8],
row_len: usize,
nrows: usize,
terms: &[(&[Elem], &[u8])],
) {
check_elements("gf16::mul_add_matrix_gfni", row_len);
let used = nrows
.checked_mul(row_len)
.expect("gf16::mul_add_matrix_gfni: row geometry overflows");
assert!(
rows.len() >= used,
"gf16::mul_add_matrix_gfni: rows is {} bytes but {nrows} rows of {row_len} bytes need {used}",
rows.len(),
);
for (t, &(coeffs, src)) in terms.iter().enumerate() {
assert_eq!(
coeffs.len(),
nrows,
"gf16::mul_add_matrix_gfni: term {t} coefficients is {} but rows is {nrows}",
coeffs.len(),
);
assert_eq!(
src.len(),
row_len,
"gf16::mul_add_matrix_gfni: term {t} source is {} bytes but row_len is {row_len} bytes",
src.len(),
);
}
if row_len == 0 || nrows == 0 || terms.is_empty() {
return;
}
mul_add_matrix_gfni_with(token, rows, row_len, nrows, terms);
}
#[allow(clippy::used_underscore_binding)]
#[allow(unsafe_code)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_matrix_gfni_with<M: Matrix<Elem> + ?Sized>(
_token: archmage::X64V3GfniCryptoToken,
rows: &mut [u8],
row_len: usize,
nrows: usize,
terms: &M,
) {
check_elements("gf16::mul_add_matrix_gfni_with", row_len);
if row_len == 0 || nrows == 0 || terms.len() == 0 {
return;
}
let used = nrows
.checked_mul(row_len)
.expect("gf16::mul_add_matrix_gfni_with: row geometry overflows");
assert!(
rows.len() >= used,
"gf16::mul_add_matrix_gfni_with: rows is {} bytes but {nrows} rows of {row_len} bytes need {used}",
rows.len(),
);
let nrows = nrows.min(rows.len() / row_len);
let mut span = row_len;
for term in 0..terms.len() {
span = span.min(terms.source(term).len());
}
if nrows == 0 || span == 0 {
return;
}
let base = rows.as_mut_ptr();
let swap = swap_mask_avx2();
let mut j = 0;
while j + 4 <= nrows {
unsafe { matrix_group::<4, M>(base.add(j * row_len), row_len, span, j, terms, swap) };
j += 4;
}
if j + 2 <= nrows {
unsafe { matrix_group::<2, M>(base.add(j * row_len), row_len, span, j, terms, swap) };
j += 2;
}
if j < nrows {
unsafe { matrix_group::<1, M>(base.add(j * row_len), row_len, span, j, terms, swap) };
}
}
#[allow(unsafe_code)]
#[archmage::rite(v3_gfni_crypto)]
unsafe fn matrix_group<const N: usize, M: Matrix<Elem> + ?Sized>(
base: *mut u8,
row_len: usize,
span: usize,
first: usize,
terms: &M,
swap: __m256i,
) {
let mut rows = [core::ptr::null_mut::<u8>(); N];
for (k, row) in rows.iter_mut().enumerate() {
*row = unsafe { base.add(k * row_len) };
}
for block_start in (0..terms.len()).step_by(TERM_BLOCK) {
let block_len = (terms.len() - block_start).min(TERM_BLOCK);
let mut words = [[(0i16, 0i16); N]; TERM_BLOCK];
for (t, row_words) in words.iter_mut().take(block_len).enumerate() {
for (k, slot) in row_words.iter_mut().enumerate() {
let coeff = *terms.coefficient(block_start + t, first + k);
*slot = broadcast_words(TowerCoeff::new(coeff));
}
}
let mut offset = 0;
while offset + 32 <= span {
unsafe {
let mut acc = [_mm256_setzero_si256(); N];
for (k, a) in acc.iter_mut().enumerate() {
*a = _mm256_loadu_si256(rows[k].add(offset).cast());
}
for (t, row_words) in words.iter().take(block_len).enumerate() {
let src = terms.source(block_start + t);
let x = _mm256_loadu_si256(src.as_ptr().add(offset).cast());
let swapped = _mm256_shuffle_epi8(x, swap);
for (k, a) in acc.iter_mut().enumerate() {
let (same, cross) = row_words[k];
let scaled = scale_gfni(
x,
swapped,
_mm256_set1_epi16(same),
_mm256_set1_epi16(cross),
);
*a = _mm256_xor_si256(*a, scaled);
}
}
for (k, &a) in acc.iter().enumerate() {
_mm256_storeu_si256(rows[k].add(offset).cast(), a);
}
}
offset += 32;
}
if offset < span {
for (k, &row) in rows.iter().enumerate() {
let tail =
unsafe { core::slice::from_raw_parts_mut(row.add(offset), span - offset) };
for term in block_start..block_start + block_len {
let coeff = *terms.coefficient(term, first + k);
mul_add_scalar(tail, coeff, &terms.source(term)[offset..span]);
}
}
}
}
}