use core::arch::x86_64::{
__m512i,
_mm512_and_si512,
_mm512_cmpeq_epi64_mask,
_mm512_rolv_epi64,
_mm512_set_epi64,
_mm512_set1_epi64,
_mm512_setzero_si512,
_mm512_storeu_si512,
_mm512_ternarylogic_epi64,
_mm512_xor_si512,
};
use core::fmt;
use core::ops::{
BitAnd,
BitAndAssign,
BitXor,
BitXorAssign,
Not,
};
use crate::{
LaneSize,
PI,
PLEN,
RC,
RHO,
};
macro_rules! u5 {
($var:ident, $body:block) => {{
{
const $var: usize = 0;
$body
}
{
const $var: usize = 1;
$body
}
{
const $var: usize = 2;
$body
}
{
const $var: usize = 3;
$body
}
{
const $var: usize = 4;
$body
}
}};
}
macro_rules! u24 {
($var:ident, $body:block) => {{
{
const $var: usize = 0;
$body
}
{
const $var: usize = 1;
$body
}
{
const $var: usize = 2;
$body
}
{
const $var: usize = 3;
$body
}
{
const $var: usize = 4;
$body
}
{
const $var: usize = 5;
$body
}
{
const $var: usize = 6;
$body
}
{
const $var: usize = 7;
$body
}
{
const $var: usize = 8;
$body
}
{
const $var: usize = 9;
$body
}
{
const $var: usize = 10;
$body
}
{
const $var: usize = 11;
$body
}
{
const $var: usize = 12;
$body
}
{
const $var: usize = 13;
$body
}
{
const $var: usize = 14;
$body
}
{
const $var: usize = 15;
$body
}
{
const $var: usize = 16;
$body
}
{
const $var: usize = 17;
$body
}
{
const $var: usize = 18;
$body
}
{
const $var: usize = 19;
$body
}
{
const $var: usize = 20;
$body
}
{
const $var: usize = 21;
$body
}
{
const $var: usize = 22;
$body
}
{
const $var: usize = 23;
$body
}
}};
}
#[derive(Copy, Clone)]
#[repr(transparent)]
pub(crate) struct U64x8(__m512i);
impl U64x8 {
#[inline(always)]
fn to_array(self) -> [u64; 8] {
let mut out = [0u64; 8];
unsafe {
_mm512_storeu_si512(out.as_mut_ptr().cast::<__m512i>(), self.0);
}
out
}
#[inline(always)]
fn chi(a: Self, b: Self, c: Self) -> Self {
Self(unsafe { _mm512_ternarylogic_epi64::<0xD2>(a.0, b.0, c.0) })
}
}
impl Default for U64x8 {
#[inline(always)]
fn default() -> Self {
Self(unsafe { _mm512_setzero_si512() })
}
}
impl PartialEq for U64x8 {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
unsafe { _mm512_cmpeq_epi64_mask(self.0, other.0) == 0xFF }
}
}
impl fmt::Debug for U64x8 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let a = self.to_array();
write!(f, "U64x8({a:#018x?})")
}
}
impl BitAnd for U64x8 {
type Output = Self;
#[inline(always)]
fn bitand(self, rhs: Self) -> Self {
Self(unsafe { _mm512_and_si512(self.0, rhs.0) })
}
}
impl BitAndAssign for U64x8 {
#[inline(always)]
fn bitand_assign(&mut self, rhs: Self) {
*self = *self & rhs;
}
}
impl BitXor for U64x8 {
type Output = Self;
#[inline(always)]
fn bitxor(self, rhs: Self) -> Self {
Self(unsafe { _mm512_xor_si512(self.0, rhs.0) })
}
}
impl BitXorAssign for U64x8 {
#[inline(always)]
fn bitxor_assign(&mut self, rhs: Self) {
*self = *self ^ rhs;
}
}
impl Not for U64x8 {
type Output = Self;
#[inline(always)]
fn not(self) -> Self {
Self(unsafe { _mm512_xor_si512(self.0, _mm512_set1_epi64(-1)) })
}
}
impl LaneSize for U64x8 {
const KECCAK_F_ROUND_COUNT: usize = 24;
#[inline(always)]
fn truncate_rc(rc: u64) -> Self {
Self(unsafe { _mm512_set1_epi64(rc as i64) })
}
#[inline(always)]
fn rotate_left(self, n: u32) -> Self {
unsafe { Self(_mm512_rolv_epi64(self.0, _mm512_set1_epi64(i64::from(n)))) }
}
}
#[target_feature(enable = "avx512f")]
fn keccak_p_u64x8(state: &mut [U64x8; PLEN], round_count: usize) {
let round_consts = &RC[(24 - round_count)..24];
for &rc in round_consts {
let mut array = [U64x8::default(); 5];
u5!(x, {
u5!(y, {
array[x] ^= state[5 * y + x];
});
});
u5!(x, {
let t1 = array[(x + 4) % 5];
let t2 = array[(x + 1) % 5].rotate_left(1);
u5!(y, {
state[5 * y + x] ^= t1 ^ t2;
});
});
let mut last = state[1];
u24!(x, {
array[0] = state[PI[x]];
state[PI[x]] = last.rotate_left(RHO[x]);
last = array[0];
});
u5!(y_step, {
let y = 5 * y_step;
array.copy_from_slice(&state[y..][..5]);
u5!(x, {
state[y + x] = U64x8::chi(array[x], array[(x + 1) % 5], array[(x + 2) % 5]);
});
});
state[0] ^= U64x8::truncate_rc(rc);
}
}
#[target_feature(enable = "avx512f")]
pub(crate) unsafe fn p1600x8_avx512(states: &mut [[u64; PLEN]; 8], round_count: usize) {
debug_assert!(round_count <= 24);
let mut lanes = [U64x8::default(); PLEN];
for (i, lane) in lanes.iter_mut().enumerate() {
*lane = U64x8(_mm512_set_epi64(
states[7][i] as i64,
states[6][i] as i64,
states[5][i] as i64,
states[4][i] as i64,
states[3][i] as i64,
states[2][i] as i64,
states[1][i] as i64,
states[0][i] as i64,
));
}
keccak_p_u64x8(&mut lanes, round_count);
for (i, lane) in lanes.iter().enumerate() {
let a = lane.to_array();
for (s, &v) in states.iter_mut().zip(a.iter()) {
s[i] = v;
}
}
}