#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
compile_error!("the sha_ni backend needs an x86 or x86_64 target");
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
use crate::Schedule;
use crate::block::rounds;
use crate::mem::{load_u8x16, load_u32x4, store_u32x4};
use crate::ubc_check::RecompressFrom;
const REVERSE: i32 = 0b00_01_10_11;
#[inline]
#[target_feature(enable = "sse2")]
fn spill<const I: usize>(w: &mut Schedule, msg: __m128i) {
store_u32x4(w.window_mut::<I, 4>(), msg);
}
#[inline]
#[target_feature(enable = "sse2,ssse3")]
fn msg<const I: usize>(block: &[u8; 64], swap: __m128i) -> __m128i {
const { assert!(I + 16 <= 64, "the load runs past the block") }
let raw = load_u8x16(block[I..I + 16].try_into().unwrap());
_mm_shuffle_epi8(raw, swap)
}
#[inline]
#[target_feature(enable = "sse2,ssse3")]
fn words<const T: usize>(m1: &Schedule, dm: &[u32; 80]) -> __m128i {
const { assert!(T + 4 <= 80, "the group runs past the schedule") }
let spilled = load_u32x4(m1.window::<T, 4>());
let diff = load_u32x4(dm[T..T + 4].try_into().unwrap());
_mm_xor_si128(spilled, _mm_shuffle_epi32(diff, REVERSE))
}
#[inline]
#[target_feature(enable = "sse2")]
fn load_abcd(state: &[u32; 5]) -> __m128i {
_mm_shuffle_epi32(load_u32x4(state.first_chunk().unwrap()), REVERSE)
}
#[inline]
#[target_feature(enable = "sse2")]
fn store_abcd(state: &mut [u32; 5], abcd: __m128i) {
store_u32x4(
state.first_chunk_mut().unwrap(),
_mm_shuffle_epi32(abcd, REVERSE),
);
}
#[inline]
#[target_feature(enable = "sha,sse2,ssse3,sse4.1")]
pub(crate) fn compress_spill(
state: &mut [u32; 5],
block: &[u8; 64],
w: &mut Schedule,
at_60: &mut [u32; 5],
at_64: &mut [u32; 5],
) {
macro_rules! group {
(
$w:expr, $t:literal, $k:expr, $abcd:ident, $live:ident, $held:ident,
$ready:ident, $next:ident, $second:ident, $first:ident
) => {{
spill::<$t>($w, $ready);
$live = _mm_sha1nexte_epu32($live, $ready);
$held = $abcd;
$next = _mm_sha1msg2_epu32($next, $ready);
$abcd = _mm_sha1rnds4_epu32($abcd, $live, $k);
$first = _mm_sha1msg1_epu32($first, $ready);
$second = _mm_xor_si128($second, $ready);
}};
(
$w:expr, $t:literal, $k:expr, $abcd:ident, $live:ident, $held:ident,
$ready:ident, $next:ident, $second:ident, $first:ident => $at:ident
) => {{
spill::<$t>($w, $ready);
$live = _mm_sha1nexte_epu32($live, $ready);
store_u32x4((&mut $at[1..5]).try_into().unwrap(), $live);
store_u32x4($at.first_chunk_mut().unwrap(), $abcd);
$held = $abcd;
$next = _mm_sha1msg2_epu32($next, $ready);
$abcd = _mm_sha1rnds4_epu32($abcd, $live, $k);
$first = _mm_sha1msg1_epu32($first, $ready);
$second = _mm_xor_si128($second, $ready);
}};
}
let swap = _mm_set_epi64x(0x0001_0203_0405_0607, 0x0809_0A0B_0C0D_0E0F);
let mut abcd = load_abcd(state);
let abcd_in = abcd;
let e_in = _mm_set_epi32(state[4] as i32, 0, 0, 0);
let mut msg0 = msg::<0>(block, swap);
let mut msg1 = msg::<16>(block, swap);
let mut msg2 = msg::<32>(block, swap);
let mut msg3 = msg::<48>(block, swap);
spill::<0>(w, msg0);
let mut e0 = _mm_add_epi32(e_in, msg0);
let mut e1 = abcd;
abcd = _mm_sha1rnds4_epu32(abcd, e0, 0);
spill::<4>(w, msg1);
e1 = _mm_sha1nexte_epu32(e1, msg1);
e0 = abcd;
abcd = _mm_sha1rnds4_epu32(abcd, e1, 0);
msg0 = _mm_sha1msg1_epu32(msg0, msg1);
spill::<8>(w, msg2);
e0 = _mm_sha1nexte_epu32(e0, msg2);
e1 = abcd;
abcd = _mm_sha1rnds4_epu32(abcd, e0, 0);
msg1 = _mm_sha1msg1_epu32(msg1, msg2);
msg0 = _mm_xor_si128(msg0, msg2);
spill::<12>(w, msg3);
e1 = _mm_sha1nexte_epu32(e1, msg3);
e0 = abcd;
msg0 = _mm_sha1msg2_epu32(msg0, msg3);
abcd = _mm_sha1rnds4_epu32(abcd, e1, 0);
msg2 = _mm_sha1msg1_epu32(msg2, msg3);
msg1 = _mm_xor_si128(msg1, msg3);
group!(w, 16, 0, abcd, e0, e1, msg0, msg1, msg2, msg3);
group!(w, 20, 1, abcd, e1, e0, msg1, msg2, msg3, msg0);
group!(w, 24, 1, abcd, e0, e1, msg2, msg3, msg0, msg1);
group!(w, 28, 1, abcd, e1, e0, msg3, msg0, msg1, msg2);
group!(w, 32, 1, abcd, e0, e1, msg0, msg1, msg2, msg3);
group!(w, 36, 1, abcd, e1, e0, msg1, msg2, msg3, msg0);
group!(w, 40, 2, abcd, e0, e1, msg2, msg3, msg0, msg1);
group!(w, 44, 2, abcd, e1, e0, msg3, msg0, msg1, msg2);
group!(w, 48, 2, abcd, e0, e1, msg0, msg1, msg2, msg3);
group!(w, 52, 2, abcd, e1, e0, msg1, msg2, msg3, msg0);
group!(w, 56, 2, abcd, e0, e1, msg2, msg3, msg0, msg1);
group!(w, 60, 3, abcd, e1, e0, msg3, msg0, msg1, msg2 => at_60);
group!(w, 64, 3, abcd, e0, e1, msg0, msg1, msg2, msg3 => at_64);
spill::<68>(w, msg1);
e1 = _mm_sha1nexte_epu32(e1, msg1);
e0 = abcd;
msg2 = _mm_sha1msg2_epu32(msg2, msg1);
abcd = _mm_sha1rnds4_epu32(abcd, e1, 3);
msg3 = _mm_xor_si128(msg3, msg1);
spill::<72>(w, msg2);
e0 = _mm_sha1nexte_epu32(e0, msg2);
e1 = abcd;
msg3 = _mm_sha1msg2_epu32(msg3, msg2);
abcd = _mm_sha1rnds4_epu32(abcd, e0, 3);
spill::<76>(w, msg3);
e1 = _mm_sha1nexte_epu32(e1, msg3);
e0 = abcd;
abcd = _mm_sha1rnds4_epu32(abcd, e1, 3);
e0 = _mm_sha1nexte_epu32(e0, e_in);
abcd = _mm_add_epi32(abcd, abcd_in);
store_abcd(state, abcd);
state[4] = _mm_extract_epi32(e0, 3) as u32;
}
#[inline]
#[target_feature(enable = "sha,sse2,ssse3,sse4.1")]
pub(crate) fn recompress(
step: RecompressFrom,
m1: &Schedule,
dm: &[u32; 80],
state: &[u32; 5],
chaining_out: &[u32; 5],
) -> bool {
let target = rounds::partner_start(step, m1, dm, state, chaining_out);
let mut abcd = load_abcd(&target);
let abcd_in = abcd;
let e_in = _mm_set_epi32(target[4] as i32, 0, 0, 0);
let mut e0 = _mm_add_epi32(e_in, words::<0>(m1, dm));
let mut e1 = abcd;
abcd = _mm_sha1rnds4_epu32(abcd, e0, 0);
macro_rules! group {
($g:expr, $k:expr, $live:ident, $held:ident) => {{
let ready = words::<{ 4 * $g }>(m1, dm);
$live = _mm_sha1nexte_epu32($live, ready);
$held = abcd;
abcd = _mm_sha1rnds4_epu32(abcd, $live, $k);
}};
}
group!(1, 0, e1, e0);
group!(2, 0, e0, e1);
group!(3, 0, e1, e0);
group!(4, 0, e0, e1);
group!(5, 1, e1, e0);
group!(6, 1, e0, e1);
group!(7, 1, e1, e0);
group!(8, 1, e0, e1);
group!(9, 1, e1, e0);
group!(10, 2, e0, e1);
group!(11, 2, e1, e0);
group!(12, 2, e0, e1);
group!(13, 2, e1, e0);
group!(14, 2, e0, e1);
group!(15, 3, e1, e0);
group!(16, 3, e0, e1);
group!(17, 3, e1, e0);
group!(18, 3, e0, e1);
group!(19, 3, e1, e0);
e0 = _mm_sha1nexte_epu32(e0, e_in);
abcd = _mm_add_epi32(abcd, abcd_in);
let mut ends_on = [0u32; 5];
store_abcd(&mut ends_on, abcd);
ends_on[4] = _mm_extract_epi32(e0, 3) as u32;
crate::block::xor(&ends_on, chaining_out) == 0
}