Skip to main content

simd-tokens

Guides safe runtime CPU-feature dispatch in Rust with simd_tokens proof tokens and #[simd_entry] instead of is_x86_feature_detected! or is_aarch64_feature_detected! checks followed by unsafe calls. Use when writing or refactoring code that uses simd_tokens or #[target_feature] kernels, adding AVX2, AVX-512 or NEON dispatch with a scalar fallback, building SIMD backend traits or dispatch tables, or removing unsafe from SIMD code (e.g. to reach #![forbid(unsafe_code)]). Don't use for tuning kernel performance, choosing intrinsics, or benchmarking (use simd-optimizer).

Quellinformationen

Repository
google/simd_tokens
Letzte Quellaktivität
16. Oktober 2026 um 12:37
Erkannte Sprache von SKILL.md
Englisch
Sterne
1
Forks
0

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
38 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
simd-tokens
description
Guides safe runtime CPU-feature dispatch in Rust with simd_tokens proof tokens and #[simd_entry] instead of is_x86_feature_detected! or is_aarch64_feature_detected! checks followed by unsafe calls. Use when writing or refactoring code that uses simd_tokens or #[target_feature] kernels, adding AVX2, AVX-512 or NEON dispatch with a scalar fallback, building SIMD backend traits or dispatch tables, or removing unsafe from SIMD code (e.g. to reach #![forbid(unsafe_code)]). Don't use for tuning kernel performance, choosing intrinsics, or benchmarking (use simd-optimizer).
# Using `simd_tokens` Effectively `simd_tokens` provides zero-sized proof types (tokens) that prove CPU features are available at runtime, enabling safe dispatch of `#[target_feature]` functions without `unsafe`. Dependencies: `//security/ise_memory_safety/rust_simd/simd_tokens`, plus `//third_party/rust/safe_unaligned_simd/v0_2:safe_unaligned_simd` for safe loads and stores. ## Core Pattern 1. **Annotate kernel entrypoints with `#[simd_entry]`** and add a `token: impl SimdToken` parameter: ```rust use simd_tokens::{simd_entry, SimdToken}; #[simd_entry] #[target_feature(enable = "avx2")] pub fn transform(data: &mut [f32], token: impl SimdToken) { // ... SIMD intrinsics ... } ``` - Declare the function as safe `fn`, **not** `unsafe fn`: `#[simd_entry]` rejects an `unsafe fn` unless you write `#[simd_entry(still_unsafe)]`, which is only for functions with further safety preconditions (callers then still need `unsafe`). - By convention, place the token argument (`token: impl SimdToken`) as the last argument. - `#[simd_entry]` generates a safe wrapper whose token bound requires the matching feature trait (`SimdToken + Avx2`). 2. **Dispatch by checking tokens in descending capability order**: ```rust use simd_tokens::{Avx2Token, Sse2Token}; if let Some(token) = Avx2Token::try_new() { transform(data, token); // 100% safe — NO unsafe block! } else if let Some(token) = Sse2Token::try_new() { transform_sse2(data, token); } else { transform_scalar(data); } ``` 3. **Keep kernel bodies free of `unsafe`**: inside a `#[target_feature]` function, value-based intrinsics (`_mm256_add_ps`, `vaddq_f32`, ...) are safe to call. Only pointer-based loads and stores still need `unsafe`, so use the reference-based versions from `safe_unaligned_simd` and split slices with `as_chunks::<N>()` / `as_chunks_mut::<N>()`: ```rust use safe_unaligned_simd::x86_64::{_mm256_loadu_ps, _mm256_storeu_ps}; use simd_tokens::{simd_entry, SimdToken}; use std::arch::x86_64::{_mm256_mul_ps, _mm256_set1_ps}; #[simd_entry] #[target_feature(enable = "avx2")] pub fn scale(data: &mut [f32], factor: f32, token: impl SimdToken) { let factor_v = _mm256_set1_ps(factor); let (chunks, rest) = data.as_chunks_mut::<8>(); for chunk in chunks { let v = _mm256_mul_ps(_mm256_loadu_ps(chunk), factor_v); _mm256_storeu_ps(chunk, v); } for x in rest { *x *= factor; } } ``` With `#[simd_entry]` on the entry points and safe loads/stores in the bodies, a SIMD crate usually needs no `unsafe` at all: add `#![forbid(unsafe_code)]` so the compiler enforces it. 4. **Composing standalone entrypoints with `expose_raw_variant`**: If a kernel is **both** an external entrypoint (called with a token) and invoked internally by another kernel, use `#[simd_entry(expose_raw_variant)]`. This exposes a token-free `<fn>_raw` helper safe to call from matching `#[target_feature]` functions. (If a helper is *never* called with a token, do not use `#[simd_entry]` at all; see Antipatterns 3 and 4). -------------------------------------------------------------------------------- ## Antipatterns to Avoid ### 1. Check-then-Unsafe (Not using `#[simd_entry]`) ❌ **Antipattern**: Checking token presence (or storing a token in a struct) but leaving kernels as bare `#[target_feature]` functions called via `unsafe`: ```rust // ❌ WRONG: token checked, but call still requires unsafe! if let Some(_token) = Avx2Token::try_new() { unsafe { // SAFETY: token proved AVX2 exists process_avx2(data); } } ``` ✅ **Fix**: Add `token: impl SimdToken` and `#[simd_entry]` to the kernel. Pass the token to eliminate the `unsafe` block completely: ```rust // ✅ CORRECT: #[simd_entry] makes the call completely safe #[simd_entry] #[target_feature(enable = "avx2")] fn process_avx2(data: &mut [f32], token: impl SimdToken) { ... } if let Some(token) = Avx2Token::try_new() { process_avx2(data, token); // Safe! } ``` If using a backend struct or trait implementation, pass the token directly to the `#[simd_entry]` function (`*self` when the trait is implemented on the token itself, `self.0` for a wrapper like `struct Avx2Backend(Avx2Token)`) — never wrap the call in `unsafe`. Prefer implementing the trait on the token; the wrapper struct is unnecessary (see Antipattern 2). -------------------------------------------------------------------------------- ### 2. Manual Token Caching & Dispatch Tables ❌ **Antipattern**: Storing tokens or SIMD wrappers in `OnceLock`, `lazy_static`, or static variables to avoid re-checking CPU features: ```rust // ❌ WRONG: redundant caching overhead static AVX2_BACKEND: OnceLock<Avx2Backend> = OnceLock::new(); static AVX2_TOKEN: OnceLock<Option<Avx2Token>> = OnceLock::new(); ``` ✅ **Fix**: Call `<Token>::try_new()` directly at dispatch sites. - `try_new()` **already caches** the result internally using an atomic (`AtomicU8`). The first call detects features; subsequent calls are a single relaxed atomic load. - Tokens are **zero-sized types (ZST)** (`size_of::<Avx2Token>() == 0`) and implement `Copy`. Passing or creating them has zero heap and zero runtime memory cost. - Redundant `OnceLock` wrappers add synchronization overhead and boilerplate for no benefit. **Dispatch Tables & `&'static dyn Trait`**: For dynamic dispatch tables, implement the trait directly on the tokens and use `.as_static()`. This works for your crate's own trait even though the token types are defined in `simd_tokens` (the orphan rule allows implementing a local trait for a foreign type), so no wrapper struct and no `OnceLock` are needed: ```rust pub trait Backend { fn process(&self, data: &mut [f32]); } impl Backend for Avx2Token { #[inline(always)] fn process(&self, data: &mut [f32]) { process_avx2(data, *self); } } impl Backend for ScalarToken { #[inline(always)] fn process(&self, data: &mut [f32]) { process_scalar(data); } } pub fn get_backend() -> &'static dyn Backend { if let Some(token) = Avx2Token::try_new() { return token.as_static(); // &'static dyn Backend — zero alloc, zero locks! } ScalarToken.as_static() } ``` `token.as_static()` returns `&'static Self` directly. Because tokens are ZSTs and valid for the lifetime of the process, this requires no `OnceLock`, no `Box::leak`, no heap allocation, and no synchronization. -------------------------------------------------------------------------------- ### 3. Misusing `expose_raw_variant` on Pure Helpers ❌ **Antipattern**: Putting `#[simd_entry(expose_raw_variant)]` on internal helper functions that are never called with a token from the outside: ```rust // ❌ WRONG: helper is never called directly with a token! #[simd_entry(expose_raw_variant)] #[target_feature(enable = "avx2")] fn internal_butterfly(data: &mut [f32], _token: impl SimdToken) { ... } #[simd_entry] #[target_feature(enable = "avx2")] pub fn transform(data: &mut [f32], token: impl SimdToken) { internal_butterfly_raw(data); } ``` ✅ **Fix**: If a helper is never invoked directly with a token, it does **not** need `#[simd_entry]`. Keep it as a plain, safe `#[target_feature]` function: ```rust // ✅ CORRECT: internal helper is just a standard target_feature function #[inline] #[target_feature(enable = "avx2")] fn internal_butterfly(data: &mut [f32]) { ... } #[simd_entry] #[target_feature(enable = "avx2")] pub fn transform(data: &mut [f32], token: impl SimdToken) { internal_butterfly(data); // safe: the caller enables the same features } ``` Calling a safe `#[target_feature]` function needs no `unsafe` from a function that enables the same (or more) features, which a `#[simd_entry]` body does. Do not declare such helpers `unsafe fn` or wrap their calls in `unsafe`. Only use `expose_raw_variant` when a function is **both** an external token-entrypoint and called internally by another kernel. -------------------------------------------------------------------------------- ### 4. Non-`#[simd_entry]` Leaf Helpers: `#[inline(always)]` vs. `#[target_feature]` Choose inlining and feature annotations on non-`#[simd_entry]` leaf helpers based on whether the helper calls SIMD intrinsics: - **Pure-Rust leaf helpers (no intrinsics)**: For slice/chunk extraction, scalar math, or autovectorizable loops over `[T; N]`, use `#[inline(always)]` with **no** `#[target_feature]` and **no** `#[simd_entry]`. Because baseline features are a subset of every caller's features, the helper is safe to call from all contexts (`scalar`, `avx2`, `avx512`, `neon`), and LLVM inlines a copy into each caller to compile and vectorize under that caller's target features: ```rust // ✅ CORRECT: no intrinsics -> #[inline(always)], reusable everywhere #[inline(always)] fn mix_block(a: &[f32; 8], b: &[f32; 8]) -> [f32; 8] { std::array::from_fn(|i| a[i] + b[i]) } ``` - **Intrinsic-using leaf helpers (`core::arch` / `safe_unaligned_simd`)**: If a leaf calls *any* SIMD intrinsic — even baseline ones like `sse2` on `x86_64` or `neon` on `aarch64` — annotate it with **`#[inline]` + `#[target_feature(enable = "...")]`** (never `#[inline(always)]`): - ❌ Do **not** omit `#[target_feature]` and rely on `#[inline(always)]`: `rustc` checks `#[target_feature]` safety per-function before inlining, so calling any intrinsic without a matching `#[target_feature]` fails with `E0133` (even when the feature is enabled in the build config). Wrapping calls in `unsafe` inside a featureless safe `fn` is unsound and violates `#![forbid(unsafe_code)]`. - ❌ Do **not** combine `#[inline(always)]` with `#[target_feature]`: `rustc` currently rejects this combination as a hard error.
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen