about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJubilee <46493976+workingjubilee@users.noreply.github.com>2022-01-26 22:21:00 -0800
committerGitHub <noreply@github.com>2022-01-26 22:21:00 -0800
commit7072c2278ed46bf902b1d14900bf2d0875237413 (patch)
treec0a0f4cc949e2f020fea25751f5f182c2f547503
parentcad74346fb8f55f0e3a7dc7c4b64b56681d8ee2b (diff)
parent0031b02cee0c7679120c7a942c378cd13bfb5021 (diff)
downloadrust-7072c2278ed46bf902b1d14900bf2d0875237413.tar.gz
rust-7072c2278ed46bf902b1d14900bf2d0875237413.zip
Merge portable-simd#232 - ./feat/typecast
Add Simd::cast
Add core_simd/tests/cast.rs
-rw-r--r--crates/core_simd/src/intrinsics.rs3
-rw-r--r--crates/core_simd/src/vector.rs29
-rw-r--r--crates/core_simd/tests/cast.rs37
3 files changed, 69 insertions, 0 deletions
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 @@ extern "platform-intrinsic" {
 
     /// fptoui/fptosi/uitofp/sitofp
     pub(crate) fn simd_cast<T, U>(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<T, U>(x: T) -> U;
 
     /// neg/fneg
     pub(crate) fn simd_neg<T>(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 @@ where
         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<f32, 4> = Simd::from_array([1.9, -4.5, f32::INFINITY, f32::NAN]);
+    /// let ints = floats.cast::<i32>();
+    /// 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<U: SimdElement>(self) -> Simd<U, LANES> {
+        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.
     ///
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<const N: usize> = Simd<$start, N>;
+            $(
+                mod $target {
+                    use super::*;
+                    test_helpers::test_lanes! {
+                        fn cast_as<const N: usize>() {
+                            test_helpers::test_unary_elementwise(
+                                &Vector::<N>::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);