|
@@ -0,0 +1,212 @@
|
|
|
+#![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. In
|
|
|
+/// the case of the `thumbv7em-none-eabihf` target 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. The program can't reference to the entry
|
|
|
+/// point, much less invoke it.
|
|
|
+///
|
|
|
+/// # Examples
|
|
|
+///
|
|
|
+/// - Simple entry point
|
|
|
+///
|
|
|
+/// ``` no_run
|
|
|
+/// # #![no_main]
|
|
|
+/// # use riscv_rt_macros::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 = "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(),
|
|
|
+ )
|
|
|
+}
|