#[cfg(target_feature = "sha3")]
use core::arch::aarch64::veor3q_u8;
use core::arch::aarch64::{
uint8x16_t, uint64x2_t, vdupq_n_u8, vdupq_n_u64, veorq_u8, vextq_u8, vgetq_lane_u64,
vmull_high_p64, vmull_p64, vreinterpretq_p64_u8, vreinterpretq_u8_u64, vreinterpretq_u64_u8,
vzip1q_u64, vzip2q_u64,
};
use core::mem::transmute;
use crate::clmul::basis::TAIL_128;
#[inline]
unsafe fn clmul_low(a: uint8x16_t, b: uint8x16_t) -> uint8x16_t {
unsafe {
transmute(vmull_p64(
vgetq_lane_u64::<0>(vreinterpretq_u64_u8(a)),
vgetq_lane_u64::<0>(vreinterpretq_u64_u8(b)),
))
}
}
#[inline]
unsafe fn clmul_high(a: uint8x16_t, b: uint8x16_t) -> uint8x16_t {
unsafe {
transmute(vmull_high_p64(
vreinterpretq_p64_u8(a),
vreinterpretq_p64_u8(b),
))
}
}
#[inline(always)]
unsafe fn xor3(a: uint8x16_t, b: uint8x16_t, c: uint8x16_t) -> uint8x16_t {
unsafe {
#[cfg(target_feature = "sha3")]
{
veor3q_u8(a, b, c)
}
#[cfg(not(target_feature = "sha3"))]
{
veorq_u8(veorq_u8(a, b), c)
}
}
}
#[inline(always)]
unsafe fn reduce(low: uint8x16_t, middle: uint8x16_t, high: uint8x16_t) -> uint8x16_t {
unsafe {
let tail = vreinterpretq_u8_u64(vdupq_n_u64(TAIL_128 as u64));
let m = veorq_u8(middle, clmul_high(high, tail));
let raised = vextq_u8::<8>(vdupq_n_u8(0), m);
veorq_u8(
xor3(low, clmul_low(high, tail), clmul_high(m, tail)),
raised,
)
}
}
#[inline(always)]
pub(crate) fn poly_add_128(a: u128, b: u128) -> u128 {
unsafe {
transmute::<uint8x16_t, u128>(veorq_u8(
transmute::<u128, uint8x16_t>(a),
transmute::<u128, uint8x16_t>(b),
))
}
}
#[inline]
pub(crate) fn poly_mul_128(a: u128, b: u128) -> u128 {
const {
assert!(
cfg!(target_endian = "little"),
"the halves of a `u128` are its vector lanes only on little-endian targets"
);
}
unsafe {
let a = transmute::<u128, uint8x16_t>(a);
let b = transmute::<u128, uint8x16_t>(b);
let swapped = vextq_u8::<8>(b, b);
let middle = veorq_u8(clmul_low(a, swapped), clmul_high(a, swapped));
transmute::<uint8x16_t, u128>(reduce(clmul_low(a, b), middle, clmul_high(a, b)))
}
}
#[inline]
pub(crate) fn poly_square_128(a: u128) -> u128 {
const {
assert!(
cfg!(target_endian = "little"),
"the halves of a `u128` are its vector lanes only on little-endian targets"
);
}
unsafe {
let a = transmute::<u128, uint8x16_t>(a);
let square = reduce(clmul_low(a, a), vdupq_n_u8(0), clmul_high(a, a));
transmute::<uint8x16_t, u128>(square)
}
}
#[inline]
pub(crate) fn poly_dot_128(pairs: impl Iterator<Item = (u128, u128)>) -> u128 {
const {
assert!(cfg!(target_endian = "little"));
}
unsafe {
let zero = vdupq_n_u8(0);
let (mut low, mut high, mut middle) = (zero, zero, zero);
for (a, b) in pairs {
let a = transmute::<u128, uint8x16_t>(a);
let b = transmute::<u128, uint8x16_t>(b);
let swapped = vextq_u8::<8>(b, b);
low = veorq_u8(low, clmul_low(a, b));
high = veorq_u8(high, clmul_high(a, b));
middle = veorq_u8(
middle,
veorq_u8(clmul_low(a, swapped), clmul_high(a, swapped)),
);
}
transmute::<uint8x16_t, u128>(reduce(low, middle, high))
}
}
#[inline]
pub(crate) fn poly_mul_128_by_64(a: u128, b: u64) -> u128 {
const {
assert!(cfg!(target_endian = "little"));
}
unsafe {
let a = transmute::<u128, uint8x16_t>(a);
let b = vreinterpretq_u8_u64(vdupq_n_u64(b));
let zero = vdupq_n_u8(0);
let low = clmul_low(a, b);
let middle = clmul_high(a, b);
let tail = vreinterpretq_u8_u64(vdupq_n_u64(TAIL_128 as u64));
let folded = veorq_u8(vextq_u8::<8>(zero, middle), clmul_high(middle, tail));
transmute::<uint8x16_t, u128>(veorq_u8(low, folded))
}
}
#[derive(Clone, Copy)]
pub(crate) struct SplitMultiplier {
halves_low: uint8x16_t,
halves_high: uint8x16_t,
}
impl SplitMultiplier {
#[inline]
pub(crate) fn new(t: u128) -> Self {
let companion = unsafe {
let t = transmute::<u128, uint8x16_t>(t);
let raised = vextq_u8::<8>(vdupq_n_u8(0), t);
transmute::<uint8x16_t, u128>(veorq_u8(raised, clmul_high(t, tail())))
};
Self::from_parts(t, companion)
}
#[inline(always)]
pub(crate) fn from_parts(t: u128, companion: u128) -> Self {
debug_assert_eq!(
companion,
poly_mul_128(t, 1 << 64),
"the companion must be the multiplier times x^64"
);
unsafe {
let t = transmute::<u128, uint64x2_t>(t);
let companion = transmute::<u128, uint64x2_t>(companion);
Self {
halves_low: transmute::<uint64x2_t, uint8x16_t>(vzip1q_u64(t, companion)),
halves_high: transmute::<uint64x2_t, uint8x16_t>(vzip2q_u64(t, companion)),
}
}
}
#[inline(always)]
pub(crate) fn mul(self, v: u128) -> u128 {
unsafe {
let v = transmute::<u128, uint8x16_t>(v);
let low = veorq_u8(
clmul_low(v, self.halves_low),
clmul_high(v, self.halves_low),
);
let high = veorq_u8(
clmul_low(v, self.halves_high),
clmul_high(v, self.halves_high),
);
let raised = vextq_u8::<8>(vdupq_n_u8(0), high);
transmute::<uint8x16_t, u128>(veorq_u8(low, veorq_u8(raised, clmul_high(high, tail()))))
}
}
}
#[inline(always)]
unsafe fn tail() -> uint8x16_t {
unsafe { vreinterpretq_u8_u64(vdupq_n_u64(TAIL_128 as u64)) }
}