ch32v_rt_macros/
lib.rs

1use proc_macro2::{Ident, Span};
2use proc_macro_error::proc_macro_error;
3use syn::{
4    parse, parse_macro_input, spanned::Spanned, ItemFn, PathArguments, ReturnType, Type, Visibility,
5};
6
7use proc_macro::TokenStream;
8use quote::quote;
9
10#[proc_macro_attribute]
11pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
12    let f = parse_macro_input!(input as ItemFn);
13
14    // check the function arguments
15    if !f.sig.inputs.is_empty() {
16        return parse::Error::new(
17            f.sig.inputs.last().unwrap().span(),
18            "`#[entry]` function accepts no arguments",
19        )
20        .to_compile_error()
21        .into();
22    }
23
24    // check the function signature
25    let valid_signature = f.sig.constness.is_none()
26        && f.sig.asyncness.is_none()
27        && f.vis == Visibility::Inherited
28        && f.sig.abi.is_none()
29        && f.sig.generics.params.is_empty()
30        && f.sig.generics.where_clause.is_none()
31        && f.sig.variadic.is_none()
32        && match f.sig.output {
33            ReturnType::Default => false,
34            ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)),
35        };
36
37    if !valid_signature {
38        return parse::Error::new(
39            f.span(),
40            "`#[entry]` function must have signature `[unsafe] fn() -> !`",
41        )
42        .to_compile_error()
43        .into();
44    }
45
46    if !args.is_empty() {
47        return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
48            .to_compile_error()
49            .into();
50    }
51
52    // XXX should we blacklist other attributes?
53    let attrs = f.attrs;
54    let unsafety = f.sig.unsafety;
55    let args = f.sig.inputs;
56    let stmts = f.block.stmts;
57
58    quote!(
59        #[allow(non_snake_case)]
60        #[export_name = "main"]
61        #(#attrs)*
62        pub #unsafety fn __risc_v_rt__main(#args) -> ! {
63            #(#stmts)*
64        }
65    )
66    .into()
67}
68
69#[allow(unused)]
70fn is_simple_type(ty: &Type, name: &str) -> bool {
71    if let Type::Path(p) = ty {
72        if p.qself.is_none() && p.path.leading_colon.is_none() && p.path.segments.len() == 1 {
73            let segment = p.path.segments.first().unwrap();
74            if segment.ident == name && segment.arguments == PathArguments::None {
75                return true;
76            }
77        }
78    }
79    false
80}
81
82/// This attribute allows placing functions into ram.
83#[proc_macro_attribute]
84#[proc_macro_error]
85pub fn highcode(_args: TokenStream, input: TokenStream) -> TokenStream {
86    let f = parse_macro_input!(input as ItemFn);
87
88    let section = quote! {
89        #[link_section = ".highcode"]
90        #[inline(never)] // make certain function is not inlined
91    };
92
93    quote!(
94        #section
95        #f
96    )
97    .into()
98}
99
100/// Marks a function as an interrupt handler
101#[proc_macro_attribute]
102pub fn interrupt(_args: TokenStream, input: TokenStream) -> TokenStream {
103    let mut f = parse_macro_input!(input as ItemFn);
104
105    // check the function arguments
106    if !f.sig.inputs.is_empty() {
107        return parse::Error::new(
108            f.sig.inputs.last().unwrap().span(),
109            "`#[interrupt]` function accepts no arguments",
110        )
111        .to_compile_error()
112        .into();
113    }
114
115    let ident = f.sig.ident.clone();
116    let ident_s = &ident.clone();
117
118    let valid_signature = f.sig.constness.is_none()
119        && f.vis == Visibility::Inherited
120        && f.sig.abi.is_none()
121        && f.sig.generics.params.is_empty()
122        && f.sig.generics.where_clause.is_none()
123        && f.sig.variadic.is_none()
124        && match f.sig.output {
125            ReturnType::Default => true,
126            ReturnType::Type(_, ref ty) => match **ty {
127                Type::Tuple(ref tuple) => tuple.elems.is_empty(),
128                Type::Never(..) => true,
129                _ => false,
130            },
131        }
132        && f.sig.inputs.len() <= 1;
133
134    if !valid_signature {
135        return parse::Error::new(
136            f.span(),
137            "`#[interrupt]` handlers must have signature `[unsafe] fn() [-> !]`",
138        )
139        .to_compile_error()
140        .into();
141    }
142
143    let inner_fn_export_name = format!("__ch32v_rt_internal_{}", f.sig.ident);
144    f.sig.ident = Ident::new(&inner_fn_export_name, proc_macro2::Span::call_site());
145
146    // let ident = &f.sig.ident;
147    let interrupt_name = ident_s.to_string();
148
149    //let attrs = f.attrs;
150    //let unsafety = f.sig.unsafety;
151
152    // global asm to add mret instruction at the end of the interrupt handler
153    let asm_src = format!(
154        r#"
155        .section .trap, "ax"
156        .global {0}
157    {0}:
158        addi sp, sp, -4
159        sw ra, 0(sp)
160        jal {1}
161        lw ra, 0(sp)
162        addi sp, sp, 4
163        mret
164    "#,
165        interrupt_name, inner_fn_export_name
166    );
167
168    quote!(
169        core::arch::global_asm!(#asm_src);
170
171        #[link_section = ".trap"]
172        #[export_name = #inner_fn_export_name]
173        #[allow(non_snake_case)]
174        #f
175    )
176    .into()
177}