use crate::kernel::gf1::LaneTest;
#[allow(
clippy::cast_lossless,
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::used_underscore_binding
)]
#[archmage::arcane(import_intrinsics)]
pub fn expand_sse42(
_token: archmage::X64V2Token,
dst: &mut [u8],
one_w: u64,
src: &[u8],
bits: usize,
bytes: usize,
) {
assert_eq!(dst.len(), bits.saturating_mul(bytes));
assert!(matches!(bytes, 1 | 2 | 4 | 8));
assert!(crate::bits::bytes_for(bits) <= src.len());
let (dst_units, _) = dst.as_chunks_mut::<16>();
let lanes = match bytes {
1 => {
let sel = _mm_loadu_si128(&EXPAND_SEL_B1);
let one = _mm_set1_epi8(one_w as i8);
let zero = _mm_setzero_si128();
for (u, unit) in dst_units.iter_mut().enumerate() {
let mut rep = [0u8; 16];
rep[..8].fill(src[2 * u]);
rep[8..].fill(src[2 * u + 1]);
let masked = _mm_and_si128(_mm_loadu_si128(&rep), sel);
let is_zero = _mm_cmpeq_epi8(masked, zero);
_mm_storeu_si128(unit, _mm_andnot_si128(is_zero, one));
}
dst_units.len() * 16
}
2 => {
let sel = _mm_loadu_si128(&EXPAND_SEL_B2);
let one = _mm_set1_epi16(one_w as i16);
let zero = _mm_setzero_si128();
for (u, unit) in dst_units.iter_mut().enumerate() {
let mut rep = [0u8; 16];
rep.fill(src[u]);
let masked = _mm_and_si128(_mm_loadu_si128(&rep), sel);
let is_zero = _mm_cmpeq_epi16(masked, zero);
_mm_storeu_si128(unit, _mm_andnot_si128(is_zero, one));
}
dst_units.len() * 8
}
4 => {
let sel_even = _mm_loadu_si128(&EXPAND_SEL_B4_LO);
let sel_odd = _mm_loadu_si128(&EXPAND_SEL_B4_HI);
let one = _mm_set1_epi32(one_w as i32);
let zero = _mm_setzero_si128();
for (u, unit) in dst_units.iter_mut().enumerate() {
let mut rep = [0u8; 16];
rep.fill(src[(4 * u) / 8]);
let sel = if u % 2 == 0 { sel_even } else { sel_odd };
let masked = _mm_and_si128(_mm_loadu_si128(&rep), sel);
let is_zero = _mm_cmpeq_epi32(masked, zero);
_mm_storeu_si128(unit, _mm_andnot_si128(is_zero, one));
}
dst_units.len() * 4
}
_ => {
let sels = [
_mm_loadu_si128(&EXPAND_SEL_B8_0),
_mm_loadu_si128(&EXPAND_SEL_B8_2),
_mm_loadu_si128(&EXPAND_SEL_B8_4),
_mm_loadu_si128(&EXPAND_SEL_B8_6),
];
let one = _mm_set1_epi64x(one_w as i64);
let zero = _mm_setzero_si128();
for (u, unit) in dst_units.iter_mut().enumerate() {
let mut rep = [0u8; 16];
rep.fill(src[(2 * u) / 8]);
let masked = _mm_and_si128(_mm_loadu_si128(&rep), sels[u % 4]);
let is_zero = _mm_cmpeq_epi64(masked, zero);
_mm_storeu_si128(unit, _mm_andnot_si128(is_zero, one));
}
dst_units.len() * 2
}
};
for i in lanes..bits {
let bit = (src[i / 8] >> (i % 8)) & 1;
let lane = one_w & 0u64.wrapping_sub(u64::from(bit));
let lane_bytes = lane.to_le_bytes();
dst[i * bytes..(i + 1) * bytes].copy_from_slice(&lane_bytes[..bytes]);
}
}
const EXPAND_SEL_B1: [u8; 16] = [
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
];
const EXPAND_SEL_B2: [u8; 16] = [
0x01, 0x01, 0x02, 0x02, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0x40, 0x40, 0x80, 0x80,
];
const EXPAND_SEL_B4_LO: [u8; 16] = [
0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08,
];
const EXPAND_SEL_B4_HI: [u8; 16] = [
0x10, 0x10, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x40, 0x40, 0x40, 0x40, 0x80, 0x80, 0x80, 0x80,
];
const EXPAND_SEL_B8_0: [u8; 16] = [
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
];
const EXPAND_SEL_B8_2: [u8; 16] = [
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
];
const EXPAND_SEL_B8_4: [u8; 16] = [
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
];
const EXPAND_SEL_B8_6: [u8; 16] = [
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
];
const REV_DWORD: [u8; 16] = [3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12];
const REV_QWORD: [u8; 16] = [7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8];
#[allow(clippy::cast_lossless, clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn compress_sse42(
_token: archmage::X64V2Token,
dst: &mut [u8],
src: &[u8],
bits: usize,
bytes: usize,
test: &LaneTest,
) {
assert_eq!(src.len(), bits.saturating_mul(bytes));
assert_eq!(dst.len(), crate::bits::bytes_for(bits));
assert!(matches!(bytes, 1 | 2 | 4 | 8));
dst.fill(0);
match *test {
LaneTest::NonZero => compress_eq_inner(dst, src, bits, bytes, 0, false),
LaneTest::ZeroOrOne(one) => compress_eq_inner(dst, src, bits, bytes, one, true),
LaneTest::Parity(tau) => compress_parity_sse42(dst, src, bits, bytes, tau),
}
}
#[allow(
clippy::cast_lossless,
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::cast_sign_loss
)]
#[archmage::rite(v2, import_intrinsics)]
fn compress_eq_inner(
dst: &mut [u8],
src: &[u8],
bits: usize,
bytes: usize,
one: u64,
or_zero: bool,
) {
let zero = _mm_setzero_si128();
let one_b = _mm_set1_epi8(one as i8);
let width_mask: u64 = match bytes {
1 => 0xff,
2 => 0xffff,
4 => 0xffff_ffff,
_ => u64::MAX,
};
let one_in_range = one & !width_mask == 0;
let one_w16 = _mm_set1_epi16(one as i16);
let one_w32 = _mm_set1_epi32(one as i32);
let one_w64 = _mm_set1_epi64x(one as i64);
let (src_units, _) = src.as_chunks::<16>();
let mut lane = 0usize;
for unit in src_units {
let lanes_here = 16 / bytes;
if lane + lanes_here > bits {
break;
}
let v = _mm_loadu_si128(unit);
let is_zero = match bytes {
1 => _mm_cmpeq_epi8(v, zero),
2 => _mm_cmpeq_epi16(v, zero),
4 => _mm_cmpeq_epi32(v, zero),
_ => _mm_cmpeq_epi64(v, zero),
};
let is_one = if one_in_range {
match bytes {
1 => _mm_cmpeq_epi8(v, one_b),
2 => _mm_cmpeq_epi16(v, one_w16),
4 => _mm_cmpeq_epi32(v, one_w32),
_ => _mm_cmpeq_epi64(v, one_w64),
}
} else {
zero
};
let pass = if or_zero {
_mm_or_si128(is_zero, is_one)
} else {
_mm_xor_si128(is_zero, _mm_set1_epi8(-1))
};
let lane_bits = match bytes {
1 => _mm_movemask_epi8(pass) as u32,
2 => _mm_movemask_epi8(_mm_packs_epi16(pass, zero)) as u32 & 0xff,
4 => _mm_movemask_ps(_mm_castsi128_ps(pass)) as u32 & 0x0f,
_ => _mm_movemask_pd(_mm_castsi128_pd(pass)) as u32 & 0x03,
};
for j in 0..lanes_here {
if lane_bits >> j & 1 == 1 {
dst[(lane + j) / 8] |= 1 << ((lane + j) % 8);
}
}
lane += lanes_here;
}
for i in lane..bits {
let mut word = 0u64;
for (shift, &byte) in src[i * bytes..(i + 1) * bytes].iter().enumerate() {
word |= u64::from(byte) << (8 * shift);
}
let ok = if or_zero {
word == 0 || word == one
} else {
word != 0
};
if ok {
dst[i / 8] |= 1 << (i % 8);
}
}
}
#[allow(
clippy::cast_lossless,
clippy::cast_possible_truncation,
clippy::cast_possible_wrap
)]
#[archmage::rite(v2, import_intrinsics)]
fn compress_parity_sse42(dst: &mut [u8], src: &[u8], bits: usize, bytes: usize, tau: u64) {
const PARITY_LUT: [u8; 16] = [0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0];
let lut = _mm_loadu_si128(&PARITY_LUT);
let tau_b = _mm_set1_epi8(tau as i8);
let tau_w16 = _mm_set1_epi16(tau as i16);
let tau_w32 = _mm_set1_epi32(tau as i32);
let tau_w64 = _mm_set1_epi64x(tau as i64);
let mask_lo = _mm_set1_epi8(0x0f);
let (src_units, _) = src.as_chunks::<16>();
let mut lane = 0usize;
for unit in src_units {
let lanes_here = 16 / bytes;
if lane + lanes_here > bits {
break;
}
let v = _mm_loadu_si128(unit);
let masked = match bytes {
1 => _mm_and_si128(v, tau_b),
2 => _mm_and_si128(v, tau_w16),
4 => _mm_and_si128(v, tau_w32),
_ => _mm_and_si128(v, tau_w64),
};
let folded = match bytes {
1 => masked,
2 => _mm_xor_si128(masked, _mm_srli_epi16::<8>(masked)),
4 => {
let rev = _mm_shuffle_epi8(masked, _mm_loadu_si128(&REV_DWORD));
let pairs = _mm_xor_si128(masked, rev);
_mm_xor_si128(pairs, _mm_srli_epi16::<8>(pairs))
}
_ => {
let rev_q = _mm_shuffle_epi8(masked, _mm_loadu_si128(&REV_QWORD));
let t1 = _mm_xor_si128(masked, rev_q);
let rev_d = _mm_shuffle_epi8(t1, _mm_loadu_si128(&REV_DWORD));
let t2 = _mm_xor_si128(t1, rev_d);
_mm_xor_si128(t2, _mm_srli_epi16::<8>(t2))
}
};
let lo_nib = _mm_and_si128(folded, mask_lo);
let hi_nib = _mm_and_si128(_mm_srli_epi16::<4>(folded), mask_lo);
let p = _mm_xor_si128(_mm_shuffle_epi8(lut, lo_nib), _mm_shuffle_epi8(lut, hi_nib));
let mut tmp = [0u8; 16];
_mm_storeu_si128(&mut tmp, p);
for j in 0..lanes_here {
if tmp[j * bytes] & 1 == 1 {
dst[(lane + j) / 8] |= 1 << ((lane + j) % 8);
}
}
lane += lanes_here;
}
for i in lane..bits {
let mut word = 0u64;
for (shift, &byte) in src[i * bytes..(i + 1) * bytes].iter().enumerate() {
word |= u64::from(byte) << (8 * shift);
}
if (word & tau).count_ones() & 1 == 1 {
dst[i / 8] |= 1 << (i % 8);
}
}
}
#[allow(clippy::cast_lossless, clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn restrict_sse42(
_token: archmage::X64V2Token,
dst: &mut [u8],
valid: &mut [u8],
one_w: u64,
src: &[u8],
bits: usize,
bytes: usize,
) {
assert_eq!(src.len(), bits.saturating_mul(bytes));
assert_eq!(dst.len(), crate::bits::bytes_for(bits));
assert_eq!(valid.len(), crate::bits::bytes_for(bits));
assert!(matches!(bytes, 1 | 2 | 4 | 8));
dst.fill(0);
valid.fill(0);
restrict_inner(dst, valid, one_w, src, bits, bytes);
}
#[allow(
clippy::cast_lossless,
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::cast_sign_loss
)]
#[archmage::rite(v2, import_intrinsics)]
fn restrict_inner(
dst: &mut [u8],
valid: &mut [u8],
one_w: u64,
src: &[u8],
bits: usize,
bytes: usize,
) {
let zero = _mm_setzero_si128();
let one_b = _mm_set1_epi8(one_w as i8);
let width_mask: u64 = match bytes {
1 => 0xff,
2 => 0xffff,
4 => 0xffff_ffff,
_ => u64::MAX,
};
let one_in_range = one_w & !width_mask == 0;
let one_w16 = _mm_set1_epi16(one_w as i16);
let one_w32 = _mm_set1_epi32(one_w as i32);
let one_w64 = _mm_set1_epi64x(one_w as i64);
let (src_units, _) = src.as_chunks::<16>();
let mut lane = 0usize;
for unit in src_units {
let lanes_here = 16 / bytes;
if lane + lanes_here > bits {
break;
}
let v = _mm_loadu_si128(unit);
let is_zero = match bytes {
1 => _mm_cmpeq_epi8(v, zero),
2 => _mm_cmpeq_epi16(v, zero),
4 => _mm_cmpeq_epi32(v, zero),
_ => _mm_cmpeq_epi64(v, zero),
};
let is_one = if one_in_range {
match bytes {
1 => _mm_cmpeq_epi8(v, one_b),
2 => _mm_cmpeq_epi16(v, one_w16),
4 => _mm_cmpeq_epi32(v, one_w32),
_ => _mm_cmpeq_epi64(v, one_w64),
}
} else {
zero
};
let valid_v = _mm_or_si128(is_zero, is_one);
let valid_bits = match bytes {
1 => _mm_movemask_epi8(valid_v) as u32,
2 => _mm_movemask_epi8(_mm_packs_epi16(valid_v, zero)) as u32 & 0xff,
4 => _mm_movemask_ps(_mm_castsi128_ps(valid_v)) as u32 & 0x0f,
_ => _mm_movemask_pd(_mm_castsi128_pd(valid_v)) as u32 & 0x03,
};
let one_bits = match bytes {
1 => _mm_movemask_epi8(is_one) as u32,
2 => _mm_movemask_epi8(_mm_packs_epi16(is_one, zero)) as u32 & 0xff,
4 => _mm_movemask_ps(_mm_castsi128_ps(is_one)) as u32 & 0x0f,
_ => _mm_movemask_pd(_mm_castsi128_pd(is_one)) as u32 & 0x03,
};
for j in 0..lanes_here {
if valid_bits >> j & 1 == 1 {
valid[(lane + j) / 8] |= 1 << ((lane + j) % 8);
}
if one_bits >> j & 1 == 1 {
dst[(lane + j) / 8] |= 1 << ((lane + j) % 8);
}
}
lane += lanes_here;
}
for i in lane..bits {
let mut word = 0u64;
for (shift, &byte) in src[i * bytes..(i + 1) * bytes].iter().enumerate() {
word |= u64::from(byte) << (8 * shift);
}
if word == 0 || word == one_w {
valid[i / 8] |= 1 << (i % 8);
}
if word == one_w {
dst[i / 8] |= 1 << (i % 8);
}
}
}