Skip to main content

core/stdarch/crates/core_arch/src/
macros.rs

1//! Utility macros.
2
3#[allow(unused)]
4macro_rules! static_assert {
5    ($e:expr) => {
6        const {
7            assert!($e);
8        }
9    };
10    ($e:expr, $msg:expr) => {
11        const {
12            assert!($e, $msg);
13        }
14    };
15}
16
17#[allow(unused_macros)]
18macro_rules! static_assert_uimm_bits {
19    ($imm:ident, $bits:expr) => {
20        // `0 <= $imm` produces a warning if the immediate has an unsigned type
21        #[allow(unused_comparisons)]
22        {
23            static_assert!(
24                0 <= $imm && $imm < (1 << $bits),
25                concat!(
26                    stringify!($imm),
27                    " doesn't fit in ",
28                    stringify!($bits),
29                    " bits",
30                )
31            )
32        }
33    };
34}
35
36#[allow(unused_macros)]
37macro_rules! static_assert_simm_bits {
38    ($imm:ident, $bits:expr) => {
39        static_assert!(
40            (-1 << ($bits - 1)) - 1 <= $imm && $imm < (1 << ($bits - 1)),
41            concat!(
42                stringify!($imm),
43                " doesn't fit in ",
44                stringify!($bits),
45                " bits",
46            )
47        )
48    };
49}
50
51#[allow(unused)]
52macro_rules! types {
53    (
54        #![$stability_first:meta]
55        $(
56            #![$stability_more:meta]
57        )*
58
59        $(
60            $(#[$doc:meta])*
61            $(stability: [$stability_already: meta])*
62            pub struct $name:ident($len:literal x $v:vis $elem_type:ty);
63        )*
64    ) => (types! {
65        $(
66            #![$stability_more]
67        )*
68
69        $(
70            $(#[$doc])*
71            $(stability: [$stability_already])*
72            stability: [$stability_first]
73            pub struct $name($len x $v $elem_type);
74        )*
75    });
76
77    (
78        $(
79            $(#[$doc:meta])*
80            $(stability: [$stability: meta])+
81            pub struct $name:ident($len:literal x $v:vis $elem_type:ty);
82        )*
83    ) => ($(
84        $(#[$doc])*
85        $(#[$stability])+
86        #[derive(Copy, Clone)]
87        #[allow(non_camel_case_types)]
88        #[repr(simd)]
89        #[allow(clippy::missing_inline_in_public_items)]
90        pub struct $name($v [$elem_type; $len]);
91
92        impl $name {
93            /// Put the same value in every lane.
94            #[inline(always)]
95            $v fn splat(value: $elem_type) -> $name {
96                unsafe { $crate::intrinsics::simd::simd_splat(value) }
97            }
98
99            /// Returns an array reference containing the entire SIMD vector.
100            $v const fn as_array(&self) -> &[$elem_type; $len] {
101                // SAFETY: this type is just an overaligned `[T; N]` with
102                // potential padding at the end, so pointer casting to a
103                // `&[T; N]` is safe.
104                //
105                // NOTE: This deliberately doesn't just use `&self.0` because it may soon be banned
106                // see https://github.com/rust-lang/compiler-team/issues/838
107                unsafe { &*(self as *const Self as *const [$elem_type; $len]) }
108
109            }
110
111            /// Returns a mutable array reference containing the entire SIMD vector.
112            #[inline]
113            $v fn as_mut_array(&mut self) -> &mut [$elem_type; $len] {
114                // SAFETY: this type is just an overaligned `[T; N]` with
115                // potential padding at the end, so pointer casting to a
116                // `&mut [T; N]` is safe.
117                //
118                // NOTE: This deliberately doesn't just use `&mut self.0` because it may soon be banned
119                // see https://github.com/rust-lang/compiler-team/issues/838
120                unsafe { &mut *(self as *mut Self as *mut [$elem_type; $len]) }
121            }
122        }
123
124        $(#[$stability])+
125        impl crate::fmt::Debug for $name {
126            #[inline]
127            fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result {
128                crate::core_arch::simd::debug_simd_finish(f, stringify!($name), self.as_array())
129            }
130        }
131
132        $(#[$stability])+
133        impl crate::convert::From<crate::core_arch::simd::Simd<$elem_type, $len>> for $name {
134            #[inline(always)]
135            fn from(simd: crate::core_arch::simd::Simd<$elem_type, $len>) -> Self {
136                unsafe { crate::mem::transmute(simd) }
137            }
138        }
139
140        $(#[$stability])+
141        impl crate::convert::From<$name> for crate::core_arch::simd::Simd<$elem_type, $len> {
142            #[inline(always)]
143            fn from(simd: $name) -> Self {
144                unsafe { crate::mem::transmute(simd) }
145            }
146        }
147    )*);
148}
149
150#[allow(unused)]
151#[repr(simd)]
152pub(crate) struct SimdShuffleIdx<const LEN: usize>(pub(crate) [u32; LEN]);
153
154#[allow(unused)]
155macro_rules! simd_shuffle {
156    ($x:expr, $y:expr, $idx:expr $(,)?) => {{
157        $crate::intrinsics::simd::simd_shuffle(
158            $x,
159            $y,
160            const { $crate::core_arch::macros::SimdShuffleIdx($idx) },
161        )
162    }};
163}
164
165#[allow(unused)]
166macro_rules! simd_insert {
167    ($x:expr, $idx:expr, $val:expr $(,)?) => {{ $crate::intrinsics::simd::simd_insert($x, const { $idx }, $val) }};
168}
169
170#[allow(unused)]
171macro_rules! simd_extract {
172    ($x:expr, $idx:expr $(,)?) => {{ $crate::intrinsics::simd::simd_extract($x, const { $idx }) }};
173    ($x:expr, $idx:expr, $ty:ty $(,)?) => {{ $crate::intrinsics::simd::simd_extract::<_, $ty>($x, const { $idx }) }};
174}
175
176#[allow(unused)]
177macro_rules! simd_masked_load {
178    ($align:expr, $mask:expr, $ptr:expr, $default:expr) => {
179        $crate::intrinsics::simd::simd_masked_load::<_, _, _, { $align }>($mask, $ptr, $default)
180    };
181}
182
183#[allow(unused)]
184macro_rules! simd_masked_store {
185    ($align:expr, $mask:expr, $ptr:expr, $default:expr) => {
186        $crate::intrinsics::simd::simd_masked_store::<_, _, _, { $align }>($mask, $ptr, $default)
187    };
188}
189
190/// The first N indices `[0, 1, 2, ...]`.
191pub(crate) const fn identity<const N: usize>() -> [u32; N] {
192    let mut out = [0u32; N];
193    let mut i = 0usize;
194    while i < N {
195        out[i] = i as u32;
196        i += 1;
197    }
198    out
199}
200
201/// The first N even indices `[0, 2, 4, ...]`.
202pub(crate) const fn even<const N: usize>() -> [u32; N] {
203    let mut out = [0u32; N];
204    let mut i = 0usize;
205    while i < N {
206        out[i] = (2 * i) as u32;
207        i += 1;
208    }
209    out
210}
211
212/// The first N odd indices `[1, 3, 5, ...]`.
213pub(crate) const fn odd<const N: usize>() -> [u32; N] {
214    let mut out = [0u32; N];
215    let mut i = 0usize;
216    while i < N {
217        out[i] = (2 * i + 1) as u32;
218        i += 1;
219    }
220    out
221}
222
223/// Multiples of N offset by K `[K, K+N, K+2N, ...]`.
224pub(crate) const fn deinterleave_mask<const LANES: usize, const N: usize, const K: usize>()
225-> [u32; LANES] {
226    let mut out = [0u32; LANES];
227    let mut i = 0usize;
228    while i < LANES {
229        out[i] = (i * N + K) as u32;
230        i += 1;
231    }
232    out
233}
234
235#[allow(unused)]
236macro_rules! deinterleaving_load {
237    ($elem:ty, $lanes:literal, 2, $ptr:expr) => {{
238        use $crate::core_arch::macros::deinterleave_mask;
239        use $crate::core_arch::simd::Simd;
240        use $crate::mem::transmute;
241
242        type V = Simd<$elem, $lanes>;
243        type W = Simd<$elem, { $lanes * 2 }>;
244
245        let w: W = $crate::ptr::read_unaligned($ptr as *const W);
246
247        let v0: V = simd_shuffle!(w, w, deinterleave_mask::<$lanes, 2, 0>());
248        let v1: V = simd_shuffle!(w, w, deinterleave_mask::<$lanes, 2, 1>());
249
250        transmute((v0, v1))
251    }};
252
253    ($elem:ty, $lanes:literal, 3, $ptr:expr) => {{
254        use $crate::core_arch::macros::deinterleave_mask;
255        use $crate::core_arch::simd::Simd;
256        use $crate::mem::{MaybeUninit, transmute};
257
258        type V = Simd<$elem, $lanes>;
259        type W = Simd<$elem, { $lanes * 3 }>;
260
261        // NOTE: repr(simd) adds padding to make the total size a power of two.
262        // Hence reading W from ptr might read out of bounds.
263        let mut mem = MaybeUninit::<W>::uninit();
264        $crate::ptr::copy_nonoverlapping(
265            $ptr.cast::<$elem>(),
266            mem.as_mut_ptr().cast::<$elem>(),
267            $lanes * 3,
268        );
269        let w = mem.assume_init();
270
271        let v0: V = simd_shuffle!(w, w, deinterleave_mask::<$lanes, 3, 0>());
272        let v1: V = simd_shuffle!(w, w, deinterleave_mask::<$lanes, 3, 1>());
273        let v2: V = simd_shuffle!(w, w, deinterleave_mask::<$lanes, 3, 2>());
274
275        transmute((v0, v1, v2))
276    }};
277
278    ($elem:ty, $lanes:literal, 4, $ptr:expr) => {{
279        use $crate::core_arch::macros::deinterleave_mask;
280        use $crate::core_arch::simd::Simd;
281        use $crate::mem::transmute;
282
283        type V = Simd<$elem, $lanes>;
284        type W = Simd<$elem, { $lanes * 4 }>;
285
286        let w: W = $crate::ptr::read_unaligned($ptr as *const W);
287
288        let v0: V = simd_shuffle!(w, w, deinterleave_mask::<$lanes, 4, 0>());
289        let v1: V = simd_shuffle!(w, w, deinterleave_mask::<$lanes, 4, 1>());
290        let v2: V = simd_shuffle!(w, w, deinterleave_mask::<$lanes, 4, 2>());
291        let v3: V = simd_shuffle!(w, w, deinterleave_mask::<$lanes, 4, 3>());
292
293        transmute((v0, v1, v2, v3))
294    }};
295}
296
297#[allow(unused)]
298pub(crate) use deinterleaving_load;
299
300pub(crate) const fn interleave_mask<const LANES: usize, const N: usize, const K: usize>()
301-> [u32; LANES] {
302    let mut out = [0u32; LANES];
303    let mut j = 0usize;
304    while j < LANES {
305        out[j] = ((j % K) * N + j / K) as u32;
306        j += 1;
307    }
308    out
309}
310
311#[allow(unused)]
312macro_rules! interleaving_store {
313    ($elem:ty, $lanes:literal, 2, $ptr:expr, $v:expr) => {{
314        use $crate::core_arch::macros::interleave_mask;
315        use $crate::core_arch::simd::Simd;
316
317        type W = Simd<$elem, { $lanes * 2 }>;
318        let w: W = simd_shuffle!($v.0, $v.1, interleave_mask::<{ $lanes * 2 }, $lanes, 2>());
319        $crate::ptr::write_unaligned($ptr as *mut W, w);
320    }};
321
322    // N = 3
323    ($elem:ty, $lanes:literal, 3, $ptr:expr, $v:expr) => {{
324        use $crate::core_arch::macros::{identity, interleave_mask};
325        use $crate::core_arch::simd::Simd;
326
327        let v0v1: Simd<$elem, { $lanes * 2 }> =
328            simd_shuffle!($v.0, $v.1, identity::<{ $lanes * 2 }>());
329        let v2v2: Simd<$elem, { $lanes * 2 }> =
330            simd_shuffle!($v.2, $v.2, identity::<{ $lanes * 2 }>());
331
332        type W = Simd<$elem, { $lanes * 3 }>;
333
334        // NOTE: repr(simd) adds padding to make the total size a power of two.
335        // Hence writing W to ptr might write out of bounds.
336        let w: W = simd_shuffle!(v0v1, v2v2, interleave_mask::<{ $lanes * 3 }, $lanes, 3>());
337        $crate::ptr::copy_nonoverlapping(
338            (&w as *const W).cast::<$elem>(),
339            $ptr.cast::<$elem>(),
340            $lanes * 3,
341        );
342    }};
343
344    // N = 4
345    ($elem:ty, $lanes:literal, 4, $ptr:expr, $v:expr) => {{
346        use $crate::core_arch::macros::{identity, interleave_mask};
347        use $crate::core_arch::simd::Simd;
348
349        let v0v1: Simd<$elem, { $lanes * 2 }> =
350            simd_shuffle!($v.0, $v.1, identity::<{ $lanes * 2 }>());
351        let v2v3: Simd<$elem, { $lanes * 2 }> =
352            simd_shuffle!($v.2, $v.3, identity::<{ $lanes * 2 }>());
353
354        type W = Simd<$elem, { $lanes * 4 }>;
355        let w: W = simd_shuffle!(v0v1, v2v3, interleave_mask::<{ $lanes * 4 }, $lanes, 4>());
356        $crate::ptr::write_unaligned($ptr as *mut W, w);
357    }};
358}
359
360#[allow(unused)]
361pub(crate) use interleaving_store;