use core::arch::aarch64::*;
use p3_binary_field::Poly64;
#[inline]
pub(super) fn butterfly<const INVERSE: bool>(
lo: &mut [Poly64],
hi: &mut [Poly64],
t: Poly64,
) -> usize {
let covered = lo.len() / 2 * 2;
unsafe {
let twiddle = vdupq_n_u64(t.to_bits());
let bulk = covered / 8 * 8;
for start in (0..bulk).step_by(8) {
let mut low: [uint64x2_t; 4] =
core::array::from_fn(|pair| vld1q_u64(lo.as_ptr().add(start + 2 * pair).cast()));
let mut high: [uint64x2_t; 4] =
core::array::from_fn(|pair| vld1q_u64(hi.as_ptr().add(start + 2 * pair).cast()));
if INVERSE {
high = core::array::from_fn(|pair| veorq_u64(high[pair], low[pair]));
}
let products: [uint64x2_t; 4] = high.map(|value| multiply(value, twiddle));
low = core::array::from_fn(|pair| veorq_u64(low[pair], products[pair]));
if !INVERSE {
high = core::array::from_fn(|pair| veorq_u64(high[pair], low[pair]));
}
for pair in 0..4 {
vst1q_u64(lo.as_mut_ptr().add(start + 2 * pair).cast(), low[pair]);
vst1q_u64(hi.as_mut_ptr().add(start + 2 * pair).cast(), high[pair]);
}
}
for start in (bulk..covered).step_by(2) {
let low = vld1q_u64(lo.as_ptr().add(start).cast());
let high = vld1q_u64(hi.as_ptr().add(start).cast());
let high = if INVERSE { veorq_u64(high, low) } else { high };
let low = veorq_u64(low, multiply(high, twiddle));
let high = if INVERSE { high } else { veorq_u64(high, low) };
vst1q_u64(lo.as_mut_ptr().add(start).cast(), low);
vst1q_u64(hi.as_mut_ptr().add(start).cast(), high);
}
}
covered
}
#[inline]
pub(super) fn radix8<const INVERSE: bool>(rows: &mut [&mut [Poly64]; 8], t: &[Poly64; 7]) -> usize {
let covered = rows[0].len() / 2 * 2;
let bulk = covered / 4 * 4;
radix8_groups::<INVERSE, 2>(rows, t, 0..bulk);
radix8_groups::<INVERSE, 1>(rows, t, bulk..covered);
covered
}
#[inline(always)]
fn radix8_groups<const INVERSE: bool, const PAIRS: usize>(
rows: &mut [&mut [Poly64]; 8],
t: &[Poly64; 7],
range: core::ops::Range<usize>,
) {
unsafe {
let twiddles = t.map(|t| vdupq_n_u64(t.to_bits()));
for start in range.step_by(2 * PAIRS) {
let mut values: [[uint64x2_t; PAIRS]; 8] = core::array::from_fn(|row| {
core::array::from_fn(|pair| {
vld1q_u64(rows[row].as_ptr().add(start + 2 * pair).cast())
})
});
macro_rules! step {
($lo:literal, $hi:literal, $t:literal) => {{
let (mut lo, mut hi) = (values[$lo], values[$hi]);
if INVERSE {
hi = core::array::from_fn(|pair| veorq_u64(hi[pair], lo[pair]));
}
if t[$t].to_bits() != 0 {
let products = hi.map(|value| multiply(value, twiddles[$t]));
lo = core::array::from_fn(|pair| veorq_u64(lo[pair], products[pair]));
}
if !INVERSE {
hi = core::array::from_fn(|pair| veorq_u64(hi[pair], lo[pair]));
}
values[$lo] = lo;
values[$hi] = hi;
}};
}
if INVERSE {
step!(0, 1, 3);
step!(2, 3, 4);
step!(4, 5, 5);
step!(6, 7, 6);
step!(0, 2, 1);
step!(1, 3, 1);
step!(4, 6, 2);
step!(5, 7, 2);
step!(0, 4, 0);
step!(1, 5, 0);
step!(2, 6, 0);
step!(3, 7, 0);
} else {
step!(0, 4, 0);
step!(1, 5, 0);
step!(2, 6, 0);
step!(3, 7, 0);
step!(0, 2, 1);
step!(1, 3, 1);
step!(4, 6, 2);
step!(5, 7, 2);
step!(0, 1, 3);
step!(2, 3, 4);
step!(4, 5, 5);
step!(6, 7, 6);
}
for (row, pairs) in rows.iter_mut().zip(values) {
for (pair, value) in pairs.into_iter().enumerate() {
vst1q_u64(row.as_mut_ptr().add(start + 2 * pair).cast(), value);
}
}
}
}
}
#[inline(always)]
unsafe fn multiply(value: uint64x2_t, twiddle: uint64x2_t) -> uint64x2_t {
unsafe {
let a = vreinterpretq_p64_u64(value);
let b = vreinterpretq_p64_u64(twiddle);
let p0 = vreinterpretq_u64_p128(vmull_p64(vgetq_lane_p64::<0>(a), vgetq_lane_p64::<0>(b)));
let p1 = vreinterpretq_u64_p128(vmull_high_p64(a, b));
let tail = vreinterpretq_p64_u64(vdupq_n_u64(0x1b));
let t0 = vreinterpretq_u64_p128(vmull_high_p64(vreinterpretq_p64_u64(p0), tail));
let t1 = vreinterpretq_u64_p128(vmull_high_p64(vreinterpretq_p64_u64(p1), tail));
let u0 = vreinterpretq_u64_p128(vmull_high_p64(vreinterpretq_p64_u64(t0), tail));
let u1 = vreinterpretq_u64_p128(vmull_high_p64(vreinterpretq_p64_u64(t1), tail));
let (mut e0, mut e1) = (xor3(p0, t0, u0), xor3(p1, t1, u1));
core::arch::asm!(
"/* {0:v} {1:v} */",
inout(vreg) e0,
inout(vreg) e1,
options(pure, nomem, nostack, preserves_flags)
);
vzip1q_u64(e0, e1)
}
}
#[inline(always)]
unsafe fn xor3(a: uint64x2_t, b: uint64x2_t, c: uint64x2_t) -> uint64x2_t {
#[cfg(target_feature = "sha3")]
{
unsafe { veor3q_u64(a, b, c) }
}
#[cfg(not(target_feature = "sha3"))]
{
unsafe { veorq_u64(a, veorq_u64(b, c)) }
}
}