use crate::field::gf16::Elem;
use crate::kernel::gf16::mul_add_scalar;
use crate::kernel::tables::TowerCoeff;
use super::gfni::mul_add_gfni;
use super::nibble::mul_add_avx2;
use super::{
NibbleAvx2, NibbleSsse3, TableCoefficient, broadcast_words, nibble_avx2, nibble_ssse3,
scale_avx2, scale_gfni, scale_ssse3, swap_mask256,
};
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
#[allow(unsafe_code)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_scatter_gfni(
token: archmage::X64V3GfniCryptoToken,
rows: &mut [u8],
row_len: usize,
coeffs: &[Elem],
src: &[u8],
) {
super::check_elements("gf16::mul_add_scatter_gfni", row_len);
assert_eq!(
row_len,
src.len(),
"gf16::mul_add_scatter_gfni: row_len is {row_len} bytes but src is {} bytes",
src.len(),
);
let used = coeffs
.len()
.checked_mul(row_len)
.expect("gf16::mul_add_scatter_gfni: row geometry overflows");
assert!(
rows.len() >= used,
"gf16::mul_add_scatter_gfni: rows is {} bytes but {} rows of {row_len} bytes need {used}",
rows.len(),
coeffs.len(),
);
if row_len == 0 || coeffs.is_empty() {
return;
}
let base = rows.as_mut_ptr();
let swap = swap_mask256();
let mut j = 0;
while j + 4 <= coeffs.len() {
let mut group = [Elem(0); 4];
group.copy_from_slice(&coeffs[j..j + 4]);
unsafe { scatter_group_gfni(token, base.add(j * row_len), row_len, group, src, swap) };
j += 4;
}
if j + 2 <= coeffs.len() {
let mut group = [Elem(0); 2];
group.copy_from_slice(&coeffs[j..j + 2]);
unsafe { scatter_group_gfni(token, base.add(j * row_len), row_len, group, src, swap) };
j += 2;
}
if j < coeffs.len() {
unsafe {
let row = core::slice::from_raw_parts_mut(base.add(j * row_len), row_len);
mul_add_gfni(token, row, TowerCoeff::new(coeffs[j]), src);
}
}
}
#[allow(unsafe_code)]
#[archmage::rite(v3_gfni_crypto)]
unsafe fn scatter_group_gfni<const N: usize>(
token: archmage::X64V3GfniCryptoToken,
base: *mut u8,
row_len: usize,
coeffs: [Elem; N],
src: &[u8],
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) };
}
let mut same = [_mm256_setzero_si256(); N];
let mut cross = [_mm256_setzero_si256(); N];
for (k, &coeff) in coeffs.iter().enumerate() {
let (same_word, cross_word) = broadcast_words(TowerCoeff::new(coeff));
same[k] = _mm256_set1_epi16(same_word);
cross[k] = _mm256_set1_epi16(cross_word);
}
let src_ptr = src.as_ptr();
let head = super::peel_to_align(rows[0], row_len, 2);
for (k, &coeff) in coeffs.iter().enumerate() {
let lead = unsafe { core::slice::from_raw_parts_mut(rows[k], head) };
mul_add_gfni(token, lead, TowerCoeff::new(coeff), &src[..head]);
}
let mut offset = head;
while offset + 32 <= row_len {
unsafe {
let x = _mm256_loadu_si256(src_ptr.add(offset).cast());
let swapped = _mm256_shuffle_epi8(x, swap);
for (k, &row) in rows.iter().enumerate() {
let p = row.add(offset);
let d = _mm256_loadu_si256(p.cast());
let scaled = scale_gfni(x, swapped, same[k], cross[k]);
_mm256_storeu_si256(p.cast(), _mm256_xor_si256(d, scaled));
}
}
offset += 32;
}
for (k, &coeff) in coeffs.iter().enumerate() {
let tail =
unsafe { core::slice::from_raw_parts_mut(rows[k].add(offset), row_len - offset) };
mul_add_scalar(tail, coeff, &src[offset..]);
}
}
#[allow(unsafe_code)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_scatter_avx2<C: TableCoefficient>(
token: archmage::X64V3Token,
rows: &mut [u8],
row_len: usize,
coeffs: &[C],
src: &[u8],
) {
super::check_elements("gf16::mul_add_scatter_avx2", row_len);
assert_eq!(
row_len,
src.len(),
"gf16::mul_add_scatter_avx2: row_len is {row_len} bytes but src is {} bytes",
src.len(),
);
let used = coeffs
.len()
.checked_mul(row_len)
.expect("gf16::mul_add_scatter_avx2: row geometry overflows");
assert!(
rows.len() >= used,
"gf16::mul_add_scatter_avx2: rows is {} bytes but {} rows of {row_len} bytes need {used}",
rows.len(),
coeffs.len(),
);
if row_len == 0 || coeffs.is_empty() {
return;
}
unsafe { scatter_rows_avx2(token, rows, row_len, coeffs, src) };
}
#[allow(unsafe_code)]
#[archmage::rite(v3)]
unsafe fn scatter_rows_avx2<C: TableCoefficient>(
token: archmage::X64V3Token,
rows: &mut [u8],
row_len: usize,
coeffs: &[C],
src: &[u8],
) {
let base = rows.as_mut_ptr();
let src_ptr = src.as_ptr();
for group in (0..coeffs.len()).step_by(4) {
let count = (coeffs.len() - group).min(4);
let vectors: [NibbleAvx2; 4] = core::array::from_fn(|i| {
coeffs[group + i.min(count - 1)].with_tables(|tables| nibble_avx2(tables))
});
let head = super::peel_to_align(unsafe { base.add(group * row_len) }, row_len, 2);
for slot in 0..count {
unsafe {
let lead =
core::slice::from_raw_parts_mut(base.add((group + slot) * row_len), head);
coeffs[group + slot]
.with_tables(|tables| mul_add_avx2(token, lead, tables, &src[..head]));
}
}
let mut offset = head;
while offset + 32 <= row_len {
unsafe {
let source = _mm256_loadu_si256(src_ptr.add(offset).cast());
for (slot, vector) in vectors.iter().take(count).enumerate() {
let ptr = base.add((group + slot) * row_len + offset);
_mm256_storeu_si256(
ptr.cast(),
_mm256_xor_si256(
_mm256_loadu_si256(ptr.cast()),
scale_avx2(source, vector),
),
);
}
}
offset += 32;
}
for slot in 0..count {
let tail = unsafe {
core::slice::from_raw_parts_mut(
base.add((group + slot) * row_len + offset),
row_len - offset,
)
};
mul_add_scalar(tail, coeffs[group + slot].coefficient(), &src[offset..]);
}
}
}
#[allow(clippy::used_underscore_binding)]
#[allow(unsafe_code)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_scatter_ssse3<C: TableCoefficient>(
_token: archmage::X64V2Token,
rows: &mut [u8],
row_len: usize,
coeffs: &[C],
src: &[u8],
) {
super::check_elements("gf16::mul_add_scatter_ssse3", row_len);
assert_eq!(
row_len,
src.len(),
"gf16::mul_add_scatter_ssse3: row_len is {row_len} bytes but src is {} bytes",
src.len(),
);
let used = coeffs
.len()
.checked_mul(row_len)
.expect("gf16::mul_add_scatter_ssse3: row geometry overflows");
assert!(
rows.len() >= used,
"gf16::mul_add_scatter_ssse3: rows is {} bytes but {} rows of {row_len} bytes need {used}",
rows.len(),
coeffs.len(),
);
if row_len == 0 || coeffs.is_empty() {
return;
}
unsafe { scatter_rows_ssse3(rows, row_len, coeffs, src) };
}
#[allow(unsafe_code)]
#[archmage::rite(v2)]
unsafe fn scatter_rows_ssse3<C: TableCoefficient>(
rows: &mut [u8],
row_len: usize,
coeffs: &[C],
src: &[u8],
) {
let base = rows.as_mut_ptr();
let src_ptr = src.as_ptr();
for group in (0..coeffs.len()).step_by(4) {
let count = (coeffs.len() - group).min(4);
let vectors: [NibbleSsse3; 4] = core::array::from_fn(|i| {
coeffs[group + i.min(count - 1)].with_tables(|tables| nibble_ssse3(tables))
});
let mut offset = 0;
while offset + 16 <= row_len {
unsafe {
let source = _mm_loadu_si128(src_ptr.add(offset).cast());
for (slot, vector) in vectors.iter().take(count).enumerate() {
let ptr = base.add((group + slot) * row_len + offset);
_mm_storeu_si128(
ptr.cast(),
_mm_xor_si128(_mm_loadu_si128(ptr.cast()), scale_ssse3(source, vector)),
);
}
}
offset += 16;
}
for slot in 0..count {
let tail = unsafe {
core::slice::from_raw_parts_mut(
base.add((group + slot) * row_len + offset),
row_len - offset,
)
};
mul_add_scalar(tail, coeffs[group + slot].coefficient(), &src[offset..]);
}
}
}