use crate::kernel::scalar;
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn xor_avx2(_token: archmage::X64V3Token, dst: &mut [u8], src: &[u8]) {
assert_eq!(dst.len(), src.len());
let (dst_tiles, dst_tail) = dst.as_chunks_mut::<128>();
let (src_tiles, src_tail) = src.as_chunks::<128>();
for (dst_tile, src_tile) in dst_tiles.iter_mut().zip(src_tiles) {
let (dst_lanes, _) = dst_tile.as_chunks_mut::<32>();
let (src_lanes, _) = src_tile.as_chunks::<32>();
for (dst_lane, src_lane) in dst_lanes.iter_mut().zip(src_lanes) {
let d = _mm256_loadu_si256(&*dst_lane);
let s = _mm256_loadu_si256(src_lane);
_mm256_storeu_si256(dst_lane, _mm256_xor_si256(d, s));
}
}
let (dst_lanes, dst_tail) = dst_tail.as_chunks_mut::<32>();
let (src_lanes, src_tail) = src_tail.as_chunks::<32>();
for (dst_lane, src_lane) in dst_lanes.iter_mut().zip(src_lanes) {
let d = _mm256_loadu_si256(&*dst_lane);
let s = _mm256_loadu_si256(src_lane);
_mm256_storeu_si256(dst_lane, _mm256_xor_si256(d, s));
}
let (dst_lanes, dst_tail) = dst_tail.as_chunks_mut::<16>();
let (src_lanes, src_tail) = src_tail.as_chunks::<16>();
for (dst_lane, src_lane) in dst_lanes.iter_mut().zip(src_lanes) {
let d = _mm_loadu_si128(&*dst_lane);
let s = _mm_loadu_si128(src_lane);
_mm_storeu_si128(dst_lane, _mm_xor_si128(d, s));
}
scalar::xor(dst_tail, src_tail);
}
#[archmage::arcane(import_intrinsics)]
pub fn xor_gather_avx2(
token: archmage::X64V3Token,
region: &[u8],
dst: &mut [u8],
offsets: &[u32],
) {
let whole = dst.len() & !31;
let tail = dst.len() - whole;
if whole == 0 || whole > 8 * 32 || tail != 0 {
for &start in offsets {
let start = start as usize;
assert!(
start + dst.len() <= region.len(),
"xor_gather_avx2: source out of region bounds",
);
xor_avx2(token, dst, ®ion[start..start + dst.len()]);
}
return;
}
for (index, &start) in offsets.iter().enumerate() {
let start = start as usize;
assert!(
start + dst.len() <= region.len(),
"xor_gather_avx2: offset {index} ({start}) + {} exceeds region of {} bytes",
dst.len(),
region.len(),
);
}
let (dst_lanes, _) = dst.as_chunks_mut::<32>();
for (lane, dst_lane) in dst_lanes.iter_mut().enumerate() {
let mut acc = _mm256_loadu_si256(&*dst_lane);
for &start in offsets {
let base = start as usize + lane * 32;
let src_lane = region[base..base + 32].first_chunk().unwrap();
acc = _mm256_xor_si256(acc, _mm256_loadu_si256(src_lane));
}
_mm256_storeu_si256(dst_lane, acc);
}
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn xor_sse2(_token: archmage::X64V1Token, dst: &mut [u8], src: &[u8]) {
assert_eq!(dst.len(), src.len());
let (dst_tiles, dst_tail) = dst.as_chunks_mut::<64>();
let (src_tiles, src_tail) = src.as_chunks::<64>();
for (dst_tile, src_tile) in dst_tiles.iter_mut().zip(src_tiles) {
let (dst_lanes, _) = dst_tile.as_chunks_mut::<16>();
let (src_lanes, _) = src_tile.as_chunks::<16>();
for (dst_lane, src_lane) in dst_lanes.iter_mut().zip(src_lanes) {
let d = _mm_loadu_si128(&*dst_lane);
let s = _mm_loadu_si128(src_lane);
_mm_storeu_si128(dst_lane, _mm_xor_si128(d, s));
}
}
let (dst_lanes, dst_tail) = dst_tail.as_chunks_mut::<16>();
let (src_lanes, src_tail) = src_tail.as_chunks::<16>();
for (dst_lane, src_lane) in dst_lanes.iter_mut().zip(src_lanes) {
let d = _mm_loadu_si128(&*dst_lane);
let s = _mm_loadu_si128(src_lane);
_mm_storeu_si128(dst_lane, _mm_xor_si128(d, s));
}
scalar::xor(dst_tail, src_tail);
}
const AVX2_ROW_TILE: usize = 4 * 32;
const SSE2_ROW_TILE: usize = 4 * 16;
#[archmage::arcane]
pub fn xor_rows_avx2(token: archmage::X64V3Token, dst: &mut [u8], src: &[u8], row_len: usize) {
assert_eq!(dst.len(), src.len());
assert!(dst.len().is_multiple_of(row_len));
debug_assert_ne!(row_len, 0);
let rows = dst.len() / row_len;
let four_span = rows / 4 * 4 * row_len;
let two_span = rows % 4 / 2 * 2 * row_len;
xor_streams_avx2::<4>(&mut dst[..four_span], &src[..four_span], row_len);
xor_streams_avx2::<2>(
&mut dst[four_span..four_span + two_span],
&src[four_span..four_span + two_span],
row_len,
);
if rows % 2 == 1 {
let odd = four_span + two_span;
xor_avx2(token, &mut dst[odd..], &src[odd..]);
}
}
#[archmage::rite(v3, import_intrinsics)]
fn xor_streams_avx2<const W: usize>(dst: &mut [u8], src: &[u8], row_len: usize) {
debug_assert_eq!(dst.len(), src.len());
debug_assert!(row_len != 0 && dst.len().is_multiple_of(W * row_len));
let group_span = W * row_len;
for (rows_dst, rows_src) in dst
.chunks_exact_mut(group_span)
.zip(src.chunks_exact(group_span))
{
let mut offset = 0;
while offset + AVX2_ROW_TILE <= row_len {
for r in 0..W {
let base = r * row_len + offset;
let (d_lanes, _) = rows_dst[base..base + AVX2_ROW_TILE].as_chunks_mut::<32>();
let (s_lanes, _) = rows_src[base..base + AVX2_ROW_TILE].as_chunks::<32>();
for (d_lane, s_lane) in d_lanes.iter_mut().zip(s_lanes) {
let d = _mm256_loadu_si256(&*d_lane);
let s = _mm256_loadu_si256(s_lane);
_mm256_storeu_si256(d_lane, _mm256_xor_si256(d, s));
}
}
offset += AVX2_ROW_TILE;
}
while offset + 32 <= row_len {
for r in 0..W {
let base = r * row_len + offset;
let d_lane = rows_dst[base..base + 32].first_chunk_mut().unwrap();
let s_lane = rows_src[base..base + 32].first_chunk().unwrap();
let d = _mm256_loadu_si256(&*d_lane);
let s = _mm256_loadu_si256(s_lane);
_mm256_storeu_si256(d_lane, _mm256_xor_si256(d, s));
}
offset += 32;
}
if offset < row_len {
for r in 0..W {
let row = r * row_len;
let (d_row, s_row) = (
&mut rows_dst[row..row + row_len],
&rows_src[row..row + row_len],
);
scalar::xor(&mut d_row[offset..], &s_row[offset..]);
}
}
}
}
#[archmage::arcane]
pub fn xor_rows_sse2(token: archmage::X64V1Token, dst: &mut [u8], src: &[u8], row_len: usize) {
assert_eq!(dst.len(), src.len());
assert!(dst.len().is_multiple_of(row_len));
debug_assert_ne!(row_len, 0);
let rows = dst.len() / row_len;
let four_span = rows / 4 * 4 * row_len;
let two_span = rows % 4 / 2 * 2 * row_len;
xor_streams_sse2::<4>(&mut dst[..four_span], &src[..four_span], row_len);
xor_streams_sse2::<2>(
&mut dst[four_span..four_span + two_span],
&src[four_span..four_span + two_span],
row_len,
);
if rows % 2 == 1 {
let odd = four_span + two_span;
xor_sse2(token, &mut dst[odd..], &src[odd..]);
}
}
#[archmage::rite(v1, import_intrinsics)]
fn xor_streams_sse2<const W: usize>(dst: &mut [u8], src: &[u8], row_len: usize) {
debug_assert_eq!(dst.len(), src.len());
debug_assert!(row_len != 0 && dst.len().is_multiple_of(W * row_len));
let group_span = W * row_len;
for (rows_dst, rows_src) in dst
.chunks_exact_mut(group_span)
.zip(src.chunks_exact(group_span))
{
let mut offset = 0;
while offset + SSE2_ROW_TILE <= row_len {
for r in 0..W {
let base = r * row_len + offset;
let (d_lanes, _) = rows_dst[base..base + SSE2_ROW_TILE].as_chunks_mut::<16>();
let (s_lanes, _) = rows_src[base..base + SSE2_ROW_TILE].as_chunks::<16>();
for (d_lane, s_lane) in d_lanes.iter_mut().zip(s_lanes) {
let d = _mm_loadu_si128(&*d_lane);
let s = _mm_loadu_si128(s_lane);
_mm_storeu_si128(d_lane, _mm_xor_si128(d, s));
}
}
offset += SSE2_ROW_TILE;
}
while offset + 16 <= row_len {
for r in 0..W {
let base = r * row_len + offset;
let d_lane = rows_dst[base..base + 16].first_chunk_mut().unwrap();
let s_lane = rows_src[base..base + 16].first_chunk().unwrap();
let d = _mm_loadu_si128(&*d_lane);
let s = _mm_loadu_si128(s_lane);
_mm_storeu_si128(d_lane, _mm_xor_si128(d, s));
}
offset += 16;
}
if offset < row_len {
for r in 0..W {
let row = r * row_len;
let (d_row, s_row) = (
&mut rows_dst[row..row + row_len],
&rows_src[row..row + row_len],
);
scalar::xor(&mut d_row[offset..], &s_row[offset..]);
}
}
}
}