#[cfg(not(target_feature = "avx512f"))]
use core::arch::x86_64::_mm256_shuffle_epi32;
#[cfg(target_feature = "avx512vl")]
use core::arch::x86_64::_mm256_ternarylogic_epi64;
use core::arch::x86_64::{
__m256i, _mm_set_epi64x, _mm256_blend_epi32, _mm256_broadcastsi128_si256,
_mm256_clmulepi64_epi128, _mm256_loadu_si256, _mm256_permute2x128_si256,
_mm256_permute4x64_epi64, _mm256_setzero_si256, _mm256_shuffle_epi8, _mm256_slli_epi64,
_mm256_srli_epi64, _mm256_storeu_si256, _mm256_unpackhi_epi64, _mm256_unpacklo_epi64,
_mm256_xor_si256,
};
use super::triples::Plan;
pub(crate) type Reg = __m256i;
pub(crate) const WIDTH: usize = 2;
#[inline(always)]
pub(crate) fn zero() -> Reg {
unsafe { _mm256_setzero_si256() }
}
#[inline(always)]
pub(crate) fn xor(a: Reg, b: Reg) -> Reg {
unsafe { _mm256_xor_si256(a, b) }
}
#[inline(always)]
pub(crate) fn xor3(a: Reg, b: Reg, c: Reg) -> Reg {
#[cfg(target_feature = "avx512vl")]
{
unsafe { _mm256_ternarylogic_epi64::<{ super::XOR3 }>(a, b, c) }
}
#[cfg(not(target_feature = "avx512vl"))]
{
xor(xor(a, b), c)
}
}
#[inline(always)]
pub(crate) fn unpack_low_64(a: Reg, b: Reg) -> Reg {
unsafe { _mm256_unpacklo_epi64(a, b) }
}
#[inline(always)]
pub(crate) fn unpack_high_64(a: Reg, b: Reg) -> Reg {
unsafe { _mm256_unpackhi_epi64(a, b) }
}
#[inline(always)]
pub(crate) fn shl_64<const N: i32>(a: Reg) -> Reg {
unsafe { _mm256_slli_epi64::<N>(a) }
}
#[inline(always)]
pub(crate) fn shr_64<const N: i32>(a: Reg) -> Reg {
unsafe { _mm256_srli_epi64::<N>(a) }
}
#[inline(always)]
pub(crate) fn shuffle_bytes(table: Reg, indices: Reg) -> Reg {
unsafe { _mm256_shuffle_epi8(table, indices) }
}
#[inline(always)]
pub(crate) fn clmul<const IMM: i32>(a: Reg, b: Reg) -> Reg {
unsafe { _mm256_clmulepi64_epi128::<IMM>(a, b) }
}
#[cfg(not(target_feature = "avx512f"))]
#[inline(always)]
pub(crate) fn swap_halves(a: Reg) -> Reg {
unsafe { _mm256_shuffle_epi32::<{ super::SWAP_QUADWORDS }>(a) }
}
#[inline(always)]
pub(crate) fn broadcast(value: u128) -> Reg {
unsafe {
let lane = _mm_set_epi64x((value >> 64) as i64, value as i64);
_mm256_broadcastsi128_si256(lane)
}
}
#[inline(always)]
pub(crate) unsafe fn load(from: *const u128) -> Reg {
unsafe { _mm256_loadu_si256(from.cast()) }
}
#[inline(always)]
pub(crate) unsafe fn store(to: *mut u128, value: Reg) {
unsafe { _mm256_storeu_si256(to.cast(), value) }
}
#[cfg(not(target_feature = "avx512f"))]
#[inline(always)]
pub(crate) fn interleave(a: Reg, b: Reg, block_len: usize) -> (Reg, Reg) {
match block_len {
1 => interleave_u128(a, b),
WIDTH => (a, b),
_ => panic!("unsupported block_len"),
}
}
#[inline(always)]
pub(crate) fn interleave_64(a: Reg, b: Reg, block_len: usize) -> (Reg, Reg) {
match block_len {
1 => interleave_u64(a, b),
2 => interleave_u128(a, b),
4 => (a, b),
_ => panic!("unsupported block_len"),
}
}
const PLAN: Plan<4> = Plan::NEW;
const fn blend_imm(mask: u32) -> i32 {
let mut imm = 0;
let mut p = 0;
while p < 4 {
if mask >> p & 1 == 1 {
imm |= 0b11 << (2 * p);
}
p += 1;
}
imm
}
const fn permute_imm(index: [usize; 4]) -> i32 {
let mut imm = 0;
let mut k = 0;
while k < 4 {
imm |= (index[k] as i32) << (2 * k);
k += 1;
}
imm
}
#[inline(always)]
fn blend_permute<const SECOND: i32, const THIRD: i32, const PERMUTE: i32>(
a: Reg,
b: Reg,
c: Reg,
) -> Reg {
unsafe {
let blended = _mm256_blend_epi32::<THIRD>(_mm256_blend_epi32::<SECOND>(a, b), c);
_mm256_permute4x64_epi64::<PERMUTE>(blended)
}
}
#[inline(always)]
fn permute_blend<
const P0: i32,
const P1: i32,
const P2: i32,
const SECOND: i32,
const THIRD: i32,
>(
[a, b, c]: [Reg; 3],
) -> Reg {
unsafe {
let (a, b, c) = (
_mm256_permute4x64_epi64::<P0>(a),
_mm256_permute4x64_epi64::<P1>(b),
_mm256_permute4x64_epi64::<P2>(c),
);
_mm256_blend_epi32::<THIRD>(_mm256_blend_epi32::<SECOND>(a, b), c)
}
}
macro_rules! gather {
($d:literal, $regs:expr) => {{
const SECOND: i32 = blend_imm(PLAN.gather_mask[$d][0]);
const THIRD: i32 = blend_imm(PLAN.gather_mask[$d][1]);
const PERMUTE: i32 = permute_imm(PLAN.gather_index[$d]);
let [a, b, c] = $regs;
blend_permute::<SECOND, THIRD, PERMUTE>(a, b, c)
}};
}
macro_rules! scatter {
($r:literal, $coordinates:expr) => {{
const P0: i32 = permute_imm(PLAN.scatter_index[0]);
const P1: i32 = permute_imm(PLAN.scatter_index[1]);
const P2: i32 = permute_imm(PLAN.scatter_index[2]);
const SECOND: i32 = blend_imm(PLAN.scatter_mask[$r][0]);
const THIRD: i32 = blend_imm(PLAN.scatter_mask[$r][1]);
permute_blend::<P0, P1, P2, SECOND, THIRD>($coordinates)
}};
}
#[inline(always)]
pub(crate) fn deinterleave_3(registers: [Reg; 3]) -> [Reg; 3] {
[
gather!(0, registers),
gather!(1, registers),
gather!(2, registers),
]
}
#[inline(always)]
pub(crate) fn interleave_3(coordinates: [Reg; 3]) -> [Reg; 3] {
[
scatter!(0, coordinates),
scatter!(1, coordinates),
scatter!(2, coordinates),
]
}
#[inline(always)]
fn interleave_u64(a: Reg, b: Reg) -> (Reg, Reg) {
(unpack_low_64(a, b), unpack_high_64(a, b))
}
#[inline(always)]
fn interleave_u128(a: Reg, b: Reg) -> (Reg, Reg) {
let crossed = unsafe { _mm256_permute2x128_si256::<0x21>(a, b) };
unsafe {
(
_mm256_blend_epi32::<0b1111_0000>(a, crossed),
_mm256_blend_epi32::<0b1111_0000>(crossed, b),
)
}
}