diff options
Diffstat (limited to 'macros/src/codegen.rs')
-rw-r--r-- | macros/src/codegen.rs | 298 |
1 files changed, 173 insertions, 125 deletions
diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index af3def64..af09ad42 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -2,8 +2,7 @@ use proc_macro::TokenStream; use std::{ - collections::HashMap, - sync::atomic::{AtomicUsize, Ordering}, + collections::{BTreeMap, HashMap}, time::{SystemTime, UNIX_EPOCH}, }; @@ -20,13 +19,13 @@ use crate::{ // NOTE to avoid polluting the user namespaces we map some identifiers to pseudo-hygienic names. // In some instances we also use the pseudo-hygienic names for safety, for example the user should // not modify the priority field of resources. -type Aliases = HashMap<Ident, Ident>; +type Aliases = BTreeMap<Ident, Ident>; struct Context { // Alias #[cfg(feature = "timer-queue")] baseline: Ident, - dispatchers: HashMap<u8, Dispatcher>, + dispatchers: BTreeMap<u8, Dispatcher>, // Alias (`fn`) idle: Ident, // Alias (`fn`) @@ -41,9 +40,11 @@ struct Context { schedule_enum: Ident, // Task -> Alias (`fn`) schedule_fn: Aliases, - tasks: HashMap<Ident, Task>, + tasks: BTreeMap<Ident, Task>, // Alias (`struct` / `static mut`) timer_queue: Ident, + // Generator of Ident names or suffixes + ident_gen: IdentGenerator, } struct Dispatcher { @@ -63,19 +64,22 @@ struct Task { impl Default for Context { fn default() -> Self { + let mut ident_gen = IdentGenerator::new(); + Context { #[cfg(feature = "timer-queue")] - baseline: mk_ident(None), - dispatchers: HashMap::new(), - idle: mk_ident(Some("idle")), - init: mk_ident(Some("init")), - priority: mk_ident(None), + baseline: ident_gen.mk_ident(None, false), + dispatchers: BTreeMap::new(), + idle: ident_gen.mk_ident(Some("idle"), false), + init: ident_gen.mk_ident(Some("init"), false), + priority: ident_gen.mk_ident(None, false), statics: Aliases::new(), resources: HashMap::new(), - schedule_enum: mk_ident(None), + schedule_enum: ident_gen.mk_ident(None, false), schedule_fn: Aliases::new(), - tasks: HashMap::new(), - timer_queue: mk_ident(None), + tasks: BTreeMap::new(), + timer_queue: ident_gen.mk_ident(None, false), + ident_gen, } } } @@ -164,13 +168,13 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream { () => quote!(), }; - let timer_queue = timer_queue(&ctxt, app, analysis); + let timer_queue = timer_queue(&mut ctxt, app, analysis); let pre_init = pre_init(&ctxt, &app, analysis); let assertions = assertions(app, analysis); - let main = mk_ident(None); + let main = ctxt.ident_gen.mk_ident(None, false); quote!( #resources @@ -236,7 +240,7 @@ fn resources(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2: pub static #mut_ #name: #ty = #expr; )); - let alias = mk_ident(None); + let alias = ctxt.ident_gen.mk_ident(None, true); // XXX is randomness required? if let Some(Ownership::Shared { ceiling }) = analysis.ownerships.get(name) { items.push(mk_resource( ctxt, @@ -252,7 +256,7 @@ fn resources(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2: ctxt.statics.insert(name.clone(), alias); } else { - let alias = mk_ident(None); + let alias = ctxt.ident_gen.mk_ident(None, false); let symbol = format!("{}::{}", name, alias); items.push( @@ -360,7 +364,7 @@ fn init(ctxt: &mut Context, app: &App, analysis: &Analysis) -> (proc_macro2::Tok let (late_resources, late_resources_ident, ret) = if app.init.returns_late_resources { // create `LateResources` struct in the root of the crate - let ident = mk_ident(None); + let ident = ctxt.ident_gen.mk_ident(None, false); let fields = app .resources @@ -405,7 +409,7 @@ fn init(ctxt: &mut Context, app: &App, analysis: &Analysis) -> (proc_macro2::Tok let baseline = &ctxt.baseline; let baseline_let = match () { #[cfg(feature = "timer-queue")] - () => quote!(let #baseline = rtfm::Instant::artificial(0);), + () => quote!(let ref #baseline = rtfm::Instant::artificial(0);), #[cfg(not(feature = "timer-queue"))] () => quote!(), @@ -415,7 +419,7 @@ fn init(ctxt: &mut Context, app: &App, analysis: &Analysis) -> (proc_macro2::Tok #[cfg(feature = "timer-queue")] () => quote!( #[allow(unused_variables)] - let start = #baseline; + let start = *#baseline; ), #[cfg(not(feature = "timer-queue"))] () => quote!(), @@ -430,21 +434,27 @@ fn init(ctxt: &mut Context, app: &App, analysis: &Analysis) -> (proc_macro2::Tok #module + // unsafe trampoline to deter end-users from calling this non-reentrant function #(#attrs)* - #unsafety fn #init(mut core: rtfm::Peripherals) #ret { - #(#locals)* + unsafe fn #init(core: rtfm::Peripherals) #ret { + #[inline(always)] + #unsafety fn init(mut core: rtfm::Peripherals) #ret { + #(#locals)* + + #baseline_let - #baseline_let + #prelude - #prelude + let mut device = unsafe { #device::Peripherals::steal() }; - let mut device = unsafe { #device::Peripherals::steal() }; + #start_let - #start_let + #(#stmts)* - #(#stmts)* + #(#assigns)* + } - #(#assigns)* + init(core) } ), has_late_resources, @@ -563,7 +573,7 @@ fn module( #[derive(Clone, Copy)] pub struct Schedule<'a> { #[doc(hidden)] - pub #priority: &'a core::cell::Cell<u8>, + pub #priority: &'a rtfm::export::Priority, } )); } @@ -582,7 +592,7 @@ fn module( #[derive(Clone, Copy)] pub struct Spawn<'a> { #[doc(hidden)] - pub #priority: &'a core::cell::Cell<u8>, + pub #priority: &'a rtfm::export::Priority, } )); } else { @@ -591,8 +601,10 @@ fn module( () => { let baseline = &ctxt.baseline; quote!( + // NOTE this field is visible so we use a shared reference to make it + // immutable #[doc(hidden)] - pub #baseline: rtfm::Instant, + pub #baseline: &'a rtfm::Instant, ) } #[cfg(not(feature = "timer-queue"))] @@ -605,7 +617,7 @@ fn module( pub struct Spawn<'a> { #baseline_field #[doc(hidden)] - pub #priority: &'a core::cell::Cell<u8>, + pub #priority: &'a rtfm::export::Priority, } )); } @@ -687,7 +699,7 @@ fn prelude( let mut exprs = vec![]; // NOTE This field is just to avoid unused type parameter errors around `'a` - defs.push(quote!(#[allow(dead_code)] #priority: &'a core::cell::Cell<u8>)); + defs.push(quote!(#[allow(dead_code)] pub #priority: &'a rtfm::export::Priority)); exprs.push(parse_quote!(#priority)); let mut may_call_lock = false; @@ -826,7 +838,8 @@ fn prelude( #(#cfgs)* pub #name: &'a #mut_ #name )); - let alias = mk_ident(None); + // XXX is randomness required? + let alias = ctxt.ident_gen.mk_ident(None, true); items.push(quote!( #(#cfgs)* let #mut_ #alias = unsafe { @@ -843,7 +856,8 @@ fn prelude( #(#cfgs)* pub #name: rtfm::Exclusive<'a, #name> )); - let alias = mk_ident(None); + // XXX is randomness required? + let alias = ctxt.ident_gen.mk_ident(None, true); items.push(quote!( #(#cfgs)* let #mut_ #alias = unsafe { @@ -910,7 +924,7 @@ fn prelude( } } - let alias = mk_ident(None); + let alias = ctxt.ident_gen.mk_ident(None, false); let unsafety = if needs_unsafe { Some(quote!(unsafe)) } else { @@ -971,7 +985,8 @@ fn prelude( continue; } - ctxt.schedule_fn.insert(task.clone(), mk_ident(None)); + ctxt.schedule_fn + .insert(task.clone(), ctxt.ident_gen.mk_ident(None, false)); } items.push(quote!( @@ -987,7 +1002,7 @@ fn prelude( quote!() } else { quote!( - let ref #priority = core::cell::Cell::new(#logical_prio); + let ref #priority = unsafe { rtfm::export::Priority::new(#logical_prio) }; #(#items)* ) @@ -1031,13 +1046,19 @@ fn idle( quote!( #module + // unsafe trampoline to deter end-users from calling this non-reentrant function #(#attrs)* - #unsafety fn #idle() -> ! { - #(#locals)* + unsafe fn #idle() -> ! { + #[inline(always)] + #unsafety fn idle() -> ! { + #(#locals)* - #prelude + #prelude - #(#stmts)* + #(#stmts)* + } + + idle() } ), quote!(#idle()), @@ -1083,7 +1104,7 @@ fn exceptions(ctxt: &mut Context, app: &App, analysis: &Analysis) -> Vec<proc_ma let baseline = &ctxt.baseline; let baseline_let = match () { #[cfg(feature = "timer-queue")] - () => quote!(let #baseline = rtfm::Instant::now();), + () => quote!(let ref #baseline = rtfm::Instant::now();), #[cfg(not(feature = "timer-queue"))] () => quote!(), }; @@ -1092,7 +1113,7 @@ fn exceptions(ctxt: &mut Context, app: &App, analysis: &Analysis) -> Vec<proc_ma #[cfg(feature = "timer-queue")] () => quote!( #[allow(unused_variables)] - let start = #baseline; + let start = *#baseline; ), #[cfg(not(feature = "timer-queue"))] () => quote!(), @@ -1100,26 +1121,31 @@ fn exceptions(ctxt: &mut Context, app: &App, analysis: &Analysis) -> Vec<proc_ma let locals = mk_locals(&exception.statics, false); let symbol = ident.to_string(); - let alias = mk_ident(None); + let alias = ctxt.ident_gen.mk_ident(None, false); let unsafety = &exception.unsafety; quote!( #module - #[doc(hidden)] + // unsafe trampoline to deter end-users from calling this non-reentrant function #[export_name = #symbol] #(#attrs)* - #unsafety fn #alias() { - #(#locals)* + unsafe fn #alias() { + #[inline(always)] + #unsafety fn exception() { + #(#locals)* - #baseline_let + #baseline_let - #prelude + #prelude - #start_let + #start_let - rtfm::export::run(move || { - #(#stmts)* - }) + rtfm::export::run(move || { + #(#stmts)* + }) + } + + exception() } ) }) @@ -1163,7 +1189,7 @@ fn interrupts( let baseline = &ctxt.baseline; let baseline_let = match () { #[cfg(feature = "timer-queue")] - () => quote!(let #baseline = rtfm::Instant::now();), + () => quote!(let ref #baseline = rtfm::Instant::now();), #[cfg(not(feature = "timer-queue"))] () => quote!(), }; @@ -1172,34 +1198,40 @@ fn interrupts( #[cfg(feature = "timer-queue")] () => quote!( #[allow(unused_variables)] - let start = #baseline; + let start = *#baseline; ), #[cfg(not(feature = "timer-queue"))] () => quote!(), }; let locals = mk_locals(&interrupt.statics, false); - let alias = mk_ident(None); + let alias = ctxt.ident_gen.mk_ident(None, false); let symbol = ident.to_string(); let unsafety = &interrupt.unsafety; scoped.push(quote!( + // unsafe trampoline to deter end-users from calling this non-reentrant function #(#attrs)* #[export_name = #symbol] - #unsafety fn #alias() { - // check that this interrupt exists - let _ = #device::interrupt::#ident; + unsafe fn #alias() { + #[inline(always)] + #unsafety fn interrupt() { + // check that this interrupt exists + let _ = #device::interrupt::#ident; - #(#locals)* + #(#locals)* - #baseline_let + #baseline_let + + #prelude - #prelude + #start_let - #start_let + rtfm::export::run(move || { + #(#stmts)* + }) + } - rtfm::export::run(move || { - #(#stmts)* - }) + interrupt() } )); } @@ -1213,10 +1245,10 @@ fn tasks(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::Tok // first pass to generate buffers (statics and resources) and spawn aliases for (name, task) in &app.tasks { #[cfg(feature = "timer-queue")] - let scheduleds_alias = mk_ident(None); - let free_alias = mk_ident(None); - let inputs_alias = mk_ident(None); - let task_alias = mk_ident(Some(&name.to_string())); + let scheduleds_alias = ctxt.ident_gen.mk_ident(None, false); + let free_alias = ctxt.ident_gen.mk_ident(None, false); + let inputs_alias = ctxt.ident_gen.mk_ident(None, false); + let task_alias = ctxt.ident_gen.mk_ident(Some(&name.to_string()), false); let inputs = &task.inputs; @@ -1277,7 +1309,7 @@ fn tasks(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::Tok alias: task_alias, free_queue: free_alias, inputs: inputs_alias, - spawn_fn: mk_ident(None), + spawn_fn: ctxt.ident_gen.mk_ident(None, false), #[cfg(feature = "timer-queue")] scheduleds: scheduleds_alias, @@ -1296,7 +1328,7 @@ fn tasks(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::Tok #[cfg(feature = "timer-queue")] () => { let baseline = &ctxt.baseline; - quote!(let scheduled = #baseline;) + quote!(let scheduled = *#baseline;) } #[cfg(not(feature = "timer-queue"))] () => quote!(), @@ -1325,26 +1357,33 @@ fn tasks(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::Tok let attrs = &task.attrs; let cfgs = &task.cfgs; let task_alias = &ctxt.tasks[name].alias; - let baseline_arg = match () { + let (baseline, baseline_arg) = match () { #[cfg(feature = "timer-queue")] () => { let baseline = &ctxt.baseline; - quote!(#baseline: rtfm::Instant,) + (quote!(#baseline,), quote!(#baseline: &rtfm::Instant,)) } #[cfg(not(feature = "timer-queue"))] - () => quote!(), + () => (quote!(), quote!()), }; + let pats = tuple_pat(inputs); items.push(quote!( + // unsafe trampoline to deter end-users from calling this non-reentrant function #(#attrs)* #(#cfgs)* - #unsafety fn #task_alias(#baseline_arg #(#inputs,)*) { - #(#locals)* + unsafe fn #task_alias(#baseline_arg #(#inputs,)*) { + #[inline(always)] + #unsafety fn task(#baseline_arg #(#inputs,)*) { + #(#locals)* + + #prelude - #prelude + #scheduled_let - #scheduled_let + #(#stmts)* + } - #(#stmts)* + task(#baseline #pats) } )); } @@ -1362,8 +1401,8 @@ fn dispatchers( let device = &app.args.device; for (level, dispatcher) in &analysis.dispatchers { - let ready_alias = mk_ident(None); - let enum_alias = mk_ident(None); + let ready_alias = ctxt.ident_gen.mk_ident(None, false); + let enum_alias = ctxt.ident_gen.mk_ident(None, false); let capacity = mk_typenum_capacity(dispatcher.capacity, true); let variants = dispatcher @@ -1427,7 +1466,7 @@ fn dispatchers( let baseline = ptr::read(#scheduleds.get_ref().get_unchecked(usize::from(index))); ); - call = quote!(#alias(baseline, #pats)); + call = quote!(#alias(&baseline, #pats)); } #[cfg(not(feature = "timer-queue"))] () => { @@ -1452,7 +1491,7 @@ fn dispatchers( let attrs = &dispatcher.attrs; let interrupt = &dispatcher.interrupt; let symbol = interrupt.to_string(); - let alias = mk_ident(None); + let alias = ctxt.ident_gen.mk_ident(None, false); dispatchers.push(quote!( #(#attrs)* #[export_name = #symbol] @@ -1534,7 +1573,7 @@ fn spawn(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenSt #(#cfgs)* unsafe fn #alias( #baseline_arg - #priority: &core::cell::Cell<u8>, + #priority: &rtfm::export::Priority, #(#args,)* ) -> Result<(), #ty> { use core::ptr; @@ -1583,7 +1622,7 @@ fn spawn(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenSt if is_idle { quote!(rtfm::Instant::now(),) } else { - quote!(self.#baseline,) + quote!(*self.#baseline,) } } #[cfg(not(feature = "timer-queue"))] @@ -1632,7 +1671,7 @@ fn schedule(ctxt: &Context, app: &App) -> proc_macro2::TokenStream { #[inline(always)] #(#cfgs)* unsafe fn #alias( - #priority: &core::cell::Cell<u8>, + #priority: &rtfm::export::Priority, instant: rtfm::Instant, #(#args,)* ) -> Result<(), #ty> { @@ -1703,7 +1742,7 @@ fn schedule(ctxt: &Context, app: &App) -> proc_macro2::TokenStream { quote!(#(#items)*) } -fn timer_queue(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenStream { +fn timer_queue(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenStream { let tasks = &analysis.timer_queue.tasks; if tasks.is_empty() { @@ -1778,14 +1817,14 @@ fn timer_queue(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::T .collect::<Vec<_>>(); let logical_prio = analysis.timer_queue.priority; - let alias = mk_ident(None); + let alias = ctxt.ident_gen.mk_ident(None, false); items.push(quote!( #[export_name = "SysTick"] #[doc(hidden)] unsafe fn #alias() { use rtfm::Mutex; - let ref #priority = core::cell::Cell::new(#logical_prio); + let ref #priority = rtfm::export::Priority::new(#logical_prio); rtfm::export::run(|| { rtfm::export::sys_tick(#tq { #priority }, |task, index| { @@ -1929,7 +1968,7 @@ fn mk_resource( #(#cfgs)* pub struct #struct_<'a> { #[doc(hidden)] - pub #priority: &'a core::cell::Cell<u8>, + pub #priority: &'a rtfm::export::Priority, } )); @@ -1938,7 +1977,7 @@ fn mk_resource( items.push(quote!( #(#cfgs)* struct #struct_<'a> { - #priority: &'a core::cell::Cell<u8>, + #priority: &'a rtfm::export::Priority, } )); @@ -1989,52 +2028,61 @@ fn mk_typenum_capacity(capacity: u8, power_of_two: bool) -> proc_macro2::TokenSt quote!(rtfm::export::consts::#ident) } -fn mk_ident(name: Option<&str>) -> Ident { - static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); +struct IdentGenerator { + call_count: u32, + rng: rand::rngs::SmallRng, +} - let elapsed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); +impl IdentGenerator { + fn new() -> IdentGenerator { + let elapsed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); - let secs = elapsed.as_secs(); - let nanos = elapsed.subsec_nanos(); + let secs = elapsed.as_secs(); + let nanos = elapsed.subsec_nanos(); - let count = CALL_COUNT.fetch_add(1, Ordering::SeqCst) as u32; - let mut seed: [u8; 16] = [0; 16]; + let mut seed: [u8; 16] = [0; 16]; - for (i, v) in seed.iter_mut().take(8).enumerate() { - *v = ((secs >> (i * 8)) & 0xFF) as u8 - } + for (i, v) in seed.iter_mut().take(8).enumerate() { + *v = ((secs >> (i * 8)) & 0xFF) as u8 + } - for (i, v) in seed.iter_mut().skip(8).take(4).enumerate() { - *v = ((nanos >> (i * 8)) & 0xFF) as u8 - } + for (i, v) in seed.iter_mut().skip(8).take(4).enumerate() { + *v = ((nanos >> (i * 8)) & 0xFF) as u8 + } - for (i, v) in seed.iter_mut().skip(12).enumerate() { - *v = ((count >> (i * 8)) & 0xFF) as u8 - } + let rng = rand::rngs::SmallRng::from_seed(seed); - let n; - let mut s = if let Some(name) = name { - n = 4; - format!("{}_", name) - } else { - n = 16; - String::new() - }; + IdentGenerator { call_count: 0, rng } + } - let mut rng = rand::rngs::SmallRng::from_seed(seed); - for i in 0..n { - if i == 0 || rng.gen() { - s.push(('a' as u8 + rng.gen::<u8>() % 25) as char) + fn mk_ident(&mut self, name: Option<&str>, random: bool) -> Ident { + let s = if let Some(name) = name { + format!("{}_", name) } else { - s.push(('0' as u8 + rng.gen::<u8>() % 10) as char) + "__rtfm_internal_".to_string() + }; + + let mut s = format!("{}{}", s, self.call_count); + self.call_count += 1; + + if random { + s.push('_'); + + for i in 0..4 { + if i == 0 || self.rng.gen() { + s.push(('a' as u8 + self.rng.gen::<u8>() % 25) as char) + } else { + s.push(('0' as u8 + self.rng.gen::<u8>() % 10) as char) + } + } } - } - Ident::new(&s, Span::call_site()) + Ident::new(&s, Span::call_site()) + } } // `once = true` means that these locals will be called from a function that will run *once* -fn mk_locals(locals: &HashMap<Ident, Static>, once: bool) -> proc_macro2::TokenStream { +fn mk_locals(locals: &BTreeMap<Ident, Static>, once: bool) -> proc_macro2::TokenStream { let lt = if once { Some(quote!('static)) } else { None }; let locals = locals |