123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- #![deny(warnings)]
- extern crate proc_macro;
- #[macro_use]
- extern crate quote;
- extern crate core;
- extern crate proc_macro2;
- #[macro_use]
- extern crate syn;
- use proc_macro2::Span;
- use syn::{
- parse::{self, Parse},
- spanned::Spanned,
- FnArg, ItemFn, LitInt, LitStr, PathArguments, ReturnType, Type, Visibility,
- };
- 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. 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 arguments
- if f.sig.inputs.len() > 3 {
- return parse::Error::new(
- f.sig.inputs.last().unwrap().span(),
- "`#[entry]` function has too many arguments",
- )
- .to_compile_error()
- .into();
- }
- for arg in &f.sig.inputs {
- match arg {
- FnArg::Receiver(_) => {
- return parse::Error::new(arg.span(), "invalid argument")
- .to_compile_error()
- .into();
- }
- FnArg::Typed(t) => {
- if !is_simple_type(&t.ty, "usize") {
- return parse::Error::new(t.ty.span(), "argument type must be usize")
- .to_compile_error()
- .into();
- }
- }
- }
- }
- // check the function signature
- let valid_signature = f.sig.constness.is_none()
- && f.sig.asyncness.is_none()
- && f.vis == Visibility::Inherited
- && f.sig.abi.is_none()
- && f.sig.generics.params.is_empty()
- && f.sig.generics.where_clause.is_none()
- && f.sig.variadic.is_none()
- && match f.sig.output {
- ReturnType::Default => false,
- ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)),
- };
- if !valid_signature {
- return parse::Error::new(
- f.span(),
- "`#[entry]` function must have signature `[unsafe] fn([arg0: usize, ...]) -> !`",
- )
- .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.sig.unsafety;
- let args = f.sig.inputs;
- let stmts = f.block.stmts;
- quote!(
- #[allow(non_snake_case)]
- #[export_name = "main"]
- #(#attrs)*
- pub #unsafety fn __risc_v_rt__main(#args) -> ! {
- #(#stmts)*
- }
- )
- .into()
- }
- #[allow(unused)]
- fn is_simple_type(ty: &Type, name: &str) -> bool {
- if let Type::Path(p) = ty {
- if p.qself.is_none() && p.path.leading_colon.is_none() && p.path.segments.len() == 1 {
- let segment = p.path.segments.first().unwrap();
- if segment.ident == name && segment.arguments == PathArguments::None {
- return true;
- }
- }
- }
- false
- }
- /// 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.sig.constness.is_none()
- && f.sig.asyncness.is_none()
- && f.vis == Visibility::Inherited
- && f.sig.unsafety.is_some()
- && f.sig.abi.is_none()
- && f.sig.inputs.is_empty()
- && f.sig.generics.params.is_empty()
- && f.sig.generics.where_clause.is_none()
- && f.sig.variadic.is_none()
- && match f.sig.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.sig.ident;
- let block = f.block;
- quote!(
- #[export_name = "__pre_init"]
- #(#attrs)*
- pub unsafe fn #ident() #block
- )
- .into()
- }
- struct AsmLoopArgs {
- asm_template: String,
- count: usize,
- }
- impl Parse for AsmLoopArgs {
- fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
- let template: LitStr = input.parse().unwrap();
- _ = input.parse::<Token![,]>().unwrap();
- let count: LitInt = input.parse().unwrap();
- Ok(Self {
- asm_template: template.value(),
- count: count.base10_parse().unwrap(),
- })
- }
- }
- /// Loops an asm expression n times.
- ///
- /// `loop_asm!` takes 2 arguments, the first is a string literal and the second is a number literal
- /// See [the formatting syntax documentation in `std::fmt`](../std/fmt/index.html)
- /// for details.
- ///
- /// Argument 1 is an assembly expression, all "{}" in this assembly expression will be replaced with the
- /// current loop index.
- ///
- /// Argument 2 is the number of loops to do with the provided expression.
- ///
- /// # Examples
- ///
- /// ```
- /// # use riscv_rt_macros::loop_asm;
- /// unsafe {
- /// loop_asm!("fmv.w.x f{}, x0", 32); // => core::arch::asm!("fmv.w.x f0, x0") ... core::arch::asm!("fmv.w.x f31, x0")
- /// }
- /// ```
- #[proc_macro]
- pub fn loop_asm(input: TokenStream) -> TokenStream {
- let args = parse_macro_input!(input as AsmLoopArgs);
- let tokens = (0..args.count)
- .map(|i| {
- let i = i.to_string();
- let asm = args.asm_template.replace("{}", &i);
- format!("core::arch::asm!(\"{}\");", asm)
- })
- .collect::<Vec<String>>()
- .join("\n");
- tokens.parse().unwrap()
- }
|