#![allow(unsafe_code)]
pub(crate) const LANES: usize = 4;
#[cfg(target_arch = "x86_64")]
pub(crate) fn supported() -> bool {
std::is_x86_feature_detected!("avx2")
}
#[cfg(target_arch = "x86_64")]
pub(crate) fn compress4(states: &mut [[u64; 8]; LANES], blocks: &[[u8; 128]; LANES]) {
unsafe { avx2::compress4(states, blocks) }
}
#[cfg(target_arch = "x86_64")]
mod avx2 {
use crate::hash::sha512::K512;
use core::arch::x86_64::*;
#[inline(always)]
unsafe fn ror<const R: i32, const L: i32>(x: __m256i) -> __m256i {
unsafe { _mm256_or_si256(_mm256_srli_epi64::<R>(x), _mm256_slli_epi64::<L>(x)) }
}
#[inline(always)]
unsafe fn add(a: __m256i, b: __m256i) -> __m256i {
unsafe { _mm256_add_epi64(a, b) }
}
#[inline(always)]
unsafe fn xor(a: __m256i, b: __m256i) -> __m256i {
unsafe { _mm256_xor_si256(a, b) }
}
#[inline(always)]
unsafe fn transpose4(v: &mut [__m256i; 4]) {
unsafe {
let t0 = _mm256_unpacklo_epi64(v[0], v[1]); let t1 = _mm256_unpackhi_epi64(v[0], v[1]); let t2 = _mm256_unpacklo_epi64(v[2], v[3]); let t3 = _mm256_unpackhi_epi64(v[2], v[3]); v[0] = _mm256_permute2x128_si256::<0x20>(t0, t2); v[1] = _mm256_permute2x128_si256::<0x20>(t1, t3); v[2] = _mm256_permute2x128_si256::<0x31>(t0, t2); v[3] = _mm256_permute2x128_si256::<0x31>(t1, t3); }
}
#[target_feature(enable = "avx2")]
pub(crate) unsafe fn compress4(states: &mut [[u64; 8]; 4], blocks: &[[u8; 128]; 4]) {
unsafe {
let mut lo = [_mm256_setzero_si256(); 4];
let mut hi = [_mm256_setzero_si256(); 4];
for lane in 0..4 {
let p = states[lane].as_ptr();
lo[lane] = _mm256_loadu_si256(p as *const __m256i);
hi[lane] = _mm256_loadu_si256(p.add(4) as *const __m256i);
}
transpose4(&mut lo);
transpose4(&mut hi);
let mut h = [_mm256_setzero_si256(); 8];
h[..4].copy_from_slice(&lo);
h[4..].copy_from_slice(&hi);
let h0 = h;
let bswap = _mm256_set_epi8(
8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, );
let mut w = [_mm256_setzero_si256(); 80];
for q in 0..4 {
let mut quad = [_mm256_setzero_si256(); 4];
for (lane, row) in quad.iter_mut().enumerate() {
let p = blocks[lane].as_ptr().add(32 * q);
*row = _mm256_shuffle_epi8(_mm256_loadu_si256(p as *const __m256i), bswap);
}
transpose4(&mut quad);
w[4 * q..4 * q + 4].copy_from_slice(&quad);
}
for t in 16..80 {
let s0 = xor(
xor(ror::<1, 63>(w[t - 15]), ror::<8, 56>(w[t - 15])),
_mm256_srli_epi64::<7>(w[t - 15]),
);
let s1 = xor(
xor(ror::<19, 45>(w[t - 2]), ror::<61, 3>(w[t - 2])),
_mm256_srli_epi64::<6>(w[t - 2]),
);
w[t] = add(add(w[t - 16], s0), add(w[t - 7], s1));
}
let [mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut hh] =
[h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7]];
for t in 0..80 {
let s1 = xor(xor(ror::<14, 50>(e), ror::<18, 46>(e)), ror::<41, 23>(e));
let ch = xor(_mm256_and_si256(e, f), _mm256_andnot_si256(e, g));
let kt = _mm256_set1_epi64x(K512[t] as i64);
let t1 = add(add(add(hh, s1), add(ch, kt)), w[t]);
let s0 = xor(xor(ror::<28, 36>(a), ror::<34, 30>(a)), ror::<39, 25>(a));
let maj = xor(
xor(_mm256_and_si256(a, b), _mm256_and_si256(a, c)),
_mm256_and_si256(b, c),
);
let t2 = add(s0, maj);
hh = g;
g = f;
f = e;
e = add(d, t1);
d = c;
c = b;
b = a;
a = add(t1, t2);
}
let mut out_lo = [add(a, h0[0]), add(b, h0[1]), add(c, h0[2]), add(d, h0[3])];
let mut out_hi = [add(e, h0[4]), add(f, h0[5]), add(g, h0[6]), add(hh, h0[7])];
transpose4(&mut out_lo);
transpose4(&mut out_hi);
for lane in 0..4 {
let p = states[lane].as_mut_ptr();
_mm256_storeu_si256(p as *mut __m256i, out_lo[lane]);
_mm256_storeu_si256(p.add(4) as *mut __m256i, out_hi[lane]);
}
}
}
}
#[cfg(all(test, target_arch = "x86_64"))]
mod tests {
use super::*;
use crate::hash::sha512::compress512_soft as soft;
#[test]
fn compress4_matches_scalar() {
if !supported() {
return;
}
let mut s = 0x9e37_79b9_7f4a_7c15u64;
let mut next = || {
s ^= s << 13;
s ^= s >> 7;
s ^= s << 17;
s
};
for _ in 0..200 {
let mut states = [[0u64; 8]; LANES];
for lane in states.iter_mut() {
for w in lane.iter_mut() {
*w = next();
}
}
let mut want = states;
for _block in 0..3 {
let mut blocks = [[0u8; 128]; LANES];
for lane in blocks.iter_mut() {
for byte in lane.iter_mut() {
*byte = (next() >> 23) as u8;
}
}
for lane in 0..LANES {
soft(&mut want[lane], &blocks[lane]);
}
compress4(&mut states, &blocks);
}
assert_eq!(states, want);
}
}
}