use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::{format_ident, quote};
use syn::{Error, ItemStatic};
#[cfg_attr(feature = "sp-naive", path = "naive.rs")]
mod arch;
fn compiler_error(err: Error) -> TokenStream {
err.to_compile_error().into()
}
#[proc_macro_attribute]
pub fn def_percpu(attr: TokenStream, item: TokenStream) -> TokenStream {
if !attr.is_empty() {
return compiler_error(Error::new(
Span::call_site(),
"expect an empty attribute: `#[def_percpu]`",
));
}
let ast = syn::parse_macro_input!(item as ItemStatic);
let attrs = &ast.attrs;
let vis = &ast.vis;
let name = &ast.ident;
let ty = &ast.ty;
let init_expr = &ast.expr;
let inner_symbol_name = &format_ident!("__PERCPU_{}", name);
let struct_name = &format_ident!("{}_WRAPPER", name);
let ty_str = quote!(#ty).to_string();
let is_primitive_int = ["bool", "u8", "u16", "u32", "u64", "usize"].contains(&ty_str.as_str());
let no_preempt_guard = if cfg!(feature = "preempt") {
quote! { let _guard = percpu::__priv::NoPreemptGuard::new(); }
} else {
quote! {}
};
let read_write_methods = if is_primitive_int {
let read_current_raw = arch::gen_read_current_raw(inner_symbol_name, ty);
let write_current_raw =
arch::gen_write_current_raw(inner_symbol_name, &format_ident!("val"), ty);
quote! {
#[inline]
pub unsafe fn read_current_raw(&self) -> #ty {
#read_current_raw
}
#[inline]
pub unsafe fn write_current_raw(&self, val: #ty) {
#write_current_raw
}
pub fn read_current(&self) -> #ty {
#no_preempt_guard
unsafe { self.read_current_raw() }
}
pub fn write_current(&self, val: #ty) {
#no_preempt_guard
unsafe { self.write_current_raw(val) }
}
}
} else {
quote! {}
};
let symbol_vma = arch::gen_symbol_vma(inner_symbol_name);
let offset = arch::gen_offset(inner_symbol_name);
let current_ptr = arch::gen_current_ptr(inner_symbol_name, ty);
quote! {
#[cfg_attr(not(target_os = "macos"), link_section = ".percpu")] #(#attrs)*
static mut #inner_symbol_name: #ty = #init_expr;
#[doc = concat!("Wrapper struct for the per-CPU data [`", stringify!(#name), "`]")]
#[allow(non_camel_case_types)]
#vis struct #struct_name {}
#(#attrs)*
#vis static #name: #struct_name = #struct_name {};
impl #struct_name {
#[inline]
pub fn symbol_vma(&self) -> usize {
#symbol_vma
}
#[inline]
pub fn offset(&self) -> usize {
#offset
}
#[inline]
pub unsafe fn current_ptr(&self) -> *const #ty {
#current_ptr
}
#[inline]
pub unsafe fn current_ref_raw(&self) -> &#ty {
&*self.current_ptr()
}
#[inline]
#[allow(clippy::mut_from_ref)]
pub unsafe fn current_ref_mut_raw(&self) -> &mut #ty {
&mut *(self.current_ptr() as *mut #ty)
}
pub fn with_current<F, T>(&self, f: F) -> T
where
F: FnOnce(&mut #ty) -> T,
{
#no_preempt_guard
f(unsafe { self.current_ref_mut_raw() })
}
pub fn reset_to_init(&self) {
self.with_current(|val| unsafe {
*val = #init_expr;
});
}
#[inline]
pub unsafe fn remote_ptr(&self, cpu_id: usize) -> *const #ty {
let base = percpu::percpu_area_base(cpu_id);
let offset = #offset;
(base + offset) as *const #ty
}
#[inline]
pub unsafe fn remote_ref_raw(&self, cpu_id: usize) -> &#ty {
&*self.remote_ptr(cpu_id)
}
#[inline]
#[allow(clippy::mut_from_ref)]
pub unsafe fn remote_ref_mut_raw(&self, cpu_id: usize) -> &mut #ty {
&mut *(self.remote_ptr(cpu_id) as *mut #ty)
}
#read_write_methods
}
}
.into()
}
#[doc(hidden)]
#[cfg(not(feature = "sp-naive"))]
#[proc_macro]
pub fn percpu_symbol_vma(item: TokenStream) -> TokenStream {
let symbol = &format_ident!("{}", item.to_string());
let offset = arch::gen_symbol_vma(symbol);
quote!({ #offset }).into()
}