#![cfg(any(target_arch = "x86_64", target_arch = "x86"))]
mod gather;
#[cfg(test)]
mod tests;
use std::arch::x86_64::__m256i;
use std::arch::x86_64::_mm256_and_si256;
use std::arch::x86_64::_mm256_movemask_epi8;
use std::arch::x86_64::_mm256_set1_epi32;
use vortex_buffer::Alignment;
use vortex_buffer::Buffer;
use vortex_buffer::BufferMut;
use self::gather::Avx2Gather;
use self::gather::GatherFn;
use super::FixedWidthTakeValue;
use super::take_values_scalar;
use crate::dtype::PType;
use crate::dtype::UnsignedPType;
use crate::match_each_unsigned_integer_ptype;
#[target_feature(enable = "avx2")]
pub(super) unsafe fn take_avx2<V: FixedWidthTakeValue, I: UnsignedPType>(
buffer: &[V],
indices: &[I],
) -> Buffer<V> {
if buffer.is_empty() {
assert!(
indices.is_empty(),
"cannot take a non-empty set of indices from an empty buffer"
);
return Buffer::empty();
}
macro_rules! dispatch {
($lane:ty) => {{
match_each_unsigned_integer_ptype!(I::PTYPE, |Idx| {
let indices = unsafe { std::mem::transmute::<&[I], &[Idx]>(indices) };
exec_take::<V, $lane, Idx, Avx2Gather>(buffer, indices)
})
}};
}
match size_of::<V>() {
4 if I::PTYPE == PType::U32 && !i32_gather_can_address(buffer.len()) => {
take_values_scalar(buffer, indices)
}
4 => dispatch!(u32),
8 => dispatch!(u64),
_ => take_values_scalar(buffer, indices),
}
}
const fn i32_gather_can_address(values_len: usize) -> bool {
values_len <= i32::MAX as usize + 1
}
#[inline(always)]
fn exec_take<Out, Lane, Idx, Gather>(values: &[Out], indices: &[Idx]) -> Buffer<Out>
where
Out: FixedWidthTakeValue,
Idx: UnsignedPType,
Gather: GatherFn<Idx, Lane>,
{
assert_eq!(
size_of::<Out>(),
size_of::<Lane>(),
"gather lane and output element must have the same size"
);
let indices_len = indices.len();
let max_index = Idx::from(values.len());
let mut buffer =
BufferMut::<Out>::with_capacity_aligned(indices_len, Alignment::of::<__m256i>());
let buf_uninit = buffer.spare_capacity_mut();
let mut offset = 0;
let mut all_indices_valid = unsafe { _mm256_set1_epi32(-1) };
while offset + Gather::STRIDE < indices_len {
let valid_mask = unsafe {
Gather::gather(
indices.as_ptr().add(offset),
max_index,
values.as_ptr().cast::<Lane>(),
buf_uninit.as_mut_ptr().add(offset).cast::<Lane>(),
)
};
all_indices_valid = unsafe { _mm256_and_si256(all_indices_valid, valid_mask) };
offset += Gather::WIDTH;
}
assert!(
unsafe { _mm256_movemask_epi8(all_indices_valid) } == -1,
"take index out of bounds"
);
while offset < indices_len {
buf_uninit[offset].write(values[indices[offset].as_()]);
offset += 1;
}
assert_eq!(offset, indices_len);
unsafe { buffer.set_len(indices_len) };
buffer = buffer.aligned(Alignment::of::<Out>());
buffer.freeze()
}