From 92293aff9d44c74a57f85edf9af0f1b449f20ff0 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Thu, 4 Feb 2021 00:31:57 -0500 Subject: [PATCH 1/3] Add bitmask that supports up to 64 lanes. Simplify mask op API. --- crates/core_simd/src/lib.rs | 8 +- crates/core_simd/src/masks/bitmask.rs | 220 ++++++++++++++ crates/core_simd/src/masks/full_masks.rs | 301 +++++++++++++++++++ crates/core_simd/src/masks/full_masks/mod.rs | 205 ------------- crates/core_simd/src/masks/mod.rs | 252 ++++++++-------- 5 files changed, 645 insertions(+), 341 deletions(-) create mode 100644 crates/core_simd/src/masks/bitmask.rs create mode 100644 crates/core_simd/src/masks/full_masks.rs delete mode 100644 crates/core_simd/src/masks/full_masks/mod.rs diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs index 62489e4fbd50b..de9cb566022e2 100644 --- a/crates/core_simd/src/lib.rs +++ b/crates/core_simd/src/lib.rs @@ -1,12 +1,6 @@ #![no_std] #![allow(incomplete_features)] -#![feature( - repr_simd, - platform_intrinsics, - link_llvm_intrinsics, - simd_ffi, - const_generics -)] +#![feature(repr_simd, platform_intrinsics, simd_ffi, const_generics)] #![warn(missing_docs)] //! Portable SIMD module. diff --git a/crates/core_simd/src/masks/bitmask.rs b/crates/core_simd/src/masks/bitmask.rs new file mode 100644 index 0000000000000..51ed8037043da --- /dev/null +++ b/crates/core_simd/src/masks/bitmask.rs @@ -0,0 +1,220 @@ +/// Implemented for bitmask sizes that are supported by the implementation. +pub trait LanesAtMost64 {} +impl LanesAtMost64 for BitMask<1> {} +impl LanesAtMost64 for BitMask<2> {} +impl LanesAtMost64 for BitMask<4> {} +impl LanesAtMost64 for BitMask<8> {} +impl LanesAtMost64 for BitMask<16> {} +impl LanesAtMost64 for BitMask<32> {} +impl LanesAtMost64 for BitMask<64> {} + +/// A mask where each lane is represented by a single bit. +#[derive(Copy, Clone, Debug)] +#[repr(transparent)] +pub struct BitMask(u64) +where + BitMask: LanesAtMost64; + +impl BitMask +where + Self: LanesAtMost64, +{ + /// Construct a mask by setting all lanes to the given value. + pub fn splat(value: bool) -> Self { + if value { + Self(u64::MAX) + } else { + Self(u64::MIN) + } + } + + /// Tests the value of the specified lane. + /// + /// # Panics + /// Panics if `lane` is greater than or equal to the number of lanes in the vector. + #[inline] + pub fn test(&self, lane: usize) -> bool { + assert!(lane < LANES, "lane index out of range"); + (self.0 >> lane) & 0x1 > 0 + } + + /// Sets the value of the specified lane. + /// + /// # Panics + /// Panics if `lane` is greater than or equal to the number of lanes in the vector. + #[inline] + pub fn set(&mut self, lane: usize, value: bool) { + assert!(lane < LANES, "lane index out of range"); + self.0 ^= ((value ^ self.test(lane)) as u64) << lane + } +} + +impl core::ops::BitAnd for BitMask +where + Self: LanesAtMost64, +{ + type Output = Self; + #[inline] + fn bitand(self, rhs: Self) -> Self { + Self(self.0 & rhs.0) + } +} + +impl core::ops::BitAnd for BitMask +where + Self: LanesAtMost64, +{ + type Output = Self; + #[inline] + fn bitand(self, rhs: bool) -> Self { + self & Self::splat(rhs) + } +} + +impl core::ops::BitAnd> for bool +where + BitMask: LanesAtMost64, +{ + type Output = BitMask; + #[inline] + fn bitand(self, rhs: BitMask) -> BitMask { + BitMask::::splat(self) & rhs + } +} + +impl core::ops::BitOr for BitMask +where + Self: LanesAtMost64, +{ + type Output = Self; + #[inline] + fn bitor(self, rhs: Self) -> Self { + Self(self.0 | rhs.0) + } +} + +impl core::ops::BitOr for BitMask +where + Self: LanesAtMost64, +{ + type Output = Self; + #[inline] + fn bitor(self, rhs: bool) -> Self { + self | Self::splat(rhs) + } +} + +impl core::ops::BitOr> for bool +where + BitMask: LanesAtMost64, +{ + type Output = BitMask; + #[inline] + fn bitor(self, rhs: BitMask) -> BitMask { + BitMask::::splat(self) | rhs + } +} + +impl core::ops::BitXor for BitMask +where + Self: LanesAtMost64, +{ + type Output = Self; + #[inline] + fn bitxor(self, rhs: Self) -> Self::Output { + Self(self.0 ^ rhs.0) + } +} + +impl core::ops::BitXor for BitMask +where + Self: LanesAtMost64, +{ + type Output = Self; + #[inline] + fn bitxor(self, rhs: bool) -> Self::Output { + self ^ Self::splat(rhs) + } +} + +impl core::ops::BitXor> for bool +where + BitMask: LanesAtMost64, +{ + type Output = BitMask; + #[inline] + fn bitxor(self, rhs: BitMask) -> Self::Output { + BitMask::::splat(self) ^ rhs + } +} + +impl core::ops::Not for BitMask +where + Self: LanesAtMost64, +{ + type Output = BitMask; + #[inline] + fn not(self) -> Self::Output { + Self(!self.0) + } +} + +impl core::ops::BitAndAssign for BitMask +where + Self: LanesAtMost64, +{ + #[inline] + fn bitand_assign(&mut self, rhs: Self) { + self.0 &= rhs.0; + } +} + +impl core::ops::BitAndAssign for BitMask +where + Self: LanesAtMost64, +{ + #[inline] + fn bitand_assign(&mut self, rhs: bool) { + *self &= Self::splat(rhs); + } +} + +impl core::ops::BitOrAssign for BitMask +where + Self: LanesAtMost64, +{ + #[inline] + fn bitor_assign(&mut self, rhs: Self) { + self.0 |= rhs.0; + } +} + +impl core::ops::BitOrAssign for BitMask +where + Self: LanesAtMost64, +{ + #[inline] + fn bitor_assign(&mut self, rhs: bool) { + *self |= Self::splat(rhs); + } +} + +impl core::ops::BitXorAssign for BitMask +where + Self: LanesAtMost64, +{ + #[inline] + fn bitxor_assign(&mut self, rhs: Self) { + self.0 ^= rhs.0; + } +} + +impl core::ops::BitXorAssign for BitMask +where + Self: LanesAtMost64, +{ + #[inline] + fn bitxor_assign(&mut self, rhs: bool) { + *self ^= Self::splat(rhs); + } +} diff --git a/crates/core_simd/src/masks/full_masks.rs b/crates/core_simd/src/masks/full_masks.rs new file mode 100644 index 0000000000000..d7c4af47727ca --- /dev/null +++ b/crates/core_simd/src/masks/full_masks.rs @@ -0,0 +1,301 @@ +//! Masks that take up full SIMD vector registers. + +/// The error type returned when converting an integer to a mask fails. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct TryFromMaskError(()); + +impl core::fmt::Display for TryFromMaskError { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!( + f, + "mask vector must have all bits set or unset in each lane" + ) + } +} + +macro_rules! define_mask { + { $(#[$attr:meta])* struct $name:ident($type:ty); } => { + $(#[$attr])* + #[derive(Copy, Clone, Default, PartialEq, PartialOrd, Eq, Ord, Hash)] + #[repr(transparent)] + pub struct $name($type); + + impl $name<$lanes> { + /// Construct a mask by setting all lanes to the given value. + pub fn splat(value: bool) -> Self { + Self(<$type>::splat( + if value { + -1 + } else { + 0 + } + )) + } + + /// Tests the value of the specified lane. + /// + /// # Panics + /// Panics if `lane` is greater than or equal to the number of lanes in the vector. + #[inline] + pub fn test(&self, lane: usize) -> bool { + assert!(lane < LANES, "lane index out of range"); + self.0[lane] == -1 + } + + /// Sets the value of the specified lane. + /// + /// # Panics + /// Panics if `lane` is greater than or equal to the number of lanes in the vector. + #[inline] + pub fn set(&mut self, lane: usize, value: bool) { + assert!(lane < LANES, "lane index out of range"); + self.0[lane] = if value { + -1 + } else { + 0 + } + } + } + + impl core::convert::From for $name<$lanes> { + fn from(value: bool) -> Self { + Self::splat(value) + } + } + + impl core::convert::TryFrom<$type> for $name<$lanes> { + type Error = TryFromMaskError; + fn try_from(value: $type) -> Result { + if value.as_slice().iter().all(|x| *x == 0 || *x == -1) { + Ok(Self(value)) + } else { + Err(TryFromMaskError(())) + } + } + } + + impl core::convert::From<$name<$lanes>> for $type { + fn from(value: $name<$lanes>) -> Self { + value.0 + } + } + + impl core::convert::From> for $name<$lanes> + where + crate::BitMask<$lanes>: crate::LanesAtMost64, + { + fn from(value: crate::BitMask<$lanes>) -> Self { + // TODO use an intrinsic to do this efficiently (with LLVM's sext instruction) + let mut mask = Self::splat(false); + for lane in 0..LANES { + mask.set(lane, value.test(lane)); + } + mask + } + } + + impl core::convert::From<$name<$lanes>> for crate::BitMask<$lanes> + where + crate::BitMask<$lanes>: crate::LanesAtMost64, + { + fn from(value: $name<$lanes>) -> Self { + // TODO use an intrinsic to do this efficiently (with LLVM's trunc instruction) + let mut mask = Self::splat(false); + for lane in 0..LANES { + mask.set(lane, value.test(lane)); + } + mask + } + } + + impl core::fmt::Debug for $name<$lanes> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + f.debug_list() + .entries((0..LANES).map(|lane| self.test(lane))) + .finish() + } + } + + impl core::fmt::Binary for $name<$lanes> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + core::fmt::Binary::fmt(&self.0, f) + } + } + + impl core::fmt::Octal for $name<$lanes> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + core::fmt::Octal::fmt(&self.0, f) + } + } + + impl core::fmt::LowerHex for $name<$lanes> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + core::fmt::LowerHex::fmt(&self.0, f) + } + } + + impl core::fmt::UpperHex for $name<$lanes> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + core::fmt::UpperHex::fmt(&self.0, f) + } + } + + impl core::ops::BitAnd for $name { + type Output = Self; + #[inline] + fn bitand(self, rhs: Self) -> Self { + Self(self.0 & rhs.0) + } + } + + impl core::ops::BitAnd for $name { + type Output = Self; + #[inline] + fn bitand(self, rhs: bool) -> Self { + self & Self::splat(rhs) + } + } + + impl core::ops::BitAnd<$name> for bool { + type Output = $name; + #[inline] + fn bitand(self, rhs: $name) -> $name { + $name::::splat(self) & rhs + } + } + + impl core::ops::BitOr for $name { + type Output = Self; + #[inline] + fn bitor(self, rhs: Self) -> Self { + Self(self.0 | rhs.0) + } + } + + impl core::ops::BitOr for $name { + type Output = Self; + #[inline] + fn bitor(self, rhs: bool) -> Self { + self | Self::splat(rhs) + } + } + + impl core::ops::BitOr<$name> for bool { + type Output = $name; + #[inline] + fn bitor(self, rhs: $name) -> $name { + $name::::splat(self) | rhs + } + } + + impl core::ops::BitXor for $name { + type Output = Self; + #[inline] + fn bitxor(self, rhs: Self) -> Self::Output { + Self(self.0 ^ rhs.0) + } + } + + impl core::ops::BitXor for $name { + type Output = Self; + #[inline] + fn bitxor(self, rhs: bool) -> Self::Output { + self ^ Self::splat(rhs) + } + } + + impl core::ops::BitXor<$name> for bool { + type Output = $name; + #[inline] + fn bitxor(self, rhs: $name) -> Self::Output { + $name::::splat(self) ^ rhs + } + } + + impl core::ops::Not for $name { + type Output = $name; + #[inline] + fn not(self) -> Self::Output { + Self(!self.0) + } + } + + impl core::ops::BitAndAssign for $name { + #[inline] + fn bitand_assign(&mut self, rhs: Self) { + self.0 &= rhs.0; + } + } + + impl core::ops::BitAndAssign for $name { + #[inline] + fn bitand_assign(&mut self, rhs: bool) { + *self &= Self::splat(rhs); + } + } + + impl core::ops::BitOrAssign for $name { + #[inline] + fn bitor_assign(&mut self, rhs: Self) { + self.0 |= rhs.0; + } + } + + impl core::ops::BitOrAssign for $name { + #[inline] + fn bitor_assign(&mut self, rhs: bool) { + *self |= Self::splat(rhs); + } + } + + impl core::ops::BitXorAssign for $name { + #[inline] + fn bitxor_assign(&mut self, rhs: Self) { + self.0 ^= rhs.0; + } + } + + impl core::ops::BitXorAssign for $name { + #[inline] + fn bitxor_assign(&mut self, rhs: bool) { + *self ^= Self::splat(rhs); + } + } + } +} + +define_mask! { + /// A mask equivalent to [SimdI8](crate::SimdI8), where all bits in the lane must be either set + /// or unset. + struct SimdMask8(crate::SimdI8); +} + +define_mask! { + /// A mask equivalent to [SimdI16](crate::SimdI16), where all bits in the lane must be either set + /// or unset. + struct SimdMask16(crate::SimdI16); +} + +define_mask! { + /// A mask equivalent to [SimdI32](crate::SimdI32), where all bits in the lane must be either set + /// or unset. + struct SimdMask32(crate::SimdI32); +} + +define_mask! { + /// A mask equivalent to [SimdI64](crate::SimdI64), where all bits in the lane must be either set + /// or unset. + struct SimdMask64(crate::SimdI64); +} + +define_mask! { + /// A mask equivalent to [SimdI128](crate::SimdI128), where all bits in the lane must be either set + /// or unset. + struct SimdMask128(crate::SimdI64); +} + +define_mask! { + /// A mask equivalent to [SimdIsize](crate::SimdIsize), where all bits in the lane must be either set + /// or unset. + struct SimdMaskSize(crate::SimdI64); +} diff --git a/crates/core_simd/src/masks/full_masks/mod.rs b/crates/core_simd/src/masks/full_masks/mod.rs deleted file mode 100644 index eac5178512e52..0000000000000 --- a/crates/core_simd/src/masks/full_masks/mod.rs +++ /dev/null @@ -1,205 +0,0 @@ -//! Masks that take up full SIMD vector registers. - -/// The error type returned when converting an integer to a mask fails. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct TryFromMaskError(()); - -impl core::fmt::Display for TryFromMaskError { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - write!(f, "mask vector must have all bits set or unset in each lane") - } -} - -macro_rules! define_mask { - { $(#[$attr:meta])* struct $name:ident($type:ty); } => { - $(#[$attr])* - #[derive(Copy, Clone, Default, PartialEq, PartialOrd, Eq, Ord, Hash)] - #[repr(transparent)] - pub struct $name($type); - - delegate_ops_to_inner! { $name } - - impl $name<$lanes> { - /// Construct a mask by setting all lanes to the given value. - pub fn splat(value: bool) -> Self { - Self(<$type>::splat( - if value { - -1 - } else { - 0 - } - )) - } - - /// Tests the value of the specified lane. - /// - /// # Panics - /// Panics if `lane` is greater than or equal to the number of lanes in the vector. - #[inline] - pub fn test(&self, lane: usize) -> bool { - self.0[lane] == -1 - } - - /// Sets the value of the specified lane. - /// - /// # Panics - /// Panics if `lane` is greater than or equal to the number of lanes in the vector. - #[inline] - pub fn set(&mut self, lane: usize, value: bool) { - self.0[lane] = if value { - -1 - } else { - 0 - } - } - } - - impl core::convert::From for $name<$lanes> { - fn from(value: bool) -> Self { - Self::splat(value) - } - } - - impl core::convert::TryFrom<$type> for $name<$lanes> { - type Error = TryFromMaskError; - fn try_from(value: $type) -> Result { - if value.as_slice().iter().all(|x| *x == 0 || *x == -1) { - Ok(Self(value)) - } else { - Err(TryFromMaskError(())) - } - } - } - - impl core::convert::From<$name<$lanes>> for $type { - fn from(value: $name<$lanes>) -> Self { - value.0 - } - } - - impl core::fmt::Debug for $name<$lanes> { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - f.debug_list() - .entries((0..LANES).map(|lane| self.test(lane))) - .finish() - } - } - - impl core::fmt::Binary for $name<$lanes> { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - core::fmt::Binary::fmt(&self.0, f) - } - } - - impl core::fmt::Octal for $name<$lanes> { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - core::fmt::Octal::fmt(&self.0, f) - } - } - - impl core::fmt::LowerHex for $name<$lanes> { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - core::fmt::LowerHex::fmt(&self.0, f) - } - } - - impl core::fmt::UpperHex for $name<$lanes> { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - core::fmt::UpperHex::fmt(&self.0, f) - } - } - } -} - -define_mask! { - /// A mask equivalent to [SimdI8](crate::SimdI8), where all bits in the lane must be either set - /// or unset. - struct SimdI8Mask(crate::SimdI8); -} - -define_mask! { - /// A mask equivalent to [SimdI16](crate::SimdI16), where all bits in the lane must be either set - /// or unset. - struct SimdI16Mask(crate::SimdI16); -} - -define_mask! { - /// A mask equivalent to [SimdI32](crate::SimdI32), where all bits in the lane must be either set - /// or unset. - struct SimdI32Mask(crate::SimdI32); -} - -define_mask! { - /// A mask equivalent to [SimdI64](crate::SimdI64), where all bits in the lane must be either set - /// or unset. - struct SimdI64Mask(crate::SimdI64); -} - -define_mask! { - /// A mask equivalent to [SimdI128](crate::SimdI128), where all bits in the lane must be either set - /// or unset. - struct SimdI128Mask(crate::SimdI64); -} - -define_mask! { - /// A mask equivalent to [SimdIsize](crate::SimdIsize), where all bits in the lane must be either set - /// or unset. - struct SimdIsizeMask(crate::SimdI64); -} - -macro_rules! implement_mask_ext { - { $($vector:ident => $mask:ident,)* } => { - $( - impl crate::masks::MaskExt<$mask> for crate::$vector { - #[inline] - fn lanes_eq(&self, other: &Self) -> $mask { - unsafe { crate::intrinsics::simd_eq(self, other) } - } - - #[inline] - fn lanes_ne(&self, other: &Self) -> $mask { - unsafe { crate::intrinsics::simd_ne(self, other) } - } - - #[inline] - fn lanes_lt(&self, other: &Self) -> $mask { - unsafe { crate::intrinsics::simd_lt(self, other) } - } - - #[inline] - fn lanes_gt(&self, other: &Self) -> $mask { - unsafe { crate::intrinsics::simd_gt(self, other) } - } - - #[inline] - fn lanes_le(&self, other: &Self) -> $mask { - unsafe { crate::intrinsics::simd_le(self, other) } - } - - #[inline] - fn lanes_ge(&self, other: &Self) -> $mask { - unsafe { crate::intrinsics::simd_ge(self, other) } - } - } - )* - } -} - -implement_mask_ext! { - SimdI8 => SimdI8Mask, - SimdI16 => SimdI16Mask, - SimdI32 => SimdI32Mask, - SimdI64 => SimdI64Mask, - SimdI128 => SimdI128Mask, - SimdIsize => SimdIsizeMask, - - SimdU8 => SimdI8Mask, - SimdU16 => SimdI16Mask, - SimdU32 => SimdI32Mask, - SimdU64 => SimdI64Mask, - SimdU128 => SimdI128Mask, - SimdUsize => SimdIsizeMask, - - SimdF32 => SimdI32Mask, - SimdF64 => SimdI64Mask, -} diff --git a/crates/core_simd/src/masks/mod.rs b/crates/core_simd/src/masks/mod.rs index a075040affe72..61e1199095fbc 100644 --- a/crates/core_simd/src/masks/mod.rs +++ b/crates/core_simd/src/masks/mod.rs @@ -1,10 +1,101 @@ //! Types and traits associated with masking lanes of vectors. #![allow(non_camel_case_types)] -/// Implements bitwise ops on mask types by delegating the operators to the inner type. -macro_rules! delegate_ops_to_inner { - { $name:ident } => { - impl core::ops::BitAnd for $name { +mod full_masks; +pub use full_masks::*; + +mod bitmask; +pub use bitmask::*; + +macro_rules! define_opaque_mask { + { + $(#[$attr:meta])* + struct $name:ident($inner_ty:ty); + } => { + $(#[$attr])* + #[allow(non_camel_case_types)] + pub struct $name($inner_ty) where BitMask: LanesAtMost64; + + impl $name<$lanes> where BitMask<$lanes>: LanesAtMost64 { + /// Construct a mask by setting all lanes to the given value. + pub fn splat(value: bool) -> Self { + Self(<$inner_ty>::splat(value)) + } + + /// Tests the value of the specified lane. + /// + /// # Panics + /// Panics if `lane` is greater than or equal to the number of lanes in the vector. + #[inline] + pub fn test(&self, lane: usize) -> bool { + self.0.test(lane) + } + + /// Sets the value of the specified lane. + /// + /// # Panics + /// Panics if `lane` is greater than or equal to the number of lanes in the vector. + #[inline] + pub fn set(&mut self, lane: usize, value: bool) { + self.0.set(lane, value); + } + } + + impl From> for $name<$lanes> + where + BitMask<$lanes>: LanesAtMost64, + { + fn from(value: BitMask<$lanes>) -> Self { + Self(value.into()) + } + } + + impl From<$name<$lanes>> for crate::BitMask<$lanes> + where + BitMask<$lanes>: LanesAtMost64, + { + fn from(value: $name<$lanes>) -> Self { + value.0.into() + } + } + + impl Copy for $name<$lanes> where BitMask<$lanes>: LanesAtMost64 {} + + impl Clone for $name<$lanes> where BitMask<$lanes>: LanesAtMost64 { + #[inline] + fn clone(&self) -> Self { + *self + } + } + + impl Default for $name<$lanes> where BitMask<$lanes>: LanesAtMost64 { + #[inline] + fn default() -> Self { + Self::splat(false) + } + } + + impl PartialEq for $name<$lanes> where BitMask<$lanes>: LanesAtMost64 { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } + } + + impl PartialOrd for $name<$lanes> where BitMask<$lanes>: LanesAtMost64 { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + self.0.partial_cmp(&other.0) + } + } + + impl core::fmt::Debug for $name<$lanes> where BitMask<$lanes>: LanesAtMost64 { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + core::fmt::Debug::fmt(&self.0, f) + } + } + + impl core::ops::BitAnd for $name where BitMask: LanesAtMost64 { type Output = Self; #[inline] fn bitand(self, rhs: Self) -> Self { @@ -12,7 +103,7 @@ macro_rules! delegate_ops_to_inner { } } - impl core::ops::BitAnd for $name { + impl core::ops::BitAnd for $name where BitMask: LanesAtMost64 { type Output = Self; #[inline] fn bitand(self, rhs: bool) -> Self { @@ -20,7 +111,7 @@ macro_rules! delegate_ops_to_inner { } } - impl core::ops::BitAnd<$name> for bool { + impl core::ops::BitAnd<$name> for bool where BitMask: LanesAtMost64 { type Output = $name; #[inline] fn bitand(self, rhs: $name) -> $name { @@ -28,7 +119,7 @@ macro_rules! delegate_ops_to_inner { } } - impl core::ops::BitOr for $name { + impl core::ops::BitOr for $name where BitMask: LanesAtMost64 { type Output = Self; #[inline] fn bitor(self, rhs: Self) -> Self { @@ -36,7 +127,7 @@ macro_rules! delegate_ops_to_inner { } } - impl core::ops::BitOr for $name { + impl core::ops::BitOr for $name where BitMask: LanesAtMost64 { type Output = Self; #[inline] fn bitor(self, rhs: bool) -> Self { @@ -44,7 +135,7 @@ macro_rules! delegate_ops_to_inner { } } - impl core::ops::BitOr<$name> for bool { + impl core::ops::BitOr<$name> for bool where BitMask: LanesAtMost64 { type Output = $name; #[inline] fn bitor(self, rhs: $name) -> $name { @@ -52,7 +143,7 @@ macro_rules! delegate_ops_to_inner { } } - impl core::ops::BitXor for $name { + impl core::ops::BitXor for $name where BitMask: LanesAtMost64 { type Output = Self; #[inline] fn bitxor(self, rhs: Self) -> Self::Output { @@ -60,7 +151,7 @@ macro_rules! delegate_ops_to_inner { } } - impl core::ops::BitXor for $name { + impl core::ops::BitXor for $name where BitMask: LanesAtMost64 { type Output = Self; #[inline] fn bitxor(self, rhs: bool) -> Self::Output { @@ -68,7 +159,7 @@ macro_rules! delegate_ops_to_inner { } } - impl core::ops::BitXor<$name> for bool { + impl core::ops::BitXor<$name> for bool where BitMask: LanesAtMost64 { type Output = $name; #[inline] fn bitxor(self, rhs: $name) -> Self::Output { @@ -76,7 +167,7 @@ macro_rules! delegate_ops_to_inner { } } - impl core::ops::Not for $name { + impl core::ops::Not for $name where BitMask: LanesAtMost64 { type Output = $name; #[inline] fn not(self) -> Self::Output { @@ -84,123 +175,47 @@ macro_rules! delegate_ops_to_inner { } } - impl core::ops::BitAndAssign for $name { + impl core::ops::BitAndAssign for $name where BitMask: LanesAtMost64 { #[inline] fn bitand_assign(&mut self, rhs: Self) { self.0 &= rhs.0; } } - impl core::ops::BitAndAssign for $name { + impl core::ops::BitAndAssign for $name where BitMask: LanesAtMost64 { #[inline] fn bitand_assign(&mut self, rhs: bool) { *self &= Self::splat(rhs); } } - impl core::ops::BitOrAssign for $name { + impl core::ops::BitOrAssign for $name where BitMask: LanesAtMost64 { #[inline] fn bitor_assign(&mut self, rhs: Self) { self.0 |= rhs.0; } } - impl core::ops::BitOrAssign for $name { + impl core::ops::BitOrAssign for $name where BitMask: LanesAtMost64 { #[inline] fn bitor_assign(&mut self, rhs: bool) { *self |= Self::splat(rhs); } } - impl core::ops::BitXorAssign for $name { + impl core::ops::BitXorAssign for $name where BitMask: LanesAtMost64 { #[inline] fn bitxor_assign(&mut self, rhs: Self) { self.0 ^= rhs.0; } } - impl core::ops::BitXorAssign for $name { + impl core::ops::BitXorAssign for $name where BitMask: LanesAtMost64 { #[inline] fn bitxor_assign(&mut self, rhs: bool) { *self ^= Self::splat(rhs); } } - } -} - -pub mod full_masks; - -macro_rules! define_opaque_mask { - { - $(#[$attr:meta])* - struct $name:ident($inner_ty:ty); - } => { - $(#[$attr])* - #[allow(non_camel_case_types)] - pub struct $name($inner_ty); - - delegate_ops_to_inner! { $name } - - impl $name<$lanes> { - /// Construct a mask by setting all lanes to the given value. - pub fn splat(value: bool) -> Self { - Self(<$inner_ty>::splat(value)) - } - - /// Tests the value of the specified lane. - /// - /// # Panics - /// Panics if `lane` is greater than or equal to the number of lanes in the vector. - #[inline] - pub fn test(&self, lane: usize) -> bool { - self.0.test(lane) - } - - /// Sets the value of the specified lane. - /// - /// # Panics - /// Panics if `lane` is greater than or equal to the number of lanes in the vector. - #[inline] - pub fn set(&mut self, lane: usize, value: bool) { - self.0.set(lane, value); - } - } - - impl Copy for $name<$lanes> {} - - impl Clone for $name<$lanes> { - #[inline] - fn clone(&self) -> Self { - *self - } - } - - impl Default for $name<$lanes> { - #[inline] - fn default() -> Self { - Self::splat(false) - } - } - - impl PartialEq for $name<$lanes> { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } - } - - impl PartialOrd for $name<$lanes> { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } - } - - impl core::fmt::Debug for $name<$lanes> { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - core::fmt::Debug::fmt(&self.0, f) - } - } }; } @@ -208,103 +223,82 @@ define_opaque_mask! { /// Mask for vectors with `LANES` 8-bit elements. /// /// The layout of this type is unspecified. - struct Mask8(full_masks::SimdI8Mask); + struct Mask8(SimdMask8); } define_opaque_mask! { /// Mask for vectors with `LANES` 16-bit elements. /// /// The layout of this type is unspecified. - struct Mask16(full_masks::SimdI16Mask); + struct Mask16(SimdMask16); } define_opaque_mask! { /// Mask for vectors with `LANES` 32-bit elements. /// /// The layout of this type is unspecified. - struct Mask32(full_masks::SimdI32Mask); + struct Mask32(SimdMask32); } define_opaque_mask! { /// Mask for vectors with `LANES` 64-bit elements. /// /// The layout of this type is unspecified. - struct Mask64(full_masks::SimdI64Mask); + struct Mask64(SimdMask64); } define_opaque_mask! { /// Mask for vectors with `LANES` 128-bit elements. /// /// The layout of this type is unspecified. - struct Mask128(full_masks::SimdI128Mask); + struct Mask128(SimdMask128); } define_opaque_mask! { /// Mask for vectors with `LANES` pointer-width elements. /// /// The layout of this type is unspecified. - struct MaskSize(full_masks::SimdIsizeMask); -} - -/// Mask-related operations using a particular mask layout. -pub trait MaskExt { - /// Test if each lane is equal to the corresponding lane in `other`. - fn lanes_eq(&self, other: &Self) -> Mask; - - /// Test if each lane is not equal to the corresponding lane in `other`. - fn lanes_ne(&self, other: &Self) -> Mask; - - /// Test if each lane is less than the corresponding lane in `other`. - fn lanes_lt(&self, other: &Self) -> Mask; - - /// Test if each lane is greater than the corresponding lane in `other`. - fn lanes_gt(&self, other: &Self) -> Mask; - - /// Test if each lane is less than or equal to the corresponding lane in `other`. - fn lanes_le(&self, other: &Self) -> Mask; - - /// Test if each lane is greater than or equal to the corresponding lane in `other`. - fn lanes_ge(&self, other: &Self) -> Mask; + struct MaskSize(SimdMaskSize); } macro_rules! implement_mask_ops { { $($vector:ident => $mask:ident,)* } => { $( - impl crate::$vector { + impl crate::$vector where BitMask: LanesAtMost64 { /// Test if each lane is equal to the corresponding lane in `other`. #[inline] pub fn lanes_eq(&self, other: &Self) -> $mask { - $mask(MaskExt::lanes_eq(self, other)) + unsafe { $mask(crate::intrinsics::simd_eq(self, other)) } } /// Test if each lane is not equal to the corresponding lane in `other`. #[inline] pub fn lanes_ne(&self, other: &Self) -> $mask { - $mask(MaskExt::lanes_ne(self, other)) + unsafe { $mask(crate::intrinsics::simd_ne(self, other)) } } /// Test if each lane is less than the corresponding lane in `other`. #[inline] pub fn lanes_lt(&self, other: &Self) -> $mask { - $mask(MaskExt::lanes_lt(self, other)) + unsafe { $mask(crate::intrinsics::simd_lt(self, other)) } } /// Test if each lane is greater than the corresponding lane in `other`. #[inline] pub fn lanes_gt(&self, other: &Self) -> $mask { - $mask(MaskExt::lanes_gt(self, other)) + unsafe { $mask(crate::intrinsics::simd_gt(self, other)) } } /// Test if each lane is less than or equal to the corresponding lane in `other`. #[inline] pub fn lanes_le(&self, other: &Self) -> $mask { - $mask(MaskExt::lanes_le(self, other)) + unsafe { $mask(crate::intrinsics::simd_le(self, other)) } } /// Test if each lane is greater than or equal to the corresponding lane in `other`. #[inline] pub fn lanes_ge(&self, other: &Self) -> $mask { - $mask(MaskExt::lanes_ge(self, other)) + unsafe { $mask(crate::intrinsics::simd_ge(self, other)) } } } )* From 9e96c8a2a8336428aa1995e73901815af02e79cb Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Thu, 4 Feb 2021 20:47:13 -0500 Subject: [PATCH 2/3] Add missing From implementation, add simple mask API tests --- crates/core_simd/src/masks/mod.rs | 18 +++++++++ crates/core_simd/tests/masks.rs | 67 +++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 crates/core_simd/tests/masks.rs diff --git a/crates/core_simd/src/masks/mod.rs b/crates/core_simd/src/masks/mod.rs index 61e1199095fbc..e51052f53f2f5 100644 --- a/crates/core_simd/src/masks/mod.rs +++ b/crates/core_simd/src/masks/mod.rs @@ -59,6 +59,24 @@ macro_rules! define_opaque_mask { } } + impl From<$inner_ty> for $name<$lanes> + where + BitMask<$lanes>: LanesAtMost64, + { + fn from(value: $inner_ty) -> Self { + Self(value) + } + } + + impl From<$name<$lanes>> for $inner_ty + where + BitMask<$lanes>: LanesAtMost64, + { + fn from(value: $name<$lanes>) -> Self { + value.0 + } + } + impl Copy for $name<$lanes> where BitMask<$lanes>: LanesAtMost64 {} impl Clone for $name<$lanes> where BitMask<$lanes>: LanesAtMost64 { diff --git a/crates/core_simd/tests/masks.rs b/crates/core_simd/tests/masks.rs new file mode 100644 index 0000000000000..1ae003fc27fca --- /dev/null +++ b/crates/core_simd/tests/masks.rs @@ -0,0 +1,67 @@ +use core_simd::{BitMask, Mask8, SimdMask8, SimdI8}; +use core::convert::TryFrom; + +#[cfg(target_arch = "wasm32")] +use wasm_bindgen_test::*; + +#[cfg(target_arch = "wasm32")] +wasm_bindgen_test_configure!(run_in_browser); + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn mask_format_round_trip() { + let ints = SimdI8::from_array([-1, 0, 0, -1]); + + let simd_mask = SimdMask8::try_from(ints).unwrap(); + + let bitmask = BitMask::from(simd_mask); + + let opaque_mask = Mask8::from(bitmask); + + let simd_mask_returned = SimdMask8::from(opaque_mask); + + let ints_returned = SimdI8::from(simd_mask_returned); + + assert_eq!(ints_returned, ints); +} + +macro_rules! test_mask_api { + { $name:ident } => { + #[allow(non_snake_case)] + mod $name { + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn set_and_test() { + let values = [true, false, false, true, false, false, true, false]; + let mut mask = core_simd::$name::<8>::splat(false); + for (lane, value) in values.iter().copied().enumerate() { + mask.set(lane, value); + } + for (lane, value) in values.iter().copied().enumerate() { + assert_eq!(mask.test(lane), value); + } + } + + #[test] + #[should_panic] + fn set_invalid_lane() { + let mut mask = core_simd::$name::<8>::splat(false); + mask.set(8, true); + let _ = mask; + } + + #[test] + #[should_panic] + fn test_invalid_lane() { + let mask = core_simd::$name::<8>::splat(false); + let _ = mask.test(8); + } + } + } +} + +mod mask_api { + test_mask_api!{ Mask8 } + test_mask_api!{ SimdMask8 } + test_mask_api!{ BitMask } +} From 26061b4e84f833a100b4cd0978940997ecd72be3 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Tue, 9 Feb 2021 00:09:27 -0500 Subject: [PATCH 3/3] Fix wasm tests --- crates/core_simd/tests/masks.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/crates/core_simd/tests/masks.rs b/crates/core_simd/tests/masks.rs index 1ae003fc27fca..03a835b9c66f7 100644 --- a/crates/core_simd/tests/masks.rs +++ b/crates/core_simd/tests/masks.rs @@ -1,5 +1,5 @@ -use core_simd::{BitMask, Mask8, SimdMask8, SimdI8}; use core::convert::TryFrom; +use core_simd::{BitMask, Mask8, SimdI8, SimdMask8}; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; @@ -29,6 +29,9 @@ macro_rules! test_mask_api { { $name:ident } => { #[allow(non_snake_case)] mod $name { + #[cfg(target_arch = "wasm32")] + use wasm_bindgen_test::*; + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn set_and_test() { @@ -61,7 +64,7 @@ macro_rules! test_mask_api { } mod mask_api { - test_mask_api!{ Mask8 } - test_mask_api!{ SimdMask8 } - test_mask_api!{ BitMask } + test_mask_api! { Mask8 } + test_mask_api! { SimdMask8 } + test_mask_api! { BitMask } }