Make implementation more scalable by using a helper trait to determine bitmask size. Improve bitmask to int conversion.

This commit is contained in:
Caleb Zulawski
2021-04-29 02:41:09 +00:00
committed by Jubilee Young
parent 0660d7ef11
commit 000a06bcab
12 changed files with 293 additions and 206 deletions
+1
View File
@@ -7,6 +7,7 @@ macro_rules! implement_mask_ops {
where
crate::$vector<LANES>: LanesAtMost32,
crate::$inner_ty<LANES>: LanesAtMost32,
crate::$mask<LANES>: crate::Mask,
{
/// Test if each lane is equal to the corresponding lane in `other`.
#[inline]
+3
View File
@@ -79,6 +79,9 @@ extern "platform-intrinsic" {
// truncate integer vector to bitmask
pub(crate) fn simd_bitmask<T, U>(x: T) -> U;
// select
pub(crate) fn simd_select_bitmask<T, U>(m: T, a: U, b: U) -> U;
}
#[cfg(feature = "std")]
+106 -85
View File
@@ -1,42 +1,80 @@
/// A mask where each lane is represented by a single bit.
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Ord, Eq, Hash)]
#[repr(transparent)]
pub struct BitMask<const LANES: usize>(u64);
use crate::Mask;
use core::marker::PhantomData;
impl<const LANES: usize> BitMask<LANES>
{
/// Helper trait for limiting int conversion types
pub trait ConvertToInt {}
impl<const LANES: usize> ConvertToInt for crate::SimdI8<LANES> where Self: crate::LanesAtMost32 {}
impl<const LANES: usize> ConvertToInt for crate::SimdI16<LANES> where Self: crate::LanesAtMost32 {}
impl<const LANES: usize> ConvertToInt for crate::SimdI32<LANES> where Self: crate::LanesAtMost32 {}
impl<const LANES: usize> ConvertToInt for crate::SimdI64<LANES> where Self: crate::LanesAtMost32 {}
impl<const LANES: usize> ConvertToInt for crate::SimdIsize<LANES> where Self: crate::LanesAtMost32 {}
/// A mask where each lane is represented by a single bit.
#[repr(transparent)]
pub struct BitMask<T: Mask, const LANES: usize>(T::BitMask, PhantomData<[(); LANES]>);
impl<T: Mask, const LANES: usize> Copy for BitMask<T, LANES> {}
impl<T: Mask, const LANES: usize> Clone for BitMask<T, LANES> {
fn clone(&self) -> Self {
*self
}
}
impl<T: Mask, const LANES: usize> PartialEq for BitMask<T, LANES> {
fn eq(&self, other: &Self) -> bool {
self.0.as_ref() == other.0.as_ref()
}
}
impl<T: Mask, const LANES: usize> PartialOrd for BitMask<T, LANES> {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.0.as_ref().partial_cmp(other.0.as_ref())
}
}
impl<T: Mask, const LANES: usize> Eq for BitMask<T, LANES> {}
impl<T: Mask, const LANES: usize> Ord for BitMask<T, LANES> {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.0.as_ref().cmp(other.0.as_ref())
}
}
impl<T: Mask, const LANES: usize> BitMask<T, LANES> {
#[inline]
pub fn splat(value: bool) -> Self {
let mut mask = T::BitMask::default();
if value {
Self(u64::MAX >> (64 - LANES))
mask.as_mut().fill(u8::MAX)
} else {
Self(u64::MIN)
mask.as_mut().fill(u8::MIN)
}
if LANES % 8 > 0 {
*mask.as_mut().last_mut().unwrap() &= u8::MAX >> (8 - LANES % 8);
}
Self(mask, PhantomData)
}
#[inline]
pub unsafe fn test_unchecked(&self, lane: usize) -> bool {
(self.0 >> lane) & 0x1 > 0
(self.0.as_ref()[lane / 8] >> lane % 8) & 0x1 > 0
}
#[inline]
pub unsafe fn set_unchecked(&mut self, lane: usize, value: bool) {
self.0 ^= ((value ^ self.test_unchecked(lane)) as u64) << lane
self.0.as_mut()[lane / 8] ^= ((value ^ self.test_unchecked(lane)) as u8) << (lane % 8)
}
#[inline]
pub fn to_int<V, T>(self) -> V
pub fn to_int<V>(self) -> V
where
V: Default + AsMut<[T; LANES]>,
T: From<i8>,
V: ConvertToInt + Default + core::ops::Not<Output = V>,
{
// TODO this should be an intrinsic sign-extension
let mut v = V::default();
for i in 0..LANES {
let lane = unsafe { self.test_unchecked(i) };
v.as_mut()[i] = (-(lane as i8)).into();
unsafe {
let mask: T::IntBitMask = core::mem::transmute_copy(&self);
crate::intrinsics::simd_select_bitmask(mask, !V::default(), V::default())
}
v
}
#[inline]
@@ -44,13 +82,22 @@ impl<const LANES: usize> BitMask<LANES>
where
V: crate::LanesAtMost32,
{
let mask: V::BitMask = crate::intrinsics::simd_bitmask(value);
Self(mask.into())
// TODO remove the transmute when rustc is more flexible
assert_eq!(
core::mem::size_of::<T::IntBitMask>(),
core::mem::size_of::<T::BitMask>()
);
let mask: T::IntBitMask = crate::intrinsics::simd_bitmask(value);
Self(core::mem::transmute_copy(&mask), PhantomData)
}
#[inline]
pub fn to_bitmask(self) -> u64 {
self.0
pub fn to_bitmask<U: Mask>(self) -> U::BitMask {
assert_eq!(
core::mem::size_of::<T::BitMask>(),
core::mem::size_of::<U::BitMask>()
);
unsafe { core::mem::transmute_copy(&self.0) }
}
#[inline]
@@ -64,87 +111,61 @@ impl<const LANES: usize> BitMask<LANES>
}
}
impl<const LANES: usize> core::ops::BitAnd for BitMask<LANES>
impl<T: Mask, const LANES: usize> core::ops::BitAnd for BitMask<T, LANES>
where
T::BitMask: Default + AsRef<[u8]> + AsMut<[u8]>,
{
type Output = Self;
#[inline]
fn bitand(self, rhs: Self) -> Self {
Self(self.0 & rhs.0)
fn bitand(mut self, rhs: Self) -> Self {
for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) {
*l &= r;
}
self
}
}
impl<const LANES: usize> core::ops::BitAnd<bool> for BitMask<LANES>
impl<T: Mask, const LANES: usize> core::ops::BitOr for BitMask<T, LANES>
where
T::BitMask: Default + AsRef<[u8]> + AsMut<[u8]>,
{
type Output = Self;
#[inline]
fn bitand(self, rhs: bool) -> Self {
self & Self::splat(rhs)
fn bitor(mut self, rhs: Self) -> Self {
for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) {
*l |= r;
}
self
}
}
impl<const LANES: usize> core::ops::BitAnd<BitMask<LANES>> for bool
{
type Output = BitMask<LANES>;
#[inline]
fn bitand(self, rhs: BitMask<LANES>) -> BitMask<LANES> {
BitMask::<LANES>::splat(self) & rhs
}
}
impl<const LANES: usize> core::ops::BitOr for BitMask<LANES>
{
impl<T: Mask, const LANES: usize> core::ops::BitXor for BitMask<T, LANES> {
type Output = Self;
#[inline]
fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
fn bitxor(mut self, rhs: Self) -> Self::Output {
for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) {
*l ^= r;
}
self
}
}
impl<const LANES: usize> core::ops::BitXor for BitMask<LANES>
{
impl<T: Mask, const LANES: usize> core::ops::Not for BitMask<T, LANES> {
type Output = Self;
#[inline]
fn bitxor(self, rhs: Self) -> Self::Output {
Self(self.0 ^ rhs.0)
fn not(mut self) -> Self::Output {
for x in self.0.as_mut() {
*x = !*x;
}
if LANES % 8 > 0 {
*self.0.as_mut().last_mut().unwrap() &= u8::MAX >> (8 - LANES % 8);
}
self
}
}
impl<const LANES: usize> core::ops::Not for BitMask<LANES>
{
type Output = BitMask<LANES>;
#[inline]
fn not(self) -> Self::Output {
Self(!self.0) & Self::splat(true)
}
}
impl<const LANES: usize> core::ops::BitAndAssign for BitMask<LANES>
{
#[inline]
fn bitand_assign(&mut self, rhs: Self) {
self.0 &= rhs.0;
}
}
impl<const LANES: usize> core::ops::BitOrAssign for BitMask<LANES>
{
#[inline]
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl<const LANES: usize> core::ops::BitXorAssign for BitMask<LANES>
{
#[inline]
fn bitxor_assign(&mut self, rhs: Self) {
self.0 ^= rhs.0;
}
}
pub type Mask8<const LANES: usize> = BitMask<LANES>;
pub type Mask16<const LANES: usize> = BitMask<LANES>;
pub type Mask32<const LANES: usize> = BitMask<LANES>;
pub type Mask64<const LANES: usize> = BitMask<LANES>;
pub type Mask128<const LANES: usize> = BitMask<LANES>;
pub type MaskSize<const LANES: usize> = BitMask<LANES>;
pub type Mask8<T, const LANES: usize> = BitMask<T, LANES>;
pub type Mask16<T, const LANES: usize> = BitMask<T, LANES>;
pub type Mask32<T, const LANES: usize> = BitMask<T, LANES>;
pub type Mask64<T, const LANES: usize> = BitMask<T, LANES>;
pub type MaskSize<T, const LANES: usize> = BitMask<T, LANES>;
+68 -57
View File
@@ -1,5 +1,8 @@
//! Masks that take up full SIMD vector registers.
use crate::Mask;
use core::marker::PhantomData;
macro_rules! define_mask {
{
$(#[$attr:meta])*
@@ -8,20 +11,19 @@ macro_rules! define_mask {
);
} => {
$(#[$attr])*
#[derive(Default, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[repr(transparent)]
pub struct $name<const $lanes: usize>(crate::$type<$lanes2>)
pub struct $name<T: Mask, const $lanes: usize>(crate::$type<$lanes2>, PhantomData<T>)
where
crate::$type<LANES>: crate::LanesAtMost32;
impl_full_mask_reductions! { $name, $type }
impl<const LANES: usize> Copy for $name<LANES>
impl<T: Mask, const LANES: usize> Copy for $name<T, LANES>
where
crate::$type<LANES>: crate::LanesAtMost32,
{}
impl<const LANES: usize> Clone for $name<LANES>
impl<T: Mask, const LANES: usize> Clone for $name<T, LANES>
where
crate::$type<LANES>: crate::LanesAtMost32,
{
@@ -31,18 +33,53 @@ macro_rules! define_mask {
}
}
impl<const LANES: usize> $name<LANES>
impl<T: Mask, const LANES: usize> PartialEq for $name<T, LANES>
where
crate::$type<LANES>: crate::LanesAtMost32,
{
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T: Mask, const LANES: usize> PartialOrd for $name<T, LANES>
where
crate::$type<LANES>: crate::LanesAtMost32,
{
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl<T: Mask, const LANES: usize> Eq for $name<T, LANES>
where
crate::$type<LANES>: crate::LanesAtMost32,
{}
impl<T: Mask, const LANES: usize> Ord for $name<T, LANES>
where
crate::$type<LANES>: crate::LanesAtMost32,
{
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.0.cmp(&other.0)
}
}
impl<T: Mask, const LANES: usize> $name<T, LANES>
where
crate::$type<LANES>: crate::LanesAtMost32,
{
pub fn splat(value: bool) -> Self {
Self(<crate::$type<LANES>>::splat(
if value {
-1
} else {
0
}
))
Self(
<crate::$type<LANES>>::splat(
if value {
-1
} else {
0
}
),
PhantomData,
)
}
#[inline]
@@ -66,96 +103,70 @@ macro_rules! define_mask {
#[inline]
pub unsafe fn from_int_unchecked(value: crate::$type<LANES>) -> Self {
Self(value)
Self(value, PhantomData)
}
#[inline]
pub fn to_bitmask(self) -> u64 {
let mask: <crate::$type<LANES> as crate::LanesAtMost32>::BitMask = unsafe { crate::intrinsics::simd_bitmask(self.0) };
mask.into()
pub fn to_bitmask<U: crate::Mask>(self) -> U::BitMask {
unsafe {
// TODO remove the transmute when rustc is more flexible
assert_eq!(core::mem::size_of::<U::IntBitMask>(), core::mem::size_of::<U::BitMask>());
let mask: U::IntBitMask = crate::intrinsics::simd_bitmask(self.0);
core::mem::transmute_copy(&mask)
}
}
}
impl<const LANES: usize> core::convert::From<$name<LANES>> for crate::$type<LANES>
impl<T: Mask, const LANES: usize> core::convert::From<$name<T, LANES>> for crate::$type<LANES>
where
crate::$type<LANES>: crate::LanesAtMost32,
{
fn from(value: $name<LANES>) -> Self {
fn from(value: $name<T, LANES>) -> Self {
value.0
}
}
impl<const LANES: usize> core::ops::BitAnd for $name<LANES>
impl<T: Mask, const LANES: usize> core::ops::BitAnd for $name<T, LANES>
where
crate::$type<LANES>: crate::LanesAtMost32,
{
type Output = Self;
#[inline]
fn bitand(self, rhs: Self) -> Self {
Self(self.0 & rhs.0)
Self(self.0 & rhs.0, PhantomData)
}
}
impl<const LANES: usize> core::ops::BitOr for $name<LANES>
impl<T: Mask, const LANES: usize> core::ops::BitOr for $name<T, LANES>
where
crate::$type<LANES>: crate::LanesAtMost32,
{
type Output = Self;
#[inline]
fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
Self(self.0 | rhs.0, PhantomData)
}
}
impl<const LANES: usize> core::ops::BitXor for $name<LANES>
impl<T: Mask, const LANES: usize> core::ops::BitXor for $name<T, LANES>
where
crate::$type<LANES>: crate::LanesAtMost32,
{
type Output = Self;
#[inline]
fn bitxor(self, rhs: Self) -> Self::Output {
Self(self.0 ^ rhs.0)
Self(self.0 ^ rhs.0, PhantomData)
}
}
impl<const LANES: usize> core::ops::Not for $name<LANES>
impl<T: Mask, const LANES: usize> core::ops::Not for $name<T, LANES>
where
crate::$type<LANES>: crate::LanesAtMost32,
{
type Output = $name<LANES>;
type Output = Self;
#[inline]
fn not(self) -> Self::Output {
Self(!self.0)
}
}
impl<const LANES: usize> core::ops::BitAndAssign for $name<LANES>
where
crate::$type<LANES>: crate::LanesAtMost32,
{
#[inline]
fn bitand_assign(&mut self, rhs: Self) {
self.0 &= rhs.0;
}
}
impl<const LANES: usize> core::ops::BitOrAssign for $name<LANES>
where
crate::$type<LANES>: crate::LanesAtMost32,
{
#[inline]
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl<const LANES: usize> core::ops::BitXorAssign for $name<LANES>
where
crate::$type<LANES>: crate::LanesAtMost32,
{
#[inline]
fn bitxor_assign(&mut self, rhs: Self) {
self.0 ^= rhs.0;
Self(!self.0, PhantomData)
}
}
}
+93 -53
View File
@@ -2,16 +2,30 @@
//! Types representing
#![allow(non_camel_case_types)]
#[cfg_attr(not(all(target_arch = "x86_64", target_feature = "avx512f")), path = "full_masks.rs")]
#[cfg_attr(all(target_arch = "x86_64", target_feature = "avx512f"), path = "bitmask.rs")]
#[cfg_attr(
not(all(target_arch = "x86_64", target_feature = "avx512f")),
path = "full_masks.rs"
)]
#[cfg_attr(
all(target_arch = "x86_64", target_feature = "avx512f"),
path = "bitmask.rs"
)]
mod mask_impl;
use crate::{LanesAtMost32, SimdI16, SimdI32, SimdI64, SimdI8, SimdIsize};
/// Converts masks to bitmasks, with one bit set for each lane.
pub trait ToBitMask {
/// Converts this mask to a bitmask.
fn to_bitmask(self) -> u64;
mod sealed {
pub trait Sealed {}
}
/// Helper trait for mask types.
pub trait Mask: sealed::Sealed {
/// The bitmask representation of a mask.
type BitMask: Copy + Default + AsRef<[u8]> + AsMut<[u8]>;
// TODO remove this when rustc intrinsics are more flexible
#[doc(hidden)]
type IntBitMask;
}
macro_rules! define_opaque_mask {
@@ -22,13 +36,47 @@ macro_rules! define_opaque_mask {
} => {
$(#[$attr])*
#[allow(non_camel_case_types)]
pub struct $name<const LANES: usize>($inner_ty) where $bits_ty<LANES>: LanesAtMost32;
pub struct $name<const LANES: usize>($inner_ty)
where
$bits_ty<LANES>: LanesAtMost32,
Self: Mask;
impl<const LANES: usize> sealed::Sealed for $name<LANES>
where
$bits_ty<LANES>: LanesAtMost32,
Self: Mask,
{}
impl Mask for $name<1> {
type BitMask = [u8; 1];
type IntBitMask = u8;
}
impl Mask for $name<2> {
type BitMask = [u8; 1];
type IntBitMask = u8;
}
impl Mask for $name<4> {
type BitMask = [u8; 1];
type IntBitMask = u8;
}
impl Mask for $name<8> {
type BitMask = [u8; 1];
type IntBitMask = u8;
}
impl Mask for $name<16> {
type BitMask = [u8; 2];
type IntBitMask = u16;
}
impl Mask for $name<32> {
type BitMask = [u8; 4];
type IntBitMask = u32;
}
impl_opaque_mask_reductions! { $name, $bits_ty }
impl<const LANES: usize> $name<LANES>
where
$bits_ty<LANES>: LanesAtMost32
$bits_ty<LANES>: LanesAtMost32,
Self: Mask,
{
/// Construct a mask by setting all lanes to the given value.
pub fn splat(value: bool) -> Self {
@@ -125,48 +173,18 @@ macro_rules! define_opaque_mask {
assert!(lane < LANES, "lane index out of range");
unsafe { self.set_unchecked(lane, value); }
}
}
impl ToBitMask for $name<1> {
fn to_bitmask(self) -> u64 {
self.0.to_bitmask()
}
}
impl ToBitMask for $name<2> {
fn to_bitmask(self) -> u64 {
self.0.to_bitmask()
}
}
impl ToBitMask for $name<4> {
fn to_bitmask(self) -> u64 {
self.0.to_bitmask()
}
}
impl ToBitMask for $name<8> {
fn to_bitmask(self) -> u64 {
self.0.to_bitmask()
}
}
impl ToBitMask for $name<16> {
fn to_bitmask(self) -> u64 {
self.0.to_bitmask()
}
}
impl ToBitMask for $name<32> {
fn to_bitmask(self) -> u64 {
self.0.to_bitmask()
/// Convert this mask to a bitmask, with one bit set per lane.
pub fn to_bitmask(self) -> <Self as Mask>::BitMask {
self.0.to_bitmask::<Self>()
}
}
// vector/array conversion
impl<const LANES: usize> From<[bool; LANES]> for $name<LANES>
where
$bits_ty<LANES>: crate::LanesAtMost32
$bits_ty<LANES>: crate::LanesAtMost32,
Self: Mask,
{
fn from(array: [bool; LANES]) -> Self {
Self::from_array(array)
@@ -175,7 +193,8 @@ macro_rules! define_opaque_mask {
impl <const LANES: usize> From<$name<LANES>> for [bool; LANES]
where
$bits_ty<LANES>: crate::LanesAtMost32
$bits_ty<LANES>: crate::LanesAtMost32,
$name<LANES>: Mask,
{
fn from(vector: $name<LANES>) -> Self {
vector.to_array()
@@ -184,13 +203,14 @@ macro_rules! define_opaque_mask {
impl<const LANES: usize> Copy for $name<LANES>
where
$inner_ty: Copy,
$bits_ty<LANES>: LanesAtMost32,
Self: Mask,
{}
impl<const LANES: usize> Clone for $name<LANES>
where
$bits_ty<LANES>: LanesAtMost32,
Self: Mask,
{
#[inline]
fn clone(&self) -> Self {
@@ -201,6 +221,7 @@ macro_rules! define_opaque_mask {
impl<const LANES: usize> Default for $name<LANES>
where
$bits_ty<LANES>: LanesAtMost32,
Self: Mask,
{
#[inline]
fn default() -> Self {
@@ -211,6 +232,7 @@ macro_rules! define_opaque_mask {
impl<const LANES: usize> PartialEq for $name<LANES>
where
$bits_ty<LANES>: LanesAtMost32,
Self: Mask,
{
#[inline]
fn eq(&self, other: &Self) -> bool {
@@ -221,6 +243,7 @@ macro_rules! define_opaque_mask {
impl<const LANES: usize> PartialOrd for $name<LANES>
where
$bits_ty<LANES>: LanesAtMost32,
Self: Mask,
{
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
@@ -231,6 +254,7 @@ macro_rules! define_opaque_mask {
impl<const LANES: usize> core::fmt::Debug for $name<LANES>
where
$bits_ty<LANES>: crate::LanesAtMost32,
Self: Mask,
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_list()
@@ -242,6 +266,7 @@ macro_rules! define_opaque_mask {
impl<const LANES: usize> core::ops::BitAnd for $name<LANES>
where
$bits_ty<LANES>: LanesAtMost32,
Self: Mask,
{
type Output = Self;
#[inline]
@@ -253,6 +278,7 @@ macro_rules! define_opaque_mask {
impl<const LANES: usize> core::ops::BitAnd<bool> for $name<LANES>
where
$bits_ty<LANES>: LanesAtMost32,
Self: Mask,
{
type Output = Self;
#[inline]
@@ -264,6 +290,7 @@ macro_rules! define_opaque_mask {
impl<const LANES: usize> core::ops::BitAnd<$name<LANES>> for bool
where
$bits_ty<LANES>: LanesAtMost32,
$name<LANES>: Mask,
{
type Output = $name<LANES>;
#[inline]
@@ -275,6 +302,7 @@ macro_rules! define_opaque_mask {
impl<const LANES: usize> core::ops::BitOr for $name<LANES>
where
$bits_ty<LANES>: LanesAtMost32,
Self: Mask,
{
type Output = Self;
#[inline]
@@ -286,6 +314,7 @@ macro_rules! define_opaque_mask {
impl<const LANES: usize> core::ops::BitOr<bool> for $name<LANES>
where
$bits_ty<LANES>: LanesAtMost32,
Self: Mask,
{
type Output = Self;
#[inline]
@@ -297,6 +326,7 @@ macro_rules! define_opaque_mask {
impl<const LANES: usize> core::ops::BitOr<$name<LANES>> for bool
where
$bits_ty<LANES>: LanesAtMost32,
$name<LANES>: Mask,
{
type Output = $name<LANES>;
#[inline]
@@ -308,6 +338,7 @@ macro_rules! define_opaque_mask {
impl<const LANES: usize> core::ops::BitXor for $name<LANES>
where
$bits_ty<LANES>: LanesAtMost32,
Self: Mask,
{
type Output = Self;
#[inline]
@@ -319,6 +350,7 @@ macro_rules! define_opaque_mask {
impl<const LANES: usize> core::ops::BitXor<bool> for $name<LANES>
where
$bits_ty<LANES>: LanesAtMost32,
Self: Mask,
{
type Output = Self;
#[inline]
@@ -330,6 +362,7 @@ macro_rules! define_opaque_mask {
impl<const LANES: usize> core::ops::BitXor<$name<LANES>> for bool
where
$bits_ty<LANES>: LanesAtMost32,
$name<LANES>: Mask,
{
type Output = $name<LANES>;
#[inline]
@@ -341,6 +374,7 @@ macro_rules! define_opaque_mask {
impl<const LANES: usize> core::ops::Not for $name<LANES>
where
$bits_ty<LANES>: LanesAtMost32,
Self: Mask,
{
type Output = $name<LANES>;
#[inline]
@@ -352,16 +386,18 @@ macro_rules! define_opaque_mask {
impl<const LANES: usize> core::ops::BitAndAssign for $name<LANES>
where
$bits_ty<LANES>: LanesAtMost32,
Self: Mask,
{
#[inline]
fn bitand_assign(&mut self, rhs: Self) {
self.0 &= rhs.0;
self.0 = self.0 & rhs.0;
}
}
impl<const LANES: usize> core::ops::BitAndAssign<bool> for $name<LANES>
where
$bits_ty<LANES>: LanesAtMost32,
Self: Mask,
{
#[inline]
fn bitand_assign(&mut self, rhs: bool) {
@@ -372,16 +408,18 @@ macro_rules! define_opaque_mask {
impl<const LANES: usize> core::ops::BitOrAssign for $name<LANES>
where
$bits_ty<LANES>: LanesAtMost32,
Self: Mask,
{
#[inline]
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
self.0 = self.0 | rhs.0;
}
}
impl<const LANES: usize> core::ops::BitOrAssign<bool> for $name<LANES>
where
$bits_ty<LANES>: LanesAtMost32,
Self: Mask,
{
#[inline]
fn bitor_assign(&mut self, rhs: bool) {
@@ -392,16 +430,18 @@ macro_rules! define_opaque_mask {
impl<const LANES: usize> core::ops::BitXorAssign for $name<LANES>
where
$bits_ty<LANES>: LanesAtMost32,
Self: Mask,
{
#[inline]
fn bitxor_assign(&mut self, rhs: Self) {
self.0 ^= rhs.0;
self.0 = self.0 ^ rhs.0;
}
}
impl<const LANES: usize> core::ops::BitXorAssign<bool> for $name<LANES>
where
$bits_ty<LANES>: LanesAtMost32,
Self: Mask,
{
#[inline]
fn bitxor_assign(&mut self, rhs: bool) {
@@ -415,7 +455,7 @@ define_opaque_mask! {
/// Mask for vectors with `LANES` 8-bit elements.
///
/// The layout of this type is unspecified.
struct Mask8<const LANES: usize>(mask_impl::Mask8<LANES>);
struct Mask8<const LANES: usize>(mask_impl::Mask8<Self, LANES>);
@bits SimdI8
}
@@ -423,7 +463,7 @@ define_opaque_mask! {
/// Mask for vectors with `LANES` 16-bit elements.
///
/// The layout of this type is unspecified.
struct Mask16<const LANES: usize>(mask_impl::Mask16<LANES>);
struct Mask16<const LANES: usize>(mask_impl::Mask16<Self, LANES>);
@bits SimdI16
}
@@ -431,7 +471,7 @@ define_opaque_mask! {
/// Mask for vectors with `LANES` 32-bit elements.
///
/// The layout of this type is unspecified.
struct Mask32<const LANES: usize>(mask_impl::Mask32<LANES>);
struct Mask32<const LANES: usize>(mask_impl::Mask32<Self, LANES>);
@bits SimdI32
}
@@ -439,7 +479,7 @@ define_opaque_mask! {
/// Mask for vectors with `LANES` 64-bit elements.
///
/// The layout of this type is unspecified.
struct Mask64<const LANES: usize>(mask_impl::Mask64<LANES>);
struct Mask64<const LANES: usize>(mask_impl::Mask64<Self, LANES>);
@bits SimdI64
}
@@ -447,7 +487,7 @@ define_opaque_mask! {
/// Mask for vectors with `LANES` pointer-width elements.
///
/// The layout of this type is unspecified.
struct MaskSize<const LANES: usize>(mask_impl::MaskSize<LANES>);
struct MaskSize<const LANES: usize>(mask_impl::MaskSize<Self, LANES>);
@bits SimdIsize
}
+3 -2
View File
@@ -104,7 +104,7 @@ macro_rules! impl_float_reductions {
macro_rules! impl_full_mask_reductions {
{ $name:ident, $bits_ty:ident } => {
impl<const LANES: usize> $name<LANES>
impl<T: crate::Mask, const LANES: usize> $name<T, LANES>
where
crate::$bits_ty<LANES>: crate::LanesAtMost32
{
@@ -125,7 +125,8 @@ macro_rules! impl_opaque_mask_reductions {
{ $name:ident, $bits_ty:ident } => {
impl<const LANES: usize> $name<LANES>
where
crate::$bits_ty<LANES>: crate::LanesAtMost32
crate::$bits_ty<LANES>: crate::LanesAtMost32,
$name<LANES>: crate::Mask,
{
/// Returns true if any lane is set, or false otherwise.
#[inline]
+1
View File
@@ -42,6 +42,7 @@ macro_rules! impl_float_vector {
Self: crate::LanesAtMost32,
crate::$bits_ty<LANES>: crate::LanesAtMost32,
crate::$mask_impl_ty<LANES>: crate::LanesAtMost32,
crate::$mask_ty<LANES>: crate::Mask,
{
/// Returns true for each lane if it has a positive sign, including
/// `+0.0`, `NaN`s with positive sign bit and positive infinity.
+1
View File
@@ -30,6 +30,7 @@ macro_rules! impl_integer_vector {
where
Self: crate::LanesAtMost32,
crate::$mask_impl_ty<LANES>: crate::LanesAtMost32,
crate::$mask_ty<LANES>: crate::Mask,
{
/// Returns true for each positive lane and false if it is zero or negative.
pub fn is_positive(self) -> crate::$mask_ty<LANES> {
-1
View File
@@ -1,6 +1,5 @@
#![allow(non_camel_case_types)]
/// 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 } => {
+6 -4
View File
@@ -68,10 +68,12 @@ macro_rules! test_mask_api {
#[test]
fn to_bitmask() {
use core_simd::ToBitMask;
let values = [true, false, false, true, false, false, true, false];
let mask = core_simd::$name::<8>::from_array(values);
assert_eq!(mask.to_bitmask(), 0b01001001);
let values = [
true, false, false, true, false, false, true, false,
false, false, false, false, false, false, false, false,
];
let mask = core_simd::$name::<16>::from_array(values);
assert_eq!(mask.to_bitmask(), [0b01001001, 0]);
}
}
}
+1 -4
View File
@@ -3,14 +3,11 @@
// Adapted from proptest's array code
// Copyright 2017 Jason Lingle
use core::{marker::PhantomData, mem::MaybeUninit};
use proptest::{
strategy::{NewTree, Strategy, ValueTree},
test_runner::TestRunner,
};
use core::{
marker::PhantomData,
mem::MaybeUninit,
};
#[must_use = "strategies do nothing unless used"]
#[derive(Clone, Copy, Debug)]
+10
View File
@@ -281,6 +281,11 @@ macro_rules! test_lanes {
core_simd::SimdIsize<$lanes>: core_simd::LanesAtMost32,
core_simd::SimdF32<$lanes>: core_simd::LanesAtMost32,
core_simd::SimdF64<$lanes>: core_simd::LanesAtMost32,
core_simd::Mask8<$lanes>: core_simd::Mask,
core_simd::Mask16<$lanes>: core_simd::Mask,
core_simd::Mask32<$lanes>: core_simd::Mask,
core_simd::Mask64<$lanes>: core_simd::Mask,
core_simd::MaskSize<$lanes>: core_simd::Mask,
$body
#[cfg(target_arch = "wasm32")]
@@ -350,6 +355,11 @@ macro_rules! test_lanes_panic {
core_simd::SimdIsize<$lanes>: core_simd::LanesAtMost32,
core_simd::SimdF32<$lanes>: core_simd::LanesAtMost32,
core_simd::SimdF64<$lanes>: core_simd::LanesAtMost32,
core_simd::Mask8<$lanes>: core_simd::Mask,
core_simd::Mask16<$lanes>: core_simd::Mask,
core_simd::Mask32<$lanes>: core_simd::Mask,
core_simd::Mask64<$lanes>: core_simd::Mask,
core_simd::MaskSize<$lanes>: core_simd::Mask,
$body
#[test]