use core::arch::x86_64::*;
use super::cpu::HaveAvx2;
pub(crate) fn bignum_aff_point_select_p384(z: &mut [u64; 12], table: &[u64], index: u8) {
match HaveAvx2::check() {
Some(token) => select_avx2::<12, 3>(z, table, index, token),
None => unsafe { select_sse2::<12, 6>(z, table, index) },
}
}
pub(crate) fn bignum_aff_point_select_p521(z: &mut [u64; 18], table: &[u64], index: u8) {
match HaveAvx2::check() {
Some(token) => select_avx2::<18, 4>(z, table, index, token),
None => unsafe { select_sse2::<18, 9>(z, table, index) },
}
}
fn select_avx2<const N: usize, const V: usize>(
z: &mut [u64; N],
table: &[u64],
index: u8,
_token: HaveAvx2,
) {
const { assert!(N == 4 * V || N == 4 * V + 2) };
unsafe { _select_avx2::<N, V>(z, table, index) }
}
#[target_feature(enable = "avx,avx2")]
fn _select_avx2<const N: usize, const V: usize>(z: &mut [u64; N], table: &[u64], index: u8) {
super::cpu::prefetch(table, N);
let mut acc = [_mm256_setzero_si256(); V];
let mut tail = _mm_setzero_si128();
let desired_index = _mm256_set1_epi32(index as i32);
let ones = _mm256_set1_epi32(1);
let mut index = ones;
let (rows, _) = table.as_chunks::<N>();
for row in rows {
let mask = _mm256_cmpeq_epi32(index, desired_index);
index = _mm256_add_epi32(index, ones);
for (i, acc) in acc.iter_mut().enumerate() {
let word = unsafe { _mm256_loadu_si256(row.as_ptr().add(4 * i).cast()) };
*acc = _mm256_xor_si256(*acc, _mm256_and_si256(word, mask));
}
if N > 4 * V {
let word = unsafe { _mm_loadu_si128(row.as_ptr().add(4 * V).cast()) };
tail = _mm_xor_si128(tail, _mm_and_si128(word, _mm256_castsi256_si128(mask)));
}
}
for (i, acc) in acc.iter().enumerate() {
unsafe { _mm256_storeu_si256(z.as_mut_ptr().add(4 * i).cast(), *acc) };
}
if N > 4 * V {
unsafe { _mm_storeu_si128(z.as_mut_ptr().add(4 * V).cast(), tail) };
}
}
#[target_feature(enable = "sse2")]
fn select_sse2<const N: usize, const V: usize>(z: &mut [u64; N], table: &[u64], index: u8) {
const { assert!(N == 2 * V) };
super::cpu::prefetch(table, N);
let mut acc = [_mm_setzero_si128(); V];
let desired_index = _mm_set1_epi32(index as i32);
let ones = _mm_set1_epi32(1);
let mut index = ones;
let (rows, _) = table.as_chunks::<N>();
for row in rows {
let mask = _mm_cmpeq_epi32(index, desired_index);
index = _mm_add_epi32(index, ones);
for (i, acc) in acc.iter_mut().enumerate() {
let word = unsafe { _mm_loadu_si128(row.as_ptr().add(2 * i).cast()) };
*acc = _mm_xor_si128(*acc, _mm_and_si128(word, mask));
}
}
for (i, acc) in acc.iter().enumerate() {
unsafe { _mm_storeu_si128(z.as_mut_ptr().add(2 * i).cast(), *acc) };
}
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
#[test]
fn sse2_matches_avx2() {
let Some(token) = HaveAvx2::check() else {
return;
};
let table: Vec<u64> = (0..18 * 16u64)
.map(|i| i.wrapping_mul(0x9e3779b97f4a7c15))
.collect();
for index in 0..=17 {
let (mut want, mut got) = ([1; 12], [2; 12]);
select_avx2::<12, 3>(&mut want, &table[..12 * 16], index, token);
unsafe { select_sse2::<12, 6>(&mut got, &table[..12 * 16], index) };
assert_eq!(got, want, "p384, index {index}");
let (mut want, mut got) = ([1; 18], [2; 18]);
select_avx2::<18, 4>(&mut want, &table, index, token);
unsafe { select_sse2::<18, 9>(&mut got, &table, index) };
assert_eq!(got, want, "p521, index {index}");
}
}
}