use crate::field::Elem;
use crate::field::binary::Binary;
use crate::field::binary::tower::{Rijndael16, Rijndael32, Tower, TowerSpec};
use crate::kernel::scalar;
use crate::kernel::tables::{Tower2Coeff, TowerCoeff};
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
#[inline]
const fn pack32(lo: u16, hi: u16) -> u32 {
(lo as u32) | ((hi as u32) << 16)
}
#[inline]
#[must_use]
pub fn gf32_tiles(coeff: Elem<Binary<32, Tower<Rijndael32>>>) -> [u32; 4] {
let (c0, c1) = coeff.to_components();
#[allow(clippy::cast_possible_truncation)]
let delta =
Elem::<Binary<16, Tower<Rijndael16>>>::from_raw(Rijndael32::CONSTANT_COEFFICIENT as u16);
let t = Tower2Coeff::derive(c0, c1, delta);
let [s0, s1] = t.same;
let [x0, x1] = t.cross;
let same_a = pack32(TowerCoeff::new(s0).same, TowerCoeff::new(s1).same);
let cross_a = pack32(TowerCoeff::new(s0).cross, TowerCoeff::new(s1).cross);
let same_b = pack32(TowerCoeff::new(x0).same, TowerCoeff::new(x1).same);
let cross_b = pack32(TowerCoeff::new(x0).cross, TowerCoeff::new(x1).cross);
[same_a, cross_a, same_b, cross_b]
}
pub(crate) const SWAP1: [u8; 32] = [
1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13,
12, 15, 14,
];
pub(crate) const SWAP2: [u8; 32] = [
2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13, 2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14,
15, 12, 13,
];
pub(crate) const REV4: [u8; 32] = [
3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12, 3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15,
14, 13, 12,
];
#[archmage::rite(v3, import_intrinsics)]
pub(crate) fn load_mask(m: &[u8; 32]) -> __m256i {
_mm256_loadu_si256(m)
}
#[archmage::rite(v3)]
fn set1_tile(tile: u32) -> __m256i {
_mm256_set1_epi32(i32::from_ne_bytes(tile.to_ne_bytes()))
}
#[archmage::rite(v3_gfni_crypto)]
fn scale32(x: __m256i, lane: &Lane32) -> __m256i {
_mm256_xor_si256(
_mm256_xor_si256(
_mm256_gf2p8mul_epi8(x, lane.same_a),
_mm256_gf2p8mul_epi8(_mm256_shuffle_epi8(x, lane.s1), lane.cross_a),
),
_mm256_xor_si256(
_mm256_gf2p8mul_epi8(_mm256_shuffle_epi8(x, lane.s2), lane.same_b),
_mm256_gf2p8mul_epi8(_mm256_shuffle_epi8(x, lane.rev4), lane.cross_b),
),
)
}
struct Lane32 {
same_a: __m256i,
cross_a: __m256i,
same_b: __m256i,
cross_b: __m256i,
s1: __m256i,
s2: __m256i,
rev4: __m256i,
}
#[archmage::rite(v3_gfni_crypto)]
fn lane32(tiles: [u32; 4]) -> Lane32 {
Lane32 {
same_a: set1_tile(tiles[0]),
cross_a: set1_tile(tiles[1]),
same_b: set1_tile(tiles[2]),
cross_b: set1_tile(tiles[3]),
s1: load_mask(&SWAP1),
s2: load_mask(&SWAP2),
rev4: load_mask(&REV4),
}
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_gfni(
_token: archmage::X64V3GfniCryptoToken,
dst: &mut [u8],
coeff: Elem<Binary<32, Tower<Rijndael32>>>,
tiles: [u32; 4],
src: &[u8],
) {
assert_eq!(
dst.len(),
src.len(),
"gf32::mul_add_gfni: dst is {} bytes but src is {} bytes",
dst.len(),
src.len(),
);
assert!(
dst.len().is_multiple_of(4),
"gf32::mul_add_gfni: buffer of {} bytes is not a whole number of 4-byte elements",
dst.len(),
);
let l = lane32(tiles);
let (lanes, dst_tail) = dst.as_chunks_mut::<32>();
let (src_lanes, src_tail) = src.as_chunks::<32>();
for (dst_lane, src_lane) in lanes.iter_mut().zip(src_lanes) {
let x = _mm256_loadu_si256(src_lane);
let r = scale32(x, &l);
let d = _mm256_loadu_si256(&*dst_lane);
_mm256_storeu_si256(dst_lane, _mm256_xor_si256(d, r));
}
scalar::mul_add::<Binary<32, Tower<Rijndael32>>>(dst_tail, 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<Binary<32, Tower<Rijndael32>>>,
tiles: [u32; 4],
) {
assert!(
dst.len().is_multiple_of(4),
"gf32::mul_assign_gfni: buffer of {} bytes is not a whole number of 4-byte elements",
dst.len(),
);
let l = lane32(tiles);
let (lanes, dst_tail) = dst.as_chunks_mut::<32>();
for dst_lane in lanes.iter_mut() {
let x = _mm256_loadu_si256(&*dst_lane);
let r = scale32(x, &l);
_mm256_storeu_si256(dst_lane, r);
}
scalar::mul_assign::<Binary<32, Tower<Rijndael32>>>(dst_tail, coeff);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_into_gfni(
_token: archmage::X64V3GfniCryptoToken,
dst: &mut [u8],
coeff: Elem<Binary<32, Tower<Rijndael32>>>,
tiles: [u32; 4],
src: &[u8],
) {
assert_eq!(
dst.len(),
src.len(),
"gf32::mul_into_gfni: dst is {} bytes but src is {} bytes",
dst.len(),
src.len(),
);
assert!(
dst.len().is_multiple_of(4),
"gf32::mul_into_gfni: buffer of {} bytes is not a whole number of 4-byte elements",
dst.len(),
);
let l = lane32(tiles);
let (lanes, dst_tail) = dst.as_chunks_mut::<32>();
let (src_lanes, src_tail) = src.as_chunks::<32>();
for (dst_lane, src_lane) in lanes.iter_mut().zip(src_lanes) {
let x = _mm256_loadu_si256(src_lane);
let r = scale32(x, &l);
_mm256_storeu_si256(dst_lane, r);
}
dst_tail.copy_from_slice(src_tail);
scalar::mul_assign::<Binary<32, Tower<Rijndael32>>>(dst_tail, coeff);
}