Skip to main content

hermes_simd_core/view/
mask_reg.rs

1//! Monomorphized SIMD mask register wrapper.
2
3use super::vector_reg::{assert_runtime_supported, Vector};
4use crate::arch::SimdArch;
5use crate::kernel::SimdKernel;
6use crate::mask::BitMask;
7use crate::scalar::Scalar;
8use core::marker::PhantomData;
9
10/// A type-safe, architecture-native SIMD mask type.
11#[repr(transparent)]
12pub struct Mask<T, Arch>
13where
14    Arch: SimdArch + SimdKernel<T>,
15    T: Scalar,
16{
17    /// The underlying raw mask register or representation.
18    pub raw: Arch::Mask,
19    _marker: PhantomData<T>,
20}
21
22impl<T, Arch> Clone for Mask<T, Arch>
23where
24    Arch: SimdArch + SimdKernel<T>,
25    T: Scalar,
26{
27    #[inline(always)]
28    fn clone(&self) -> Self {
29        *self
30    }
31}
32
33impl<T, Arch> Copy for Mask<T, Arch>
34where
35    Arch: SimdArch + SimdKernel<T>,
36    T: Scalar,
37{
38}
39
40impl<T, Arch> core::fmt::Debug for Mask<T, Arch>
41where
42    Arch: SimdArch + SimdKernel<T>,
43    T: Scalar,
44{
45    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
46        assert_runtime_supported::<T, Arch>();
47        let bm = unsafe { self.to_bitmask() };
48        f.debug_tuple("Mask").field(&bm.to_bools()).finish()
49    }
50}
51
52impl<T, Arch> PartialEq for Mask<T, Arch>
53where
54    Arch: SimdArch + SimdKernel<T>,
55    T: Scalar,
56{
57    #[inline]
58    fn eq(&self, other: &Self) -> bool {
59        assert_runtime_supported::<T, Arch>();
60        unsafe { self.to_bitmask() == other.to_bitmask() }
61    }
62}
63
64impl<T, Arch> Eq for Mask<T, Arch>
65where
66    Arch: SimdArch + SimdKernel<T>,
67    T: Scalar,
68{
69}
70
71impl<T, Arch> Mask<T, Arch>
72where
73    Arch: SimdArch + SimdKernel<T>,
74    T: Scalar,
75{
76    /// Create a new Mask wrapping a raw mask register.
77    #[inline(always)]
78    pub const fn new(raw: Arch::Mask) -> Self {
79        Self {
80            raw,
81            _marker: PhantomData,
82        }
83    }
84
85    /// Construct a Mask from a portable `BitMask<64>`.
86    ///
87    /// # Safety
88    /// Processor must support the target feature of `Arch`.
89    #[inline(always)]
90    pub unsafe fn from_bitmask(bm: BitMask<64>) -> Self {
91        Self::new(Arch::mask_from_bitmask(bm.0))
92    }
93
94    /// Convert the Mask to a portable `BitMask<64>`.
95    ///
96    /// # Safety
97    /// Processor must support the target feature of `Arch`.
98    #[inline(always)]
99    pub unsafe fn to_bitmask(self) -> BitMask<64> {
100        BitMask(Arch::mask_to_bitmask(self.raw))
101    }
102
103    /// Returns `true` if any lanes of the mask are active.
104    #[inline(always)]
105    pub fn any(self) -> bool {
106        assert_runtime_supported::<T, Arch>();
107        unsafe { !self.to_bitmask().is_none_active() }
108    }
109
110    /// Returns `true` if all lanes of the mask are active.
111    #[inline(always)]
112    pub fn all(self) -> bool {
113        assert_runtime_supported::<T, Arch>();
114        let lanes = <Arch as SimdKernel<T>>::LANE_COUNT;
115        let expected = if lanes >= 64 {
116            u64::MAX
117        } else {
118            (1u64 << lanes) - 1
119        };
120        unsafe { (self.to_bitmask().0 & expected) == expected }
121    }
122
123    /// Returns `true` if no lanes of the mask are active.
124    #[inline(always)]
125    pub fn none(self) -> bool {
126        assert_runtime_supported::<T, Arch>();
127        unsafe { self.to_bitmask().is_none_active() }
128    }
129
130    /// Select elements from `true_val` where the mask is active, and from `false_val` otherwise.
131    #[inline(always)]
132    pub fn select(self, true_val: Vector<T, Arch>, false_val: Vector<T, Arch>) -> Vector<T, Arch> {
133        assert_runtime_supported::<T, Arch>();
134        let zero = Vector::<T, Arch>::zero();
135        Vector::new(unsafe { Arch::masked_add(true_val.raw, zero.raw, self.raw, false_val.raw) })
136    }
137}
138
139// Bitwise operations on Mask
140impl<T, Arch> core::ops::BitAnd for Mask<T, Arch>
141where
142    Arch: SimdArch + SimdKernel<T>,
143    T: Scalar,
144{
145    type Output = Self;
146    #[inline(always)]
147    fn bitand(self, rhs: Self) -> Self::Output {
148        assert_runtime_supported::<T, Arch>();
149        unsafe {
150            let bm_self = self.to_bitmask();
151            let bm_rhs = rhs.to_bitmask();
152            Self::from_bitmask(bm_self & bm_rhs)
153        }
154    }
155}
156
157impl<T, Arch> core::ops::BitOr for Mask<T, Arch>
158where
159    Arch: SimdArch + SimdKernel<T>,
160    T: Scalar,
161{
162    type Output = Self;
163    #[inline(always)]
164    fn bitor(self, rhs: Self) -> Self::Output {
165        assert_runtime_supported::<T, Arch>();
166        unsafe {
167            let bm_self = self.to_bitmask();
168            let bm_rhs = rhs.to_bitmask();
169            Self::from_bitmask(bm_self | bm_rhs)
170        }
171    }
172}
173
174impl<T, Arch> core::ops::BitXor for Mask<T, Arch>
175where
176    Arch: SimdArch + SimdKernel<T>,
177    T: Scalar,
178{
179    type Output = Self;
180    #[inline(always)]
181    fn bitxor(self, rhs: Self) -> Self::Output {
182        assert_runtime_supported::<T, Arch>();
183        unsafe {
184            let bm_self = self.to_bitmask();
185            let bm_rhs = rhs.to_bitmask();
186            Self::from_bitmask(BitMask(bm_self.0 ^ bm_rhs.0))
187        }
188    }
189}
190
191impl<T, Arch> core::ops::Not for Mask<T, Arch>
192where
193    Arch: SimdArch + SimdKernel<T>,
194    T: Scalar,
195{
196    type Output = Self;
197    #[inline(always)]
198    fn not(self) -> Self::Output {
199        assert_runtime_supported::<T, Arch>();
200        unsafe {
201            let bm = self.to_bitmask();
202            let lanes = <Arch as SimdKernel<T>>::LANE_COUNT;
203            let active_mask = if lanes >= 64 {
204                u64::MAX
205            } else {
206                (1u64 << lanes) - 1
207            };
208            Self::from_bitmask(BitMask((!bm.0) & active_mask))
209        }
210    }
211}