#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
use super::super::{Affine8D, Blocked, Gfni, brem};
use crate::field::gf8b::Elem;
use crate::field::gf8d;
use crate::kernel::Matrix;
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_into_matrix6_shuffle_8b(
_token: archmage::X64V3GfniCryptoToken,
rows: &mut [u8],
row_len: usize,
terms: &[(&[Elem], &[u8])],
) {
mul_into_matrix6_shuffle_checked::<Gfni>(rows, row_len, terms);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_into_matrix6_shuffle_8d(
_token: archmage::X64V3GfniCryptoToken,
rows: &mut [u8],
row_len: usize,
terms: &[(&[gf8d::Elem], &[u8])],
) {
mul_into_matrix6_shuffle_checked::<Affine8D>(rows, row_len, terms);
}
#[archmage::rite(v3_gfni_crypto)]
fn mul_into_matrix6_shuffle_checked<S: Blocked>(
rows: &mut [u8],
row_len: usize,
terms: &[(&[S::Coeff], &[u8])],
) {
assert!(
row_len
.checked_mul(6)
.is_some_and(|needed| needed <= rows.len()),
"six-row shuffle matrix: rows buffer does not hold six rows of {row_len} bytes"
);
for &(coeffs, src) in terms {
assert_eq!(coeffs.len(), 6);
assert_eq!(src.len(), row_len);
}
mul_into_matrix6_shuffle_impl::<S, _>(rows, row_len, terms);
}
#[inline]
#[archmage::rite(v3_gfni_crypto, import_intrinsics)]
fn nibble_product<S: Blocked>(lo: __m256i, hi: __m256i, coeff: S::Coeff) -> __m256i {
let table = S::table(coeff);
let lo_table = _mm256_broadcastsi128_si256(_mm_loadu_si128(&table.lo));
let hi_table = _mm256_broadcastsi128_si256(_mm_loadu_si128(&table.hi));
_mm256_xor_si256(
_mm256_shuffle_epi8(lo_table, lo),
_mm256_shuffle_epi8(hi_table, hi),
)
}
#[allow(unsafe_code)]
#[archmage::rite(v3_gfni_crypto)]
fn mul_into_matrix6_shuffle_impl<S: Blocked, M: Matrix<S::Coeff> + ?Sized>(
rows: &mut [u8],
row_len: usize,
terms: &M,
) {
let base = rows.as_mut_ptr();
let ptrs = unsafe {
[
base,
base.add(row_len),
base.add(row_len * 2),
base.add(row_len * 3),
base.add(row_len * 4),
base.add(row_len * 5),
]
};
let mask = _mm256_set1_epi8(0x0f);
let len = row_len & !31;
let mut offset = 0;
while offset < len {
let (mut a0, mut a1, mut a2, mut a3, mut a4, mut a5) = (
_mm256_setzero_si256(),
_mm256_setzero_si256(),
_mm256_setzero_si256(),
_mm256_setzero_si256(),
_mm256_setzero_si256(),
_mm256_setzero_si256(),
);
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 lo = _mm256_and_si256(x, mask);
let hi = _mm256_and_si256(_mm256_srli_epi16::<4>(x), mask);
a0 = _mm256_xor_si256(a0, nibble_product::<S>(lo, hi, *terms.coefficient(term, 0)));
a1 = _mm256_xor_si256(a1, nibble_product::<S>(lo, hi, *terms.coefficient(term, 1)));
a2 = _mm256_xor_si256(a2, nibble_product::<S>(lo, hi, *terms.coefficient(term, 2)));
a3 = _mm256_xor_si256(a3, nibble_product::<S>(lo, hi, *terms.coefficient(term, 3)));
a4 = _mm256_xor_si256(a4, nibble_product::<S>(lo, hi, *terms.coefficient(term, 4)));
a5 = _mm256_xor_si256(a5, nibble_product::<S>(lo, hi, *terms.coefficient(term, 5)));
}
unsafe {
core::arch::x86_64::_mm256_storeu_si256(ptrs[0].add(offset).cast(), a0);
core::arch::x86_64::_mm256_storeu_si256(ptrs[1].add(offset).cast(), a1);
core::arch::x86_64::_mm256_storeu_si256(ptrs[2].add(offset).cast(), a2);
core::arch::x86_64::_mm256_storeu_si256(ptrs[3].add(offset).cast(), a3);
core::arch::x86_64::_mm256_storeu_si256(ptrs[4].add(offset).cast(), a4);
core::arch::x86_64::_mm256_storeu_si256(ptrs[5].add(offset).cast(), a5);
}
offset += 32;
}
if offset == row_len {
return;
}
let remaining = row_len - offset;
for (row_index, &ptr) in ptrs.iter().enumerate() {
let tail = unsafe { core::slice::from_raw_parts_mut(ptr.add(offset), remaining) };
tail.fill(0);
for term in 0..terms.len() {
let coeff = *terms.coefficient(term, row_index);
if S::byte(coeff) != 0 {
brem::<S>(tail, coeff, &terms.source(term)[offset..]);
}
}
}
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_into_matrix6_shuffle_packed_8b(
_token: archmage::X64V3GfniCryptoToken,
rows: &mut [u8],
row_len: usize,
tables: &[[u8; 32]],
srcs: &[&[u8]],
) {
mul_into_matrix6_shuffle_packed_checked(rows, row_len, tables, srcs);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_into_matrix6_shuffle_packed_8d(
_token: archmage::X64V3GfniCryptoToken,
rows: &mut [u8],
row_len: usize,
tables: &[[u8; 32]],
srcs: &[&[u8]],
) {
mul_into_matrix6_shuffle_packed_checked(rows, row_len, tables, srcs);
}
#[archmage::rite(v3_gfni_crypto)]
fn mul_into_matrix6_shuffle_packed_checked(
rows: &mut [u8],
row_len: usize,
tables: &[[u8; 32]],
srcs: &[&[u8]],
) {
assert!(
row_len
.checked_mul(6)
.is_some_and(|needed| needed <= rows.len()),
"packed six-row shuffle matrix: rows buffer does not hold six rows of {row_len} bytes"
);
assert_eq!(tables.len(), srcs.len() * 6);
for &src in srcs {
assert_eq!(src.len(), row_len);
}
mul_into_matrix6_shuffle_packed_impl(rows, row_len, tables, srcs);
}
#[inline]
#[allow(unsafe_code)]
#[archmage::rite(v3_gfni_crypto)]
fn packed_nibble_product(lo: __m256i, hi: __m256i, table: *const [u8; 32]) -> __m256i {
let (lo_table, hi_table) = unsafe {
let bytes = table.cast::<u8>();
(
_mm256_broadcastsi128_si256(core::arch::x86_64::_mm_loadu_si128(bytes.cast())),
_mm256_broadcastsi128_si256(core::arch::x86_64::_mm_loadu_si128(bytes.add(16).cast())),
)
};
_mm256_xor_si256(
_mm256_shuffle_epi8(lo_table, lo),
_mm256_shuffle_epi8(hi_table, hi),
)
}
#[allow(unsafe_code)]
#[archmage::rite(v3_gfni_crypto)]
fn mul_into_matrix6_shuffle_packed_impl(
rows: &mut [u8],
row_len: usize,
tables: &[[u8; 32]],
srcs: &[&[u8]],
) {
let base = rows.as_mut_ptr();
let ptrs = unsafe {
[
base,
base.add(row_len),
base.add(row_len * 2),
base.add(row_len * 3),
base.add(row_len * 4),
base.add(row_len * 5),
]
};
let mask = _mm256_set1_epi8(0x0f);
let len = row_len & !31;
let table_ptr = tables.as_ptr();
let source_ptr = srcs.as_ptr();
let mut offset = 0;
while offset < len {
let (mut a0, mut a1, mut a2, mut a3, mut a4, mut a5) = (
_mm256_setzero_si256(),
_mm256_setzero_si256(),
_mm256_setzero_si256(),
_mm256_setzero_si256(),
_mm256_setzero_si256(),
_mm256_setzero_si256(),
);
for term in 0..srcs.len() {
let (src, term_tables) = unsafe { (*source_ptr.add(term), table_ptr.add(term * 6)) };
let x =
unsafe { core::arch::x86_64::_mm256_loadu_si256(src.as_ptr().add(offset).cast()) };
let lo = _mm256_and_si256(x, mask);
let hi = _mm256_and_si256(_mm256_srli_epi16::<4>(x), mask);
unsafe {
a0 = _mm256_xor_si256(a0, packed_nibble_product(lo, hi, term_tables));
a1 = _mm256_xor_si256(a1, packed_nibble_product(lo, hi, term_tables.add(1)));
a2 = _mm256_xor_si256(a2, packed_nibble_product(lo, hi, term_tables.add(2)));
a3 = _mm256_xor_si256(a3, packed_nibble_product(lo, hi, term_tables.add(3)));
a4 = _mm256_xor_si256(a4, packed_nibble_product(lo, hi, term_tables.add(4)));
a5 = _mm256_xor_si256(a5, packed_nibble_product(lo, hi, term_tables.add(5)));
}
}
unsafe {
core::arch::x86_64::_mm256_storeu_si256(ptrs[0].add(offset).cast(), a0);
core::arch::x86_64::_mm256_storeu_si256(ptrs[1].add(offset).cast(), a1);
core::arch::x86_64::_mm256_storeu_si256(ptrs[2].add(offset).cast(), a2);
core::arch::x86_64::_mm256_storeu_si256(ptrs[3].add(offset).cast(), a3);
core::arch::x86_64::_mm256_storeu_si256(ptrs[4].add(offset).cast(), a4);
core::arch::x86_64::_mm256_storeu_si256(ptrs[5].add(offset).cast(), a5);
}
offset += 32;
}
if offset == row_len {
return;
}
let remaining = row_len - offset;
for (row_index, &ptr) in ptrs.iter().enumerate() {
let tail = unsafe { core::slice::from_raw_parts_mut(ptr.add(offset), remaining) };
tail.fill(0);
for (term, &src) in srcs.iter().enumerate() {
let coefficient = tables[term * 6 + row_index][1];
if coefficient != 0 {
for (dst, &value) in tail.iter_mut().zip(&src[offset..]) {
let low = tables[term * 6 + row_index][usize::from(value & 0x0f)];
let high = tables[term * 6 + row_index][16 + usize::from(value >> 4)];
*dst ^= low ^ high;
}
}
}
}
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_into_matrix2_shuffle_packed(
_token: archmage::X64V3Token,
rows: &mut [u8],
row_len: usize,
tables: &[[u8; 32]],
srcs: &[&[u8]],
lanes: usize,
) {
assert!(
2usize
.checked_mul(row_len)
.is_some_and(|needed| needed <= rows.len()),
"matrix_overwrite2_shuffle: rows buffer does not hold two rows of {row_len} bytes"
);
assert_eq!(tables.len(), srcs.len() * 2);
for &src in srcs {
assert_eq!(src.len(), row_len);
}
match lanes {
1 => mul_into_matrix2_shuffle_body::<1>(rows, row_len, tables, srcs),
2 => mul_into_matrix2_shuffle_body::<2>(rows, row_len, tables, srcs),
_ => panic!("matrix_overwrite2_shuffle: lanes must be 1 or 2"),
}
}
#[allow(unsafe_code)]
#[archmage::rite(v3, import_intrinsics)]
fn mul_into_matrix2_shuffle_body<const LANES: usize>(
rows: &mut [u8],
row_len: usize,
tables: &[[u8; 32]],
srcs: &[&[u8]],
) {
let base = rows.as_mut_ptr();
let (ptr0, ptr1) = unsafe { (base, base.add(row_len)) };
let mask = _mm256_set1_epi8(0x0f);
let table_ptr = tables.as_ptr();
let source_ptr = srcs.as_ptr();
let step = 32 * LANES;
let mut offset = 0;
while offset + step <= row_len {
let mut a0 = [_mm256_setzero_si256(); LANES];
let mut a1 = [_mm256_setzero_si256(); LANES];
for term in 0..srcs.len() {
unsafe {
let src = *source_ptr.add(term);
let t = table_ptr.add(term * 2).cast::<u8>();
let lo0 =
_mm256_broadcastsi128_si256(core::arch::x86_64::_mm_loadu_si128(t.cast()));
let hi0 = _mm256_broadcastsi128_si256(core::arch::x86_64::_mm_loadu_si128(
t.add(16).cast(),
));
let lo1 = _mm256_broadcastsi128_si256(core::arch::x86_64::_mm_loadu_si128(
t.add(32).cast(),
));
let hi1 = _mm256_broadcastsi128_si256(core::arch::x86_64::_mm_loadu_si128(
t.add(48).cast(),
));
let sp = src.as_ptr().add(offset);
for l in 0..LANES {
let x = core::arch::x86_64::_mm256_loadu_si256(sp.add(32 * l).cast());
let lo = _mm256_and_si256(x, mask);
let hi = _mm256_and_si256(_mm256_srli_epi16::<4>(x), mask);
a0[l] = _mm256_xor_si256(
a0[l],
_mm256_xor_si256(
_mm256_shuffle_epi8(lo0, lo),
_mm256_shuffle_epi8(hi0, hi),
),
);
a1[l] = _mm256_xor_si256(
a1[l],
_mm256_xor_si256(
_mm256_shuffle_epi8(lo1, lo),
_mm256_shuffle_epi8(hi1, hi),
),
);
}
}
}
unsafe {
for l in 0..LANES {
core::arch::x86_64::_mm256_storeu_si256(ptr0.add(offset + 32 * l).cast(), a0[l]);
core::arch::x86_64::_mm256_storeu_si256(ptr1.add(offset + 32 * l).cast(), a1[l]);
}
}
offset += step;
}
while offset + 32 <= row_len {
let mut a0 = _mm256_setzero_si256();
let mut a1 = _mm256_setzero_si256();
for term in 0..srcs.len() {
unsafe {
let src = *source_ptr.add(term);
let t = table_ptr.add(term * 2).cast::<u8>();
let x = core::arch::x86_64::_mm256_loadu_si256(src.as_ptr().add(offset).cast());
let lo = _mm256_and_si256(x, mask);
let hi = _mm256_and_si256(_mm256_srli_epi16::<4>(x), mask);
let lo0 =
_mm256_broadcastsi128_si256(core::arch::x86_64::_mm_loadu_si128(t.cast()));
let hi0 = _mm256_broadcastsi128_si256(core::arch::x86_64::_mm_loadu_si128(
t.add(16).cast(),
));
let lo1 = _mm256_broadcastsi128_si256(core::arch::x86_64::_mm_loadu_si128(
t.add(32).cast(),
));
let hi1 = _mm256_broadcastsi128_si256(core::arch::x86_64::_mm_loadu_si128(
t.add(48).cast(),
));
a0 = _mm256_xor_si256(
a0,
_mm256_xor_si256(_mm256_shuffle_epi8(lo0, lo), _mm256_shuffle_epi8(hi0, hi)),
);
a1 = _mm256_xor_si256(
a1,
_mm256_xor_si256(_mm256_shuffle_epi8(lo1, lo), _mm256_shuffle_epi8(hi1, hi)),
);
}
}
unsafe {
core::arch::x86_64::_mm256_storeu_si256(ptr0.add(offset).cast(), a0);
core::arch::x86_64::_mm256_storeu_si256(ptr1.add(offset).cast(), a1);
}
offset += 32;
}
if offset == row_len {
return;
}
let remaining = row_len - offset;
for (row, &ptr) in [ptr0, ptr1].iter().enumerate() {
let tail = unsafe { core::slice::from_raw_parts_mut(ptr.add(offset), remaining) };
tail.fill(0);
for (term, &src) in srcs.iter().enumerate() {
let record = &tables[term * 2 + row];
if record[1] != 0 || record[16] != 0 {
for (dst, &value) in tail.iter_mut().zip(&src[offset..]) {
let low = record[usize::from(value & 0x0f)];
let high = record[16 + usize::from(value >> 4)];
*dst ^= low ^ high;
}
}
}
}
}