1#[macro_export]
53macro_rules! dispatch {
54 ($level:expr, $simd:pat => $op:expr) => {{
55 $crate::__fearless_simd_dispatch_inner!($level, $simd => $op)
56 }};
57 (@impl $level:expr, $simd:pat => $op:expr; $forced_fallback_arm:literal) => {{
58 match $crate::Level::__dispatch_target($level) {
59 #[cfg(target_arch = "aarch64")]
60 $crate::Level::Neon(neon) => {
61 $crate::__fearless_simd_dispatch_with_token!(neon, $simd => $op)
62 }
63 #[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
64 $crate::Level::WasmSimd128(wasm) => {
65 $crate::__fearless_simd_dispatch_with_token!(wasm, $simd => $op)
66 }
67 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
68 $crate::Level::Sse2(sse2) => {
69 $crate::__fearless_simd_dispatch_dispatch_sse2!(sse2, $simd => $op)
70 }
71 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
72 $crate::Level::Sse4_2(sse4_2) => {
73 $crate::__fearless_simd_dispatch_dispatch_sse4_2!(sse4_2, $simd => $op)
74 }
75 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
76 $crate::Level::Avx2(avx2) => {
77 $crate::__fearless_simd_dispatch_dispatch_avx2!(avx2, $simd => $op)
78 }
79 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
80 $crate::Level::Avx512(avx512) => {
81 $crate::__fearless_simd_dispatch_dispatch_avx512!(avx512, $simd => $op)
82 }
83 #[cfg(any(
87 $forced_fallback_arm,
88 not(any(
89 all(target_arch = "aarch64", target_feature = "neon"),
90 all(
91 any(target_arch = "x86", target_arch = "x86_64"),
92 target_feature = "sse2",
93 target_feature = "fxsr"
94 ),
95 all(target_arch = "wasm32", target_feature = "simd128")
96 ))
97 ))]
98 $crate::Level::Fallback(fallback) => {
99 $crate::__fearless_simd_dispatch_with_token!(fallback, $simd => $op)
100 }
101 _ => unreachable!(),
102 }
103 }};
104}
105
106#[macro_export]
112#[doc(hidden)]
113#[cfg(feature = "force_support_fallback")]
114macro_rules! __fearless_simd_dispatch_inner {
115 ($level:expr, $simd:pat => $op:expr) => {
116 $crate::dispatch!(@impl $level, $simd => $op; true)
117 };
118}
119
120#[macro_export]
122#[doc(hidden)]
123#[cfg(not(feature = "force_support_fallback"))]
124macro_rules! __fearless_simd_dispatch_inner {
125 ($level:expr, $simd:pat => $op:expr) => {
126 $crate::dispatch!(@impl $level, $simd => $op; false)
127 };
128}
129
130#[macro_export]
139#[doc(hidden)]
140macro_rules! __fearless_simd_dispatch_with_token {
141 ($token:expr, $simd:pat => $op:expr) => {{
142 #[inline(always)]
146 fn launder<S: $crate::Simd>(x: S) -> impl $crate::Simd {
147 x
148 }
149
150 let __fearless_simd_token = $token;
154 let $simd = launder(__fearless_simd_token);
155 $crate::Simd::vectorize(
156 __fearless_simd_token,
157 #[inline(always)]
158 || $op,
159 )
160 }};
161}
162
163#[macro_export]
165#[doc(hidden)]
166macro_rules! __fearless_simd_dispatch_pruned {
167 ($proof:expr) => {{
168 let __fearless_simd_proof = $proof;
169 let _ = __fearless_simd_proof;
170 unreachable!("this SIMD level was pruned from dispatch")
171 }};
172}
173
174#[macro_export]
176#[doc(hidden)]
177#[cfg(not(disable_dispatch_avx512))]
178macro_rules! __fearless_simd_dispatch_dispatch_avx512 {
179 ($avx512:expr, $simd:pat => $op:expr) => {
180 $crate::__fearless_simd_dispatch_with_token!($avx512, $simd => $op)
181 };
182}
183
184#[macro_export]
186#[doc(hidden)]
187#[cfg(disable_dispatch_avx512)]
188macro_rules! __fearless_simd_dispatch_dispatch_avx512 {
189 ($avx512:expr, $simd:pat => $op:expr) => {{ $crate::__fearless_simd_dispatch_pruned!($avx512) }};
190}
191
192#[macro_export]
194#[doc(hidden)]
195#[cfg(all(
196 not(disable_dispatch_avx2),
197 any(
198 disable_dispatch_avx512,
199 not(all(
200 target_feature = "adx",
201 target_feature = "aes",
202 target_feature = "avx512bitalg",
203 target_feature = "avx512bw",
204 target_feature = "avx512cd",
205 target_feature = "avx512dq",
206 target_feature = "avx512f",
207 target_feature = "avx512ifma",
208 target_feature = "avx512vbmi",
209 target_feature = "avx512vbmi2",
210 target_feature = "avx512vl",
211 target_feature = "avx512vnni",
212 target_feature = "avx512vpopcntdq",
213 target_feature = "bmi1",
214 target_feature = "bmi2",
215 target_feature = "cmpxchg16b",
216 target_feature = "fma",
217 target_feature = "fxsr",
218 target_feature = "gfni",
219 target_feature = "lzcnt",
220 target_feature = "movbe",
221 target_feature = "pclmulqdq",
222 target_feature = "popcnt",
223 target_feature = "rdrand",
224 target_feature = "rdseed",
225 target_feature = "sha",
226 target_feature = "vaes",
227 target_feature = "vpclmulqdq",
228 target_feature = "xsave",
229 target_feature = "xsavec",
230 target_feature = "xsaveopt",
231 target_feature = "xsaves",
232 ))
233 )
234))]
235macro_rules! __fearless_simd_dispatch_dispatch_avx2 {
236 ($avx2:expr, $simd:pat => $op:expr) => {
237 $crate::__fearless_simd_dispatch_with_token!($avx2, $simd => $op)
238 };
239}
240
241#[macro_export]
243#[doc(hidden)]
244#[cfg(any(
245 disable_dispatch_avx2,
246 all(
247 not(disable_dispatch_avx512),
248 target_feature = "adx",
249 target_feature = "aes",
250 target_feature = "avx512bitalg",
251 target_feature = "avx512bw",
252 target_feature = "avx512cd",
253 target_feature = "avx512dq",
254 target_feature = "avx512f",
255 target_feature = "avx512ifma",
256 target_feature = "avx512vbmi",
257 target_feature = "avx512vbmi2",
258 target_feature = "avx512vl",
259 target_feature = "avx512vnni",
260 target_feature = "avx512vpopcntdq",
261 target_feature = "bmi1",
262 target_feature = "bmi2",
263 target_feature = "cmpxchg16b",
264 target_feature = "fma",
265 target_feature = "fxsr",
266 target_feature = "gfni",
267 target_feature = "lzcnt",
268 target_feature = "movbe",
269 target_feature = "pclmulqdq",
270 target_feature = "popcnt",
271 target_feature = "rdrand",
272 target_feature = "rdseed",
273 target_feature = "sha",
274 target_feature = "vaes",
275 target_feature = "vpclmulqdq",
276 target_feature = "xsave",
277 target_feature = "xsavec",
278 target_feature = "xsaveopt",
279 target_feature = "xsaves",
280 ),
281))]
282macro_rules! __fearless_simd_dispatch_dispatch_avx2 {
283 ($avx2:expr, $simd:pat => $op:expr) => {{ $crate::__fearless_simd_dispatch_pruned!($avx2) }};
284}
285
286#[macro_export]
288#[doc(hidden)]
289#[cfg(all(
290 not(disable_dispatch_sse4_2),
291 any(
292 disable_dispatch_avx2,
293 not(all(
294 target_feature = "avx2",
295 target_feature = "bmi1",
296 target_feature = "bmi2",
297 target_feature = "cmpxchg16b",
298 target_feature = "f16c",
299 target_feature = "fma",
300 target_feature = "fxsr",
301 target_feature = "lzcnt",
302 target_feature = "movbe",
303 target_feature = "popcnt",
304 target_feature = "xsave"
305 ))
306 )
307))]
308macro_rules! __fearless_simd_dispatch_dispatch_sse4_2 {
309 ($sse4_2:expr, $simd:pat => $op:expr) => {
310 $crate::__fearless_simd_dispatch_with_token!($sse4_2, $simd => $op)
311 };
312}
313
314#[macro_export]
316#[doc(hidden)]
317#[cfg(any(
318 disable_dispatch_sse4_2,
319 all(
320 not(disable_dispatch_avx2),
321 target_feature = "avx2",
322 target_feature = "bmi1",
323 target_feature = "bmi2",
324 target_feature = "cmpxchg16b",
325 target_feature = "f16c",
326 target_feature = "fma",
327 target_feature = "fxsr",
328 target_feature = "lzcnt",
329 target_feature = "movbe",
330 target_feature = "popcnt",
331 target_feature = "xsave"
332 ),
333))]
334macro_rules! __fearless_simd_dispatch_dispatch_sse4_2 {
335 ($sse4_2:expr, $simd:pat => $op:expr) => {{ $crate::__fearless_simd_dispatch_pruned!($sse4_2) }};
336}
337
338#[macro_export]
340#[doc(hidden)]
341#[cfg(all(
342 any(
343 not(disable_dispatch_sse2),
344 all(target_feature = "sse2", target_feature = "fxsr")
345 ),
346 any(
347 disable_dispatch_sse4_2,
348 not(all(
349 target_feature = "fxsr",
350 target_feature = "sse4.2",
351 target_feature = "cmpxchg16b",
352 target_feature = "popcnt",
353 )),
354 )
355))]
356macro_rules! __fearless_simd_dispatch_dispatch_sse2 {
357 ($sse2:expr, $simd:pat => $op:expr) => {
358 $crate::__fearless_simd_dispatch_with_token!($sse2, $simd => $op)
359 };
360}
361
362#[macro_export]
364#[doc(hidden)]
365#[cfg(any(
366 all(
367 disable_dispatch_sse2,
368 not(all(target_feature = "sse2", target_feature = "fxsr"))
369 ),
370 all(
371 not(disable_dispatch_sse4_2),
372 target_feature = "fxsr",
373 target_feature = "sse4.2",
374 target_feature = "cmpxchg16b",
375 target_feature = "popcnt",
376 ),
377))]
378macro_rules! __fearless_simd_dispatch_dispatch_sse2 {
379 ($sse2:expr, $simd:pat => $op:expr) => {{ $crate::__fearless_simd_dispatch_pruned!($sse2) }};
380}
381
382#[cfg(test)]
383#[expect(
385 unreachable_patterns,
386 reason = "Level is non_exhaustive, but you must be exhaustive within the same crate."
387)]
388mod tests {
389 use crate::{Level, Simd};
390
391 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
392 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
393 enum X86DispatchBackend {
394 Fallback,
395 Sse2,
396 Sse4_2,
397 Avx2,
398 Avx512,
399 }
400
401 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
402 fn x86_dispatch_backend<S: Simd>(_: S) -> X86DispatchBackend {
403 use core::any::TypeId;
404
405 #[cfg(any(
406 not(all(target_feature = "sse2", target_feature = "fxsr")),
407 feature = "force_support_fallback"
408 ))]
409 if TypeId::of::<S>() == TypeId::of::<crate::Fallback>() {
410 return X86DispatchBackend::Fallback;
411 }
412
413 if TypeId::of::<S>() == TypeId::of::<crate::Sse2>() {
414 X86DispatchBackend::Sse2
415 } else if TypeId::of::<S>() == TypeId::of::<crate::Sse4_2>() {
416 X86DispatchBackend::Sse4_2
417 } else if TypeId::of::<S>() == TypeId::of::<crate::Avx2>() {
418 X86DispatchBackend::Avx2
419 } else if TypeId::of::<S>() == TypeId::of::<crate::Avx512>() {
420 X86DispatchBackend::Avx512
421 } else {
422 unreachable!("unexpected x86 dispatch backend")
423 }
424 }
425
426 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
427 fn expected_x86_dispatch_backend(level: Level) -> X86DispatchBackend {
428 if cfg!(not(disable_dispatch_avx512)) && level.as_avx512().is_some() {
429 X86DispatchBackend::Avx512
430 } else if cfg!(not(disable_dispatch_avx2)) && level.as_avx2().is_some() {
431 X86DispatchBackend::Avx2
432 } else if cfg!(not(disable_dispatch_sse4_2)) && level.as_sse4_2().is_some() {
433 X86DispatchBackend::Sse4_2
434 } else if cfg!(any(
435 not(disable_dispatch_sse2),
436 all(target_feature = "sse2", target_feature = "fxsr")
437 )) && level.as_sse2().is_some()
438 {
439 X86DispatchBackend::Sse2
440 } else {
441 X86DispatchBackend::Fallback
442 }
443 }
444
445 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
446 #[allow(dead_code, reason = "Compile test")]
447 fn x86_level_variants_remain_available() {
448 let _ = Level::Sse2(unsafe { crate::Sse2::assume_supported() });
449 let _ = Level::Sse4_2(unsafe { crate::Sse4_2::assume_supported() });
450 let _ = Level::Avx2(unsafe { crate::Avx2::assume_supported() });
451 let _ = Level::Avx512(unsafe { crate::Avx512::assume_supported() });
452 }
453
454 #[allow(dead_code, reason = "Compile test")]
455 fn dispatch_generic() {
456 fn generic<S: Simd, T>(_: S, x: T) -> T {
457 x
458 }
459 dispatch!(Level::new(), simd => generic::<_, ()>(simd, ()));
460 }
461
462 #[allow(dead_code, reason = "Compile test")]
463 fn dispatch_value() {
464 fn make_fn<S: Simd>() -> impl FnOnce(S) {
465 |_| ()
466 }
467 dispatch!(Level::new(), simd => (make_fn())(simd));
468 }
469
470 #[test]
471 fn dispatch_output() {
472 assert_eq!(42, dispatch!(Level::new(), _simd => 42));
473 }
474
475 #[cfg(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))]
476 #[test]
477 fn dispatch_respects_x86_multiversion_features() {
478 let level = Level::new();
479 let actual = dispatch!(level, simd => x86_dispatch_backend(simd));
480
481 assert_eq!(actual, expected_x86_dispatch_backend(level));
482 }
483
484 #[cfg(all(
485 any(target_arch = "x86", target_arch = "x86_64"),
486 target_feature = "sse2",
487 not(target_feature = "fxsr")
488 ))]
489 #[test]
490 fn x86_baseline_does_not_use_sse2_without_fxsr() {
491 assert!(Level::baseline().is_fallback());
492
493 let actual = dispatch!(Level::Fallback(crate::Fallback::new()), simd =>
494 x86_dispatch_backend(simd)
495 );
496
497 assert_eq!(actual, X86DispatchBackend::Fallback);
498 }
499
500 #[cfg(all(
501 feature = "std",
502 any(target_arch = "x86", target_arch = "x86_64"),
503 any(
504 disable_dispatch_sse2,
505 disable_dispatch_sse4_2,
506 disable_dispatch_avx2,
507 disable_dispatch_avx512
508 )
509 ))]
510 #[test]
511 fn disabled_x86_multiversioning_does_not_filter_token_access() {
512 let level = Level::new();
513
514 #[cfg(disable_dispatch_sse2)]
516 let _ = level.as_sse2().is_some();
517
518 #[cfg(disable_dispatch_sse4_2)]
519 let _ = level.as_sse4_2().is_some();
520
521 #[cfg(disable_dispatch_avx2)]
522 let _ = level.as_avx2().is_some();
523
524 #[cfg(disable_dispatch_avx512)]
525 let _ = level.as_avx512().is_some();
526 }
527
528 #[cfg(all(
529 feature = "std",
530 any(target_arch = "x86", target_arch = "x86_64"),
531 disable_dispatch_sse2,
532 target_feature = "sse2",
533 target_feature = "fxsr"
534 ))]
535 #[test]
536 fn ambient_sse2_is_the_terminal_backend_when_dispatch_is_disabled() {
537 let actual = dispatch!(Level::new(), simd => x86_dispatch_backend(simd));
538
539 assert_ne!(actual, X86DispatchBackend::Fallback);
540 }
541
542 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
544 #[test]
545 fn high_baseline_dispatches_lower_tokens() {
546 if let Some(sse2) = Level::baseline().as_sse2() {
547 let actual = dispatch!(sse2.level(), simd => x86_dispatch_backend(simd));
548
549 assert_eq!(actual, expected_x86_dispatch_backend(Level::baseline()));
550 }
551
552 if let Some(sse4_2) = Level::baseline().as_sse4_2() {
553 let actual = dispatch!(sse4_2.level(), simd => x86_dispatch_backend(simd));
554
555 assert_eq!(actual, expected_x86_dispatch_backend(Level::baseline()));
556 }
557 }
558
559 #[cfg(all(
560 not(feature = "force_support_fallback"),
561 any(target_arch = "x86", target_arch = "x86_64"),
562 not(all(target_feature = "sse2", target_feature = "fxsr"))
563 ))]
564 #[test]
565 fn fallback_dispatches_as_baseline() {
566 let actual = dispatch!(Level::Fallback(crate::Fallback::new()), simd =>
567 x86_dispatch_backend(simd)
568 );
569
570 assert_eq!(actual, expected_x86_dispatch_backend(Level::baseline()));
571 }
572
573 mod no_import_simd {
574 #[test]
576 fn dispatch_with_no_imports() {
577 let res = dispatch!(crate::Level::new(), _ => 1 + 2);
578 assert_eq!(res, 3);
579 }
580 }
581}