Add vectors of pointers

This commit is contained in:
Caleb Zulawski 2022-06-21 23:17:13 -04:00
parent 7c80b6967a
commit d3cfd7c5c9
8 changed files with 339 additions and 3 deletions

View File

@ -0,0 +1,45 @@
use crate::simd::SimdElement;
/// Supporting trait for `Simd::cast`. Typically doesn't need to be used directly.
pub trait SimdCast<Target: SimdElement>: SimdElement {}
macro_rules! into_number {
{ $($type:ty),* } => {
$(
impl SimdCast<i8> for $type {}
impl SimdCast<i16> for $type {}
impl SimdCast<i32> for $type {}
impl SimdCast<i64> for $type {}
impl SimdCast<isize> for $type {}
impl SimdCast<u8> for $type {}
impl SimdCast<u16> for $type {}
impl SimdCast<u32> for $type {}
impl SimdCast<u64> for $type {}
impl SimdCast<usize> for $type {}
impl SimdCast<f32> for $type {}
impl SimdCast<f64> for $type {}
)*
}
}
into_number! { i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, f32, f64 }
macro_rules! into_pointer {
{ $($type:ty),* } => {
$(
impl<T> SimdCast<$type> for *const T {}
impl<T> SimdCast<$type> for *mut T {}
impl<T> SimdCast<*const T> for $type {}
impl<T> SimdCast<*mut T> for $type {}
)*
}
}
into_pointer! { i8, i16, i32, i64, isize, u8, u16, u32, u64, usize }
impl<T, U> SimdCast<*const T> for *const U {}
impl<T, U> SimdCast<*const T> for *mut U {}
impl<T, U> SimdCast<*mut T> for *const U {}
impl<T, U> SimdCast<*mut T> for *mut U {}

View File

@ -1,11 +1,15 @@
mod const_ptr;
mod float;
mod int;
mod mut_ptr;
mod uint;
mod sealed {
pub trait Sealed {}
}
pub use const_ptr::*;
pub use float::*;
pub use int::*;
pub use mut_ptr::*;
pub use uint::*;

View File

@ -0,0 +1,59 @@
use super::sealed::Sealed;
use crate::simd::{intrinsics, LaneCount, Mask, Simd, SimdPartialEq, SupportedLaneCount};
/// Operations on SIMD vectors of constant pointers.
pub trait SimdConstPtr: Copy + Sealed {
/// Vector type representing the pointers as bits.
type Bits;
/// Vector of mutable pointers to the same type.
type MutPtr;
/// Mask type used for manipulating this SIMD vector type.
type Mask;
/// Returns `true` for each lane that is null.
fn is_null(self) -> Self::Mask;
/// Changes constness without changing the type.
fn as_mut(self) -> Self::MutPtr;
/// Cast pointers to raw bits.
fn to_bits(self) -> Self::Bits;
/// Cast raw bits to pointers.
fn from_bits(bits: Self::Bits) -> Self;
}
impl<T, const LANES: usize> Sealed for Simd<*const T, LANES> where
LaneCount<LANES>: SupportedLaneCount
{
}
impl<T, const LANES: usize> SimdConstPtr for Simd<*const T, LANES>
where
LaneCount<LANES>: SupportedLaneCount,
{
type Bits = Simd<usize, LANES>;
type MutPtr = Simd<*mut T, LANES>;
type Mask = Mask<isize, LANES>;
fn is_null(self) -> Self::Mask {
Simd::splat(core::ptr::null()).simd_eq(self)
}
fn as_mut(self) -> Self::MutPtr {
// Converting between pointers is safe
unsafe { intrinsics::simd_as(self) }
}
fn to_bits(self) -> Self::Bits {
// Casting pointers to usize is safe
unsafe { intrinsics::simd_as(self) }
}
fn from_bits(bits: Self::Bits) -> Self {
// Casting usize to pointers is safe
unsafe { intrinsics::simd_as(bits) }
}
}

View File

@ -0,0 +1,57 @@
use super::sealed::Sealed;
use crate::simd::{intrinsics, LaneCount, Mask, Simd, SimdPartialEq, SupportedLaneCount};
/// Operations on SIMD vectors of mutable pointers.
pub trait SimdMutPtr: Copy + Sealed {
/// Vector type representing the pointers as bits.
type Bits;
/// Vector of constant pointers to the same type.
type ConstPtr;
/// Mask type used for manipulating this SIMD vector type.
type Mask;
/// Returns `true` for each lane that is null.
fn is_null(self) -> Self::Mask;
/// Changes constness without changing the type.
fn as_const(self) -> Self::ConstPtr;
/// Cast pointers to raw bits.
fn to_bits(self) -> Self::Bits;
/// Cast raw bits to pointers.
fn from_bits(bits: Self::Bits) -> Self;
}
impl<T, const LANES: usize> Sealed for Simd<*mut T, LANES> where LaneCount<LANES>: SupportedLaneCount
{}
impl<T, const LANES: usize> SimdMutPtr for Simd<*mut T, LANES>
where
LaneCount<LANES>: SupportedLaneCount,
{
type Bits = Simd<usize, LANES>;
type ConstPtr = Simd<*const T, LANES>;
type Mask = Mask<isize, LANES>;
fn is_null(self) -> Self::Mask {
Simd::splat(core::ptr::null_mut()).simd_eq(self)
}
fn as_const(self) -> Self::ConstPtr {
// Converting between pointers is safe
unsafe { intrinsics::simd_as(self) }
}
fn to_bits(self) -> Self::Bits {
// Casting pointers to usize is safe
unsafe { intrinsics::simd_as(self) }
}
fn from_bits(bits: Self::Bits) -> Self {
// Casting usize to pointers is safe
unsafe { intrinsics::simd_as(bits) }
}
}

View File

@ -71,3 +71,45 @@ fn simd_ne(self, other: Self) -> Self::Mask {
}
impl_mask! { i8, i16, i32, i64, isize }
impl<T, const LANES: usize> SimdPartialEq for Simd<*const T, LANES>
where
LaneCount<LANES>: SupportedLaneCount,
{
type Mask = Mask<isize, LANES>;
#[inline]
fn simd_eq(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Mask::from_int_unchecked(intrinsics::simd_eq(self, other)) }
}
#[inline]
fn simd_ne(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Mask::from_int_unchecked(intrinsics::simd_ne(self, other)) }
}
}
impl<T, const LANES: usize> SimdPartialEq for Simd<*mut T, LANES>
where
LaneCount<LANES>: SupportedLaneCount,
{
type Mask = Mask<isize, LANES>;
#[inline]
fn simd_eq(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Mask::from_int_unchecked(intrinsics::simd_eq(self, other)) }
}
#[inline]
fn simd_ne(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Mask::from_int_unchecked(intrinsics::simd_ne(self, other)) }
}
}

View File

@ -7,6 +7,7 @@
mod to_bytes;
mod alias;
mod cast;
mod elements;
mod eq;
mod fmt;
@ -24,6 +25,7 @@ pub mod simd {
pub(crate) use crate::core_simd::intrinsics;
pub use crate::core_simd::alias::*;
pub use crate::core_simd::cast::*;
pub use crate::core_simd::elements::*;
pub use crate::core_simd::eq::*;
pub use crate::core_simd::lane_count::{LaneCount, SupportedLaneCount};

View File

@ -211,3 +211,117 @@ fn simd_clamp(self, min: Self, max: Self) -> Self {
}
impl_mask! { i8, i16, i32, i64, isize }
impl<T, const LANES: usize> SimdPartialOrd for Simd<*const T, LANES>
where
LaneCount<LANES>: SupportedLaneCount,
{
#[inline]
fn simd_lt(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Mask::from_int_unchecked(intrinsics::simd_lt(self, other)) }
}
#[inline]
fn simd_le(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Mask::from_int_unchecked(intrinsics::simd_le(self, other)) }
}
#[inline]
fn simd_gt(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Mask::from_int_unchecked(intrinsics::simd_gt(self, other)) }
}
#[inline]
fn simd_ge(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Mask::from_int_unchecked(intrinsics::simd_ge(self, other)) }
}
}
impl<T, const LANES: usize> SimdOrd for Simd<*const T, LANES>
where
LaneCount<LANES>: SupportedLaneCount,
{
#[inline]
fn simd_max(self, other: Self) -> Self {
self.simd_lt(other).select(other, self)
}
#[inline]
fn simd_min(self, other: Self) -> Self {
self.simd_gt(other).select(other, self)
}
#[inline]
fn simd_clamp(self, min: Self, max: Self) -> Self {
assert!(
min.simd_le(max).all(),
"each lane in `min` must be less than or equal to the corresponding lane in `max`",
);
self.simd_max(min).simd_min(max)
}
}
impl<T, const LANES: usize> SimdPartialOrd for Simd<*mut T, LANES>
where
LaneCount<LANES>: SupportedLaneCount,
{
#[inline]
fn simd_lt(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Mask::from_int_unchecked(intrinsics::simd_lt(self, other)) }
}
#[inline]
fn simd_le(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Mask::from_int_unchecked(intrinsics::simd_le(self, other)) }
}
#[inline]
fn simd_gt(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Mask::from_int_unchecked(intrinsics::simd_gt(self, other)) }
}
#[inline]
fn simd_ge(self, other: Self) -> Self::Mask {
// Safety: `self` is a vector, and the result of the comparison
// is always a valid mask.
unsafe { Mask::from_int_unchecked(intrinsics::simd_ge(self, other)) }
}
}
impl<T, const LANES: usize> SimdOrd for Simd<*mut T, LANES>
where
LaneCount<LANES>: SupportedLaneCount,
{
#[inline]
fn simd_max(self, other: Self) -> Self {
self.simd_lt(other).select(other, self)
}
#[inline]
fn simd_min(self, other: Self) -> Self {
self.simd_gt(other).select(other, self)
}
#[inline]
fn simd_clamp(self, min: Self, max: Self) -> Self {
assert!(
min.simd_le(max).all(),
"each lane in `min` must be less than or equal to the corresponding lane in `max`",
);
self.simd_max(min).simd_min(max)
}
}

View File

@ -2,7 +2,7 @@
pub(crate) mod ptr;
use crate::simd::{
intrinsics, LaneCount, Mask, MaskElement, SimdPartialOrd, SupportedLaneCount, Swizzle,
intrinsics, LaneCount, Mask, MaskElement, SimdCast, SimdPartialOrd, SupportedLaneCount, Swizzle,
};
/// A SIMD vector of `LANES` elements of type `T`. `Simd<T, N>` has the same shape as [`[T; N]`](array), but operates like `T`.
@ -211,7 +211,10 @@ pub const fn from_slice(slice: &[T]) -> Self {
#[must_use]
#[inline]
#[cfg(not(bootstrap))]
pub fn cast<U: SimdElement>(self) -> Simd<U, LANES> {
pub fn cast<U: SimdElement>(self) -> Simd<U, LANES>
where
T: SimdCast<U>,
{
// Safety: The input argument is a vector of a valid SIMD element type.
unsafe { intrinsics::simd_as(self) }
}
@ -234,7 +237,7 @@ pub fn cast<U: SimdElement>(self) -> Simd<U, LANES> {
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
pub unsafe fn to_int_unchecked<I>(self) -> Simd<I, LANES>
where
T: core::convert::FloatToInt<I>,
T: core::convert::FloatToInt<I> + SimdCast<I>,
I: SimdElement,
{
// Safety: `self` is a vector, and `FloatToInt` ensures the type can be casted to
@ -739,3 +742,13 @@ impl Sealed for f64 {}
unsafe impl SimdElement for f64 {
type Mask = i64;
}
impl<T> Sealed for *const T {}
unsafe impl<T> SimdElement for *const T {
type Mask = isize;
}
impl<T> Sealed for *mut T {}
unsafe impl<T> SimdElement for *mut T {
type Mask = isize;
}