use super::nibble::mul_add_ssse3;
use crate::field::gf8b::Elem;
use crate::kernel::Matrix;
use crate::kernel::gf8::mul_add_nibble;
use crate::kernel::tables::scale_table;
#[allow(clippy::used_underscore_binding)]
#[allow(unsafe_code)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_scatter_avx2(
_token: archmage::X64V3Token,
rows: &mut [u8],
row_len: usize,
coeffs: &[Elem],
src: &[u8],
) {
assert_eq!(row_len, src.len());
assert!(
coeffs
.len()
.checked_mul(row_len)
.is_some_and(|needed| needed <= rows.len()),
"mul_add_scatter_avx2: rows buffer does not hold {} rows of {row_len} bytes",
coeffs.len()
);
if row_len == 0 {
return;
}
let vector_len = row_len & !31;
let base = rows.as_mut_ptr();
let mask = _mm256_set1_epi8(0x0f);
for group in (0..coeffs.len()).step_by(4) {
let count = (coeffs.len() - group).min(4);
let mut lo = [_mm256_setzero_si256(); 4];
let mut hi = [_mm256_setzero_si256(); 4];
for slot in 0..count {
let table = scale_table(coeffs[group + slot]);
lo[slot] = _mm256_broadcastsi128_si256(_mm_loadu_si128(&table.lo));
hi[slot] = _mm256_broadcastsi128_si256(_mm_loadu_si128(&table.hi));
}
let (src_lanes, src_tail) = src.as_chunks::<32>();
for (l, slane) in src_lanes.iter().enumerate() {
let offset = l * 32;
let x = _mm256_loadu_si256(slane);
let indices_lo = _mm256_and_si256(x, mask);
let indices_hi = _mm256_and_si256(_mm256_srli_epi16::<4>(x), mask);
for slot in 0..count {
let product = _mm256_xor_si256(
_mm256_shuffle_epi8(lo[slot], indices_lo),
_mm256_shuffle_epi8(hi[slot], indices_hi),
);
unsafe {
let ptr = base.add((group + slot) * row_len + offset);
core::arch::x86_64::_mm256_storeu_si256(
ptr.cast(),
_mm256_xor_si256(
core::arch::x86_64::_mm256_loadu_si256(ptr.cast()),
product,
),
);
}
}
}
for slot in 0..count {
let tail = unsafe {
core::slice::from_raw_parts_mut(
base.add((group + slot) * row_len + vector_len),
row_len - vector_len,
)
};
mul_add_ssse3(
_token.v2(),
tail,
scale_table(coeffs[group + slot]),
src_tail,
);
}
}
}
#[allow(clippy::used_underscore_binding)]
#[allow(unsafe_code)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_scatter_ssse3(
_token: archmage::X64V2Token,
rows: &mut [u8],
row_len: usize,
coeffs: &[Elem],
src: &[u8],
) {
assert_eq!(row_len, src.len());
assert!(
coeffs
.len()
.checked_mul(row_len)
.is_some_and(|needed| needed <= rows.len()),
"mul_add_scatter_ssse3: rows buffer does not hold {} rows of {row_len} bytes",
coeffs.len()
);
if row_len == 0 {
return;
}
let vector_len = row_len & !15;
let base = rows.as_mut_ptr();
let mask = _mm_set1_epi8(0x0f);
for group in (0..coeffs.len()).step_by(4) {
let count = (coeffs.len() - group).min(4);
let mut lo = [_mm_setzero_si128(); 4];
let mut hi = [_mm_setzero_si128(); 4];
for slot in 0..count {
let table = scale_table(coeffs[group + slot]);
lo[slot] = _mm_loadu_si128(&table.lo);
hi[slot] = _mm_loadu_si128(&table.hi);
}
let (src_lanes, src_tail) = src.as_chunks::<16>();
for (l, slane) in src_lanes.iter().enumerate() {
let offset = l * 16;
let x = _mm_loadu_si128(slane);
let indices_lo = _mm_and_si128(x, mask);
let indices_hi = _mm_and_si128(_mm_srli_epi16::<4>(x), mask);
for slot in 0..count {
let product = _mm_xor_si128(
_mm_shuffle_epi8(lo[slot], indices_lo),
_mm_shuffle_epi8(hi[slot], indices_hi),
);
unsafe {
let ptr = base.add((group + slot) * row_len + offset);
core::arch::x86_64::_mm_storeu_si128(
ptr.cast(),
_mm_xor_si128(core::arch::x86_64::_mm_loadu_si128(ptr.cast()), product),
);
}
}
}
for slot in 0..count {
let tail = unsafe {
core::slice::from_raw_parts_mut(
base.add((group + slot) * row_len + vector_len),
row_len - vector_len,
)
};
mul_add_nibble(tail, scale_table(coeffs[group + slot]), src_tail);
}
}
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_matrix_avx2(
_token: archmage::X64V3Token,
rows: &mut [u8],
row_len: usize,
nrows: usize,
terms: &[(&[Elem], &[u8])],
) {
for (t, (coeffs, _)) in terms.iter().enumerate() {
assert_eq!(
coeffs.len(),
nrows,
"mul_add_matrix_avx2: term {t} needs {nrows} coefficients"
);
}
mul_add_matrix_avx2_with(_token, rows, row_len, nrows, terms);
}
#[allow(clippy::used_underscore_binding)]
#[allow(unsafe_code)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_matrix_avx2_with<M: Matrix<Elem> + ?Sized>(
_token: archmage::X64V3Token,
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_avx2: 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);
}
if row_len == 0 {
return;
}
let vector_len = row_len & !31;
let tile_len = row_len & !63;
let base = rows.as_mut_ptr();
let mut group = 0;
while group < nrows {
let count = (nrows - group).min(4);
matrix_tiles_avx2(base, row_len, group, count, tile_len, terms);
if tile_len < vector_len {
matrix_vector_avx2(base, row_len, group, count, tile_len, terms);
}
for slot in 0..count {
let tail = unsafe {
core::slice::from_raw_parts_mut(
base.add((group + slot) * row_len + vector_len),
row_len - vector_len,
)
};
for term in 0..terms.len() {
let src = terms.source(term);
let table = scale_table(*terms.coefficient(term, group + slot));
mul_add_ssse3(_token.v2(), tail, table, &src[vector_len..]);
}
}
group += count;
}
}
#[allow(unsafe_code)]
#[archmage::rite(v3, import_intrinsics)]
fn matrix_tiles_avx2<M: Matrix<Elem> + ?Sized>(
base: *mut u8,
row_len: usize,
group: usize,
count: usize,
tile_len: usize,
terms: &M,
) {
let mask = _mm256_set1_epi8(0x0f);
let mut offset = 0;
while offset < tile_len {
let mut acc = [[_mm256_setzero_si256(); 2]; 4];
unsafe {
for (slot, value) in acc.iter_mut().take(count).enumerate() {
let ptr = base.add((group + slot) * row_len + offset);
value[0] = core::arch::x86_64::_mm256_loadu_si256(ptr.cast());
value[1] = core::arch::x86_64::_mm256_loadu_si256(ptr.add(32).cast());
}
}
for term in 0..terms.len() {
let src = terms.source(term);
let (x0, x1) = unsafe {
let sp = src.as_ptr().add(offset);
(
core::arch::x86_64::_mm256_loadu_si256(sp.cast()),
core::arch::x86_64::_mm256_loadu_si256(sp.add(32).cast()),
)
};
let indices = [
_mm256_and_si256(x0, mask),
_mm256_and_si256(_mm256_srli_epi16::<4>(x0), mask),
_mm256_and_si256(x1, mask),
_mm256_and_si256(_mm256_srli_epi16::<4>(x1), mask),
];
for (slot, value) in acc.iter_mut().take(count).enumerate() {
let table = scale_table(*terms.coefficient(term, group + slot));
let lo = _mm256_broadcastsi128_si256(_mm_loadu_si128(&table.lo));
let hi = _mm256_broadcastsi128_si256(_mm_loadu_si128(&table.hi));
value[0] = _mm256_xor_si256(
value[0],
_mm256_xor_si256(
_mm256_shuffle_epi8(lo, indices[0]),
_mm256_shuffle_epi8(hi, indices[1]),
),
);
value[1] = _mm256_xor_si256(
value[1],
_mm256_xor_si256(
_mm256_shuffle_epi8(lo, indices[2]),
_mm256_shuffle_epi8(hi, indices[3]),
),
);
}
}
unsafe {
for (slot, value) in acc.iter().take(count).enumerate() {
let ptr = base.add((group + slot) * row_len + offset);
core::arch::x86_64::_mm256_storeu_si256(ptr.cast(), value[0]);
core::arch::x86_64::_mm256_storeu_si256(ptr.add(32).cast(), value[1]);
}
}
offset += 64;
}
}
#[allow(unsafe_code)]
#[archmage::rite(v3, import_intrinsics)]
fn matrix_vector_avx2<M: Matrix<Elem> + ?Sized>(
base: *mut u8,
row_len: usize,
group: usize,
count: usize,
offset: usize,
terms: &M,
) {
let mask = _mm256_set1_epi8(0x0f);
let mut acc = [_mm256_setzero_si256(); 4];
unsafe {
for (slot, value) in acc.iter_mut().take(count).enumerate() {
*value = core::arch::x86_64::_mm256_loadu_si256(
base.add((group + slot) * row_len + offset).cast(),
);
}
}
for term in 0..terms.len() {
let src = terms.source(term);
let x = unsafe { core::arch::x86_64::_mm256_loadu_si256(src.as_ptr().add(offset).cast()) };
let indices_lo = _mm256_and_si256(x, mask);
let indices_hi = _mm256_and_si256(_mm256_srli_epi16::<4>(x), mask);
for (slot, value) in acc.iter_mut().take(count).enumerate() {
let table = scale_table(*terms.coefficient(term, group + slot));
let lo = _mm256_broadcastsi128_si256(_mm_loadu_si128(&table.lo));
let hi = _mm256_broadcastsi128_si256(_mm_loadu_si128(&table.hi));
*value = _mm256_xor_si256(
*value,
_mm256_xor_si256(
_mm256_shuffle_epi8(lo, indices_lo),
_mm256_shuffle_epi8(hi, indices_hi),
),
);
}
}
unsafe {
for (slot, &value) in acc.iter().take(count).enumerate() {
core::arch::x86_64::_mm256_storeu_si256(
base.add((group + slot) * row_len + offset).cast(),
value,
);
}
}
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_matrix_ssse3(
_token: archmage::X64V2Token,
rows: &mut [u8],
row_len: usize,
nrows: usize,
terms: &[(&[Elem], &[u8])],
) {
for (t, (coeffs, _)) in terms.iter().enumerate() {
assert_eq!(
coeffs.len(),
nrows,
"mul_add_matrix_ssse3: term {t} needs {nrows} coefficients"
);
}
mul_add_matrix_ssse3_with(_token, rows, row_len, nrows, terms);
}
#[allow(clippy::used_underscore_binding)]
#[allow(unsafe_code)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_matrix_ssse3_with<M: Matrix<Elem> + ?Sized>(
_token: archmage::X64V2Token,
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_ssse3: 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);
}
if row_len == 0 {
return;
}
let vector_len = row_len & !15;
let base = rows.as_mut_ptr();
let mask = _mm_set1_epi8(0x0f);
let mut group = 0;
while group < nrows {
let count = (nrows - group).min(4);
let mut offset = 0;
while offset < vector_len {
let mut acc = [_mm_setzero_si128(); 4];
unsafe {
for (slot, value) in acc.iter_mut().take(count).enumerate() {
*value = core::arch::x86_64::_mm_loadu_si128(
base.add((group + slot) * row_len + offset).cast(),
);
}
}
for term in 0..terms.len() {
let src = terms.source(term);
let x =
unsafe { core::arch::x86_64::_mm_loadu_si128(src.as_ptr().add(offset).cast()) };
for (slot, value) in acc.iter_mut().take(count).enumerate() {
let table = scale_table(*terms.coefficient(term, group + slot));
let lo = _mm_loadu_si128(&table.lo);
let hi = _mm_loadu_si128(&table.hi);
let product = _mm_xor_si128(
_mm_shuffle_epi8(lo, _mm_and_si128(x, mask)),
_mm_shuffle_epi8(hi, _mm_and_si128(_mm_srli_epi16::<4>(x), mask)),
);
*value = _mm_xor_si128(*value, product);
}
}
unsafe {
for (slot, &value) in acc.iter().take(count).enumerate() {
core::arch::x86_64::_mm_storeu_si128(
base.add((group + slot) * row_len + offset).cast(),
value,
);
}
}
offset += 16;
}
for slot in 0..count {
let tail = unsafe {
core::slice::from_raw_parts_mut(
base.add((group + slot) * row_len + vector_len),
row_len - vector_len,
)
};
for term in 0..terms.len() {
let src = terms.source(term);
let table = scale_table(*terms.coefficient(term, group + slot));
mul_add_nibble(tail, table, &src[vector_len..]);
}
}
group += count;
}
}