diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs index db3b9d0e409..747096306ff 100644 --- a/crates/core_simd/src/lib.rs +++ b/crates/core_simd/src/lib.rs @@ -12,11 +12,11 @@ mod permute; #[macro_use] mod transmute; +mod comparisons; mod fmt; mod intrinsics; mod ops; mod round; -mod comparisons; mod math; diff --git a/crates/core_simd/src/masks/mod.rs b/crates/core_simd/src/masks/mod.rs index 945ee9377f1..d30399fb5ad 100644 --- a/crates/core_simd/src/masks/mod.rs +++ b/crates/core_simd/src/masks/mod.rs @@ -28,6 +28,28 @@ macro_rules! define_opaque_mask { Self(<$inner_ty>::splat(value)) } + /// Converts an array to a SIMD vector. + pub fn from_array(array: [bool; LANES]) -> Self { + let mut vector = Self::splat(false); + let mut i = 0; + while i < $lanes { + vector.set(i, array[i]); + i += 1; + } + vector + } + + /// Converts a SIMD vector to an array. + pub fn to_array(self) -> [bool; LANES] { + let mut array = [false; LANES]; + let mut i = 0; + while i < $lanes { + array[i] = self.test(i); + i += 1; + } + array + } + /// Tests the value of the specified lane. /// /// # Panics @@ -85,6 +107,19 @@ macro_rules! define_opaque_mask { } } + // vector/array conversion + impl From<[bool; $lanes]> for $name<$lanes> where $bits_ty: crate::LanesAtMost64 { + fn from(array: [bool; $lanes]) -> Self { + Self::from_array(array) + } + } + + impl From<$name<$lanes>> for [bool; $lanes] where $bits_ty: crate::LanesAtMost64 { + fn from(vector: $name<$lanes>) -> Self { + vector.to_array() + } + } + impl Copy for $name<$lanes> where $inner_ty: Copy, diff --git a/crates/core_simd/src/vector/float.rs b/crates/core_simd/src/vector/float.rs index 9d72e69ec47..85456e7bf1e 100644 --- a/crates/core_simd/src/vector/float.rs +++ b/crates/core_simd/src/vector/float.rs @@ -47,21 +47,21 @@ macro_rules! impl_float_vector { /// `+0.0`, `NaN`s with positive sign bit and positive infinity. #[inline] pub fn is_sign_positive(self) -> crate::$mask_ty { - let sign_bits = self.to_bits() & crate::$bits_ty::splat((!0 >> 1) + 1); - sign_bits.lanes_gt(crate::$bits_ty::splat(0)) + !self.is_sign_negative() } /// Returns true for each lane if it has a negative sign, including /// `-0.0`, `NaN`s with negative sign bit and negative infinity. #[inline] pub fn is_sign_negative(self) -> crate::$mask_ty { - !self.is_sign_positive() + let sign_bits = self.to_bits() & crate::$bits_ty::splat((!0 >> 1) + 1); + sign_bits.lanes_gt(crate::$bits_ty::splat(0)) } /// Returns true for each lane if its value is `NaN`. #[inline] pub fn is_nan(self) -> crate::$mask_ty { - self.lanes_eq(self) + self.lanes_ne(self) } /// Returns true for each lane if its value is positive infinity or negative infinity. @@ -79,8 +79,8 @@ macro_rules! impl_float_vector { /// Returns true for each lane if its value is subnormal. #[inline] pub fn is_subnormal(self) -> crate::$mask_ty { - let mantissa_mask = crate::$bits_ty::splat((1 << (<$type>::MANTISSA_DIGITS - 1)) - 1); - self.abs().lanes_ne(Self::splat(0.0)) & (self.to_bits() & mantissa_mask).lanes_eq(crate::$bits_ty::splat(0)) + let exponent_mask = crate::$bits_ty::splat(!0 << <$type>::MANTISSA_DIGITS); + self.abs().lanes_ne(Self::splat(0.0)) & (self.to_bits() & exponent_mask).lanes_eq(crate::$bits_ty::splat(0)) } /// Returns true for each lane if its value is neither neither zero, infinite, @@ -93,7 +93,6 @@ macro_rules! impl_float_vector { }; } - /// A SIMD vector of containing `LANES` `f32` values. #[repr(simd)] pub struct SimdF32([f32; LANES]) diff --git a/crates/core_simd/src/vector/int.rs b/crates/core_simd/src/vector/int.rs index 86762f74ff4..364a2ed935f 100644 --- a/crates/core_simd/src/vector/int.rs +++ b/crates/core_simd/src/vector/int.rs @@ -2,7 +2,7 @@ /// 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 } => { + { $name:ident, $type:ty, $mask_ty:ident, $mask_impl_ty:ident } => { impl_vector! { $name, $type } impl Eq for $name where Self: crate::LanesAtMost64 {} @@ -24,6 +24,22 @@ macro_rules! impl_integer_vector { self.as_slice().hash(state) } } + + impl $name + where + Self: crate::LanesAtMost64, + crate::$mask_impl_ty: crate::LanesAtMost64, + { + /// Returns true for each positive lane and false if it is zero or negative. + pub fn is_positive(self) -> crate::$mask_ty { + self.lanes_gt(Self::splat(0)) + } + + /// Returns true for each negative lane and false if it is zero or positive. + pub fn is_negative(self) -> crate::$mask_ty { + self.lanes_lt(Self::splat(0)) + } + } } } @@ -33,7 +49,7 @@ pub struct SimdIsize([isize; LANES]) where Self: crate::LanesAtMost64; -impl_integer_vector! { SimdIsize, isize } +impl_integer_vector! { SimdIsize, isize, MaskSize, SimdIsize } #[cfg(target_pointer_width = "32")] from_transmute_x86! { unsafe isizex4 => __m128i } @@ -53,7 +69,7 @@ pub struct SimdI128([i128; LANES]) where Self: crate::LanesAtMost64; -impl_integer_vector! { SimdI128, i128 } +impl_integer_vector! { SimdI128, i128, Mask128, SimdI128 } from_transmute_x86! { unsafe i128x2 => __m256i } //from_transmute_x86! { unsafe i128x4 => __m512i } @@ -64,7 +80,7 @@ pub struct SimdI16([i16; LANES]) where Self: crate::LanesAtMost64; -impl_integer_vector! { SimdI16, i16 } +impl_integer_vector! { SimdI16, i16, Mask16, SimdI16 } from_transmute_x86! { unsafe i16x8 => __m128i } from_transmute_x86! { unsafe i16x16 => __m256i } @@ -76,7 +92,7 @@ pub struct SimdI32([i32; LANES]) where Self: crate::LanesAtMost64; -impl_integer_vector! { SimdI32, i32 } +impl_integer_vector! { SimdI32, i32, Mask32, SimdI32 } from_transmute_x86! { unsafe i32x4 => __m128i } from_transmute_x86! { unsafe i32x8 => __m256i } @@ -88,7 +104,7 @@ pub struct SimdI64([i64; LANES]) where Self: crate::LanesAtMost64; -impl_integer_vector! { SimdI64, i64 } +impl_integer_vector! { SimdI64, i64, Mask64, SimdI64 } from_transmute_x86! { unsafe i64x2 => __m128i } from_transmute_x86! { unsafe i64x4 => __m256i } @@ -100,7 +116,7 @@ pub struct SimdI8([i8; LANES]) where Self: crate::LanesAtMost64; -impl_integer_vector! { SimdI8, i8 } +impl_integer_vector! { SimdI8, i8, Mask8, SimdI8 } from_transmute_x86! { unsafe i8x16 => __m128i } from_transmute_x86! { unsafe i8x32 => __m256i } diff --git a/crates/core_simd/tests/f32_ops.rs b/crates/core_simd/tests/f32_ops.rs index ac5499b7ffe..048c070a391 100644 --- a/crates/core_simd/tests/f32_ops.rs +++ b/crates/core_simd/tests/f32_ops.rs @@ -1,3 +1,5 @@ +#![feature(is_subnormal)] + #[macro_use] mod ops_macros; impl_float_tests! { SimdF32, f32, i32 } diff --git a/crates/core_simd/tests/f64_ops.rs b/crates/core_simd/tests/f64_ops.rs index dcdb2aa3152..8f0dd6b7365 100644 --- a/crates/core_simd/tests/f64_ops.rs +++ b/crates/core_simd/tests/f64_ops.rs @@ -1,3 +1,5 @@ +#![feature(is_subnormal)] + #[macro_use] mod ops_macros; impl_float_tests! { SimdF64, f64, i64 } diff --git a/crates/core_simd/tests/ops_macros.rs b/crates/core_simd/tests/ops_macros.rs index 8e0b9626861..a70a8a9c48b 100644 --- a/crates/core_simd/tests/ops_macros.rs +++ b/crates/core_simd/tests/ops_macros.rs @@ -147,11 +147,27 @@ macro_rules! impl_signed_tests { test_helpers::test_lanes! { fn neg() { test_helpers::test_unary_elementwise( - & as core::ops::Neg>::neg, + & as core::ops::Neg>::neg, &::neg, &|x| !x.contains(&Scalar::MIN), ); } + + fn is_positive() { + test_helpers::test_unary_mask_elementwise( + &Vector::::is_positive, + &Scalar::is_positive, + &|_| true, + ); + } + + fn is_negative() { + test_helpers::test_unary_mask_elementwise( + &Vector::::is_negative, + &Scalar::is_negative, + &|_| true, + ); + } } test_helpers::test_lanes_panic! { @@ -285,6 +301,62 @@ macro_rules! impl_float_tests { } test_helpers::test_lanes! { + fn is_sign_positive() { + test_helpers::test_unary_mask_elementwise( + &Vector::::is_sign_positive, + &Scalar::is_sign_positive, + &|_| true, + ); + } + + fn is_sign_negative() { + test_helpers::test_unary_mask_elementwise( + &Vector::::is_sign_negative, + &Scalar::is_sign_negative, + &|_| true, + ); + } + + fn is_finite() { + test_helpers::test_unary_mask_elementwise( + &Vector::::is_finite, + &Scalar::is_finite, + &|_| true, + ); + } + + fn is_infinite() { + test_helpers::test_unary_mask_elementwise( + &Vector::::is_infinite, + &Scalar::is_infinite, + &|_| true, + ); + } + + fn is_nan() { + test_helpers::test_unary_mask_elementwise( + &Vector::::is_nan, + &Scalar::is_nan, + &|_| true, + ); + } + + fn is_normal() { + test_helpers::test_unary_mask_elementwise( + &Vector::::is_normal, + &Scalar::is_normal, + &|_| true, + ); + } + + fn is_subnormal() { + test_helpers::test_unary_mask_elementwise( + &Vector::::is_subnormal, + &Scalar::is_subnormal, + &|_| true, + ); + } + fn abs() { test_helpers::test_unary_elementwise( &Vector::::abs, diff --git a/crates/test_helpers/src/biteq.rs b/crates/test_helpers/src/biteq.rs index 8c628064380..4a41fe3a16e 100644 --- a/crates/test_helpers/src/biteq.rs +++ b/crates/test_helpers/src/biteq.rs @@ -5,6 +5,16 @@ pub trait BitEq { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result; } +impl BitEq for bool { + fn biteq(&self, other: &Self) -> bool { + self == other + } + + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "{:?}", self) + } +} + macro_rules! impl_integer_biteq { { $($type:ty),* } => { $( diff --git a/crates/test_helpers/src/lib.rs b/crates/test_helpers/src/lib.rs index 2c74c02d48a..df1abcddaea 100644 --- a/crates/test_helpers/src/lib.rs +++ b/crates/test_helpers/src/lib.rs @@ -124,6 +124,32 @@ pub fn test_unary_elementwise( + fv: &dyn Fn(Vector) -> Mask, + fs: &dyn Fn(Scalar) -> bool, + check: &dyn Fn([Scalar; LANES]) -> bool, +) where + Scalar: Copy + Default + core::fmt::Debug + DefaultStrategy, + Vector: Into<[Scalar; LANES]> + From<[Scalar; LANES]> + Copy, + Mask: Into<[bool; LANES]> + From<[bool; LANES]> + Copy, +{ + test_1(&|x: [Scalar; LANES]| { + proptest::prop_assume!(check(x)); + let result_1: [bool; LANES] = fv(x.into()).into(); + let result_2: [bool; LANES] = { + let mut result = [false; LANES]; + for (i, o) in x.iter().zip(result.iter_mut()) { + *o = fs(*i); + } + result + }; + crate::prop_assert_biteq!(result_1, result_2); + Ok(()) + }); +} + /// Test a binary vector function against a binary scalar function, applied elementwise. #[inline(never)] pub fn test_binary_elementwise< @@ -307,7 +333,7 @@ macro_rules! test_lanes { } )* } -} +} /// Expand a const-generic `#[should_panic]` test into separate tests for each possible lane count. #[macro_export] @@ -382,4 +408,4 @@ macro_rules! test_lanes_panic { } )* } -} +}