use crate::parse_helpers;
use proc_macro::TokenStream;
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::{parse_macro_input, Fields, ItemStruct, LitBool, LitStr, Token};
pub(crate) struct PassArgs {
pub(crate) name: LitStr,
pub(crate) requires: Vec<LitStr>,
pub(crate) invalidates: Vec<LitStr>,
pub(crate) phase: Option<LitStr>,
pub(crate) boundary_class: Option<LitStr>,
pub(crate) requires_caps: Vec<LitStr>,
pub(crate) preserves_abi: Option<LitBool>,
pub(crate) cost_model_family: Option<LitStr>,
pub(crate) analyze_always: bool,
}
impl Parse for PassArgs {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let mut name = None;
let mut requires = Vec::new();
let mut invalidates = Vec::new();
let mut phase = None;
let mut boundary_class = None;
let mut requires_caps = Vec::new();
let mut preserves_abi = None;
let mut cost_model_family = None;
let mut analyze_always = false;
let mut seen_keys = std::collections::BTreeSet::new();
while !input.is_empty() {
let key: syn::Ident = input.parse()?;
let key_name = parse_helpers::reject_duplicate_key(&mut seen_keys, &key)?;
input.parse::<Token![=]>()?;
match key_name.as_str() {
"name" => name = Some(input.parse()?),
"requires" => {
requires = parse_helpers::parse_litstr_array(
input,
"pass metadata arrays accept only string literals. Fix: use [\"analysis_name\"].",
)?
}
"invalidates" => {
invalidates = parse_helpers::parse_litstr_array(
input,
"pass metadata arrays accept only string literals. Fix: use [\"analysis_name\"].",
)?
}
"phase" => phase = Some(input.parse()?),
"boundary_class" => boundary_class = Some(input.parse()?),
"requires_caps" => {
requires_caps = parse_helpers::parse_litstr_array(
input,
"pass metadata arrays accept only string literals. Fix: use [\"analysis_name\"].",
)?
}
"preserves_abi" => preserves_abi = Some(input.parse()?),
"cost_model_family" => cost_model_family = Some(input.parse()?),
"analyze" => {
let value: LitStr = input.parse()?;
if value.value() == "always" {
analyze_always = true;
} else {
return Err(syn::Error::new_spanned(
value,
"unsupported analyze mode. Fix: use analyze = \"always\" or omit it.",
));
}
}
_ => {
return Err(syn::Error::new(
key.span(),
"unsupported vyre_pass argument. Fix: use name, requires, invalidates, phase, boundary_class, requires_caps, preserves_abi, cost_model_family, or analyze.",
));
}
}
if input.peek(Token![,]) {
input.parse::<Token![,]>()?;
}
}
validate_unique_string_literals("requires", &requires)?;
validate_unique_string_literals("invalidates", &invalidates)?;
validate_unique_string_literals("requires_caps", &requires_caps)?;
Ok(Self {
name: name.ok_or_else(|| input.error("missing pass name. Fix: add name = \"...\"."))?,
requires,
invalidates,
phase,
boundary_class,
requires_caps,
preserves_abi,
cost_model_family,
analyze_always,
})
}
}
pub(crate) fn pass_phase_tokens(value: Option<&LitStr>) -> syn::Result<proc_macro2::TokenStream> {
let variant = match value.map(LitStr::value).as_deref() {
None | Some("unclassified") => quote! { Unclassified },
Some("canonicalization") => quote! { Canonicalization },
Some("scalar_algebra") => quote! { ScalarAlgebra },
Some("loop") => quote! { Loop },
Some("memory") => quote! { Memory },
Some("fusion_cse") => quote! { FusionCse },
Some("sync") => quote! { Sync },
Some("specialization") => quote! { Specialization },
Some("cleanup") => quote! { Cleanup },
Some("dataflow") => quote! { Dataflow },
Some("megakernel") => quote! { Megakernel },
Some(_) => {
let Some(value) = value else {
return Err(syn::Error::new(
proc_macro2::Span::call_site(),
"unsupported pass phase. Fix: pass a string literal phase or omit the attribute.",
));
};
return Err(syn::Error::new_spanned(
value,
"unsupported pass phase. Fix: use unclassified, canonicalization, scalar_algebra, loop, memory, fusion_cse, sync, specialization, cleanup, dataflow, or megakernel.",
));
}
};
Ok(quote! { ::vyre::optimizer::PassPhase::#variant })
}
pub(crate) fn boundary_class_tokens(
value: Option<&LitStr>,
) -> syn::Result<proc_macro2::TokenStream> {
let variant = match value.map(LitStr::value).as_deref() {
None | Some("unknown") => quote! { Unknown },
Some("abi_preserving") => quote! { AbiPreserving },
Some("abi_changing") => quote! { AbiChanging },
Some("backend_aware") => quote! { BackendAware },
Some("runtime_aware") => quote! { RuntimeAware },
Some("domain_specific") => quote! { DomainSpecific },
Some(_) => {
let Some(value) = value else {
return Err(syn::Error::new(
proc_macro2::Span::call_site(),
"unsupported pass boundary_class. Fix: pass a string literal boundary_class or omit the attribute.",
));
};
return Err(syn::Error::new_spanned(
value,
"unsupported pass boundary_class. Fix: use unknown, abi_preserving, abi_changing, backend_aware, runtime_aware, or domain_specific.",
));
}
};
Ok(quote! { ::vyre::optimizer::PassBoundaryClass::#variant })
}
pub(crate) fn cost_model_family_tokens(
value: Option<&LitStr>,
) -> syn::Result<proc_macro2::TokenStream> {
let variant = match value.map(LitStr::value).as_deref() {
None | Some("unknown") => quote! { Unknown },
Some("scalar") => quote! { Scalar },
Some("loop") => quote! { Loop },
Some("memory") => quote! { Memory },
Some("fusion") => quote! { Fusion },
Some("sync") => quote! { Sync },
Some("dataflow") => quote! { Dataflow },
Some("megakernel") => quote! { Megakernel },
Some(_) => {
let Some(value) = value else {
return Err(syn::Error::new(
proc_macro2::Span::call_site(),
"unsupported pass cost_model_family. Fix: pass a string literal cost_model_family or omit the attribute.",
));
};
return Err(syn::Error::new_spanned(
value,
"unsupported pass cost_model_family. Fix: use unknown, scalar, loop, memory, fusion, sync, dataflow, or megakernel.",
));
}
};
Ok(quote! { ::vyre::optimizer::CostModelFamily::#variant })
}
fn validate_unique_string_literals(field: &str, values: &[LitStr]) -> syn::Result<()> {
let mut seen = std::collections::BTreeSet::new();
for value in values {
let text = value.value();
if !seen.insert(text.clone()) {
return Err(syn::Error::new_spanned(
value,
format!(
"duplicate vyre_pass {field} entry `{text}`. Fix: list each dependency, invalidation, or capability once."
),
));
}
}
Ok(())
}
pub(crate) fn vyre_pass_impl(args: TokenStream, item: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as PassArgs);
let item = parse_macro_input!(item as ItemStruct);
if !matches!(item.fields, Fields::Unit) {
return syn::Error::new_spanned(
&item.ident,
"#[vyre_pass] supports only unit structs. Fix: move pass state into explicit scheduler/context storage and declare the pass as `pub struct PassName;`.",
)
.to_compile_error()
.into();
}
let ident = &item.ident;
let name = args.name;
let requires = args.requires;
let invalidates = args.invalidates;
let requires_caps = args.requires_caps;
let phase = match pass_phase_tokens(args.phase.as_ref()) {
Ok(tokens) => tokens,
Err(error) => return error.to_compile_error().into(),
};
let boundary_class = match boundary_class_tokens(args.boundary_class.as_ref()) {
Ok(tokens) => tokens,
Err(error) => return error.to_compile_error().into(),
};
let cost_model_family = match cost_model_family_tokens(args.cost_model_family.as_ref()) {
Ok(tokens) => tokens,
Err(error) => return error.to_compile_error().into(),
};
let preserves_abi = args.preserves_abi.map(|value| value.value).unwrap_or(true);
let analyze_body = if args.analyze_always {
quote! { ::vyre::optimizer::PassAnalysis::RUN }
} else {
quote! { Self::analyze_impl(program) }
};
let metadata = quote! {
::vyre::optimizer::PassMetadata {
name: #name,
requires: &[#(#requires),*],
invalidates: &[#(#invalidates),*],
phase: #phase,
boundary_class: #boundary_class,
requires_caps: &[#(#requires_caps),*],
preserves_abi: #preserves_abi,
cost_model_family: #cost_model_family,
}
};
quote! {
#item
impl ::vyre::optimizer::private::Sealed for #ident {}
impl ::vyre::optimizer::ProgramPass for #ident {
#[inline]
fn metadata(&self) -> ::vyre::optimizer::PassMetadata {
#metadata
}
#[inline]
fn analyze(&self, program: &::vyre::ir::Program) -> ::vyre::optimizer::PassAnalysis {
#analyze_body
}
#[inline]
fn transform(
&self,
program: ::vyre::ir::Program,
) -> ::vyre::optimizer::PassResult {
Self::transform(program)
}
#[inline]
fn fingerprint(&self, program: &::vyre::ir::Program) -> u64 {
::vyre::optimizer::fingerprint_program(program)
}
}
::inventory::submit! {
::vyre::optimizer::ProgramPassRegistration {
metadata: #metadata,
factory: || ::std::boxed::Box::new(#ident),
}
}
}
.into()
}