use super::{MapCoeff, bfactor_avx512, bmul_avx512, brem_avx512};
pub(crate) const SCATTER_PEEL_MIN: usize = 512;
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_scatter_avx512<C: MapCoeff>(
_token: archmage::X64V4xToken,
rows: &mut [u8],
row_len: usize,
coeffs: &[C],
src: &[u8],
) {
assert_eq!(
src.len(),
row_len,
"mul_add_scatter_avx512: src must span one row"
);
assert!(
coeffs
.len()
.checked_mul(row_len)
.is_some_and(|needed| needed <= rows.len()),
"mul_add_scatter_avx512: rows buffer does not hold coefficients rows of {row_len} bytes"
);
mul_add_scatter_impl(rows, coeffs, src);
}
#[allow(unsafe_code)]
#[archmage::rite(v4x, import_intrinsics)]
fn mul_add_scatter_impl<C: MapCoeff>(rows: &mut [u8], coeffs: &[C], src: &[u8]) {
let base = rows.as_mut_ptr();
let row_len = src.len();
let mut ptrs = [base; 4];
let mut group = [C::zero(); 4];
let mut filled = 0;
for (j, &coeff) in coeffs.iter().enumerate() {
if C::is_zero(coeff) {
continue;
}
ptrs[filled] = unsafe { base.add(j * row_len) };
group[filled] = coeff;
filled += 1;
if filled == 4 {
scatter_rows4(ptrs, group, src);
filled = 0;
}
}
if filled >= 2 {
scatter_rows2([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_avx512(row, C::map(group[last]), C::table(group[last]), src);
}
}
#[allow(unsafe_code)]
#[archmage::rite(v4x, import_intrinsics)]
fn scatter_rows4<C: MapCoeff>(ptrs: [*mut u8; 4], coeffs: [C; 4], src: &[u8]) {
let factors = [
bfactor_avx512(C::map(coeffs[0])),
bfactor_avx512(C::map(coeffs[1])),
bfactor_avx512(C::map(coeffs[2])),
bfactor_avx512(C::map(coeffs[3])),
];
let len = src.len();
let head = (ptrs[0] as usize).wrapping_neg() & 63;
let head = if len >= SCATTER_PEEL_MIN && len % 64 != 16 && head < len {
head
} else {
0
};
scatter_span(ptrs.iter().zip(coeffs.iter()), 0, head, src);
let mut offset = head;
while offset + 256 <= len {
unsafe {
let (s, _) = src[offset..offset + 256].as_chunks::<64>();
let x0 = _mm512_loadu_si512(&s[0]);
let x1 = _mm512_loadu_si512(&s[1]);
let x2 = _mm512_loadu_si512(&s[2]);
let x3 = _mm512_loadu_si512(&s[3]);
for (&row, &factor) in ptrs.iter().zip(&factors) {
let (d, _) =
core::slice::from_raw_parts_mut(row.add(offset), 256).as_chunks_mut::<64>();
let d0 = _mm512_loadu_si512(&d[0]);
let d1 = _mm512_loadu_si512(&d[1]);
let d2 = _mm512_loadu_si512(&d[2]);
let d3 = _mm512_loadu_si512(&d[3]);
_mm512_storeu_si512(&mut d[0], _mm512_xor_si512(d0, bmul_avx512(x0, factor)));
_mm512_storeu_si512(&mut d[1], _mm512_xor_si512(d1, bmul_avx512(x1, factor)));
_mm512_storeu_si512(&mut d[2], _mm512_xor_si512(d2, bmul_avx512(x2, factor)));
_mm512_storeu_si512(&mut d[3], _mm512_xor_si512(d3, bmul_avx512(x3, factor)));
}
}
offset += 256;
}
while offset + 64 <= len {
unsafe {
let (s64, _) = src[offset..offset + 64].as_chunks::<64>();
let x = _mm512_loadu_si512(&s64[0]);
for (&row, &factor) in ptrs.iter().zip(&factors) {
let (d64, _) =
core::slice::from_raw_parts_mut(row.add(offset), 64).as_chunks_mut::<64>();
let d = _mm512_loadu_si512(&d64[0]);
_mm512_storeu_si512(&mut d64[0], _mm512_xor_si512(d, bmul_avx512(x, factor)));
}
}
offset += 64;
}
scatter_span(ptrs.iter().zip(coeffs.iter()), offset, len, src);
}
#[allow(unsafe_code)]
#[archmage::rite(v4x, import_intrinsics)]
fn scatter_rows2<C: MapCoeff>(ptrs: [*mut u8; 2], coeffs: [C; 2], src: &[u8]) {
let factors = [
bfactor_avx512(C::map(coeffs[0])),
bfactor_avx512(C::map(coeffs[1])),
];
let len = src.len();
let head = (ptrs[0] as usize).wrapping_neg() & 63;
let head = if len >= SCATTER_PEEL_MIN && len % 64 != 16 && head < len {
head
} else {
0
};
scatter_span(ptrs.iter().zip(coeffs.iter()), 0, head, src);
let mut offset = head;
while offset + 256 <= len {
unsafe {
let (s, _) = src[offset..offset + 256].as_chunks::<64>();
let x0 = _mm512_loadu_si512(&s[0]);
let x1 = _mm512_loadu_si512(&s[1]);
let x2 = _mm512_loadu_si512(&s[2]);
let x3 = _mm512_loadu_si512(&s[3]);
for (&row, &factor) in ptrs.iter().zip(&factors) {
let (d, _) =
core::slice::from_raw_parts_mut(row.add(offset), 256).as_chunks_mut::<64>();
let d0 = _mm512_loadu_si512(&d[0]);
let d1 = _mm512_loadu_si512(&d[1]);
let d2 = _mm512_loadu_si512(&d[2]);
let d3 = _mm512_loadu_si512(&d[3]);
_mm512_storeu_si512(&mut d[0], _mm512_xor_si512(d0, bmul_avx512(x0, factor)));
_mm512_storeu_si512(&mut d[1], _mm512_xor_si512(d1, bmul_avx512(x1, factor)));
_mm512_storeu_si512(&mut d[2], _mm512_xor_si512(d2, bmul_avx512(x2, factor)));
_mm512_storeu_si512(&mut d[3], _mm512_xor_si512(d3, bmul_avx512(x3, factor)));
}
}
offset += 256;
}
while offset + 64 <= len {
unsafe {
let (s64, _) = src[offset..offset + 64].as_chunks::<64>();
let x = _mm512_loadu_si512(&s64[0]);
for (&row, &factor) in ptrs.iter().zip(&factors) {
let (d64, _) =
core::slice::from_raw_parts_mut(row.add(offset), 64).as_chunks_mut::<64>();
let d = _mm512_loadu_si512(&d64[0]);
_mm512_storeu_si512(&mut d64[0], _mm512_xor_si512(d, bmul_avx512(x, factor)));
}
}
offset += 64;
}
scatter_span(ptrs.iter().zip(coeffs.iter()), offset, len, src);
}
#[allow(unsafe_code)]
#[archmage::rite(v4x, import_intrinsics)]
fn scatter_span<'a, C: MapCoeff + 'a>(
rows: impl Iterator<Item = (&'a *mut u8, &'a C)>,
start: usize,
end: usize,
src: &[u8],
) {
if start >= end {
return;
}
for (&row, &coeff) in rows {
let dst = unsafe { core::slice::from_raw_parts_mut(row.add(start), end - start) };
super::super::gfni::mul_add_gfni_8d_impl(
dst,
C::map(coeff),
C::table(coeff),
&src[start..end],
);
}
}