qingke_rt_macros/
lib.rs

1use std::iter;
2
3use proc_macro2::Span;
4use proc_macro_error::proc_macro_error;
5use syn::{
6    parse, parse_macro_input, spanned::Spanned, Ident, ItemFn, PathArguments, ReturnType, Type,
7    Visibility,
8};
9
10use proc_macro::TokenStream;
11use quote::quote;
12
13#[proc_macro_attribute]
14pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
15    let f = parse_macro_input!(input as ItemFn);
16
17    // check the function arguments
18    if !f.sig.inputs.is_empty() {
19        return parse::Error::new(
20            f.sig.inputs.last().unwrap().span(),
21            "`#[entry]` function accepts no arguments",
22        )
23        .to_compile_error()
24        .into();
25    }
26
27    // check the function signature
28    let valid_signature = f.sig.constness.is_none()
29        && f.sig.asyncness.is_none()
30        && f.vis == Visibility::Inherited
31        && f.sig.abi.is_none()
32        && f.sig.generics.params.is_empty()
33        && f.sig.generics.where_clause.is_none()
34        && f.sig.variadic.is_none()
35        && match f.sig.output {
36            ReturnType::Default => false,
37            ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)),
38        };
39
40    if !valid_signature {
41        return parse::Error::new(
42            f.span(),
43            "`#[entry]` function must have signature `[unsafe] fn() -> !`",
44        )
45        .to_compile_error()
46        .into();
47    }
48
49    if !args.is_empty() {
50        return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
51            .to_compile_error()
52            .into();
53    }
54
55    // XXX should we blacklist other attributes?
56    let attrs = f.attrs;
57    let unsafety = f.sig.unsafety;
58    let args = f.sig.inputs;
59    let stmts = f.block.stmts;
60
61    quote!(
62        #[allow(non_snake_case)]
63        #[unsafe(export_name = "main")]
64        #(#attrs)*
65        pub #unsafety fn __risc_v_rt__main(#args) -> ! {
66            #(#stmts)*
67        }
68    )
69    .into()
70}
71
72#[allow(unused)]
73fn is_simple_type(ty: &Type, name: &str) -> bool {
74    if let Type::Path(p) = ty {
75        if p.qself.is_none() && p.path.leading_colon.is_none() && p.path.segments.len() == 1 {
76            let segment = p.path.segments.first().unwrap();
77            if segment.ident == name && segment.arguments == PathArguments::None {
78                return true;
79            }
80        }
81    }
82    false
83}
84
85/// This attribute allows placing functions into ram.
86#[proc_macro_attribute]
87#[proc_macro_error]
88pub fn highcode(_args: TokenStream, input: TokenStream) -> TokenStream {
89    let f = parse_macro_input!(input as ItemFn);
90
91    let section = quote! {
92        #[unsafe(link_section = ".highcode")]
93        #[inline(never)] // make certain function is not inlined
94    };
95
96    quote!(
97        #section
98        #f
99    )
100    .into()
101}
102
103/// Marks a function as an interrupt handler. (Wrapping as a mret function)
104///
105/// Note that Rust has also introduced the `riscv-interrupt-m` and `riscv-interrupt-s` ABI, which
106/// are used for machine and supervisor mode interrupts, respectively. These ABIs can also be used for
107/// Qingke cores, yet they add additional register saving and restoring that is not necessary.
108///
109/// Usage:
110/// ```ignore
111/// #[interrupt]
112/// fn UART0() { ... }
113///
114/// #[interrupt(core)]
115/// fn SysTick() { ... }
116/// ```
117#[proc_macro_attribute]
118pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
119    use syn::{AttributeArgs, Meta, NestedMeta};
120
121    let mut f = parse_macro_input!(input as ItemFn);
122
123    let is_core_irq = if args.is_empty() {
124        false
125    } else {
126        let args: AttributeArgs = parse_macro_input!(args as AttributeArgs);
127        if args.len() != 1 {
128            return parse::Error::new(
129                Span::call_site(),
130                "This attribute accepts no arguments or a single 'core' argument",
131            )
132            .to_compile_error()
133            .into();
134        }
135
136        if let NestedMeta::Meta(Meta::Path(ref p)) = args[0] {
137            if let Some(ident) = p.get_ident() {
138                if ident != "core" {
139                    return parse::Error::new(
140                        ident.span(),
141                        "Only core interrupts are allowed without arguments",
142                    )
143                    .to_compile_error()
144                    .into();
145                }
146            }
147        }
148        true
149    };
150
151    // check the function arguments
152    if !f.sig.inputs.is_empty() {
153        return parse::Error::new(
154            f.sig.inputs.last().unwrap().span(),
155            "`#[interrupt]` function accepts no arguments",
156        )
157        .to_compile_error()
158        .into();
159    }
160
161    let ident = f.sig.ident.clone();
162    let interrupt = ident.to_string();
163
164    let valid_signature = f.sig.constness.is_none()
165        && f.vis == Visibility::Inherited
166        && f.sig.abi.is_none()
167        && f.sig.generics.params.is_empty()
168        && f.sig.generics.where_clause.is_none()
169        && f.sig.variadic.is_none()
170        && match f.sig.output {
171            ReturnType::Default => true,
172            ReturnType::Type(_, ref ty) => match **ty {
173                Type::Tuple(ref tuple) => tuple.elems.is_empty(),
174                Type::Never(..) => true,
175                _ => false,
176            },
177        }
178        && f.sig.inputs.len() <= 1;
179
180    if !valid_signature {
181        return parse::Error::new(
182            f.span(),
183            "`#[interrupt]` handlers must have signature `[unsafe] fn() [-> !]`",
184        )
185        .to_compile_error()
186        .into();
187    }
188
189    let ident = f.sig.ident.clone();
190
191    // This will be overwritten by `export_name` in linking process, i.e. name of the interrupt
192    // let wrapper_ident = Ident::new(&format!("{}_naked_wrapper", f.sig.ident), Span::call_site());
193
194    f.sig.ident = Ident::new(&format!("__qingke_rt_{}", f.sig.ident), Span::call_site());
195
196    let wrapped_ident = &f.sig.ident;
197
198    let stmts = f.block.stmts.clone();
199    // check irq names
200    if is_core_irq {
201        f.block.stmts = iter::once(
202            syn::parse2(quote! {{
203                // Check that this interrupt actually exists
204                ::qingke_rt::CoreInterrupt::#ident;
205            }})
206            .unwrap(),
207        )
208        .chain(stmts)
209        .collect();
210    } else {
211        // FIXME: how to test irq names?
212        /*  f.block.stmts = iter::once(
213                syn::parse2(quote! {{
214                    // Check that this interrupt actually exists
215                    ::pac::interrupt::#ident;
216                }})
217                .unwrap(),
218            )
219            .chain(stmts)
220            .collect();
221        */
222    }
223
224    let wrapped_name = wrapped_ident.to_string();
225
226    let start_interrupt = format!(
227        r#"
228core::arch::global_asm!(
229    ".section .trap, \"ax\"
230    .align 2
231    .global {interrupt}
232    {interrupt}:
233        addi sp, sp, -4
234        sw ra, 0(sp)
235        jal {wrapped_name}
236        lw ra, 0(sp)
237        addi sp, sp, 4
238        mret
239");"#
240    );
241
242    let start_interrupt_asm: proc_macro2::TokenStream = start_interrupt.parse().unwrap();
243
244    quote!(
245        #start_interrupt_asm
246
247        #[allow(non_snake_case)]
248        #[unsafe(no_mangle)]
249        #[unsafe(link_section = ".trap.rust")]
250        #f
251    )
252    .into()
253}