Skip to content

Add various integer ops #362

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 59 additions & 3 deletions crates/core_simd/src/elements/int.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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<T: SimdElement>;

Expand Down Expand Up @@ -191,10 +195,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::Unsigned;

/// Returns the number of trailing zeros in the binary representation of each element.
fn trailing_zeros(self) -> Self::Unsigned;

/// Returns the number of leading ones in the binary representation of each element.
fn leading_ones(self) -> Self::Unsigned;

/// Returns the number of trailing ones in the binary representation of each element.
fn trailing_ones(self) -> Self::Unsigned;
}

macro_rules! impl_trait {
{ $($ty:ty),* } => {
{ $($ty:ident ($unsigned:ident)),* } => {
$(
impl<const LANES: usize> Sealed for Simd<$ty, LANES>
where
Expand All @@ -208,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<T: SimdElement> = Simd<T, LANES>;

#[inline]
Expand Down Expand Up @@ -307,9 +331,41 @@ 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::Unsigned {
self.cast::<$unsigned>().leading_zeros()
}

#[inline]
fn trailing_zeros(self) -> Self::Unsigned {
self.cast::<$unsigned>().trailing_zeros()
}

#[inline]
fn leading_ones(self) -> Self::Unsigned {
self.cast::<$unsigned>().leading_ones()
}

#[inline]
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) }
53 changes: 53 additions & 0 deletions crates/core_simd/src/elements/uint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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()
}
}
)*
}
Expand Down
6 changes: 6 additions & 0 deletions crates/core_simd/src/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
44 changes: 44 additions & 0 deletions crates/core_simd/src/to_bytes.rs
Original file line number Diff line number Diff line change
@@ -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>
Expand All @@ -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)
}
}
}
}
Expand Down
48 changes: 48 additions & 0 deletions crates/core_simd/tests/ops_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 _,
&|_| true,
)
}

fn trailing_zeros<const LANES: usize>() {
test_helpers::test_unary_elementwise(
&$vector::<LANES>::trailing_zeros,
&|x| x.trailing_zeros() as _,
&|_| true,
)
}

fn leading_ones<const LANES: usize>() {
test_helpers::test_unary_elementwise(
&$vector::<LANES>::leading_ones,
&|x| x.leading_ones() as _,
&|_| true,
)
}

fn trailing_ones<const LANES: usize>() {
test_helpers::test_unary_elementwise(
&$vector::<LANES>::trailing_ones,
&|x| x.trailing_ones() as _,
&|_| true,
)
}
}
}
}
Expand Down
16 changes: 12 additions & 4 deletions crates/core_simd/tests/to_bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}