use super::super::{broadcast_words, check_elements, swap_mask_avx2};
use crate::kernel::Matrix;
use crate::kernel::proven_checks::check_terms;
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;
use super::single::mul_add_gfni;
const TERM_BLOCK: usize = 16;
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_matrix_gfni<E>(
token: archmage::X64V3GfniCryptoToken,
rows: &mut [u8],
row_len: usize,
nrows: usize,
terms: &[(&[E], &[u8])],
) where
for<'a> [(&'a [E], &'a [u8])]: Matrix<TowerCoeff>,
{
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(),
);
check_terms("gf16::mul_add_matrix_gfni", row_len, nrows, terms);
if row_len == 0 || nrows == 0 || terms.is_empty() {
return;
}
mul_add_matrix_gfni_with(token, rows, row_len, nrows, terms);
}
#[allow(unsafe_code)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_matrix_gfni_with<M: Matrix<TowerCoeff> + ?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>(token, base.add(j * row_len), row_len, span, j, terms, swap);
};
j += 4;
}
if j + 2 <= nrows {
unsafe {
matrix_group::<2, M>(token, base.add(j * row_len), row_len, span, j, terms, swap);
};
j += 2;
}
if j < nrows {
unsafe {
matrix_group::<1, M>(token, 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<TowerCoeff> + ?Sized>(
token: archmage::X64V3GfniCryptoToken,
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() {
*slot = broadcast_words(terms.coefficient(block_start + t, first + k));
}
}
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_gfni(token, tail, coeff, &terms.source(term)[offset..span]);
}
}
}
}
}