Skip to content

Commit

Permalink
add exception macro
Browse files Browse the repository at this point in the history
  • Loading branch information
kiffie committed Jul 16, 2023
1 parent 6b02a4f commit 1ca027f
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 24 deletions.
2 changes: 1 addition & 1 deletion mips-rt/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mips-rt"
version = "0.3.3"
version = "0.3.4"
authors = ["Stephan <kiffie@mailbox.org"]
edition = "2021"
repository = "https://github.com/kiffie/pic32-rs"
Expand Down
6 changes: 3 additions & 3 deletions mips-rt/macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[package]
name = "mips-rt-macros"
version = "0.3.0"
version = "0.3.4"
authors = ["Stephan <kiffie@mailbox.org"]
edition = "2018"
edition = "2021"
repository = "https://github.com/kiffie/pic32-rs"
categories = ["embedded", "no-std"]
description = "Attributes re-exported in `mips-rt`"
Expand All @@ -18,4 +18,4 @@ proc-macro2 = "1.0"

[dependencies.syn]
features = ["extra-traits", "full"]
version = "1.0"
version = "2.0"
143 changes: 124 additions & 19 deletions mips-rt/macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Internal implementation details of `mips-rt`.
//!
//! Do not use this crate directly.
//! Almost identical to [`cortex_m_rt_macros`](https://github.com/rust-embedded/cortex-m-rt/)
//! Almost identical to `cortex_m_rt_macros`

use proc_macro::TokenStream;
use proc_macro2::Span;
Expand Down Expand Up @@ -44,7 +44,6 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {

// check the function signature
let valid_signature = f.sig.constness.is_none()
&& f.sig.asyncness.is_none()
&& f.vis == Visibility::Inherited
&& f.sig.abi.is_none()
&& f.sig.inputs.is_empty()
Expand All @@ -53,10 +52,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
&& f.sig.variadic.is_none()
&& match f.sig.output {
ReturnType::Default => false,
ReturnType::Type(_, ref ty) => match **ty {
Type::Never(_) => true,
_ => false,
},
ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)),
};

if !valid_signature {
Expand Down Expand Up @@ -138,6 +134,115 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
.into()
}

#[proc_macro_attribute]
pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
let mut f = parse_macro_input!(input as ItemFn);

if !args.is_empty() {
return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
.to_compile_error()
.into();
}

if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Exception) {
return error;
}

let fspan = f.span();
let ident = f.sig.ident.clone();

let ident_s = ident.to_string();
let export_name = match &*ident_s {
"general_exception" => "_general_exception_handler",
"nmi" => "_nmi_handler",
_ => {
return parse::Error::new(ident.span(), "This is not a valid exception name")
.to_compile_error()
.into();
}
};

let valid_signature = f.sig.constness.is_none()
&& f.vis == Visibility::Inherited
&& f.sig.abi.is_none()
&& f.sig.inputs.len() == 2
&& 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,
},
};

if !valid_signature {
return parse::Error::new(
fspan,
"`#[exception]`exception handlers must have signature `unsafe fn(u32, u32) [-> !]`",
)
.to_compile_error()
.into();
}

let (statics, stmts) = match extract_static_muts(f.block.stmts) {
Err(e) => return e.to_compile_error().into(),
Ok(x) => x,
};

f.sig.ident = Ident::new(&format!("__mips_rt_{}", f.sig.ident), 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::<FnArg>(
quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &mut #ty).into(),
)
.unwrap()
}));
f.block.stmts = stmts;

let tramp_ident = Ident::new(&format!("{}_trampoline", f.sig.ident), 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! {
#(#cfgs)*
{
#(#attrs)*
static mut #ident: #ty = #expr;
&mut #ident
}
}
})
.collect::<Vec<_>>();

let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone());

quote!(
#(#cfgs)*
#(#attrs)*
#[doc(hidden)]
#[export_name = #export_name]
pub unsafe extern "C" fn #tramp_ident(cp0_cause: u32, cp0_status: u32) {
#ident(
cp0_cause, cp0_status,
#(#resource_args),*
)
}

#f
).into()
}

/// Attribute to declare an Interrupt Service Routine (ISR)
#[proc_macro_attribute]
pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
Expand Down Expand Up @@ -310,10 +415,10 @@ fn extract_static_muts(
let mut seen = HashSet::new();
let mut statics = vec![];
let mut stmts = vec![];
while let Some(stmt) = istmts.next() {
for stmt in istmts.by_ref() {
match stmt {
Stmt::Item(Item::Static(var)) => {
if var.mutability.is_some() {
Stmt::Item(Item::Static(var)) => match var.mutability {
syn::StaticMutability::Mut(_) => {
if seen.contains(&var.ident) {
return Err(parse::Error::new(
var.ident.span(),
Expand All @@ -323,10 +428,9 @@ fn extract_static_muts(

seen.insert(var.ident.clone());
statics.push(var);
} else {
stmts.push(Stmt::Item(Item::Static(var)));
}
}
_ => stmts.push(Stmt::Item(Item::Static(var))),
},
_ => {
stmts.push(stmt);
break;
Expand Down Expand Up @@ -356,7 +460,7 @@ fn extract_cfgs(attrs: Vec<Attribute>) -> (Vec<Attribute>, Vec<Attribute>) {

enum WhiteListCaller {
Entry,
// Exception,
Exception,
Interrupt,
PreInit,
}
Expand All @@ -371,20 +475,21 @@ fn check_attr_whitelist(attrs: &[Attribute], caller: WhiteListCaller) -> Result<
"deny",
"forbid",
"cold",
"naked",
];

'o: for attr in attrs {
for val in whitelist {
if eq(&attr, &val) {
if eq(attr, val) {
continue 'o;
}
}

let err_str = match caller {
WhiteListCaller::Entry => "this attribute is not allowed on a mips-rt entry point",
// WhiteListCaller::Exception => {
// "this attribute is not allowed on an exception handler controlled by mips-rt"
// }
WhiteListCaller::Exception => {
"this attribute is not allowed on an exception handler controlled by mips-rt"
}
WhiteListCaller::Interrupt => {
"this attribute is not allowed on an interrupt handler controlled by mips-rt"
}
Expand All @@ -393,7 +498,7 @@ fn check_attr_whitelist(attrs: &[Attribute], caller: WhiteListCaller) -> Result<
}
};

return Err(parse::Error::new(attr.span(), &err_str)
return Err(parse::Error::new(attr.span(), err_str)
.to_compile_error()
.into());
}
Expand All @@ -403,5 +508,5 @@ fn check_attr_whitelist(attrs: &[Attribute], caller: WhiteListCaller) -> Result<

/// Returns `true` if `attr.path` matches `name`
fn eq(attr: &Attribute, name: &str) -> bool {
attr.style == AttrStyle::Outer && attr.path.is_ident(name)
attr.style == AttrStyle::Outer && attr.path().is_ident(name)
}
2 changes: 1 addition & 1 deletion mips-rt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#![no_std]

pub use mips_rt_macros::{entry, interrupt, pre_init};
pub use mips_rt_macros::{entry, exception, interrupt, pre_init};

#[doc(hidden)]
#[no_mangle]
Expand Down

0 comments on commit 1ca027f

Please sign in to comment.