Add various integer ops
This commit is contained in:
parent
490b5cf7a1
commit
8101074e2e
@ -191,10 +191,29 @@ pub trait SimdInt: Copy + Sealed {
|
|||||||
|
|
||||||
/// Returns the cumulative bitwise "xor" across the lanes of the vector.
|
/// Returns the cumulative bitwise "xor" across the lanes of the vector.
|
||||||
fn reduce_xor(self) -> Self::Scalar;
|
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 {
|
macro_rules! impl_trait {
|
||||||
{ $($ty:ty),* } => {
|
{ $($ty:ident ($unsigned:ident)),* } => {
|
||||||
$(
|
$(
|
||||||
impl<const LANES: usize> Sealed for Simd<$ty, LANES>
|
impl<const LANES: usize> Sealed for Simd<$ty, LANES>
|
||||||
where
|
where
|
||||||
@ -307,9 +326,45 @@ macro_rules! impl_trait {
|
|||||||
// Safety: `self` is an integer vector
|
// Safety: `self` is an integer vector
|
||||||
unsafe { intrinsics::simd_reduce_xor(self) }
|
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) }
|
||||||
|
@ -71,6 +71,25 @@ pub trait SimdUint: Copy + Sealed {
|
|||||||
|
|
||||||
/// Returns the cumulative bitwise "xor" across the lanes of the vector.
|
/// Returns the cumulative bitwise "xor" across the lanes of the vector.
|
||||||
fn reduce_xor(self) -> Self::Scalar;
|
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 {
|
macro_rules! impl_trait {
|
||||||
@ -148,6 +167,40 @@ macro_rules! impl_trait {
|
|||||||
// Safety: `self` is an integer vector
|
// Safety: `self` is an integer vector
|
||||||
unsafe { intrinsics::simd_reduce_xor(self) }
|
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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
|
@ -160,4 +160,10 @@ extern "platform-intrinsic" {
|
|||||||
|
|
||||||
/// convert an exposed address back to a pointer
|
/// convert an exposed address back to a pointer
|
||||||
pub(crate) fn simd_from_exposed_addr<T, U>(addr: T) -> U;
|
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;
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use crate::simd::SimdUint;
|
||||||
|
|
||||||
macro_rules! impl_to_bytes {
|
macro_rules! impl_to_bytes {
|
||||||
{ $ty:ty, $size:literal } => {
|
{ $ty:ty, $size:literal } => {
|
||||||
impl<const LANES: usize> crate::simd::Simd<$ty, LANES>
|
impl<const LANES: usize> crate::simd::Simd<$ty, LANES>
|
||||||
@ -12,12 +14,54 @@ macro_rules! impl_to_bytes {
|
|||||||
unsafe { core::mem::transmute_copy(&self) }
|
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
|
/// Create a native endian integer value from its memory representation as a byte array
|
||||||
/// in native endianness.
|
/// in native endianness.
|
||||||
pub fn from_ne_bytes(bytes: crate::simd::Simd<u8, {{ $size * LANES }}>) -> Self {
|
pub fn from_ne_bytes(bytes: crate::simd::Simd<u8, {{ $size * LANES }}>) -> Self {
|
||||||
// Safety: transmuting between vectors is safe
|
// Safety: transmuting between vectors is safe
|
||||||
unsafe { core::mem::transmute_copy(&bytes) }
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,6 +193,54 @@ macro_rules! impl_common_integer_tests {
|
|||||||
Ok(())
|
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,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,16 @@ use core_simd::simd::Simd;
|
|||||||
#[test]
|
#[test]
|
||||||
fn byte_convert() {
|
fn byte_convert() {
|
||||||
let int = Simd::<u32, 2>::from_array([0xdeadbeef, 0x8badf00d]);
|
let int = Simd::<u32, 2>::from_array([0xdeadbeef, 0x8badf00d]);
|
||||||
let bytes = int.to_ne_bytes();
|
let ne_bytes = int.to_ne_bytes();
|
||||||
assert_eq!(int[0].to_ne_bytes(), bytes[..4]);
|
let be_bytes = int.to_be_bytes();
|
||||||
assert_eq!(int[1].to_ne_bytes(), bytes[4..]);
|
let le_bytes = int.to_le_bytes();
|
||||||
assert_eq!(Simd::<u32, 2>::from_ne_bytes(bytes), int);
|
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);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user