#![doc = include_str!("../README.md")]
#![feature(proc_macro_quote)]
use proc_macro::{Span, TokenStream};
use quote::{quote, ToTokens as _};
use syn::{
parse::{Parse, ParseStream},
parse_quote, Item, ItemFn,
};
struct MacroArgs {
allow_inline: bool,
}
impl Parse for MacroArgs {
fn parse(input: ParseStream) -> syn::Result<Self> {
if input.is_empty() {
return Ok(Self {
allow_inline: false,
});
}
let arg = input.parse::<syn::Ident>()?;
if arg == "allow_inline" {
Ok(Self { allow_inline: true })
} else {
Err(syn::Error::new(arg.span(), "expected `allow_inline`"))
}
}
}
#[proc_macro_attribute]
pub fn llvm_mca(attrs: TokenStream, input: TokenStream) -> TokenStream {
let function = match syn::parse(input) {
Ok(Item::Fn(function)) => function,
_ => {
return syn::Error::new(Span::call_site().into(), "expected function")
.to_compile_error()
.into()
}
};
let original_block = function.block;
let block = syn::parse(
quote! {{
unsafe {
std::arch::asm!(";# LLVM-MCA-BEGIN", options(nostack));
}
let ret = #original_block;
unsafe {
std::arch::asm!(";# LLVM-MCA-END", options(nostack));
}
ret
}}
.into(),
)
.unwrap();
let args = match syn::parse::<MacroArgs>(attrs) {
Ok(args) => args,
Err(err) => return err.to_compile_error().into(),
};
let attrs = if args.allow_inline {
function.attrs
} else {
function
.attrs
.into_iter()
.chain(std::iter::once(parse_quote! {
#[inline(never)]
}))
.collect()
};
let result = ItemFn {
attrs,
block,
..function
};
result.into_token_stream().into()
}