From 007544e6eb9dd8af86f7778ba409c99d63cf1f36 Mon Sep 17 00:00:00 2001 From: "Sergey \"Shnatsel\" Davidoff" Date: Fri, 7 Aug 2026 10:30:59 +0100 Subject: [PATCH 1/5] Only compile Fallback level on targets without a better baseline --- CHANGELOG.md | 1 + fearless_simd/README.md | 2 + fearless_simd/src/generated.rs | 44 +++++++++- fearless_simd/src/lib.rs | 107 ++++++++++++++++++++++- fearless_simd/src/macros.rs | 144 ++++++++++++++++++------------- fearless_simd_tests/Cargo.toml | 2 +- fearless_simd_tests/tests/mod.rs | 7 ++ 7 files changed, 240 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af9fcfc8..529644dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ You can find its changes [documented below](#060-2026-07-10). - The `new_unchecked()` function on SIMD level tokens such as `Avx2` has been renamed to `assume_supported()` and is now safe to call from contexts that already contain the appropriate `#[target_feature]` annotations. Functions without such annotations can still call `assume_supported()` with an `unsafe` block. ([#293][] by [@Shnatsel][]) - On x86_64 targets with static SSE2 support, `Level::baseline()` now returns `Sse2` instead of `Fallback`. ([#270][] by [@Shnatsel][]) +- The scalar `Fallback` backend and `Level::Fallback` variant are no longer compiled when the target has a better ambient SIMD baseline (e.g. SSE2 on x86, NEON on Aarch64). The `force_support_fallback` feature continues to make them available for testing. `disable_dispatch_sse2` no longer disables SSE2 if it is the baseline level. - The `fxsr` CPU feature is now required for all x86 SIMD levels. It is present in hardware on all SIMD-capable CPUs, but it is possible to disable it in some emulators combined with a custom Rust target specification. ([#270][] by [@Shnatsel][]) - All native-width non-mask vector types now share `u8s` as their byte representation, enabling `Bytes::bitcast` between arbitrary lane types in code generic over `Simd`. The byte representation of any `SimdBase` type is now also guaranteed to be an idempotent, same-token `u8` SIMD vector, so it can be manipulated directly in generic code. - `SimdBase::Mask` now guarantees support for selecting vectors of its associated `SimdBase` type, enabling mask selection in generic code without additional bounds. diff --git a/fearless_simd/README.md b/fearless_simd/README.md index 8ac91324..95a0834a 100644 --- a/fearless_simd/README.md +++ b/fearless_simd/README.md @@ -199,6 +199,8 @@ As a last resort, you can turn off multiversioning for specific SIMD instruction These configuration flags only control automatic multiversioning. Disabling one does not remove its token type, its [`Simd`] implementation, or explicit [`kernel`] support; for example, an `Avx2` token can still be used to call an AVX2 kernel when the CPU supports it. +`disable_dispatch_sse2` has no effect when SSE2 is part of the ambient target baseline, because +that baseline remains the terminal dispatch backend. Note that later extensions can be beneficial even if you are only using 128-bit vectors: AVX2 and AVX-512 provide more efficient instructions for some operations, diff --git a/fearless_simd/src/generated.rs b/fearless_simd/src/generated.rs index d42fbb31..8dc14b15 100644 --- a/fearless_simd/src/generated.rs +++ b/fearless_simd/src/generated.rs @@ -3,7 +3,6 @@ #![expect( missing_docs, - trivial_numeric_casts, clippy::unnecessary_cast, clippy::cast_possible_truncation, clippy::unseparated_literal_suffix, @@ -11,8 +10,18 @@ clippy::wrong_self_convention, reason = "Simplifies the generator and has no effect on the machine code" )] +#![allow( + trivial_numeric_casts, + reason = "Not every conditionally compiled backend contains a trivial cast" +)] #![cfg_attr( - target_arch = "x86_64", + all( + target_arch = "x86_64", + any( + not(all(target_feature = "sse2", target_feature = "fxsr")), + feature = "force_support_fallback" + ) + ), expect( clippy::new_without_default, reason = "TODO: https://github.com/linebender/fearless_simd/issues/40" @@ -44,6 +53,22 @@ mod avx2; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] mod avx512; +// Keep this predicate in sync with `Level::Fallback`, `is_fallback`, and `dispatch!`. +#[cfg(any( + all(target_arch = "aarch64", not(target_feature = "neon")), + all( + any(target_arch = "x86", target_arch = "x86_64"), + not(all(target_feature = "sse2", target_feature = "fxsr")) + ), + all(target_arch = "wasm32", not(target_feature = "simd128")), + not(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "wasm32" + )), + feature = "force_support_fallback" +))] mod fallback; #[cfg(target_arch = "aarch64")] mod neon; @@ -61,6 +86,21 @@ mod wasm; pub use avx2::*; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub use avx512::*; +#[cfg(any( + all(target_arch = "aarch64", not(target_feature = "neon")), + all( + any(target_arch = "x86", target_arch = "x86_64"), + not(all(target_feature = "sse2", target_feature = "fxsr")) + ), + all(target_arch = "wasm32", not(target_feature = "simd128")), + not(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "wasm32" + )), + feature = "force_support_fallback" +))] pub use fallback::*; #[cfg(target_arch = "aarch64")] pub use neon::*; diff --git a/fearless_simd/src/lib.rs b/fearless_simd/src/lib.rs index 31a7711a..a987716c 100644 --- a/fearless_simd/src/lib.rs +++ b/fearless_simd/src/lib.rs @@ -166,6 +166,8 @@ //! These configuration flags only control automatic multiversioning. Disabling one does not remove its token type, its //! [`Simd`] implementation, or explicit [`kernel`] support; for example, an `Avx2` token can still be used to call an //! AVX2 kernel when the CPU supports it. +//! `disable_dispatch_sse2` has no effect when SSE2 is part of the ambient target baseline, because +//! that baseline remains the terminal dispatch backend. //! //! Note that later extensions can be beneficial even if you are only using 128-bit vectors: //! AVX2 and AVX-512 provide more efficient instructions for some operations, @@ -352,7 +354,7 @@ fn detect_x86_level() -> Level { // Safety: All features required by Sse2 were detected above. unsafe { Level::Sse2(Sse2::assume_supported()) } } else { - Level::Fallback(Fallback::new()) + Level::baseline() } } @@ -366,6 +368,22 @@ pub enum Level { /// Scalar fallback level, i.e. no supported SIMD features are to be used. /// /// This can be created with [`Level::fallback`]. + // Keep this predicate in sync with the fallback module, `is_fallback`, and `dispatch!`. + #[cfg(any( + all(target_arch = "aarch64", not(target_feature = "neon")), + all( + any(target_arch = "x86", target_arch = "x86_64"), + not(all(target_feature = "sse2", target_feature = "fxsr")) + ), + all(target_arch = "wasm32", not(target_feature = "simd128")), + not(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "wasm32" + )), + feature = "force_support_fallback" + ))] Fallback(Fallback), /// The Neon instruction set on 64 bit ARM. #[cfg(target_arch = "aarch64")] @@ -450,8 +468,30 @@ impl Level { /// Check whether this is the `Fallback` level; that is, whether no better feature level could /// be statically or dynamically detected. This is useful if there's a scalarized version of /// your algorithm that runs faster if SIMD isn't supported. + /// + /// This method is always available, even when the fallback backend is not compiled. In that + /// case, it always returns `false`. pub fn is_fallback(self) -> bool { - matches!(self, Self::Fallback(_)) + // Keep this predicate in sync with `Level::Fallback`, the fallback module, and `dispatch!`. + #[cfg(any( + all(target_arch = "aarch64", not(target_feature = "neon")), + all( + any(target_arch = "x86", target_arch = "x86_64"), + not(all(target_feature = "sse2", target_feature = "fxsr")) + ), + all(target_arch = "wasm32", not(target_feature = "simd128")), + not(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "wasm32" + )), + feature = "force_support_fallback" + ))] + return matches!(self, Self::Fallback(_)); + + #[allow(unreachable_code, reason = "Fallback is unavailable in some cfgs.")] + false } /// If this is a proof that Neon (or better) is available, access that instruction set. @@ -799,7 +839,12 @@ impl Level { return Self::Sse4_2(sse4_2); } - #[cfg(not(disable_dispatch_sse2))] + // The ambient SSE2 baseline is the terminal backend and cannot be disabled. Falling + // below it would require compiling the otherwise-unneeded fallback implementation. + #[cfg(any( + not(disable_dispatch_sse2), + all(target_feature = "sse2", target_feature = "fxsr") + ))] if let Some(sse2) = self.as_sse2().or_else(|| baseline.as_sse2()) { return Self::Sse2(sse2); } @@ -824,7 +869,41 @@ impl Level { } } - Self::Fallback(Fallback::new()) + // Keep this predicate in sync with `Level::Fallback`, the fallback module, and + // `dispatch!`. + #[cfg(any( + all(target_arch = "aarch64", not(target_feature = "neon")), + all( + any(target_arch = "x86", target_arch = "x86_64"), + not(all(target_feature = "sse2", target_feature = "fxsr")) + ), + all(target_arch = "wasm32", not(target_feature = "simd128")), + not(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "wasm32" + )), + feature = "force_support_fallback" + ))] + return Self::Fallback(Fallback::new()); + + #[cfg(not(any( + all(target_arch = "aarch64", not(target_feature = "neon")), + all( + any(target_arch = "x86", target_arch = "x86_64"), + not(all(target_feature = "sse2", target_feature = "fxsr")) + ), + all(target_arch = "wasm32", not(target_feature = "simd128")), + not(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "wasm32" + )), + feature = "force_support_fallback" + )))] + Self::baseline() } /// Create a scalar fallback level, which uses no SIMD instructions. @@ -852,6 +931,26 @@ mod tests { assert_is_send_sync::(); } + #[cfg(not(any( + all(target_arch = "aarch64", not(target_feature = "neon")), + all( + any(target_arch = "x86", target_arch = "x86_64"), + not(all(target_feature = "sse2", target_feature = "fxsr")) + ), + all(target_arch = "wasm32", not(target_feature = "simd128")), + not(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "wasm32" + )), + feature = "force_support_fallback" + )))] + #[test] + fn is_fallback_is_false_when_fallback_is_unavailable() { + assert!(!Level::baseline().is_fallback()); + } + #[cfg(all( any(feature = "std", target_arch = "wasm32"), not(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64"))) diff --git a/fearless_simd/src/macros.rs b/fearless_simd/src/macros.rs index 8f16e658..5f74d696 100644 --- a/fearless_simd/src/macros.rs +++ b/fearless_simd/src/macros.rs @@ -49,6 +49,9 @@ #[macro_export] macro_rules! dispatch { ($level:expr, $simd:pat => $op:expr) => {{ + $crate::__fearless_simd_dispatch_inner!($level, $simd => $op) + }}; + (@impl $level:expr, $simd:pat => $op:expr; $forced_fallback_arm:literal) => {{ match $crate::Level::__dispatch_target($level) { #[cfg(target_arch = "aarch64")] $crate::Level::Neon(neon) => { @@ -74,14 +77,56 @@ macro_rules! dispatch { $crate::Level::Avx512(avx512) => { $crate::__fearless_simd_dispatch_dispatch_avx512!(avx512, $simd => $op) } - $crate::Level::Fallback(fb) => { - $crate::__fearless_simd_dispatch_dispatch_fallback!(fb, $simd => $op) + // Keep this predicate in sync with `Level::Fallback`, the fallback module, and + // `Level::is_fallback`. The literal carries fearless_simd's feature selection into + // expansion in a downstream crate, where `cfg(feature = ...)` would be incorrect. + #[cfg(any( + all(target_arch = "aarch64", not(target_feature = "neon")), + all( + any(target_arch = "x86", target_arch = "x86_64"), + not(all(target_feature = "sse2", target_feature = "fxsr")) + ), + all(target_arch = "wasm32", not(target_feature = "simd128")), + not(any( + target_arch = "x86", + target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "wasm32" + )), + $forced_fallback_arm + ))] + $crate::Level::Fallback(fallback) => { + $crate::__fearless_simd_dispatch_with_token!(fallback, $simd => $op) } _ => unreachable!(), } }}; } +// This macro turns whether `force_support_fallback` is enabled into a boolean literal in +// `dispatch!`. Exported macro bodies are expanded in the downstream crate, so evaluating the +// feature cfg directly in `dispatch!` would inspect the wrong crate's features. + +/// Implementation detail of [`crate::dispatch`]; this is not public API. +#[macro_export] +#[doc(hidden)] +#[cfg(feature = "force_support_fallback")] +macro_rules! __fearless_simd_dispatch_inner { + ($level:expr, $simd:pat => $op:expr) => { + $crate::dispatch!(@impl $level, $simd => $op; true) + }; +} + +/// Implementation detail of [`crate::dispatch`]; this is not public API. +#[macro_export] +#[doc(hidden)] +#[cfg(not(feature = "force_support_fallback"))] +macro_rules! __fearless_simd_dispatch_inner { + ($level:expr, $simd:pat => $op:expr) => { + $crate::dispatch!(@impl $level, $simd => $op; false) + }; +} + // The dispatch helpers are split into cfg-selected macro definitions // because exported macro bodies are expanded in the downstream crate, // so selecting the helper definitions here preserves `fearless_simd`'s @@ -126,58 +171,6 @@ macro_rules! __fearless_simd_dispatch_pruned { }}; } -/// Implementation detail of [`crate::dispatch`]; this is not public API. -#[macro_export] -#[doc(hidden)] -#[cfg(any( - all(target_arch = "aarch64", not(target_feature = "neon")), - all( - any(target_arch = "x86", target_arch = "x86_64"), - any( - disable_dispatch_sse2, - not(all(target_feature = "sse2", target_feature = "fxsr")) - ), - ), - all(target_arch = "wasm32", not(target_feature = "simd128")), - not(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "aarch64", - target_arch = "wasm32" - )), - feature = "force_support_fallback" -))] -macro_rules! __fearless_simd_dispatch_dispatch_fallback { - ($fallback:expr, $simd:pat => $op:expr) => { - $crate::__fearless_simd_dispatch_with_token!($fallback, $simd => $op) - }; -} - -/// Implementation detail of [`crate::dispatch`]; this is not public API. -#[macro_export] -#[doc(hidden)] -#[cfg(not(any( - all(target_arch = "aarch64", not(target_feature = "neon")), - all( - any(target_arch = "x86", target_arch = "x86_64"), - any( - disable_dispatch_sse2, - not(all(target_feature = "sse2", target_feature = "fxsr")) - ), - ), - all(target_arch = "wasm32", not(target_feature = "simd128")), - not(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "aarch64", - target_arch = "wasm32" - )), - feature = "force_support_fallback" -)))] -macro_rules! __fearless_simd_dispatch_dispatch_fallback { - ($fallback:expr, $simd:pat => $op:expr) => {{ $crate::__fearless_simd_dispatch_pruned!($fallback) }}; -} - /// Implementation detail of [`crate::dispatch`]; this is not public API. #[macro_export] #[doc(hidden)] @@ -344,7 +337,10 @@ macro_rules! __fearless_simd_dispatch_dispatch_sse4_2 { #[macro_export] #[doc(hidden)] #[cfg(all( - not(disable_dispatch_sse2), + any( + not(disable_dispatch_sse2), + all(target_feature = "sse2", target_feature = "fxsr") + ), any( disable_dispatch_sse4_2, not(all( @@ -365,7 +361,10 @@ macro_rules! __fearless_simd_dispatch_dispatch_sse2 { #[macro_export] #[doc(hidden)] #[cfg(any( - disable_dispatch_sse2, + all( + disable_dispatch_sse2, + not(all(target_feature = "sse2", target_feature = "fxsr")) + ), all( not(disable_dispatch_sse4_2), target_feature = "fxsr", @@ -401,9 +400,15 @@ mod tests { fn x86_dispatch_backend(_: S) -> X86DispatchBackend { use core::any::TypeId; + #[cfg(any( + not(all(target_feature = "sse2", target_feature = "fxsr")), + feature = "force_support_fallback" + ))] if TypeId::of::() == TypeId::of::() { - X86DispatchBackend::Fallback - } else if TypeId::of::() == TypeId::of::() { + return X86DispatchBackend::Fallback; + } + + if TypeId::of::() == TypeId::of::() { X86DispatchBackend::Sse2 } else if TypeId::of::() == TypeId::of::() { X86DispatchBackend::Sse4_2 @@ -424,7 +429,11 @@ mod tests { X86DispatchBackend::Avx2 } else if cfg!(not(disable_dispatch_sse4_2)) && level.as_sse4_2().is_some() { X86DispatchBackend::Sse4_2 - } else if cfg!(not(disable_dispatch_sse2)) && level.as_sse2().is_some() { + } else if cfg!(any( + not(disable_dispatch_sse2), + all(target_feature = "sse2", target_feature = "fxsr") + )) && level.as_sse2().is_some() + { X86DispatchBackend::Sse2 } else { X86DispatchBackend::Fallback @@ -514,6 +523,20 @@ mod tests { let _ = level.as_avx512().is_some(); } + #[cfg(all( + feature = "std", + any(target_arch = "x86", target_arch = "x86_64"), + disable_dispatch_sse2, + target_feature = "sse2", + target_feature = "fxsr" + ))] + #[test] + fn ambient_sse2_is_the_terminal_backend_when_dispatch_is_disabled() { + let actual = dispatch!(Level::new(), simd => x86_dispatch_backend(simd)); + + assert_ne!(actual, X86DispatchBackend::Fallback); + } + /// Mostly useful with `RUSTFLAGS='-C target-cpu=x86-64-v3'` and higher, doesn't do much otherwise #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] #[test] @@ -533,7 +556,8 @@ mod tests { #[cfg(all( not(feature = "force_support_fallback"), - any(target_arch = "x86", target_arch = "x86_64") + any(target_arch = "x86", target_arch = "x86_64"), + not(all(target_feature = "sse2", target_feature = "fxsr")) ))] #[test] fn fallback_dispatches_as_baseline() { diff --git a/fearless_simd_tests/Cargo.toml b/fearless_simd_tests/Cargo.toml index 92039257..1a3ca906 100644 --- a/fearless_simd_tests/Cargo.toml +++ b/fearless_simd_tests/Cargo.toml @@ -21,5 +21,5 @@ workspace = true [dependencies] fastrand = "2.5.0" -fearless_simd = { workspace = true, features = ["std"] } +fearless_simd = { workspace = true, features = ["std", "force_support_fallback"] } fearless_simd_dev_macros = { workspace = true } diff --git a/fearless_simd_tests/tests/mod.rs b/fearless_simd_tests/tests/mod.rs index 55b7dd31..caed2bb2 100644 --- a/fearless_simd_tests/tests/mod.rs +++ b/fearless_simd_tests/tests/mod.rs @@ -19,6 +19,13 @@ mod harness; mod soundness; mod token_soundness; +#[test] +fn dispatches_forced_fallback_across_crates() { + let is_fallback = dispatch!(Level::fallback(), simd => simd.level().is_fallback()); + + assert!(is_fallback); +} + #[allow(clippy::allow_attributes, reason = "Only needed in some cfgs.")] #[allow( unused_variables, From 394261b972f054331bd680c887fd9e338940e564 Mon Sep 17 00:00:00 2001 From: "Sergey \"Shnatsel\" Davidoff" Date: Fri, 7 Aug 2026 10:43:48 +0100 Subject: [PATCH 2/5] Simplify fallback cfgs --- fearless_simd/src/generated.rs | 59 ++++++++---------- fearless_simd/src/lib.rs | 109 ++++++++++----------------------- fearless_simd/src/macros.rs | 27 ++++---- 3 files changed, 71 insertions(+), 124 deletions(-) diff --git a/fearless_simd/src/generated.rs b/fearless_simd/src/generated.rs index 8dc14b15..a8b38df6 100644 --- a/fearless_simd/src/generated.rs +++ b/fearless_simd/src/generated.rs @@ -49,27 +49,33 @@ //! //! All files in this subdirectory are autogenerated by the `fearless_simd_gen` crate. +// Fallback is unnecessary exactly when one of our SIMD backends is guaranteed as the baseline. +// Keep this predicate in sync with `Level::Fallback` and `dispatch!`. +macro_rules! with_fallback { + ($($item:item)*) => {$( + #[cfg(any( + feature = "force_support_fallback", + not(any( + all(target_arch = "aarch64", target_feature = "neon"), + all( + any(target_arch = "x86", target_arch = "x86_64"), + target_feature = "sse2", + target_feature = "fxsr" + ), + all(target_arch = "wasm32", target_feature = "simd128") + )) + ))] + $item + )*}; +} + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] mod avx2; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] mod avx512; -// Keep this predicate in sync with `Level::Fallback`, `is_fallback`, and `dispatch!`. -#[cfg(any( - all(target_arch = "aarch64", not(target_feature = "neon")), - all( - any(target_arch = "x86", target_arch = "x86_64"), - not(all(target_feature = "sse2", target_feature = "fxsr")) - ), - all(target_arch = "wasm32", not(target_feature = "simd128")), - not(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "aarch64", - target_arch = "wasm32" - )), - feature = "force_support_fallback" -))] -mod fallback; +with_fallback! { + mod fallback; +} #[cfg(target_arch = "aarch64")] mod neon; mod ops; @@ -86,22 +92,9 @@ mod wasm; pub use avx2::*; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub use avx512::*; -#[cfg(any( - all(target_arch = "aarch64", not(target_feature = "neon")), - all( - any(target_arch = "x86", target_arch = "x86_64"), - not(all(target_feature = "sse2", target_feature = "fxsr")) - ), - all(target_arch = "wasm32", not(target_feature = "simd128")), - not(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "aarch64", - target_arch = "wasm32" - )), - feature = "force_support_fallback" -))] -pub use fallback::*; +with_fallback! { + pub use fallback::*; +} #[cfg(target_arch = "aarch64")] pub use neon::*; pub use simd_trait::*; diff --git a/fearless_simd/src/lib.rs b/fearless_simd/src/lib.rs index a987716c..a3b5126e 100644 --- a/fearless_simd/src/lib.rs +++ b/fearless_simd/src/lib.rs @@ -368,21 +368,18 @@ pub enum Level { /// Scalar fallback level, i.e. no supported SIMD features are to be used. /// /// This can be created with [`Level::fallback`]. - // Keep this predicate in sync with the fallback module, `is_fallback`, and `dispatch!`. + // Keep this predicate in sync with the fallback module and `dispatch!`. #[cfg(any( - all(target_arch = "aarch64", not(target_feature = "neon")), - all( - any(target_arch = "x86", target_arch = "x86_64"), - not(all(target_feature = "sse2", target_feature = "fxsr")) - ), - all(target_arch = "wasm32", not(target_feature = "simd128")), + feature = "force_support_fallback", not(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "aarch64", - target_arch = "wasm32" - )), - feature = "force_support_fallback" + all(target_arch = "aarch64", target_feature = "neon"), + all( + any(target_arch = "x86", target_arch = "x86_64"), + target_feature = "sse2", + target_feature = "fxsr" + ), + all(target_arch = "wasm32", target_feature = "simd128") + )) ))] Fallback(Fallback), /// The Neon instruction set on 64 bit ARM. @@ -472,26 +469,25 @@ impl Level { /// This method is always available, even when the fallback backend is not compiled. In that /// case, it always returns `false`. pub fn is_fallback(self) -> bool { - // Keep this predicate in sync with `Level::Fallback`, the fallback module, and `dispatch!`. + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + return self.as_sse2().is_none(); + + #[cfg(target_arch = "aarch64")] + return self.as_neon().is_none(); + + #[cfg(all(target_arch = "wasm32", target_feature = "simd128"))] + return self.as_wasm_simd128().is_none(); + #[cfg(any( - all(target_arch = "aarch64", not(target_feature = "neon")), - all( - any(target_arch = "x86", target_arch = "x86_64"), - not(all(target_feature = "sse2", target_feature = "fxsr")) - ), all(target_arch = "wasm32", not(target_feature = "simd128")), not(any( target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64", target_arch = "wasm32" - )), - feature = "force_support_fallback" + )) ))] - return matches!(self, Self::Fallback(_)); - - #[allow(unreachable_code, reason = "Fallback is unavailable in some cfgs.")] - false + return true; } /// If this is a proof that Neon (or better) is available, access that instruction set. @@ -869,40 +865,6 @@ impl Level { } } - // Keep this predicate in sync with `Level::Fallback`, the fallback module, and - // `dispatch!`. - #[cfg(any( - all(target_arch = "aarch64", not(target_feature = "neon")), - all( - any(target_arch = "x86", target_arch = "x86_64"), - not(all(target_feature = "sse2", target_feature = "fxsr")) - ), - all(target_arch = "wasm32", not(target_feature = "simd128")), - not(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "aarch64", - target_arch = "wasm32" - )), - feature = "force_support_fallback" - ))] - return Self::Fallback(Fallback::new()); - - #[cfg(not(any( - all(target_arch = "aarch64", not(target_feature = "neon")), - all( - any(target_arch = "x86", target_arch = "x86_64"), - not(all(target_feature = "sse2", target_feature = "fxsr")) - ), - all(target_arch = "wasm32", not(target_feature = "simd128")), - not(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "aarch64", - target_arch = "wasm32" - )), - feature = "force_support_fallback" - )))] Self::baseline() } @@ -931,24 +893,19 @@ mod tests { assert_is_send_sync::(); } - #[cfg(not(any( - all(target_arch = "aarch64", not(target_feature = "neon")), - all( - any(target_arch = "x86", target_arch = "x86_64"), - not(all(target_feature = "sse2", target_feature = "fxsr")) - ), - all(target_arch = "wasm32", not(target_feature = "simd128")), - not(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "aarch64", - target_arch = "wasm32" - )), - feature = "force_support_fallback" - )))] #[test] - fn is_fallback_is_false_when_fallback_is_unavailable() { - assert!(!Level::baseline().is_fallback()); + fn baseline_reports_whether_fallback_is_required() { + let has_simd_baseline = cfg!(any( + all(target_arch = "aarch64", target_feature = "neon"), + all( + any(target_arch = "x86", target_arch = "x86_64"), + target_feature = "sse2", + target_feature = "fxsr" + ), + all(target_arch = "wasm32", target_feature = "simd128") + )); + + assert_eq!(Level::baseline().is_fallback(), !has_simd_baseline); } #[cfg(all( diff --git a/fearless_simd/src/macros.rs b/fearless_simd/src/macros.rs index 5f74d696..7671e123 100644 --- a/fearless_simd/src/macros.rs +++ b/fearless_simd/src/macros.rs @@ -77,23 +77,20 @@ macro_rules! dispatch { $crate::Level::Avx512(avx512) => { $crate::__fearless_simd_dispatch_dispatch_avx512!(avx512, $simd => $op) } - // Keep this predicate in sync with `Level::Fallback`, the fallback module, and - // `Level::is_fallback`. The literal carries fearless_simd's feature selection into - // expansion in a downstream crate, where `cfg(feature = ...)` would be incorrect. + // Keep this predicate in sync with `Level::Fallback` and the fallback module. The + // literal carries fearless_simd's feature selection into expansion in a downstream + // crate, where `cfg(feature = ...)` would be incorrect. #[cfg(any( - all(target_arch = "aarch64", not(target_feature = "neon")), - all( - any(target_arch = "x86", target_arch = "x86_64"), - not(all(target_feature = "sse2", target_feature = "fxsr")) - ), - all(target_arch = "wasm32", not(target_feature = "simd128")), + $forced_fallback_arm, not(any( - target_arch = "x86", - target_arch = "x86_64", - target_arch = "aarch64", - target_arch = "wasm32" - )), - $forced_fallback_arm + all(target_arch = "aarch64", target_feature = "neon"), + all( + any(target_arch = "x86", target_arch = "x86_64"), + target_feature = "sse2", + target_feature = "fxsr" + ), + all(target_arch = "wasm32", target_feature = "simd128") + )) ))] $crate::Level::Fallback(fallback) => { $crate::__fearless_simd_dispatch_with_token!(fallback, $simd => $op) From ddc1bb906e852c407252e4ed6ceba1c6857d2f1e Mon Sep 17 00:00:00 2001 From: "Sergey \"Shnatsel\" Davidoff" Date: Fri, 7 Aug 2026 11:00:52 +0100 Subject: [PATCH 3/5] make the toml formatter happy --- fearless_simd_tests/Cargo.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fearless_simd_tests/Cargo.toml b/fearless_simd_tests/Cargo.toml index 1a3ca906..0d00bf94 100644 --- a/fearless_simd_tests/Cargo.toml +++ b/fearless_simd_tests/Cargo.toml @@ -21,5 +21,8 @@ workspace = true [dependencies] fastrand = "2.5.0" -fearless_simd = { workspace = true, features = ["std", "force_support_fallback"] } +fearless_simd = { workspace = true, features = [ + "std", + "force_support_fallback", +] } fearless_simd_dev_macros = { workspace = true } From 6754a45dcc5ba57fe458bbfffa5880352cb008b8 Mon Sep 17 00:00:00 2001 From: "Sergey \"Shnatsel\" Davidoff" Date: Fri, 7 Aug 2026 11:01:05 +0100 Subject: [PATCH 4/5] Clean up clippy lint configuration for generated code --- fearless_simd/src/generated.rs | 35 +++------------------------------- 1 file changed, 3 insertions(+), 32 deletions(-) diff --git a/fearless_simd/src/generated.rs b/fearless_simd/src/generated.rs index a8b38df6..b80f7a6b 100644 --- a/fearless_simd/src/generated.rs +++ b/fearless_simd/src/generated.rs @@ -3,7 +3,6 @@ #![expect( missing_docs, - clippy::unnecessary_cast, clippy::cast_possible_truncation, clippy::unseparated_literal_suffix, clippy::use_self, @@ -12,37 +11,9 @@ )] #![allow( trivial_numeric_casts, - reason = "Not every conditionally compiled backend contains a trivial cast" -)] -#![cfg_attr( - all( - target_arch = "x86_64", - any( - not(all(target_feature = "sse2", target_feature = "fxsr")), - feature = "force_support_fallback" - ) - ), - expect( - clippy::new_without_default, - reason = "TODO: https://github.com/linebender/fearless_simd/issues/40" - ) -)] -#![cfg_attr( - target_arch = "wasm32", - expect( - clippy::new_without_default, - reason = "TODO: https://github.com/linebender/fearless_simd/issues/40" - ) -)] -#![cfg_attr( - all( - feature = "std", - all(not(target_arch = "x86_64"), not(target_arch = "wasm32")) - ), - expect( - clippy::new_without_default, - reason = "TODO: https://github.com/linebender/fearless_simd/issues/40" - ) + clippy::unnecessary_cast, + clippy::new_without_default, + reason = "Simplifies the generator and has no effect on the machine code, only tripped by some backends" )] //! A module containing generated files From 372b8f7e0a5575babf41b25c82be2aaa06aac62c Mon Sep 17 00:00:00 2001 From: "Sergey \"Shnatsel\" Davidoff" Date: Sat, 8 Aug 2026 00:08:53 +0100 Subject: [PATCH 5/5] Document that Level::Fallback is not always available --- fearless_simd/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fearless_simd/src/lib.rs b/fearless_simd/src/lib.rs index a3b5126e..2fc71414 100644 --- a/fearless_simd/src/lib.rs +++ b/fearless_simd/src/lib.rs @@ -367,6 +367,11 @@ fn detect_x86_level() -> Level { pub enum Level { /// Scalar fallback level, i.e. no supported SIMD features are to be used. /// + /// This variant is **absent** on targets that supports a higher baseline + /// (`aarch64-*`, `i686-*`, `x86_64-*`, WASM with SIMD) unless the `force_support_fallback` + /// Cargo feature is enabled. Instead of matching on this variant, + /// call [`is_fallback`](Level::is_fallback) which is always available. + /// /// This can be created with [`Level::fallback`]. // Keep this predicate in sync with the fallback module and `dispatch!`. #[cfg(any(