use crate::align::Alignment;
use crate::arch::SimdArch;
use crate::execution::{ExecutionMode, Unmasked};
use crate::iter;
use crate::kernel::SimdKernel;
use crate::scalar::Scalar;
use core::marker::PhantomData;
pub mod gather;
pub mod masked;
pub mod ops;
pub mod ops_mut;
pub mod reduce;
pub mod scan;
pub mod select;
pub mod tile;
pub mod unary;
pub use tile::{TileMatrixMultiply, TileView};
pub mod mask_reg;
pub mod vector_ops;
pub mod vector_reg;
pub use mask_reg::Mask;
pub use vector_reg::Vector;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SimdError {
LengthMismatch,
InsufficientInputLength,
InsufficientOutputLength,
UnalignedAddress,
IndexOutOfBounds,
UnsupportedTarget,
}
impl core::fmt::Display for SimdError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::LengthMismatch => write!(f, "Operand views have mismatched lengths"),
Self::InsufficientInputLength => write!(f, "Input slice has insufficient length"),
Self::InsufficientOutputLength => write!(f, "Output slice has insufficient length"),
Self::UnalignedAddress => {
write!(f, "Memory address does not satisfy alignment constraints")
}
Self::IndexOutOfBounds => write!(f, "Index is out of bounds of the view"),
Self::UnsupportedTarget => {
write!(f, "SIMD target is not supported or enabled on this host")
}
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for SimdError {}
#[repr(transparent)]
pub struct SimdView<
'a,
T: 'a,
Arch: SimdArch,
Align: Alignment,
Mode: ExecutionMode = Unmasked,
Ref: 'a = &'a [T],
> {
ptr: *mut [T],
_marker: PhantomData<(&'a T, Arch, Align, Mode, Ref)>,
}
unsafe impl<'a, T: 'a, Arch: SimdArch, Align: Alignment, Mode: ExecutionMode, Ref: 'a> Send
for SimdView<'a, T, Arch, Align, Mode, Ref>
where
Ref: Send,
{
}
unsafe impl<'a, T: 'a, Arch: SimdArch, Align: Alignment, Mode: ExecutionMode, Ref: 'a> Sync
for SimdView<'a, T, Arch, Align, Mode, Ref>
where
Ref: Sync,
{
}
impl<'a, T: 'a, Arch: SimdArch, Align: Alignment, Mode: ExecutionMode> Clone
for SimdView<'a, T, Arch, Align, Mode, &'a [T]>
{
#[inline(always)]
fn clone(&self) -> Self {
*self
}
}
impl<'a, T: 'a, Arch: SimdArch, Align: Alignment, Mode: ExecutionMode> Copy
for SimdView<'a, T, Arch, Align, Mode, &'a [T]>
{
}
impl<'a, T: 'a, Arch: SimdArch, Align: Alignment, Mode: ExecutionMode>
SimdView<'a, T, Arch, Align, Mode, &'a [T]>
{
#[inline]
pub fn new(data: &'a [T]) -> Option<Self> {
if !Arch::is_runtime_supported() {
return None;
}
if Align::IS_ALIGNED {
let req_align = Arch::REGISTER_WIDTH_BITS as usize / 8;
if req_align > 0 && Align::ALIGN_BYTES < req_align {
return None;
}
let addr = data.as_ptr() as usize;
if addr % Align::ALIGN_BYTES != 0 {
return None;
}
}
Some(Self {
ptr: data as *const [T] as *mut [T],
_marker: PhantomData,
})
}
}
impl<'a, T: 'a, Arch: SimdArch, Align: Alignment, Mode: ExecutionMode>
SimdView<'a, T, Arch, Align, Mode, &'a mut [T]>
{
#[inline]
pub fn new_mut(data: &'a mut [T]) -> Option<Self> {
if !Arch::is_runtime_supported() {
return None;
}
if Align::IS_ALIGNED {
let req_align = Arch::REGISTER_WIDTH_BITS as usize / 8;
if req_align > 0 && Align::ALIGN_BYTES < req_align {
return None;
}
let addr = data.as_ptr() as usize;
if addr % Align::ALIGN_BYTES != 0 {
return None;
}
}
Some(Self {
ptr: data as *mut [T],
_marker: PhantomData,
})
}
#[inline(always)]
pub fn as_slice_mut(&mut self) -> &mut [T] {
unsafe { &mut *self.ptr }
}
#[inline(always)]
pub fn downgrade(self) -> SimdView<'a, T, Arch, Align, Mode, &'a [T]> {
SimdView {
ptr: self.ptr,
_marker: PhantomData,
}
}
}
impl<'a, T: 'a, Arch: SimdArch, Align: Alignment, Mode: ExecutionMode, Ref: 'a>
SimdView<'a, T, Arch, Align, Mode, Ref>
{
#[inline(always)]
pub fn as_slice(&self) -> &[T] {
unsafe { &*self.ptr }
}
#[inline(always)]
pub fn len(&self) -> usize {
self.as_slice().len()
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline(always)]
pub fn into_unaligned(self) -> SimdView<'a, T, Arch, crate::align::Unaligned, Mode, Ref> {
SimdView {
ptr: self.ptr,
_marker: PhantomData,
}
}
#[inline]
pub fn try_into_aligned<const A: usize>(
self,
) -> Option<SimdView<'a, T, Arch, crate::align::Aligned<A>, Mode, Ref>> {
let req_align = Arch::REGISTER_WIDTH_BITS as usize / 8;
if req_align > 0 && A < req_align {
return None;
}
let addr = self.as_slice().as_ptr() as usize;
if addr % A == 0 {
Some(SimdView {
ptr: self.ptr,
_marker: PhantomData,
})
} else {
None
}
}
}
impl<'a, T: 'a, Arch: SimdArch, Align: Alignment, Mode: ExecutionMode>
SimdView<'a, T, Arch, Align, Mode, &'a [T]>
{
#[inline]
pub fn slice_unaligned(
self,
range: core::ops::Range<usize>,
) -> SimdView<'a, T, Arch, crate::align::Unaligned, Mode, &'a [T]> {
let sub = &self.as_slice()[range];
SimdView {
ptr: sub as *const [T] as *mut [T],
_marker: PhantomData,
}
}
#[inline]
pub fn slice_aligned<const A: usize>(
self,
range: core::ops::Range<usize>,
) -> Option<SimdView<'a, T, Arch, crate::align::Aligned<A>, Mode, &'a [T]>> {
let req_align = Arch::REGISTER_WIDTH_BITS as usize / 8;
if req_align > 0 && A < req_align {
return None;
}
let sub = &self.as_slice()[range];
let addr = sub.as_ptr() as usize;
if addr % A == 0 {
Some(SimdView {
ptr: sub as *const [T] as *mut [T],
_marker: PhantomData,
})
} else {
None
}
}
}
impl<'a, T: 'a, Arch: SimdArch, Align: Alignment, Mode: ExecutionMode>
SimdView<'a, T, Arch, Align, Mode, &'a mut [T]>
{
#[inline]
pub fn slice_unaligned_mut(
mut self,
range: core::ops::Range<usize>,
) -> SimdView<'a, T, Arch, crate::align::Unaligned, Mode, &'a mut [T]> {
let sub = &mut self.as_slice_mut()[range];
SimdView {
ptr: sub as *mut [T],
_marker: PhantomData,
}
}
#[inline]
pub fn slice_aligned_mut<const A: usize>(
mut self,
range: core::ops::Range<usize>,
) -> Option<SimdView<'a, T, Arch, crate::align::Aligned<A>, Mode, &'a mut [T]>> {
let req_align = Arch::REGISTER_WIDTH_BITS as usize / 8;
if req_align > 0 && A < req_align {
return None;
}
let sub = &mut self.as_slice_mut()[range];
let addr = sub.as_ptr() as usize;
if addr % A == 0 {
Some(SimdView {
ptr: sub as *mut [T],
_marker: PhantomData,
})
} else {
None
}
}
}
impl<
'a,
T: Scalar + 'a,
Arch: SimdArch + SimdKernel<T>,
Align: Alignment,
Mode: ExecutionMode,
Ref: 'a,
> SimdView<'a, T, Arch, Align, Mode, Ref>
{
#[inline(always)]
pub fn simd_chunks(&self) -> iter::SimdChunks<'a, T, Arch, Align, Mode> {
unsafe {
iter::SimdChunks::from_raw_parts(self.as_slice().as_ptr(), self.len(), Arch::LANE_COUNT)
}
}
#[inline(always)]
pub fn zip_chunks<'b>(
&self,
other: &'b SimdView<'b, T, Arch, Align, Mode, &'b [T]>,
) -> iter::ZipChunks<'a, 'b, T, Arch, Align, Mode> {
unsafe {
iter::ZipChunks::from_raw_parts(
self.as_slice().as_ptr(),
self.len(),
other.as_slice().as_ptr(),
other.len(),
)
}
}
}
impl<'a, T: Scalar + 'a, Arch: SimdArch + SimdKernel<T>, Align: Alignment, Mode: ExecutionMode>
SimdView<'a, T, Arch, Align, Mode, &'a mut [T]>
{
#[inline(always)]
pub fn simd_chunks_mut(self) -> iter::SimdChunksMut<'a, T, Arch, Align, Mode> {
unsafe {
iter::SimdChunksMut::from_raw_parts(self.ptr as *mut T, self.len(), Arch::LANE_COUNT)
}
}
#[inline(always)]
pub fn zip_chunks_mut<'b>(
self,
other: &'b SimdView<'b, T, Arch, Align, Mode, &'b [T]>,
) -> iter::ZipChunksMut<'a, 'b, T, Arch, Align, Mode> {
unsafe {
iter::ZipChunksMut::from_raw_parts(
self.ptr as *mut T,
self.len(),
other.as_slice().as_ptr(),
other.len(),
)
}
}
}
impl<'a, T: 'a, Arch: SimdArch, Align: Alignment, Mode: ExecutionMode, Ref: 'a> core::ops::Deref
for SimdView<'a, T, Arch, Align, Mode, Ref>
{
type Target = [T];
#[inline(always)]
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
impl<'a, T: 'a, Arch: SimdArch, Align: Alignment, Mode: ExecutionMode> core::ops::DerefMut
for SimdView<'a, T, Arch, Align, Mode, &'a mut [T]>
{
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_slice_mut()
}
}
impl<'a, T: 'a, Arch: SimdArch, Align: Alignment, Mode: ExecutionMode>
SimdView<'a, T, Arch, Align, Mode, &'a [T]>
where
T: bytemuck::Pod,
{
#[inline]
pub fn cast<U: bytemuck::Pod>(self) -> Option<SimdView<'a, U, Arch, Align, Mode, &'a [U]>> {
let casted = bytemuck::try_cast_slice(unsafe { &*self.ptr }).ok()?;
SimdView::new(casted)
}
}
impl<'a, T: 'a, Arch: SimdArch, Align: Alignment, Mode: ExecutionMode>
SimdView<'a, T, Arch, Align, Mode, &'a mut [T]>
where
T: bytemuck::Pod,
{
#[inline]
pub fn cast_mut<U: bytemuck::Pod>(
self,
) -> Option<SimdView<'a, U, Arch, Align, Mode, &'a mut [U]>> {
let casted = bytemuck::try_cast_slice_mut(unsafe { &mut *self.ptr }).ok()?;
SimdView::new_mut(casted)
}
}
#[inline(never)]
pub(crate) fn check_lengths_equal(len1: usize, len2: usize) -> Result<(), SimdError> {
if len1 != len2 {
return Err(SimdError::LengthMismatch);
}
Ok(())
}
#[inline(never)]
pub(crate) fn check_output_length(input_len: usize, output_len: usize) -> Result<(), SimdError> {
if output_len < input_len {
return Err(SimdError::InsufficientOutputLength);
}
Ok(())
}