about summary refs log tree commit diff
diff options
context:
space:
mode:
authorCaleb Zulawski <caleb.zulawski@gmail.com>2023-07-30 15:53:32 -0400
committerCaleb Zulawski <caleb.zulawski@gmail.com>2023-07-30 15:53:32 -0400
commit8101074e2e1b8fb3a7469446746625b7febb7f33 (patch)
tree3fc567eb816eec6e050e0b493e0078614f2819be
parent490b5cf7a123c10c69b15816e349e2ee7025ee1a (diff)
downloadrust-8101074e2e1b8fb3a7469446746625b7febb7f33.tar.gz
rust-8101074e2e1b8fb3a7469446746625b7febb7f33.zip
Add various integer ops
-rw-r--r--crates/core_simd/src/elements/int.rs59
-rw-r--r--crates/core_simd/src/elements/uint.rs53
-rw-r--r--crates/core_simd/src/intrinsics.rs6
-rw-r--r--crates/core_simd/src/to_bytes.rs44
-rw-r--r--crates/core_simd/tests/ops_macros.rs48
-rw-r--r--crates/core_simd/tests/to_bytes.rs16
6 files changed, 220 insertions, 6 deletions
diff --git a/crates/core_simd/src/elements/int.rs b/crates/core_simd/src/elements/int.rs
index 6db89ff9a65..6992b679515 100644
--- a/crates/core_simd/src/elements/int.rs
+++ b/crates/core_simd/src/elements/int.rs
@@ -191,10 +191,29 @@ pub trait SimdInt: Copy + Sealed {
 
     /// Returns the cumulative bitwise "xor" across the lanes of the vector.
     fn reduce_xor(self) -> Self::Scalar;
+
+    /// Reverses the byte order of each element.
+    fn swap_bytes(self) -> Self;
+
+    /// Reverses the order of bits in each elemnent.
+    /// The least significant bit becomes the most significant bit, second least-significant bit becomes second most-significant bit, etc.
+    fn reverse_bits(self) -> Self;
+
+    /// Returns the number of leading zeros in the binary representation of each element.
+    fn leading_zeros(self) -> Self;
+
+    /// Returns the number of trailing zeros in the binary representation of each element.
+    fn trailing_zeros(self) -> Self;
+
+    /// Returns the number of leading ones in the binary representation of each element.
+    fn leading_ones(self) -> Self;
+
+    /// Returns the number of trailing ones in the binary representation of each element.
+    fn trailing_ones(self) -> Self;
 }
 
 macro_rules! impl_trait {
-    { $($ty:ty),* } => {
+    { $($ty:ident ($unsigned:ident)),* } => {
         $(
         impl<const LANES: usize> Sealed for Simd<$ty, LANES>
         where
@@ -307,9 +326,45 @@ macro_rules! impl_trait {
                 // Safety: `self` is an integer vector
                 unsafe { intrinsics::simd_reduce_xor(self) }
             }
+
+            #[inline]
+            fn swap_bytes(self) -> Self {
+                // Safety: `self` is an integer vector
+                unsafe { intrinsics::simd_bswap(self) }
+            }
+
+            #[inline]
+            fn reverse_bits(self) -> Self {
+                // Safety: `self` is an integer vector
+                unsafe { intrinsics::simd_bitreverse(self) }
+            }
+
+            #[inline]
+            fn leading_zeros(self) -> Self {
+                // Safety: `self` is an integer vector
+                unsafe { intrinsics::simd_ctlz(self) }
+            }
+
+            #[inline]
+            fn trailing_zeros(self) -> Self {
+                // Safety: `self` is an integer vector
+                unsafe { intrinsics::simd_cttz(self) }
+            }
+
+            #[inline]
+            fn leading_ones(self) -> Self {
+                use crate::simd::SimdUint;
+                self.cast::<$unsigned>().leading_ones().cast()
+            }
+
+            #[inline]
+            fn trailing_ones(self) -> Self {
+                use crate::simd::SimdUint;
+                self.cast::<$unsigned>().trailing_ones().cast()
+            }
         }
         )*
     }
 }
 
-impl_trait! { i8, i16, i32, i64, isize }
+impl_trait! { i8 (u8), i16 (u16), i32 (u32), i64 (u64), isize (usize) }
diff --git a/crates/core_simd/src/elements/uint.rs b/crates/core_simd/src/elements/uint.rs
index 3926c395ec9..7490340e4f4 100644
--- a/crates/core_simd/src/elements/uint.rs
+++ b/crates/core_simd/src/elements/uint.rs
@@ -71,6 +71,25 @@ pub trait SimdUint: Copy + Sealed {
 
     /// Returns the cumulative bitwise "xor" across the lanes of the vector.
     fn reduce_xor(self) -> Self::Scalar;
+
+    /// Reverses the byte order of each element.
+    fn swap_bytes(self) -> Self;
+
+    /// Reverses the order of bits in each elemnent.
+    /// The least significant bit becomes the most significant bit, second least-significant bit becomes second most-significant bit, etc.
+    fn reverse_bits(self) -> Self;
+
+    /// Returns the number of leading zeros in the binary representation of each element.
+    fn leading_zeros(self) -> Self;
+
+    /// Returns the number of trailing zeros in the binary representation of each element.
+    fn trailing_zeros(self) -> Self;
+
+    /// Returns the number of leading ones in the binary representation of each element.
+    fn leading_ones(self) -> Self;
+
+    /// Returns the number of trailing ones in the binary representation of each element.
+    fn trailing_ones(self) -> Self;
 }
 
 macro_rules! impl_trait {
@@ -148,6 +167,40 @@ macro_rules! impl_trait {
                 // Safety: `self` is an integer vector
                 unsafe { intrinsics::simd_reduce_xor(self) }
             }
+
+            #[inline]
+            fn swap_bytes(self) -> Self {
+                // Safety: `self` is an integer vector
+                unsafe { intrinsics::simd_bswap(self) }
+            }
+
+            #[inline]
+            fn reverse_bits(self) -> Self {
+                // Safety: `self` is an integer vector
+                unsafe { intrinsics::simd_bitreverse(self) }
+            }
+
+            #[inline]
+            fn leading_zeros(self) -> Self {
+                // Safety: `self` is an integer vector
+                unsafe { intrinsics::simd_ctlz(self) }
+            }
+
+            #[inline]
+            fn trailing_zeros(self) -> Self {
+                // Safety: `self` is an integer vector
+                unsafe { intrinsics::simd_cttz(self) }
+            }
+
+            #[inline]
+            fn leading_ones(self) -> Self {
+                (!self).leading_zeros()
+            }
+
+            #[inline]
+            fn trailing_ones(self) -> Self {
+                (!self).trailing_zeros()
+            }
         }
         )*
     }
diff --git a/crates/core_simd/src/intrinsics.rs b/crates/core_simd/src/intrinsics.rs
index dd6698e2ba5..b27893bc729 100644
--- a/crates/core_simd/src/intrinsics.rs
+++ b/crates/core_simd/src/intrinsics.rs
@@ -160,4 +160,10 @@ extern "platform-intrinsic" {
 
     /// convert an exposed address back to a pointer
     pub(crate) fn simd_from_exposed_addr<T, U>(addr: T) -> U;
+
+    // Integer operations
+    pub(crate) fn simd_bswap<T>(x: T) -> T;
+    pub(crate) fn simd_bitreverse<T>(x: T) -> T;
+    pub(crate) fn simd_ctlz<T>(x: T) -> T;
+    pub(crate) fn simd_cttz<T>(x: T) -> T;
 }
diff --git a/crates/core_simd/src/to_bytes.rs b/crates/core_simd/src/to_bytes.rs
index b36b1a347b2..563b0c95a8a 100644
--- a/crates/core_simd/src/to_bytes.rs
+++ b/crates/core_simd/src/to_bytes.rs
@@ -1,3 +1,5 @@
+use crate::simd::SimdUint;
+
 macro_rules! impl_to_bytes {
     { $ty:ty, $size:literal } => {
         impl<const LANES: usize> crate::simd::Simd<$ty, LANES>
@@ -12,12 +14,54 @@ macro_rules! impl_to_bytes {
                 unsafe { core::mem::transmute_copy(&self) }
             }
 
+            /// Return the memory representation of this integer as a byte array in big-endian
+            /// (network) byte order.
+            pub fn to_be_bytes(self) -> crate::simd::Simd<u8, {{ $size * LANES }}> {
+                let bytes = self.to_ne_bytes();
+                if cfg!(target_endian = "big") {
+                    bytes
+                } else {
+                    bytes.swap_bytes()
+                }
+            }
+
+            /// Return the memory representation of this integer as a byte array in little-endian
+            /// byte order.
+            pub fn to_le_bytes(self) -> crate::simd::Simd<u8, {{ $size * LANES }}> {
+                let bytes = self.to_ne_bytes();
+                if cfg!(target_endian = "little") {
+                    bytes
+                } else {
+                    bytes.swap_bytes()
+                }
+            }
+
             /// Create a native endian integer value from its memory representation as a byte array
             /// in native endianness.
             pub fn from_ne_bytes(bytes: crate::simd::Simd<u8, {{ $size * LANES }}>) -> Self {
                 // Safety: transmuting between vectors is safe
                 unsafe { core::mem::transmute_copy(&bytes) }
             }
+
+            /// Create an integer value from its representation as a byte array in big endian.
+            pub fn from_be_bytes(bytes: crate::simd::Simd<u8, {{ $size * LANES }}>) -> Self {
+                let bytes = if cfg!(target_endian = "big") {
+                    bytes
+                } else {
+                    bytes.swap_bytes()
+                };
+                Self::from_ne_bytes(bytes)
+            }
+
+            /// Create an integer value from its representation as a byte array in little endian.
+            pub fn from_le_bytes(bytes: crate::simd::Simd<u8, {{ $size * LANES }}>) -> Self {
+                let bytes = if cfg!(target_endian = "little") {
+                    bytes
+                } else {
+                    bytes.swap_bytes()
+                };
+                Self::from_ne_bytes(bytes)
+            }
         }
     }
 }
diff --git a/crates/core_simd/tests/ops_macros.rs b/crates/core_simd/tests/ops_macros.rs
index dfc0e1a3708..bd1856e1bcc 100644
--- a/crates/core_simd/tests/ops_macros.rs
+++ b/crates/core_simd/tests/ops_macros.rs
@@ -193,6 +193,54 @@ macro_rules! impl_common_integer_tests {
                     Ok(())
                 });
             }
+
+            fn swap_bytes<const LANES: usize>() {
+                test_helpers::test_unary_elementwise(
+                    &$vector::<LANES>::swap_bytes,
+                    &$scalar::swap_bytes,
+                    &|_| true,
+                )
+            }
+
+            fn reverse_bits<const LANES: usize>() {
+                test_helpers::test_unary_elementwise(
+                    &$vector::<LANES>::reverse_bits,
+                    &$scalar::reverse_bits,
+                    &|_| true,
+                )
+            }
+
+            fn leading_zeros<const LANES: usize>() {
+                test_helpers::test_unary_elementwise(
+                    &$vector::<LANES>::leading_zeros,
+                    &|x| x.leading_zeros() as $scalar,
+                    &|_| true,
+                )
+            }
+
+            fn trailing_zeros<const LANES: usize>() {
+                test_helpers::test_unary_elementwise(
+                    &$vector::<LANES>::leading_zeros,
+                    &|x| x.trailing_zeros() as $scalar,
+                    &|_| true,
+                )
+            }
+
+            fn leading_ones<const LANES: usize>() {
+                test_helpers::test_unary_elementwise(
+                    &$vector::<LANES>::leading_ones,
+                    &|x| x.leading_ones() as $scalar,
+                    &|_| true,
+                )
+            }
+
+            fn trailing_ones<const LANES: usize>() {
+                test_helpers::test_unary_elementwise(
+                    &$vector::<LANES>::leading_ones,
+                    &|x| x.trailing_ones() as $scalar,
+                    &|_| true,
+                )
+            }
         }
     }
 }
diff --git a/crates/core_simd/tests/to_bytes.rs b/crates/core_simd/tests/to_bytes.rs
index be0ee4349c5..7dd740d65dd 100644
--- a/crates/core_simd/tests/to_bytes.rs
+++ b/crates/core_simd/tests/to_bytes.rs
@@ -7,8 +7,16 @@ use core_simd::simd::Simd;
 #[test]
 fn byte_convert() {
     let int = Simd::<u32, 2>::from_array([0xdeadbeef, 0x8badf00d]);
-    let bytes = int.to_ne_bytes();
-    assert_eq!(int[0].to_ne_bytes(), bytes[..4]);
-    assert_eq!(int[1].to_ne_bytes(), bytes[4..]);
-    assert_eq!(Simd::<u32, 2>::from_ne_bytes(bytes), int);
+    let ne_bytes = int.to_ne_bytes();
+    let be_bytes = int.to_be_bytes();
+    let le_bytes = int.to_le_bytes();
+    assert_eq!(int[0].to_ne_bytes(), ne_bytes[..4]);
+    assert_eq!(int[1].to_ne_bytes(), ne_bytes[4..]);
+    assert_eq!(int[0].to_be_bytes(), be_bytes[..4]);
+    assert_eq!(int[1].to_be_bytes(), be_bytes[4..]);
+    assert_eq!(int[0].to_le_bytes(), le_bytes[..4]);
+    assert_eq!(int[1].to_le_bytes(), le_bytes[4..]);
+    assert_eq!(Simd::<u32, 2>::from_ne_bytes(ne_bytes), int);
+    assert_eq!(Simd::<u32, 2>::from_be_bytes(be_bytes), int);
+    assert_eq!(Simd::<u32, 2>::from_le_bytes(le_bytes), int);
 }