#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
use crate::field::gf8b::Elem;
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_elementwise_gfni(
_token: archmage::X64V3GfniCryptoToken,
dst: &mut [u8],
a: &[u8],
b: &[u8],
) {
assert_eq!(
dst.len(),
a.len(),
"mul_elementwise_gfni: dst and a differ in length"
);
assert_eq!(
dst.len(),
b.len(),
"mul_elementwise_gfni: dst and b differ in length"
);
let (dst_lanes, dst_rest) = dst.as_chunks_mut::<32>();
let (a_lanes, a_rest) = a.as_chunks::<32>();
let (b_lanes, b_rest) = b.as_chunks::<32>();
for ((dlane, alane), blane) in dst_lanes.iter_mut().zip(a_lanes).zip(b_lanes) {
let x = _mm256_loadu_si256(alane);
let y = _mm256_loadu_si256(blane);
_mm256_storeu_si256(dlane, _mm256_gf2p8mul_epi8(x, y));
}
let (dst16, dst_tail) = dst_rest.as_chunks_mut::<16>();
let (a16, a_tail) = a_rest.as_chunks::<16>();
let (b16, b_tail) = b_rest.as_chunks::<16>();
for ((d16, a16s), b16s) in dst16.iter_mut().zip(a16).zip(b16) {
let x = _mm_loadu_si128(a16s);
let y = _mm_loadu_si128(b16s);
_mm_storeu_si128(d16, _mm_gf2p8mul_epi8(x, y));
}
for ((d, &x), &y) in dst_tail.iter_mut().zip(a_tail).zip(b_tail) {
*d = Elem(x).mul(Elem(y)).0;
}
}
#[inline]
fn gf_mul_ref<const RED: u8>(mut a: u8, mut b: u8) -> u8 {
let mut product = 0u8;
for _ in 0..8 {
if b & 1 != 0 {
product ^= a;
}
let carry = a & 0x80 != 0;
a <<= 1;
if carry {
a ^= RED;
}
b >>= 1;
}
product
}
#[archmage::rite(v3)]
pub fn multiply_vectors_avx2<const RED: u8>(mut a: __m256i, mut b: __m256i) -> __m256i {
let zero = _mm256_setzero_si256();
let one = _mm256_set1_epi8(1);
let reduction = _mm256_set1_epi8(RED.cast_signed());
let low7 = _mm256_set1_epi8(0x7f);
let mut product = zero;
for round in 0..8 {
let active = _mm256_cmpeq_epi8(_mm256_and_si256(b, one), one);
product = _mm256_xor_si256(product, _mm256_and_si256(a, active));
if round == 7 {
break;
}
let carry = _mm256_cmpgt_epi8(zero, a);
a = _mm256_xor_si256(_mm256_add_epi8(a, a), _mm256_and_si256(reduction, carry));
b = _mm256_and_si256(_mm256_srli_epi16::<1>(b), low7);
}
product
}
#[archmage::rite(v2)]
pub fn multiply_vectors_sse<const RED: u8>(mut a: __m128i, mut b: __m128i) -> __m128i {
let zero = _mm_setzero_si128();
let one = _mm_set1_epi8(1);
let reduction = _mm_set1_epi8(RED.cast_signed());
let low7 = _mm_set1_epi8(0x7f);
let mut product = zero;
for round in 0..8 {
let active = _mm_cmpeq_epi8(_mm_and_si128(b, one), one);
product = _mm_xor_si128(product, _mm_and_si128(a, active));
if round == 7 {
break;
}
let carry = _mm_cmpgt_epi8(zero, a);
a = _mm_xor_si128(_mm_add_epi8(a, a), _mm_and_si128(reduction, carry));
b = _mm_and_si128(_mm_srli_epi16::<1>(b), low7);
}
product
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_elementwise_avx2<const RED: u8>(
_token: archmage::X64V3Token,
dst: &mut [u8],
a: &[u8],
b: &[u8],
) {
assert_eq!(
dst.len(),
a.len(),
"mul_elementwise_avx2: dst and a differ in length"
);
assert_eq!(
dst.len(),
b.len(),
"mul_elementwise_avx2: dst and b differ in length"
);
let (dst_lanes, dst_rest) = dst.as_chunks_mut::<32>();
let (a_lanes, a_rest) = a.as_chunks::<32>();
let (b_lanes, b_rest) = b.as_chunks::<32>();
for ((dlane, alane), blane) in dst_lanes.iter_mut().zip(a_lanes).zip(b_lanes) {
let x = _mm256_loadu_si256(alane);
let y = _mm256_loadu_si256(blane);
_mm256_storeu_si256(dlane, multiply_vectors_avx2::<RED>(x, y));
}
mul_elementwise_ssse3::<RED>(_token.v2(), dst_rest, a_rest, b_rest);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_elementwise_ssse3<const RED: u8>(
_token: archmage::X64V2Token,
dst: &mut [u8],
a: &[u8],
b: &[u8],
) {
assert_eq!(
dst.len(),
a.len(),
"mul_elementwise_ssse3: dst and a differ in length"
);
assert_eq!(
dst.len(),
b.len(),
"mul_elementwise_ssse3: dst and b differ in length"
);
let (dst_lanes, dst_rest) = dst.as_chunks_mut::<16>();
let (a_lanes, a_rest) = a.as_chunks::<16>();
let (b_lanes, b_rest) = b.as_chunks::<16>();
for ((dlane, alane), blane) in dst_lanes.iter_mut().zip(a_lanes).zip(b_lanes) {
let x = _mm_loadu_si128(alane);
let y = _mm_loadu_si128(blane);
_mm_storeu_si128(dlane, multiply_vectors_sse::<RED>(x, y));
}
for ((d, &x), &y) in dst_rest.iter_mut().zip(a_rest).zip(b_rest) {
*d = gf_mul_ref::<RED>(x, y);
}
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_elementwise_assign_gfni(
_token: archmage::X64V3GfniCryptoToken,
dst: &mut [u8],
src: &[u8],
) {
assert_eq!(
dst.len(),
src.len(),
"mul_elementwise_assign_gfni: dst and src differ in length"
);
let (dst_lanes, dst_rest) = dst.as_chunks_mut::<32>();
let (src_lanes, src_rest) = src.as_chunks::<32>();
for (dlane, slane) in dst_lanes.iter_mut().zip(src_lanes) {
let x = _mm256_loadu_si256(&*dlane);
let y = _mm256_loadu_si256(slane);
_mm256_storeu_si256(dlane, _mm256_gf2p8mul_epi8(x, y));
}
let (dst16, dst_tail) = dst_rest.as_chunks_mut::<16>();
let (src16, src_tail) = src_rest.as_chunks::<16>();
for (d16, s16) in dst16.iter_mut().zip(src16) {
let x = _mm_loadu_si128(&*d16);
let y = _mm_loadu_si128(s16);
_mm_storeu_si128(d16, _mm_gf2p8mul_epi8(x, y));
}
for (d, s) in dst_tail.iter_mut().zip(src_tail) {
*d = Elem(*d).mul(Elem(*s)).0;
}
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_elementwise_assign_avx2<const RED: u8>(
_token: archmage::X64V3Token,
dst: &mut [u8],
src: &[u8],
) {
assert_eq!(
dst.len(),
src.len(),
"mul_elementwise_assign_avx2: dst and src differ in length"
);
let (dst_lanes, dst_rest) = dst.as_chunks_mut::<32>();
let (src_lanes, src_rest) = src.as_chunks::<32>();
for (dlane, slane) in dst_lanes.iter_mut().zip(src_lanes) {
let x = _mm256_loadu_si256(&*dlane);
let y = _mm256_loadu_si256(slane);
_mm256_storeu_si256(dlane, multiply_vectors_avx2::<RED>(x, y));
}
mul_elementwise_assign_ssse3::<RED>(_token.v2(), dst_rest, src_rest);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_elementwise_assign_ssse3<const RED: u8>(
_token: archmage::X64V2Token,
dst: &mut [u8],
src: &[u8],
) {
assert_eq!(
dst.len(),
src.len(),
"mul_elementwise_assign_ssse3: dst and src differ in length"
);
let (dst_lanes, dst_rest) = dst.as_chunks_mut::<16>();
let (src_lanes, src_rest) = src.as_chunks::<16>();
for (dlane, slane) in dst_lanes.iter_mut().zip(src_lanes) {
let x = _mm_loadu_si128(&*dlane);
let y = _mm_loadu_si128(slane);
_mm_storeu_si128(dlane, multiply_vectors_sse::<RED>(x, y));
}
for (d, &s) in dst_rest.iter_mut().zip(src_rest) {
*d = gf_mul_ref::<RED>(*d, s);
}
}