polyhal_macro/
lib.rs

1mod percpu;
2
3use proc_macro::TokenStream;
4use proc_macro2::Span;
5use quote::{format_ident, quote};
6use syn::{parse_macro_input, Error, ItemFn, ItemStatic};
7
8#[proc_macro_attribute]
9pub fn arch_entry(_input: TokenStream, annotated_item: TokenStream) -> TokenStream {
10    let annotated_item = parse_macro_input!(annotated_item as ItemFn);
11    TokenStream::from(quote! {
12        #[export_name = "_main_for_arch"]
13        #annotated_item
14    })
15}
16
17#[proc_macro_attribute]
18pub fn arch_interrupt(_input: TokenStream, annotated_item: TokenStream) -> TokenStream {
19    let annotated_item = parse_macro_input!(annotated_item as ItemFn);
20    TokenStream::from(quote! {
21        #[export_name = "_interrupt_for_arch"]
22        #annotated_item
23    })
24}
25
26/// Defines a per-CPU data structure.
27///
28/// It should be used on a `static` variable.
29///
30/// See the [crate-level documentation](../percpu/index.html) for more details.
31#[proc_macro_attribute]
32pub fn def_percpu(attr: TokenStream, item: TokenStream) -> TokenStream {
33    if !attr.is_empty() {
34        return compiler_error(Error::new(
35            Span::call_site(),
36            "expect an empty attribute: `#[def_percpu]`",
37        ));
38    }
39
40    let ast = syn::parse_macro_input!(item as ItemStatic);
41
42    let attrs = &ast.attrs;
43    let vis = &ast.vis;
44    let name = &ast.ident;
45    let ty = &ast.ty;
46    let init_expr = &ast.expr;
47
48    let inner_symbol_name = &format_ident!("__PERCPU_{}", name);
49    let struct_name = &format_ident!("{}_WRAPPER", name);
50
51    let ty_str = quote!(#ty).to_string();
52    let is_primitive_int = ["bool", "u8", "u16", "u32", "u64", "usize"].contains(&ty_str.as_str());
53
54    // Do not generate `fn read_current()`, `fn write_current()`, etc for non primitive types.
55    let read_write_methods = if is_primitive_int {
56        quote! {
57            /// Returns the value of the per-CPU data on the current CPU.
58            ///
59            /// # Safety
60            ///
61            /// Caller must ensure that preemption is disabled on the current CPU.
62            #[inline]
63            pub unsafe fn read_current_raw(&self) -> #ty {
64                unsafe { *self.current_ptr() }
65            }
66
67            /// Set the value of the per-CPU data on the current CPU.
68            ///
69            /// # Safety
70            ///
71            /// Caller must ensure that preemption is disabled on the current CPU.
72            #[inline]
73            pub unsafe fn write_current_raw(&self, val: #ty) {
74                unsafe { *self.current_ref_mut_raw() = val };
75            }
76
77            /// Returns the value of the per-CPU data on the current CPU. Preemption will
78            /// be disabled during the call.
79            pub fn read_current(&self) -> #ty {
80                unsafe { self.read_current_raw() }
81            }
82
83            /// Set the value of the per-CPU data on the current CPU. Preemption will
84            /// be disabled during the call.
85            pub fn write_current(&self, val: #ty) {
86                unsafe { self.write_current_raw(val) }
87            }
88        }
89    } else {
90        quote! {}
91    };
92
93    let current_ptr = percpu::gen_current_ptr(inner_symbol_name, ty);
94    quote! {
95        #[cfg_attr(not(target_os = "macos"), link_section = "percpu")] // unimplemented on macos
96        #[used(linker)]
97        #(#attrs)*
98        static mut #inner_symbol_name: #ty = #init_expr;
99
100        #[doc = concat!("Wrapper struct for the per-CPU data [`", stringify!(#name), "`]")]
101        #[allow(non_camel_case_types)]
102        #vis struct #struct_name {}
103
104        #(#attrs)*
105        #vis static #name: #struct_name = #struct_name {};
106
107        impl #struct_name {
108            /// Returns the offset relative to the per-CPU data area base on the current CPU.
109            #[inline]
110            pub fn offset(&self) -> usize {
111                extern "Rust" {
112                    fn __start_percpu();
113                }
114                unsafe {
115                    &#inner_symbol_name as *const _ as usize - __start_percpu as usize
116                }
117            }
118
119            /// Returns the raw pointer of this per-CPU data on the current CPU.
120            ///
121            /// # Safety
122            ///
123            /// Caller must ensure that preemption is disabled on the current CPU.
124            #[inline]
125            pub unsafe fn current_ptr(&self) -> *const #ty {
126                #current_ptr
127            }
128
129            /// Returns the reference of the per-CPU data on the current CPU.
130            ///
131            /// # Safety
132            ///
133            /// Caller must ensure that preemption is disabled on the current CPU.
134            #[inline]
135            pub unsafe fn current_ref_raw(&self) -> &#ty {
136                &*self.current_ptr()
137            }
138
139            /// Returns the mutable reference of the per-CPU data on the current CPU.
140            ///
141            /// # Safety
142            ///
143            /// Caller must ensure that preemption is disabled on the current CPU.
144            #[inline]
145            #[allow(clippy::mut_from_ref)]
146            pub unsafe fn current_ref_mut_raw(&self) -> &mut #ty {
147                &mut *(self.current_ptr() as *mut #ty)
148            }
149
150            /// Manipulate the per-CPU data on the current CPU in the given closure.
151            /// Preemption will be disabled during the call.
152            pub fn with_current<F, T>(&self, f: F) -> T
153            where
154                F: FnOnce(&mut #ty) -> T,
155            {
156                f(unsafe { self.current_ref_mut_raw() })
157            }
158
159            #read_write_methods
160        }
161    }
162    .into()
163}
164
165#[proc_macro]
166pub fn define_arch_mods(_input: TokenStream) -> TokenStream {
167    quote! {
168        #[cfg(target_arch = "riscv64")]
169        mod riscv64;
170        #[cfg(target_arch = "riscv64")]
171        #[allow(unused_imports)]
172        pub use riscv64::*;
173        #[cfg(target_arch = "aarch64")]
174        mod aarch64;
175        #[cfg(target_arch = "aarch64")]
176        #[allow(unused_imports)]
177        pub use aarch64::*;
178        #[cfg(target_arch = "x86_64")]
179        mod x86_64;
180        #[cfg(target_arch = "x86_64")]
181        #[allow(unused_imports)]
182        pub use x86_64::*;
183        #[cfg(target_arch = "loongarch64")]
184        mod loongarch64;
185        #[cfg(target_arch = "loongarch64")]
186        #[allow(unused_imports)]
187        pub use loongarch64::*;
188    }
189    .into()
190}
191
192fn compiler_error(err: Error) -> TokenStream {
193    err.to_compile_error().into()
194}