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 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 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 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#[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)] };
95
96 quote!(
97 #section
98 #f
99 )
100 .into()
101}
102
103#[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 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 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 if is_core_irq {
201 f.block.stmts = iter::once(
202 syn::parse2(quote! {{
203 ::qingke_rt::CoreInterrupt::#ident;
205 }})
206 .unwrap(),
207 )
208 .chain(stmts)
209 .collect();
210 } else {
211 }
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}