use core::arch::asm;
use ic_core::Zeroize;
pub const LANES: usize = 16;
pub const STRIDE: usize = LANES * 64;
#[repr(C, align(64))]
pub struct Start([u32; 16]);
impl Start {
pub fn new(key: &[u8; 32], nonce: &[u8; 12], counter: u32) -> Start {
let mut s = Start([0u32; 16]);
s.0[..4].copy_from_slice(&super::SIGMA);
for i in 0..8 {
s.0[4 + i] =
u32::from_le_bytes([key[i * 4], key[i * 4 + 1], key[i * 4 + 2], key[i * 4 + 3]]);
}
for i in 0..3 {
s.0[13 + i] = u32::from_le_bytes([
nonce[i * 4],
nonce[i * 4 + 1],
nonce[i * 4 + 2],
nonce[i * 4 + 3],
]);
}
s.0[12] = counter;
s
}
pub fn set_counter(&mut self, counter: u32) {
self.0[12] = counter;
}
}
impl Drop for Start {
fn drop(&mut self) {
self.0.zeroize();
}
}
#[repr(C, align(64))]
struct LaneOffsets([u32; 16]);
static LANE_OFFSETS: LaneOffsets =
LaneOffsets([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
#[repr(C, align(64))]
struct Scratch([u8; STRIDE]);
macro_rules! qr4 {
($(($a:literal, $b:literal, $c:literal, $d:literal)),*) => {
concat!(
$("vpaddd zmm", $a, ", zmm", $a, ", zmm", $b, "\n",)*
$("vpxord zmm", $d, ", zmm", $d, ", zmm", $a, "\n",)*
$("vprold zmm", $d, ", zmm", $d, ", 16\n",)*
$("vpaddd zmm", $c, ", zmm", $c, ", zmm", $d, "\n",)*
$("vpxord zmm", $b, ", zmm", $b, ", zmm", $c, "\n",)*
$("vprold zmm", $b, ", zmm", $b, ", 12\n",)*
$("vpaddd zmm", $a, ", zmm", $a, ", zmm", $b, "\n",)*
$("vpxord zmm", $d, ", zmm", $d, ", zmm", $a, "\n",)*
$("vprold zmm", $d, ", zmm", $d, ", 8\n",)*
$("vpaddd zmm", $c, ", zmm", $c, ", zmm", $d, "\n",)*
$("vpxord zmm", $b, ", zmm", $b, ", zmm", $c, "\n",)*
$("vprold zmm", $b, ", zmm", $b, ", 7\n",)*
)
};
}
macro_rules! double_round {
() => {
concat!(
qr4!((0, 4, 8, 12), (1, 5, 9, 13), (2, 6, 10, 14), (3, 7, 11, 15)),
qr4!((0, 5, 10, 15), (1, 6, 11, 12), (2, 7, 8, 13), (3, 4, 9, 14)),
)
};
}
macro_rules! load_start {
($($w:literal),*) => {
concat!(
$("vpbroadcastd zmm", $w, ", dword ptr [{s} + ", $w, " * 4]\n",)*
"vpaddd zmm12, zmm12, zmmword ptr [{o}]\n",
)
};
}
macro_rules! feed_forward {
($($w:literal),*) => {
concat!(
"vpaddd zmm12, zmm12, zmmword ptr [{o}]\n",
$("vpaddd zmm", $w, ", zmm", $w, ", dword ptr [{s} + ", $w, " * 4] {{1to16}}\n",)*
$("vmovdqa64 zmmword ptr [{k} + ", $w, " * 64], zmm", $w, "\n",)*
)
};
}
macro_rules! transpose_words {
($g:literal) => {
concat!(
"vmovdqa64 zmm0, zmmword ptr [{k} + ",
$g,
" * 256]\n",
"vmovdqa64 zmm1, zmmword ptr [{k} + ",
$g,
" * 256 + 64]\n",
"vmovdqa64 zmm2, zmmword ptr [{k} + ",
$g,
" * 256 + 128]\n",
"vmovdqa64 zmm3, zmmword ptr [{k} + ",
$g,
" * 256 + 192]\n",
"vpunpckldq zmm4, zmm0, zmm1\n",
"vpunpckhdq zmm5, zmm0, zmm1\n",
"vpunpckldq zmm6, zmm2, zmm3\n",
"vpunpckhdq zmm7, zmm2, zmm3\n",
"vpunpcklqdq zmm0, zmm4, zmm6\n",
"vpunpckhqdq zmm1, zmm4, zmm6\n",
"vpunpcklqdq zmm2, zmm5, zmm7\n",
"vpunpckhqdq zmm3, zmm5, zmm7\n",
"vmovdqa64 zmmword ptr [{k} + ",
$g,
" * 256], zmm0\n",
"vmovdqa64 zmmword ptr [{k} + ",
$g,
" * 256 + 64], zmm1\n",
"vmovdqa64 zmmword ptr [{k} + ",
$g,
" * 256 + 128], zmm2\n",
"vmovdqa64 zmmword ptr [{k} + ",
$g,
" * 256 + 192], zmm3\n",
)
};
}
macro_rules! transpose_lanes_xor {
($e:literal) => {
concat!(
"vmovdqa64 zmm0, zmmword ptr [{k} + ",
$e,
" * 64]\n",
"vmovdqa64 zmm1, zmmword ptr [{k} + 256 + ",
$e,
" * 64]\n",
"vmovdqa64 zmm2, zmmword ptr [{k} + 512 + ",
$e,
" * 64]\n",
"vmovdqa64 zmm3, zmmword ptr [{k} + 768 + ",
$e,
" * 64]\n",
"vshufi32x4 zmm4, zmm0, zmm1, 0x44\n",
"vshufi32x4 zmm5, zmm0, zmm1, 0xee\n",
"vshufi32x4 zmm6, zmm2, zmm3, 0x44\n",
"vshufi32x4 zmm7, zmm2, zmm3, 0xee\n",
"vshufi32x4 zmm0, zmm4, zmm6, 0x88\n",
"vshufi32x4 zmm1, zmm4, zmm6, 0xdd\n",
"vshufi32x4 zmm2, zmm5, zmm7, 0x88\n",
"vshufi32x4 zmm3, zmm5, zmm7, 0xdd\n",
"vpxord zmm0, zmm0, zmmword ptr [{p} + ",
$e,
" * 64]\n",
"vpxord zmm1, zmm1, zmmword ptr [{p} + 256 + ",
$e,
" * 64]\n",
"vpxord zmm2, zmm2, zmmword ptr [{p} + 512 + ",
$e,
" * 64]\n",
"vpxord zmm3, zmm3, zmmword ptr [{p} + 768 + ",
$e,
" * 64]\n",
"vmovdqu64 zmmword ptr [{p} + ",
$e,
" * 64], zmm0\n",
"vmovdqu64 zmmword ptr [{p} + 256 + ",
$e,
" * 64], zmm1\n",
"vmovdqu64 zmmword ptr [{p} + 512 + ",
$e,
" * 64], zmm2\n",
"vmovdqu64 zmmword ptr [{p} + 768 + ",
$e,
" * 64], zmm3\n",
)
};
}
macro_rules! wipe_scratch {
($($w:literal),*) => {
concat!($("vmovdqa64 zmmword ptr [{k} + ", $w, " * 64], zmm0\n",)*)
};
}
#[inline(never)]
pub unsafe fn group(start: &Start, data: &mut [u8; STRIDE]) {
let mut scratch = core::mem::MaybeUninit::<Scratch>::uninit();
unsafe {
asm!(
load_start!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15),
double_round!(),
double_round!(),
double_round!(),
double_round!(),
double_round!(),
double_round!(),
double_round!(),
double_round!(),
double_round!(),
double_round!(),
feed_forward!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15),
transpose_words!(0),
transpose_words!(1),
transpose_words!(2),
transpose_words!(3),
transpose_lanes_xor!(0),
transpose_lanes_xor!(1),
transpose_lanes_xor!(2),
transpose_lanes_xor!(3),
"vzeroall",
wipe_scratch!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15),
s = in(reg) start.0.as_ptr(),
o = in(reg) LANE_OFFSETS.0.as_ptr(),
k = in(reg) scratch.as_mut_ptr() as *mut u8,
p = in(reg) data.as_mut_ptr(),
out("xmm0") _, out("xmm1") _, out("xmm2") _, out("xmm3") _,
out("xmm4") _, out("xmm5") _, out("xmm6") _, out("xmm7") _,
out("xmm8") _, out("xmm9") _, out("xmm10") _, out("xmm11") _,
out("xmm12") _, out("xmm13") _, out("xmm14") _, out("xmm15") _,
options(nostack, preserves_flags),
);
}
}