swift_check/arch/
mod.rs

1pub const WIDTH: usize = 16;
2#[macro_use]
3mod cfg_macros;
4
5#[cfg(all(mirai, not(feature = "verify")))]
6compile_error!(
7    "To run mirai on `swift-check` you must enable the `verify` feature: \n\
8     `cargo mirai --tests --features verify`"
9);
10
11macro_rules! impl_bit_ops {
12    ($ident:ident) => {
13        impl core::ops::BitAnd for $ident {
14            type Output = Self;
15
16            #[inline(always)]
17            fn bitand(self, rhs: Self) -> Self::Output {
18                $ident(self.0 & rhs.0)
19            }
20        }
21
22        impl core::ops::BitOr for $ident {
23            type Output = Self;
24
25            #[inline(always)]
26            fn bitor(self, rhs: Self) -> Self::Output {
27                $ident(self.0 | rhs.0)
28            }
29        }
30    };
31}
32
33cfg_verify!(
34    pub(crate) fn is_aligned(ptr: *const Ptr) -> bool {
35        byte_ptr(ptr).align_offset(WIDTH) == 0
36    }
37    macro_rules! contract {
38        ($kind:ident!($args:expr)) => {
39            mirai_annotations::$kind!($args)
40        };
41        ($expr:expr) => {
42            $expr
43        };
44    }
45);
46
47cfg_runtime!(
48    #[allow(unused_macros)]
49    macro_rules! contract {
50        ($kind:ident!($args:expr)) => {};
51        ($expr:expr) => {};
52    }
53);
54
55cfg_neon!(
56    pub mod aarch64;
57    pub use aarch64 as arch;
58);
59
60cfg_sse!(
61    pub mod x86_64;
62    pub use x86_64 as arch;
63);
64
65cfg_simd128!(
66    pub mod wasm;
67    pub use wasm as arch;
68);
69
70cfg_fallback!(
71    pub mod fallback;
72    pub use fallback as arch;
73);
74
75#[doc(hidden)]
76pub use arch::{
77    eq, not, xor, or, and, splat, byte_ptr, simd_ptr, load_partial, load_aligned, maybe_aligned_load
78};
79
80#[doc(hidden)]
81pub use arch::{MoveMask, Ptr, STEP, STEP_SIZE};
82
83pub use arch::Vector;
84pub use arch::{load, load_unchecked};
85
86cfg_simd!(
87    #[doc(hidden)]
88    pub mod simd_scan;
89    #[doc(hidden)]
90    pub use simd_scan as scan;
91);
92
93cfg_fallback!(
94    #[doc(hidden)]
95    pub mod fallback_scan;
96    #[doc(hidden)]
97    pub use fallback_scan as scan;
98);
99
100cfg_i8!(
101    macro_rules! impl_cmp {
102        (
103            $CONST:ident,
104            overflow => $handle_overflow:expr,
105            default => $handle_non_overflow:expr
106            $(, max => $handle_max:expr)?
107            $(, min => $handle_min:expr)?
108            $(,)?
109        ) => {
110            match $CONST {
111                $(255 => $handle_max,)?
112                128..=255 => $handle_overflow,
113                $(0 => $handle_min,)?
114                _ => $handle_non_overflow
115            }
116        };
117    }
118
119    macro_rules! impl_gt {
120        ($MIN:ident, $gt:ident, $handle_max:expr $(, $handle_min:expr)? $(,)?) => {
121            impl_cmp!(
122                $MIN,
123                overflow => {
124                    // ensure that the value is less than 0 and greater than MAX
125                    move |data| {
126                        and(
127                            arch::less_than(data, splat(0)),
128                            arch::$gt(data, splat($MIN))
129                        )
130                    }
131                },
132                default => {
133                    // everything else we need to check that the value is either below 0 or greater
134                    // than max
135                    move |data| {
136                        or(
137                            arch::less_than(data, splat(0)),
138                            arch::$gt(data, splat($MIN))
139                        )
140                    }
141                },
142                max => $handle_max
143                $(, min => $handle_min)?
144            )
145        };
146    }
147
148    #[doc(hidden)] #[inline(always)]
149    pub const fn greater_than<const MIN: u8>() -> impl Fn(Vector) -> Vector {
150        unsafe { impl_gt!(
151            MIN,
152            greater_than,
153            move |_| { splat(0) }
154        ) }
155    }
156
157    #[doc(hidden)] #[inline(always)]
158    pub const fn greater_than_or_eq<const MIN: u8>() -> impl Fn(Vector) -> Vector {
159        unsafe { impl_gt!(
160            MIN,
161            greater_than_or_eq,
162            move |data| { eq(data, splat(255)) },
163            move |_| {
164                // gt or eq 0 would always be true, splat all ones
165                splat(0xFF)
166            }
167        ) }
168    }
169
170    macro_rules! impl_lt {
171        ($MAX:ident, $lt:ident, $handle_min:expr $(, $handle_max:expr)? $(,)?) => {
172            impl_cmp!(
173                $MAX,
174                overflow => move |data| unsafe {
175                    // overflow, so less than 127 OR MIN
176                    or(
177                        arch::greater_than_or_eq(data, splat(0)),
178                        arch::$lt(data, splat($MAX))
179                    )
180                },
181                default => move |data| unsafe {
182                    // no overflow, but data coming in could have, so greater than 0 and less than
183                    // MIN
184                    and(
185                        arch::greater_than_or_eq(data, splat(0)),
186                        arch::$lt(data, splat(MAX))
187                    )
188                },
189                $(max => $handle_max,)?
190                min => $handle_min
191            )
192        };
193    }
194
195    #[doc(hidden)] #[inline(always)]
196    pub const fn less_than<const MAX: u8>() -> impl Fn(Vector) -> Vector {
197        impl_lt!(
198            MAX,
199            less_than,
200            move |_| unsafe {
201                // always fails
202                splat(0)
203            }
204        )
205    }
206
207    #[doc(hidden)] #[inline(always)]
208    pub const fn less_than_or_eq<const MAX: u8>() -> impl Fn(Vector) -> Vector {
209        impl_lt!(
210            MAX,
211            less_than_or_eq,
212            move |data| unsafe {
213                // lt or eq 0 is practically just eq 0
214                eq(data, splat(0))
215            },
216            move |_| unsafe {
217                // lt or eq maximum value is always true
218                splat(0xFF)
219            }
220        )
221    }
222
223    macro_rules! impl_range_cast {
224        ($MIN:ident, $MAX:ident, $gt:ident, $lt:ident, $eq:expr, $max_128:expr) => {
225            match ($MIN, $MAX) {
226                _ if $MIN == $MAX => $eq,
227                (0..=127, 129..=255) => {
228                    // gt min OR lt MAX
229                    move |data| unsafe {
230                        or(
231                            arch::$gt(data, splat($MIN)),
232                            arch::$lt(data, splat($MAX))
233                        )
234                    }
235                },
236                (0..=127, 128) => $max_128,
237                _ => {
238                    // gte min AND lte MAX
239                    move |data| unsafe {
240                        and(
241                            arch::$gt(data, splat($MIN)),
242                            arch::$lt(data, splat($MAX))
243                        )
244                    }
245                }
246            }
247        };
248    }
249
250    #[doc(hidden)] #[inline(always)]
251    pub const fn range<const MIN: u8, const MAX: u8>() -> impl Fn(Vector) -> Vector {
252        impl_range_cast!(MIN, MAX, greater_than_or_eq, less_than_or_eq,
253            move |data| unsafe { eq(data, splat(MIN)) },
254            move |data| unsafe {
255                or(
256                    arch::greater_than_or_eq(data, splat(MIN)),
257                    eq(data, splat(MAX))
258                )
259            }
260        )
261    }
262
263    #[doc(hidden)] #[inline(always)]
264    pub const fn exclusive_range<const MIN: u8, const MAX: u8>() -> impl Fn(Vector) -> Vector {
265        if MIN.abs_diff(MAX) == 1 {
266            move |_data: Vector| -> Vector { unsafe { splat(0) } }
267        } else {
268            impl_range_cast!(
269                MIN, MAX, greater_than, less_than, move |_| unsafe { splat(0) },
270                move |data| unsafe { arch::greater_than(data, splat(MIN)) }
271            )
272        }
273    }
274);
275
276cfg_u8!(
277    #[doc(hidden)] #[inline(always)]
278    pub const fn less_than<const MAX: u8>() -> impl Fn(Vector) -> Vector {
279        match MAX {
280            0 => move |_| unsafe {
281                // always false
282                splat(0)
283            },
284            _ => move |data| unsafe {
285                arch::less_than(data, splat(MAX))
286            }
287        }
288    }
289
290    #[doc(hidden)] #[inline(always)]
291    pub const fn less_than_or_eq<const MAX: u8>() -> impl Fn(Vector) -> Vector {
292        match MAX {
293            255 => move |_| unsafe {
294                // always true
295                splat(0xFF)
296            },
297            _ => move |data| unsafe {
298                arch::less_than_or_eq(data, splat(MAX))
299            }
300        }
301    }
302
303    #[doc(hidden)] #[inline(always)]
304    pub const fn greater_than<const MIN: u8>() -> impl Fn(Vector) -> Vector {
305        match MIN {
306            255 => move |_| unsafe {
307                // always false
308                splat(0)
309            },
310            _ => move |data| unsafe {
311                arch::greater_than(data, splat(MIN))
312            }
313        }
314    }
315
316    #[doc(hidden)] #[inline(always)]
317    pub const fn greater_than_or_eq<const MIN: u8>() -> impl Fn(Vector) -> Vector {
318        match MIN {
319            0 => move |_| unsafe {
320                // always true
321                splat(0xFF)
322            },
323            _ => move |data| unsafe {
324                arch::greater_than_or_eq(data, splat(MIN))
325            }
326        }
327    }
328
329    #[doc(hidden)] #[inline(always)]
330    pub const fn range<const MIN: u8, const MAX: u8>() -> impl Fn(Vector) -> Vector {
331        match (MIN, MAX) {
332            _ if MIN == MAX => move |data| unsafe { eq(data, splat(MIN)) },
333            _ => move |data| unsafe {
334                and(
335                    arch::greater_than_or_eq(data, splat(MIN)),
336                    arch::less_than_or_eq(data, splat(MAX))
337                )
338            }
339        }
340    }
341
342    #[doc(hidden)] #[inline(always)]
343    pub const fn exclusive_range<const MIN: u8, const MAX: u8>() -> impl Fn(Vector) -> Vector {
344        match (MIN, MAX, MIN.abs_diff(MAX)) {
345            (_, _, 1) |
346            (_, _, _) if MIN == MAX => move |_| unsafe { splat(0) },
347            _ => move |data| unsafe {
348                and(
349                    arch::greater_than(data, splat(MIN)),
350                    arch::less_than(data, splat(MAX))
351                )
352            }
353        }
354    }
355);