use crate::field::Elem;
use crate::field::binary::Binary;
use crate::field::binary::tower::{Rijndael32, Rijndael64, Tower, TowerSpec};
use crate::kernel::scalar;
use crate::kernel::tables::Tower2Coeff;
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
use super::gf32::{REV4, SWAP1, SWAP2, gf32_tiles, load_mask};
#[inline]
const fn pack64(lo: u32, hi: u32) -> u64 {
(lo as u64) | ((hi as u64) << 32)
}
#[inline]
#[must_use]
pub fn gf64_tiles(coeff: Elem<Binary<64, Tower<Rijndael64>>>) -> [u64; 8] {
let (c0, c1) = coeff.to_components();
#[allow(clippy::cast_possible_truncation)]
let delta =
Elem::<Binary<32, Tower<Rijndael32>>>::from_raw(Rijndael64::CONSTANT_COEFFICIENT as u32);
let tower = Tower2Coeff::derive(c0, c1, delta);
let [s0, s1] = tower.same;
let [x0, x1] = tower.cross;
let same0 = gf32_tiles(s0);
let same1 = gf32_tiles(s1);
let cross0 = gf32_tiles(x0);
let cross1 = gf32_tiles(x1);
[
pack64(same0[0], same1[0]),
pack64(same0[1], same1[1]),
pack64(same0[2], same1[2]),
pack64(same0[3], same1[3]),
pack64(cross0[0], cross1[0]),
pack64(cross0[1], cross1[1]),
pack64(cross0[2], cross1[2]),
pack64(cross0[3], cross1[3]),
]
}
pub(crate) const SWAP4: [u8; 32] = [
4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15,
8, 9, 10, 11,
];
const SWAP1_4: [u8; 32] = [
5, 4, 7, 6, 1, 0, 3, 2, 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2, 13, 12, 15, 14,
9, 8, 11, 10,
];
const SWAP2_4: [u8; 32] = [
6, 7, 4, 5, 2, 3, 0, 1, 14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1, 14, 15, 12, 13,
10, 11, 8, 9,
];
const REV8: [u8; 32] = [
7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12,
11, 10, 9, 8,
];
#[archmage::rite(v3)]
fn set1_tile(tile: u64) -> __m256i {
_mm256_set1_epi64x(i64::from_ne_bytes(tile.to_ne_bytes()))
}
#[archmage::rite(v3_gfni_crypto)]
fn scale64(x: __m256i, lane: &Lane64) -> __m256i {
_mm256_xor_si256(
_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),
),
),
_mm256_xor_si256(
_mm256_xor_si256(
_mm256_gf2p8mul_epi8(_mm256_shuffle_epi8(x, lane.s4), lane.same_c),
_mm256_gf2p8mul_epi8(_mm256_shuffle_epi8(x, lane.s1_4), lane.cross_c),
),
_mm256_xor_si256(
_mm256_gf2p8mul_epi8(_mm256_shuffle_epi8(x, lane.s2_4), lane.same_d),
_mm256_gf2p8mul_epi8(_mm256_shuffle_epi8(x, lane.rev8), lane.cross_d),
),
),
)
}
struct Lane64 {
same_a: __m256i,
cross_a: __m256i,
same_b: __m256i,
cross_b: __m256i,
same_c: __m256i,
cross_c: __m256i,
same_d: __m256i,
cross_d: __m256i,
s1: __m256i,
s2: __m256i,
rev4: __m256i,
s4: __m256i,
s1_4: __m256i,
s2_4: __m256i,
rev8: __m256i,
}
#[archmage::rite(v3_gfni_crypto)]
fn lane64(tiles: [u64; 8]) -> Lane64 {
Lane64 {
same_a: set1_tile(tiles[0]),
cross_a: set1_tile(tiles[1]),
same_b: set1_tile(tiles[2]),
cross_b: set1_tile(tiles[3]),
same_c: set1_tile(tiles[4]),
cross_c: set1_tile(tiles[5]),
same_d: set1_tile(tiles[6]),
cross_d: set1_tile(tiles[7]),
s1: load_mask(&SWAP1),
s2: load_mask(&SWAP2),
rev4: load_mask(&REV4),
s4: load_mask(&SWAP4),
s1_4: load_mask(&SWAP1_4),
s2_4: load_mask(&SWAP2_4),
rev8: load_mask(&REV8),
}
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_gfni(
_token: archmage::X64V3GfniCryptoToken,
dst: &mut [u8],
coeff: Elem<Binary<64, Tower<Rijndael64>>>,
tiles: [u64; 8],
src: &[u8],
) {
assert_eq!(
dst.len(),
src.len(),
"gf64::mul_add_gfni: dst is {} bytes but src is {} bytes",
dst.len(),
src.len(),
);
assert!(
dst.len().is_multiple_of(8),
"gf64::mul_add_gfni: buffer of {} bytes is not a whole number of 8-byte elements",
dst.len(),
);
let l = lane64(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 = scale64(x, &l);
let d = _mm256_loadu_si256(&*dst_lane);
_mm256_storeu_si256(dst_lane, _mm256_xor_si256(d, r));
}
scalar::mul_add::<Binary<64, Tower<Rijndael64>>>(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<64, Tower<Rijndael64>>>,
tiles: [u64; 8],
) {
assert!(
dst.len().is_multiple_of(8),
"gf64::mul_assign_gfni: buffer of {} bytes is not a whole number of 8-byte elements",
dst.len(),
);
let l = lane64(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 = scale64(x, &l);
_mm256_storeu_si256(dst_lane, r);
}
scalar::mul_assign::<Binary<64, Tower<Rijndael64>>>(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<64, Tower<Rijndael64>>>,
tiles: [u64; 8],
src: &[u8],
) {
assert_eq!(
dst.len(),
src.len(),
"gf64::mul_into_gfni: dst is {} bytes but src is {} bytes",
dst.len(),
src.len(),
);
assert!(
dst.len().is_multiple_of(8),
"gf64::mul_into_gfni: buffer of {} bytes is not a whole number of 8-byte elements",
dst.len(),
);
let l = lane64(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 = scale64(x, &l);
_mm256_storeu_si256(dst_lane, r);
}
dst_tail.copy_from_slice(src_tail);
scalar::mul_assign::<Binary<64, Tower<Rijndael64>>>(dst_tail, coeff);
}