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 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 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 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#[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)] };
92
93 quote!(
94 #section
95 #f
96 )
97 .into()
98}
99
100#[proc_macro_attribute]
102pub fn interrupt(_args: TokenStream, input: TokenStream) -> TokenStream {
103 let mut f = parse_macro_input!(input as ItemFn);
104
105 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 interrupt_name = ident_s.to_string();
148
149 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}