1use proc_macro::TokenStream;
63use proc_macro2::Span;
64use quote::{format_ident, quote};
65use syn::{Attribute, Error, ItemStatic};
66
67mod address;
68
69fn compiler_error(err: Error) -> TokenStream {
70 err.to_compile_error().into()
71}
72
73fn def_percpu_impl(attr: TokenStream, item: TokenStream) -> TokenStream {
74 if !attr.is_empty() {
75 return compiler_error(Error::new(
76 Span::call_site(),
77 "expect an empty attribute: `#[def_percpu]`",
78 ));
79 }
80
81 let ast = syn::parse_macro_input!(item as ItemStatic);
82
83 let attrs = &ast.attrs;
84 let vis = &ast.vis;
85 let name = &ast.ident;
86 let ty = &ast.ty;
87 let init_expr = &ast.expr;
88
89 let inner_symbol_name = &format_ident!("__PERCPU_{}", name);
90 let alignment_descriptor_name = &format_ident!("__PERCPU_{}_ALIGNMENT", name);
91 let initial_value_name = &format_ident!("__PERCPU_{}_INITIAL_VALUE", name);
92 let initializer_name = &format_ident!("__PERCPU_{}_INITIALIZE", name);
93 let descriptor_name = &format_ident!("__PERCPU_{}_DESCRIBE", name);
94 let registration_name = &format_ident!("__PERCPU_{}_REGISTRATION", name);
95 let symbol_provider_name = &format_ident!("__PERCPU_{}_SYMBOL", name);
96 let struct_name = &format_ident!("{}_WRAPPER", name);
97 let conditional_attrs = conditional_attributes(attrs);
98
99 let ty_str = quote!(#ty).to_string();
100 let is_primitive_int = ["bool", "u8", "u16", "u32", "u64", "usize"].contains(&ty_str.as_str());
101
102 let (access_trait, storage_type, initial_value) = if is_primitive_int {
103 let atomic_ty = match ty_str.as_str() {
104 "bool" => quote!(::core::sync::atomic::AtomicBool),
105 "u8" => quote!(::core::sync::atomic::AtomicU8),
106 "u16" => quote!(::core::sync::atomic::AtomicU16),
107 "u32" => quote!(::core::sync::atomic::AtomicU32),
108 "u64" => quote!(::core::sync::atomic::AtomicU64),
109 "usize" => quote!(::core::sync::atomic::AtomicUsize),
110 _ => unreachable!("primitive type classification must stay exhaustive"),
111 };
112 (
113 quote!(ax_percpu::__priv::PerCpuPrimitiveSymbol),
114 atomic_ty.clone(),
115 quote!(#atomic_ty::new(#init_expr)),
116 )
117 } else {
118 (
119 quote!(ax_percpu::__priv::PerCpuObjectSymbol),
120 quote!(#ty),
121 quote!(#init_expr),
122 )
123 };
124
125 let storage_definition = quote! {
126 static mut #inner_symbol_name: ::core::mem::MaybeUninit<#storage_type> =
127 ::core::mem::MaybeUninit::uninit();
128 };
129
130 let offset = address::gen_offset(inner_symbol_name);
131 let current_ptr_pinned =
132 address::gen_current_ptr_pinned(inner_symbol_name, &format_ident!("pin"), ty);
133 let remote_ptr = address::gen_remote_ptr(inner_symbol_name, &format_ident!("area"), ty);
134 let initialization = quote! {
135 #(#conditional_attrs)*
136 #[allow(non_upper_case_globals)]
137 const #initial_value_name: #storage_type = #initial_value;
138
139 #(#conditional_attrs)*
140 #[allow(non_snake_case)]
141 unsafe extern "C" fn #initializer_name(destination: *mut u8) {
142 let destination = destination.cast::<::core::mem::MaybeUninit<#storage_type>>();
143 unsafe {
147 destination.write(::core::mem::MaybeUninit::new(#initial_value_name));
148 }
149 }
150
151 #(#conditional_attrs)*
152 #[allow(non_snake_case)]
153 unsafe extern "C" fn #descriptor_name() -> ax_percpu::__priv::PerCpuInitDescriptor {
154 let storage_address =
155 ::core::ptr::addr_of!(#inner_symbol_name).cast::<u8>() as usize;
156 unsafe {
159 ax_percpu::__priv::PerCpuInitDescriptor::new(
160 storage_address,
161 ::core::mem::size_of::<#storage_type>(),
162 ::core::mem::align_of::<#storage_type>(),
163 #initializer_name,
164 )
165 }
166 }
167
168 #(#conditional_attrs)*
169 #[cfg_attr(
170 not(target_os = "macos"),
171 unsafe(link_section = ".percpu.init")
172 )]
173 #[used]
174 static #registration_name: ax_percpu::__priv::PerCpuInitRegistration =
175 unsafe { ax_percpu::__priv::PerCpuInitRegistration::new(#descriptor_name) };
179 };
180
181 quote! {
182 #[cfg_attr(
183 not(target_os = "macos"),
184 unsafe(link_section = ".percpu.align")
185 )]
186 #[used]
187 static #alignment_descriptor_name: usize = ::core::mem::align_of::<#storage_type>();
188
189 #[cfg_attr(
190 not(target_os = "macos"),
191 unsafe(link_section = ".percpu.template.storage")
192 )]
193 #(#attrs)*
194 #storage_definition
195
196 #initialization
197
198 #[doc(hidden)]
199 #[allow(non_camel_case_types)]
200 #(#conditional_attrs)*
201 #vis struct #symbol_provider_name;
202
203 #(#conditional_attrs)*
206 unsafe impl ax_percpu::__priv::PerCpuSymbol<#ty> for #symbol_provider_name {
207 #[inline]
208 fn offset() -> usize {
209 #offset
210 }
211
212 #[inline]
213 fn current_ptr(pin: &ax_percpu::CpuPin<'_>) -> ::core::ptr::NonNull<#ty> {
214 #current_ptr_pinned
215 }
216
217 #[inline]
218 fn remote_ptr(area: ax_percpu::PerCpuArea) -> ::core::ptr::NonNull<#ty> {
219 #remote_ptr
220 }
221 }
222
223 #(#conditional_attrs)*
226 unsafe impl #access_trait<#ty> for #symbol_provider_name {}
227
228 #[doc = concat!("Wrapper type for the per-CPU data [`", stringify!(#name), "`]")]
229 #[allow(non_camel_case_types)]
230 #(#conditional_attrs)*
231 #vis type #struct_name = ax_percpu::PerCpu<#ty, #symbol_provider_name>;
232
233 #(#attrs)*
234 #vis static #name: #struct_name = ax_percpu::PerCpu::new();
235 }
236 .into()
237}
238
239fn conditional_attributes(attrs: &[Attribute]) -> Vec<&Attribute> {
240 attrs
241 .iter()
242 .filter(|attribute| {
243 attribute.path().is_ident("cfg") || attribute.path().is_ident("cfg_attr")
244 })
245 .collect()
246}
247#[proc_macro_attribute]
253pub fn def_percpu(attr: TokenStream, item: TokenStream) -> TokenStream {
254 def_percpu_impl(attr, item)
255}