From 8101074e2e1b8fb3a7469446746625b7febb7f33 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sun, 30 Jul 2023 15:53:32 -0400 Subject: [PATCH 1/4] Add various integer ops --- crates/core_simd/src/elements/int.rs | 59 ++++++++++++++++++++++++++- crates/core_simd/src/elements/uint.rs | 53 ++++++++++++++++++++++++ crates/core_simd/src/intrinsics.rs | 6 +++ crates/core_simd/src/to_bytes.rs | 44 ++++++++++++++++++++ crates/core_simd/tests/ops_macros.rs | 48 ++++++++++++++++++++++ crates/core_simd/tests/to_bytes.rs | 16 ++++++-- 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 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(addr: T) -> U; + + // Integer operations + pub(crate) fn simd_bswap(x: T) -> T; + pub(crate) fn simd_bitreverse(x: T) -> T; + pub(crate) fn simd_ctlz(x: T) -> T; + pub(crate) fn simd_cttz(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 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 { + 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 { + 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) -> 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) -> 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) -> 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() { + test_helpers::test_unary_elementwise( + &$vector::::swap_bytes, + &$scalar::swap_bytes, + &|_| true, + ) + } + + fn reverse_bits() { + test_helpers::test_unary_elementwise( + &$vector::::reverse_bits, + &$scalar::reverse_bits, + &|_| true, + ) + } + + fn leading_zeros() { + test_helpers::test_unary_elementwise( + &$vector::::leading_zeros, + &|x| x.leading_zeros() as $scalar, + &|_| true, + ) + } + + fn trailing_zeros() { + test_helpers::test_unary_elementwise( + &$vector::::leading_zeros, + &|x| x.trailing_zeros() as $scalar, + &|_| true, + ) + } + + fn leading_ones() { + test_helpers::test_unary_elementwise( + &$vector::::leading_ones, + &|x| x.leading_ones() as $scalar, + &|_| true, + ) + } + + fn trailing_ones() { + test_helpers::test_unary_elementwise( + &$vector::::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::::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::::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::::from_ne_bytes(ne_bytes), int); + assert_eq!(Simd::::from_be_bytes(be_bytes), int); + assert_eq!(Simd::::from_le_bytes(le_bytes), int); } From b1245ffb1277ad4274f600607f9058281baf3bc6 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sun, 30 Jul 2023 16:20:20 -0400 Subject: [PATCH 2/4] Fix bad copy-paste --- crates/core_simd/tests/ops_macros.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/core_simd/tests/ops_macros.rs b/crates/core_simd/tests/ops_macros.rs index bd1856e1bcc..23e914e64b5 100644 --- a/crates/core_simd/tests/ops_macros.rs +++ b/crates/core_simd/tests/ops_macros.rs @@ -220,7 +220,7 @@ macro_rules! impl_common_integer_tests { fn trailing_zeros() { test_helpers::test_unary_elementwise( - &$vector::::leading_zeros, + &$vector::::trailing_zeros, &|x| x.trailing_zeros() as $scalar, &|_| true, ) @@ -236,7 +236,7 @@ macro_rules! impl_common_integer_tests { fn trailing_ones() { test_helpers::test_unary_elementwise( - &$vector::::leading_ones, + &$vector::::trailing_ones, &|x| x.trailing_ones() as $scalar, &|_| true, ) From c948b703ff57f25a1a41be5e03553065454080b9 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Tue, 1 Aug 2023 07:58:44 -0400 Subject: [PATCH 3/4] Simplify signed leading_ones/trailing_ones --- crates/core_simd/src/elements/int.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/crates/core_simd/src/elements/int.rs b/crates/core_simd/src/elements/int.rs index 6992b679515..e81ed7bf601 100644 --- a/crates/core_simd/src/elements/int.rs +++ b/crates/core_simd/src/elements/int.rs @@ -213,7 +213,7 @@ pub trait SimdInt: Copy + Sealed { } macro_rules! impl_trait { - { $($ty:ident ($unsigned:ident)),* } => { + { $($ty:ty),* } => { $( impl Sealed for Simd<$ty, LANES> where @@ -353,18 +353,16 @@ macro_rules! impl_trait { #[inline] fn leading_ones(self) -> Self { - use crate::simd::SimdUint; - self.cast::<$unsigned>().leading_ones().cast() + (!self).leading_zeros() } #[inline] fn trailing_ones(self) -> Self { - use crate::simd::SimdUint; - self.cast::<$unsigned>().trailing_ones().cast() + (!self).trailing_zeros() } } )* } } -impl_trait! { i8 (u8), i16 (u16), i32 (u32), i64 (u64), isize (usize) } +impl_trait! { i8, i16, i32, i64, isize } From 0a1e7453204383a109f4854cc588fd3b35c990c6 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sun, 10 Sep 2023 18:20:47 -0400 Subject: [PATCH 4/4] Return unsigned integers from some signed integer functions --- crates/core_simd/src/elements/int.rs | 37 +++++++++++++++------------- crates/core_simd/tests/ops_macros.rs | 8 +++--- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/crates/core_simd/src/elements/int.rs b/crates/core_simd/src/elements/int.rs index e81ed7bf601..c341c59545c 100644 --- a/crates/core_simd/src/elements/int.rs +++ b/crates/core_simd/src/elements/int.rs @@ -1,6 +1,7 @@ use super::sealed::Sealed; use crate::simd::{ - intrinsics, LaneCount, Mask, Simd, SimdCast, SimdElement, SimdPartialOrd, SupportedLaneCount, + intrinsics, LaneCount, Mask, Simd, SimdCast, SimdElement, SimdPartialOrd, SimdUint, + SupportedLaneCount, }; /// Operations on SIMD vectors of signed integers. @@ -11,6 +12,9 @@ pub trait SimdInt: Copy + Sealed { /// Scalar type contained by this SIMD vector type. type Scalar; + /// A SIMD vector of unsigned integers with the same element size. + type Unsigned; + /// A SIMD vector with a different element type. type Cast; @@ -200,20 +204,20 @@ pub trait SimdInt: Copy + Sealed { fn reverse_bits(self) -> Self; /// Returns the number of leading zeros in the binary representation of each element. - fn leading_zeros(self) -> Self; + fn leading_zeros(self) -> Self::Unsigned; /// Returns the number of trailing zeros in the binary representation of each element. - fn trailing_zeros(self) -> Self; + fn trailing_zeros(self) -> Self::Unsigned; /// Returns the number of leading ones in the binary representation of each element. - fn leading_ones(self) -> Self; + fn leading_ones(self) -> Self::Unsigned; /// Returns the number of trailing ones in the binary representation of each element. - fn trailing_ones(self) -> Self; + fn trailing_ones(self) -> Self::Unsigned; } macro_rules! impl_trait { - { $($ty:ty),* } => { + { $($ty:ident ($unsigned:ident)),* } => { $( impl Sealed for Simd<$ty, LANES> where @@ -227,6 +231,7 @@ macro_rules! impl_trait { { type Mask = Mask<<$ty as SimdElement>::Mask, LANES>; type Scalar = $ty; + type Unsigned = Simd<$unsigned, LANES>; type Cast = Simd; #[inline] @@ -340,29 +345,27 @@ macro_rules! impl_trait { } #[inline] - fn leading_zeros(self) -> Self { - // Safety: `self` is an integer vector - unsafe { intrinsics::simd_ctlz(self) } + fn leading_zeros(self) -> Self::Unsigned { + self.cast::<$unsigned>().leading_zeros() } #[inline] - fn trailing_zeros(self) -> Self { - // Safety: `self` is an integer vector - unsafe { intrinsics::simd_cttz(self) } + fn trailing_zeros(self) -> Self::Unsigned { + self.cast::<$unsigned>().trailing_zeros() } #[inline] - fn leading_ones(self) -> Self { - (!self).leading_zeros() + fn leading_ones(self) -> Self::Unsigned { + self.cast::<$unsigned>().leading_ones() } #[inline] - fn trailing_ones(self) -> Self { - (!self).trailing_zeros() + fn trailing_ones(self) -> Self::Unsigned { + self.cast::<$unsigned>().trailing_ones() } } )* } } -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/tests/ops_macros.rs b/crates/core_simd/tests/ops_macros.rs index 23e914e64b5..135f3ecf7b2 100644 --- a/crates/core_simd/tests/ops_macros.rs +++ b/crates/core_simd/tests/ops_macros.rs @@ -213,7 +213,7 @@ macro_rules! impl_common_integer_tests { fn leading_zeros() { test_helpers::test_unary_elementwise( &$vector::::leading_zeros, - &|x| x.leading_zeros() as $scalar, + &|x| x.leading_zeros() as _, &|_| true, ) } @@ -221,7 +221,7 @@ macro_rules! impl_common_integer_tests { fn trailing_zeros() { test_helpers::test_unary_elementwise( &$vector::::trailing_zeros, - &|x| x.trailing_zeros() as $scalar, + &|x| x.trailing_zeros() as _, &|_| true, ) } @@ -229,7 +229,7 @@ macro_rules! impl_common_integer_tests { fn leading_ones() { test_helpers::test_unary_elementwise( &$vector::::leading_ones, - &|x| x.leading_ones() as $scalar, + &|x| x.leading_ones() as _, &|_| true, ) } @@ -237,7 +237,7 @@ macro_rules! impl_common_integer_tests { fn trailing_ones() { test_helpers::test_unary_elementwise( &$vector::::trailing_ones, - &|x| x.trailing_ones() as $scalar, + &|x| x.trailing_ones() as _, &|_| true, ) }