From 04be48ff97757a803e934ab8d2e90631b59557f8 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Fri, 15 Apr 2022 01:44:18 -0400 Subject: [PATCH] Add float trait, and seal traits. --- crates/core_simd/src/elements.rs | 6 + crates/core_simd/src/elements/float.rs | 344 +++++++++++++++++++++++++ crates/core_simd/src/elements/int.rs | 9 +- crates/core_simd/src/elements/uint.rs | 9 +- crates/core_simd/src/mod.rs | 3 - crates/core_simd/src/reduction.rs | 127 --------- crates/core_simd/src/vector/float.rs | 211 +-------------- crates/core_simd/tests/ops_macros.rs | 4 +- 8 files changed, 368 insertions(+), 345 deletions(-) create mode 100644 crates/core_simd/src/elements/float.rs delete mode 100644 crates/core_simd/src/reduction.rs diff --git a/crates/core_simd/src/elements.rs b/crates/core_simd/src/elements.rs index 0fb1f5b9fe97..701eb66b248a 100644 --- a/crates/core_simd/src/elements.rs +++ b/crates/core_simd/src/elements.rs @@ -1,5 +1,11 @@ +mod float; mod int; mod uint; +mod sealed { + pub trait Sealed {} +} + +pub use float::*; pub use int::*; pub use uint::*; diff --git a/crates/core_simd/src/elements/float.rs b/crates/core_simd/src/elements/float.rs new file mode 100644 index 000000000000..fafbd2a4d213 --- /dev/null +++ b/crates/core_simd/src/elements/float.rs @@ -0,0 +1,344 @@ +use super::sealed::Sealed; +use crate::simd::{ + intrinsics, LaneCount, Mask, Simd, SimdElement, SimdPartialEq, SimdPartialOrd, + SupportedLaneCount, +}; + +/// Operations on SIMD vectors of floats. +pub trait SimdFloat: Sized + Sealed { + /// Mask type used for manipulating this SIMD vector type. + type Mask; + + /// Scalar type contained by this SIMD vector type. + type Scalar; + + /// Bit representation of this SIMD vector type. + type Bits; + + /// Raw transmutation to an unsigned integer vector type with the + /// same size and number of lanes. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn to_bits(self) -> Self::Bits; + + /// Raw transmutation from an unsigned integer vector type with the + /// same size and number of lanes. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn from_bits(bits: Self::Bits) -> Self; + + /// Produces a vector where every lane has the absolute value of the + /// equivalently-indexed lane in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn abs(self) -> Self; + + /// Takes the reciprocal (inverse) of each lane, `1/x`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn recip(self) -> Self; + + /// Converts each lane from radians to degrees. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn to_degrees(self) -> Self; + + /// Converts each lane from degrees to radians. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn to_radians(self) -> Self; + + /// Returns true for each lane if it has a positive sign, including + /// `+0.0`, `NaN`s with positive sign bit and positive infinity. + #[must_use = "method returns a new mask and does not mutate the original value"] + fn is_sign_positive(self) -> Self::Mask; + + /// Returns true for each lane if it has a negative sign, including + /// `-0.0`, `NaN`s with negative sign bit and negative infinity. + #[must_use = "method returns a new mask and does not mutate the original value"] + fn is_sign_negative(self) -> Self::Mask; + + /// Returns true for each lane if its value is `NaN`. + #[must_use = "method returns a new mask and does not mutate the original value"] + fn is_nan(self) -> Self::Mask; + + /// Returns true for each lane if its value is positive infinity or negative infinity. + #[must_use = "method returns a new mask and does not mutate the original value"] + fn is_infinite(self) -> Self::Mask; + + /// Returns true for each lane if its value is neither infinite nor `NaN`. + #[must_use = "method returns a new mask and does not mutate the original value"] + fn is_finite(self) -> Self::Mask; + + /// Returns true for each lane if its value is subnormal. + #[must_use = "method returns a new mask and does not mutate the original value"] + fn is_subnormal(self) -> Self::Mask; + + /// Returns true for each lane if its value is neither zero, infinite, + /// subnormal, nor `NaN`. + #[must_use = "method returns a new mask and does not mutate the original value"] + fn is_normal(self) -> Self::Mask; + + /// Replaces each lane with a number that represents its sign. + /// + /// * `1.0` if the number is positive, `+0.0`, or `INFINITY` + /// * `-1.0` if the number is negative, `-0.0`, or `NEG_INFINITY` + /// * `NAN` if the number is `NAN` + #[must_use = "method returns a new vector and does not mutate the original value"] + fn signum(self) -> Self; + + /// Returns each lane with the magnitude of `self` and the sign of `sign`. + /// + /// If any lane is a `NAN`, then a `NAN` with the sign of `sign` is returned. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn copysign(self, sign: Self) -> Self; + + /// Returns the minimum of each lane. + /// + /// If one of the values is `NAN`, then the other value is returned. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn simd_min(self, other: Self) -> Self; + + /// Returns the maximum of each lane. + /// + /// If one of the values is `NAN`, then the other value is returned. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn simd_max(self, other: Self) -> Self; + + /// Restrict each lane to a certain interval unless it is NaN. + /// + /// For each lane in `self`, returns the corresponding lane in `max` if the lane is + /// greater than `max`, and the corresponding lane in `min` if the lane is less + /// than `min`. Otherwise returns the lane in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn simd_clamp(self, min: Self, max: Self) -> Self; + + /// Returns the sum of the lanes of the vector. + /// + /// # Examples + /// + /// ``` + /// # #![feature(portable_simd)] + /// # use core::simd::f32x2; + /// let v = f32x2::from_array([1., 2.]); + /// assert_eq!(v.reduce_sum(), 3.); + /// ``` + fn reduce_sum(self) -> Self::Scalar; + + /// Reducing multiply. Returns the product of the lanes of the vector. + /// + /// # Examples + /// + /// ``` + /// # #![feature(portable_simd)] + /// # use core::simd::f32x2; + /// let v = f32x2::from_array([3., 4.]); + /// assert_eq!(v.reduce_product(), 12.); + /// ``` + fn reduce_product(self) -> Self::Scalar; + + /// Returns the maximum lane in the vector. + /// + /// Returns values based on equality, so a vector containing both `0.` and `-0.` may + /// return either. + /// + /// This function will not return `NaN` unless all lanes are `NaN`. + /// + /// # Examples + /// + /// ``` + /// # #![feature(portable_simd)] + /// # use core::simd::f32x2; + /// let v = f32x2::from_array([1., 2.]); + /// assert_eq!(v.reduce_max(), 2.); + /// + /// // NaN values are skipped... + /// let v = f32x2::from_array([1., f32::NAN]); + /// assert_eq!(v.reduce_max(), 1.); + /// + /// // ...unless all values are NaN + /// let v = f32x2::from_array([f32::NAN, f32::NAN]); + /// assert!(v.reduce_max().is_nan()); + /// ``` + fn reduce_max(self) -> Self::Scalar; + + /// Returns the minimum lane in the vector. + /// + /// Returns values based on equality, so a vector containing both `0.` and `-0.` may + /// return either. + /// + /// This function will not return `NaN` unless all lanes are `NaN`. + /// + /// # Examples + /// + /// ``` + /// # #![feature(portable_simd)] + /// # use core::simd::f32x2; + /// let v = f32x2::from_array([3., 7.]); + /// assert_eq!(v.reduce_min(), 3.); + /// + /// // NaN values are skipped... + /// let v = f32x2::from_array([1., f32::NAN]); + /// assert_eq!(v.reduce_min(), 1.); + /// + /// // ...unless all values are NaN + /// let v = f32x2::from_array([f32::NAN, f32::NAN]); + /// assert!(v.reduce_min().is_nan()); + /// ``` + fn reduce_min(self) -> Self::Scalar; +} + +macro_rules! impl_trait { + { $($ty:ty { bits: $bits_ty:ty, mask: $mask_ty:ty }),* } => { + $( + impl Sealed for Simd<$ty, LANES> + where + LaneCount: SupportedLaneCount, + { + } + + impl SimdFloat for Simd<$ty, LANES> + where + LaneCount: SupportedLaneCount, + { + type Mask = Mask<<$mask_ty as SimdElement>::Mask, LANES>; + type Scalar = $ty; + type Bits = Simd<$bits_ty, LANES>; + + #[inline] + fn to_bits(self) -> Simd<$bits_ty, LANES> { + assert_eq!(core::mem::size_of::(), core::mem::size_of::()); + unsafe { core::mem::transmute_copy(&self) } + } + + #[inline] + fn from_bits(bits: Simd<$bits_ty, LANES>) -> Self { + assert_eq!(core::mem::size_of::(), core::mem::size_of::()); + unsafe { core::mem::transmute_copy(&bits) } + } + + #[inline] + fn abs(self) -> Self { + unsafe { intrinsics::simd_fabs(self) } + } + + #[inline] + fn recip(self) -> Self { + Self::splat(1.0) / self + } + + #[inline] + fn to_degrees(self) -> Self { + // to_degrees uses a special constant for better precision, so extract that constant + self * Self::splat(Self::Scalar::to_degrees(1.)) + } + + #[inline] + fn to_radians(self) -> Self { + self * Self::splat(Self::Scalar::to_radians(1.)) + } + + #[inline] + fn is_sign_positive(self) -> Self::Mask { + !self.is_sign_negative() + } + + #[inline] + fn is_sign_negative(self) -> Self::Mask { + let sign_bits = self.to_bits() & Simd::splat((!0 >> 1) + 1); + sign_bits.simd_gt(Simd::splat(0)) + } + + #[inline] + fn is_nan(self) -> Self::Mask { + self.simd_ne(self) + } + + #[inline] + fn is_infinite(self) -> Self::Mask { + self.abs().simd_eq(Self::splat(Self::Scalar::INFINITY)) + } + + #[inline] + fn is_finite(self) -> Self::Mask { + self.abs().simd_lt(Self::splat(Self::Scalar::INFINITY)) + } + + #[inline] + fn is_subnormal(self) -> Self::Mask { + self.abs().simd_ne(Self::splat(0.0)) & (self.to_bits() & Self::splat(Self::Scalar::INFINITY).to_bits()).simd_eq(Simd::splat(0)) + } + + #[inline] + #[must_use = "method returns a new mask and does not mutate the original value"] + fn is_normal(self) -> Self::Mask { + !(self.abs().simd_eq(Self::splat(0.0)) | self.is_nan() | self.is_subnormal() | self.is_infinite()) + } + + #[inline] + fn signum(self) -> Self { + self.is_nan().select(Self::splat(Self::Scalar::NAN), Self::splat(1.0).copysign(self)) + } + + #[inline] + fn copysign(self, sign: Self) -> Self { + let sign_bit = sign.to_bits() & Self::splat(-0.).to_bits(); + let magnitude = self.to_bits() & !Self::splat(-0.).to_bits(); + Self::from_bits(sign_bit | magnitude) + } + + #[inline] + fn simd_min(self, other: Self) -> Self { + unsafe { intrinsics::simd_fmin(self, other) } + } + + #[inline] + fn simd_max(self, other: Self) -> Self { + unsafe { intrinsics::simd_fmax(self, other) } + } + + #[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`", + ); + let mut x = self; + x = x.simd_lt(min).select(min, x); + x = x.simd_gt(max).select(max, x); + x + } + + #[inline] + fn reduce_sum(self) -> Self::Scalar { + // LLVM sum is inaccurate on i586 + if cfg!(all(target_arch = "x86", not(target_feature = "sse2"))) { + self.as_array().iter().sum() + } else { + // Safety: `self` is a float vector + unsafe { intrinsics::simd_reduce_add_ordered(self, 0.) } + } + } + + #[inline] + fn reduce_product(self) -> Self::Scalar { + // LLVM product is inaccurate on i586 + if cfg!(all(target_arch = "x86", not(target_feature = "sse2"))) { + self.as_array().iter().product() + } else { + // Safety: `self` is a float vector + unsafe { intrinsics::simd_reduce_mul_ordered(self, 1.) } + } + } + + #[inline] + fn reduce_max(self) -> Self::Scalar { + // Safety: `self` is a float vector + unsafe { intrinsics::simd_reduce_max(self) } + } + + #[inline] + fn reduce_min(self) -> Self::Scalar { + // Safety: `self` is a float vector + unsafe { intrinsics::simd_reduce_min(self) } + } + } + )* + } +} + +impl_trait! { f32 { bits: u32, mask: i32 }, f64 { bits: u64, mask: i64 } } diff --git a/crates/core_simd/src/elements/int.rs b/crates/core_simd/src/elements/int.rs index 611354274565..c3139b4ba3eb 100644 --- a/crates/core_simd/src/elements/int.rs +++ b/crates/core_simd/src/elements/int.rs @@ -1,9 +1,10 @@ +use super::sealed::Sealed; use crate::simd::{ intrinsics, LaneCount, Mask, Simd, SimdElement, SimdPartialOrd, SupportedLaneCount, }; /// Operations on SIMD vectors of signed integers. -pub trait SimdInt: Sized { +pub trait SimdInt: Sized + Sealed { /// Mask type used for manipulating this SIMD vector type. type Mask; @@ -167,6 +168,12 @@ pub trait SimdInt: Sized { macro_rules! impl_trait { { $($ty:ty),* } => { $( + impl Sealed for Simd<$ty, LANES> + where + LaneCount: SupportedLaneCount, + { + } + impl SimdInt for Simd<$ty, LANES> where LaneCount: SupportedLaneCount, diff --git a/crates/core_simd/src/elements/uint.rs b/crates/core_simd/src/elements/uint.rs index da3213535a30..cba1a9b0ce0d 100644 --- a/crates/core_simd/src/elements/uint.rs +++ b/crates/core_simd/src/elements/uint.rs @@ -1,7 +1,8 @@ +use super::sealed::Sealed; use crate::simd::{intrinsics, LaneCount, Simd, SupportedLaneCount}; /// Operations on SIMD vectors of unsigned integers. -pub trait SimdUint: Sized { +pub trait SimdUint: Sized + Sealed { /// Scalar type contained by this SIMD vector type. type Scalar; @@ -61,6 +62,12 @@ pub trait SimdUint: Sized { macro_rules! impl_trait { { $($ty:ty),* } => { $( + impl Sealed for Simd<$ty, LANES> + where + LaneCount: SupportedLaneCount, + { + } + impl SimdUint for Simd<$ty, LANES> where LaneCount: SupportedLaneCount, diff --git a/crates/core_simd/src/mod.rs b/crates/core_simd/src/mod.rs index 2d4fe2b7fde6..590b2e4a1531 100644 --- a/crates/core_simd/src/mod.rs +++ b/crates/core_simd/src/mod.rs @@ -1,6 +1,3 @@ -#[macro_use] -mod reduction; - #[macro_use] mod swizzle; diff --git a/crates/core_simd/src/reduction.rs b/crates/core_simd/src/reduction.rs deleted file mode 100644 index 9d8639feeeed..000000000000 --- a/crates/core_simd/src/reduction.rs +++ /dev/null @@ -1,127 +0,0 @@ -use crate::simd::intrinsics::{ - simd_reduce_add_ordered, simd_reduce_max, simd_reduce_min, simd_reduce_mul_ordered, -}; -use crate::simd::{LaneCount, Simd, SupportedLaneCount}; - -macro_rules! impl_float_reductions { - { $scalar:ty } => { - impl Simd<$scalar, LANES> - where - LaneCount: SupportedLaneCount, - { - - /// Reducing add. Returns the sum of the lanes of the vector. - /// - /// # Examples - /// - /// ``` - /// # #![feature(portable_simd)] - /// # use core::simd::Simd; - #[doc = concat!("# use core::simd::", stringify!($scalar), "x2;")] - #[doc = concat!("let v = ", stringify!($scalar), "x2::from_array([1., 2.]);")] - /// assert_eq!(v.reduce_sum(), 3.); - /// ``` - #[inline] - pub fn reduce_sum(self) -> $scalar { - // LLVM sum is inaccurate on i586 - if cfg!(all(target_arch = "x86", not(target_feature = "sse2"))) { - self.as_array().iter().sum() - } else { - // Safety: `self` is a float vector - unsafe { simd_reduce_add_ordered(self, 0.) } - } - } - - /// Reducing multiply. Returns the product of the lanes of the vector. - /// - /// # Examples - /// - /// ``` - /// # #![feature(portable_simd)] - /// # use core::simd::Simd; - #[doc = concat!("# use core::simd::", stringify!($scalar), "x2;")] - #[doc = concat!("let v = ", stringify!($scalar), "x2::from_array([3., 4.]);")] - /// assert_eq!(v.reduce_product(), 12.); - /// ``` - #[inline] - pub fn reduce_product(self) -> $scalar { - // LLVM product is inaccurate on i586 - if cfg!(all(target_arch = "x86", not(target_feature = "sse2"))) { - self.as_array().iter().product() - } else { - // Safety: `self` is a float vector - unsafe { simd_reduce_mul_ordered(self, 1.) } - } - } - - /// Reducing maximum. Returns the maximum lane in the vector. - /// - /// Returns values based on equality, so a vector containing both `0.` and `-0.` may - /// return either. - /// - /// This function will not return `NaN` unless all lanes are `NaN`. - /// - /// # Examples - /// - /// ``` - /// # #![feature(portable_simd)] - /// # use core::simd::Simd; - #[doc = concat!("# use core::simd::", stringify!($scalar), "x2;")] - #[doc = concat!("let v = ", stringify!($scalar), "x2::from_array([1., 2.]);")] - /// assert_eq!(v.reduce_max(), 2.); - /// - /// // NaN values are skipped... - #[doc = concat!("let v = ", stringify!($scalar), "x2::from_array([1., ", stringify!($scalar), "::NAN]);")] - /// assert_eq!(v.reduce_max(), 1.); - /// - /// // ...unless all values are NaN - #[doc = concat!("let v = ", stringify!($scalar), "x2::from_array([", - stringify!($scalar), "::NAN, ", - stringify!($scalar), "::NAN]);" - )] - /// assert!(v.reduce_max().is_nan()); - /// ``` - #[inline] - pub fn reduce_max(self) -> $scalar { - // Safety: `self` is a float vector - unsafe { simd_reduce_max(self) } - } - - /// Reducing minimum. Returns the minimum lane in the vector. - /// - /// Returns values based on equality, so a vector containing both `0.` and `-0.` may - /// return either. - /// - /// This function will not return `NaN` unless all lanes are `NaN`. - /// - /// # Examples - /// - /// ``` - /// # #![feature(portable_simd)] - /// # use core::simd::Simd; - #[doc = concat!("# use core::simd::", stringify!($scalar), "x2;")] - #[doc = concat!("let v = ", stringify!($scalar), "x2::from_array([3., 7.]);")] - /// assert_eq!(v.reduce_min(), 3.); - /// - /// // NaN values are skipped... - #[doc = concat!("let v = ", stringify!($scalar), "x2::from_array([1., ", stringify!($scalar), "::NAN]);")] - /// assert_eq!(v.reduce_min(), 1.); - /// - /// // ...unless all values are NaN - #[doc = concat!("let v = ", stringify!($scalar), "x2::from_array([", - stringify!($scalar), "::NAN, ", - stringify!($scalar), "::NAN]);" - )] - /// assert!(v.reduce_min().is_nan()); - /// ``` - #[inline] - pub fn reduce_min(self) -> $scalar { - // Safety: `self` is a float vector - unsafe { simd_reduce_min(self) } - } - } - } -} - -impl_float_reductions! { f32 } -impl_float_reductions! { f64 } diff --git a/crates/core_simd/src/vector/float.rs b/crates/core_simd/src/vector/float.rs index 13b1d3995a1c..f836c99b1e2d 100644 --- a/crates/core_simd/src/vector/float.rs +++ b/crates/core_simd/src/vector/float.rs @@ -1,145 +1,6 @@ #![allow(non_camel_case_types)] -use crate::simd::intrinsics; -use crate::simd::{LaneCount, Mask, Simd, SimdPartialEq, SimdPartialOrd, SupportedLaneCount}; - -/// Implements inherent methods for a float vector containing multiple -/// `$lanes` of float `$type`, which uses `$bits_ty` as its binary -/// representation. -macro_rules! impl_float_vector { - { $type:ty, $bits_ty:ty, $mask_ty:ty } => { - impl Simd<$type, LANES> - where - LaneCount: SupportedLaneCount, - { - /// Raw transmutation to an unsigned integer vector type with the - /// same size and number of lanes. - #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn to_bits(self) -> Simd<$bits_ty, LANES> { - assert_eq!(core::mem::size_of::(), core::mem::size_of::>()); - unsafe { core::mem::transmute_copy(&self) } - } - - /// Raw transmutation from an unsigned integer vector type with the - /// same size and number of lanes. - #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn from_bits(bits: Simd<$bits_ty, LANES>) -> Self { - assert_eq!(core::mem::size_of::(), core::mem::size_of::>()); - unsafe { core::mem::transmute_copy(&bits) } - } - - /// Produces a vector where every lane has the absolute value of the - /// equivalently-indexed lane in `self`. - #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn abs(self) -> Self { - unsafe { intrinsics::simd_fabs(self) } - } - - /// Takes the reciprocal (inverse) of each lane, `1/x`. - #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn recip(self) -> Self { - Self::splat(1.0) / self - } - - /// Converts each lane from radians to degrees. - #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn to_degrees(self) -> Self { - // to_degrees uses a special constant for better precision, so extract that constant - self * Self::splat(<$type>::to_degrees(1.)) - } - - /// Converts each lane from degrees to radians. - #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn to_radians(self) -> Self { - self * Self::splat(<$type>::to_radians(1.)) - } - - /// Returns true for each lane if it has a positive sign, including - /// `+0.0`, `NaN`s with positive sign bit and positive infinity. - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn is_sign_positive(self) -> Mask<$mask_ty, LANES> { - !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] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn is_sign_negative(self) -> Mask<$mask_ty, LANES> { - let sign_bits = self.to_bits() & Simd::splat((!0 >> 1) + 1); - sign_bits.simd_gt(Simd::splat(0)) - } - - /// Returns true for each lane if its value is `NaN`. - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn is_nan(self) -> Mask<$mask_ty, LANES> { - self.simd_ne(self) - } - - /// Returns true for each lane if its value is positive infinity or negative infinity. - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn is_infinite(self) -> Mask<$mask_ty, LANES> { - self.abs().simd_eq(Self::splat(<$type>::INFINITY)) - } - - /// Returns true for each lane if its value is neither infinite nor `NaN`. - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn is_finite(self) -> Mask<$mask_ty, LANES> { - self.abs().simd_lt(Self::splat(<$type>::INFINITY)) - } - - /// Returns true for each lane if its value is subnormal. - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn is_subnormal(self) -> Mask<$mask_ty, LANES> { - self.abs().simd_ne(Self::splat(0.0)) & (self.to_bits() & Self::splat(<$type>::INFINITY).to_bits()).simd_eq(Simd::splat(0)) - } - - /// Returns true for each lane if its value is neither zero, infinite, - /// subnormal, nor `NaN`. - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn is_normal(self) -> Mask<$mask_ty, LANES> { - !(self.abs().simd_eq(Self::splat(0.0)) | self.is_nan() | self.is_subnormal() | self.is_infinite()) - } - - /// Replaces each lane with a number that represents its sign. - /// - /// * `1.0` if the number is positive, `+0.0`, or `INFINITY` - /// * `-1.0` if the number is negative, `-0.0`, or `NEG_INFINITY` - /// * `NAN` if the number is `NAN` - #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn signum(self) -> Self { - self.is_nan().select(Self::splat(<$type>::NAN), Self::splat(1.0).copysign(self)) - } - - /// Returns each lane with the magnitude of `self` and the sign of `sign`. - /// - /// If any lane is a `NAN`, then a `NAN` with the sign of `sign` is returned. - #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn copysign(self, sign: Self) -> Self { - let sign_bit = sign.to_bits() & Self::splat(-0.).to_bits(); - let magnitude = self.to_bits() & !Self::splat(-0.).to_bits(); - Self::from_bits(sign_bit | magnitude) - } - } - }; -} - -impl_float_vector! { f32, u32, i32 } -impl_float_vector! { f64, u64, i64 } +use crate::simd::Simd; /// A 64-bit SIMD vector with two elements of type `f32`. pub type f32x2 = Simd; @@ -161,73 +22,3 @@ pub type f64x4 = Simd; /// A 512-bit SIMD vector with eight elements of type `f64`. pub type f64x8 = Simd; - -mod sealed { - pub trait Sealed {} -} -use sealed::Sealed; - -/// SIMD operations on vectors of floating point numbers. -pub trait SimdFloat: Sized + Sealed { - /// Returns the minimum of each lane. - /// - /// If one of the values is `NAN`, then the other value is returned. - #[must_use = "method returns a new vector and does not mutate the original value"] - fn simd_min(self, other: Self) -> Self; - - /// Returns the maximum of each lane. - /// - /// If one of the values is `NAN`, then the other value is returned. - #[must_use = "method returns a new vector and does not mutate the original value"] - fn simd_max(self, other: Self) -> Self; - - /// Restrict each lane to a certain interval unless it is NaN. - /// - /// For each lane in `self`, returns the corresponding lane in `max` if the lane is - /// greater than `max`, and the corresponding lane in `min` if the lane is less - /// than `min`. Otherwise returns the lane in `self`. - #[must_use = "method returns a new vector and does not mutate the original value"] - fn simd_clamp(self, min: Self, max: Self) -> Self; -} - -macro_rules! impl_simd_float { - { $($float:ty),* } => { - $( - impl Sealed for Simd<$float, LANES> - where - LaneCount: SupportedLaneCount, - { - } - - impl SimdFloat for Simd<$float, LANES> - where - LaneCount: SupportedLaneCount, - { - #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] - fn simd_min(self, other: Self) -> Self { - unsafe { intrinsics::simd_fmin(self, other) } - } - - #[inline] - fn simd_max(self, other: Self) -> Self { - unsafe { intrinsics::simd_fmax(self, other) } - } - - #[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`", - ); - let mut x = self; - x = x.simd_lt(min).select(min, x); - x = x.simd_gt(max).select(max, x); - x - } - } - )* - } -} - -impl_simd_float! { f32, f64 } diff --git a/crates/core_simd/tests/ops_macros.rs b/crates/core_simd/tests/ops_macros.rs index 48c512be7d02..f759394d0758 100644 --- a/crates/core_simd/tests/ops_macros.rs +++ b/crates/core_simd/tests/ops_macros.rs @@ -348,6 +348,7 @@ macro_rules! impl_unsigned_tests { macro_rules! impl_float_tests { { $scalar:tt, $int_scalar:tt } => { mod $scalar { + use core_simd::SimdFloat; type Vector = core_simd::Simd; type Scalar = $scalar; @@ -464,7 +465,6 @@ macro_rules! impl_float_tests { } fn simd_min() { - use core_simd::simd::SimdFloat; // Regular conditions (both values aren't zero) test_helpers::test_binary_elementwise( &Vector::::simd_min, @@ -488,7 +488,6 @@ macro_rules! impl_float_tests { } fn simd_max() { - use core_simd::simd::SimdFloat; // Regular conditions (both values aren't zero) test_helpers::test_binary_elementwise( &Vector::::simd_max, @@ -512,7 +511,6 @@ macro_rules! impl_float_tests { } fn simd_clamp() { - use core_simd::simd::SimdFloat; test_helpers::test_3(&|value: [Scalar; LANES], mut min: [Scalar; LANES], mut max: [Scalar; LANES]| { for (min, max) in min.iter_mut().zip(max.iter_mut()) { if max < min {