use super::{bfactor_avx512, bmul_avx512, brem_avx512, brem_half_avx512};
use crate::kernel::gf8::{Coeffs, Prepared};
pub(crate) const SCATTER_PEEL_MIN: usize = 512;
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_scatter_avx512(
_token: archmage::X64V4xToken,
rows: &mut [u8],
row_len: usize,
coeffs: &(impl Coeffs + ?Sized),
src: &[u8],
) {
assert_eq!(
src.len(),
row_len,
"mul_add_scatter_avx512: src must span one row"
);
assert!(
coeffs
.count()
.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);
}
struct Group<const N: usize> {
ptrs: [*mut u8; N],
maps: [u64; N],
picks: [usize; N],
}
#[allow(unsafe_code)]
#[archmage::rite(v4x, import_intrinsics)]
fn mul_add_scatter_impl(rows: &mut [u8], coeffs: &(impl Coeffs + ?Sized), src: &[u8]) {
let base = rows.as_mut_ptr();
let row_len = src.len();
let mut group = Group {
ptrs: [base; 4],
maps: [0; 4],
picks: [0; 4],
};
let mut filled = 0;
for j in 0..coeffs.count() {
if coeffs.byte(j) == 0 {
continue;
}
group.ptrs[filled] = unsafe { base.add(j * row_len) };
group.maps[filled] = coeffs.affine(j);
group.picks[filled] = j;
filled += 1;
if filled == 4 {
scatter_rows4(&group, coeffs, src);
filled = 0;
}
}
if filled >= 2 {
let pair = Group {
ptrs: [group.ptrs[0], group.ptrs[1]],
maps: [group.maps[0], group.maps[1]],
picks: [group.picks[0], group.picks[1]],
};
scatter_rows2(&pair, coeffs, src);
}
if filled == 1 || filled == 3 {
let last = filled - 1;
let row = unsafe { core::slice::from_raw_parts_mut(group.ptrs[last], src.len()) };
let coeff = Prepared {
table: coeffs.table(group.picks[last]),
affine: group.maps[last],
};
brem_avx512(row, coeff, src);
}
}
#[allow(unsafe_code)]
#[archmage::rite(v4x, import_intrinsics)]
fn scatter_rows4(group: &Group<4>, coeffs: &(impl Coeffs + ?Sized), src: &[u8]) {
let ptrs = &group.ptrs;
let factors = [
bfactor_avx512(group.maps[0]),
bfactor_avx512(group.maps[1]),
bfactor_avx512(group.maps[2]),
bfactor_avx512(group.maps[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(group, coeffs, 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(group, coeffs, offset, len, src);
}
#[allow(unsafe_code)]
#[archmage::rite(v4x, import_intrinsics)]
fn scatter_rows2(group: &Group<2>, coeffs: &(impl Coeffs + ?Sized), src: &[u8]) {
let ptrs = &group.ptrs;
let factors = [bfactor_avx512(group.maps[0]), bfactor_avx512(group.maps[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(group, coeffs, 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(group, coeffs, offset, len, src);
}
#[allow(unsafe_code)]
#[archmage::rite(v4x, import_intrinsics)]
fn scatter_span<const N: usize>(
group: &Group<N>,
coeffs: &(impl Coeffs + ?Sized),
start: usize,
end: usize,
src: &[u8],
) {
if start >= end {
return;
}
for ((&row, &affine), &pick) in group.ptrs.iter().zip(&group.maps).zip(&group.picks) {
let dst = unsafe { core::slice::from_raw_parts_mut(row.add(start), end - start) };
let coeff = Prepared {
table: coeffs.table(pick),
affine,
};
brem_half_avx512(dst, coeff, &src[start..end]);
}
}