Skip to content

Commit

Permalink
Return unsigned integers from some signed integer functions
Browse files Browse the repository at this point in the history
  • Loading branch information
calebzulawski committed Sep 10, 2023
1 parent c948b70 commit 0a1e745
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 21 deletions.
37 changes: 20 additions & 17 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 @@ -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<const LANES: usize> Sealed for Simd<$ty, LANES>
where
Expand All @@ -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<T: SimdElement> = Simd<T, LANES>;

#[inline]
Expand Down Expand Up @@ -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) }
8 changes: 4 additions & 4 deletions crates/core_simd/tests/ops_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,31 +213,31 @@ macro_rules! impl_common_integer_tests {
fn leading_zeros<const LANES: usize>() {
test_helpers::test_unary_elementwise(
&$vector::<LANES>::leading_zeros,
&|x| x.leading_zeros() as $scalar,
&|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 $scalar,
&|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 $scalar,
&|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 $scalar,
&|x| x.trailing_ones() as _,
&|_| true,
)
}
Expand Down

2 comments on commit 0a1e745

@sammysheep
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was trying to use these in a const generic way and got stuck I think because the return types all differ.

I like this change to unsigned, but is there any advantage to making each return type different over choosing a fixed unsigned integer that would be sufficient for all valid vector sizes?

@calebzulawski
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be efficient, the return type must have the same width as the input. Functions that change the total vector size are often not very efficient. You could use cast to produce a uniform result type, but it will take more instructions.

Please sign in to comment.