Skip to content

Commit 5e32004

Browse files
Merge pull request #246 from rust-lang/feature/bitmask_array
Add bitmask array
2 parents c44a608 + bca8dec commit 5e32004

File tree

5 files changed

+143
-0
lines changed

5 files changed

+143
-0
lines changed

crates/core_simd/src/masks.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ mod mask_impl;
1515
mod to_bitmask;
1616
pub use to_bitmask::ToBitMask;
1717

18+
#[cfg(feature = "generic_const_exprs")]
19+
pub use to_bitmask::{bitmask_len, ToBitMaskArray};
20+
1821
use crate::simd::{intrinsics, LaneCount, Simd, SimdElement, SimdPartialEq, SupportedLaneCount};
1922
use core::cmp::Ordering;
2023
use core::{fmt, mem};

crates/core_simd/src/masks/bitmask.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,26 @@ where
115115
unsafe { Self(intrinsics::simd_bitmask(value), PhantomData) }
116116
}
117117

118+
#[cfg(feature = "generic_const_exprs")]
119+
#[inline]
120+
#[must_use = "method returns a new array and does not mutate the original value"]
121+
pub fn to_bitmask_array<const N: usize>(self) -> [u8; N] {
122+
assert!(core::mem::size_of::<Self>() == N);
123+
124+
// Safety: converting an integer to an array of bytes of the same size is safe
125+
unsafe { core::mem::transmute_copy(&self.0) }
126+
}
127+
128+
#[cfg(feature = "generic_const_exprs")]
129+
#[inline]
130+
#[must_use = "method returns a new mask and does not mutate the original value"]
131+
pub fn from_bitmask_array<const N: usize>(bitmask: [u8; N]) -> Self {
132+
assert!(core::mem::size_of::<Self>() == N);
133+
134+
// Safety: converting an array of bytes to an integer of the same size is safe
135+
Self(unsafe { core::mem::transmute_copy(&bitmask) }, PhantomData)
136+
}
137+
118138
#[inline]
119139
pub fn to_bitmask_integer<U>(self) -> U
120140
where

crates/core_simd/src/masks/full_masks.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ use super::MaskElement;
44
use crate::simd::intrinsics;
55
use crate::simd::{LaneCount, Simd, SupportedLaneCount, ToBitMask};
66

7+
#[cfg(feature = "generic_const_exprs")]
8+
use crate::simd::ToBitMaskArray;
9+
710
#[repr(transparent)]
811
pub struct Mask<T, const LANES: usize>(Simd<T, LANES>)
912
where
@@ -139,6 +142,68 @@ where
139142
unsafe { Mask(intrinsics::simd_cast(self.0)) }
140143
}
141144

145+
#[cfg(feature = "generic_const_exprs")]
146+
#[inline]
147+
#[must_use = "method returns a new array and does not mutate the original value"]
148+
pub fn to_bitmask_array<const N: usize>(self) -> [u8; N]
149+
where
150+
super::Mask<T, LANES>: ToBitMaskArray,
151+
[(); <super::Mask<T, LANES> as ToBitMaskArray>::BYTES]: Sized,
152+
{
153+
assert_eq!(<super::Mask<T, LANES> as ToBitMaskArray>::BYTES, N);
154+
155+
// Safety: N is the correct bitmask size
156+
unsafe {
157+
// Compute the bitmask
158+
let bitmask: [u8; <super::Mask<T, LANES> as ToBitMaskArray>::BYTES] =
159+
intrinsics::simd_bitmask(self.0);
160+
161+
// Transmute to the return type, previously asserted to be the same size
162+
let mut bitmask: [u8; N] = core::mem::transmute_copy(&bitmask);
163+
164+
// LLVM assumes bit order should match endianness
165+
if cfg!(target_endian = "big") {
166+
for x in bitmask.as_mut() {
167+
*x = x.reverse_bits();
168+
}
169+
};
170+
171+
bitmask
172+
}
173+
}
174+
175+
#[cfg(feature = "generic_const_exprs")]
176+
#[inline]
177+
#[must_use = "method returns a new mask and does not mutate the original value"]
178+
pub fn from_bitmask_array<const N: usize>(mut bitmask: [u8; N]) -> Self
179+
where
180+
super::Mask<T, LANES>: ToBitMaskArray,
181+
[(); <super::Mask<T, LANES> as ToBitMaskArray>::BYTES]: Sized,
182+
{
183+
assert_eq!(<super::Mask<T, LANES> as ToBitMaskArray>::BYTES, N);
184+
185+
// Safety: N is the correct bitmask size
186+
unsafe {
187+
// LLVM assumes bit order should match endianness
188+
if cfg!(target_endian = "big") {
189+
for x in bitmask.as_mut() {
190+
*x = x.reverse_bits();
191+
}
192+
}
193+
194+
// Transmute to the bitmask type, previously asserted to be the same size
195+
let bitmask: [u8; <super::Mask<T, LANES> as ToBitMaskArray>::BYTES] =
196+
core::mem::transmute_copy(&bitmask);
197+
198+
// Compute the regular mask
199+
Self::from_int_unchecked(intrinsics::simd_select_bitmask(
200+
bitmask,
201+
Self::splat(true).to_int(),
202+
Self::splat(false).to_int(),
203+
))
204+
}
205+
}
206+
142207
#[inline]
143208
pub(crate) fn to_bitmask_integer<U: ReverseBits>(self) -> U
144209
where

crates/core_simd/src/masks/to_bitmask.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,25 @@ pub unsafe trait ToBitMask: Sealed {
3131
fn from_bitmask(bitmask: Self::BitMask) -> Self;
3232
}
3333

34+
/// Converts masks to and from byte array bitmasks.
35+
///
36+
/// Each bit of the bitmask corresponds to a mask lane, starting with the LSB of the first byte.
37+
///
38+
/// # Safety
39+
/// This trait is `unsafe` and sealed, since the `BYTES` value must match the number of lanes in
40+
/// the mask.
41+
#[cfg(feature = "generic_const_exprs")]
42+
pub unsafe trait ToBitMaskArray: Sealed {
43+
/// The length of the bitmask array.
44+
const BYTES: usize;
45+
46+
/// Converts a mask to a bitmask.
47+
fn to_bitmask_array(self) -> [u8; Self::BYTES];
48+
49+
/// Converts a bitmask to a mask.
50+
fn from_bitmask_array(bitmask: [u8; Self::BYTES]) -> Self;
51+
}
52+
3453
macro_rules! impl_integer_intrinsic {
3554
{ $(unsafe impl ToBitMask<BitMask=$int:ty> for Mask<_, $lanes:literal>)* } => {
3655
$(
@@ -58,3 +77,25 @@ impl_integer_intrinsic! {
5877
unsafe impl ToBitMask<BitMask=u32> for Mask<_, 32>
5978
unsafe impl ToBitMask<BitMask=u64> for Mask<_, 64>
6079
}
80+
81+
/// Returns the minimum numnber of bytes in a bitmask with `lanes` lanes.
82+
#[cfg(feature = "generic_const_exprs")]
83+
pub const fn bitmask_len(lanes: usize) -> usize {
84+
(lanes + 7) / 8
85+
}
86+
87+
#[cfg(feature = "generic_const_exprs")]
88+
unsafe impl<T: MaskElement, const LANES: usize> ToBitMaskArray for Mask<T, LANES>
89+
where
90+
LaneCount<LANES>: SupportedLaneCount,
91+
{
92+
const BYTES: usize = bitmask_len(LANES);
93+
94+
fn to_bitmask_array(self) -> [u8; Self::BYTES] {
95+
self.0.to_bitmask_array()
96+
}
97+
98+
fn from_bitmask_array(bitmask: [u8; Self::BYTES]) -> Self {
99+
Mask(mask_impl::Mask::from_bitmask_array(bitmask))
100+
}
101+
}

crates/core_simd/tests/masks.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,20 @@ macro_rules! test_mask_api {
122122
cast_impl::<i64>();
123123
cast_impl::<isize>();
124124
}
125+
126+
#[cfg(feature = "generic_const_exprs")]
127+
#[test]
128+
fn roundtrip_bitmask_array_conversion() {
129+
use core_simd::ToBitMaskArray;
130+
let values = [
131+
true, false, false, true, false, false, true, false,
132+
true, true, false, false, false, false, false, true,
133+
];
134+
let mask = core_simd::Mask::<$type, 16>::from_array(values);
135+
let bitmask = mask.to_bitmask_array();
136+
assert_eq!(bitmask, [0b01001001, 0b10000011]);
137+
assert_eq!(core_simd::Mask::<$type, 16>::from_bitmask_array(bitmask), mask);
138+
}
125139
}
126140
}
127141
}

0 commit comments

Comments
 (0)