Skip to content

Commit 8c31005

Browse files
Merge pull request #423 from rust-lang/bitmask-again-again-again
Implement special swizzles for masks and remove `{to,from}_bitmask_vector`
2 parents 7cd6f95 + bd92b7c commit 8c31005

File tree

6 files changed

+186
-158
lines changed

6 files changed

+186
-158
lines changed

crates/core_simd/src/masks.rs

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -308,48 +308,6 @@ where
308308
Self(mask_impl::Mask::from_bitmask_integer(bitmask))
309309
}
310310

311-
/// Create a bitmask vector from a mask.
312-
///
313-
/// Each bit is set if the corresponding element in the mask is `true`.
314-
/// The remaining bits are unset.
315-
///
316-
/// The bits are packed into the first N bits of the vector:
317-
/// ```
318-
/// # #![feature(portable_simd)]
319-
/// # #[cfg(feature = "as_crate")] use core_simd::simd;
320-
/// # #[cfg(not(feature = "as_crate"))] use core::simd;
321-
/// # use simd::mask32x8;
322-
/// let mask = mask32x8::from_array([true, false, true, false, false, false, true, false]);
323-
/// assert_eq!(mask.to_bitmask_vector()[0], 0b01000101);
324-
/// ```
325-
#[inline]
326-
#[must_use = "method returns a new integer and does not mutate the original value"]
327-
pub fn to_bitmask_vector(self) -> Simd<u8, N> {
328-
self.0.to_bitmask_vector()
329-
}
330-
331-
/// Create a mask from a bitmask vector.
332-
///
333-
/// For each bit, if it is set, the corresponding element in the mask is set to `true`.
334-
///
335-
/// The bits are packed into the first N bits of the vector:
336-
/// ```
337-
/// # #![feature(portable_simd)]
338-
/// # #[cfg(feature = "as_crate")] use core_simd::simd;
339-
/// # #[cfg(not(feature = "as_crate"))] use core::simd;
340-
/// # use simd::{mask32x8, u8x8};
341-
/// let bitmask = u8x8::from_array([0b01000101, 0, 0, 0, 0, 0, 0, 0]);
342-
/// assert_eq!(
343-
/// mask32x8::from_bitmask_vector(bitmask),
344-
/// mask32x8::from_array([true, false, true, false, false, false, true, false]),
345-
/// );
346-
/// ```
347-
#[inline]
348-
#[must_use = "method returns a new mask and does not mutate the original value"]
349-
pub fn from_bitmask_vector(bitmask: Simd<u8, N>) -> Self {
350-
Self(mask_impl::Mask::from_bitmask_vector(bitmask))
351-
}
352-
353311
/// Find the index of the first set element.
354312
///
355313
/// ```

crates/core_simd/src/masks/bitmask.rs

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -122,23 +122,6 @@ where
122122
unsafe { Self(core::intrinsics::simd::simd_bitmask(value), PhantomData) }
123123
}
124124

125-
#[inline]
126-
#[must_use = "method returns a new vector and does not mutate the original value"]
127-
pub fn to_bitmask_vector(self) -> Simd<u8, N> {
128-
let mut bitmask = Simd::splat(0);
129-
bitmask.as_mut_array()[..self.0.as_ref().len()].copy_from_slice(self.0.as_ref());
130-
bitmask
131-
}
132-
133-
#[inline]
134-
#[must_use = "method returns a new mask and does not mutate the original value"]
135-
pub fn from_bitmask_vector(bitmask: Simd<u8, N>) -> Self {
136-
let mut bytes = <LaneCount<N> as SupportedLaneCount>::BitMask::default();
137-
let len = bytes.as_ref().len();
138-
bytes.as_mut().copy_from_slice(&bitmask.as_array()[..len]);
139-
Self(bytes, PhantomData)
140-
}
141-
142125
#[inline]
143126
pub fn to_bitmask_integer(self) -> u64 {
144127
let mut bitmask = [0u8; 8];

crates/core_simd/src/masks/full_masks.rs

Lines changed: 0 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -140,62 +140,6 @@ where
140140
unsafe { Mask(core::intrinsics::simd::simd_cast(self.0)) }
141141
}
142142

143-
#[inline]
144-
#[must_use = "method returns a new vector and does not mutate the original value"]
145-
pub fn to_bitmask_vector(self) -> Simd<u8, N> {
146-
let mut bitmask = Simd::splat(0);
147-
148-
// Safety: Bytes is the right size array
149-
unsafe {
150-
// Compute the bitmask
151-
let mut bytes: <LaneCount<N> as SupportedLaneCount>::BitMask =
152-
core::intrinsics::simd::simd_bitmask(self.0);
153-
154-
// LLVM assumes bit order should match endianness
155-
if cfg!(target_endian = "big") {
156-
for x in bytes.as_mut() {
157-
*x = x.reverse_bits()
158-
}
159-
if N % 8 > 0 {
160-
bytes.as_mut()[N / 8] >>= 8 - N % 8;
161-
}
162-
}
163-
164-
bitmask.as_mut_array()[..bytes.as_ref().len()].copy_from_slice(bytes.as_ref());
165-
}
166-
167-
bitmask
168-
}
169-
170-
#[inline]
171-
#[must_use = "method returns a new mask and does not mutate the original value"]
172-
pub fn from_bitmask_vector(bitmask: Simd<u8, N>) -> Self {
173-
let mut bytes = <LaneCount<N> as SupportedLaneCount>::BitMask::default();
174-
175-
// Safety: Bytes is the right size array
176-
unsafe {
177-
let len = bytes.as_ref().len();
178-
bytes.as_mut().copy_from_slice(&bitmask.as_array()[..len]);
179-
180-
// LLVM assumes bit order should match endianness
181-
if cfg!(target_endian = "big") {
182-
for x in bytes.as_mut() {
183-
*x = x.reverse_bits();
184-
}
185-
if N % 8 > 0 {
186-
bytes.as_mut()[N / 8] >>= 8 - N % 8;
187-
}
188-
}
189-
190-
// Compute the regular mask
191-
Self::from_int_unchecked(core::intrinsics::simd::simd_select_bitmask(
192-
bytes,
193-
Self::splat(true).to_int(),
194-
Self::splat(false).to_int(),
195-
))
196-
}
197-
}
198-
199143
#[inline]
200144
unsafe fn to_bitmask_impl<U: ReverseBits, const M: usize>(self) -> U
201145
where

crates/core_simd/src/swizzle.rs

Lines changed: 179 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,9 @@ where
312312
///
313313
/// ```
314314
/// # #![feature(portable_simd)]
315-
/// # use core::simd::Simd;
315+
/// # #[cfg(feature = "as_crate")] use core_simd::simd;
316+
/// # #[cfg(not(feature = "as_crate"))] use core::simd;
317+
/// # use simd::Simd;
316318
/// let a = Simd::from_array([0, 4, 1, 5]);
317319
/// let b = Simd::from_array([2, 6, 3, 7]);
318320
/// let (x, y) = a.deinterleave(b);
@@ -383,4 +385,180 @@ where
383385
}
384386
Resize::<N>::concat_swizzle(self, Simd::splat(value))
385387
}
388+
389+
/// Extract a vector from another vector.
390+
///
391+
/// ```
392+
/// # #![feature(portable_simd)]
393+
/// # #[cfg(feature = "as_crate")] use core_simd::simd;
394+
/// # #[cfg(not(feature = "as_crate"))] use core::simd;
395+
/// # use simd::u32x4;
396+
/// let x = u32x4::from_array([0, 1, 2, 3]);
397+
/// assert_eq!(x.extract::<1, 2>().to_array(), [1, 2]);
398+
/// ```
399+
#[inline]
400+
#[must_use = "method returns a new vector and does not mutate the original inputs"]
401+
pub fn extract<const START: usize, const LEN: usize>(self) -> Simd<T, LEN>
402+
where
403+
LaneCount<LEN>: SupportedLaneCount,
404+
{
405+
struct Extract<const N: usize, const START: usize>;
406+
impl<const N: usize, const START: usize, const LEN: usize> Swizzle<LEN> for Extract<N, START> {
407+
const INDEX: [usize; LEN] = const {
408+
assert!(START + LEN <= N, "index out of bounds");
409+
let mut index = [0; LEN];
410+
let mut i = 0;
411+
while i < LEN {
412+
index[i] = START + i;
413+
i += 1;
414+
}
415+
index
416+
};
417+
}
418+
Extract::<N, START>::swizzle(self)
419+
}
420+
}
421+
422+
impl<T, const N: usize> Mask<T, N>
423+
where
424+
T: MaskElement,
425+
LaneCount<N>: SupportedLaneCount,
426+
{
427+
/// Reverse the order of the elements in the mask.
428+
#[inline]
429+
#[must_use = "method returns a new vector and does not mutate the original inputs"]
430+
pub fn reverse(self) -> Self {
431+
// Safety: swizzles are safe for masks
432+
unsafe { Self::from_int_unchecked(self.to_int().reverse()) }
433+
}
434+
435+
/// Rotates the mask such that the first `OFFSET` elements of the slice move to the end
436+
/// while the last `self.len() - OFFSET` elements move to the front. After calling `rotate_elements_left`,
437+
/// the element previously at index `OFFSET` will become the first element in the slice.
438+
#[inline]
439+
#[must_use = "method returns a new vector and does not mutate the original inputs"]
440+
pub fn rotate_elements_left<const OFFSET: usize>(self) -> Self {
441+
// Safety: swizzles are safe for masks
442+
unsafe { Self::from_int_unchecked(self.to_int().rotate_elements_left::<OFFSET>()) }
443+
}
444+
445+
/// Rotates the mask such that the first `self.len() - OFFSET` elements of the mask move to
446+
/// the end while the last `OFFSET` elements move to the front. After calling `rotate_elements_right`,
447+
/// the element previously at index `self.len() - OFFSET` will become the first element in the slice.
448+
#[inline]
449+
#[must_use = "method returns a new vector and does not mutate the original inputs"]
450+
pub fn rotate_elements_right<const OFFSET: usize>(self) -> Self {
451+
// Safety: swizzles are safe for masks
452+
unsafe { Self::from_int_unchecked(self.to_int().rotate_elements_right::<OFFSET>()) }
453+
}
454+
455+
/// Interleave two masks.
456+
///
457+
/// The resulting masks contain elements taken alternatively from `self` and `other`, first
458+
/// filling the first result, and then the second.
459+
///
460+
/// The reverse of this operation is [`Mask::deinterleave`].
461+
///
462+
/// ```
463+
/// # #![feature(portable_simd)]
464+
/// # #[cfg(feature = "as_crate")] use core_simd::simd;
465+
/// # #[cfg(not(feature = "as_crate"))] use core::simd;
466+
/// # use simd::mask32x4;
467+
/// let a = mask32x4::from_array([false, true, false, true]);
468+
/// let b = mask32x4::from_array([false, false, true, true]);
469+
/// let (x, y) = a.interleave(b);
470+
/// assert_eq!(x.to_array(), [false, false, true, false]);
471+
/// assert_eq!(y.to_array(), [false, true, true, true]);
472+
/// ```
473+
#[inline]
474+
#[must_use = "method returns a new vector and does not mutate the original inputs"]
475+
pub fn interleave(self, other: Self) -> (Self, Self) {
476+
let (lo, hi) = self.to_int().interleave(other.to_int());
477+
// Safety: swizzles are safe for masks
478+
unsafe { (Self::from_int_unchecked(lo), Self::from_int_unchecked(hi)) }
479+
}
480+
481+
/// Deinterleave two masks.
482+
///
483+
/// The first result takes every other element of `self` and then `other`, starting with
484+
/// the first element.
485+
///
486+
/// The second result takes every other element of `self` and then `other`, starting with
487+
/// the second element.
488+
///
489+
/// The reverse of this operation is [`Mask::interleave`].
490+
///
491+
/// ```
492+
/// # #![feature(portable_simd)]
493+
/// # #[cfg(feature = "as_crate")] use core_simd::simd;
494+
/// # #[cfg(not(feature = "as_crate"))] use core::simd;
495+
/// # use simd::mask32x4;
496+
/// let a = mask32x4::from_array([false, true, false, true]);
497+
/// let b = mask32x4::from_array([false, false, true, true]);
498+
/// let (x, y) = a.deinterleave(b);
499+
/// assert_eq!(x.to_array(), [false, false, false, true]);
500+
/// assert_eq!(y.to_array(), [true, true, false, true]);
501+
/// ```
502+
#[inline]
503+
#[must_use = "method returns a new vector and does not mutate the original inputs"]
504+
pub fn deinterleave(self, other: Self) -> (Self, Self) {
505+
let (even, odd) = self.to_int().deinterleave(other.to_int());
506+
// Safety: swizzles are safe for masks
507+
unsafe {
508+
(
509+
Self::from_int_unchecked(even),
510+
Self::from_int_unchecked(odd),
511+
)
512+
}
513+
}
514+
515+
/// Resize a mask.
516+
///
517+
/// If `M` > `N`, extends the length of a mask, setting the new elements to `value`.
518+
/// If `M` < `N`, truncates the mask to the first `M` elements.
519+
///
520+
/// ```
521+
/// # #![feature(portable_simd)]
522+
/// # #[cfg(feature = "as_crate")] use core_simd::simd;
523+
/// # #[cfg(not(feature = "as_crate"))] use core::simd;
524+
/// # use simd::mask32x4;
525+
/// let x = mask32x4::from_array([false, true, true, false]);
526+
/// assert_eq!(x.resize::<8>(true).to_array(), [false, true, true, false, true, true, true, true]);
527+
/// assert_eq!(x.resize::<2>(true).to_array(), [false, true]);
528+
/// ```
529+
#[inline]
530+
#[must_use = "method returns a new vector and does not mutate the original inputs"]
531+
pub fn resize<const M: usize>(self, value: bool) -> Mask<T, M>
532+
where
533+
LaneCount<M>: SupportedLaneCount,
534+
{
535+
// Safety: swizzles are safe for masks
536+
unsafe {
537+
Mask::<T, M>::from_int_unchecked(self.to_int().resize::<M>(if value {
538+
T::TRUE
539+
} else {
540+
T::FALSE
541+
}))
542+
}
543+
}
544+
545+
/// Extract a vector from another vector.
546+
///
547+
/// ```
548+
/// # #![feature(portable_simd)]
549+
/// # #[cfg(feature = "as_crate")] use core_simd::simd;
550+
/// # #[cfg(not(feature = "as_crate"))] use core::simd;
551+
/// # use simd::mask32x4;
552+
/// let x = mask32x4::from_array([false, true, true, false]);
553+
/// assert_eq!(x.extract::<1, 2>().to_array(), [true, true]);
554+
/// ```
555+
#[inline]
556+
#[must_use = "method returns a new vector and does not mutate the original inputs"]
557+
pub fn extract<const START: usize, const LEN: usize>(self) -> Mask<T, LEN>
558+
where
559+
LaneCount<LEN>: SupportedLaneCount,
560+
{
561+
// Safety: swizzles are safe for masks
562+
unsafe { Mask::<T, LEN>::from_int_unchecked(self.to_int().extract::<START, LEN>()) }
563+
}
386564
}

crates/core_simd/src/vector.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,9 @@ where
442442
///
443443
/// When the element is disabled, that memory location is not accessed and the corresponding
444444
/// value from `or` is passed through.
445+
///
446+
/// # Safety
447+
/// Enabled loads must not exceed the length of `slice`.
445448
#[must_use]
446449
#[inline]
447450
pub unsafe fn load_select_unchecked(
@@ -459,6 +462,9 @@ where
459462
///
460463
/// When the element is disabled, that memory location is not accessed and the corresponding
461464
/// value from `or` is passed through.
465+
///
466+
/// # Safety
467+
/// Enabled `ptr` elements must be safe to read as if by `std::ptr::read`.
462468
#[must_use]
463469
#[inline]
464470
pub unsafe fn load_select_ptr(
@@ -1214,6 +1220,7 @@ fn lane_indices<const N: usize>() -> Simd<usize, N>
12141220
where
12151221
LaneCount<N>: SupportedLaneCount,
12161222
{
1223+
#![allow(clippy::needless_range_loop)]
12171224
let mut index = [0; N];
12181225
for i in 0..N {
12191226
index[i] = i;

0 commit comments

Comments
 (0)