From 044d4c07d8f83485027cc266712af33c63ec8d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas=20Rodr=C3=ADguez?= Date: Mon, 2 Feb 2026 11:30:59 +0100 Subject: [PATCH 1/5] riscv-macros: add rvrt_llvm_arch_patch and rvrt-pre-default-start-trap macros for riscv-rt --- riscv-macros/CHANGELOG.md | 11 +- riscv-macros/Cargo.toml | 7 +- riscv-macros/src/lib.rs | 48 ++++++++ riscv-macros/src/riscv_rt.rs | 1 + riscv-macros/src/riscv_rt/asm.rs | 202 +++++++++++++++++++++++++++++++ riscv-rt/CHANGELOG.md | 6 +- riscv-rt/Cargo.toml | 9 +- riscv-rt/src/asm.rs | 7 +- 8 files changed, 278 insertions(+), 13 deletions(-) create mode 100644 riscv-macros/src/riscv_rt.rs create mode 100644 riscv-macros/src/riscv_rt/asm.rs diff --git a/riscv-macros/CHANGELOG.md b/riscv-macros/CHANGELOG.md index 4dd05aaf..44a31ffa 100644 --- a/riscv-macros/CHANGELOG.md +++ b/riscv-macros/CHANGELOG.md @@ -5,7 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## [Unreleased] +## v0.4.1 - Unreleased + +### Added + +- New `rvrt_llvm_arch_patch` and `rvrt_default_start_trap` for generating assembly code + required by the `riscv-rt` crate. +- New `riscv-rt` feature to opt-in `riscv-rt`-related macros. +- New `rvrt-pre-default-start-trap` feature to opt-in assembly injection at the + beginning of `_default_start_trap`. +- New `s-mode` feature to adapt macros to S-Mode execution. ## v0.4.0 - 2025-12-19 diff --git a/riscv-macros/Cargo.toml b/riscv-macros/Cargo.toml index 483cc242..71c4057c 100644 --- a/riscv-macros/Cargo.toml +++ b/riscv-macros/Cargo.toml @@ -9,7 +9,7 @@ keywords = ["riscv", "register", "peripheral"] license = "MIT OR Apache-2.0" name = "riscv-macros" repository = "https://github.com/rust-embedded/riscv" -version = "0.4.0" +version = "0.4.1" edition = "2021" [lib] @@ -18,8 +18,11 @@ proc-macro = true [features] rt = [] rt-v-trap = ["rt"] +riscv-rt = ["rt", "syn/extra-traits", "syn/full"] +rvrt-pre-default-start-trap = ["riscv-rt"] +s-mode = [] [dependencies] proc-macro2 = "1.0" quote = "1.0" -syn = { version = "2.0" } +syn = "2.0" diff --git a/riscv-macros/src/lib.rs b/riscv-macros/src/lib.rs index b1534601..bb7a626d 100644 --- a/riscv-macros/src/lib.rs +++ b/riscv-macros/src/lib.rs @@ -4,6 +4,9 @@ use syn::{parse_macro_input, DeriveInput}; mod riscv; +#[cfg(feature = "riscv-rt")] +mod riscv_rt; + /// Attribute-like macro that implements the traits of the `riscv-types` crate for a given enum. /// /// As these traits are unsafe, the macro must be called with the `unsafe` keyword followed by the trait name. @@ -59,3 +62,48 @@ pub fn pac_enum(attr: TokenStream, item: TokenStream) -> TokenStream { } .into() } + +/// Temporary patch macro to deal with LLVM bug. +/// +/// # Note +/// +/// This macro is intended to be used internally by the `riscv-rt` crate. Do not use it directly in your code. +#[cfg(feature = "riscv-rt")] +#[proc_macro] +pub fn rvrt_llvm_arch_patch(_input: TokenStream) -> TokenStream { + let q = if let Ok(arch) = std::env::var("RISCV_RT_LLVM_ARCH_PATCH") { + let patch = format!(".attribute arch,\"{arch}\""); + quote! { core::arch::global_asm!{#patch} } + } else { + quote!(compile_error!("RISCV_RT_LLVM_ARCH_PATCH is not set")) + }; + q.into() +} + +/// Generates assembly code required for the default handling of traps. +/// +/// The main routine generated is `_default_start_trap`. If no `_start_trap` function +/// is defined, the linker will use this function as the default trap entry point. +/// +/// If the `pre-default-start-trap` feature is enabled, the generated code will also +/// include a call to a user-defined function `_pre_default_start_trap` at the beginning +/// of the `_default_start_trap` routine. +/// +/// If the `rt-v-trap` feature is enabled, the macro will also include the assembly code +/// for the `_start_DefaultInterrupt_trap` and `_continue_interrupt_trap` routines, which +/// are required for handling core interrupts in vectored trap mode. +/// +/// # Note +/// +/// This macro is intended to be used internally by the `riscv-rt` crate. Do not use it directly in your code. +#[cfg(feature = "riscv-rt")] +#[proc_macro] +pub fn rvrt_default_start_trap(_input: TokenStream) -> TokenStream { + match riscv_rt::asm::RiscvArch::try_from_env() { + Some(arch) => arch.default_start_trap().into(), + None => quote! { + compile_error!("RISCV_RT_BASE_ISA environment variable is not set or is invalid"); + } + .into(), + } +} diff --git a/riscv-macros/src/riscv_rt.rs b/riscv-macros/src/riscv_rt.rs new file mode 100644 index 00000000..a15ceb88 --- /dev/null +++ b/riscv-macros/src/riscv_rt.rs @@ -0,0 +1 @@ +pub mod asm; diff --git a/riscv-macros/src/riscv_rt/asm.rs b/riscv-macros/src/riscv_rt/asm.rs new file mode 100644 index 00000000..09874bb1 --- /dev/null +++ b/riscv-macros/src/riscv_rt/asm.rs @@ -0,0 +1,202 @@ +#![allow(unknown_lints)] // reason = "required for next line" +#![allow(clippy::manual_is_multiple_of)] // reason = "requires MSRV bump (1.87+)" + +use proc_macro2::{Span, TokenStream}; +use syn::{ + parse::{Parse, ParseStream}, + Error, Ident, Result, +}; + +/// Represents a base RISC-V architecture variant. +#[derive(Clone, Copy, Debug)] +pub enum RiscvArch { + Rv32I, + Rv32E, + Rv64I, + Rv64E, +} + +impl Parse for RiscvArch { + fn parse(input: ParseStream) -> Result { + let ident: Ident = input.parse()?; + match ident.to_string().as_str() { + "rv32i" => Ok(Self::Rv32I), + "rv32e" => Ok(Self::Rv32E), + "rv64i" => Ok(Self::Rv64I), + "rv64e" => Ok(Self::Rv64E), + _ => Err(Error::new(ident.span(), "Invalid RISC-V architecture")), + } + } +} + +impl RiscvArch { + /// Attempts to create a `RiscvArch` from the `RISCV_RT_BASE_ISA` environment variable. + /// + /// Returns `None` if the variable is not set or has an invalid value. + pub fn try_from_env() -> Option { + let arch = std::env::var("RISCV_RT_BASE_ISA").ok()?; + match arch.as_str() { + "rv32i" => Some(Self::Rv32I), + "rv32e" => Some(Self::Rv32E), + "rv64i" => Some(Self::Rv64I), + "rv64e" => Some(Self::Rv64E), + _ => None, + } + } + + /// Returns the register width in bytes for the architecture. + pub const fn width(&self) -> usize { + match self { + Self::Rv32I | Self::Rv32E => 4, + Self::Rv64I | Self::Rv64E => 8, + } + } + + /// Returns the store instruction for the architecture. + pub const fn store(&self) -> &str { + match self { + Self::Rv32I | Self::Rv32E => "sw", + Self::Rv64I | Self::Rv64E => "sd", + } + } + + /// Returns the load instruction for the architecture. + pub const fn load(&self) -> &str { + match self { + Self::Rv32I | Self::Rv32E => "lw", + Self::Rv64I | Self::Rv64E => "ld", + } + } + + /// Returns a sorted list of registers to be saved/restored in the trap frame. + pub fn trap_frame(&self) -> Vec<&str> { + match self { + Self::Rv32I | Self::Rv64I => vec![ + "ra", "t0", "t1", "t2", "t3", "t4", "t5", "t6", "a0", "a1", "a2", "a3", "a4", "a5", + "a6", "a7", + ], + Self::Rv32E | Self::Rv64E => { + vec!["ra", "t0", "t1", "t2", "a0", "a1", "a2", "a3", "a4", "a5"] + } + } + } + + /// Standard RISC-V ABI requires the stack to be 16-byte aligned. + /// However, in LLVM, for RV32E and RV64E, the stack must be 4-byte aligned + /// to be compatible with the implementation of ilp32e in GCC + /// + /// Related: https://llvm.org/docs/RISCVUsage.html + pub const fn byte_alignment(&self) -> usize { + match self { + Self::Rv32E | Self::Rv64E => 4, + _ => 16, + } + } + + /// Generate the assembly instructions to store the trap frame. + /// + /// The `filter` function is used to filter which registers to store. + /// This is useful to optimize the binary size in vectored interrupt mode, which divides the trap + /// frame storage in two parts: the first part saves space in the stack and stores only the `a0` register, + /// while the second part stores the remaining registers. + pub fn store_trap bool>(&self, mut filter: T) -> String { + let width = self.width(); + let store = self.store(); + self.trap_frame() + .iter() + .enumerate() + .filter(|(_, ®)| !reg.starts_with('_') && filter(reg)) + .map(|(i, reg)| format!("{store} {reg}, {i}*{width}(sp)")) + .collect::>() + .join("\n ") + } + + /// Generate the assembly instructions to load the trap frame. + pub fn load_trap(&self) -> String { + let width = self.width(); + let load = self.load(); + self.trap_frame() + .iter() + .enumerate() + .filter(|(_, ®)| !reg.starts_with('_')) + .map(|(i, reg)| format!("{load} {reg}, {i}*{width}(sp)")) + .collect::>() + .join("\n ") + } + + pub fn default_start_trap(&self) -> TokenStream { + let width = self.width(); + let trap_size = self.trap_frame().len(); + let byte_alignment = self.byte_alignment(); + // ensure we do not break that sp is 16-byte aligned + if (trap_size * width) % byte_alignment != 0 { + return Error::new(Span::call_site(), "Trap frame size must be 16-byte aligned") + .to_compile_error(); + } + let store = self.store_trap(|_| true); + let load = self.load_trap(); + + #[cfg(feature = "s-mode")] + let ret = "sret"; + #[cfg(not(feature = "s-mode"))] + let ret = "mret"; + + let pre_default_start_trap = if cfg!(feature = "rvrt-pre-default-start-trap") { + r#" + j _pre_default_start_trap +.global _pre_default_start_trap_ret +_pre_default_start_trap_ret:"# + } else { + "" + }; + + let vectored_trap = if cfg!(feature = "rt-v-trap") { + let store_start = self.store_trap(|reg| reg == "a0"); + let store_continue = self.store_trap(|reg| reg != "a0"); + + format!( + r#" + +.section .trap.continue, \"ax\" +.balign 4 +.global _start_DefaultHandler_trap +_start_DefaultHandler_trap: + addi sp, sp, -{trap_size} * {width} + {store_start} + la a0, DefaultHandler +.global _continue_interrupt_trap +_continue_interrupt_trap: + {store_continue} + jalr ra, a0, 0 + {load} + addi sp, sp, {trap_size} * {width} + {ret}"# + ) + } else { + String::new() + }; + + format!( + r#" +#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] +core::arch::global_asm!( +" +.section .trap.start, \"ax\" +.balign 4 /* Alignment required for xtvec */ +.global _default_start_trap +_default_start_trap:{pre_default_start_trap} + addi sp, sp, - {trap_size} * {width} + {store} + add a0, sp, zero + jal ra, _start_trap_rust + {load} + addi sp, sp, {trap_size} * {width} + {ret}{vectored_trap} +" +); +"# + ) + .parse() + .unwrap() + } +} diff --git a/riscv-rt/CHANGELOG.md b/riscv-rt/CHANGELOG.md index 06535d63..f4eb0ca2 100644 --- a/riscv-rt/CHANGELOG.md +++ b/riscv-rt/CHANGELOG.md @@ -5,7 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## [Unreleased] +## v0.17.2 - Unreleased + +### Changed + +- Use macros from `riscv-macros` instead of `riscv-rt-macros` ## v0.17.1 - 2026-01-13 diff --git a/riscv-rt/Cargo.toml b/riscv-rt/Cargo.toml index 03f8db74..bc5fdfc6 100644 --- a/riscv-rt/Cargo.toml +++ b/riscv-rt/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "riscv-rt" -version = "0.17.1" +version = "0.17.2" rust-version = "1.81" repository = "https://github.com/rust-embedded/riscv" authors = ["The RISC-V Team "] @@ -27,6 +27,7 @@ riscv-target-parser = { path = "../riscv-target-parser", version = "0.1.2" } riscv = { path = "../riscv", version = "0.16.0", features = ["rt"] } riscv-types = { path = "../riscv-types", version = "0.1.0" } riscv-rt-macros = { path = "macros", version = "0.6.1" } +riscv-macros = { path = "../riscv-macros", version = "0.4.1", features = ["riscv-rt"] } defmt = { version = "1.0.1", optional = true } @@ -38,9 +39,9 @@ riscv = { path = "../riscv", version = "0.16.0", features = ["critical-section-s [features] pre-init = [] post-init = [] -s-mode = ["riscv-rt-macros/s-mode"] +s-mode = ["riscv-rt-macros/s-mode", "riscv-macros/s-mode"] single-hart = [] -v-trap = ["riscv-rt-macros/v-trap", "riscv/rt-v-trap"] +v-trap = ["riscv-rt-macros/v-trap", "riscv/rt-v-trap", "riscv-macros/rt-v-trap"] u-boot = ["riscv-rt-macros/u-boot", "single-hart"] no-interrupts = [] no-exceptions = [] @@ -50,4 +51,4 @@ no-xtvec = [] device = [] memory = [] defmt = ["dep:defmt"] -pre-default-start-trap = ["riscv-rt-macros/pre-default-start-trap"] +pre-default-start-trap = ["riscv-rt-macros/pre-default-start-trap", "riscv-macros/rvrt-pre-default-start-trap"] diff --git a/riscv-rt/src/asm.rs b/riscv-rt/src/asm.rs index cef84699..114ac702 100644 --- a/riscv-rt/src/asm.rs +++ b/riscv-rt/src/asm.rs @@ -35,7 +35,7 @@ macro_rules! cfg_global_asm { // - https://github.com/rust-embedded/riscv/issues/175 // - https://github.com/rust-lang/rust/issues/80608 // - https://github.com/llvm/llvm-project/issues/61991 -riscv_rt_macros::llvm_arch_patch!(); +riscv_macros::rvrt_llvm_arch_patch!(); // Entry point of all programs (_start). It initializes DWARF call frame information, // the stack pointer, the frame pointer (needed for closures to work in start_rust) @@ -217,10 +217,7 @@ _default_mp_hook: ret", ); -riscv_rt_macros::default_start_trap!(); - -#[cfg(feature = "v-trap")] -riscv_rt_macros::vectored_interrupt_trap!(); +riscv_macros::rvrt_default_start_trap!(); #[rustfmt::skip] global_asm!( From def2275a0ad3a6122d6e317a179e331f442a9f06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas=20Rodr=C3=ADguez?= Date: Mon, 2 Feb 2026 11:39:10 +0100 Subject: [PATCH 2/5] riscv-macros: add post_init macro for riscv-rt --- riscv-macros/CHANGELOG.md | 1 + riscv-macros/src/lib.rs | 28 ++ riscv-macros/src/riscv_rt.rs | 249 ++++++++++++++++++ riscv-rt/src/lib.rs | 2 +- .../riscv-rt/post_init/fail_arg_count.rs | 4 - .../riscv-rt/post_init/fail_arg_count.stderr | 5 - .../tests/riscv-rt/post_init/fail_arg_type.rs | 4 - .../riscv-rt/post_init/fail_arg_type.stderr | 5 - .../riscv-rt/post_init/fail_async.stderr | 5 - .../tests/riscv-rt/post_init/fail_full.rs | 8 + .../tests/riscv-rt/post_init/fail_full.stderr | 65 +++++ .../{pass_empty.rs => pass_empty_safe.rs} | 0 .../{fail_async.rs => pass_empty_unsafe.rs} | 2 +- 13 files changed, 353 insertions(+), 25 deletions(-) delete mode 100644 tests-trybuild/tests/riscv-rt/post_init/fail_arg_count.rs delete mode 100644 tests-trybuild/tests/riscv-rt/post_init/fail_arg_count.stderr delete mode 100644 tests-trybuild/tests/riscv-rt/post_init/fail_arg_type.rs delete mode 100644 tests-trybuild/tests/riscv-rt/post_init/fail_arg_type.stderr delete mode 100644 tests-trybuild/tests/riscv-rt/post_init/fail_async.stderr create mode 100644 tests-trybuild/tests/riscv-rt/post_init/fail_full.rs create mode 100644 tests-trybuild/tests/riscv-rt/post_init/fail_full.stderr rename tests-trybuild/tests/riscv-rt/post_init/{pass_empty.rs => pass_empty_safe.rs} (100%) rename tests-trybuild/tests/riscv-rt/post_init/{fail_async.rs => pass_empty_unsafe.rs} (57%) diff --git a/riscv-macros/CHANGELOG.md b/riscv-macros/CHANGELOG.md index 44a31ffa..e2cc7ed9 100644 --- a/riscv-macros/CHANGELOG.md +++ b/riscv-macros/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added +- New `post_init` macro for Rust routines that must be executed before main. - New `rvrt_llvm_arch_patch` and `rvrt_default_start_trap` for generating assembly code required by the `riscv-rt` crate. - New `riscv-rt` feature to opt-in `riscv-rt`-related macros. diff --git a/riscv-macros/src/lib.rs b/riscv-macros/src/lib.rs index bb7a626d..89054802 100644 --- a/riscv-macros/src/lib.rs +++ b/riscv-macros/src/lib.rs @@ -63,6 +63,34 @@ pub fn pac_enum(attr: TokenStream, item: TokenStream) -> TokenStream { .into() } +/// Attribute to mark which function will be called before jumping to the entry point. +/// You must enable the `post-init` feature in the `riscv-rt` crate to use this macro. +/// +/// In contrast with `__pre_init`, this function is called after the static variables +/// are initialized, so it is safe to access them. It is also safe to run Rust code. +/// +/// The function must have the signature of `[unsafe] fn([usize])`, where the argument +/// corresponds to the hart ID of the current hart. This is useful for multi-hart systems +/// to perform hart-specific initialization. +/// +/// # IMPORTANT +/// +/// This attribute can appear at most *once* in the dependency graph. +/// +/// # Examples +/// +/// ``` +/// #[riscv_macros::post_init] +/// unsafe fn before_main(hart_id: usize) { +/// // do something here +/// } +/// ``` +#[cfg(feature = "riscv-rt")] +#[proc_macro_attribute] +pub fn post_init(args: TokenStream, input: TokenStream) -> TokenStream { + riscv_rt::Fn::post_init(args, input) +} + /// Temporary patch macro to deal with LLVM bug. /// /// # Note diff --git a/riscv-macros/src/riscv_rt.rs b/riscv-macros/src/riscv_rt.rs index a15ceb88..86c6dfee 100644 --- a/riscv-macros/src/riscv_rt.rs +++ b/riscv-macros/src/riscv_rt.rs @@ -1 +1,250 @@ +use proc_macro::TokenStream; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use syn::{ + parse_macro_input, punctuated::Punctuated, spanned::Spanned, token::Comma, Error, FnArg, Ident, + ItemFn, Result, ReturnType, Type, Visibility, +}; + pub mod asm; + +/// Enum representing the supported runtime function attributes +pub enum Fn { + PostInit, +} + +impl Fn { + /// Convenience method to generate the token stream for the `post_init` attribute + pub fn post_init(args: TokenStream, input: TokenStream) -> TokenStream { + let errors = Self::PostInit.check_args_empty(args).err(); + Self::PostInit.quote_fn(input, errors) + } + + /// Generate the token stream for the function with the given attribute + fn quote_fn(&self, item: TokenStream, mut errors: Option) -> TokenStream { + let mut func = parse_macro_input!(item as ItemFn); + + self.check_fn(&func, &mut errors); + let extras = self.add_extras(&mut func, &mut errors); + let export_name = self.export_name(&func); + let link_section = self.link_section(&func); + + let tokens = match errors { + Some(err) => err.to_compile_error(), + None => quote! { + #export_name + #link_section + #func + #extras + }, + }; + tokens.into() + } + + /// Check if the function signature is valid for the given attribute + fn check_fn(&self, f: &ItemFn, errors: &mut Option) { + // First, check that the function is private + if f.vis != Visibility::Inherited { + combine_err(errors, Error::new(f.vis.span(), "function must be private")); + } + + let sig = &f.sig; + // Next, check common aspects of the signature individually to accumulate errors + if let Some(constness) = sig.constness { + let span = constness.span(); + combine_err(errors, Error::new(span, "function must not be const")); + } + if let Some(asyncness) = sig.asyncness { + let span = asyncness.span(); + combine_err(errors, Error::new(span, "function must not be async")); + } + if let Some(abi) = &sig.abi { + combine_err(errors, Error::new(abi.span(), "ABI must not be specified")); + } + if !sig.generics.params.is_empty() { + let span = sig.generics.params.span(); + combine_err(errors, Error::new(span, "generics are not allowed")); + } + + // Check input parameters... + self.check_inputs(&sig.inputs, errors); + // ... and variadic arguments (they are at the end of input parameters) + if let Some(variadic) = &sig.variadic { + combine_err( + errors, + Error::new(variadic.span(), "variadic arguments are not allowed"), + ); + } + + // Check output type... + self.check_output(&sig.output, errors); + // ... and where clause (they are after output type) + if let Some(where_clause) = &sig.generics.where_clause { + let span = where_clause.span(); + combine_err(errors, Error::new(span, "where clause is not allowed")); + } + } + + /// Check if the function has valid input arguments for the given attribute + fn check_inputs(&self, inputs: &Punctuated, errors: &mut Option) { + // Use this match to specify expected input arguments for different functions in the future + match self { + Self::PostInit => self.check_fn_args(inputs, &["usize"], errors), + } + } + + /// Check if the function has a valid output type for the given attribute + fn check_output(&self, output: &ReturnType, errors: &mut Option) { + // Use this match to specify expected output types for different functions in the future + match self { + Self::PostInit => check_output_empty(output, errors), + } + } + + /// Additional items to append to the function for the given attribute + fn add_extras(&self, func: &mut ItemFn, _errors: &mut Option) -> Option { + // Append to function name the prefix __riscv_rt_ (to prevent users from calling it directly) + func.sig.ident = Ident::new( + &format!("__riscv_rt_{}", func.sig.ident), + func.sig.ident.span(), + ); + + // Use this match to specify extra items for different functions in the future + match self { + Self::PostInit => None, + } + } + + /// The export name for the given attribute + fn export_name(&self, _f: &ItemFn) -> Option { + // Use this match to specify export names for different functions in the future + let export_name = match self { + Self::PostInit => Some("__post_init".to_string()), + }; + + export_name.map(|name| match self { + Self::PostInit => quote! { + #[export_name = #name] + }, + }) + } + + /// The link section attribute for the given attribute (if any) + fn link_section(&self, _f: &ItemFn) -> Option { + // Use this match to specify section names for different functions in the future + let section_name: Option = match self { + // TODO: check if we want specific sections for these functions + Self::PostInit => None, + }; + + section_name.map(|section| quote! { + #[cfg_attr(any(target_arch = "riscv32", target_arch = "riscv64"), link_section = #section)] + }) + } + + /// Check that no arguments were provided to the macro attribute + fn check_args_empty(&self, args: TokenStream) -> Result<()> { + if args.is_empty() { + Ok(()) + } else { + let args: TokenStream2 = args.into(); + Err(Error::new(args.span(), "macro arguments are not allowed")) + } + } + + /// Iterates through the input arguments and checks that their types match the expected types + fn check_fn_args( + &self, + inputs: &Punctuated, + expected_types: &[&str], + errors: &mut Option, + ) { + let mut expected_iter = expected_types.iter(); + for arg in inputs.iter() { + match expected_iter.next() { + Some(expected) => { + if let Err(e) = check_arg_type(arg, expected) { + combine_err(errors, e); + } + } + None => { + combine_err(errors, Error::new(arg.span(), "too many input arguments")); + } + } + } + } +} + +/// Combine a new error into an optional accumulator +fn combine_err(acc: &mut Option, err: Error) { + match acc { + Some(e) => e.combine(err), + None => *acc = Some(err), + } +} + +/// Check if a function argument matches the expected type +fn check_arg_type(arg: &FnArg, expected: &str) -> Result<()> { + match arg { + FnArg::Typed(argument) => { + if !is_correct_type(&argument.ty, expected) { + Err(Error::new( + argument.ty.span(), + format!("argument type must be `{expected}`"), + )) + } else { + Ok(()) + } + } + FnArg::Receiver(_) => Err(Error::new(arg.span(), "invalid argument")), + } +} + +/// Check if a type matches the expected type name +fn is_correct_type(ty: &Type, expected: &str) -> bool { + // Parse the expected type string into a Type. We strip the path to compare only the last segment. + let mut correct = syn::parse_str(expected).unwrap(); + correct = strip_type_path(&correct).unwrap(); + + if let Some(ty) = strip_type_path(ty) { + ty == correct + } else { + false + } +} + +/// Strip the path of a type, returning only the last segment (e.g., `core::usize` -> `usize`) +fn strip_type_path(ty: &Type) -> Option { + match ty { + Type::Ptr(ty) => { + let mut ty = ty.clone(); + *ty.elem = strip_type_path(&ty.elem)?; + Some(Type::Ptr(ty)) + } + Type::Reference(ty) => { + let mut ty = ty.clone(); + // We ignore mutability when comparing reference types + ty.mutability = None; + ty.elem = Box::new(strip_type_path(&ty.elem)?); + Some(Type::Reference(ty)) + } + Type::Path(ty) => { + let mut ty = ty.clone(); + let last_segment = ty.path.segments.last().unwrap().clone(); + ty.path.segments = Punctuated::new(); + ty.path.segments.push_value(last_segment); + Some(Type::Path(ty)) + } + _ => None, + } +} + +/// Make sure the output type is either `()` or absent +fn check_output_empty(output: &ReturnType, errors: &mut Option) { + let is_valid = matches!(output, ReturnType::Default) + || matches!(output, ReturnType::Type(_, ty) if matches!(**ty, Type::Tuple(ref tuple) if tuple.elems.is_empty())); + + if !is_valid { + combine_err(errors, Error::new(output.span(), "return type must be ()")); + } +} diff --git a/riscv-rt/src/lib.rs b/riscv-rt/src/lib.rs index 5f960e1f..08886a43 100644 --- a/riscv-rt/src/lib.rs +++ b/riscv-rt/src/lib.rs @@ -703,7 +703,7 @@ pub use riscv_rt_macros::{core_interrupt, entry, exception, external_interrupt}; pub use riscv_types::*; #[cfg(feature = "post-init")] -pub use riscv_rt_macros::post_init; +pub use riscv_macros::post_init; #[cfg(feature = "pre-init")] pub use riscv_rt_macros::pre_init; diff --git a/tests-trybuild/tests/riscv-rt/post_init/fail_arg_count.rs b/tests-trybuild/tests/riscv-rt/post_init/fail_arg_count.rs deleted file mode 100644 index 6c94330d..00000000 --- a/tests-trybuild/tests/riscv-rt/post_init/fail_arg_count.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[riscv_rt::post_init] -fn before_main(_hart_id: usize, _dtb: usize) {} - -fn main() {} diff --git a/tests-trybuild/tests/riscv-rt/post_init/fail_arg_count.stderr b/tests-trybuild/tests/riscv-rt/post_init/fail_arg_count.stderr deleted file mode 100644 index 32ac7e78..00000000 --- a/tests-trybuild/tests/riscv-rt/post_init/fail_arg_count.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: `#[post_init]` function has too many arguments - --> tests/riscv-rt/post_init/fail_arg_count.rs:2:33 - | -2 | fn before_main(_hart_id: usize, _dtb: usize) {} - | ^^^^ diff --git a/tests-trybuild/tests/riscv-rt/post_init/fail_arg_type.rs b/tests-trybuild/tests/riscv-rt/post_init/fail_arg_type.rs deleted file mode 100644 index fc5894c5..00000000 --- a/tests-trybuild/tests/riscv-rt/post_init/fail_arg_type.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[riscv_rt::post_init] -fn before_main(_hart_id: String) {} - -fn main() {} diff --git a/tests-trybuild/tests/riscv-rt/post_init/fail_arg_type.stderr b/tests-trybuild/tests/riscv-rt/post_init/fail_arg_type.stderr deleted file mode 100644 index 73f4f80c..00000000 --- a/tests-trybuild/tests/riscv-rt/post_init/fail_arg_type.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: argument type must be usize - --> tests/riscv-rt/post_init/fail_arg_type.rs:2:26 - | -2 | fn before_main(_hart_id: String) {} - | ^^^^^^ diff --git a/tests-trybuild/tests/riscv-rt/post_init/fail_async.stderr b/tests-trybuild/tests/riscv-rt/post_init/fail_async.stderr deleted file mode 100644 index 0dff16ff..00000000 --- a/tests-trybuild/tests/riscv-rt/post_init/fail_async.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: `#[post_init]` function must have signature `[unsafe] fn([usize])` - --> tests/riscv-rt/post_init/fail_async.rs:2:1 - | -2 | async fn before_main() {} - | ^^^^^ diff --git a/tests-trybuild/tests/riscv-rt/post_init/fail_full.rs b/tests-trybuild/tests/riscv-rt/post_init/fail_full.rs new file mode 100644 index 00000000..dc6fcc00 --- /dev/null +++ b/tests-trybuild/tests/riscv-rt/post_init/fail_full.rs @@ -0,0 +1,8 @@ +#[riscv_rt::post_init(arg)] +pub const async extern "Rust" fn before_main<'a, T>(_h: u32, _d: &'a T, _v: ...) -> ! +where + T: Copy, +{ +} + +fn main() {} diff --git a/tests-trybuild/tests/riscv-rt/post_init/fail_full.stderr b/tests-trybuild/tests/riscv-rt/post_init/fail_full.stderr new file mode 100644 index 00000000..b35e029b --- /dev/null +++ b/tests-trybuild/tests/riscv-rt/post_init/fail_full.stderr @@ -0,0 +1,65 @@ +error: macro arguments are not allowed + --> tests/riscv-rt/post_init/fail_full.rs:1:23 + | +1 | #[riscv_rt::post_init(arg)] + | ^^^ + +error: function must be private + --> tests/riscv-rt/post_init/fail_full.rs:2:1 + | +2 | pub const async extern "Rust" fn before_main<'a, T>(_h: u32, _d: &'a T, _v: ...) -> ! + | ^^^ + +error: function must not be const + --> tests/riscv-rt/post_init/fail_full.rs:2:5 + | +2 | pub const async extern "Rust" fn before_main<'a, T>(_h: u32, _d: &'a T, _v: ...) -> ! + | ^^^^^ + +error: function must not be async + --> tests/riscv-rt/post_init/fail_full.rs:2:11 + | +2 | pub const async extern "Rust" fn before_main<'a, T>(_h: u32, _d: &'a T, _v: ...) -> ! + | ^^^^^ + +error: ABI must not be specified + --> tests/riscv-rt/post_init/fail_full.rs:2:17 + | +2 | pub const async extern "Rust" fn before_main<'a, T>(_h: u32, _d: &'a T, _v: ...) -> ! + | ^^^^^^ + +error: generics are not allowed + --> tests/riscv-rt/post_init/fail_full.rs:2:46 + | +2 | pub const async extern "Rust" fn before_main<'a, T>(_h: u32, _d: &'a T, _v: ...) -> ! + | ^^ + +error: argument type must be `usize` + --> tests/riscv-rt/post_init/fail_full.rs:2:57 + | +2 | pub const async extern "Rust" fn before_main<'a, T>(_h: u32, _d: &'a T, _v: ...) -> ! + | ^^^ + +error: too many input arguments + --> tests/riscv-rt/post_init/fail_full.rs:2:62 + | +2 | pub const async extern "Rust" fn before_main<'a, T>(_h: u32, _d: &'a T, _v: ...) -> ! + | ^^ + +error: variadic arguments are not allowed + --> tests/riscv-rt/post_init/fail_full.rs:2:73 + | +2 | pub const async extern "Rust" fn before_main<'a, T>(_h: u32, _d: &'a T, _v: ...) -> ! + | ^^ + +error: return type must be () + --> tests/riscv-rt/post_init/fail_full.rs:2:82 + | +2 | pub const async extern "Rust" fn before_main<'a, T>(_h: u32, _d: &'a T, _v: ...) -> ! + | ^ + +error: where clause is not allowed + --> tests/riscv-rt/post_init/fail_full.rs:3:1 + | +3 | where + | ^^^^^ diff --git a/tests-trybuild/tests/riscv-rt/post_init/pass_empty.rs b/tests-trybuild/tests/riscv-rt/post_init/pass_empty_safe.rs similarity index 100% rename from tests-trybuild/tests/riscv-rt/post_init/pass_empty.rs rename to tests-trybuild/tests/riscv-rt/post_init/pass_empty_safe.rs diff --git a/tests-trybuild/tests/riscv-rt/post_init/fail_async.rs b/tests-trybuild/tests/riscv-rt/post_init/pass_empty_unsafe.rs similarity index 57% rename from tests-trybuild/tests/riscv-rt/post_init/fail_async.rs rename to tests-trybuild/tests/riscv-rt/post_init/pass_empty_unsafe.rs index 27c95b93..7539e0b7 100644 --- a/tests-trybuild/tests/riscv-rt/post_init/fail_async.rs +++ b/tests-trybuild/tests/riscv-rt/post_init/pass_empty_unsafe.rs @@ -1,4 +1,4 @@ #[riscv_rt::post_init] -async fn before_main() {} +unsafe fn before_main() {} fn main() {} From 1ca1fe8f864bafd495f780f12c39d8d48d2c30c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas=20Rodr=C3=ADguez?= Date: Mon, 2 Feb 2026 11:48:49 +0100 Subject: [PATCH 3/5] riscv-macros: add entry macro for riscv-rt --- riscv-macros/CHANGELOG.md | 2 + riscv-macros/Cargo.toml | 1 + riscv-macros/src/lib.rs | 45 +++++++++++ riscv-macros/src/riscv_rt.rs | 28 ++++++- riscv-rt/Cargo.toml | 2 +- riscv-rt/src/lib.rs | 3 +- .../tests/riscv-rt/entry/fail_full.rs | 8 ++ .../tests/riscv-rt/entry/fail_full.stderr | 79 +++++++++++++++++++ .../tests/riscv-rt/entry/pass_empty_safe.rs | 6 ++ .../tests/riscv-rt/entry/pass_empty_unsafe.rs | 6 ++ .../tests/riscv-rt/entry/pass_safe.rs | 6 ++ .../tests/riscv-rt/entry/pass_unsafe.rs | 5 ++ 12 files changed, 187 insertions(+), 4 deletions(-) create mode 100644 tests-trybuild/tests/riscv-rt/entry/fail_full.rs create mode 100644 tests-trybuild/tests/riscv-rt/entry/fail_full.stderr create mode 100644 tests-trybuild/tests/riscv-rt/entry/pass_empty_safe.rs create mode 100644 tests-trybuild/tests/riscv-rt/entry/pass_empty_unsafe.rs create mode 100644 tests-trybuild/tests/riscv-rt/entry/pass_safe.rs create mode 100644 tests-trybuild/tests/riscv-rt/entry/pass_unsafe.rs diff --git a/riscv-macros/CHANGELOG.md b/riscv-macros/CHANGELOG.md index e2cc7ed9..0aa7ea1e 100644 --- a/riscv-macros/CHANGELOG.md +++ b/riscv-macros/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added +- New `entry` macro for the Rust entry point (for `riscv-rt`). +- New `rvrt-u-boot` feature to adapt `entry` macro for U-Boot. - New `post_init` macro for Rust routines that must be executed before main. - New `rvrt_llvm_arch_patch` and `rvrt_default_start_trap` for generating assembly code required by the `riscv-rt` crate. diff --git a/riscv-macros/Cargo.toml b/riscv-macros/Cargo.toml index 71c4057c..b23f9708 100644 --- a/riscv-macros/Cargo.toml +++ b/riscv-macros/Cargo.toml @@ -20,6 +20,7 @@ rt = [] rt-v-trap = ["rt"] riscv-rt = ["rt", "syn/extra-traits", "syn/full"] rvrt-pre-default-start-trap = ["riscv-rt"] +rvrt-u-boot = ["riscv-rt"] s-mode = [] [dependencies] diff --git a/riscv-macros/src/lib.rs b/riscv-macros/src/lib.rs index 89054802..c9a76915 100644 --- a/riscv-macros/src/lib.rs +++ b/riscv-macros/src/lib.rs @@ -91,6 +91,51 @@ pub fn post_init(args: TokenStream, input: TokenStream) -> TokenStream { riscv_rt::Fn::post_init(args, input) } +/// Attribute to declare the entry point of the program +/// +/// 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. +/// +/// # Signature +/// +/// ## Regular Usage +/// +/// The type of the specified function must be `[unsafe] fn([usize[, usize[, usize]]]) -> !` (never ending function). +/// The optional arguments correspond to the values passed in registers `a0`, `a1`, and `a2`. +/// The first argument holds the hart ID of the current hart, which is useful for multi-hart systems. +/// The other two arguments are currently unused and reserved for future use. +/// +/// ## With U-Boot +/// +/// This runtime supports being booted by U-Boot. In this case, the entry point function +/// must have the signature `[unsafe] fn([c_int[, *const *const c_char]]) -> !`, where the first argument +/// corresponds to the `argc` parameter and the second argument corresponds to the `argv` parameter passed by U-Boot. +/// +/// Remember to enable the `u-boot` feature in the `riscv-rt` crate to use this functionality. +/// +/// # IMPORTANT +/// +/// This attribute can appear at most *once* in the dependency graph. +/// +/// The entry point will be called by the reset handler. The program can't reference to the entry +/// point, much less invoke it. +/// +/// # Examples +/// +/// ``` no_run +/// #[riscv_macros::entry] +/// fn main() -> ! { +/// loop { +/// /* .. */ +/// } +/// } +/// ``` +#[cfg(feature = "riscv-rt")] +#[proc_macro_attribute] +pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { + riscv_rt::Fn::entry(args, input) +} + /// Temporary patch macro to deal with LLVM bug. /// /// # Note diff --git a/riscv-macros/src/riscv_rt.rs b/riscv-macros/src/riscv_rt.rs index 86c6dfee..23c614b4 100644 --- a/riscv-macros/src/riscv_rt.rs +++ b/riscv-macros/src/riscv_rt.rs @@ -11,6 +11,7 @@ pub mod asm; /// Enum representing the supported runtime function attributes pub enum Fn { PostInit, + Entry, } impl Fn { @@ -20,6 +21,12 @@ impl Fn { Self::PostInit.quote_fn(input, errors) } + /// Convenience method to generate the token stream for the `entry` attribute + pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { + let errors = Self::Entry.check_args_empty(args).err(); + Self::Entry.quote_fn(input, errors) + } + /// Generate the token stream for the function with the given attribute fn quote_fn(&self, item: TokenStream, mut errors: Option) -> TokenStream { let mut func = parse_macro_input!(item as ItemFn); @@ -90,6 +97,10 @@ impl Fn { // Use this match to specify expected input arguments for different functions in the future match self { Self::PostInit => self.check_fn_args(inputs, &["usize"], errors), + #[cfg(not(feature = "rvrt-u-boot"))] + Self::Entry => self.check_fn_args(inputs, &["usize", "usize", "usize"], errors), + #[cfg(feature = "rvrt-u-boot")] + Self::Entry => self.check_fn_args(inputs, &["c_int", "*const *const c_char"], errors), } } @@ -98,6 +109,7 @@ impl Fn { // Use this match to specify expected output types for different functions in the future match self { Self::PostInit => check_output_empty(output, errors), + Self::Entry => check_output_never(output, errors), } } @@ -111,7 +123,7 @@ impl Fn { // Use this match to specify extra items for different functions in the future match self { - Self::PostInit => None, + Self::PostInit | Self::Entry => None, } } @@ -120,12 +132,17 @@ impl Fn { // Use this match to specify export names for different functions in the future let export_name = match self { Self::PostInit => Some("__post_init".to_string()), + Self::Entry => Some("main".to_string()), }; export_name.map(|name| match self { Self::PostInit => quote! { #[export_name = #name] }, + Self::Entry => quote! { + // to avoid two main symbols when testing on host + #[cfg_attr(any(target_arch = "riscv32", target_arch = "riscv64"), export_name = #name)] + }, }) } @@ -134,7 +151,7 @@ impl Fn { // Use this match to specify section names for different functions in the future let section_name: Option = match self { // TODO: check if we want specific sections for these functions - Self::PostInit => None, + Self::PostInit | Self::Entry => None, }; section_name.map(|section| quote! { @@ -248,3 +265,10 @@ fn check_output_empty(output: &ReturnType, errors: &mut Option) { combine_err(errors, Error::new(output.span(), "return type must be ()")); } } + +/// Make sure the output type is `!` (never) +fn check_output_never(output: &ReturnType, errors: &mut Option) { + if !matches!(output, ReturnType::Type(_, ty) if matches!(**ty, Type::Never(_))) { + combine_err(errors, Error::new(output.span(), "return type must be !")); + } +} diff --git a/riscv-rt/Cargo.toml b/riscv-rt/Cargo.toml index bc5fdfc6..c576f92e 100644 --- a/riscv-rt/Cargo.toml +++ b/riscv-rt/Cargo.toml @@ -42,7 +42,7 @@ post-init = [] s-mode = ["riscv-rt-macros/s-mode", "riscv-macros/s-mode"] single-hart = [] v-trap = ["riscv-rt-macros/v-trap", "riscv/rt-v-trap", "riscv-macros/rt-v-trap"] -u-boot = ["riscv-rt-macros/u-boot", "single-hart"] +u-boot = ["riscv-rt-macros/u-boot", "riscv-macros/rvrt-u-boot", "single-hart"] no-interrupts = [] no-exceptions = [] no-mhartid = ["single-hart"] diff --git a/riscv-rt/src/lib.rs b/riscv-rt/src/lib.rs index 08886a43..b1f26adb 100644 --- a/riscv-rt/src/lib.rs +++ b/riscv-rt/src/lib.rs @@ -699,7 +699,8 @@ use riscv::register::{ mtvec::{self as xtvec, Mtvec as Xtvec, TrapMode}, }; -pub use riscv_rt_macros::{core_interrupt, entry, exception, external_interrupt}; +pub use riscv_macros::entry; +pub use riscv_rt_macros::{core_interrupt, exception, external_interrupt}; pub use riscv_types::*; #[cfg(feature = "post-init")] diff --git a/tests-trybuild/tests/riscv-rt/entry/fail_full.rs b/tests-trybuild/tests/riscv-rt/entry/fail_full.rs new file mode 100644 index 00000000..cddf63cc --- /dev/null +++ b/tests-trybuild/tests/riscv-rt/entry/fail_full.rs @@ -0,0 +1,8 @@ +#[riscv_rt::entry(arg)] +pub const async extern "Rust" fn entry<'a, T>(_a: u32, _b: &'a T, _c: String, _d: usize, _e: ...) +where + T: Copy, +{ +} + +fn main() {} diff --git a/tests-trybuild/tests/riscv-rt/entry/fail_full.stderr b/tests-trybuild/tests/riscv-rt/entry/fail_full.stderr new file mode 100644 index 00000000..ad047a82 --- /dev/null +++ b/tests-trybuild/tests/riscv-rt/entry/fail_full.stderr @@ -0,0 +1,79 @@ +error: macro arguments are not allowed + --> tests/riscv-rt/entry/fail_full.rs:1:19 + | +1 | #[riscv_rt::entry(arg)] + | ^^^ + +error: function must be private + --> tests/riscv-rt/entry/fail_full.rs:2:1 + | +2 | pub const async extern "Rust" fn entry<'a, T>(_a: u32, _b: &'a T, _c: String, _d: usize, _e: ...) + | ^^^ + +error: function must not be const + --> tests/riscv-rt/entry/fail_full.rs:2:5 + | +2 | pub const async extern "Rust" fn entry<'a, T>(_a: u32, _b: &'a T, _c: String, _d: usize, _e: ...) + | ^^^^^ + +error: function must not be async + --> tests/riscv-rt/entry/fail_full.rs:2:11 + | +2 | pub const async extern "Rust" fn entry<'a, T>(_a: u32, _b: &'a T, _c: String, _d: usize, _e: ...) + | ^^^^^ + +error: ABI must not be specified + --> tests/riscv-rt/entry/fail_full.rs:2:17 + | +2 | pub const async extern "Rust" fn entry<'a, T>(_a: u32, _b: &'a T, _c: String, _d: usize, _e: ...) + | ^^^^^^ + +error: generics are not allowed + --> tests/riscv-rt/entry/fail_full.rs:2:40 + | +2 | pub const async extern "Rust" fn entry<'a, T>(_a: u32, _b: &'a T, _c: String, _d: usize, _e: ...) + | ^^ + +error: argument type must be `usize` + --> tests/riscv-rt/entry/fail_full.rs:2:51 + | +2 | pub const async extern "Rust" fn entry<'a, T>(_a: u32, _b: &'a T, _c: String, _d: usize, _e: ...) + | ^^^ + +error: argument type must be `usize` + --> tests/riscv-rt/entry/fail_full.rs:2:60 + | +2 | pub const async extern "Rust" fn entry<'a, T>(_a: u32, _b: &'a T, _c: String, _d: usize, _e: ...) + | ^ + +error: argument type must be `usize` + --> tests/riscv-rt/entry/fail_full.rs:2:71 + | +2 | pub const async extern "Rust" fn entry<'a, T>(_a: u32, _b: &'a T, _c: String, _d: usize, _e: ...) + | ^^^^^^ + +error: too many input arguments + --> tests/riscv-rt/entry/fail_full.rs:2:79 + | +2 | pub const async extern "Rust" fn entry<'a, T>(_a: u32, _b: &'a T, _c: String, _d: usize, _e: ...) + | ^^ + +error: variadic arguments are not allowed + --> tests/riscv-rt/entry/fail_full.rs:2:90 + | +2 | pub const async extern "Rust" fn entry<'a, T>(_a: u32, _b: &'a T, _c: String, _d: usize, _e: ...) + | ^^ + +error: return type must be ! + --> tests/riscv-rt/entry/fail_full.rs:1:1 + | +1 | #[riscv_rt::entry(arg)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `riscv_rt::entry` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: where clause is not allowed + --> tests/riscv-rt/entry/fail_full.rs:3:1 + | +3 | where + | ^^^^^ diff --git a/tests-trybuild/tests/riscv-rt/entry/pass_empty_safe.rs b/tests-trybuild/tests/riscv-rt/entry/pass_empty_safe.rs new file mode 100644 index 00000000..d0f9f3af --- /dev/null +++ b/tests-trybuild/tests/riscv-rt/entry/pass_empty_safe.rs @@ -0,0 +1,6 @@ +#[riscv_rt::entry] +fn entry() -> ! { + loop {} +} + +fn main() {} diff --git a/tests-trybuild/tests/riscv-rt/entry/pass_empty_unsafe.rs b/tests-trybuild/tests/riscv-rt/entry/pass_empty_unsafe.rs new file mode 100644 index 00000000..48d3a89d --- /dev/null +++ b/tests-trybuild/tests/riscv-rt/entry/pass_empty_unsafe.rs @@ -0,0 +1,6 @@ +#[riscv_rt::entry] +unsafe fn entry() -> ! { + loop {} +} + +fn main() {} diff --git a/tests-trybuild/tests/riscv-rt/entry/pass_safe.rs b/tests-trybuild/tests/riscv-rt/entry/pass_safe.rs new file mode 100644 index 00000000..0a4174bf --- /dev/null +++ b/tests-trybuild/tests/riscv-rt/entry/pass_safe.rs @@ -0,0 +1,6 @@ +#[riscv_rt::entry] +fn entry(_a0: usize, _a1: usize, _a2: usize) -> ! { + loop {} +} + +fn main() {} diff --git a/tests-trybuild/tests/riscv-rt/entry/pass_unsafe.rs b/tests-trybuild/tests/riscv-rt/entry/pass_unsafe.rs new file mode 100644 index 00000000..7c7a64bf --- /dev/null +++ b/tests-trybuild/tests/riscv-rt/entry/pass_unsafe.rs @@ -0,0 +1,5 @@ +#[riscv_rt::entry] +unsafe fn entry(_a0: usize, _a1: usize, _a2: usize) -> ! { + loop {} +} +fn main() {} From b37c8ef0999e943f6a19f10928113b2742992f7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas=20Rodr=C3=ADguez?= Date: Mon, 2 Feb 2026 12:02:33 +0100 Subject: [PATCH 4/5] riscv-macros: add exception, core_interrupt, and external_interrupt macros for riscv-rt --- riscv-macros/CHANGELOG.md | 1 + riscv-macros/src/lib.rs | 64 +++++++++++ riscv-macros/src/riscv_rt.rs | 108 +++++++++++++++++- riscv-macros/src/riscv_rt/asm.rs | 35 ++++++ riscv-rt/CHANGELOG.md | 2 +- riscv-rt/Cargo.toml | 2 +- riscv-rt/src/lib.rs | 3 +- .../core_interrupt/fail_empty_args.rs | 4 + .../core_interrupt/fail_empty_args.stderr | 55 +++++++++ .../core_interrupt/fail_empty_macro.rs | 4 - .../core_interrupt/fail_empty_macro.stderr | 7 -- ..._impl_interrupt_number.rs => fail_impl.rs} | 0 ...terrupt_number.stderr => fail_impl.stderr} | 17 ++- .../core_interrupt/fail_signatures.rs | 10 -- .../core_interrupt/fail_signatures.stderr | 17 --- .../riscv-rt/exception/fail_empty_args.rs | 4 + .../riscv-rt/exception/fail_empty_args.stderr | 61 ++++++++++ .../riscv-rt/exception/fail_empty_macro.rs | 4 - .../exception/fail_empty_macro.stderr | 7 -- ..._impl_exception_number.rs => fail_impl.rs} | 0 ...ception_number.stderr => fail_impl.stderr} | 17 ++- .../riscv-rt/exception/fail_signatures.rs | 10 -- .../riscv-rt/exception/fail_signatures.stderr | 17 --- .../riscv-rt/exception/pass_exception.rs | 4 +- .../external_interrupt/fail_empty_args.rs | 4 + .../external_interrupt/fail_empty_args.stderr | 55 +++++++++ .../external_interrupt/fail_empty_macro.rs | 4 - .../fail_empty_macro.stderr | 7 -- ..._impl_interrupt_number.rs => fail_impl.rs} | 0 ...terrupt_number.stderr => fail_impl.stderr} | 4 +- .../external_interrupt/fail_signatures.rs | 39 ------- .../external_interrupt/fail_signatures.stderr | 17 --- 32 files changed, 418 insertions(+), 165 deletions(-) create mode 100644 tests-trybuild/tests/riscv-rt/core_interrupt/fail_empty_args.rs create mode 100644 tests-trybuild/tests/riscv-rt/core_interrupt/fail_empty_args.stderr delete mode 100644 tests-trybuild/tests/riscv-rt/core_interrupt/fail_empty_macro.rs delete mode 100644 tests-trybuild/tests/riscv-rt/core_interrupt/fail_empty_macro.stderr rename tests-trybuild/tests/riscv-rt/core_interrupt/{fail_impl_interrupt_number.rs => fail_impl.rs} (100%) rename tests-trybuild/tests/riscv-rt/core_interrupt/{fail_impl_interrupt_number.stderr => fail_impl.stderr} (60%) delete mode 100644 tests-trybuild/tests/riscv-rt/core_interrupt/fail_signatures.rs delete mode 100644 tests-trybuild/tests/riscv-rt/core_interrupt/fail_signatures.stderr create mode 100644 tests-trybuild/tests/riscv-rt/exception/fail_empty_args.rs create mode 100644 tests-trybuild/tests/riscv-rt/exception/fail_empty_args.stderr delete mode 100644 tests-trybuild/tests/riscv-rt/exception/fail_empty_macro.rs delete mode 100644 tests-trybuild/tests/riscv-rt/exception/fail_empty_macro.stderr rename tests-trybuild/tests/riscv-rt/exception/{fail_impl_exception_number.rs => fail_impl.rs} (100%) rename tests-trybuild/tests/riscv-rt/exception/{fail_impl_exception_number.stderr => fail_impl.stderr} (60%) delete mode 100644 tests-trybuild/tests/riscv-rt/exception/fail_signatures.rs delete mode 100644 tests-trybuild/tests/riscv-rt/exception/fail_signatures.stderr create mode 100644 tests-trybuild/tests/riscv-rt/external_interrupt/fail_empty_args.rs create mode 100644 tests-trybuild/tests/riscv-rt/external_interrupt/fail_empty_args.stderr delete mode 100644 tests-trybuild/tests/riscv-rt/external_interrupt/fail_empty_macro.rs delete mode 100644 tests-trybuild/tests/riscv-rt/external_interrupt/fail_empty_macro.stderr rename tests-trybuild/tests/riscv-rt/external_interrupt/{fail_impl_interrupt_number.rs => fail_impl.rs} (100%) rename tests-trybuild/tests/riscv-rt/external_interrupt/{fail_impl_interrupt_number.stderr => fail_impl.stderr} (85%) delete mode 100644 tests-trybuild/tests/riscv-rt/external_interrupt/fail_signatures.rs delete mode 100644 tests-trybuild/tests/riscv-rt/external_interrupt/fail_signatures.stderr diff --git a/riscv-macros/CHANGELOG.md b/riscv-macros/CHANGELOG.md index 0aa7ea1e..ddc23a87 100644 --- a/riscv-macros/CHANGELOG.md +++ b/riscv-macros/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added +- New `exception`, `core_interrupt`, and `external_interrupt` macros for trap handlers. - New `entry` macro for the Rust entry point (for `riscv-rt`). - New `rvrt-u-boot` feature to adapt `entry` macro for U-Boot. - New `post_init` macro for Rust routines that must be executed before main. diff --git a/riscv-macros/src/lib.rs b/riscv-macros/src/lib.rs index c9a76915..56cc8f78 100644 --- a/riscv-macros/src/lib.rs +++ b/riscv-macros/src/lib.rs @@ -136,6 +136,70 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { riscv_rt::Fn::entry(args, input) } +/// Attribute to declare an exception handler. +/// +/// The function must have the signature `[unsafe] fn([&[mut] riscv_rt::TrapFrame]) [-> !]`. +/// +/// The argument of the macro must be a path to a variant of an enum that implements the `riscv_rt::ExceptionNumber` trait. +/// +/// # Example +/// +/// ``` ignore,no_run +/// #[riscv_rt::exception(riscv::interrupt::Exception::LoadMisaligned)] +/// fn load_misaligned(trap_frame: &mut riscv_rt::TrapFrame) -> ! { +/// loop{}; +/// } +/// ``` +#[cfg(feature = "riscv-rt")] +#[proc_macro_attribute] +pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { + riscv_rt::Fn::trap(args, input, riscv_rt::TrapType::Exception) +} + +/// Attribute to declare a core interrupt handler. +/// +/// The function must have the signature `[unsafe] fn() [-> !]`. +/// +/// The argument of the macro must be a path to a variant of an enum that implements the `riscv_rt::CoreInterruptNumber` trait. +/// +/// If the `v-trap` feature is enabled, this macro generates the corresponding interrupt trap handler in assembly. +/// This feature relies on the `RISCV_RT_BASE_ISA` environment variable being set to one of +/// `rv32i`, `rv32e`, `rv64i`, or `rv64e`. Otherwise, this will **panic**. +/// +/// # Example +/// +/// ``` ignore,no_run +/// #[riscv_rt::core_interrupt(riscv::interrupt::Interrupt::SupervisorSoft)] +/// fn supervisor_soft() -> ! { +/// loop{}; +/// } +/// ``` +#[cfg(feature = "riscv-rt")] +#[proc_macro_attribute] +pub fn core_interrupt(args: TokenStream, input: TokenStream) -> TokenStream { + riscv_rt::Fn::trap(args, input, riscv_rt::TrapType::CoreInterrupt) +} + +/// Attribute to declare an external interrupt handler. +/// +/// The function must have the signature `[unsafe] fn() [-> !]`. +/// +/// The argument of the macro must be a path to a variant of an enum that implements the `riscv_rt::ExternalInterruptNumber` trait. +/// +/// # Example +/// +/// ``` ignore,no_run +/// #[riscv_rt::external_interrupt(e310x::interrupt::Interrupt::GPIO0)] +/// fn gpio0() -> ! { +/// loop{}; +/// } +/// ``` +#[cfg(feature = "riscv-rt")] +#[proc_macro_attribute] +pub fn external_interrupt(args: TokenStream, input: TokenStream) -> TokenStream { + riscv_rt::Fn::trap(args, input, riscv_rt::TrapType::ExternalInterrupt) +} + /// Temporary patch macro to deal with LLVM bug. /// /// # Note diff --git a/riscv-macros/src/riscv_rt.rs b/riscv-macros/src/riscv_rt.rs index 23c614b4..886e8ba4 100644 --- a/riscv-macros/src/riscv_rt.rs +++ b/riscv-macros/src/riscv_rt.rs @@ -3,15 +3,42 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; use syn::{ parse_macro_input, punctuated::Punctuated, spanned::Spanned, token::Comma, Error, FnArg, Ident, - ItemFn, Result, ReturnType, Type, Visibility, + ItemFn, Path, Result, ReturnType, Type, Visibility, }; pub mod asm; +/// Convenience struct to represent a trap handler +pub struct Trap { + /// Path to trap variant. Must implement the appropriate `riscv-types` trait. + path: syn::Path, + /// Type of trap handler + ty: TrapType, +} + +/// Enum representing the type of trap handler +pub enum TrapType { + Exception, + CoreInterrupt, + ExternalInterrupt, +} + +impl TrapType { + /// Get the trait that must be implemented by the corresponding trap + fn impl_trait(&self) -> &str { + match self { + Self::Exception => "riscv_rt::ExceptionNumber", + Self::ExternalInterrupt => "riscv_rt::ExternalInterruptNumber", + Self::CoreInterrupt => "riscv_rt::CoreInterruptNumber", + } + } +} + /// Enum representing the supported runtime function attributes pub enum Fn { PostInit, Entry, + Trap(Trap), } impl Fn { @@ -27,6 +54,24 @@ impl Fn { Self::Entry.quote_fn(input, errors) } + /// Convenience method to generate the token stream for trap handler attributes + pub fn trap(args: TokenStream, input: TokenStream, ty: TrapType) -> TokenStream { + let (path, errors) = match syn::parse::(args) { + Ok(path) => (path, None), + Err(e) => { + let path = syn::parse_str("invalid").unwrap(); + let impl_trait = ty.impl_trait(); + let err = Error::new( + e.span(), + format!("attribute expects a path to a variant of an enum that implements the `{impl_trait}` trait"), + ); + (path, Some(err)) + } + }; + let trap = Trap { path, ty }; + Self::Trap(trap).quote_fn(input, errors) + } + /// Generate the token stream for the function with the given attribute fn quote_fn(&self, item: TokenStream, mut errors: Option) -> TokenStream { let mut func = parse_macro_input!(item as ItemFn); @@ -101,6 +146,14 @@ impl Fn { Self::Entry => self.check_fn_args(inputs, &["usize", "usize", "usize"], errors), #[cfg(feature = "rvrt-u-boot")] Self::Entry => self.check_fn_args(inputs, &["c_int", "*const *const c_char"], errors), + Self::Trap(Trap { ty, .. }) => match ty { + TrapType::Exception => { + self.check_fn_args(inputs, &["&riscv_rt::TrapFrame"], errors) + } + TrapType::CoreInterrupt | TrapType::ExternalInterrupt => { + self.check_fn_args(inputs, &[], errors) + } + }, } } @@ -110,11 +163,12 @@ impl Fn { match self { Self::PostInit => check_output_empty(output, errors), Self::Entry => check_output_never(output, errors), + Self::Trap(_) => check_output_empty_or_never(output, errors), } } /// Additional items to append to the function for the given attribute - fn add_extras(&self, func: &mut ItemFn, _errors: &mut Option) -> Option { + fn add_extras(&self, func: &mut ItemFn, errors: &mut Option) -> Option { // Append to function name the prefix __riscv_rt_ (to prevent users from calling it directly) func.sig.ident = Ident::new( &format!("__riscv_rt_{}", func.sig.ident), @@ -124,6 +178,37 @@ impl Fn { // Use this match to specify extra items for different functions in the future match self { Self::PostInit | Self::Entry => None, + Self::Trap(Trap { path, ty }) => { + let mut extras = vec![]; + + // Set ABI to extern "C" + func.sig.abi = Some(syn::parse(quote! { extern "C" }.into()).unwrap()); + + // Compile-time check to ensure the trap path implements the trap trait + let impl_trait = format!("::{}", ty.impl_trait()); + let impl_trait: Path = syn::parse_str(&impl_trait).unwrap(); + + extras.push(quote! { + + const _: fn() = || { + fn assert_impl(_arg: T) {} + assert_impl(#path); + }; + }); + + if cfg!(feature = "rt-v-trap") && matches!(ty, TrapType::CoreInterrupt) { + let interr_ident = &path.segments.last().unwrap().ident; + match asm::RiscvArch::try_from_env() { + Some(arch) => extras.push(arch.start_interrupt_trap(interr_ident)), + None => combine_err(errors, Error::new( + path.span(), + "RISCV_RT_BASE_ISA must be defined for core interrupt handlers when `v-trap` feature is enabled", + )), + } + } + + Some(quote! { #(#extras)* }) + } } } @@ -133,10 +218,11 @@ impl Fn { let export_name = match self { Self::PostInit => Some("__post_init".to_string()), Self::Entry => Some("main".to_string()), + Self::Trap(Trap { path, .. }) => Some(path.segments.last().unwrap().ident.to_string()), }; export_name.map(|name| match self { - Self::PostInit => quote! { + Self::PostInit | Self::Trap(_) => quote! { #[export_name = #name] }, Self::Entry => quote! { @@ -151,7 +237,7 @@ impl Fn { // Use this match to specify section names for different functions in the future let section_name: Option = match self { // TODO: check if we want specific sections for these functions - Self::PostInit | Self::Entry => None, + Self::PostInit | Self::Entry | Self::Trap(_) => None, }; section_name.map(|section| quote! { @@ -272,3 +358,17 @@ fn check_output_never(output: &ReturnType, errors: &mut Option) { combine_err(errors, Error::new(output.span(), "return type must be !")); } } + +/// Make sure the output type is either `()`, `!` (never), or absent +fn check_output_empty_or_never(output: &ReturnType, errors: &mut Option) { + let is_valid = matches!(output, ReturnType::Default) + || matches!(output, ReturnType::Type(_, ty) if matches!(**ty, Type::Tuple(ref tuple) if tuple.elems.is_empty())) + || matches!(output, ReturnType::Type(_, ty) if matches!(**ty, Type::Never(_))); + + if !is_valid { + combine_err( + errors, + Error::new(output.span(), "return type must be () or !"), + ); + } +} diff --git a/riscv-macros/src/riscv_rt/asm.rs b/riscv-macros/src/riscv_rt/asm.rs index 09874bb1..81eeb024 100644 --- a/riscv-macros/src/riscv_rt/asm.rs +++ b/riscv-macros/src/riscv_rt/asm.rs @@ -194,6 +194,41 @@ _default_start_trap:{pre_default_start_trap} {ret}{vectored_trap} " ); +"# + ) + .parse() + .unwrap() + } + + /// Returns the assembly code for the `_start_` routine in vectored interrupt mode. + /// + /// This routine saves the trap frame, sets `a0` to the interrupt identifier, and jumps to + /// the common interrupt handler `_continue_interrupt_trap`. + /// + /// # Note + /// + /// This function is only needed for core interrupts when the vectored trap mode is enabled. + pub fn start_interrupt_trap(&self, ident: &Ident) -> TokenStream { + let interrupt = ident.to_string(); + let width = self.width(); + let trap_size = self.trap_frame().len(); + let store = self.store_trap(|r| r == "a0"); + + format!( + r#" +#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] +core::arch::global_asm!( +" +.section .trap.start.{interrupt}, \"ax\" +.balign 4 +.global _start_{interrupt}_trap +_start_{interrupt}_trap: + addi sp, sp, -{trap_size} * {width} + {store} + la a0, {interrupt} + j _continue_interrupt_trap +" +); "# ) .parse() diff --git a/riscv-rt/CHANGELOG.md b/riscv-rt/CHANGELOG.md index f4eb0ca2..7540b492 100644 --- a/riscv-rt/CHANGELOG.md +++ b/riscv-rt/CHANGELOG.md @@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Use macros from `riscv-macros` instead of `riscv-rt-macros` -## v0.17.1 - 2026-01-13 +## v0.18.0 - 2026-01-13 ### Fixed diff --git a/riscv-rt/Cargo.toml b/riscv-rt/Cargo.toml index c576f92e..8615ada4 100644 --- a/riscv-rt/Cargo.toml +++ b/riscv-rt/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "riscv-rt" -version = "0.17.2" +version = "0.18.0" rust-version = "1.81" repository = "https://github.com/rust-embedded/riscv" authors = ["The RISC-V Team "] diff --git a/riscv-rt/src/lib.rs b/riscv-rt/src/lib.rs index b1f26adb..1e863e3d 100644 --- a/riscv-rt/src/lib.rs +++ b/riscv-rt/src/lib.rs @@ -699,8 +699,7 @@ use riscv::register::{ mtvec::{self as xtvec, Mtvec as Xtvec, TrapMode}, }; -pub use riscv_macros::entry; -pub use riscv_rt_macros::{core_interrupt, exception, external_interrupt}; +pub use riscv_macros::{core_interrupt, entry, exception, external_interrupt}; pub use riscv_types::*; #[cfg(feature = "post-init")] diff --git a/tests-trybuild/tests/riscv-rt/core_interrupt/fail_empty_args.rs b/tests-trybuild/tests/riscv-rt/core_interrupt/fail_empty_args.rs new file mode 100644 index 00000000..a0205ecf --- /dev/null +++ b/tests-trybuild/tests/riscv-rt/core_interrupt/fail_empty_args.rs @@ -0,0 +1,4 @@ +#[riscv_rt::core_interrupt] +pub const async extern "Rust" fn my_interrupt(_a: T, _b: ...) -> usize {} + +fn main() {} diff --git a/tests-trybuild/tests/riscv-rt/core_interrupt/fail_empty_args.stderr b/tests-trybuild/tests/riscv-rt/core_interrupt/fail_empty_args.stderr new file mode 100644 index 00000000..caf94d75 --- /dev/null +++ b/tests-trybuild/tests/riscv-rt/core_interrupt/fail_empty_args.stderr @@ -0,0 +1,55 @@ +error: attribute expects a path to a variant of an enum that implements the `riscv_rt::CoreInterruptNumber` trait + --> tests/riscv-rt/core_interrupt/fail_empty_args.rs:1:1 + | +1 | #[riscv_rt::core_interrupt] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `riscv_rt::core_interrupt` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: function must be private + --> tests/riscv-rt/core_interrupt/fail_empty_args.rs:2:1 + | +2 | pub const async extern "Rust" fn my_interrupt(_a: T, _b: ...) -> usize {} + | ^^^ + +error: function must not be const + --> tests/riscv-rt/core_interrupt/fail_empty_args.rs:2:5 + | +2 | pub const async extern "Rust" fn my_interrupt(_a: T, _b: ...) -> usize {} + | ^^^^^ + +error: function must not be async + --> tests/riscv-rt/core_interrupt/fail_empty_args.rs:2:11 + | +2 | pub const async extern "Rust" fn my_interrupt(_a: T, _b: ...) -> usize {} + | ^^^^^ + +error: ABI must not be specified + --> tests/riscv-rt/core_interrupt/fail_empty_args.rs:2:17 + | +2 | pub const async extern "Rust" fn my_interrupt(_a: T, _b: ...) -> usize {} + | ^^^^^^ + +error: generics are not allowed + --> tests/riscv-rt/core_interrupt/fail_empty_args.rs:2:47 + | +2 | pub const async extern "Rust" fn my_interrupt(_a: T, _b: ...) -> usize {} + | ^ + +error: too many input arguments + --> tests/riscv-rt/core_interrupt/fail_empty_args.rs:2:50 + | +2 | pub const async extern "Rust" fn my_interrupt(_a: T, _b: ...) -> usize {} + | ^^ + +error: variadic arguments are not allowed + --> tests/riscv-rt/core_interrupt/fail_empty_args.rs:2:57 + | +2 | pub const async extern "Rust" fn my_interrupt(_a: T, _b: ...) -> usize {} + | ^^ + +error: return type must be () or ! + --> tests/riscv-rt/core_interrupt/fail_empty_args.rs:2:66 + | +2 | pub const async extern "Rust" fn my_interrupt(_a: T, _b: ...) -> usize {} + | ^ diff --git a/tests-trybuild/tests/riscv-rt/core_interrupt/fail_empty_macro.rs b/tests-trybuild/tests/riscv-rt/core_interrupt/fail_empty_macro.rs deleted file mode 100644 index 0cec9d6c..00000000 --- a/tests-trybuild/tests/riscv-rt/core_interrupt/fail_empty_macro.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[riscv_rt::core_interrupt] -fn my_interrupt() {} - -fn main() {} diff --git a/tests-trybuild/tests/riscv-rt/core_interrupt/fail_empty_macro.stderr b/tests-trybuild/tests/riscv-rt/core_interrupt/fail_empty_macro.stderr deleted file mode 100644 index 99d53458..00000000 --- a/tests-trybuild/tests/riscv-rt/core_interrupt/fail_empty_macro.stderr +++ /dev/null @@ -1,7 +0,0 @@ -error: `#[core_interrupt]` attribute expects a path to a variant of an enum that implements the riscv_rt :: CoreInterruptNumber trait. - --> tests/riscv-rt/core_interrupt/fail_empty_macro.rs:1:1 - | -1 | #[riscv_rt::core_interrupt] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the attribute macro `riscv_rt::core_interrupt` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests-trybuild/tests/riscv-rt/core_interrupt/fail_impl_interrupt_number.rs b/tests-trybuild/tests/riscv-rt/core_interrupt/fail_impl.rs similarity index 100% rename from tests-trybuild/tests/riscv-rt/core_interrupt/fail_impl_interrupt_number.rs rename to tests-trybuild/tests/riscv-rt/core_interrupt/fail_impl.rs diff --git a/tests-trybuild/tests/riscv-rt/core_interrupt/fail_impl_interrupt_number.stderr b/tests-trybuild/tests/riscv-rt/core_interrupt/fail_impl.stderr similarity index 60% rename from tests-trybuild/tests/riscv-rt/core_interrupt/fail_impl_interrupt_number.stderr rename to tests-trybuild/tests/riscv-rt/core_interrupt/fail_impl.stderr index 96e6fba5..ce2a266f 100644 --- a/tests-trybuild/tests/riscv-rt/core_interrupt/fail_impl_interrupt_number.stderr +++ b/tests-trybuild/tests/riscv-rt/core_interrupt/fail_impl.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `riscv::interrupt::Exception: CoreInterruptNumber` is not satisfied - --> tests/riscv-rt/core_interrupt/fail_impl_interrupt_number.rs:1:28 + --> tests/riscv-rt/core_interrupt/fail_impl.rs:1:28 | 1 | #[riscv_rt::core_interrupt(riscv::interrupt::Exception::LoadMisaligned)] | ---------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- @@ -7,11 +7,18 @@ error[E0277]: the trait bound `riscv::interrupt::Exception: CoreInterruptNumber` | | the trait `CoreInterruptNumber` is not implemented for `riscv::interrupt::Exception` | required by a bound introduced by this call | - = help: the following other types implement trait `CoreInterruptNumber`: - riscv::interrupt::Interrupt - riscv::interrupt::supervisor::Interrupt +help: the following other types implement trait `CoreInterruptNumber` + --> $WORKSPACE/riscv/src/interrupt/machine.rs + | + | unsafe impl CoreInterruptNumber for Interrupt {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `riscv::interrupt::Interrupt` + | + ::: $WORKSPACE/riscv/src/interrupt/supervisor.rs + | + | unsafe impl CoreInterruptNumber for Interrupt {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `riscv::interrupt::supervisor::Interrupt` note: required by a bound in `assert_impl` - --> tests/riscv-rt/core_interrupt/fail_impl_interrupt_number.rs:1:1 + --> tests/riscv-rt/core_interrupt/fail_impl.rs:1:1 | 1 | #[riscv_rt::core_interrupt(riscv::interrupt::Exception::LoadMisaligned)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_impl` diff --git a/tests-trybuild/tests/riscv-rt/core_interrupt/fail_signatures.rs b/tests-trybuild/tests/riscv-rt/core_interrupt/fail_signatures.rs deleted file mode 100644 index 0beab5b9..00000000 --- a/tests-trybuild/tests/riscv-rt/core_interrupt/fail_signatures.rs +++ /dev/null @@ -1,10 +0,0 @@ -#[riscv_rt::core_interrupt(riscv::interrupt::Interrupt::SupervisorSoft)] -fn my_interrupt(code: usize) {} - -#[riscv_rt::core_interrupt(riscv::interrupt::Interrupt::SupervisorTimer)] -fn my_other_interrupt() -> usize {} - -#[riscv_rt::core_interrupt(riscv::interrupt::Interrupt::SupervisorExternal)] -async fn my_async_interrupt() {} - -fn main() {} diff --git a/tests-trybuild/tests/riscv-rt/core_interrupt/fail_signatures.stderr b/tests-trybuild/tests/riscv-rt/core_interrupt/fail_signatures.stderr deleted file mode 100644 index 38b0575d..00000000 --- a/tests-trybuild/tests/riscv-rt/core_interrupt/fail_signatures.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: `#[core_interrupt]` function must have signature `[unsafe] [extern "C"] fn() [-> !]` - --> tests/riscv-rt/core_interrupt/fail_signatures.rs:2:1 - | -2 | fn my_interrupt(code: usize) {} - | ^^ - -error: `#[core_interrupt]` function must have signature `[unsafe] [extern "C"] fn() [-> !]` - --> tests/riscv-rt/core_interrupt/fail_signatures.rs:5:1 - | -5 | fn my_other_interrupt() -> usize {} - | ^^ - -error: `#[core_interrupt]` function must have signature `[unsafe] [extern "C"] fn() [-> !]` - --> tests/riscv-rt/core_interrupt/fail_signatures.rs:8:1 - | -8 | async fn my_async_interrupt() {} - | ^^^^^ diff --git a/tests-trybuild/tests/riscv-rt/exception/fail_empty_args.rs b/tests-trybuild/tests/riscv-rt/exception/fail_empty_args.rs new file mode 100644 index 00000000..de7c5938 --- /dev/null +++ b/tests-trybuild/tests/riscv-rt/exception/fail_empty_args.rs @@ -0,0 +1,4 @@ +#[riscv_rt::exception] +pub const async extern "Rust" fn my_exception(_a: T, _b: usize, _c: ...) -> usize {} + +fn main() {} diff --git a/tests-trybuild/tests/riscv-rt/exception/fail_empty_args.stderr b/tests-trybuild/tests/riscv-rt/exception/fail_empty_args.stderr new file mode 100644 index 00000000..c7cbbfd0 --- /dev/null +++ b/tests-trybuild/tests/riscv-rt/exception/fail_empty_args.stderr @@ -0,0 +1,61 @@ +error: attribute expects a path to a variant of an enum that implements the `riscv_rt::ExceptionNumber` trait + --> tests/riscv-rt/exception/fail_empty_args.rs:1:1 + | +1 | #[riscv_rt::exception] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `riscv_rt::exception` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: function must be private + --> tests/riscv-rt/exception/fail_empty_args.rs:2:1 + | +2 | pub const async extern "Rust" fn my_exception(_a: T, _b: usize, _c: ...) -> usize {} + | ^^^ + +error: function must not be const + --> tests/riscv-rt/exception/fail_empty_args.rs:2:5 + | +2 | pub const async extern "Rust" fn my_exception(_a: T, _b: usize, _c: ...) -> usize {} + | ^^^^^ + +error: function must not be async + --> tests/riscv-rt/exception/fail_empty_args.rs:2:11 + | +2 | pub const async extern "Rust" fn my_exception(_a: T, _b: usize, _c: ...) -> usize {} + | ^^^^^ + +error: ABI must not be specified + --> tests/riscv-rt/exception/fail_empty_args.rs:2:17 + | +2 | pub const async extern "Rust" fn my_exception(_a: T, _b: usize, _c: ...) -> usize {} + | ^^^^^^ + +error: generics are not allowed + --> tests/riscv-rt/exception/fail_empty_args.rs:2:47 + | +2 | pub const async extern "Rust" fn my_exception(_a: T, _b: usize, _c: ...) -> usize {} + | ^ + +error: argument type must be `&riscv_rt::TrapFrame` + --> tests/riscv-rt/exception/fail_empty_args.rs:2:54 + | +2 | pub const async extern "Rust" fn my_exception(_a: T, _b: usize, _c: ...) -> usize {} + | ^ + +error: too many input arguments + --> tests/riscv-rt/exception/fail_empty_args.rs:2:57 + | +2 | pub const async extern "Rust" fn my_exception(_a: T, _b: usize, _c: ...) -> usize {} + | ^^ + +error: variadic arguments are not allowed + --> tests/riscv-rt/exception/fail_empty_args.rs:2:68 + | +2 | pub const async extern "Rust" fn my_exception(_a: T, _b: usize, _c: ...) -> usize {} + | ^^ + +error: return type must be () or ! + --> tests/riscv-rt/exception/fail_empty_args.rs:2:77 + | +2 | pub const async extern "Rust" fn my_exception(_a: T, _b: usize, _c: ...) -> usize {} + | ^ diff --git a/tests-trybuild/tests/riscv-rt/exception/fail_empty_macro.rs b/tests-trybuild/tests/riscv-rt/exception/fail_empty_macro.rs deleted file mode 100644 index 48b8af93..00000000 --- a/tests-trybuild/tests/riscv-rt/exception/fail_empty_macro.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[riscv_rt::exception] -fn my_exception() {} - -fn main() {} diff --git a/tests-trybuild/tests/riscv-rt/exception/fail_empty_macro.stderr b/tests-trybuild/tests/riscv-rt/exception/fail_empty_macro.stderr deleted file mode 100644 index 7489bd94..00000000 --- a/tests-trybuild/tests/riscv-rt/exception/fail_empty_macro.stderr +++ /dev/null @@ -1,7 +0,0 @@ -error: `#[exception]` attribute expects a path to a variant of an enum that implements the riscv_rt :: ExceptionNumber trait. - --> tests/riscv-rt/exception/fail_empty_macro.rs:1:1 - | -1 | #[riscv_rt::exception] - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the attribute macro `riscv_rt::exception` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests-trybuild/tests/riscv-rt/exception/fail_impl_exception_number.rs b/tests-trybuild/tests/riscv-rt/exception/fail_impl.rs similarity index 100% rename from tests-trybuild/tests/riscv-rt/exception/fail_impl_exception_number.rs rename to tests-trybuild/tests/riscv-rt/exception/fail_impl.rs diff --git a/tests-trybuild/tests/riscv-rt/exception/fail_impl_exception_number.stderr b/tests-trybuild/tests/riscv-rt/exception/fail_impl.stderr similarity index 60% rename from tests-trybuild/tests/riscv-rt/exception/fail_impl_exception_number.stderr rename to tests-trybuild/tests/riscv-rt/exception/fail_impl.stderr index ce4c506b..519f8258 100644 --- a/tests-trybuild/tests/riscv-rt/exception/fail_impl_exception_number.stderr +++ b/tests-trybuild/tests/riscv-rt/exception/fail_impl.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `riscv::interrupt::Interrupt: ExceptionNumber` is not satisfied - --> tests/riscv-rt/exception/fail_impl_exception_number.rs:1:23 + --> tests/riscv-rt/exception/fail_impl.rs:1:23 | 1 | #[riscv_rt::exception(riscv::interrupt::Interrupt::SupervisorSoft)] | ----------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- @@ -7,11 +7,18 @@ error[E0277]: the trait bound `riscv::interrupt::Interrupt: ExceptionNumber` is | | the trait `ExceptionNumber` is not implemented for `riscv::interrupt::Interrupt` | required by a bound introduced by this call | - = help: the following other types implement trait `ExceptionNumber`: - riscv::interrupt::Exception - riscv::interrupt::supervisor::Exception +help: the following other types implement trait `ExceptionNumber` + --> $WORKSPACE/riscv/src/interrupt/machine.rs + | + | unsafe impl ExceptionNumber for Exception { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `riscv::interrupt::Exception` + | + ::: $WORKSPACE/riscv/src/interrupt/supervisor.rs + | + | unsafe impl ExceptionNumber for Exception { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `riscv::interrupt::supervisor::Exception` note: required by a bound in `assert_impl` - --> tests/riscv-rt/exception/fail_impl_exception_number.rs:1:1 + --> tests/riscv-rt/exception/fail_impl.rs:1:1 | 1 | #[riscv_rt::exception(riscv::interrupt::Interrupt::SupervisorSoft)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_impl` diff --git a/tests-trybuild/tests/riscv-rt/exception/fail_signatures.rs b/tests-trybuild/tests/riscv-rt/exception/fail_signatures.rs deleted file mode 100644 index 6970d9be..00000000 --- a/tests-trybuild/tests/riscv-rt/exception/fail_signatures.rs +++ /dev/null @@ -1,10 +0,0 @@ -#[riscv_rt::exception(riscv::interrupt::Exception::LoadMisaligned)] -fn my_exception(code: usize) {} - -#[riscv_rt::exception(riscv::interrupt::Exception::StoreMisaligned)] -fn my_other_exception(trap_frame: &riscv_rt::TrapFrame, code: usize) {} - -#[riscv_rt::exception(riscv::interrupt::Exception::LoadFault)] -async fn my_async_exception(trap_frame: &riscv_rt::TrapFrame, code: usize) {} - -fn main() {} diff --git a/tests-trybuild/tests/riscv-rt/exception/fail_signatures.stderr b/tests-trybuild/tests/riscv-rt/exception/fail_signatures.stderr deleted file mode 100644 index 4d282385..00000000 --- a/tests-trybuild/tests/riscv-rt/exception/fail_signatures.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: `#[exception]` function must have signature `[unsafe] [extern "C"] fn([&[mut] riscv_rt::TrapFrame]) [-> !]` - --> tests/riscv-rt/exception/fail_signatures.rs:2:1 - | -2 | fn my_exception(code: usize) {} - | ^^ - -error: `#[exception]` function must have signature `[unsafe] [extern "C"] fn([&[mut] riscv_rt::TrapFrame]) [-> !]` - --> tests/riscv-rt/exception/fail_signatures.rs:5:1 - | -5 | fn my_other_exception(trap_frame: &riscv_rt::TrapFrame, code: usize) {} - | ^^ - -error: `#[exception]` function must have signature `[unsafe] [extern "C"] fn([&[mut] riscv_rt::TrapFrame]) [-> !]` - --> tests/riscv-rt/exception/fail_signatures.rs:8:1 - | -8 | async fn my_async_exception(trap_frame: &riscv_rt::TrapFrame, code: usize) {} - | ^^^^^ diff --git a/tests-trybuild/tests/riscv-rt/exception/pass_exception.rs b/tests-trybuild/tests/riscv-rt/exception/pass_exception.rs index e0cdab5b..714f8f01 100644 --- a/tests-trybuild/tests/riscv-rt/exception/pass_exception.rs +++ b/tests-trybuild/tests/riscv-rt/exception/pass_exception.rs @@ -4,10 +4,10 @@ use riscv::interrupt::Exception::*; fn simple_exception() {} #[riscv_rt::exception(LoadFault)] -fn unmutable_exception(_trap_frame: &riscv_rt::TrapFrame) {} +fn immutable_exception(_trap_frame: &riscv_rt::TrapFrame) {} #[riscv_rt::exception(StoreMisaligned)] -fn mutable_exception(_trap_frame: &mut riscv_rt::TrapFrame) {} +unsafe fn mutable_exception(_trap_frame: &mut riscv_rt::TrapFrame) {} #[riscv_rt::exception(StoreFault)] fn no_return_exception(_trap_frame: &mut riscv_rt::TrapFrame) -> ! { diff --git a/tests-trybuild/tests/riscv-rt/external_interrupt/fail_empty_args.rs b/tests-trybuild/tests/riscv-rt/external_interrupt/fail_empty_args.rs new file mode 100644 index 00000000..603e7175 --- /dev/null +++ b/tests-trybuild/tests/riscv-rt/external_interrupt/fail_empty_args.rs @@ -0,0 +1,4 @@ +#[riscv_rt::external_interrupt] +pub const async extern "Rust" fn my_interrupt(_a: T, _b: ...) -> usize {} + +fn main() {} diff --git a/tests-trybuild/tests/riscv-rt/external_interrupt/fail_empty_args.stderr b/tests-trybuild/tests/riscv-rt/external_interrupt/fail_empty_args.stderr new file mode 100644 index 00000000..04b8302a --- /dev/null +++ b/tests-trybuild/tests/riscv-rt/external_interrupt/fail_empty_args.stderr @@ -0,0 +1,55 @@ +error: attribute expects a path to a variant of an enum that implements the `riscv_rt::ExternalInterruptNumber` trait + --> tests/riscv-rt/external_interrupt/fail_empty_args.rs:1:1 + | +1 | #[riscv_rt::external_interrupt] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `riscv_rt::external_interrupt` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: function must be private + --> tests/riscv-rt/external_interrupt/fail_empty_args.rs:2:1 + | +2 | pub const async extern "Rust" fn my_interrupt(_a: T, _b: ...) -> usize {} + | ^^^ + +error: function must not be const + --> tests/riscv-rt/external_interrupt/fail_empty_args.rs:2:5 + | +2 | pub const async extern "Rust" fn my_interrupt(_a: T, _b: ...) -> usize {} + | ^^^^^ + +error: function must not be async + --> tests/riscv-rt/external_interrupt/fail_empty_args.rs:2:11 + | +2 | pub const async extern "Rust" fn my_interrupt(_a: T, _b: ...) -> usize {} + | ^^^^^ + +error: ABI must not be specified + --> tests/riscv-rt/external_interrupt/fail_empty_args.rs:2:17 + | +2 | pub const async extern "Rust" fn my_interrupt(_a: T, _b: ...) -> usize {} + | ^^^^^^ + +error: generics are not allowed + --> tests/riscv-rt/external_interrupt/fail_empty_args.rs:2:47 + | +2 | pub const async extern "Rust" fn my_interrupt(_a: T, _b: ...) -> usize {} + | ^ + +error: too many input arguments + --> tests/riscv-rt/external_interrupt/fail_empty_args.rs:2:50 + | +2 | pub const async extern "Rust" fn my_interrupt(_a: T, _b: ...) -> usize {} + | ^^ + +error: variadic arguments are not allowed + --> tests/riscv-rt/external_interrupt/fail_empty_args.rs:2:57 + | +2 | pub const async extern "Rust" fn my_interrupt(_a: T, _b: ...) -> usize {} + | ^^ + +error: return type must be () or ! + --> tests/riscv-rt/external_interrupt/fail_empty_args.rs:2:66 + | +2 | pub const async extern "Rust" fn my_interrupt(_a: T, _b: ...) -> usize {} + | ^ diff --git a/tests-trybuild/tests/riscv-rt/external_interrupt/fail_empty_macro.rs b/tests-trybuild/tests/riscv-rt/external_interrupt/fail_empty_macro.rs deleted file mode 100644 index 7767ed24..00000000 --- a/tests-trybuild/tests/riscv-rt/external_interrupt/fail_empty_macro.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[riscv_rt::external_interrupt] -fn my_interrupt() {} - -fn main() {} diff --git a/tests-trybuild/tests/riscv-rt/external_interrupt/fail_empty_macro.stderr b/tests-trybuild/tests/riscv-rt/external_interrupt/fail_empty_macro.stderr deleted file mode 100644 index 5ef53507..00000000 --- a/tests-trybuild/tests/riscv-rt/external_interrupt/fail_empty_macro.stderr +++ /dev/null @@ -1,7 +0,0 @@ -error: `#[external_interrupt]` attribute expects a path to a variant of an enum that implements the riscv_rt :: ExternalInterruptNumber trait. - --> tests/riscv-rt/external_interrupt/fail_empty_macro.rs:1:1 - | -1 | #[riscv_rt::external_interrupt] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the attribute macro `riscv_rt::external_interrupt` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests-trybuild/tests/riscv-rt/external_interrupt/fail_impl_interrupt_number.rs b/tests-trybuild/tests/riscv-rt/external_interrupt/fail_impl.rs similarity index 100% rename from tests-trybuild/tests/riscv-rt/external_interrupt/fail_impl_interrupt_number.rs rename to tests-trybuild/tests/riscv-rt/external_interrupt/fail_impl.rs diff --git a/tests-trybuild/tests/riscv-rt/external_interrupt/fail_impl_interrupt_number.stderr b/tests-trybuild/tests/riscv-rt/external_interrupt/fail_impl.stderr similarity index 85% rename from tests-trybuild/tests/riscv-rt/external_interrupt/fail_impl_interrupt_number.stderr rename to tests-trybuild/tests/riscv-rt/external_interrupt/fail_impl.stderr index 03dace2c..426dc440 100644 --- a/tests-trybuild/tests/riscv-rt/external_interrupt/fail_impl_interrupt_number.stderr +++ b/tests-trybuild/tests/riscv-rt/external_interrupt/fail_impl.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `riscv::interrupt::Interrupt: ExternalInterruptNumber` is not satisfied - --> tests/riscv-rt/external_interrupt/fail_impl_interrupt_number.rs:1:32 + --> tests/riscv-rt/external_interrupt/fail_impl.rs:1:32 | 1 | #[riscv_rt::external_interrupt(riscv::interrupt::Interrupt::SupervisorSoft)] | -------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- @@ -8,7 +8,7 @@ error[E0277]: the trait bound `riscv::interrupt::Interrupt: ExternalInterruptNum | required by a bound introduced by this call | note: required by a bound in `assert_impl` - --> tests/riscv-rt/external_interrupt/fail_impl_interrupt_number.rs:1:1 + --> tests/riscv-rt/external_interrupt/fail_impl.rs:1:1 | 1 | #[riscv_rt::external_interrupt(riscv::interrupt::Interrupt::SupervisorSoft)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_impl` diff --git a/tests-trybuild/tests/riscv-rt/external_interrupt/fail_signatures.rs b/tests-trybuild/tests/riscv-rt/external_interrupt/fail_signatures.rs deleted file mode 100644 index 29757bae..00000000 --- a/tests-trybuild/tests/riscv-rt/external_interrupt/fail_signatures.rs +++ /dev/null @@ -1,39 +0,0 @@ -use riscv_rt::result::{Error, Result}; - -/// Just a dummy type to test the `ExternalInterrupt` trait. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum ExternalInterrupt { - GPIO, - UART, - PWM, -} -unsafe impl riscv_rt::InterruptNumber for ExternalInterrupt { - const MAX_INTERRUPT_NUMBER: usize = 2; - - #[inline] - fn number(self) -> usize { - self as usize - } - - #[inline] - fn from_number(value: usize) -> Result { - match value { - 0 => Ok(Self::GPIO), - 1 => Ok(Self::UART), - 2 => Ok(Self::PWM), - _ => Err(Error::InvalidVariant(value)), - } - } -} -unsafe impl riscv::ExternalInterruptNumber for ExternalInterrupt {} - -#[riscv_rt::external_interrupt(ExternalInterrupt::GPIO)] -fn my_interrupt() -> usize {} - -#[riscv_rt::external_interrupt(ExternalInterrupt::UART)] -fn my_other_interrupt(code: usize) -> usize {} - -#[riscv_rt::external_interrupt(ExternalInterrupt::PWM)] -async fn my_async_interrupt(code: usize) -> usize {} - -fn main() {} diff --git a/tests-trybuild/tests/riscv-rt/external_interrupt/fail_signatures.stderr b/tests-trybuild/tests/riscv-rt/external_interrupt/fail_signatures.stderr deleted file mode 100644 index 4f202b3c..00000000 --- a/tests-trybuild/tests/riscv-rt/external_interrupt/fail_signatures.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: `#[external_interrupt]` function must have signature `[unsafe] [extern "C"] fn() [-> !]` - --> tests/riscv-rt/external_interrupt/fail_signatures.rs:31:1 - | -31 | fn my_interrupt() -> usize {} - | ^^ - -error: `#[external_interrupt]` function must have signature `[unsafe] [extern "C"] fn() [-> !]` - --> tests/riscv-rt/external_interrupt/fail_signatures.rs:34:1 - | -34 | fn my_other_interrupt(code: usize) -> usize {} - | ^^ - -error: `#[external_interrupt]` function must have signature `[unsafe] [extern "C"] fn() [-> !]` - --> tests/riscv-rt/external_interrupt/fail_signatures.rs:37:1 - | -37 | async fn my_async_interrupt(code: usize) -> usize {} - | ^^^^^ From aa8c38995764bf3adfb9df67ef0a7247fb1b26aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas=20Rodr=C3=ADguez?= Date: Mon, 2 Feb 2026 12:13:01 +0100 Subject: [PATCH 5/5] riscv-rt: deprecate riscv-rt-macros; remove `pre_init` attribute macro --- riscv-rt/CHANGELOG.md | 6 +++++- riscv-rt/Cargo.toml | 11 +++++------ riscv-rt/macros/Cargo.toml | 2 +- riscv-rt/macros/src/lib.rs | 2 ++ riscv-rt/src/lib.rs | 3 --- tests-build/src/lib.rs | 10 ++++++++-- 6 files changed, 21 insertions(+), 13 deletions(-) diff --git a/riscv-rt/CHANGELOG.md b/riscv-rt/CHANGELOG.md index 7540b492..5de59ea6 100644 --- a/riscv-rt/CHANGELOG.md +++ b/riscv-rt/CHANGELOG.md @@ -5,12 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## v0.17.2 - Unreleased +## v0.19.0 - Unreleased ### Changed - Use macros from `riscv-macros` instead of `riscv-rt-macros` +### Removed + +- We no longer re-export the `pre_init` macro. Use `core::arch::global_asm` instead. + ## v0.18.0 - 2026-01-13 ### Fixed diff --git a/riscv-rt/Cargo.toml b/riscv-rt/Cargo.toml index 8615ada4..69f49c15 100644 --- a/riscv-rt/Cargo.toml +++ b/riscv-rt/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "riscv-rt" -version = "0.18.0" +version = "0.19.0" rust-version = "1.81" repository = "https://github.com/rust-embedded/riscv" authors = ["The RISC-V Team "] @@ -26,7 +26,6 @@ riscv-target-parser = { path = "../riscv-target-parser", version = "0.1.2" } [dependencies] riscv = { path = "../riscv", version = "0.16.0", features = ["rt"] } riscv-types = { path = "../riscv-types", version = "0.1.0" } -riscv-rt-macros = { path = "macros", version = "0.6.1" } riscv-macros = { path = "../riscv-macros", version = "0.4.1", features = ["riscv-rt"] } defmt = { version = "1.0.1", optional = true } @@ -39,10 +38,10 @@ riscv = { path = "../riscv", version = "0.16.0", features = ["critical-section-s [features] pre-init = [] post-init = [] -s-mode = ["riscv-rt-macros/s-mode", "riscv-macros/s-mode"] +s-mode = ["riscv-macros/s-mode"] single-hart = [] -v-trap = ["riscv-rt-macros/v-trap", "riscv/rt-v-trap", "riscv-macros/rt-v-trap"] -u-boot = ["riscv-rt-macros/u-boot", "riscv-macros/rvrt-u-boot", "single-hart"] +v-trap = ["riscv/rt-v-trap", "riscv-macros/rt-v-trap"] +u-boot = ["riscv-macros/rvrt-u-boot", "single-hart"] no-interrupts = [] no-exceptions = [] no-mhartid = ["single-hart"] @@ -51,4 +50,4 @@ no-xtvec = [] device = [] memory = [] defmt = ["dep:defmt"] -pre-default-start-trap = ["riscv-rt-macros/pre-default-start-trap", "riscv-macros/rvrt-pre-default-start-trap"] +pre-default-start-trap = ["riscv-macros/rvrt-pre-default-start-trap"] diff --git a/riscv-rt/macros/Cargo.toml b/riscv-rt/macros/Cargo.toml index f821e413..ed7ce48f 100644 --- a/riscv-rt/macros/Cargo.toml +++ b/riscv-rt/macros/Cargo.toml @@ -10,7 +10,7 @@ keywords = ["riscv", "runtime", "startup"] license = "MIT OR Apache-2.0" name = "riscv-rt-macros" repository = "https://github.com/rust-embedded/riscv" -version = "0.6.1" +version = "0.7.0" edition = "2021" [lib] diff --git a/riscv-rt/macros/src/lib.rs b/riscv-rt/macros/src/lib.rs index 4d899bb9..92b9d4fd 100644 --- a/riscv-rt/macros/src/lib.rs +++ b/riscv-rt/macros/src/lib.rs @@ -1,3 +1,5 @@ +//! This crate has been deprecated. Please use the `riscv-macros` crate instead. + #![deny(warnings)] #![allow(unknown_lints)] // reason = "required for next line" #![allow(clippy::manual_is_multiple_of)] // reason = "requires MSRV bump" diff --git a/riscv-rt/src/lib.rs b/riscv-rt/src/lib.rs index 1e863e3d..35d460ca 100644 --- a/riscv-rt/src/lib.rs +++ b/riscv-rt/src/lib.rs @@ -671,7 +671,6 @@ //! [attr-exception]: attr.exception.html //! [attr-external-interrupt]: attr.external_interrupt.html //! [attr-core-interrupt]: attr.core_interrupt.html -//! [attr-pre-init]: attr.pre_init.html //! [attr-post-init]: attr.post_init.html // NOTE: Adapted from cortex-m/src/lib.rs @@ -704,8 +703,6 @@ pub use riscv_types::*; #[cfg(feature = "post-init")] pub use riscv_macros::post_init; -#[cfg(feature = "pre-init")] -pub use riscv_rt_macros::pre_init; /// We export this static with an informative name so that if an application attempts to link /// two copies of riscv-rt together, linking will fail. We also declare a links key in diff --git a/tests-build/src/lib.rs b/tests-build/src/lib.rs index ae51a665..dc5c4c4b 100644 --- a/tests-build/src/lib.rs +++ b/tests-build/src/lib.rs @@ -43,5 +43,11 @@ pub enum Exception { } #[cfg(feature = "pre-init")] -#[cfg_attr(feature = "pre-init", riscv_rt::pre_init)] -unsafe fn pre_init() {} +core::arch::global_asm!( + " +.section .init.pre_init, \"ax\" +.global __pre_init +__pre_init: + // Do some pre-initialization work here and return + ret" +);