/rust/registry/src/index.crates.io-1949cf8c6b5b557f/escape-simd-0.1.0/src/simd/bits.rs
Line | Count | Source |
1 | | use super::traits::BitMask; |
2 | | |
3 | | macro_rules! impl_bits { |
4 | | () => {}; |
5 | | ($($ty:ty)*) => { |
6 | | $( |
7 | | impl BitMask for $ty { |
8 | | const LEN: usize = std::mem::size_of::<$ty>() * 8; |
9 | | |
10 | | #[inline] |
11 | 0 | fn first_offset(&self) -> usize { |
12 | 0 | self.as_little_endian().trailing_zeros() as usize |
13 | 0 | } Unexecuted instantiation: <u16 as escape_simd::simd::traits::BitMask>::first_offset Unexecuted instantiation: <u32 as escape_simd::simd::traits::BitMask>::first_offset Unexecuted instantiation: <u64 as escape_simd::simd::traits::BitMask>::first_offset |
14 | | |
15 | | #[inline] |
16 | 0 | fn as_little_endian(&self) -> Self { |
17 | | // The software bitmask already uses a canonical layout |
18 | | // (lane `i` -> bit `i`), so there is no byte order to swap. |
19 | 0 | self.clone() |
20 | 0 | } Unexecuted instantiation: <u16 as escape_simd::simd::traits::BitMask>::as_little_endian Unexecuted instantiation: <u32 as escape_simd::simd::traits::BitMask>::as_little_endian Unexecuted instantiation: <u64 as escape_simd::simd::traits::BitMask>::as_little_endian |
21 | | |
22 | | #[inline] |
23 | 0 | fn all_zero(&self) -> bool { |
24 | 0 | *self == 0 |
25 | 0 | } Unexecuted instantiation: <u16 as escape_simd::simd::traits::BitMask>::all_zero Unexecuted instantiation: <u32 as escape_simd::simd::traits::BitMask>::all_zero Unexecuted instantiation: <u64 as escape_simd::simd::traits::BitMask>::all_zero |
26 | | |
27 | | #[inline] |
28 | 0 | fn clear_high_bits(&self, n: usize) -> Self { |
29 | 0 | debug_assert!(n <= Self::LEN); |
30 | 0 | *self & ((u64::MAX as $ty) >> n) |
31 | 0 | } Unexecuted instantiation: <u16 as escape_simd::simd::traits::BitMask>::clear_high_bits Unexecuted instantiation: <u32 as escape_simd::simd::traits::BitMask>::clear_high_bits Unexecuted instantiation: <u64 as escape_simd::simd::traits::BitMask>::clear_high_bits |
32 | | } |
33 | | )* |
34 | | }; |
35 | | } |
36 | | |
37 | | impl_bits!(u16 u32 u64); |
38 | | |
39 | | #[cfg(target_arch = "aarch64")] |
40 | | /// Use u64 representation the bitmask of Neon vector. |
41 | | /// (low) |
42 | | /// Vector: 00-ff-ff-ff-ff-00-00-00 |
43 | | /// Mask : 0000-1111-1111-1111-1111-0000-0000-0000 |
44 | | /// |
45 | | /// first_offset() = 1 |
46 | | /// clear_high_bits(4) = Mask(0000-1111-1111-1111-[0000]-0000-0000-0000) |
47 | | /// |
48 | | /// reference: https://community.arm.com/arm-community-blogs/b/infrastructure-solutions-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon |
49 | | pub struct NeonBits(u64); |
50 | | |
51 | | #[cfg(target_arch = "aarch64")] |
52 | | impl NeonBits { |
53 | | /// Wraps the raw `u64` produced by the NEON `vshrn` bitmask extraction, |
54 | | /// normalizing it to the canonical `lane i -> nibble i` layout that |
55 | | /// `first_offset`, `clear_high_bits` and `all_zero` assume. |
56 | | /// |
57 | | /// On little-endian the extraction is already canonical. On big-endian the |
58 | | /// `vreinterpretq_u16_u8` + `vshrn_n_u16` step packs each lane pair with its |
59 | | /// two nibbles (and the byte pairs) reversed relative to lane order, so the |
60 | | /// whole nibble sequence ends up reversed. Reverse the 16 nibbles to restore |
61 | | /// `lane i -> nibble i` (a plain `swap_bytes` only fixes the byte order, not |
62 | | /// the nibble order within each byte). |
63 | | #[inline] |
64 | | pub fn new(u: u64) -> Self { |
65 | | #[cfg(target_endian = "little")] |
66 | | { |
67 | | Self(u) |
68 | | } |
69 | | #[cfg(target_endian = "big")] |
70 | | { |
71 | | let b = u.swap_bytes(); |
72 | | Self(((b & 0x0f0f_0f0f_0f0f_0f0f) << 4) | ((b & 0xf0f0_f0f0_f0f0_f0f0) >> 4)) |
73 | | } |
74 | | } |
75 | | } |
76 | | |
77 | | #[cfg(target_arch = "aarch64")] |
78 | | impl BitMask for NeonBits { |
79 | | const LEN: usize = 16; |
80 | | |
81 | | #[inline] |
82 | | fn first_offset(&self) -> usize { |
83 | | (self.as_little_endian().0.trailing_zeros() as usize) >> 2 |
84 | | } |
85 | | |
86 | | #[inline] |
87 | | fn as_little_endian(&self) -> Self { |
88 | | // `new` already normalized the bits to the canonical `lane i -> nibble i` |
89 | | // layout on every target, so there is no byte order left to swap. |
90 | | Self(self.0) |
91 | | } |
92 | | |
93 | | #[inline] |
94 | | fn all_zero(&self) -> bool { |
95 | | self.0 == 0 |
96 | | } |
97 | | |
98 | | #[inline] |
99 | | fn clear_high_bits(&self, n: usize) -> Self { |
100 | | debug_assert!(n <= Self::LEN); |
101 | | Self(self.0 & u64::MAX >> (n * 4)) |
102 | | } |
103 | | } |