use proc_macro2::{Ident, Span};
use proc_macro_error::proc_macro_error;
use syn::{
parse, parse_macro_input, spanned::Spanned, ItemFn, PathArguments, ReturnType, Type, Visibility,
};
use proc_macro::TokenStream;
use quote::quote;
#[proc_macro_attribute]
pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
let f = parse_macro_input!(input as ItemFn);
if !f.sig.inputs.is_empty() {
return parse::Error::new(
f.sig.inputs.last().unwrap().span(),
"`#[entry]` function accepts no arguments",
)
.to_compile_error()
.into();
}
let valid_signature = f.sig.constness.is_none()
&& f.sig.asyncness.is_none()
&& f.vis == Visibility::Inherited
&& f.sig.abi.is_none()
&& f.sig.generics.params.is_empty()
&& f.sig.generics.where_clause.is_none()
&& f.sig.variadic.is_none()
&& match f.sig.output {
ReturnType::Default => false,
ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)),
};
if !valid_signature {
return parse::Error::new(
f.span(),
"`#[entry]` function must have signature `[unsafe] fn() -> !`",
)
.to_compile_error()
.into();
}
if !args.is_empty() {
return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
.to_compile_error()
.into();
}
let attrs = f.attrs;
let unsafety = f.sig.unsafety;
let args = f.sig.inputs;
let stmts = f.block.stmts;
quote!(
#[allow(non_snake_case)]
#[export_name = "main"]
#(#attrs)*
pub #unsafety fn __risc_v_rt__main(#args) -> ! {
#(#stmts)*
}
)
.into()
}
#[allow(unused)]
fn is_simple_type(ty: &Type, name: &str) -> bool {
if let Type::Path(p) = ty {
if p.qself.is_none() && p.path.leading_colon.is_none() && p.path.segments.len() == 1 {
let segment = p.path.segments.first().unwrap();
if segment.ident == name && segment.arguments == PathArguments::None {
return true;
}
}
}
false
}
#[proc_macro_attribute]
#[proc_macro_error]
pub fn highcode(_args: TokenStream, input: TokenStream) -> TokenStream {
let f = parse_macro_input!(input as ItemFn);
let section = quote! {
#[link_section = ".highcode"]
#[inline(never)] };
quote!(
#section
#f
)
.into()
}
#[proc_macro_attribute]
pub fn interrupt(_args: TokenStream, input: TokenStream) -> TokenStream {
let mut f = parse_macro_input!(input as ItemFn);
if !f.sig.inputs.is_empty() {
return parse::Error::new(
f.sig.inputs.last().unwrap().span(),
"`#[interrupt]` function accepts no arguments",
)
.to_compile_error()
.into();
}
let ident = f.sig.ident.clone();
let ident_s = &ident.clone();
let valid_signature = f.sig.constness.is_none()
&& f.vis == Visibility::Inherited
&& f.sig.abi.is_none()
&& f.sig.generics.params.is_empty()
&& f.sig.generics.where_clause.is_none()
&& f.sig.variadic.is_none()
&& match f.sig.output {
ReturnType::Default => true,
ReturnType::Type(_, ref ty) => match **ty {
Type::Tuple(ref tuple) => tuple.elems.is_empty(),
Type::Never(..) => true,
_ => false,
},
}
&& f.sig.inputs.len() <= 1;
if !valid_signature {
return parse::Error::new(
f.span(),
"`#[interrupt]` handlers must have signature `[unsafe] fn() [-> !]`",
)
.to_compile_error()
.into();
}
let inner_fn_export_name = format!("__ch32v_rt_internal_{}", f.sig.ident);
f.sig.ident = Ident::new(&inner_fn_export_name, proc_macro2::Span::call_site());
let interrupt_name = ident_s.to_string();
let asm_src = format!(
r#"
.section .trap, "ax"
.global {0}
{0}:
addi sp, sp, -4
sw ra, 0(sp)
jal {1}
lw ra, 0(sp)
addi sp, sp, 4
mret
"#,
interrupt_name, inner_fn_export_name
);
quote!(
core::arch::global_asm!(#asm_src);
#[link_section = ".trap"]
#[export_name = #inner_fn_export_name]
#[allow(non_snake_case)]
#f
)
.into()
}