Skip to main content

archmage_macros/
lib.rs

1//! Proc-macros for archmage SIMD capability tokens.
2//!
3//! [Official guide and examples](https://imazen.github.io/archmage/) ·
4//! [Intrinsics browser](https://imazen.github.io/archmage/intrinsics/) ·
5//! [Archmage API](https://docs.rs/archmage/latest/archmage/) ·
6//! [Magetypes vector API](https://docs.rs/magetypes/latest/magetypes/)
7//!
8//! Applications should import these macros through `archmage`, which pins the
9//! compatible macro release. `#[magetypes]` generates functions; the separate
10//! magetypes crate supplies their vector types. See the
11//! [complete generic call chains](https://imazen.github.io/archmage/magetypes/dispatch/types-and-dispatch/).
12//!
13//! Provides `#[arcane]`, `#[rite]`, `#[autoversion]`, `incant!`, and `#[magetypes]`.
14
15#[cfg(test)]
16mod expansion_tests;
17#[cfg(test)]
18mod raw_snapshots;
19
20mod arcane;
21mod attune;
22mod autoversion;
23mod common;
24mod engine;
25mod generated;
26mod incant;
27mod magetypes;
28mod rewrite;
29mod rite;
30mod tiers;
31mod token_discovery;
32
33use proc_macro::TokenStream;
34use syn::parse_macro_input;
35
36use arcane::*;
37use autoversion::*;
38use common::*;
39use incant::*;
40use magetypes::*;
41use rite::*;
42use tiers::*;
43
44// Re-export items used by the test module (via `use super::*`).
45#[cfg(test)]
46use generated::{token_to_features, trait_to_features};
47#[cfg(test)]
48use quote::ToTokens;
49#[cfg(test)]
50use syn::{FnArg, PatType};
51#[cfg(test)]
52use token_discovery::*;
53
54// LightFn, filter_inline_attrs, is_lint_attr, filter_lint_attrs, gen_cfg_guard,
55// build_turbofish, replace_self_in_tokens, suffix_path → moved to common.rs
56// ArcaneArgs, arcane_impl, arcane_impl_* → moved to arcane.rs
57// generate_imports → moved to common.rs
58
59#[doc = include_str!("docs/arcane.md")]
60#[proc_macro_attribute]
61pub fn arcane(attr: TokenStream, item: TokenStream) -> TokenStream {
62    let args = parse_macro_input!(attr as ArcaneArgs);
63    let input_fn = parse_macro_input!(item as LightFn);
64    arcane_impl(input_fn, "arcane", args).into()
65}
66
67/// Legacy alias for [`arcane`].
68///
69/// **Deprecated:** Use `#[arcane]` instead. This alias exists only for migration.
70#[proc_macro_attribute]
71#[doc(hidden)]
72pub fn simd_fn(attr: TokenStream, item: TokenStream) -> TokenStream {
73    let args = parse_macro_input!(attr as ArcaneArgs);
74    let input_fn = parse_macro_input!(item as LightFn);
75    arcane_impl(input_fn, "simd_fn", args).into()
76}
77
78/// Descriptive alias for [`arcane`].
79///
80/// Generates a safe wrapper around a `#[target_feature]` inner function.
81/// The token type in your signature determines which CPU features are enabled.
82/// Creates an LLVM optimization boundary — use [`token_target_features`]
83/// (alias for [`rite`]) for inner helpers to avoid this.
84///
85/// Since Rust 1.87, value-based SIMD intrinsics are safe inside
86/// `#[target_feature]` functions. This macro generates the `#[target_feature]`
87/// wrapper so you never need to write `unsafe` for SIMD code.
88///
89/// See [`arcane`] for full documentation and examples.
90#[proc_macro_attribute]
91pub fn token_target_features_boundary(attr: TokenStream, item: TokenStream) -> TokenStream {
92    let args = parse_macro_input!(attr as ArcaneArgs);
93    let input_fn = parse_macro_input!(item as LightFn);
94    arcane_impl(input_fn, "token_target_features_boundary", args).into()
95}
96
97// ============================================================================
98// Rite macro for inner SIMD functions (inlines into matching #[target_feature] callers)
99// ============================================================================
100
101/// Annotate inner SIMD helpers called from `#[arcane]` functions.
102///
103/// Unlike `#[arcane]`, which creates an inner `#[target_feature]` function behind
104/// a safe boundary, `#[rite]` adds `#[target_feature]` and `#[inline]` directly.
105/// LLVM inlines it into any caller with matching features — no boundary crossing.
106///
107/// # Three Modes
108///
109/// **Token-based:** Reads the token type from the function signature.
110/// ```ignore
111/// #[rite]
112/// fn helper(_: X64V3Token, v: __m256) -> __m256 { _mm256_add_ps(v, v) }
113/// ```
114///
115/// **Tier-based:** Specify the tier name directly, no token parameter needed.
116/// ```ignore
117/// #[rite(v3)]
118/// fn helper(v: __m256) -> __m256 { _mm256_add_ps(v, v) }
119/// ```
120///
121/// Both produce identical code. The token form can be easier to remember if
122/// you already have the token in scope.
123///
124/// **Multi-tier:** Specify multiple tiers to generate suffixed variants.
125/// ```ignore
126/// #[rite(v3, v4)]
127/// fn process(data: &[f32; 4]) -> f32 { data.iter().sum() }
128/// // Generates: process_v3() and process_v4()
129/// ```
130///
131/// Each variant gets its own `#[target_feature]` and `#[cfg(target_arch)]`.
132/// Since Rust 1.86, calling these from a matching `#[arcane]` or `#[rite]`
133/// context is safe — no `unsafe` needed when the caller has matching or
134/// superset features.
135///
136/// # Safety
137///
138/// `#[rite]` functions can only be safely called from contexts where the
139/// required CPU features are enabled:
140/// - From within `#[arcane]` functions with matching/superset tokens
141/// - From within other `#[rite]` functions with matching/superset tokens
142///
143/// A globally enabled feature (`-Ctarget-cpu`, `-Ctarget-feature`) does not
144/// make the call safe: rustc requires the caller's own `#[target_feature]`
145/// to cover the callee's, whatever the build enables (E0133 otherwise).
146/// Calling from other contexts requires `unsafe` and the caller must ensure
147/// the CPU supports the required features.
148///
149/// # Cross-Architecture Behavior
150///
151/// Like `#[arcane]`, defaults to cfg-out (no function on wrong arch).
152/// `stub` has been removed; use `incant!` or explicit call-site cfg guards.
153///
154/// # Options
155///
156/// | Option | Effect |
157/// |--------|--------|
158/// | tier name(s) | `v3`, `neon`, etc. One = single function; multiple = suffixed variants |
159/// | `import_intrinsics` | Auto-import `archmage::intrinsics::{arch}::*` (includes safe memory ops) |
160/// | `import_magetypes` | Auto-import `magetypes::simd::{ns}::*` and `magetypes::simd::backends::*` |
161///
162/// See `#[arcane]` docs for the full namespace mapping table.
163///
164/// `#[rite]` has no trait-method mode: it applies `#[target_feature]` directly,
165/// which rustc rejects on a safe trait method on x86-64 and AArch64 (wasm32's
166/// `simd128` functions are safe to call anywhere, so there it compiles).
167/// `#[rite(in_trait)]` says so and points at `#[arcane(in_trait, _self = Type)]`;
168/// a plain `#[rite]` on such a method leaves rustc's error on it. Inside an
169/// inherent impl no flag is needed, so `in_impl` is rejected too.
170///
171/// # Comparison with #[arcane]
172///
173/// | Aspect | `#[arcane]` | `#[rite]` |
174/// |--------|-------------|-----------|
175/// | Creates wrapper | Yes | No |
176/// | Entry point | Yes | No |
177/// | Inlines into caller | When feature context permits | When feature context permits |
178/// | Safe to call anywhere | Yes (with token) | Only from feature-enabled context |
179/// | Multi-tier variants | No | Yes (`#[rite(v3, v4, neon)]`) |
180/// | `import_intrinsics` | Yes | Yes |
181/// | `import_magetypes` | Yes | Yes |
182#[proc_macro_attribute]
183pub fn rite(attr: TokenStream, item: TokenStream) -> TokenStream {
184    let args = parse_macro_input!(attr as RiteArgs);
185    let input_fn = parse_macro_input!(item as LightFn);
186    rite_impl(input_fn, args).into()
187}
188
189/// Descriptive alias for [`rite`].
190///
191/// Applies `#[target_feature]` + `#[inline]` based on the token type in your
192/// function signature. No wrapper, no optimization boundary. Use for functions
193/// called from within `#[arcane]`/`#[token_target_features_boundary]` code.
194///
195/// Since Rust 1.86, calling a `#[target_feature]` function from another function
196/// with matching features is safe — no `unsafe` needed.
197///
198/// See [`rite`] for full documentation and examples.
199#[proc_macro_attribute]
200pub fn token_target_features(attr: TokenStream, item: TokenStream) -> TokenStream {
201    let args = parse_macro_input!(attr as RiteArgs);
202    let input_fn = parse_macro_input!(item as LightFn);
203    rite_impl(input_fn, args).into()
204}
205
206// RiteArgs, rite_impl, rite_single_impl, rite_multi_tier_impl → moved to rite.rs
207
208// =============================================================================
209// magetypes! macro - generate platform variants from generic function
210// =============================================================================
211
212/// Generate platform-specific variants from a function by replacing `Token`.
213///
214/// Use `Token` as a placeholder for the token type. The macro generates
215/// suffixed variants with `Token` replaced by the concrete token type, and
216/// each variant wrapped in the appropriate `#[cfg(target_arch = ...)]` guard.
217///
218/// # Default tiers
219///
220/// Without arguments, generates `_v3`, `_v4`, `_neon`, `_wasm128`, `_scalar`:
221///
222/// ```rust,ignore
223/// #[magetypes]
224/// fn process(token: Token, data: &[f32]) -> f32 {
225///     inner_simd_work(token, data)
226/// }
227/// ```
228///
229/// # Explicit tiers
230///
231/// Specify which tiers to generate:
232///
233/// ```rust,ignore
234/// #[magetypes(v1, v3, neon)]
235/// fn process(token: Token, data: &[f32]) -> f32 {
236///     inner_simd_work(token, data)
237/// }
238/// // Generates: process_v1, process_v3, process_neon, process_scalar
239/// ```
240///
241/// `scalar` is always included implicitly.
242///
243/// Known tiers: `v1`, `v2`, `v3`, `v4`, `v4x`, `neon`, `neon_aes`,
244/// `neon_sha3`, `neon_crc`, `wasm128`, `wasm128_relaxed`, `scalar`.
245///
246/// # What gets replaced
247///
248/// **Only `Token`** is replaced — with the concrete token type for each variant
249/// (e.g., `archmage::X64V3Token`, `archmage::ScalarToken`). SIMD types like
250/// `f32x8` and constants like `LANES` are **not** replaced by this macro.
251///
252/// # Options
253///
254/// | Option | Effect |
255/// |--------|--------|
256/// | `rite` | Variants use `#[rite(import_intrinsics)]` (no wrapper) instead of `#[arcane]` |
257/// | `define(f32x8, ...)` | Alias each named generic vector to its `Token` instantiation inside the body |
258/// | `in_impl` | Receiver-less associated function in an inherent impl: variants get `#[arcane(in_impl)]` |
259///
260/// # Usage with incant!
261///
262/// The generated variants work with `incant!` for dispatch:
263///
264/// ```rust,ignore
265/// pub fn process_api(data: &[f32]) -> f32 {
266///     incant!(process(data))
267/// }
268///
269/// // Or with matching explicit tiers:
270/// pub fn process_api(data: &[f32]) -> f32 {
271///     incant!(process(data), [v1, v3, neon, scalar])
272/// }
273/// ```
274#[proc_macro_attribute]
275pub fn magetypes(attr: TokenStream, item: TokenStream) -> TokenStream {
276    let input_fn = parse_macro_input!(item as LightFn);
277
278    // Parse attribute args: [rite,] [define(type, ...),] tier1, tier2(feature), ...
279    //
280    // Special keywords:
281    //   `rite`: flag that changes per-tier variants to use
282    //           `#[archmage::rite(import_intrinsics)]` (direct
283    //           `#[target_feature]` + `#[inline]`) instead of
284    //           `#[archmage::arcane]` (safe wrapper + inner trampoline).
285    //
286    //   `define(name1, name2, ...)`: list of magetypes type names to inject
287    //           as local type aliases at the top of each variant body
288    //           (e.g., `type f32x8 = ::magetypes::simd::generic::f32x8<Token>;`).
289    //           `Token` in the alias RHS is substituted per tier.
290    //
291    // Assumption: neither `rite` nor `define` is or will become a tier name.
292    // `token-registry.toml` must not declare `short_name = "rite"` or
293    // `short_name = "define"`.
294    let (rite_flag, in_impl, defines, tier_names) =
295        match syn::parse::Parser::parse(parse_magetypes_attr, attr) {
296            Ok(parsed) => parsed,
297            Err(e) => return e.to_compile_error().into(),
298        };
299
300    let tiers = if tier_names.is_empty() {
301        default_tiers(true)
302    } else {
303        match resolve_tiers(&tier_names, input_fn.sig.ident.span(), true) {
304            Ok(t) => t,
305            Err(e) => return e.to_compile_error().into(),
306        }
307    };
308
309    magetypes_impl(input_fn, &tiers, rite_flag, in_impl, &defines).into()
310}
311
312/// Parse `#[magetypes]` attributes: `rite` and `in_impl` flags, `define(list)`,
313/// and tier names.
314///
315/// Returns `(rite_flag, in_impl, defines, tier_names)`. Tier names preserve the
316/// `+`/`-` modifier prefixes and `(cfg(feat))` gates for the tier resolver.
317fn parse_magetypes_attr(
318    input: syn::parse::ParseStream<'_>,
319) -> syn::Result<(bool, bool, Vec<String>, Vec<String>)> {
320    use syn::Token;
321    let mut rite_flag = false;
322    let mut in_impl = false;
323    let mut defines = Vec::new();
324    let mut tier_names = Vec::new();
325
326    while !input.is_empty() {
327        // Peek the leading ident without consuming — `rite` and `define` are
328        // special, anything else is a tier name (possibly prefixed with +/-).
329        let peek_rite = input.peek(syn::Ident) && {
330            let fork = input.fork();
331            fork.parse::<syn::Ident>()
332                .is_ok_and(|i| i == "rite" && !fork.peek(syn::token::Paren))
333        };
334        let peek_define = input.peek(syn::Ident) && {
335            let fork = input.fork();
336            fork.parse::<syn::Ident>()
337                .is_ok_and(|i| i == "define" && fork.peek(syn::token::Paren))
338        };
339        let peek_in_impl = input.peek(syn::Ident) && {
340            let fork = input.fork();
341            fork.parse::<syn::Ident>().is_ok_and(|i| i == "in_impl")
342        };
343
344        if peek_rite {
345            input.parse::<syn::Ident>()?;
346            rite_flag = true;
347        } else if peek_in_impl {
348            // A receiver-less associated function in an inherent impl: the
349            // generated #[arcane] variants must call their siblings as `Self::`.
350            input.parse::<syn::Ident>()?;
351            in_impl = true;
352        } else if peek_define {
353            input.parse::<syn::Ident>()?;
354            let content;
355            syn::parenthesized!(content in input);
356            while !content.is_empty() {
357                let ty: syn::Ident = content.parse()?;
358                defines.push(ty.to_string());
359                if content.peek(Token![,]) {
360                    let _: Token![,] = content.parse()?;
361                }
362            }
363        } else {
364            // Fall through to tier-name parsing (preserves +/- prefix and cfg gates).
365            tier_names.push(parse_one_tier(input)?);
366        }
367
368        if input.peek(Token![,]) {
369            let _: Token![,] = input.parse()?;
370        }
371    }
372
373    Ok((rite_flag, in_impl, defines, tier_names))
374}
375
376// =============================================================================
377// incant! macro - dispatch to platform-specific variants
378// =============================================================================
379// incant! macro - dispatch to platform-specific variants
380// =============================================================================
381
382/// Dispatch to platform-specific SIMD variants.
383///
384/// # Entry Point Mode (no token yet)
385///
386/// Summons tokens and dispatches to the best available variant:
387///
388/// ```rust,ignore
389/// pub fn public_api(data: &[f32]) -> f32 {
390///     incant!(dot(Token, data))
391/// }
392/// ```
393///
394/// Expands to runtime feature detection + dispatch to `dot_v3`, `dot_v4`,
395/// `dot_neon`, `dot_wasm128`, or `dot_scalar`. The `Token` marker is
396/// replaced with the summoned token. Token can appear at any position
397/// to match the callee's signature:
398///
399/// ```rust,ignore
400/// incant!(process(Token, data), [v3, scalar])  // token-first
401/// incant!(process(data, Token), [v3, scalar])  // token-last
402/// ```
403///
404/// If `Token` is omitted, the token is prepended (backward compatible).
405///
406/// # Explicit Tiers
407///
408/// Specify which tiers to dispatch to:
409///
410/// ```rust,ignore
411/// pub fn api(data: &[f32]) -> f32 {
412///     incant!(process(Token, data), [v1, v3, neon, scalar])
413/// }
414/// ```
415///
416/// Always include `scalar` in explicit tier lists. Currently auto-appended
417/// if omitted; will become a compile error in v1.0. Tiers are automatically
418/// sorted by dispatch priority (highest first).
419///
420/// Known tiers: `v1`, `v2`, `v3`, `v4`, `v4x`, `neon`, `neon_aes`,
421/// `neon_sha3`, `neon_crc`, `wasm128`, `wasm128_relaxed`, `scalar`.
422///
423/// # Automatic Rewriting (inside tier macros)
424///
425/// When `incant!` appears inside an `#[arcane]`, `#[rite]`, or
426/// `#[autoversion]` function body, the outer macro **rewrites** it to
427/// a direct call at compile time — bypassing the runtime dispatcher:
428///
429/// ```rust,ignore
430/// #[arcane]
431/// fn outer(token: X64V3Token, data: &[f32]) -> f32 {
432///     // Rewritten to: inner_v3(token, data) — zero overhead
433///     incant!(inner(token, data), [v3, scalar])
434/// }
435/// ```
436///
437/// The rewriter recognizes the caller's token variable by name and
438/// handles downcasting (V4 caller → V3 callee), upgrade attempts
439/// (summon a higher tier), and feature-gated tiers automatically.
440///
441/// Use `Token` or the caller's token variable name in the args to
442/// control token position:
443///
444/// ```rust,ignore
445/// #[arcane]
446/// fn outer(my_token: X64V3Token, data: &[f32]) -> f32 {
447///     // my_token recognized, placed where it appears in args
448///     incant!(inner(data, my_token), [v3, scalar])
449/// }
450/// ```
451///
452/// # Passthrough Mode (generic token dispatch)
453///
454/// For functions generic over token types, use `with token` for
455/// compile-time dispatch via `IntoConcreteToken`:
456///
457/// ```rust,ignore
458/// fn dispatch<T: IntoConcreteToken>(token: T, data: &[f32]) -> f32 {
459///     incant!(process(data) with token, [v3, neon, scalar])
460/// }
461/// ```
462///
463/// The compiler monomorphizes the dispatch — when `T = X64V3Token`,
464/// only the V3 branch survives. No runtime summon, no overhead.
465///
466/// This is different from the rewriter: passthrough works on generic
467/// `IntoConcreteToken` bounds where the concrete tier isn't known at
468/// macro time. The rewriter works when the concrete tier IS known
469/// (inside `#[arcane]`/`#[rite]`/`#[autoversion]` bodies).
470///
471/// # Variant Naming
472///
473/// Functions must have suffixed variants matching the selected tiers:
474/// - `_v1` for `X64V1Token`
475/// - `_v2` for `X64V2Token`
476/// - `_v3` for `X64V3Token`
477/// - `_v4` for `X64V4Token` (requires `avx512` feature)
478/// - `_v4x` for `X64V4xToken` (requires `avx512` feature)
479/// - `_neon` for `NeonToken`
480/// - `_neon_aes` for `NeonAesToken`
481/// - `_neon_sha3` for `NeonSha3Token`
482/// - `_neon_crc` for `NeonCrcToken`
483/// - `_wasm128` for `Wasm128Token`
484/// - `_scalar` for `ScalarToken`
485#[proc_macro]
486pub fn incant(input: TokenStream) -> TokenStream {
487    if let Some(expanded) = expanded_passthrough(&input.clone().into()) {
488        return expanded.into();
489    }
490    let input = parse_macro_input!(input as IncantInput);
491    incant_impl(input).into()
492}
493
494/// Legacy alias for [`incant!`].
495#[proc_macro]
496pub fn simd_route(input: TokenStream) -> TokenStream {
497    if let Some(expanded) = expanded_passthrough(&input.clone().into()) {
498        return expanded.into();
499    }
500    let input = parse_macro_input!(input as IncantInput);
501    incant_impl(input).into()
502}
503
504/// Descriptive alias for [`incant!`].
505///
506/// Dispatches to architecture-specific function variants at runtime.
507/// Looks for suffixed functions (`_v3`, `_v4`, `_neon`, `_wasm128`, `_scalar`)
508/// and calls the best one the CPU supports.
509///
510/// See [`incant!`] for full documentation and examples.
511#[proc_macro]
512pub fn dispatch_variant(input: TokenStream) -> TokenStream {
513    if let Some(expanded) = expanded_passthrough(&input.clone().into()) {
514        return expanded.into();
515    }
516    let input = parse_macro_input!(input as IncantInput);
517    incant_impl(input).into()
518}
519
520// =============================================================================
521
522/// Let the compiler auto-vectorize scalar code for each architecture.
523///
524/// Write a plain scalar function and let `#[autoversion]` generate
525/// architecture-specific copies — each compiled with different
526/// `#[target_feature]` flags via `#[arcane]` — plus a runtime dispatcher
527/// that calls the best one the CPU supports.
528///
529/// # Quick start
530///
531/// ```rust,ignore
532/// use archmage::autoversion;
533///
534/// #[autoversion]
535/// fn sum_of_squares(data: &[f32]) -> f32 {
536///     let mut sum = 0.0f32;
537///     for &x in data {
538///         sum += x * x;
539///     }
540///     sum
541/// }
542///
543/// // Call directly — no token, no unsafe:
544/// let result = sum_of_squares(&my_data);
545/// ```
546///
547/// Each variant gets `#[arcane]` → `#[target_feature(enable = "avx2,fma,...")]`,
548/// which unlocks the compiler's auto-vectorizer for that feature set.
549/// On x86-64, that loop compiles to `vfmadd231ps`. On aarch64, `fmla`.
550/// The `_scalar` fallback compiles without SIMD target features.
551///
552/// # SimdToken — optional placeholder
553///
554/// You can optionally write `_token: SimdToken` as a parameter. The macro
555/// recognizes it and strips it from the dispatcher — both forms produce
556/// identical output. Prefer the tokenless form for new code.
557///
558/// ```rust,ignore
559/// #[autoversion]
560/// fn normalize(_token: SimdToken, data: &mut [f32], scale: f32) {
561///     for x in data.iter_mut() { *x = (*x - 128.0) * scale; }
562/// }
563/// // Dispatcher is: fn normalize(data: &mut [f32], scale: f32)
564/// ```
565///
566/// # What gets generated
567///
568/// `#[autoversion] fn process(data: &[f32]) -> f32` expands to:
569///
570/// - `process_v4(token: X64V4Token, ...)` — AVX-512
571/// - `process_v3(token: X64V3Token, ...)` — AVX2+FMA
572/// - `process_neon(token: NeonToken, ...)` — aarch64 NEON
573/// - `process_wasm128(token: Wasm128Token, ...)` — WASM SIMD
574/// - `process_scalar(token: ScalarToken, ...)` — no SIMD, always available
575/// - `process(data: &[f32]) -> f32` — **dispatcher**
576///
577/// Variants are private. The dispatcher gets the original function's visibility.
578/// Within the same module, call variants directly for testing or benchmarking.
579///
580/// # Explicit tiers
581///
582/// ```rust,ignore
583/// #[autoversion(v3, v4, neon, arm_v2, wasm128)]
584/// fn process(data: &[f32]) -> f32 { ... }
585/// ```
586///
587/// `scalar` is always included implicitly.
588///
589/// Default tiers: `v4`, `v3`, `neon`, `wasm128`, `scalar`.
590///
591/// Known tiers: `v1`, `v2`, `v3`, `v3_crypto`, `v3_gfni_crypto`, `v4`, `v4x`,
592/// `neon`, `neon_aes`, `neon_sha3`, `neon_crc`, `arm_v2`, `arm_v3`, `wasm128`,
593/// `wasm128_relaxed`, `x64_crypto`, `scalar`.
594///
595/// # Methods
596///
597/// For inherent methods, `self` works naturally:
598///
599/// ```rust,ignore
600/// impl ImageBuffer {
601///     #[autoversion]
602///     fn normalize(&mut self, gamma: f32) {
603///         for pixel in &mut self.data {
604///             *pixel = (*pixel / 255.0).powf(gamma);
605///         }
606///     }
607/// }
608/// buffer.normalize(2.2);
609/// ```
610///
611/// An associated function without a receiver in an inherent impl needs
612/// `in_impl`, so the dispatcher calls the variants as `Self::process_v3(...)`:
613///
614/// ```rust,ignore
615/// impl Table {
616///     #[autoversion(in_impl)]
617///     fn build(n: usize) -> Self { Self::with_capacity(n) }
618/// }
619/// ```
620///
621/// A trait impl cannot take the variants as extra items, so `in_trait` (alias
622/// `nested`) places them inside the dispatcher's body. A method with a receiver
623/// also needs `_self = Type`: each variant takes `_self: &Type` in place of
624/// the receiver, `Self` becomes the named type, and `self` in the body becomes
625/// `_self`:
626///
627/// ```rust,ignore
628/// impl Work for MyType {
629///     #[autoversion(v3, neon, scalar, in_trait, _self = MyType)]
630///     fn run(&self, data: &[f32]) -> f32 {
631///         self.weights.iter().zip(data).map(|(w, d)| w * d).sum()
632///     }
633/// }
634/// ```
635///
636/// `_self = Type` without `in_trait` keeps the variants beside the dispatcher,
637/// which only an inherent impl accepts; plain `self` already works there. The
638/// variants of an `in_trait` function are local to its body and cannot be
639/// called directly.
640///
641/// `#[autoversion]` cannot dispatch an `impl Trait` return type: every variant
642/// would return a distinct opaque type and the dispatcher can return only one.
643/// Return a concrete type or `Box<dyn Trait>`.
644///
645/// # Nesting with `incant!`
646///
647/// Hand-written SIMD for specific tiers, autoversion for the rest:
648///
649/// ```rust,ignore
650/// pub fn process(data: &[f32]) -> f32 {
651///     incant!(process(data), [v4, scalar])
652/// }
653///
654/// #[arcane(import_intrinsics)]
655/// fn process_v4(_t: X64V4Token, data: &[f32]) -> f32 { /* AVX-512 */ }
656///
657/// // Bridge: incant! passes ScalarToken, autoversion doesn't need one
658/// fn process_scalar(_: ScalarToken, data: &[f32]) -> f32 {
659///     process_auto(data)
660/// }
661///
662/// #[autoversion(v3, neon)]
663/// fn process_auto(data: &[f32]) -> f32 { data.iter().sum() }
664/// ```
665///
666/// # Comparison with `#[magetypes]` + `incant!`
667///
668/// | | `#[autoversion]` | `#[magetypes]` + `incant!` |
669/// |---|---|---|
670/// | Generates variants + dispatcher | Yes | Variants only (+ separate `incant!`) |
671/// | Body touched | No (signature only) | Yes (text substitution) |
672/// | Best for | Scalar auto-vectorization | Hand-written SIMD types |
673#[proc_macro_attribute]
674pub fn autoversion(attr: TokenStream, item: TokenStream) -> TokenStream {
675    let args = parse_macro_input!(attr as AutoversionArgs);
676    let input_fn = parse_macro_input!(item as LightFn);
677    autoversion_impl(input_fn, args).into()
678}
679
680#[cfg(test)]
681mod tests;
682
683/// Establish a feature context or generate explicit direct/proof/dispatch outputs.
684///
685/// `#[attune(v3)]` keeps the function name. `#[attune]` infers a registered
686/// terminal tier suffix: `_v3` selects a direct context; `_v3_t` selects a proof
687/// boundary, adding a concrete proof parameter when omitted. A written proof
688/// must match the suffix. `make(_v3, _v3_t, _)` requests a direct function,
689/// a proof-taking wrapper, and a central dispatcher with a private scalar fallback.
690/// `dispatch` is an alias for `_`; `make(dispatch, +v4(avx512))` adds a private
691/// V4 implementation without exposing additional entry points.
692///
693/// Selectors can also be written directly: `#[attune(_v3, _v3_t, dispatch)]`.
694/// Selector-local options are visibility, `cfg(feature)`, and `inline(policy)`:
695/// `_v4x_t(cfg(avx512), pub)`. A top-level `v3` preserves the written function
696/// name; `_v3` generates a suffixed output. Top-level inline policy applies to
697/// bodies; a proof/dispatcher selector's inline policy applies to its wrapper.
698///
699/// `inline(default)` selects body hints from the operation's visibility before
700/// lowering to private helpers: unrestricted `pub` gets `#[inline]`; restricted
701/// or private operations get no attribute. Direct output visibility overrides
702/// participate; wrapper visibility overrides apply independently.
703/// `inline(none)`, `inline(hint)`, and `inline(never)` select explicit body policies.
704/// Per-output `make(inline(...) _v3)` overrides the definition-level policy.
705/// Definition-level policy does not alter proof-wrapper or dispatcher defaults.
706#[proc_macro_attribute]
707pub fn attune(attr: TokenStream, item: TokenStream) -> TokenStream {
708    attune::expand(attr.into(), item.into())
709        .unwrap_or_else(|error| error.to_compile_error())
710        .into()
711}
712
713/// Invoke a family using the enclosing attune feature context, or runtime proof
714/// entries outside one. Explicit tier lists never acquire an implicit fallback.
715/// Annotated callers can reuse a named concrete token or an `IntoConcreteToken`
716/// parameter without CPU detection. Covered calls remain direct. `using(token)`
717/// overrides inference and is required to supply proof in unannotated callers.
718#[proc_macro]
719pub fn attuned(input: TokenStream) -> TokenStream {
720    if let Some(expanded) = expanded_passthrough(&input.clone().into()) {
721        return expanded.into();
722    }
723    let input = parse_macro_input!(input as attune::call::Call);
724    input.expand(None, false).into()
725}
726
727/// Explicitly reconsider stronger tiers at runtime, retaining a guaranteed
728/// fallback covered by the caller's feature context (or scalar outside one).
729#[proc_macro]
730pub fn reattune(input: TokenStream) -> TokenStream {
731    if let Some(expanded) = expanded_passthrough(&input.clone().into()) {
732        return expanded.into();
733    }
734    let input = parse_macro_input!(input as attune::call::Call);
735    input.expand(None, true).into()
736}
737
738#[cfg(test)]
739mod variant_tests;