use crate::field::gf8b::Elem;
use crate::kernel::gf8::{mul_add_nibble, mul_assign_nibble, mul_into_nibble};
use crate::kernel::tables::{ScaleTable, scale_table};
use crate::kernel::x86::HALF_LANE_PEEL_MIN;
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_gfni(
_token: archmage::X64V3GfniCryptoToken,
dst: &mut [u8],
coeff: Elem,
src: &[u8],
) {
assert_eq!(dst.len(), src.len());
if dst.len() >= HALF_LANE_PEEL_MIN && dst.as_ptr().align_offset(32) == 16 {
let factor_half = _mm256_castsi256_si128(_mm256_set1_epi8(coeff.0.cast_signed()));
let (d, _) = dst[..16].as_chunks_mut::<16>();
let (s, _) = src[..16].as_chunks::<16>();
let x = _mm_loadu_si128(&s[0]);
let d0 = _mm_loadu_si128(&d[0]);
_mm_storeu_si128(
&mut d[0],
_mm_xor_si128(d0, _mm_gf2p8mul_epi8(x, factor_half)),
);
mul_add_gfni_impl(&mut dst[16..], coeff, &src[16..]);
} else {
mul_add_gfni_impl(dst, coeff, src);
}
}
#[archmage::rite(v3_gfni_crypto, import_intrinsics)]
pub(in crate::kernel::x86::gf8) fn mul_add_gfni_impl(dst: &mut [u8], coeff: Elem, src: &[u8]) {
let factor = _mm256_set1_epi8(coeff.0.cast_signed());
let factor_half = _mm256_castsi256_si128(factor);
let (dst_tiles, dst_mid) = dst.as_chunks_mut::<128>();
let (src_tiles, src_mid) = src.as_chunks::<128>();
for (dtile, stile) in dst_tiles.iter_mut().zip(src_tiles) {
let (d, _) = dtile.as_chunks_mut::<32>();
let (s, _) = stile.as_chunks::<32>();
let x0 = _mm256_loadu_si256(&s[0]);
let x1 = _mm256_loadu_si256(&s[1]);
let x2 = _mm256_loadu_si256(&s[2]);
let x3 = _mm256_loadu_si256(&s[3]);
let d0 = _mm256_loadu_si256(&d[0]);
let d1 = _mm256_loadu_si256(&d[1]);
let d2 = _mm256_loadu_si256(&d[2]);
let d3 = _mm256_loadu_si256(&d[3]);
let r0 = _mm256_xor_si256(d0, _mm256_gf2p8mul_epi8(x0, factor));
let r1 = _mm256_xor_si256(d1, _mm256_gf2p8mul_epi8(x1, factor));
let r2 = _mm256_xor_si256(d2, _mm256_gf2p8mul_epi8(x2, factor));
let r3 = _mm256_xor_si256(d3, _mm256_gf2p8mul_epi8(x3, factor));
_mm256_storeu_si256(&mut d[0], r0);
_mm256_storeu_si256(&mut d[1], r1);
_mm256_storeu_si256(&mut d[2], r2);
_mm256_storeu_si256(&mut d[3], r3);
}
let (dst_lanes, dst_rest) = dst_mid.as_chunks_mut::<32>();
let (src_lanes, src_rest) = src_mid.as_chunks::<32>();
for (dlane, slane) in dst_lanes.iter_mut().zip(src_lanes) {
let x = _mm256_loadu_si256(slane);
let d = _mm256_loadu_si256(dlane);
let r = _mm256_xor_si256(d, _mm256_gf2p8mul_epi8(x, factor));
_mm256_storeu_si256(dlane, r);
}
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(s16);
let d = _mm_loadu_si128(d16);
let r = _mm_xor_si128(d, _mm_gf2p8mul_epi8(x, factor_half));
_mm_storeu_si128(d16, r);
}
mul_add_nibble(dst_tail, scale_table(coeff), src_tail);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_assign_gfni(_token: archmage::X64V3GfniCryptoToken, dst: &mut [u8], coeff: Elem) {
let factor = _mm256_set1_epi8(coeff.0.cast_signed());
let factor_half = _mm256_castsi256_si128(factor);
let head = if dst.len() >= HALF_LANE_PEEL_MIN && dst.as_ptr().align_offset(32) == 16 {
16
} else {
0
};
if head != 0 {
let (d, _) = dst[..head].as_chunks_mut::<16>();
let value = _mm_gf2p8mul_epi8(_mm_loadu_si128(&d[0]), factor_half);
_mm_storeu_si128(&mut d[0], value);
}
let dst = &mut dst[head..];
let (lanes, rest) = dst.as_chunks_mut::<32>();
for lane in lanes {
let d = _mm256_loadu_si256(&*lane);
_mm256_storeu_si256(lane, _mm256_gf2p8mul_epi8(d, factor));
}
let (sixteens, tail) = rest.as_chunks_mut::<16>();
for s16 in sixteens {
let d = _mm_loadu_si128(&*s16);
_mm_storeu_si128(s16, _mm_gf2p8mul_epi8(d, factor_half));
}
mul_assign_nibble(tail, scale_table(coeff));
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_into_gfni(
_token: archmage::X64V3GfniCryptoToken,
dst: &mut [u8],
coeff: Elem,
src: &[u8],
) {
assert_eq!(dst.len(), src.len());
if let Some(peel) = crate::kernel::x86::nt_split(dst, 1) {
let (head, body) = dst.split_at_mut(peel);
let (src_head, src_body) = src.split_at(peel);
mul_into_impl::<false>(head, coeff, src_head);
mul_into_impl::<true>(body, coeff, src_body);
_mm_sfence();
} else {
let head = if dst.len() >= HALF_LANE_PEEL_MIN && dst.as_ptr().align_offset(32) == 16 {
16
} else {
0
};
if head != 0 {
let (dst_head, dst) = dst.split_at_mut(head);
let (src_head, src) = src.split_at(head);
mul_into_impl::<false>(dst_head, coeff, src_head);
mul_into_impl::<false>(dst, coeff, src);
} else {
mul_into_impl::<false>(dst, coeff, src);
}
}
}
#[archmage::rite(v3_gfni_crypto, import_intrinsics)]
#[allow(unsafe_code)]
fn mul_into_impl<const NT: bool>(dst: &mut [u8], coeff: Elem, src: &[u8]) {
let factor = _mm256_set1_epi8(coeff.0.cast_signed());
let factor_half = _mm256_castsi256_si128(factor);
let prefetch = crate::kernel::x86::prefetch_dst(dst, NT);
let tiles = dst.len() / 128 * 128;
let (mut drest, dst_mid) = dst.split_at_mut(tiles);
let (mut srest, src_mid) = src.split_at(tiles);
while !drest.is_empty() {
if prefetch && drest.len() >= crate::kernel::x86::PREFETCH_AHEAD + 128 {
crate::kernel::x86::prefetch_tile(&drest[crate::kernel::x86::PREFETCH_AHEAD..]);
}
let (dtile, dnext) = drest.split_at_mut(128);
let (stile, snext) = srest.split_at(128);
let (s, _) = stile.as_chunks::<32>();
let r0 = _mm256_gf2p8mul_epi8(_mm256_loadu_si256(&s[0]), factor);
let r1 = _mm256_gf2p8mul_epi8(_mm256_loadu_si256(&s[1]), factor);
let r2 = _mm256_gf2p8mul_epi8(_mm256_loadu_si256(&s[2]), factor);
let r3 = _mm256_gf2p8mul_epi8(_mm256_loadu_si256(&s[3]), factor);
unsafe {
let dp = dtile.as_mut_ptr();
crate::kernel::x86::store_avx2::<NT>(dp, r0);
crate::kernel::x86::store_avx2::<NT>(dp.add(32), r1);
crate::kernel::x86::store_avx2::<NT>(dp.add(64), r2);
crate::kernel::x86::store_avx2::<NT>(dp.add(96), r3);
}
drest = dnext;
srest = snext;
}
let (dst_lanes, dst_mid2) = dst_mid.as_chunks_mut::<32>();
let (src_lanes, src_mid2) = src_mid.as_chunks::<32>();
for (dlane, slane) in dst_lanes.iter_mut().zip(src_lanes) {
let x = _mm256_loadu_si256(slane);
_mm256_storeu_si256(dlane, _mm256_gf2p8mul_epi8(x, factor));
}
let (dst16, dst_tail) = dst_mid2.as_chunks_mut::<16>();
let (src16, src_tail) = src_mid2.as_chunks::<16>();
for (d16, s16) in dst16.iter_mut().zip(src16) {
let x = _mm_loadu_si128(s16);
_mm_storeu_si128(d16, _mm_gf2p8mul_epi8(x, factor_half));
}
mul_into_nibble(dst_tail, scale_table(coeff), src_tail);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_gfni_8d(
_token: archmage::X64V3GfniCryptoToken,
dst: &mut [u8],
map: u64,
table: &ScaleTable,
src: &[u8],
) {
assert_eq!(dst.len(), src.len());
if dst.len() >= HALF_LANE_PEEL_MIN && dst.as_ptr().align_offset(32) == 16 {
let factor_half = _mm256_castsi256_si128(_mm256_set1_epi64x(map.cast_signed()));
let (d, _) = dst[..16].as_chunks_mut::<16>();
let (s, _) = src[..16].as_chunks::<16>();
let x = _mm_loadu_si128(&s[0]);
let d0 = _mm_loadu_si128(&d[0]);
_mm_storeu_si128(
&mut d[0],
_mm_xor_si128(d0, _mm_gf2p8affine_epi64_epi8::<0>(x, factor_half)),
);
mul_add_gfni_8d_impl(&mut dst[16..], map, table, &src[16..]);
} else {
mul_add_gfni_8d_impl(dst, map, table, src);
}
}
#[archmage::rite(v3_gfni_crypto, import_intrinsics)]
pub(in crate::kernel::x86::gf8) fn mul_add_gfni_8d_impl(
dst: &mut [u8],
map: u64,
table: &ScaleTable,
src: &[u8],
) {
let factor = _mm256_set1_epi64x(map.cast_signed());
let factor_half = _mm256_castsi256_si128(factor);
let (dst_tiles, dst_mid) = dst.as_chunks_mut::<128>();
let (src_tiles, src_mid) = src.as_chunks::<128>();
for (dtile, stile) in dst_tiles.iter_mut().zip(src_tiles) {
let (d, _) = dtile.as_chunks_mut::<32>();
let (s, _) = stile.as_chunks::<32>();
let x0 = _mm256_loadu_si256(&s[0]);
let x1 = _mm256_loadu_si256(&s[1]);
let x2 = _mm256_loadu_si256(&s[2]);
let x3 = _mm256_loadu_si256(&s[3]);
let d0 = _mm256_loadu_si256(&d[0]);
let d1 = _mm256_loadu_si256(&d[1]);
let d2 = _mm256_loadu_si256(&d[2]);
let d3 = _mm256_loadu_si256(&d[3]);
let r0 = _mm256_xor_si256(d0, _mm256_gf2p8affine_epi64_epi8::<0>(x0, factor));
let r1 = _mm256_xor_si256(d1, _mm256_gf2p8affine_epi64_epi8::<0>(x1, factor));
let r2 = _mm256_xor_si256(d2, _mm256_gf2p8affine_epi64_epi8::<0>(x2, factor));
let r3 = _mm256_xor_si256(d3, _mm256_gf2p8affine_epi64_epi8::<0>(x3, factor));
_mm256_storeu_si256(&mut d[0], r0);
_mm256_storeu_si256(&mut d[1], r1);
_mm256_storeu_si256(&mut d[2], r2);
_mm256_storeu_si256(&mut d[3], r3);
}
let (dst_lanes, dst_rest) = dst_mid.as_chunks_mut::<32>();
let (src_lanes, src_rest) = src_mid.as_chunks::<32>();
for (dlane, slane) in dst_lanes.iter_mut().zip(src_lanes) {
let x = _mm256_loadu_si256(slane);
let d = _mm256_loadu_si256(dlane);
let r = _mm256_xor_si256(d, _mm256_gf2p8affine_epi64_epi8::<0>(x, factor));
_mm256_storeu_si256(dlane, r);
}
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(s16);
let d = _mm_loadu_si128(d16);
let r = _mm_xor_si128(d, _mm_gf2p8affine_epi64_epi8::<0>(x, factor_half));
_mm_storeu_si128(d16, r);
}
mul_add_nibble(dst_tail, table, src_tail);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_assign_gfni_8d(
_token: archmage::X64V3GfniCryptoToken,
dst: &mut [u8],
map: u64,
table: &ScaleTable,
) {
mul_assign_gfni_8d_impl(dst, map, table);
}
#[archmage::rite(v3_gfni_crypto, import_intrinsics)]
pub(in crate::kernel::x86::gf8) fn mul_assign_gfni_8d_impl(
dst: &mut [u8],
map: u64,
table: &ScaleTable,
) {
let factor = _mm256_set1_epi64x(map.cast_signed());
let factor_half = _mm256_castsi256_si128(factor);
let head = if dst.len() >= HALF_LANE_PEEL_MIN && dst.as_ptr().align_offset(32) == 16 {
16
} else {
0
};
if head != 0 {
let (d, _) = dst[..head].as_chunks_mut::<16>();
let value = _mm_gf2p8affine_epi64_epi8::<0>(_mm_loadu_si128(&d[0]), factor_half);
_mm_storeu_si128(&mut d[0], value);
}
let dst = &mut dst[head..];
let (lanes, rest) = dst.as_chunks_mut::<32>();
for lane in lanes {
let d = _mm256_loadu_si256(&*lane);
_mm256_storeu_si256(lane, _mm256_gf2p8affine_epi64_epi8::<0>(d, factor));
}
let (sixteens, tail) = rest.as_chunks_mut::<16>();
for s16 in sixteens {
let d = _mm_loadu_si128(&*s16);
_mm_storeu_si128(s16, _mm_gf2p8affine_epi64_epi8::<0>(d, factor_half));
}
mul_assign_nibble(tail, table);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_into_gfni_8d(
_token: archmage::X64V3GfniCryptoToken,
dst: &mut [u8],
map: u64,
table: &ScaleTable,
src: &[u8],
) {
assert_eq!(dst.len(), src.len());
if let Some(peel) = crate::kernel::x86::nt_split(dst, 1) {
let (head, body) = dst.split_at_mut(peel);
let (src_head, src_body) = src.split_at(peel);
mul_into_gfni_8d_impl::<false>(head, map, table, src_head);
mul_into_gfni_8d_impl::<true>(body, map, table, src_body);
_mm_sfence();
} else {
let head = if dst.len() >= HALF_LANE_PEEL_MIN && dst.as_ptr().align_offset(32) == 16 {
16
} else {
0
};
if head != 0 {
let (dst_head, dst) = dst.split_at_mut(head);
let (src_head, src) = src.split_at(head);
mul_into_gfni_8d_impl::<false>(dst_head, map, table, src_head);
mul_into_gfni_8d_impl::<false>(dst, map, table, src);
} else {
mul_into_gfni_8d_impl::<false>(dst, map, table, src);
}
}
}
#[archmage::rite(v3_gfni_crypto, import_intrinsics)]
#[allow(unsafe_code)]
pub(in crate::kernel::x86::gf8) fn mul_into_gfni_8d_impl<const NT: bool>(
dst: &mut [u8],
map: u64,
table: &ScaleTable,
src: &[u8],
) {
let factor = _mm256_set1_epi64x(map.cast_signed());
let factor_half = _mm256_castsi256_si128(factor);
let prefetch = crate::kernel::x86::prefetch_dst(dst, NT);
let tiles = dst.len() / 128 * 128;
let (mut drest, dst_mid) = dst.split_at_mut(tiles);
let (mut srest, src_mid) = src.split_at(tiles);
while !drest.is_empty() {
if prefetch && drest.len() >= crate::kernel::x86::PREFETCH_AHEAD + 128 {
crate::kernel::x86::prefetch_tile(&drest[crate::kernel::x86::PREFETCH_AHEAD..]);
}
let (dtile, dnext) = drest.split_at_mut(128);
let (stile, snext) = srest.split_at(128);
let (s, _) = stile.as_chunks::<32>();
let r0 = _mm256_gf2p8affine_epi64_epi8::<0>(_mm256_loadu_si256(&s[0]), factor);
let r1 = _mm256_gf2p8affine_epi64_epi8::<0>(_mm256_loadu_si256(&s[1]), factor);
let r2 = _mm256_gf2p8affine_epi64_epi8::<0>(_mm256_loadu_si256(&s[2]), factor);
let r3 = _mm256_gf2p8affine_epi64_epi8::<0>(_mm256_loadu_si256(&s[3]), factor);
unsafe {
let dp = dtile.as_mut_ptr();
crate::kernel::x86::store_avx2::<NT>(dp, r0);
crate::kernel::x86::store_avx2::<NT>(dp.add(32), r1);
crate::kernel::x86::store_avx2::<NT>(dp.add(64), r2);
crate::kernel::x86::store_avx2::<NT>(dp.add(96), r3);
}
drest = dnext;
srest = snext;
}
let (dst_lanes, dst_mid2) = dst_mid.as_chunks_mut::<32>();
let (src_lanes, src_mid2) = src_mid.as_chunks::<32>();
for (dlane, slane) in dst_lanes.iter_mut().zip(src_lanes) {
let x = _mm256_loadu_si256(slane);
_mm256_storeu_si256(dlane, _mm256_gf2p8affine_epi64_epi8::<0>(x, factor));
}
let (dst16, dst_tail) = dst_mid2.as_chunks_mut::<16>();
let (src16, src_tail) = src_mid2.as_chunks::<16>();
for (d16, s16) in dst16.iter_mut().zip(src16) {
let x = _mm_loadu_si128(s16);
_mm_storeu_si128(d16, _mm_gf2p8affine_epi64_epi8::<0>(x, factor_half));
}
mul_into_nibble(dst_tail, table, src_tail);
}