#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
use super::src_tail_avx512;
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_into_u8_avx512(_token: archmage::X64V4Token, dst: &mut [u8], src: &[u8], c: u8, p: u8) {
assert!(p <= 127, "residue u8: modulus {p} exceeds 127");
assert_eq!(
dst.len(),
src.len(),
"residue::mul_into_u8_avx512: dst length differs from src"
);
let (lo_table, hi_table) = tables(c, p);
let mut lo = [0u8; 16];
let mut hi = [0u8; 16];
_mm_storeu_si128(&mut lo, lo_table);
_mm_storeu_si128(&mut hi, hi_table);
let lo_v = _mm512_broadcast_i32x4(lo_table);
let hi_v = _mm512_broadcast_i32x4(hi_table);
let mask0f = _mm512_set1_epi8(0x0F);
#[allow(clippy::cast_possible_wrap)]
let cp = _mm512_set1_epi8(p as i8);
let (dst_lanes, dst_tail) = dst.as_chunks_mut::<64>();
let (src_lanes, _) = src.as_chunks::<64>();
for (d, s) in dst_lanes.iter_mut().zip(src_lanes) {
let v = _mm512_loadu_si512(s);
let rlo = _mm512_shuffle_epi8(lo_v, _mm512_and_si512(v, mask0f));
let rhi = _mm512_shuffle_epi8(hi_v, _mm512_and_si512(_mm512_srli_epi16::<4>(v), mask0f));
let sum = _mm512_add_epi8(rlo, rhi);
let sub = _mm512_sub_epi8(sum, cp);
_mm512_storeu_si512(d, _mm512_min_epu8(sum, sub));
}
for (d, s) in dst_tail.iter_mut().zip(src_tail_avx512(src)) {
let t = lo[*s as usize & 15].wrapping_add(hi[*s as usize >> 4]);
*d = if t >= p { t - p } else { t };
}
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_u8_avx512(_token: archmage::X64V4Token, dst: &mut [u8], src: &[u8], c: u8, p: u8) {
assert!(p <= 127, "residue u8: modulus {p} exceeds 127");
assert_eq!(
dst.len(),
src.len(),
"residue::mul_add_u8_avx512: dst length differs from src"
);
let (lo_table, hi_table) = tables(c, p);
let mut lo = [0u8; 16];
let mut hi = [0u8; 16];
_mm_storeu_si128(&mut lo, lo_table);
_mm_storeu_si128(&mut hi, hi_table);
let lo_v = _mm512_broadcast_i32x4(lo_table);
let hi_v = _mm512_broadcast_i32x4(hi_table);
let mask0f = _mm512_set1_epi8(0x0F);
#[allow(clippy::cast_possible_wrap)]
let cp = _mm512_set1_epi8(p as i8);
#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
let mu = _mm512_set1_epi16(((256u32 / u32::from(p)) as u16) as i16);
let cp16 = _mm512_set1_epi16(i16::from(p));
let (dst_lanes, dst_tail) = dst.as_chunks_mut::<64>();
let (src_lanes, _) = src.as_chunks::<64>();
for (d, s) in dst_lanes.iter_mut().zip(src_lanes) {
let acc = _mm512_loadu_si512(&*d);
let acc_lo = _mm512_and_si512(acc, _mm512_set1_epi16(255));
let acc_hi = _mm512_srli_epi16::<8>(acc);
let normed = {
let q_lo = _mm512_srli_epi16::<8>(_mm512_mullo_epi16(acc_lo, mu));
let q_hi = _mm512_srli_epi16::<8>(_mm512_mullo_epi16(acc_hi, mu));
let r_lo = _mm512_sub_epi16(acc_lo, _mm512_mullo_epi16(q_lo, cp16));
let r_hi = _mm512_sub_epi16(acc_hi, _mm512_mullo_epi16(q_hi, cp16));
let c_lo = _mm512_min_epu16(r_lo, _mm512_sub_epi16(r_lo, cp16));
let c_hi = _mm512_min_epu16(r_hi, _mm512_sub_epi16(r_hi, cp16));
_mm512_or_si512(c_lo, _mm512_slli_epi16::<8>(c_hi))
};
let v = _mm512_loadu_si512(s);
let rlo = _mm512_shuffle_epi8(lo_v, _mm512_and_si512(v, mask0f));
let rhi = _mm512_shuffle_epi8(hi_v, _mm512_and_si512(_mm512_srli_epi16::<4>(v), mask0f));
let prod = _mm512_min_epu8(
_mm512_add_epi8(rlo, rhi),
_mm512_sub_epi8(_mm512_add_epi8(rlo, rhi), cp),
);
let sum = _mm512_add_epi8(normed, prod);
_mm512_storeu_si512(d, _mm512_min_epu8(sum, _mm512_sub_epi8(sum, cp)));
}
for (d, s) in dst_tail.iter_mut().zip(src_tail_avx512(src)) {
let acc = *d % p;
let t = lo[*s as usize & 15].wrapping_add(hi[*s as usize >> 4]);
let prod = if t >= p { t - p } else { t };
let sum = acc + prod;
*d = if sum >= p { sum - p } else { sum };
}
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_assign_u8_avx512(_token: archmage::X64V4Token, dst: &mut [u8], c: u8, p: u8) {
assert!(p <= 127, "residue u8: modulus {p} exceeds 127");
let (lo_table, hi_table) = tables(c, p);
let mut lo = [0u8; 16];
let mut hi = [0u8; 16];
_mm_storeu_si128(&mut lo, lo_table);
_mm_storeu_si128(&mut hi, hi_table);
let lo_v = _mm512_broadcast_i32x4(lo_table);
let hi_v = _mm512_broadcast_i32x4(hi_table);
let mask0f = _mm512_set1_epi8(0x0F);
#[allow(clippy::cast_possible_wrap)]
let cp = _mm512_set1_epi8(p as i8);
let (dst_lanes, dst_tail) = dst.as_chunks_mut::<64>();
for d in dst_lanes.iter_mut() {
let v = _mm512_loadu_si512(&*d);
let rlo = _mm512_shuffle_epi8(lo_v, _mm512_and_si512(v, mask0f));
let rhi = _mm512_shuffle_epi8(hi_v, _mm512_and_si512(_mm512_srli_epi16::<4>(v), mask0f));
let sum = _mm512_add_epi8(rlo, rhi);
_mm512_storeu_si512(d, _mm512_min_epu8(sum, _mm512_sub_epi8(sum, cp)));
}
for d in dst_tail.iter_mut() {
let t = lo[*d as usize & 15].wrapping_add(hi[*d as usize >> 4]);
*d = if t >= p { t - p } else { t };
}
}
#[archmage::rite(v4)]
fn tables(c: u8, p: u8) -> (__m128i, __m128i) {
let indices = _mm512_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
let modulus = _mm512_set1_epi32(i32::from(p));
let reciprocal = _mm512_set1_epi32(65536 / i32::from(p));
let low = _mm512_mullo_epi32(indices, _mm512_set1_epi32(i32::from(c)));
let high = _mm512_slli_epi32::<4>(low);
(
table_residues(low, reciprocal, modulus),
table_residues(high, reciprocal, modulus),
)
}
#[archmage::rite(v4)]
fn table_residues(products: __m512i, reciprocal: __m512i, modulus: __m512i) -> __m128i {
let quotient = _mm512_srli_epi32::<16>(_mm512_mullo_epi32(products, reciprocal));
let residue = _mm512_sub_epi32(products, _mm512_mullo_epi32(quotient, modulus));
let canonical = _mm512_min_epu32(residue, _mm512_sub_epi32(residue, modulus));
_mm512_cvtepi32_epi8(canonical)
}