fearless_simd v0.5 gives Rust a safe SIMD API that supports a + b

The post starts from a familiar complaint: raw SIMD intrinsics are unpleasant to use. Adding two vectors portably means a pile of cfg attributes for AVX2, SSE and NEON, all wrapped in unsafe, and it still ignores AVX-512, 32-bit ARM, WebAssembly, platforms without SIMD, loading a &[f32] into the right form, and picking the best implementation for the running CPU. Rust has SIMD abstractions that handle all of that and let you write a + b, but the author says they were full of hidden unsafe code, 'vast quantities of it', that was occasionally gotten wrong.

The post explains why unsafe was needed at all. It is illegal to call an intrinsic such as _mm256_add_ps on a CPU that lacks the instruction, so every call used to need unsafe plus some error-prone way of tracking required and available instruction sets. In Rust 1.87 the compiler started tracking the required instruction sets itself, so an intrinsic can be called without unsafe inside a function annotated with the correct #[target_feature]. That only moves the unsafe up one layer, because calling the annotated function still needs it. And a + b cannot use the mechanism directly: #[target_feature] cannot go on the implementation of +, because + must be available always.

The fix has three parts. First, a CPU feature token: a zero-sized type with a private inner field, obtainable only from a function that runs the runtime feature check (for example is_x86_feature_detected!("avx2")). Holding an Avx2 token proves AVX2 is available, and because the type is zero-sized, passing it around costs nothing at runtime. Second, a macro that is safe to invoke. It takes a function that accepts the token and expands it into a wrapper containing a single unsafe call to an inner #[target_feature] function. The compiler then rejects any intrinsic outside AVX2 inside that function. The author compares the result to println!, which safely encapsulates unsafe code, and says only this one macro needs review and audit, not hundreds of bespoke unsafe blocks. The only things that could go wrong in it are mapping the token to the wrong #[target_feature] and allowing an unsafe fn to be called from a safe context, and both are easy to check.

Third, generics. A type such as f32x8 is made generic over a Level (Avx2, NoSimd and so on) and carries the token inside it. The Add implementation for each level calls the safe wrapper or, for NoSimd, a plain scalar loop. That solves safety and runtime instruction selection together; add a convenience function that returns the best Level available and a + b just works.

There is one catch, which the author calls the ABI problem. A function call costs a handful of instructions, far more than the single add instruction, and the compiler cannot inline a #[target_feature] function into a caller that lacks the feature. The workaround is to call a + b from a function that itself has #[target_feature] or #[inline(always)], so the implementation is copied into a target-feature context. Skip that and the code still compiles but performance plummets; the author invites readers to remove #[target_feature] from an example and look at the generated assembly. The author is unsure what can be done and thinks the limitation looks fundamental for any approach that implements a + b with SIMD. The Struct Target Features RFC solves it for add_avx2(token, a, b) and add::<S: Simd>(token, a, b), but the author sees no path to a + b yet.

The production version of these ideas is fearless_simd v0.5. Its macro is exposed publicly so users can mix high-level operations like a + b with platform-specific intrinsics. It contains more than one unsafe block because it also provides the functionality of the safe_unaligned_simd crate, though the author says that too is done with significantly less unsafe code than the original. In the acknowledgements the author says they are shocked to be the first to put this into production, as far as they can tell, and credits the pulp crate, which has used CPU feature tokens for years but relied on handwritten unsafe wrappers around intrinsics that occasionally got them wrong.

Key facts

  • Since Rust 1.87 the compiler tracks required instruction sets, so intrinsics can be called without unsafe inside functions with the right #[target_feature]; unsafe still has to sit somewhere in the chain.
  • fearless_simd v0.5 uses an unforgeable zero-sized CPU feature token, one safe-to-invoke macro with a single unsafe block, and generics over instruction-set levels to make a + b work on SIMD types.
  • The author says only the one macro needs auditing, and that its only failure modes are mapping the token to the wrong #[target_feature] and letting an unsafe fn be called from a safe context.
  • a + b must be called from a function with #[inline(always)] or #[target_feature], otherwise the code compiles but performance plummets.
  • fearless_simd still has more than one unsafe block because it also provides the functionality of safe_unaligned_simd.

Why it matters

Rust developers who wanted readable SIMD code had to choose between raw intrinsics wrapped in unsafe and abstraction crates that hid large amounts of unsafe underneath. The post argues that the second option only concealed the problem, and that mistakes did slip in. By combining the compiler's target-feature tracking from Rust 1.87 with a token type and one macro, fearless_simd reduces the audited surface to a single macro. The author describes the result as a never-before-seen low level of unsafe code for a SIMD abstraction.

Who it affects

Rust programmers writing performance-sensitive numeric or data-processing code who use or consider SIMD, and maintainers of SIMD abstraction crates who have to justify and review unsafe code. The author names the sheer amount of unsafe as the barrier that kept them away from high-level SIMD abstractions.

How to use it

fearless_simd v0.5 is available from the package registry, and the post links a small example of how the pieces fit together in production. The macro behind it is public, so you can mix high-level operations like a + b with platform-specific intrinsics. In your own code, call a + b from a function annotated with #[target_feature] or #[inline(always)] so the operation is inlined into a context where the instructions are known to be available. A convenience function returns the best Level for the running CPU, and a NoSimd level covers machines without SIMD.

How solid is it

This is a single author's explanatory blog post, and it rests on the author's own reasoning and code examples. It names a released crate version, fearless_simd v0.5, as the production implementation. The post gives no benchmark numbers or measured performance figures for fearless_simd. The author says that, as far as they can tell, nobody else has put this approach into production, and that the pulp crate used CPU feature tokens for years with handwritten unsafe wrappers.

Risks and caveats

The inlining limitation is the main one: forget #[inline(always)] or #[target_feature] on the calling function and a + b compiles fine but runs far slower, with no error to warn you. The author calls the limitation quite fundamental for any approach that implements a + b with SIMD and says the Struct Target Features RFC does not yet offer a path to a + b. The safety argument also depends on the macro being correct, though the author says its two possible failure modes are easy to check. And fearless_simd is not free of unsafe: it contains more than one unsafe block because it also provides the functionality of safe_unaligned_simd.

“But now SIMD in Rust can be truly fearless.”

— Author of the fearless_simd blog post