use super::super::check_scattered;
use super::super::gfni::mul_add_gfni_8d_impl;
use super::{MapCoeff, bfactor_avx512, bmul_avx512};
use crate::kernel::Matrix;
pub(crate) const MATRIX_PEEL_MIN: usize = 8192;
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_matrix_avx512<C: MapCoeff>(
_token: archmage::X64V4xToken,
rows: &mut [u8],
row_len: usize,
nrows: usize,
terms: &[(&[C], &[u8])],
) {
assert!(
nrows
.checked_mul(row_len)
.is_some_and(|needed| needed <= rows.len()),
"mul_add_matrix_avx512: rows buffer does not hold {nrows} rows of {row_len} bytes"
);
for (t, (coeffs, src)) in terms.iter().enumerate() {
assert_eq!(
coeffs.len(),
nrows,
"mul_add_matrix_avx512: term {t} needs {nrows} coefficients"
);
assert_eq!(
src.len(),
row_len,
"mul_add_matrix_avx512: term {t} source must span one row"
);
}
if row_len == 0 || nrows == 0 || terms.is_empty() {
return;
}
mul_add_matrix_impl::<C, [(&[C], &[u8])], false>(rows, row_len, nrows, terms);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_into_matrix_avx512<C: MapCoeff>(
_token: archmage::X64V4xToken,
rows: &mut [u8],
row_len: usize,
nrows: usize,
terms: &[(&[C], &[u8])],
) {
assert!(
nrows
.checked_mul(row_len)
.is_some_and(|needed| needed <= rows.len()),
"mul_into_matrix_avx512: rows buffer does not hold {nrows} rows of {row_len} bytes"
);
for (t, (coeffs, src)) in terms.iter().enumerate() {
assert_eq!(
coeffs.len(),
nrows,
"mul_into_matrix_avx512: term {t} needs {nrows} coefficients"
);
assert_eq!(
src.len(),
row_len,
"mul_into_matrix_avx512: term {t} source must span one row"
);
}
if terms.is_empty() {
rows[..nrows * row_len].fill(0);
return;
}
if row_len == 0 || nrows == 0 {
return;
}
mul_into_matrix_avx512_with(_token, rows, row_len, nrows, terms);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_matrix_avx512_with<C: MapCoeff, M: Matrix<C> + ?Sized>(
_token: archmage::X64V4xToken,
rows: &mut [u8],
row_len: usize,
nrows: usize,
terms: &M,
) {
assert!(
nrows
.checked_mul(row_len)
.is_some_and(|needed| needed <= rows.len()),
"mul_add_matrix_avx512: rows buffer does not hold {nrows} rows of {row_len} bytes"
);
for term in 0..terms.len() {
assert_eq!(
terms.source(term).len(),
row_len,
"mul_add_matrix_avx512: term {term} source must span one row"
);
}
if terms.len() == 0 || row_len == 0 || nrows == 0 {
return;
}
mul_add_matrix_impl::<C, M, false>(rows, row_len, nrows, terms);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_into_matrix_avx512_with<C: MapCoeff, M: Matrix<C> + ?Sized>(
_token: archmage::X64V4xToken,
rows: &mut [u8],
row_len: usize,
nrows: usize,
terms: &M,
) {
assert!(
nrows
.checked_mul(row_len)
.is_some_and(|needed| needed <= rows.len()),
"mul_into_matrix_avx512: rows buffer does not hold {nrows} rows of {row_len} bytes"
);
for term in 0..terms.len() {
assert_eq!(
terms.source(term).len(),
row_len,
"mul_into_matrix_avx512: term {term} source must span one row"
);
}
if terms.len() == 0 {
if row_len != 0 && nrows != 0 {
rows[..nrows * row_len].fill(0);
}
return;
}
if row_len == 0 || nrows == 0 {
return;
}
mul_add_matrix_impl::<C, M, true>(rows, row_len, nrows, terms);
}
#[allow(unsafe_code)]
#[archmage::rite(v4x, import_intrinsics)]
fn mul_add_matrix_impl<C: MapCoeff, M: Matrix<C> + ?Sized, const OVERWRITE: bool>(
rows: &mut [u8],
row_len: usize,
nrows: usize,
terms: &M,
) {
let base = rows.as_mut_ptr();
let mut g = 0;
while g + 4 <= nrows {
let ptrs = unsafe {
[
base.add(g * row_len),
base.add((g + 1) * row_len),
base.add((g + 2) * row_len),
base.add((g + 3) * row_len),
]
};
matrix_rows4::<C, M, OVERWRITE>(ptrs, row_len, g, terms);
g += 4;
}
if g + 2 <= nrows {
let ptrs = unsafe { [base.add(g * row_len), base.add((g + 1) * row_len)] };
matrix_rows2::<C, M, OVERWRITE>(ptrs, row_len, g, terms);
g += 2;
}
if g < nrows {
let ptr = unsafe { base.add(g * row_len) };
matrix_rows1::<C, M, OVERWRITE>(ptr, row_len, g, terms);
}
}
#[archmage::rite(v4x, import_intrinsics)]
fn matrix_rows4<C: MapCoeff, M: Matrix<C> + ?Sized, const OVERWRITE: bool>(
ptrs: [*mut u8; 4],
row_len: usize,
g: usize,
terms: &M,
) {
let head = (ptrs[0] as usize).wrapping_neg() & 63;
let head = if row_len >= MATRIX_PEEL_MIN && head < row_len && row_len.is_multiple_of(64) {
head
} else {
0
};
if head > 0 {
matrix_tail::<C, M, OVERWRITE>(&ptrs, head, 0, g, terms);
}
let vector_len = row_len & !63;
let mut tile = head;
while tile + 256 <= vector_len {
rows_tile4::<C, M, OVERWRITE>(ptrs, tile, g, terms);
tile += 256;
}
while tile + 64 <= vector_len {
rows_lane4::<C, M, OVERWRITE>(ptrs, tile, g, terms);
tile += 64;
}
matrix_tail::<C, M, OVERWRITE>(&ptrs, row_len, tile, g, terms);
}
#[archmage::rite(v4x, import_intrinsics)]
fn matrix_rows2<C: MapCoeff, M: Matrix<C> + ?Sized, const OVERWRITE: bool>(
ptrs: [*mut u8; 2],
row_len: usize,
g: usize,
terms: &M,
) {
let head = (ptrs[0] as usize).wrapping_neg() & 63;
let head = if row_len >= MATRIX_PEEL_MIN && head < row_len && row_len.is_multiple_of(64) {
head
} else {
0
};
if head > 0 {
matrix_tail::<C, M, OVERWRITE>(&ptrs, head, 0, g, terms);
}
let vector_len = row_len & !63;
let mut tile = head;
while tile + 256 <= vector_len {
rows_tile2::<C, M, OVERWRITE>(ptrs, tile, g, terms);
tile += 256;
}
while tile + 64 <= vector_len {
rows_lane2::<C, M, OVERWRITE>(ptrs, tile, g, terms);
tile += 64;
}
matrix_tail::<C, M, OVERWRITE>(&ptrs, row_len, tile, g, terms);
}
#[allow(unsafe_code)]
#[archmage::rite(v4x, import_intrinsics)]
fn matrix_rows1<C: MapCoeff, M: Matrix<C> + ?Sized, const OVERWRITE: bool>(
ptr: *mut u8,
row_len: usize,
g: usize,
terms: &M,
) {
let head = (ptr as usize).wrapping_neg() & 63;
let head = if row_len >= MATRIX_PEEL_MIN && head < row_len && row_len.is_multiple_of(64) {
head
} else {
0
};
if head > 0 {
matrix_tail::<C, M, OVERWRITE>(&[ptr], head, 0, g, terms);
}
let vector_len = row_len & !63;
let mut tile = head;
while tile + 256 <= vector_len {
rows_tile1::<C, M, OVERWRITE>(ptr, tile, g, terms);
tile += 256;
}
while tile + 64 <= vector_len {
rows_lane1::<C, M, OVERWRITE>(ptr, tile, g, terms);
tile += 64;
}
let tail = unsafe { core::slice::from_raw_parts_mut(ptr.add(tile), row_len - tile) };
if OVERWRITE {
tail.fill(0);
}
for term in 0..terms.len() {
let coeff = *terms.coefficient(term, g);
if !C::is_zero(coeff) {
mul_add_gfni_8d_impl(
tail,
C::map(coeff),
C::table(coeff),
&terms.source(term)[tile..],
);
}
}
}
#[allow(unsafe_code)]
#[archmage::rite(v4x, import_intrinsics)]
fn rows_tile4<C: MapCoeff, M: Matrix<C> + ?Sized, const OVERWRITE: bool>(
ptrs: [*mut u8; 4],
tile: usize,
g: usize,
terms: &M,
) {
unsafe {
let mut acc = [[_mm512_setzero_si512(); 4]; 4];
if !OVERWRITE {
for (&ptr, slots) in ptrs.iter().zip(acc.iter_mut()) {
for (lane, slot) in slots.iter_mut().enumerate() {
{
let (rc, _) = core::slice::from_raw_parts(ptr.add(tile + 64 * lane), 64)
.as_chunks::<64>();
*slot = _mm512_loadu_si512(&rc[0]);
}
}
}
}
for term in 0..terms.len() {
let maps = [
C::map(*terms.coefficient(term, g)),
C::map(*terms.coefficient(term, g + 1)),
C::map(*terms.coefficient(term, g + 2)),
C::map(*terms.coefficient(term, g + 3)),
];
let f = [
bfactor_avx512(maps[0]),
bfactor_avx512(maps[1]),
bfactor_avx512(maps[2]),
bfactor_avx512(maps[3]),
];
let src = terms.source(term);
let (s, _) = src[tile..tile + 256].as_chunks::<64>();
let x = [
_mm512_loadu_si512(&s[0]),
_mm512_loadu_si512(&s[1]),
_mm512_loadu_si512(&s[2]),
_mm512_loadu_si512(&s[3]),
];
for (slots, &factor) in acc.iter_mut().zip(&f) {
for (slot, &value) in slots.iter_mut().zip(&x) {
*slot = _mm512_xor_si512(*slot, bmul_avx512(value, factor));
}
}
}
for (&ptr, slots) in ptrs.iter().zip(&acc) {
for (lane, &value) in slots.iter().enumerate() {
{
let (rc, _) = core::slice::from_raw_parts_mut(ptr.add(tile + 64 * lane), 64)
.as_chunks_mut::<64>();
_mm512_storeu_si512(&mut rc[0], value);
}
}
}
}
}
#[allow(unsafe_code)]
#[archmage::rite(v4x, import_intrinsics)]
fn rows_lane4<C: MapCoeff, M: Matrix<C> + ?Sized, const OVERWRITE: bool>(
ptrs: [*mut u8; 4],
tile: usize,
g: usize,
terms: &M,
) {
unsafe {
let mut acc = [_mm512_setzero_si512(); 4];
if !OVERWRITE {
for (&ptr, slot) in ptrs.iter().zip(acc.iter_mut()) {
{
let (rc, _) = core::slice::from_raw_parts(ptr.add(tile), 64).as_chunks::<64>();
*slot = _mm512_loadu_si512(&rc[0]);
}
}
}
for term in 0..terms.len() {
let src = terms.source(term);
let (s, _) = src[tile..tile + 64].as_chunks::<64>();
let x = _mm512_loadu_si512(&s[0]);
for (row, slot) in acc.iter_mut().enumerate() {
let map = C::map(*terms.coefficient(term, g + row));
*slot = _mm512_xor_si512(*slot, bmul_avx512(x, bfactor_avx512(map)));
}
}
for (&ptr, &value) in ptrs.iter().zip(&acc) {
{
let (rc, _) =
core::slice::from_raw_parts_mut(ptr.add(tile), 64).as_chunks_mut::<64>();
_mm512_storeu_si512(&mut rc[0], value);
}
}
}
}
#[allow(unsafe_code)]
#[archmage::rite(v4x, import_intrinsics)]
fn rows_tile2<C: MapCoeff, M: Matrix<C> + ?Sized, const OVERWRITE: bool>(
ptrs: [*mut u8; 2],
tile: usize,
g: usize,
terms: &M,
) {
unsafe {
let mut acc = [[_mm512_setzero_si512(); 4]; 2];
if !OVERWRITE {
for (&ptr, slots) in ptrs.iter().zip(acc.iter_mut()) {
for (lane, slot) in slots.iter_mut().enumerate() {
{
let (rc, _) = core::slice::from_raw_parts(ptr.add(tile + 64 * lane), 64)
.as_chunks::<64>();
*slot = _mm512_loadu_si512(&rc[0]);
}
}
}
}
for term in 0..terms.len() {
let maps = [
C::map(*terms.coefficient(term, g)),
C::map(*terms.coefficient(term, g + 1)),
];
let f = [bfactor_avx512(maps[0]), bfactor_avx512(maps[1])];
let src = terms.source(term);
let (s, _) = src[tile..tile + 256].as_chunks::<64>();
let x = [
_mm512_loadu_si512(&s[0]),
_mm512_loadu_si512(&s[1]),
_mm512_loadu_si512(&s[2]),
_mm512_loadu_si512(&s[3]),
];
for (slots, &factor) in acc.iter_mut().zip(&f) {
for (slot, &value) in slots.iter_mut().zip(&x) {
*slot = _mm512_xor_si512(*slot, bmul_avx512(value, factor));
}
}
}
for (&ptr, slots) in ptrs.iter().zip(&acc) {
for (lane, &value) in slots.iter().enumerate() {
{
let (rc, _) = core::slice::from_raw_parts_mut(ptr.add(tile + 64 * lane), 64)
.as_chunks_mut::<64>();
_mm512_storeu_si512(&mut rc[0], value);
}
}
}
}
}
#[allow(unsafe_code)]
#[archmage::rite(v4x, import_intrinsics)]
fn rows_lane2<C: MapCoeff, M: Matrix<C> + ?Sized, const OVERWRITE: bool>(
ptrs: [*mut u8; 2],
tile: usize,
g: usize,
terms: &M,
) {
unsafe {
let mut acc = [_mm512_setzero_si512(); 2];
if !OVERWRITE {
for (&ptr, slot) in ptrs.iter().zip(acc.iter_mut()) {
{
let (rc, _) = core::slice::from_raw_parts(ptr.add(tile), 64).as_chunks::<64>();
*slot = _mm512_loadu_si512(&rc[0]);
}
}
}
for term in 0..terms.len() {
let src = terms.source(term);
let (s, _) = src[tile..tile + 64].as_chunks::<64>();
let x = _mm512_loadu_si512(&s[0]);
for (row, slot) in acc.iter_mut().enumerate() {
let map = C::map(*terms.coefficient(term, g + row));
*slot = _mm512_xor_si512(*slot, bmul_avx512(x, bfactor_avx512(map)));
}
}
for (&ptr, &value) in ptrs.iter().zip(&acc) {
{
let (rc, _) =
core::slice::from_raw_parts_mut(ptr.add(tile), 64).as_chunks_mut::<64>();
_mm512_storeu_si512(&mut rc[0], value);
}
}
}
}
#[allow(unsafe_code)]
#[archmage::rite(v4x, import_intrinsics)]
fn rows_tile1<C: MapCoeff, M: Matrix<C> + ?Sized, const OVERWRITE: bool>(
ptr: *mut u8,
tile: usize,
g: usize,
terms: &M,
) {
unsafe {
let mut acc = [_mm512_setzero_si512(); 4];
if !OVERWRITE {
for (lane, slot) in acc.iter_mut().enumerate() {
{
let (rc, _) = core::slice::from_raw_parts(ptr.add(tile + 64 * lane), 64)
.as_chunks::<64>();
*slot = _mm512_loadu_si512(&rc[0]);
}
}
}
for term in 0..terms.len() {
let factor = bfactor_avx512(C::map(*terms.coefficient(term, g)));
let src = terms.source(term);
let (s, _) = src[tile..tile + 256].as_chunks::<64>();
acc[0] = _mm512_xor_si512(acc[0], bmul_avx512(_mm512_loadu_si512(&s[0]), factor));
acc[1] = _mm512_xor_si512(acc[1], bmul_avx512(_mm512_loadu_si512(&s[1]), factor));
acc[2] = _mm512_xor_si512(acc[2], bmul_avx512(_mm512_loadu_si512(&s[2]), factor));
acc[3] = _mm512_xor_si512(acc[3], bmul_avx512(_mm512_loadu_si512(&s[3]), factor));
}
for (lane, &value) in acc.iter().enumerate() {
{
let (rc, _) = core::slice::from_raw_parts_mut(ptr.add(tile + 64 * lane), 64)
.as_chunks_mut::<64>();
_mm512_storeu_si512(&mut rc[0], value);
}
}
}
}
#[allow(unsafe_code)]
#[archmage::rite(v4x, import_intrinsics)]
fn rows_lane1<C: MapCoeff, M: Matrix<C> + ?Sized, const OVERWRITE: bool>(
ptr: *mut u8,
tile: usize,
g: usize,
terms: &M,
) {
unsafe {
let mut acc = _mm512_setzero_si512();
if !OVERWRITE {
{
let (rc, _) = core::slice::from_raw_parts(ptr.add(tile), 64).as_chunks::<64>();
acc = _mm512_loadu_si512(&rc[0]);
}
}
for term in 0..terms.len() {
let factor = bfactor_avx512(C::map(*terms.coefficient(term, g)));
let src = terms.source(term);
let (s, _) = src[tile..tile + 64].as_chunks::<64>();
acc = _mm512_xor_si512(acc, bmul_avx512(_mm512_loadu_si512(&s[0]), factor));
}
{
let (rc, _) = core::slice::from_raw_parts_mut(ptr.add(tile), 64).as_chunks_mut::<64>();
_mm512_storeu_si512(&mut rc[0], acc);
}
}
}
#[allow(unsafe_code)]
#[archmage::rite(v4x, import_intrinsics)]
fn matrix_tail<C: MapCoeff, M: Matrix<C> + ?Sized, const OVERWRITE: bool>(
ptrs: &[*mut u8],
row_len: usize,
tile: usize,
g: usize,
terms: &M,
) {
if tile == row_len {
return;
}
for (slot, &row) in ptrs.iter().enumerate() {
let tail = unsafe { core::slice::from_raw_parts_mut(row.add(tile), row_len - tile) };
if OVERWRITE {
tail.fill(0);
}
for term in 0..terms.len() {
let coeff = *terms.coefficient(term, g + slot);
if !C::is_zero(coeff) {
mul_add_gfni_8d_impl(
tail,
C::map(coeff),
C::table(coeff),
&terms.source(term)[tile..row_len],
);
}
}
}
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_matrix_at_avx512<C: MapCoeff>(
_token: archmage::X64V4xToken,
dst: &mut [u8],
row_len: usize,
row_starts: &[usize],
terms: &[(&[C], &[u8])],
) {
check_scattered("mul_add_matrix_at_avx512", dst.len(), row_len, row_starts);
for (t, (coeffs, src)) in terms.iter().enumerate() {
assert_eq!(
coeffs.len(),
row_starts.len(),
"mul_add_matrix_at_avx512: term {t} needs {} coefficients",
row_starts.len()
);
assert_eq!(
src.len(),
row_len,
"mul_add_matrix_at_avx512: term {t} source must span one row"
);
}
if terms.is_empty() || row_starts.is_empty() {
return;
}
mul_add_matrix_at_impl(dst, row_len, row_starts, terms);
}
#[allow(unsafe_code)]
#[archmage::rite(v4x, import_intrinsics)]
fn mul_add_matrix_at_impl<C: MapCoeff>(
dst: &mut [u8],
row_len: usize,
row_starts: &[usize],
terms: &[(&[C], &[u8])],
) {
let base = dst.as_mut_ptr();
let nrows = row_starts.len();
let mut g = 0;
while g + 4 <= nrows {
let ptrs = unsafe {
[
base.add(row_starts[g]),
base.add(row_starts[g + 1]),
base.add(row_starts[g + 2]),
base.add(row_starts[g + 3]),
]
};
matrix_rows4::<C, [(&[C], &[u8])], false>(ptrs, row_len, g, terms);
g += 4;
}
if g + 2 <= nrows {
let ptrs = unsafe { [base.add(row_starts[g]), base.add(row_starts[g + 1])] };
matrix_rows2::<C, [(&[C], &[u8])], false>(ptrs, row_len, g, terms);
g += 2;
}
if g < nrows {
let ptr = unsafe { base.add(row_starts[g]) };
matrix_rows1::<C, [(&[C], &[u8])], false>(ptr, row_len, g, terms);
}
}
#[cfg(all(feature = "simd512", any(target_arch = "x86", target_arch = "x86_64")))]
struct PreparedMatrix<'a> {
prepared: &'a [crate::kernel::gf8::Prepared8D],
nrows: usize,
sources: &'a [&'a [u8]],
}
#[cfg(all(feature = "simd512", any(target_arch = "x86", target_arch = "x86_64")))]
impl crate::kernel::Matrix<crate::kernel::gf8::Prepared8D> for PreparedMatrix<'_> {
#[inline]
fn len(&self) -> usize {
self.sources.len()
}
#[inline]
fn coefficient(&self, term: usize, row: usize) -> &crate::kernel::gf8::Prepared8D {
&self.prepared[term * self.nrows + row]
}
#[inline]
fn source(&self, term: usize) -> &[u8] {
self.sources[term]
}
}
#[cfg(all(feature = "simd512", any(target_arch = "x86", target_arch = "x86_64")))]
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_matrix_avx512_8d_with(
_token: archmage::X64V4xToken,
rows: &mut [u8],
row_len: usize,
nrows: usize,
prepared: &[crate::kernel::gf8::Prepared8D],
srcs: &[&[u8]],
) {
let terms = PreparedMatrix {
prepared,
nrows,
sources: srcs,
};
mul_add_matrix_avx512_with(_token, rows, row_len, nrows, &terms);
}
#[cfg(all(feature = "simd512", any(target_arch = "x86", target_arch = "x86_64")))]
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_into_matrix_avx512_8d_with(
_token: archmage::X64V4xToken,
rows: &mut [u8],
row_len: usize,
nrows: usize,
prepared: &[crate::kernel::gf8::Prepared8D],
srcs: &[&[u8]],
) {
let terms = PreparedMatrix {
prepared,
nrows,
sources: srcs,
};
mul_into_matrix_avx512_with(_token, rows, row_len, nrows, &terms);
}