@@ 11,7 11,7 @@ pub fn entry(
args: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
- let f = syn::parse_macro_input!(input as syn::ItemFn);
+ let mut f = syn::parse_macro_input!(input as syn::ItemFn);
// check the function signature
let valid_signature = f.sig.constness.is_none()
@@ 23,10 23,7 @@ pub fn entry(
&& f.sig.variadic.is_none()
&& match f.sig.output {
syn::ReturnType::Default => false,
- syn::ReturnType::Type(_, ref ty) => match **ty {
- syn::Type::Never(_) => true,
- _ => false,
- },
+ syn::ReturnType::Type(_, ref ty) => matches!(**ty, syn::Type::Never(_)),
};
if !valid_signature {
@@ 47,16 44,53 @@ pub fn entry(
.into();
}
+ let (statics, stmts) = match extract_static_muts(f.block.stmts) {
+ Err(e) => return e.to_compile_error().into(),
+ Ok(x) => x,
+ };
+
// Rename the function so it is not callable
- let ident = syn::Ident::new(
- &format!("_avr_device_rt_{}", f.sig.ident),
+ f.sig.ident = syn::Ident::new(
+ &format!("__avr_device_rt_{}", f.sig.ident),
+ proc_macro2::Span::call_site(),
+ );
+ f.sig.inputs.extend(statics.iter().map(|statik| {
+ let ident = &statik.ident;
+ let ty = &statik.ty;
+ let attrs = &statik.attrs;
+
+ // Note that we use an explicit `'static` lifetime for the entry point arguments. This makes
+ // it more flexible, and is sound here, since the entry will not be called again, ever.
+ syn::parse::<syn::FnArg>(
+ quote::quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &'static mut #ty).into(),
+ )
+ .unwrap()
+ }));
+ f.block.stmts = stmts;
+
+ let tramp_ident = syn::Ident::new(
+ &format!("{}_trampoline", f.sig.ident),
proc_macro2::Span::call_site(),
);
+ let ident = &f.sig.ident;
- let attrs = f.attrs;
- let block = f.block;
- let stmts = block.stmts;
- let unsafety = f.sig.unsafety;
+ let resource_args = statics
+ .iter()
+ .map(|statik| {
+ let (ref cfgs, ref attrs) = extract_cfgs(statik.attrs.clone());
+ let ident = &statik.ident;
+ let ty = &statik.ty;
+ let expr = &statik.expr;
+ quote::quote! {
+ #(#cfgs)*
+ {
+ #(#attrs)*
+ static mut #ident: #ty = #expr;
+ &mut #ident
+ }
+ }
+ })
+ .collect::<Vec<_>>();
quote::quote! (
#[cfg(not(any(doc, target_arch = "avr")))]
@@ 66,12 100,16 @@ pub fn entry(
https://github.com/Rahix/avr-device/pull/41 for more details."
);
- #(#attrs)*
#[doc(hidden)]
#[export_name = "main"]
- pub #unsafety extern "C" fn #ident() -> ! {
- #(#stmts)*
+ pub unsafe extern "C" fn #tramp_ident() {
+ #ident(
+ #(#resource_args),*
+ )
}
+
+ #[doc(hidden)]
+ #f
)
.into()
}
@@ 81,16 119,12 @@ pub fn interrupt(
args: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
- let f = syn::parse_macro_input!(input as syn::ItemFn);
+ let mut f: syn::ItemFn = syn::parse(input).expect("`#[interrupt]` must be applied to a function");
let args: Vec<_> = args.into_iter().collect();
let fspan = f.span();
- let ident = f.sig.ident;
+ let ident = f.sig.ident.clone();
let ident_s = ident.to_string();
- let attrs = f.attrs;
- let block = f.block;
- let stmts = block.stmts;
- let unsafety = f.sig.unsafety;
let chip = if let Some(tree) = args.get(0) {
if let proc_macro::TokenTree::Ident(ident) = tree {
@@ 137,6 171,42 @@ pub fn interrupt(
.into();
}
+ let (statics, stmts) = match extract_static_muts(f.block.stmts.iter().cloned()) {
+ Err(e) => return e.to_compile_error().into(),
+ Ok(x) => x,
+ };
+
+ f.sig.ident = syn::Ident::new(&format!("__avr_device_rt_{}", f.sig.ident), proc_macro2::Span::call_site());
+ f.sig.inputs.extend(statics.iter().map(|statik| {
+ let ident = &statik.ident;
+ let ty = &statik.ty;
+ let attrs = &statik.attrs;
+ syn::parse::<syn::FnArg>(quote::quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &mut #ty).into())
+ .unwrap()
+ }));
+ f.block.stmts = stmts;
+
+ let tramp_ident = syn::Ident::new(&format!("{}_trampoline", f.sig.ident), proc_macro2::Span::call_site());
+ let ident = &f.sig.ident;
+
+ let resource_args = statics
+ .iter()
+ .map(|statik| {
+ let (ref cfgs, ref attrs) = extract_cfgs(statik.attrs.clone());
+ let ident = &statik.ident;
+ let ty = &statik.ty;
+ let expr = &statik.expr;
+ quote::quote! {
+ #(#cfgs)*
+ {
+ #(#attrs)*
+ static mut #ident: #ty = #expr;
+ &mut #ident
+ }
+ }
+ })
+ .collect::<Vec<_>>();
+
let vect = if let Some(v) = vector::lookup_vector(&chip, &ident_s) {
v
} else {
@@ 149,18 219,77 @@ pub fn interrupt(
};
let vector = format!("__vector_{}", vect);
let vector_ident = syn::Ident::new(&vector, proc_macro2::Span::call_site());
+ let vector_ident_s = vector_ident.to_string();
quote::quote! (
- #[no_mangle]
- pub unsafe extern "avr-interrupt" fn #vector_ident() {
- #ident();
+ #[doc(hidden)]
+ #[export_name = #vector_ident_s]
+ pub unsafe extern "avr-interrupt" fn #tramp_ident() {
+ #ident(
+ #(#resource_args),*
+ )
}
- #(#attrs)*
- #[allow(non_snake_case)]
- #unsafety fn #ident() {
- #(#stmts)*
- }
+ #[doc(hidden)]
+ #f
)
.into()
}
+
+/// Extracts `static mut` vars from the beginning of the given statements
+fn extract_static_muts(
+ stmts: impl IntoIterator<Item = syn::Stmt>,
+) -> Result<(Vec<syn::ItemStatic>, Vec<syn::Stmt>), syn::parse::Error> {
+ let mut istmts = stmts.into_iter();
+
+ let mut seen = std::collections::HashSet::new();
+ let mut statics = vec![];
+ let mut stmts = vec![];
+ while let Some(stmt) = istmts.next() {
+ match stmt {
+ syn::Stmt::Item(syn::Item::Static(var)) => {
+ if var.mutability.is_some() {
+ if seen.contains(&var.ident) {
+ return Err(syn::parse::Error::new(
+ var.ident.span(),
+ format!("the name `{}` is defined multiple times", var.ident),
+ ));
+ }
+
+ seen.insert(var.ident.clone());
+ statics.push(var);
+ } else {
+ stmts.push(syn::Stmt::Item(syn::Item::Static(var)));
+ }
+ }
+ _ => {
+ stmts.push(stmt);
+ break;
+ }
+ }
+ }
+
+ stmts.extend(istmts);
+
+ Ok((statics, stmts))
+}
+
+fn extract_cfgs(attrs: Vec<syn::Attribute>) -> (Vec<syn::Attribute>, Vec<syn::Attribute>) {
+ let mut cfgs = vec![];
+ let mut not_cfgs = vec![];
+
+ for attr in attrs {
+ if eq(&attr, "cfg") {
+ cfgs.push(attr);
+ } else {
+ not_cfgs.push(attr);
+ }
+ }
+
+ (cfgs, not_cfgs)
+}
+
+/// Returns `true` if `attr.path` matches `name`
+fn eq(attr: &syn::Attribute, name: &str) -> bool {
+ attr.style == syn::AttrStyle::Outer && attr.path.is_ident(name)
+}