Skip to content

Commit 22f50df

Browse files
Merge pull request #362 from rust-lang/int-ops
Add various integer ops
2 parents 7ce6ec9 + 0a1e745 commit 22f50df

File tree

6 files changed

+222
-7
lines changed

6 files changed

+222
-7
lines changed

crates/core_simd/src/elements/int.rs

+59-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use super::sealed::Sealed;
22
use crate::simd::{
3-
intrinsics, LaneCount, Mask, Simd, SimdCast, SimdElement, SimdPartialOrd, SupportedLaneCount,
3+
intrinsics, LaneCount, Mask, Simd, SimdCast, SimdElement, SimdPartialOrd, SimdUint,
4+
SupportedLaneCount,
45
};
56

67
/// Operations on SIMD vectors of signed integers.
@@ -11,6 +12,9 @@ pub trait SimdInt: Copy + Sealed {
1112
/// Scalar type contained by this SIMD vector type.
1213
type Scalar;
1314

15+
/// A SIMD vector of unsigned integers with the same element size.
16+
type Unsigned;
17+
1418
/// A SIMD vector with a different element type.
1519
type Cast<T: SimdElement>;
1620

@@ -191,10 +195,29 @@ pub trait SimdInt: Copy + Sealed {
191195

192196
/// Returns the cumulative bitwise "xor" across the lanes of the vector.
193197
fn reduce_xor(self) -> Self::Scalar;
198+
199+
/// Reverses the byte order of each element.
200+
fn swap_bytes(self) -> Self;
201+
202+
/// Reverses the order of bits in each elemnent.
203+
/// The least significant bit becomes the most significant bit, second least-significant bit becomes second most-significant bit, etc.
204+
fn reverse_bits(self) -> Self;
205+
206+
/// Returns the number of leading zeros in the binary representation of each element.
207+
fn leading_zeros(self) -> Self::Unsigned;
208+
209+
/// Returns the number of trailing zeros in the binary representation of each element.
210+
fn trailing_zeros(self) -> Self::Unsigned;
211+
212+
/// Returns the number of leading ones in the binary representation of each element.
213+
fn leading_ones(self) -> Self::Unsigned;
214+
215+
/// Returns the number of trailing ones in the binary representation of each element.
216+
fn trailing_ones(self) -> Self::Unsigned;
194217
}
195218

196219
macro_rules! impl_trait {
197-
{ $($ty:ty),* } => {
220+
{ $($ty:ident ($unsigned:ident)),* } => {
198221
$(
199222
impl<const LANES: usize> Sealed for Simd<$ty, LANES>
200223
where
@@ -208,6 +231,7 @@ macro_rules! impl_trait {
208231
{
209232
type Mask = Mask<<$ty as SimdElement>::Mask, LANES>;
210233
type Scalar = $ty;
234+
type Unsigned = Simd<$unsigned, LANES>;
211235
type Cast<T: SimdElement> = Simd<T, LANES>;
212236

213237
#[inline]
@@ -307,9 +331,41 @@ macro_rules! impl_trait {
307331
// Safety: `self` is an integer vector
308332
unsafe { intrinsics::simd_reduce_xor(self) }
309333
}
334+
335+
#[inline]
336+
fn swap_bytes(self) -> Self {
337+
// Safety: `self` is an integer vector
338+
unsafe { intrinsics::simd_bswap(self) }
339+
}
340+
341+
#[inline]
342+
fn reverse_bits(self) -> Self {
343+
// Safety: `self` is an integer vector
344+
unsafe { intrinsics::simd_bitreverse(self) }
345+
}
346+
347+
#[inline]
348+
fn leading_zeros(self) -> Self::Unsigned {
349+
self.cast::<$unsigned>().leading_zeros()
350+
}
351+
352+
#[inline]
353+
fn trailing_zeros(self) -> Self::Unsigned {
354+
self.cast::<$unsigned>().trailing_zeros()
355+
}
356+
357+
#[inline]
358+
fn leading_ones(self) -> Self::Unsigned {
359+
self.cast::<$unsigned>().leading_ones()
360+
}
361+
362+
#[inline]
363+
fn trailing_ones(self) -> Self::Unsigned {
364+
self.cast::<$unsigned>().trailing_ones()
365+
}
310366
}
311367
)*
312368
}
313369
}
314370

315-
impl_trait! { i8, i16, i32, i64, isize }
371+
impl_trait! { i8 (u8), i16 (u16), i32 (u32), i64 (u64), isize (usize) }

crates/core_simd/src/elements/uint.rs

+53
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,25 @@ pub trait SimdUint: Copy + Sealed {
7777

7878
/// Returns the cumulative bitwise "xor" across the lanes of the vector.
7979
fn reduce_xor(self) -> Self::Scalar;
80+
81+
/// Reverses the byte order of each element.
82+
fn swap_bytes(self) -> Self;
83+
84+
/// Reverses the order of bits in each elemnent.
85+
/// The least significant bit becomes the most significant bit, second least-significant bit becomes second most-significant bit, etc.
86+
fn reverse_bits(self) -> Self;
87+
88+
/// Returns the number of leading zeros in the binary representation of each element.
89+
fn leading_zeros(self) -> Self;
90+
91+
/// Returns the number of trailing zeros in the binary representation of each element.
92+
fn trailing_zeros(self) -> Self;
93+
94+
/// Returns the number of leading ones in the binary representation of each element.
95+
fn leading_ones(self) -> Self;
96+
97+
/// Returns the number of trailing ones in the binary representation of each element.
98+
fn trailing_ones(self) -> Self;
8099
}
81100

82101
macro_rules! impl_trait {
@@ -160,6 +179,40 @@ macro_rules! impl_trait {
160179
// Safety: `self` is an integer vector
161180
unsafe { intrinsics::simd_reduce_xor(self) }
162181
}
182+
183+
#[inline]
184+
fn swap_bytes(self) -> Self {
185+
// Safety: `self` is an integer vector
186+
unsafe { intrinsics::simd_bswap(self) }
187+
}
188+
189+
#[inline]
190+
fn reverse_bits(self) -> Self {
191+
// Safety: `self` is an integer vector
192+
unsafe { intrinsics::simd_bitreverse(self) }
193+
}
194+
195+
#[inline]
196+
fn leading_zeros(self) -> Self {
197+
// Safety: `self` is an integer vector
198+
unsafe { intrinsics::simd_ctlz(self) }
199+
}
200+
201+
#[inline]
202+
fn trailing_zeros(self) -> Self {
203+
// Safety: `self` is an integer vector
204+
unsafe { intrinsics::simd_cttz(self) }
205+
}
206+
207+
#[inline]
208+
fn leading_ones(self) -> Self {
209+
(!self).leading_zeros()
210+
}
211+
212+
#[inline]
213+
fn trailing_ones(self) -> Self {
214+
(!self).trailing_zeros()
215+
}
163216
}
164217
)*
165218
}

crates/core_simd/src/intrinsics.rs

+6
Original file line numberDiff line numberDiff line change
@@ -160,4 +160,10 @@ extern "platform-intrinsic" {
160160

161161
/// convert an exposed address back to a pointer
162162
pub(crate) fn simd_from_exposed_addr<T, U>(addr: T) -> U;
163+
164+
// Integer operations
165+
pub(crate) fn simd_bswap<T>(x: T) -> T;
166+
pub(crate) fn simd_bitreverse<T>(x: T) -> T;
167+
pub(crate) fn simd_ctlz<T>(x: T) -> T;
168+
pub(crate) fn simd_cttz<T>(x: T) -> T;
163169
}

crates/core_simd/src/to_bytes.rs

+44
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use crate::simd::SimdUint;
2+
13
macro_rules! impl_to_bytes {
24
{ $ty:ty, $size:literal } => {
35
impl<const LANES: usize> crate::simd::Simd<$ty, LANES>
@@ -12,12 +14,54 @@ macro_rules! impl_to_bytes {
1214
unsafe { core::mem::transmute_copy(&self) }
1315
}
1416

17+
/// Return the memory representation of this integer as a byte array in big-endian
18+
/// (network) byte order.
19+
pub fn to_be_bytes(self) -> crate::simd::Simd<u8, {{ $size * LANES }}> {
20+
let bytes = self.to_ne_bytes();
21+
if cfg!(target_endian = "big") {
22+
bytes
23+
} else {
24+
bytes.swap_bytes()
25+
}
26+
}
27+
28+
/// Return the memory representation of this integer as a byte array in little-endian
29+
/// byte order.
30+
pub fn to_le_bytes(self) -> crate::simd::Simd<u8, {{ $size * LANES }}> {
31+
let bytes = self.to_ne_bytes();
32+
if cfg!(target_endian = "little") {
33+
bytes
34+
} else {
35+
bytes.swap_bytes()
36+
}
37+
}
38+
1539
/// Create a native endian integer value from its memory representation as a byte array
1640
/// in native endianness.
1741
pub fn from_ne_bytes(bytes: crate::simd::Simd<u8, {{ $size * LANES }}>) -> Self {
1842
// Safety: transmuting between vectors is safe
1943
unsafe { core::mem::transmute_copy(&bytes) }
2044
}
45+
46+
/// Create an integer value from its representation as a byte array in big endian.
47+
pub fn from_be_bytes(bytes: crate::simd::Simd<u8, {{ $size * LANES }}>) -> Self {
48+
let bytes = if cfg!(target_endian = "big") {
49+
bytes
50+
} else {
51+
bytes.swap_bytes()
52+
};
53+
Self::from_ne_bytes(bytes)
54+
}
55+
56+
/// Create an integer value from its representation as a byte array in little endian.
57+
pub fn from_le_bytes(bytes: crate::simd::Simd<u8, {{ $size * LANES }}>) -> Self {
58+
let bytes = if cfg!(target_endian = "little") {
59+
bytes
60+
} else {
61+
bytes.swap_bytes()
62+
};
63+
Self::from_ne_bytes(bytes)
64+
}
2165
}
2266
}
2367
}

crates/core_simd/tests/ops_macros.rs

+48
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,54 @@ macro_rules! impl_common_integer_tests {
197197
Ok(())
198198
});
199199
}
200+
201+
fn swap_bytes<const LANES: usize>() {
202+
test_helpers::test_unary_elementwise(
203+
&$vector::<LANES>::swap_bytes,
204+
&$scalar::swap_bytes,
205+
&|_| true,
206+
)
207+
}
208+
209+
fn reverse_bits<const LANES: usize>() {
210+
test_helpers::test_unary_elementwise(
211+
&$vector::<LANES>::reverse_bits,
212+
&$scalar::reverse_bits,
213+
&|_| true,
214+
)
215+
}
216+
217+
fn leading_zeros<const LANES: usize>() {
218+
test_helpers::test_unary_elementwise(
219+
&$vector::<LANES>::leading_zeros,
220+
&|x| x.leading_zeros() as _,
221+
&|_| true,
222+
)
223+
}
224+
225+
fn trailing_zeros<const LANES: usize>() {
226+
test_helpers::test_unary_elementwise(
227+
&$vector::<LANES>::trailing_zeros,
228+
&|x| x.trailing_zeros() as _,
229+
&|_| true,
230+
)
231+
}
232+
233+
fn leading_ones<const LANES: usize>() {
234+
test_helpers::test_unary_elementwise(
235+
&$vector::<LANES>::leading_ones,
236+
&|x| x.leading_ones() as _,
237+
&|_| true,
238+
)
239+
}
240+
241+
fn trailing_ones<const LANES: usize>() {
242+
test_helpers::test_unary_elementwise(
243+
&$vector::<LANES>::trailing_ones,
244+
&|x| x.trailing_ones() as _,
245+
&|_| true,
246+
)
247+
}
200248
}
201249
}
202250
}

crates/core_simd/tests/to_bytes.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,16 @@ use core_simd::simd::Simd;
77
#[test]
88
fn byte_convert() {
99
let int = Simd::<u32, 2>::from_array([0xdeadbeef, 0x8badf00d]);
10-
let bytes = int.to_ne_bytes();
11-
assert_eq!(int[0].to_ne_bytes(), bytes[..4]);
12-
assert_eq!(int[1].to_ne_bytes(), bytes[4..]);
13-
assert_eq!(Simd::<u32, 2>::from_ne_bytes(bytes), int);
10+
let ne_bytes = int.to_ne_bytes();
11+
let be_bytes = int.to_be_bytes();
12+
let le_bytes = int.to_le_bytes();
13+
assert_eq!(int[0].to_ne_bytes(), ne_bytes[..4]);
14+
assert_eq!(int[1].to_ne_bytes(), ne_bytes[4..]);
15+
assert_eq!(int[0].to_be_bytes(), be_bytes[..4]);
16+
assert_eq!(int[1].to_be_bytes(), be_bytes[4..]);
17+
assert_eq!(int[0].to_le_bytes(), le_bytes[..4]);
18+
assert_eq!(int[1].to_le_bytes(), le_bytes[4..]);
19+
assert_eq!(Simd::<u32, 2>::from_ne_bytes(ne_bytes), int);
20+
assert_eq!(Simd::<u32, 2>::from_be_bytes(be_bytes), int);
21+
assert_eq!(Simd::<u32, 2>::from_le_bytes(le_bytes), int);
1422
}

0 commit comments

Comments
 (0)