Change to various generic impls
This commit is contained in:
parent
054f25f2b0
commit
dc4dc99649
8 changed files with 370 additions and 400 deletions
|
|
@ -1,88 +1,36 @@
|
|||
macro_rules! debug_wrapper {
|
||||
{ $($trait:ident => $name:ident,)* } => {
|
||||
macro_rules! impl_fmt_trait {
|
||||
{ $($trait:ident,)* } => {
|
||||
$(
|
||||
pub(crate) fn $name<T: core::fmt::$trait>(slice: &[T], f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
#[repr(transparent)]
|
||||
struct Wrapper<'a, T: core::fmt::$trait>(&'a T);
|
||||
impl<Element, const LANES: usize> core::fmt::$trait for crate::Simd<Element, LANES>
|
||||
where
|
||||
crate::LaneCount<LANES>: crate::SupportedLaneCount,
|
||||
Element: crate::SimdElement + core::fmt::$trait,
|
||||
{
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
#[repr(transparent)]
|
||||
struct Wrapper<'a, T: core::fmt::$trait>(&'a T);
|
||||
|
||||
impl<T: core::fmt::$trait> core::fmt::Debug for Wrapper<'_, T> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
impl<T: core::fmt::$trait> core::fmt::Debug for Wrapper<'_, T> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
f.debug_list()
|
||||
.entries(slice.iter().map(|x| Wrapper(x)))
|
||||
.finish()
|
||||
f.debug_list()
|
||||
.entries(self.as_array().iter().map(|x| Wrapper(x)))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
debug_wrapper! {
|
||||
Debug => format,
|
||||
Binary => format_binary,
|
||||
LowerExp => format_lower_exp,
|
||||
UpperExp => format_upper_exp,
|
||||
Octal => format_octal,
|
||||
LowerHex => format_lower_hex,
|
||||
UpperHex => format_upper_hex,
|
||||
}
|
||||
|
||||
macro_rules! impl_fmt_trait {
|
||||
{ $($type:ident => $(($trait:ident, $format:ident)),*;)* } => {
|
||||
$( // repeat type
|
||||
$( // repeat trait
|
||||
impl<const LANES: usize> core::fmt::$trait for crate::$type<LANES>
|
||||
where
|
||||
crate::LaneCount<LANES>: crate::SupportedLaneCount,
|
||||
{
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
$format(self.as_ref(), f)
|
||||
}
|
||||
}
|
||||
)*
|
||||
)*
|
||||
};
|
||||
{ integers: $($type:ident,)* } => {
|
||||
impl_fmt_trait! {
|
||||
$($type =>
|
||||
(Debug, format),
|
||||
(Binary, format_binary),
|
||||
(LowerExp, format_lower_exp),
|
||||
(UpperExp, format_upper_exp),
|
||||
(Octal, format_octal),
|
||||
(LowerHex, format_lower_hex),
|
||||
(UpperHex, format_upper_hex);
|
||||
)*
|
||||
}
|
||||
};
|
||||
{ floats: $($type:ident,)* } => {
|
||||
impl_fmt_trait! {
|
||||
$($type =>
|
||||
(Debug, format),
|
||||
(LowerExp, format_lower_exp),
|
||||
(UpperExp, format_upper_exp);
|
||||
)*
|
||||
}
|
||||
};
|
||||
{ masks: $($type:ident,)* } => {
|
||||
impl_fmt_trait! {
|
||||
$($type =>
|
||||
(Debug, format);
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_fmt_trait! {
|
||||
integers:
|
||||
SimdU8, SimdU16, SimdU32, SimdU64,
|
||||
SimdI8, SimdI16, SimdI32, SimdI64,
|
||||
SimdUsize, SimdIsize,
|
||||
}
|
||||
|
||||
impl_fmt_trait! {
|
||||
floats:
|
||||
SimdF32, SimdF64,
|
||||
Debug,
|
||||
Binary,
|
||||
LowerExp,
|
||||
UpperExp,
|
||||
Octal,
|
||||
LowerHex,
|
||||
UpperHex,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#![allow(incomplete_features)]
|
||||
#![feature(
|
||||
const_evaluatable_checked,
|
||||
const_fn_trait_bound,
|
||||
const_generics,
|
||||
platform_intrinsics,
|
||||
repr_simd,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
macro_rules! impl_shuffle_lane {
|
||||
{ $name:ident, $fn:ident, $n:literal } => {
|
||||
impl $name<$n> {
|
||||
{ $fn:ident, $n:literal } => {
|
||||
impl<Element> crate::Simd<Element, $n>
|
||||
where
|
||||
Element: crate::SimdElement,
|
||||
{
|
||||
/// A const SIMD shuffle that takes 2 SIMD vectors and produces another vector, using
|
||||
/// the indices in the const parameter. The first or "self" vector will have its lanes
|
||||
/// indexed from 0, and the second vector will have its first lane indexed at $n.
|
||||
|
|
@ -138,12 +141,8 @@ macro_rules! impl_shuffle_lane {
|
|||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_shuffle_2pow_lanes {
|
||||
{ $name:ident } => {
|
||||
impl_shuffle_lane!{ $name, simd_shuffle2, 2 }
|
||||
impl_shuffle_lane!{ $name, simd_shuffle4, 4 }
|
||||
impl_shuffle_lane!{ $name, simd_shuffle8, 8 }
|
||||
impl_shuffle_lane!{ $name, simd_shuffle16, 16 }
|
||||
impl_shuffle_lane!{ $name, simd_shuffle32, 32 }
|
||||
}
|
||||
}
|
||||
impl_shuffle_lane! { simd_shuffle2, 2 }
|
||||
impl_shuffle_lane! { simd_shuffle4, 4 }
|
||||
impl_shuffle_lane! { simd_shuffle8, 8 }
|
||||
impl_shuffle_lane! { simd_shuffle16, 16 }
|
||||
impl_shuffle_lane! { simd_shuffle32, 32 }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,3 @@
|
|||
#[macro_use]
|
||||
mod vector_impl;
|
||||
|
||||
mod float;
|
||||
mod int;
|
||||
mod uint;
|
||||
|
|
@ -21,13 +18,325 @@ where
|
|||
Element: SimdElement,
|
||||
LaneCount<LANES>: SupportedLaneCount;
|
||||
|
||||
impl<Element, const LANES: usize> Simd<Element, LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
Element: SimdElement,
|
||||
{
|
||||
/// Construct a SIMD vector by setting all lanes to the given value.
|
||||
pub const fn splat(value: Element) -> Self {
|
||||
Self([value; LANES])
|
||||
}
|
||||
|
||||
/// Returns an array reference containing the entire SIMD vector.
|
||||
pub const fn as_array(&self) -> &[Element; LANES] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Returns a mutable array reference containing the entire SIMD vector.
|
||||
pub fn as_mut_array(&mut self) -> &mut [Element; LANES] {
|
||||
&mut self.0
|
||||
}
|
||||
|
||||
/// Converts an array to a SIMD vector.
|
||||
pub const fn from_array(array: [Element; LANES]) -> Self {
|
||||
Self(array)
|
||||
}
|
||||
|
||||
/// Converts a SIMD vector to an array.
|
||||
pub const fn to_array(self) -> [Element; LANES] {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// SIMD gather: construct a SIMD vector by reading from a slice, using potentially discontiguous indices.
|
||||
/// If an index is out of bounds, that lane instead selects the value from the "or" vector.
|
||||
/// ```
|
||||
/// # #![feature(portable_simd)]
|
||||
/// # use core_simd::*;
|
||||
/// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
|
||||
/// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 5]);
|
||||
/// let alt = SimdI32::from_array([-5, -4, -3, -2]);
|
||||
///
|
||||
/// let result = SimdI32::<4>::gather_or(&vec, idxs, alt); // Note the lane that is out-of-bounds.
|
||||
/// assert_eq!(result, SimdI32::from_array([-5, 13, 10, 15]));
|
||||
/// ```
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn gather_or(slice: &[Element], idxs: crate::SimdUsize<LANES>, or: Self) -> Self {
|
||||
Self::gather_select(slice, crate::MaskSize::splat(true), idxs, or)
|
||||
}
|
||||
|
||||
/// SIMD gather: construct a SIMD vector by reading from a slice, using potentially discontiguous indices.
|
||||
/// Out-of-bounds indices instead use the default value for that lane (0).
|
||||
/// ```
|
||||
/// # #![feature(portable_simd)]
|
||||
/// # use core_simd::*;
|
||||
/// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
|
||||
/// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 5]);
|
||||
///
|
||||
/// let result = SimdI32::<4>::gather_or_default(&vec, idxs); // Note the lane that is out-of-bounds.
|
||||
/// assert_eq!(result, SimdI32::from_array([0, 13, 10, 15]));
|
||||
/// ```
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn gather_or_default(slice: &[Element], idxs: crate::SimdUsize<LANES>) -> Self
|
||||
where
|
||||
Element: Default,
|
||||
{
|
||||
Self::gather_or(slice, idxs, Self::splat(Element::default()))
|
||||
}
|
||||
|
||||
/// SIMD gather: construct a SIMD vector by reading from a slice, using potentially discontiguous indices.
|
||||
/// Out-of-bounds or masked indices instead select the value from the "or" vector.
|
||||
/// ```
|
||||
/// # #![feature(portable_simd)]
|
||||
/// # use core_simd::*;
|
||||
/// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
|
||||
/// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 5]);
|
||||
/// let alt = SimdI32::from_array([-5, -4, -3, -2]);
|
||||
/// let mask = MaskSize::from_array([true, true, true, false]); // Note the mask of the last lane.
|
||||
///
|
||||
/// let result = SimdI32::<4>::gather_select(&vec, mask, idxs, alt); // Note the lane that is out-of-bounds.
|
||||
/// assert_eq!(result, SimdI32::from_array([-5, 13, 10, -2]));
|
||||
/// ```
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn gather_select(
|
||||
slice: &[Element],
|
||||
mask: crate::MaskSize<LANES>,
|
||||
idxs: crate::SimdUsize<LANES>,
|
||||
or: Self,
|
||||
) -> Self {
|
||||
let mask = (mask & idxs.lanes_lt(crate::SimdUsize::splat(slice.len()))).to_int();
|
||||
let base_ptr = crate::vector::ptr::SimdConstPtr::splat(slice.as_ptr());
|
||||
// Ferris forgive me, I have done pointer arithmetic here.
|
||||
let ptrs = base_ptr.wrapping_add(idxs);
|
||||
// SAFETY: The ptrs have been bounds-masked to prevent memory-unsafe reads insha'allah
|
||||
unsafe { crate::intrinsics::simd_gather(or, ptrs, mask) }
|
||||
}
|
||||
|
||||
/// SIMD scatter: write a SIMD vector's values into a slice, using potentially discontiguous indices.
|
||||
/// Out-of-bounds indices are not written.
|
||||
/// `scatter` writes "in order", so if an index receives two writes, only the last is guaranteed.
|
||||
/// ```
|
||||
/// # #![feature(portable_simd)]
|
||||
/// # use core_simd::*;
|
||||
/// let mut vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
|
||||
/// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 0]);
|
||||
/// let vals = SimdI32::from_array([-27, 82, -41, 124]);
|
||||
///
|
||||
/// vals.scatter(&mut vec, idxs); // index 0 receives two writes.
|
||||
/// assert_eq!(vec, vec![124, 11, 12, 82, 14, 15, 16, 17, 18]);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn scatter(self, slice: &mut [Element], idxs: crate::SimdUsize<LANES>) {
|
||||
self.scatter_select(slice, crate::MaskSize::splat(true), idxs)
|
||||
}
|
||||
|
||||
/// SIMD scatter: write a SIMD vector's values into a slice, using potentially discontiguous indices.
|
||||
/// Out-of-bounds or masked indices are not written.
|
||||
/// `scatter_select` writes "in order", so if an index receives two writes, only the last is guaranteed.
|
||||
/// ```
|
||||
/// # #![feature(portable_simd)]
|
||||
/// # use core_simd::*;
|
||||
/// let mut vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
|
||||
/// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 0]);
|
||||
/// let vals = SimdI32::from_array([-27, 82, -41, 124]);
|
||||
/// let mask = MaskSize::from_array([true, true, true, false]); // Note the mask of the last lane.
|
||||
///
|
||||
/// vals.scatter_select(&mut vec, mask, idxs); // index 0's second write is masked, thus omitted.
|
||||
/// assert_eq!(vec, vec![-41, 11, 12, 82, 14, 15, 16, 17, 18]);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn scatter_select(
|
||||
self,
|
||||
slice: &mut [Element],
|
||||
mask: crate::MaskSize<LANES>,
|
||||
idxs: crate::SimdUsize<LANES>,
|
||||
) {
|
||||
// We must construct our scatter mask before we derive a pointer!
|
||||
let mask = (mask & idxs.lanes_lt(crate::SimdUsize::splat(slice.len()))).to_int();
|
||||
// SAFETY: This block works with *mut T derived from &mut 'a [T],
|
||||
// which means it is delicate in Rust's borrowing model, circa 2021:
|
||||
// &mut 'a [T] asserts uniqueness, so deriving &'a [T] invalidates live *mut Ts!
|
||||
// Even though this block is largely safe methods, it must be almost exactly this way
|
||||
// to prevent invalidating the raw ptrs while they're live.
|
||||
// Thus, entering this block requires all values to use being already ready:
|
||||
// 0. idxs we want to write to, which are used to construct the mask.
|
||||
// 1. mask, which depends on an initial &'a [T] and the idxs.
|
||||
// 2. actual values to scatter (self).
|
||||
// 3. &mut [T] which will become our base ptr.
|
||||
unsafe {
|
||||
// Now Entering ☢️ *mut T Zone
|
||||
let base_ptr = crate::vector::ptr::SimdMutPtr::splat(slice.as_mut_ptr());
|
||||
// Ferris forgive me, I have done pointer arithmetic here.
|
||||
let ptrs = base_ptr.wrapping_add(idxs);
|
||||
// The ptrs have been bounds-masked to prevent memory-unsafe writes insha'allah
|
||||
crate::intrinsics::simd_scatter(self, ptrs, mask)
|
||||
// Cleared ☢️ *mut T Zone
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Element, const LANES: usize> Copy for Simd<Element, LANES>
|
||||
where
|
||||
Element: SimdElement,
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
{
|
||||
}
|
||||
|
||||
impl<Element, const LANES: usize> Clone for Simd<Element, LANES>
|
||||
where
|
||||
Element: SimdElement,
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<Element, const LANES: usize> Default for Simd<Element, LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
Element: SimdElement + Default,
|
||||
{
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Self::splat(Element::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Element, const LANES: usize> PartialEq for Simd<Element, LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
Element: SimdElement + PartialEq,
|
||||
{
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
// TODO use SIMD equality
|
||||
self.to_array() == other.to_array()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Element, const LANES: usize> PartialOrd for Simd<Element, LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
Element: SimdElement + PartialOrd,
|
||||
{
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
||||
// TODO use SIMD equality
|
||||
self.to_array().partial_cmp(other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Element, const LANES: usize> Eq for Simd<Element, LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
Element: SimdElement + Eq,
|
||||
{
|
||||
}
|
||||
|
||||
impl<Element, const LANES: usize> Ord for Simd<Element, LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
Element: SimdElement + Ord,
|
||||
{
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
|
||||
// TODO use SIMD equality
|
||||
self.to_array().cmp(other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Element, const LANES: usize> core::hash::Hash for Simd<Element, LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
Element: SimdElement + core::hash::Hash,
|
||||
{
|
||||
#[inline]
|
||||
fn hash<H>(&self, state: &mut H)
|
||||
where
|
||||
H: core::hash::Hasher,
|
||||
{
|
||||
self.as_array().hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
// array references
|
||||
impl<Element, const LANES: usize> AsRef<[Element; LANES]> for Simd<Element, LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
Element: SimdElement,
|
||||
{
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[Element; LANES] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<Element, const LANES: usize> AsMut<[Element; LANES]> for Simd<Element, LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
Element: SimdElement,
|
||||
{
|
||||
#[inline]
|
||||
fn as_mut(&mut self) -> &mut [Element; LANES] {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
// slice references
|
||||
impl<Element, const LANES: usize> AsRef<[Element]> for Simd<Element, LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
Element: SimdElement,
|
||||
{
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[Element] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<Element, const LANES: usize> AsMut<[Element]> for Simd<Element, LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
Element: SimdElement,
|
||||
{
|
||||
#[inline]
|
||||
fn as_mut(&mut self) -> &mut [Element] {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
// vector/array conversion
|
||||
impl<Element, const LANES: usize> From<[Element; LANES]> for Simd<Element, LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
Element: SimdElement,
|
||||
{
|
||||
fn from(array: [Element; LANES]) -> Self {
|
||||
Self(array)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Element, const LANES: usize> From<Simd<Element, LANES>> for [Element; LANES]
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
Element: SimdElement,
|
||||
{
|
||||
fn from(vector: Simd<Element, LANES>) -> Self {
|
||||
vector.to_array()
|
||||
}
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
use sealed::Sealed;
|
||||
|
||||
/// Marker trait for types that may be used as SIMD vector elements.
|
||||
pub unsafe trait SimdElement: Sealed {
|
||||
pub unsafe trait SimdElement: Sealed + Copy {
|
||||
/// The mask element type corresponding to this element type.
|
||||
type Mask: SimdElement;
|
||||
}
|
||||
|
|
@ -106,3 +415,24 @@ pub trait Vector: sealed::Sealed {
|
|||
#[must_use]
|
||||
fn splat(val: Self::Scalar) -> Self;
|
||||
}
|
||||
|
||||
impl<Element, const LANES: usize> Sealed for Simd<Element, LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
Element: SimdElement,
|
||||
{
|
||||
}
|
||||
|
||||
impl<Element, const LANES: usize> Vector for Simd<Element, LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
Element: SimdElement,
|
||||
{
|
||||
type Scalar = Element;
|
||||
const LANES: usize = LANES;
|
||||
|
||||
#[inline]
|
||||
fn splat(val: Self::Scalar) -> Self {
|
||||
Self::splat(val)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ use crate::{LaneCount, SupportedLaneCount};
|
|||
/// representation. Called from `define_float_vector!`.
|
||||
macro_rules! impl_float_vector {
|
||||
{ $name:ident, $type:ident, $bits_ty:ident, $mask_ty:ident, $mask_impl_ty:ident } => {
|
||||
impl_vector! { $name, $type }
|
||||
impl_float_reductions! { $name, $type }
|
||||
|
||||
impl<const LANES: usize> $name<LANES>
|
||||
|
|
|
|||
|
|
@ -5,32 +5,8 @@ use crate::{LaneCount, SupportedLaneCount};
|
|||
/// Implements additional integer traits (Eq, Ord, Hash) on the specified vector `$name`, holding multiple `$lanes` of `$type`.
|
||||
macro_rules! impl_integer_vector {
|
||||
{ $name:ident, $type:ty, $mask_ty:ident, $mask_impl_ty:ident } => {
|
||||
impl_vector! { $name, $type }
|
||||
impl_integer_reductions! { $name, $type }
|
||||
|
||||
impl<const LANES: usize> Eq for $name<LANES> where LaneCount<LANES>: SupportedLaneCount {}
|
||||
|
||||
impl<const LANES: usize> Ord for $name<LANES> where LaneCount<LANES>: SupportedLaneCount {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
|
||||
// TODO use SIMD cmp
|
||||
self.as_array().cmp(other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LANES: usize> core::hash::Hash for $name<LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
{
|
||||
#[inline]
|
||||
fn hash<H>(&self, state: &mut H)
|
||||
where
|
||||
H: core::hash::Hasher
|
||||
{
|
||||
self.as_array().hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LANES: usize> $name<LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
|
|
|
|||
|
|
@ -1,35 +1,9 @@
|
|||
#![allow(non_camel_case_types)]
|
||||
|
||||
use crate::{LaneCount, SupportedLaneCount};
|
||||
|
||||
/// Implements additional integer traits (Eq, Ord, Hash) on the specified vector `$name`, holding multiple `$lanes` of `$type`.
|
||||
macro_rules! impl_unsigned_vector {
|
||||
{ $name:ident, $type:ty } => {
|
||||
impl_vector! { $name, $type }
|
||||
impl_integer_reductions! { $name, $type }
|
||||
|
||||
impl<const LANES: usize> Eq for $name<LANES> where LaneCount<LANES>: SupportedLaneCount {}
|
||||
|
||||
impl<const LANES: usize> Ord for $name<LANES> where LaneCount<LANES>: SupportedLaneCount {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
|
||||
// TODO use SIMD cmp
|
||||
self.as_array().cmp(other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LANES: usize> core::hash::Hash for $name<LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
{
|
||||
#[inline]
|
||||
fn hash<H>(&self, state: &mut H)
|
||||
where
|
||||
H: core::hash::Hasher
|
||||
{
|
||||
self.as_array().hash(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,257 +0,0 @@
|
|||
/// Implements common traits on the specified vector `$name`, holding multiple `$lanes` of `$type`.
|
||||
macro_rules! impl_vector {
|
||||
{ $name:ident, $type:ty } => {
|
||||
impl<const LANES: usize> crate::vector::sealed::Sealed for $name<LANES>
|
||||
where
|
||||
crate::LaneCount<LANES>: crate::SupportedLaneCount,
|
||||
{}
|
||||
|
||||
impl<const LANES: usize> crate::vector::Vector for $name<LANES>
|
||||
where
|
||||
crate::LaneCount<LANES>: crate::SupportedLaneCount,
|
||||
{
|
||||
type Scalar = $type;
|
||||
const LANES: usize = LANES;
|
||||
|
||||
#[inline]
|
||||
fn splat(val: Self::Scalar) -> Self {
|
||||
Self::splat(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LANES: usize> $name<LANES>
|
||||
where
|
||||
crate::LaneCount<LANES>: crate::SupportedLaneCount,
|
||||
{
|
||||
/// Construct a SIMD vector by setting all lanes to the given value.
|
||||
pub const fn splat(value: $type) -> Self {
|
||||
Self([value; LANES])
|
||||
}
|
||||
|
||||
/// Returns an array reference containing the entire SIMD vector.
|
||||
pub const fn as_array(&self) -> &[$type; LANES] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Returns a mutable array reference containing the entire SIMD vector.
|
||||
pub fn as_mut_array(&mut self) -> &mut [$type; LANES] {
|
||||
&mut self.0
|
||||
}
|
||||
|
||||
/// Converts an array to a SIMD vector.
|
||||
pub const fn from_array(array: [$type; LANES]) -> Self {
|
||||
Self(array)
|
||||
}
|
||||
|
||||
/// Converts a SIMD vector to an array.
|
||||
pub const fn to_array(self) -> [$type; LANES] {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// SIMD gather: construct a SIMD vector by reading from a slice, using potentially discontiguous indices.
|
||||
/// If an index is out of bounds, that lane instead selects the value from the "or" vector.
|
||||
/// ```
|
||||
/// # #![feature(portable_simd)]
|
||||
/// # use core_simd::*;
|
||||
/// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
|
||||
/// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 5]);
|
||||
/// let alt = SimdI32::from_array([-5, -4, -3, -2]);
|
||||
///
|
||||
/// let result = SimdI32::<4>::gather_or(&vec, idxs, alt); // Note the lane that is out-of-bounds.
|
||||
/// assert_eq!(result, SimdI32::from_array([-5, 13, 10, 15]));
|
||||
/// ```
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn gather_or(slice: &[$type], idxs: crate::SimdUsize<LANES>, or: Self) -> Self {
|
||||
Self::gather_select(slice, crate::MaskSize::splat(true), idxs, or)
|
||||
}
|
||||
|
||||
/// SIMD gather: construct a SIMD vector by reading from a slice, using potentially discontiguous indices.
|
||||
/// Out-of-bounds indices instead use the default value for that lane (0).
|
||||
/// ```
|
||||
/// # #![feature(portable_simd)]
|
||||
/// # use core_simd::*;
|
||||
/// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
|
||||
/// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 5]);
|
||||
///
|
||||
/// let result = SimdI32::<4>::gather_or_default(&vec, idxs); // Note the lane that is out-of-bounds.
|
||||
/// assert_eq!(result, SimdI32::from_array([0, 13, 10, 15]));
|
||||
/// ```
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn gather_or_default(slice: &[$type], idxs: crate::SimdUsize<LANES>) -> Self {
|
||||
Self::gather_or(slice, idxs, Self::splat(<$type>::default()))
|
||||
}
|
||||
|
||||
/// SIMD gather: construct a SIMD vector by reading from a slice, using potentially discontiguous indices.
|
||||
/// Out-of-bounds or masked indices instead select the value from the "or" vector.
|
||||
/// ```
|
||||
/// # #![feature(portable_simd)]
|
||||
/// # use core_simd::*;
|
||||
/// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
|
||||
/// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 5]);
|
||||
/// let alt = SimdI32::from_array([-5, -4, -3, -2]);
|
||||
/// let mask = MaskSize::from_array([true, true, true, false]); // Note the mask of the last lane.
|
||||
///
|
||||
/// let result = SimdI32::<4>::gather_select(&vec, mask, idxs, alt); // Note the lane that is out-of-bounds.
|
||||
/// assert_eq!(result, SimdI32::from_array([-5, 13, 10, -2]));
|
||||
/// ```
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub fn gather_select(
|
||||
slice: &[$type],
|
||||
mask: crate::MaskSize<LANES>,
|
||||
idxs: crate::SimdUsize<LANES>,
|
||||
or: Self,
|
||||
) -> Self
|
||||
{
|
||||
let mask = (mask & idxs.lanes_lt(crate::SimdUsize::splat(slice.len()))).to_int();
|
||||
let base_ptr = crate::vector::ptr::SimdConstPtr::splat(slice.as_ptr());
|
||||
// Ferris forgive me, I have done pointer arithmetic here.
|
||||
let ptrs = base_ptr.wrapping_add(idxs);
|
||||
// SAFETY: The ptrs have been bounds-masked to prevent memory-unsafe reads insha'allah
|
||||
unsafe { crate::intrinsics::simd_gather(or, ptrs, mask) }
|
||||
}
|
||||
|
||||
/// SIMD scatter: write a SIMD vector's values into a slice, using potentially discontiguous indices.
|
||||
/// Out-of-bounds indices are not written.
|
||||
/// `scatter` writes "in order", so if an index receives two writes, only the last is guaranteed.
|
||||
/// ```
|
||||
/// # #![feature(portable_simd)]
|
||||
/// # use core_simd::*;
|
||||
/// let mut vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
|
||||
/// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 0]);
|
||||
/// let vals = SimdI32::from_array([-27, 82, -41, 124]);
|
||||
///
|
||||
/// vals.scatter(&mut vec, idxs); // index 0 receives two writes.
|
||||
/// assert_eq!(vec, vec![124, 11, 12, 82, 14, 15, 16, 17, 18]);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn scatter(self, slice: &mut [$type], idxs: crate::SimdUsize<LANES>) {
|
||||
self.scatter_select(slice, crate::MaskSize::splat(true), idxs)
|
||||
}
|
||||
|
||||
/// SIMD scatter: write a SIMD vector's values into a slice, using potentially discontiguous indices.
|
||||
/// Out-of-bounds or masked indices are not written.
|
||||
/// `scatter_select` writes "in order", so if an index receives two writes, only the last is guaranteed.
|
||||
/// ```
|
||||
/// # #![feature(portable_simd)]
|
||||
/// # use core_simd::*;
|
||||
/// let mut vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
|
||||
/// let idxs = SimdUsize::<4>::from_array([9, 3, 0, 0]);
|
||||
/// let vals = SimdI32::from_array([-27, 82, -41, 124]);
|
||||
/// let mask = MaskSize::from_array([true, true, true, false]); // Note the mask of the last lane.
|
||||
///
|
||||
/// vals.scatter_select(&mut vec, mask, idxs); // index 0's second write is masked, thus omitted.
|
||||
/// assert_eq!(vec, vec![-41, 11, 12, 82, 14, 15, 16, 17, 18]);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn scatter_select(
|
||||
self,
|
||||
slice: &mut [$type],
|
||||
mask: crate::MaskSize<LANES>,
|
||||
idxs: crate::SimdUsize<LANES>,
|
||||
)
|
||||
{
|
||||
// We must construct our scatter mask before we derive a pointer!
|
||||
let mask = (mask & idxs.lanes_lt(crate::SimdUsize::splat(slice.len()))).to_int();
|
||||
// SAFETY: This block works with *mut T derived from &mut 'a [T],
|
||||
// which means it is delicate in Rust's borrowing model, circa 2021:
|
||||
// &mut 'a [T] asserts uniqueness, so deriving &'a [T] invalidates live *mut Ts!
|
||||
// Even though this block is largely safe methods, it must be almost exactly this way
|
||||
// to prevent invalidating the raw ptrs while they're live.
|
||||
// Thus, entering this block requires all values to use being already ready:
|
||||
// 0. idxs we want to write to, which are used to construct the mask.
|
||||
// 1. mask, which depends on an initial &'a [T] and the idxs.
|
||||
// 2. actual values to scatter (self).
|
||||
// 3. &mut [T] which will become our base ptr.
|
||||
unsafe {
|
||||
// Now Entering ☢️ *mut T Zone
|
||||
let base_ptr = crate::vector::ptr::SimdMutPtr::splat(slice.as_mut_ptr());
|
||||
// Ferris forgive me, I have done pointer arithmetic here.
|
||||
let ptrs = base_ptr.wrapping_add(idxs);
|
||||
// The ptrs have been bounds-masked to prevent memory-unsafe writes insha'allah
|
||||
crate::intrinsics::simd_scatter(self, ptrs, mask)
|
||||
// Cleared ☢️ *mut T Zone
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LANES: usize> Copy for $name<LANES> where crate::LaneCount<LANES>: crate::SupportedLaneCount {}
|
||||
|
||||
impl<const LANES: usize> Clone for $name<LANES> where crate::LaneCount<LANES>: crate::SupportedLaneCount {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LANES: usize> Default for $name<LANES> where crate::LaneCount<LANES>: crate::SupportedLaneCount {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Self::splat(<$type>::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LANES: usize> PartialEq for $name<LANES> where crate::LaneCount<LANES>: crate::SupportedLaneCount {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
// TODO use SIMD equality
|
||||
self.to_array() == other.to_array()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LANES: usize> PartialOrd for $name<LANES> where crate::LaneCount<LANES>: crate::SupportedLaneCount {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
||||
// TODO use SIMD equalitya
|
||||
self.to_array().partial_cmp(other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
// array references
|
||||
impl<const LANES: usize> AsRef<[$type; LANES]> for $name<LANES> where crate::LaneCount<LANES>: crate::SupportedLaneCount {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[$type; LANES] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LANES: usize> AsMut<[$type; LANES]> for $name<LANES> where crate::LaneCount<LANES>: crate::SupportedLaneCount {
|
||||
#[inline]
|
||||
fn as_mut(&mut self) -> &mut [$type; LANES] {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
// slice references
|
||||
impl<const LANES: usize> AsRef<[$type]> for $name<LANES> where crate::LaneCount<LANES>: crate::SupportedLaneCount {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[$type] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LANES: usize> AsMut<[$type]> for $name<LANES> where crate::LaneCount<LANES>: crate::SupportedLaneCount {
|
||||
#[inline]
|
||||
fn as_mut(&mut self) -> &mut [$type] {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
// vector/array conversion
|
||||
impl<const LANES: usize> From<[$type; LANES]> for $name<LANES> where crate::LaneCount<LANES>: crate::SupportedLaneCount {
|
||||
fn from(array: [$type; LANES]) -> Self {
|
||||
Self(array)
|
||||
}
|
||||
}
|
||||
|
||||
impl <const LANES: usize> From<$name<LANES>> for [$type; LANES] where crate::LaneCount<LANES>: crate::SupportedLaneCount {
|
||||
fn from(vector: $name<LANES>) -> Self {
|
||||
vector.to_array()
|
||||
}
|
||||
}
|
||||
|
||||
impl_shuffle_2pow_lanes!{ $name }
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue