use super::src_tail_avx2;
#[must_use]
fn tables(c: u8, p: u8) -> ([u8; 32], [u8; 32]) {
let mut lo = [0u8; 32];
let mut hi = [0u8; 32];
let wide = u32::from(p);
for i in 0..16u32 {
#[allow(clippy::cast_possible_truncation)]
let l = (u32::from(c) * i % wide) as u8;
#[allow(clippy::cast_possible_truncation)]
let h = (u32::from(c) * 16 * i % wide) as u8;
lo[i as usize] = l;
lo[i as usize + 16] = l;
hi[i as usize] = h;
hi[i as usize + 16] = h;
}
(lo, hi)
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_into_u8_avx2(_token: archmage::X64V3Token, dst: &mut [u8], src: &[u8], c: u8, p: u8) {
assert!(p <= 127, "residue byte: modulus {p} exceeds 127");
assert_eq!(
dst.len(),
src.len(),
"residue::mul_into_u8_avx2: dst length differs from src"
);
let (lo, hi) = tables(c, p);
let lo_v = _mm256_loadu_si256(&lo);
let hi_v = _mm256_loadu_si256(&hi);
let mask0f = _mm256_set1_epi8(0x0F);
#[allow(clippy::cast_possible_wrap)]
let cp = _mm256_set1_epi8(p as i8);
let (dst_lanes, dst_tail) = dst.as_chunks_mut::<32>();
let (src_lanes, _) = src.as_chunks::<32>();
for (d, s) in dst_lanes.iter_mut().zip(src_lanes) {
let v = _mm256_loadu_si256(s);
let rlo = _mm256_shuffle_epi8(lo_v, _mm256_and_si256(v, mask0f));
let rhi = _mm256_shuffle_epi8(hi_v, _mm256_and_si256(_mm256_srli_epi16(v, 4), mask0f));
let sum = _mm256_add_epi8(rlo, rhi);
let sub = _mm256_sub_epi8(sum, cp);
_mm256_storeu_si256(d, _mm256_min_epu8(sum, sub));
}
for (d, s) in dst_tail.iter_mut().zip(src_tail_avx2(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_avx2(_token: archmage::X64V3Token, dst: &mut [u8], src: &[u8], c: u8, p: u8) {
assert!(p <= 127, "residue byte: modulus {p} exceeds 127");
assert_eq!(
dst.len(),
src.len(),
"residue::mul_add_u8_avx2: dst length differs from src"
);
let (lo, hi) = tables(c, p);
let lo_v = _mm256_loadu_si256(&lo);
let hi_v = _mm256_loadu_si256(&hi);
let mask0f = _mm256_set1_epi8(0x0F);
#[allow(clippy::cast_possible_wrap)]
let cp = _mm256_set1_epi8(p as i8);
#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
let mu = _mm256_set1_epi16(((256u32 / u32::from(p)) as u16) as i16);
let cp16 = _mm256_set1_epi16(i16::from(p));
let (dst_lanes, dst_tail) = dst.as_chunks_mut::<32>();
let (src_lanes, _) = src.as_chunks::<32>();
for (d, s) in dst_lanes.iter_mut().zip(src_lanes) {
let acc = _mm256_loadu_si256(&*d);
let acc_lo = _mm256_cvtepu8_epi16(_mm256_extracti128_si256::<0>(acc));
let acc_hi = _mm256_cvtepu8_epi16(_mm256_extracti128_si256::<1>(acc));
let normed = {
let q_lo = _mm256_srli_epi16::<8>(_mm256_mullo_epi16(acc_lo, mu));
let q_hi = _mm256_srli_epi16::<8>(_mm256_mullo_epi16(acc_hi, mu));
let r_lo = _mm256_sub_epi16(acc_lo, _mm256_mullo_epi16(q_lo, cp16));
let r_hi = _mm256_sub_epi16(acc_hi, _mm256_mullo_epi16(q_hi, cp16));
let c_lo = _mm256_min_epu16(r_lo, _mm256_sub_epi16(r_lo, cp16));
let c_hi = _mm256_min_epu16(r_hi, _mm256_sub_epi16(r_hi, cp16));
_mm256_permute4x64_epi64::<0xD8>(_mm256_packus_epi16(c_lo, c_hi))
};
let v = _mm256_loadu_si256(s);
let rlo = _mm256_shuffle_epi8(lo_v, _mm256_and_si256(v, mask0f));
let rhi = _mm256_shuffle_epi8(hi_v, _mm256_and_si256(_mm256_srli_epi16(v, 4), mask0f));
let prod = _mm256_min_epu8(
_mm256_add_epi8(rlo, rhi),
_mm256_sub_epi8(_mm256_add_epi8(rlo, rhi), cp),
);
let sum = _mm256_add_epi8(normed, prod);
_mm256_storeu_si256(d, _mm256_min_epu8(sum, _mm256_sub_epi8(sum, cp)));
}
for (d, s) in dst_tail.iter_mut().zip(src_tail_avx2(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_avx2(_token: archmage::X64V3Token, dst: &mut [u8], c: u8, p: u8) {
assert!(p <= 127, "residue byte: modulus {p} exceeds 127");
let (lo, hi) = tables(c, p);
let lo_v = _mm256_loadu_si256(&lo);
let hi_v = _mm256_loadu_si256(&hi);
let mask0f = _mm256_set1_epi8(0x0F);
#[allow(clippy::cast_possible_wrap)]
let cp = _mm256_set1_epi8(p as i8);
let (dst_lanes, dst_tail) = dst.as_chunks_mut::<32>();
for d in dst_lanes.iter_mut() {
let v = _mm256_loadu_si256(&*d);
let rlo = _mm256_shuffle_epi8(lo_v, _mm256_and_si256(v, mask0f));
let rhi = _mm256_shuffle_epi8(hi_v, _mm256_and_si256(_mm256_srli_epi16(v, 4), mask0f));
let sum = _mm256_add_epi8(rlo, rhi);
_mm256_storeu_si256(d, _mm256_min_epu8(sum, _mm256_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 };
}
}