use std::arch::x86_64::{
__m128i, _mm_and_si128, _mm_cmpeq_epi8, _mm_cvtsi128_si32, _mm_loadu_si128, _mm_max_epu8,
_mm_movemask_epi8, _mm_set1_epi8, _mm_setzero_si128, _mm_shuffle_epi8, _mm_srli_epi16,
};
use super::STEP;
use crate::lattice::Quotient;
use crate::shuffle::{self, CHUNK, IDENTITY, STRIDE, WAYS};
unsafe fn low(v: __m128i) -> u8 {
unsafe { _mm_cvtsi128_si32(v) as u32 as u8 }
}
#[target_feature(enable = "ssse3")]
pub(crate) unsafe fn sweep_shuffle(q: &Quotient, hay: &[u8]) -> bool {
#[inline(always)]
unsafe fn sweep(q: &Quotient, block: &[u8], stride: usize, mut live: __m128i) -> (__m128i, u8) {
unsafe {
let identity = _mm_loadu_si128(IDENTITY.as_ptr().cast::<__m128i>());
let mut compose = [identity; WAYS];
let mut high = [identity; WAYS];
for step in 0..stride {
for (way, (f, h)) in compose.iter_mut().zip(&mut high).enumerate() {
let byte = block[way * stride + step];
let row = _mm_loadu_si128(q.rows[usize::from(byte)].as_ptr().cast::<__m128i>());
*f = _mm_shuffle_epi8(row, *f);
*h = _mm_max_epu8(*h, *f);
}
}
let mut seen = live;
for (f, h) in compose.into_iter().zip(high) {
seen = _mm_max_epu8(seen, _mm_shuffle_epi8(h, live));
live = _mm_shuffle_epi8(f, live);
}
(live, low(seen))
}
}
unsafe {
let mut live = _mm_set1_epi8(q.start as i8);
let mut rest = hay;
while rest.len() >= CHUNK {
let (chunk, after) = rest.split_at(CHUNK);
let (next, seen) = sweep(q, chunk, STRIDE, live);
if seen >= q.threshold {
return false;
}
(live, rest) = (next, after);
}
let (paved, trailing) = rest.split_at(rest.len() / WAYS * WAYS);
let (live, seen) = sweep(q, paved, paved.len() / WAYS, live);
if seen >= q.threshold {
return false;
}
shuffle::walk(q, trailing, low(live)).is_some()
}
}
#[target_feature(enable = "ssse3")]
pub(crate) unsafe fn classify(lo: &[u8; 16], hi: &[u8; 16], hay: &[u8]) -> Option<usize> {
unsafe {
let load = |p: *const u8| _mm_loadu_si128(p.cast::<__m128i>());
let (lo_tbl, hi_tbl) = (load(lo.as_ptr()), load(hi.as_ptr()));
let nibble = _mm_set1_epi8(0x0F);
for (i, block) in hay.chunks_exact(STEP).enumerate() {
let v = load(block.as_ptr());
let picked = _mm_shuffle_epi8(lo_tbl, _mm_and_si128(v, nibble));
let high = _mm_and_si128(_mm_srli_epi16::<4>(v), nibble);
let select = _mm_shuffle_epi8(hi_tbl, high);
let zero = _mm_cmpeq_epi8(_mm_and_si128(picked, select), _mm_setzero_si128());
let miss = _mm_movemask_epi8(zero) as u32;
if miss != 0xFFFF {
return Some(i * STEP + (!miss & 0xFFFF).trailing_zeros() as usize);
}
}
crate::skip::wide::tail(lo, hi, hay)
}
}