IntoConcreteToken Trait
IntoConcreteToken enables compile-time dispatch via monomorphization. Each token type returns Some(self) for its own type and None for others.
Basic Usage
#![allow(unused)] fn main() { use archmage::{IntoConcreteToken, SimdToken, X64V3Token, NeonToken, ScalarToken}; fn process<T: IntoConcreteToken>(token: T, data: &mut [f32]) { // Compiler eliminates non-matching branches via monomorphization if let Some(t) = token.as_x64v3() { process_avx2(t, data); } else if let Some(t) = token.as_neon() { process_neon(t, data); } else if let Some(_) = token.as_scalar() { process_scalar(data); } } }
When called with X64V3Token, the compiler sees:
as_x64v3()→Some(token)(takes this branch)as_neon()→None(eliminated)as_scalar()→None(eliminated)
Available Methods
#![allow(unused)] fn main() { pub trait IntoConcreteToken: SimdToken { fn as_x64v2(self) -> Option<X64V2Token> { None } fn as_x64v3(self) -> Option<X64V3Token> { None } fn as_x64v4(self) -> Option<X64V4Token> { None } // requires avx512 fn as_avx512_modern(self) -> Option<Avx512ModernToken> { None } fn as_neon(self) -> Option<NeonToken> { None } fn as_neon_aes(self) -> Option<NeonAesToken> { None } fn as_neon_sha3(self) -> Option<NeonSha3Token> { None } fn as_wasm128(self) -> Option<Simd128Token> { None } fn as_scalar(self) -> Option<ScalarToken> { None } } }
Each concrete token overrides its own method to return Some(self).
Upcasting with IntoConcreteToken
You can check if a token supports higher capabilities:
#![allow(unused)] fn main() { fn maybe_use_avx512<T: IntoConcreteToken>(token: T, data: &mut [f32]) { // Check if we actually have AVX-512 if let Some(v4) = token.as_x64v4() { fast_path_avx512(v4, data); } else if let Some(v3) = token.as_x64v3() { normal_path_avx2(v3, data); } } }
Note: This creates an LLVM optimization boundary. The generic caller and feature-enabled callee have different target settings. Do this dispatch at entry points, not in hot code.
Dispatch Order
Check from highest to lowest capability:
#![allow(unused)] fn main() { fn dispatch<T: IntoConcreteToken>(token: T, data: &[f32]) -> f32 { // Highest first #[cfg(feature = "avx512")] if let Some(t) = token.as_x64v4() { return process_v4(t, data); } if let Some(t) = token.as_x64v3() { return process_v3(t, data); } if let Some(t) = token.as_neon() { return process_neon(t, data); } if let Some(t) = token.as_wasm128() { return process_wasm(t, data); } // Scalar fallback process_scalar(data) } }
vs incant!
| Feature | IntoConcreteToken | incant! |
|---|---|---|
| Dispatch style | Explicit if/else | Macro-generated |
| Token passing | Token already obtained | Summons tokens |
| Flexibility | Full control | Convention-based |
| Verbosity | More code | Less code |
Use IntoConcreteToken when you already have a token and need to specialize. Use incant! for entry-point dispatch.
Example: Library with Generic Token API
#![allow(unused)] fn main() { use archmage::{IntoConcreteToken, SimdToken, arcane}; /// Public API accepts any token pub fn transform<T: IntoConcreteToken>(token: T, data: &mut [f32]) { if let Some(t) = token.as_x64v3() { transform_avx2(t, data); } else if let Some(t) = token.as_neon() { transform_neon(t, data); } else { transform_scalar(data); } } #[cfg(target_arch = "x86_64")] #[arcane] fn transform_avx2(token: X64V3Token, data: &mut [f32]) { // AVX2 implementation } #[cfg(target_arch = "aarch64")] #[arcane] fn transform_neon(token: NeonToken, data: &mut [f32]) { // NEON implementation } fn transform_scalar(data: &mut [f32]) { // Scalar fallback } }
Callers can pass any token:
#![allow(unused)] fn main() { if let Some(token) = Desktop64::summon() { transform(token, &mut data); // Uses AVX2 path } // Or force scalar for testing transform(ScalarToken, &mut data); }