use core::arch::x86_64::{
__m256i,
_mm_cvtsi32_si128,
_mm256_and_si256,
_mm256_cmpeq_epi64,
_mm256_movemask_epi8,
_mm256_or_si256,
_mm256_set_epi64x,
_mm256_set1_epi64x,
_mm256_setzero_si256,
_mm256_sll_epi64,
_mm256_srl_epi64,
_mm256_storeu_si256,
_mm256_xor_si256,
};
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 U64x4(__m256i);
impl U64x4 {
#[inline(always)]
fn to_array(self) -> [u64; 4] {
let mut out = [0u64; 4];
unsafe {
_mm256_storeu_si256(out.as_mut_ptr().cast::<__m256i>(), self.0);
}
out
}
}
impl Default for U64x4 {
#[inline(always)]
fn default() -> Self {
Self(unsafe { _mm256_setzero_si256() })
}
}
impl PartialEq for U64x4 {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
unsafe {
let eq = _mm256_cmpeq_epi64(self.0, other.0);
_mm256_movemask_epi8(eq) == -1i32
}
}
}
impl fmt::Debug for U64x4 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let a = self.to_array();
write!(
f,
"U64x4({:#018x}, {:#018x}, {:#018x}, {:#018x})",
a[0], a[1], a[2], a[3]
)
}
}
impl BitAnd for U64x4 {
type Output = Self;
#[inline(always)]
fn bitand(self, rhs: Self) -> Self {
Self(unsafe { _mm256_and_si256(self.0, rhs.0) })
}
}
impl BitAndAssign for U64x4 {
#[inline(always)]
fn bitand_assign(&mut self, rhs: Self) {
*self = *self & rhs;
}
}
impl BitXor for U64x4 {
type Output = Self;
#[inline(always)]
fn bitxor(self, rhs: Self) -> Self {
Self(unsafe { _mm256_xor_si256(self.0, rhs.0) })
}
}
impl BitXorAssign for U64x4 {
#[inline(always)]
fn bitxor_assign(&mut self, rhs: Self) {
*self = *self ^ rhs;
}
}
impl Not for U64x4 {
type Output = Self;
#[inline(always)]
fn not(self) -> Self {
Self(unsafe { _mm256_xor_si256(self.0, _mm256_set1_epi64x(-1)) })
}
}
impl LaneSize for U64x4 {
const KECCAK_F_ROUND_COUNT: usize = 24;
#[inline(always)]
fn truncate_rc(rc: u64) -> Self {
Self(unsafe { _mm256_set1_epi64x(rc as i64) })
}
#[inline(always)]
fn rotate_left(self, n: u32) -> Self {
debug_assert!((1..=63).contains(&n));
unsafe {
let l = _mm256_sll_epi64(self.0, _mm_cvtsi32_si128(n as i32));
let r = _mm256_srl_epi64(self.0, _mm_cvtsi32_si128((64 - n) as i32));
Self(_mm256_or_si256(l, r))
}
}
}
#[target_feature(enable = "avx2")]
fn keccak_p_u64x4(state: &mut [U64x4; PLEN], round_count: usize) {
let round_consts = &RC[(24 - round_count)..24];
for &rc in round_consts {
let mut array = [U64x4::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, {
let t1 = !array[(x + 1) % 5];
let t2 = array[(x + 2) % 5];
state[y + x] = array[x] ^ (t1 & t2);
});
});
state[0] ^= U64x4::truncate_rc(rc);
}
}
#[target_feature(enable = "avx2")]
pub(crate) unsafe fn p1600x4_avx2(states: &mut [[u64; PLEN]; 4], round_count: usize) {
debug_assert!(round_count <= 24);
let mut lanes = [U64x4::default(); PLEN];
for (i, lane) in lanes.iter_mut().enumerate() {
*lane = U64x4(_mm256_set_epi64x(
states[3][i] as i64,
states[2][i] as i64,
states[1][i] as i64,
states[0][i] as i64,
));
}
keccak_p_u64x4(&mut lanes, round_count);
for (i, lane) in lanes.iter().enumerate() {
let a = lane.to_array();
states[0][i] = a[0];
states[1][i] = a[1];
states[2][i] = a[2];
states[3][i] = a[3];
}
}