Skip to main content

arcane

Attribute Macro arcane 

Source
#[arcane]
Expand description

Mark a function as an arcane SIMD function.

This macro generates a safe wrapper around a #[target_feature] function. The token parameter type determines which CPU features are enabled.

§Expansion Modes

§Sibling (default)

Generates two functions at the same scope: a safe #[target_feature] sibling and a safe wrapper. self/Self work naturally since both functions share scope. Compatible with #![forbid(unsafe_code)].

ⓘ
#[arcane]
fn process(token: X64V3Token, data: &[f32; 8]) -> [f32; 8] { /* body */ }

Methods work naturally:

ⓘ
impl MyType {
    #[arcane]
    fn compute(&self, token: X64V3Token) -> f32 {
        self.data.iter().sum()  // self/Self just work!
    }
}

§Associated functions in an inherent impl (in_impl)

An associated function without a receiver cannot call its sibling by bare name from inside an impl block, and the macro cannot see the block. Say in_impl, and the wrapper calls Self::__arcane_fn(...):

ⓘ
impl Table {
    #[arcane(in_impl)]
    fn build(token: X64V3Token, n: usize) -> Self { Self::with_capacity(n) }
}

Methods with a receiver need no flag.

§Trait impls (in_trait, nested or _self = Type)

A trait impl cannot hold the extra sibling, so the inner function nests inside the method. in_trait (alias nested) selects that expansion. A method with a receiver also needs _self = Type, because the nested function has no self and no Self: the receiver becomes _self, Self becomes the named type, and self in the body becomes _self, so the body reads as written:

ⓘ
impl SimdOps for MyType {
    #[arcane(in_trait, _self = MyType)]
    fn compute(&self, token: X64V3Token) -> Self {
        Self::new(self.data.iter().sum())
    }
}

_self = Type implies in_trait. in_impl and in_trait describe different places and are rejected together.

§Cross-Architecture Behavior

Default (cfg-out): On the wrong architecture, the function is not emitted at all — no stub, no dead code. Code that references it must be cfg-gated.

stub has been removed. Use incant! or explicit call-site cfg guards.

§Token Parameter Forms

ⓘ
// Concrete token
#[arcane]
fn process(token: X64V3Token, data: &[f32; 8]) -> [f32; 8] { ... }

// impl Trait bound
#[arcane]
fn process(token: impl HasX64V2, data: &[f32; 8]) -> [f32; 8] { ... }

// Generic with inline or where-clause bounds
#[arcane]
fn process<T: HasX64V2>(token: T, data: &[f32; 8]) -> [f32; 8] { ... }

// Wildcard
#[arcane]
fn process(_: X64V3Token, data: &[f32; 8]) -> [f32; 8] { ... }

Exactly one parameter may be a token; two token parameters are a compile error, because the wrapper would not know which proof to assert. Wildcard and tuple patterns on other parameters are renamed to __archmage_arg_N in the wrapper’s signature so they can be forwarded.

§Options

OptionEffect
in_implReceiver-less associated function in an inherent impl: the wrapper calls Self::__arcane_fn
in_trait (alias nested)Trait impl: the inner function nests inside the method
_self = TypeImplies in_trait; the receiver becomes _self, Self and self are rewritten
inline_alwaysUse #[inline(always)] (requires nightly)
import_intrinsicsAuto-import archmage::intrinsics::{arch}::* (includes safe memory ops)
import_magetypesAuto-import magetypes::simd::{ns}::* and magetypes::simd::backends::*

§Auto-Imports

import_intrinsics and import_magetypes inject use statements into the function body, eliminating boilerplate. The macro derives the architecture and namespace from the token type:

ⓘ
// Without auto-imports — lots of boilerplate:
use std::arch::x86_64::*;
use magetypes::simd::v3::*;

#[arcane]
fn process(token: X64V3Token, data: &[f32; 8]) -> f32 {
    let v = f32x8::load_t(token, data);
    let zero = _mm256_setzero_ps();
    // ...
}

// With auto-imports — clean:
#[arcane(import_intrinsics, import_magetypes)]
fn process(token: X64V3Token, data: &[f32; 8]) -> f32 {
    let v = f32x8::load_t(token, data);
    let zero = _mm256_setzero_ps();
    // ...
}

The namespace mapping is token-driven:

Tokenimport_intrinsicsimport_magetypes
X64V1..V3Tokenarchmage::intrinsics::x86_64::*magetypes::simd::v3::*
X64V4Tokenarchmage::intrinsics::x86_64::*magetypes::simd::v4::*
X64V4xTokenarchmage::intrinsics::x86_64::*magetypes::simd::v4x::*
NeonToken / ARMarchmage::intrinsics::aarch64::*magetypes::simd::neon::*
Wasm128Tokenarchmage::intrinsics::wasm32::*magetypes::simd::wasm128::*

Works with concrete tokens, impl Trait bounds, and generic parameters.

§Supported Tokens

  • x86_64: X64V2Token, X64V3Token/Desktop64, X64V4Token/Avx512Token/Server64, X64V4xToken, Avx512Fp16Token, X64CryptoToken, X64V3CryptoToken, X64V3GfniCryptoToken
  • ARM: NeonToken/Arm64, Arm64V2Token, Arm64V3Token, NeonAesToken, NeonSha3Token, NeonCrcToken
  • WASM: Wasm128Token

§Supported Trait Bounds

HasX64V2, HasX64V4, HasNeon, HasNeonAes, HasNeonSha3, HasArm64V2, HasArm64V3

ⓘ
#![feature(target_feature_inline_always)]

#[arcane(inline_always)]
fn fast_kernel(token: Avx2Token, data: &mut [f32]) {
    // Inner function will use #[inline(always)]
}

Concrete tokens are checked through a shared, tier-specific associated constant. This rejects accidental token-name aliases without reevaluating a tag comparison in every expansion. The matching archmage release pins this macro crate exactly. Getting past the check takes deliberately shadowing archmage’s type names and copying their hidden constants, which gives undefined behavior on CPUs without the features; see the safety model.

#[arcane(suppress_const_test)] omits this accidental-misuse check for trusted generators. The caller must ensure the actual token proves the tier selected by its name. Intrinsic target-feature checking remains enabled, but does not authenticate that token: it checks instructions against the generated feature context. Ordinary callers should keep the default check enabled. This option is intended for generators whose registry already establishes that match.