From a991d48e95911c0e94f47bda10cbb50200852ec2 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Wed, 26 Jan 2022 16:58:38 -0800 Subject: [PATCH 1/2] Add Simd::cast --- crates/core_simd/src/intrinsics.rs | 3 +++ crates/core_simd/src/vector.rs | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/crates/core_simd/src/intrinsics.rs b/crates/core_simd/src/intrinsics.rs index 70f1d47c08b..2291400537c 100644 --- a/crates/core_simd/src/intrinsics.rs +++ b/crates/core_simd/src/intrinsics.rs @@ -39,6 +39,9 @@ /// fptoui/fptosi/uitofp/sitofp pub(crate) fn simd_cast(x: T) -> U; + /// follows Rust's `T as U` semantics, including saturating float casts + /// which amounts to the same as `simd_cast` for many cases + pub(crate) fn simd_as(x: T) -> U; /// neg/fneg pub(crate) fn simd_neg(x: T) -> T; diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index 7c5ec2bc314..a9e99a18c2d 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -75,6 +75,35 @@ pub const fn from_slice(slice: &[T]) -> Self { Self(array) } + /// Performs lanewise conversion of a SIMD vector's elements to another SIMD-valid type. + /// This follows the semantics of Rust's `as` conversion for casting + /// integers to unsigned integers (interpreting as the other type, so `-1` to `MAX`), + /// and from floats to integers (truncating, or saturating at the limits) for each lane, + /// or vice versa. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "std")] use core_simd::Simd; + /// # #[cfg(not(feature = "std"))] use core::simd::Simd; + /// let floats: Simd = Simd::from_array([1.9, -4.5, f32::INFINITY, f32::NAN]); + /// let ints = floats.cast::(); + /// assert_eq!(ints, Simd::from_array([1, -4, i32::MAX, 0])); + /// + /// // Formally equivalent, but `Simd::cast` can optimize better. + /// assert_eq!(ints, Simd::from_array(floats.to_array().map(|x| x as i32))); + /// + /// // The float conversion does not round-trip. + /// let floats_again = ints.cast(); + /// assert_ne!(floats, floats_again); + /// assert_eq!(floats_again, Simd::from_array([1.0, -4.0, 2147483647.0, 0.0])); + /// ``` + #[must_use] + #[inline] + pub fn cast(self) -> Simd { + unsafe { intrinsics::simd_as(self) } + } + /// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector. /// If an index is out-of-bounds, the lane is instead selected from the `or` vector. /// From 0031b02cee0c7679120c7a942c378cd13bfb5021 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Wed, 26 Jan 2022 20:54:05 -0800 Subject: [PATCH 2/2] Add core_simd/tests/cast.rs --- crates/core_simd/tests/cast.rs | 37 ++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 crates/core_simd/tests/cast.rs diff --git a/crates/core_simd/tests/cast.rs b/crates/core_simd/tests/cast.rs new file mode 100644 index 00000000000..ab5650f0713 --- /dev/null +++ b/crates/core_simd/tests/cast.rs @@ -0,0 +1,37 @@ +#![feature(portable_simd)] +macro_rules! cast_types { + ($start:ident, $($target:ident),*) => { + mod $start { + use core_simd::simd::Simd; + type Vector = Simd<$start, N>; + $( + mod $target { + use super::*; + test_helpers::test_lanes! { + fn cast_as() { + test_helpers::test_unary_elementwise( + &Vector::::cast::<$target>, + &|x| x as $target, + &|_| true, + ) + } + } + } + )* + } + }; +} + +// The hypothesis is that widening conversions aren't terribly interesting. +cast_types!(f32, f64, i8, u8, usize, isize); +cast_types!(f64, f32, i8, u8, usize, isize); +cast_types!(i8, u8, f32); +cast_types!(u8, i8, f32); +cast_types!(i16, u16, i8, u8, f32); +cast_types!(u16, i16, i8, u8, f32); +cast_types!(i32, u32, i8, u8, f32, f64); +cast_types!(u32, i32, i8, u8, f32, f64); +cast_types!(i64, u64, i8, u8, isize, usize, f32, f64); +cast_types!(u64, i64, i8, u8, isize, usize, f32, f64); +cast_types!(isize, usize, i8, u8, f32, f64); +cast_types!(usize, isize, i8, u8, f32, f64);