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