xous-kernel/xous-riscv-rt/macros/src/lib.rs

211 lines
6.2 KiB
Rust

#![deny(warnings)]
extern crate proc_macro;
extern crate rand;
#[macro_use]
extern crate quote;
extern crate core;
extern crate proc_macro2;
#[macro_use]
extern crate syn;
use proc_macro2::Span;
use rand::Rng;
use rand::SeedableRng;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
use syn::{
parse, spanned::Spanned, Ident, ItemFn, ReturnType, Type, Visibility,
};
static CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
use proc_macro::TokenStream;
/// Attribute to declare the entry point of the program
///
/// **IMPORTANT**: This attribute must appear exactly *once* in the dependency graph. Also, if you
/// are using Rust 1.30 the attribute must be used on a reachable item (i.e. there must be no
/// private modules between the item and the root of the crate); if the item is in the root of the
/// crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31 and newer releases.
///
/// The specified function will be called by the reset handler *after* RAM has been initialized.
/// If present, the FPU will also be enabled before the function is called.
///
/// The type of the specified function must be `[unsafe] fn() -> !` (never ending function)
///
/// # Properties
///
/// The entry point will be called by the reset handler.
///
/// # Examples
///
/// - Simple entry point
///
/// ``` no_run
/// #![no_main]
/// use xous_riscv_rt::entry;
/// #[entry]
/// fn main() -> ! {
/// loop {
/// /* .. */
/// }
/// }
/// ```
#[proc_macro_attribute]
pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
let f = parse_macro_input!(input as ItemFn);
// check the function signature
let valid_signature = f.constness.is_none()
&& f.vis == Visibility::Inherited
&& f.abi.is_none()
&& f.decl.inputs.is_empty()
&& f.decl.generics.params.is_empty()
&& f.decl.generics.where_clause.is_none()
&& f.decl.variadic.is_none()
&& match f.decl.output {
ReturnType::Default => false,
ReturnType::Type(_, ref ty) => match **ty {
Type::Never(_) => true,
_ => false,
},
};
if !valid_signature {
return parse::Error::new(
f.span(),
"`#[entry]` function must have signature `[unsafe] fn() -> !`",
)
.to_compile_error()
.into();
}
if !args.is_empty() {
return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
.to_compile_error()
.into();
}
// XXX should we blacklist other attributes?
let attrs = f.attrs;
let unsafety = f.unsafety;
let hash = random_ident();
let stmts = f.block.stmts;
quote!(
#[export_name = "xous_main"]
#(#attrs)*
pub #unsafety fn #hash() -> ! {
#(#stmts)*
}
)
.into()
}
/// Attribute to mark which function will be called at the beginning of the reset handler.
///
/// **IMPORTANT**: This attribute can appear at most *once* in the dependency graph. Also, if you
/// are using Rust 1.30 the attribute must be used on a reachable item (i.e. there must be no
/// private modules between the item and the root of the crate); if the item is in the root of the
/// crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31 and newer
/// releases.
///
/// The function must have the signature of `unsafe fn()`.
///
/// The function passed will be called before static variables are initialized. Any access of static
/// variables will result in undefined behavior.
///
/// # Examples
///
/// ```
/// # use riscv_rt_macros::pre_init;
/// #[pre_init]
/// unsafe fn before_main() {
/// // do something here
/// }
///
/// # fn main() {}
/// ```
#[proc_macro_attribute]
pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream {
let f = parse_macro_input!(input as ItemFn);
// check the function signature
let valid_signature = f.constness.is_none()
&& f.vis == Visibility::Inherited
&& f.unsafety.is_some()
&& f.abi.is_none()
&& f.decl.inputs.is_empty()
&& f.decl.generics.params.is_empty()
&& f.decl.generics.where_clause.is_none()
&& f.decl.variadic.is_none()
&& match f.decl.output {
ReturnType::Default => true,
ReturnType::Type(_, ref ty) => match **ty {
Type::Tuple(ref tuple) => tuple.elems.is_empty(),
_ => false,
},
};
if !valid_signature {
return parse::Error::new(
f.span(),
"`#[pre_init]` function must have signature `unsafe fn()`",
)
.to_compile_error()
.into();
}
if !args.is_empty() {
return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
.to_compile_error()
.into();
}
// XXX should we blacklist other attributes?
let attrs = f.attrs;
let ident = f.ident;
let block = f.block;
quote!(
#[export_name = "__pre_init"]
#(#attrs)*
pub unsafe fn #ident() #block
)
.into()
}
// Creates a random identifier
fn random_ident() -> Ident {
let secs = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
let count: u64 = CALL_COUNT.fetch_add(1, Ordering::SeqCst) as u64;
let mut seed: [u8; 16] = [0; 16];
for (i, v) in seed.iter_mut().take(8).enumerate() {
*v = ((secs >> (i * 8)) & 0xFF) as u8
}
for (i, v) in seed.iter_mut().skip(8).enumerate() {
*v = ((count >> (i * 8)) & 0xFF) as u8
}
let mut rng = rand::rngs::SmallRng::from_seed(seed);
Ident::new(
&(0..16)
.map(|i| {
if i == 0 || rng.gen() {
('a' as u8 + rng.gen::<u8>() % 25) as char
} else {
('0' as u8 + rng.gen::<u8>() % 10) as char
}
})
.collect::<String>(),
Span::call_site(),
)
}