diff --git a/.gitignore b/.gitignore index 0c0491e538..7405f865fd 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ /book/*/book /target Cargo.lock +*.hex diff --git a/.travis.yml b/.travis.yml index 481f8fea88..31d10e84ee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,10 @@ matrix: rust: nightly if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) -before_install: set -e +before_install: + - set -e + - sudo apt-get update + - sudo apt-get install -y binutils-arm-none-eabi install: - bash ci/install.sh diff --git a/ci/script.sh b/ci/script.sh index 645db3aacd..4b9cd22d39 100644 --- a/ci/script.sh +++ b/ci/script.sh @@ -1,8 +1,41 @@ set -euxo pipefail +arm_example() { + local COMMAND=$1 + local EXAMPLE=$2 + local BUILD_MODE=$3 + local FEATURES=$4 + local BUILD_NUM=$5 + + if [ $BUILD_MODE = "release" ]; then + local RELEASE_FLAG="--release" + else + local RELEASE_FLAG="" + fi + + if [ -n "$FEATURES" ]; then + local FEATURES_FLAG="--features $FEATURES" + local FEATURES_STR=${FEATURES/,/_}_ + else + local FEATURES_FLAG="" + local FEATURES_STR="" + fi + local CARGO_FLAGS="--example $EXAMPLE --target $TARGET $RELEASE_FLAG $FEATURES_FLAG" + + if [ $COMMAND = "run" ]; then + cargo $COMMAND $CARGO_FLAGS | diff -u ci/expected/$EXAMPLE.run - + else + cargo $COMMAND $CARGO_FLAGS + fi + arm-none-eabi-objcopy -O ihex target/$TARGET/$BUILD_MODE/examples/$EXAMPLE ci/builds/${EXAMPLE}_${FEATURES_STR}${BUILD_MODE}_${BUILD_NUM}.hex +} + + main() { local T=$TARGET + mkdir -p ci/builds + if [ $T = x86_64-unknown-linux-gnu ]; then # compile-fail and compile-pass tests case $TRAVIS_RUST_VERSION in @@ -82,19 +115,41 @@ main() { fi if [ $ex != types ]; then - cargo run --example $ex --target $T | \ - diff -u ci/expected/$ex.run - - - cargo run --example $ex --target $T --release | \ - diff -u ci/expected/$ex.run - + arm_example "run" $ex "debug" "" "1" + arm_example "run" $ex "release" "" "1" fi if [ $TARGET != thumbv6m-none-eabi ]; then - cargo run --features timer-queue --example $ex --target $T | \ - diff -u ci/expected/$ex.run - + arm_example "run" $ex "debug" "timer-queue" "1" + arm_example "run" $ex "release" "timer-queue" "1" + fi + done - cargo run --features timer-queue --example $ex --target $T --release | \ - diff -u ci/expected/$ex.run - + cargo clean + for ex in ${exs[@]}; do + if [ $ex = ramfunc ] && [ $T = thumbv6m-none-eabi ]; then + # LLD doesn't support this at the moment + continue + fi + + if [ $ex = singleton ]; then + # singleton build is currently not reproducible due to + # https://github.com/japaric/owned-singleton/issues/2 + continue + fi + + if [ $ex != types ]; then + arm_example "build" $ex "debug" "" "2" + cmp ci/builds/${ex}_debug_1.hex ci/builds/${ex}_debug_2.hex + arm_example "build" $ex "release" "" "2" + cmp ci/builds/${ex}_release_1.hex ci/builds/${ex}_release_2.hex + fi + + if [ $TARGET != thumbv6m-none-eabi ]; then + arm_example "build" $ex "debug" "timer-queue" "2" + cmp ci/builds/${ex}_timer-queue_debug_1.hex ci/builds/${ex}_timer-queue_debug_2.hex + arm_example "build" $ex "release" "timer-queue" "2" + cmp ci/builds/${ex}_timer-queue_release_1.hex ci/builds/${ex}_timer-queue_release_2.hex fi done esac diff --git a/macros/src/analyze.rs b/macros/src/analyze.rs index 19575b7728..cfd8ebc94a 100644 --- a/macros/src/analyze.rs +++ b/macros/src/analyze.rs @@ -1,6 +1,6 @@ use std::{ cmp, - collections::{HashMap, HashSet}, + collections::{BTreeMap, HashMap, HashSet}, }; use syn::{Attribute, Ident, Type}; @@ -65,7 +65,7 @@ pub struct Dispatcher { } /// Priority -> Dispatcher -pub type Dispatchers = HashMap; +pub type Dispatchers = BTreeMap; pub type Capacities = HashMap; diff --git a/macros/src/check.rs b/macros/src/check.rs index 045d152f8c..464e280ac1 100644 --- a/macros/src/check.rs +++ b/macros/src/check.rs @@ -38,10 +38,10 @@ pub fn app(app: &App) -> parse::Result<()> { // Check that all late resources have been initialized in `#[init]` if `init` has signature // `fn()` if !app.init.returns_late_resources { - for res in app - .resources - .iter() - .filter_map(|(name, res)| if res.expr.is_none() { Some(name) } else { None }) + for res in + app.resources + .iter() + .filter_map(|(name, res)| if res.expr.is_none() { Some(name) } else { None }) { if app.init.assigns.iter().all(|assign| assign.left != *res) { return Err(parse::Error::new( diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index af3def6476..af09ad4286 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; +type Aliases = BTreeMap; struct Context { // Alias #[cfg(feature = "timer-queue")] baseline: Ident, - dispatchers: HashMap, + dispatchers: BTreeMap, // Alias (`fn`) idle: Ident, // Alias (`fn`) @@ -41,9 +40,11 @@ struct Context { schedule_enum: Ident, // Task -> Alias (`fn`) schedule_fn: Aliases, - tasks: HashMap, + tasks: BTreeMap, // 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, + 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, + 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, + 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)); + 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 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 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 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, + #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, + #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::>(); 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, + 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, + #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().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 n; - let mut s = if let Some(name) = name { - n = 4; - format!("{}_", name) - } else { - n = 16; - String::new() - }; - - 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::() % 25) as char) - } else { - s.push(('0' as u8 + rng.gen::() % 10) as char) + 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 + } + + let rng = rand::rngs::SmallRng::from_seed(seed); + + IdentGenerator { call_count: 0, rng } } - Ident::new(&s, Span::call_site()) + fn mk_ident(&mut self, name: Option<&str>, random: bool) -> Ident { + let s = if let Some(name) = name { + format!("{}_", name) + } else { + "__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::() % 25) as char) + } else { + s.push(('0' as u8 + self.rng.gen::() % 10) as char) + } + } + } + + 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, once: bool) -> proc_macro2::TokenStream { +fn mk_locals(locals: &BTreeMap, once: bool) -> proc_macro2::TokenStream { let lt = if once { Some(quote!('static)) } else { None }; let locals = locals diff --git a/macros/src/syntax.rs b/macros/src/syntax.rs index ad7d8bde34..581eb8319b 100644 --- a/macros/src/syntax.rs +++ b/macros/src/syntax.rs @@ -1,5 +1,5 @@ use std::{ - collections::{HashMap, HashSet}, + collections::{BTreeMap, BTreeSet}, iter, u8, }; @@ -120,10 +120,10 @@ impl App { pub fn parse(items: Vec, args: AppArgs) -> parse::Result { let mut idle = None; let mut init = None; - let mut exceptions = HashMap::new(); - let mut interrupts = HashMap::new(); - let mut resources = HashMap::new(); - let mut tasks = HashMap::new(); + let mut exceptions = BTreeMap::new(); + let mut interrupts = BTreeMap::new(); + let mut resources = BTreeMap::new(); + let mut tasks = BTreeMap::new(); let mut free_interrupts = None; for item in items { @@ -418,25 +418,25 @@ impl App { } } -pub type Idents = HashSet; +pub type Idents = BTreeSet; -pub type Exceptions = HashMap; +pub type Exceptions = BTreeMap; -pub type Interrupts = HashMap; +pub type Interrupts = BTreeMap; -pub type Resources = HashMap; +pub type Resources = BTreeMap; pub type Statics = Vec; -pub type Tasks = HashMap; +pub type Tasks = BTreeMap; -pub type FreeInterrupts = HashMap; +pub type FreeInterrupts = BTreeMap; pub struct Idle { pub args: IdleArgs, pub attrs: Vec, pub unsafety: Option, - pub statics: HashMap, + pub statics: BTreeMap, pub stmts: Vec, } @@ -607,7 +607,7 @@ pub struct Init { pub args: InitArgs, pub attrs: Vec, pub unsafety: Option, - pub statics: HashMap, + pub statics: BTreeMap, pub stmts: Vec, // TODO remove in v0.5.x pub assigns: Vec, @@ -703,7 +703,7 @@ pub struct Exception { pub args: ExceptionArgs, pub attrs: Vec, pub unsafety: Option, - pub statics: HashMap, + pub statics: BTreeMap, pub stmts: Vec, } @@ -791,7 +791,7 @@ pub struct Interrupt { pub args: InterruptArgs, pub attrs: Vec, pub unsafety: Option, - pub statics: HashMap, + pub statics: BTreeMap, pub stmts: Vec, } @@ -1070,8 +1070,8 @@ pub struct Static { } impl Static { - fn parse(items: Vec) -> parse::Result> { - let mut statics = HashMap::new(); + fn parse(items: Vec) -> parse::Result> { + let mut statics = BTreeMap::new(); for item in items { if statics.contains_key(&item.ident) { @@ -1104,7 +1104,7 @@ pub struct Task { pub attrs: Vec, pub unsafety: Option, pub inputs: Vec, - pub statics: HashMap, + pub statics: BTreeMap, pub stmts: Vec, } diff --git a/src/export.rs b/src/export.rs index ed40b62164..6eae65f280 100644 --- a/src/export.rs +++ b/src/export.rs @@ -39,6 +39,31 @@ where f(); } +// Newtype over `Cell` that forbids mutation through a shared reference +pub struct Priority { + inner: Cell, +} + +impl Priority { + #[inline(always)] + pub unsafe fn new(value: u8) -> Self { + Priority { + inner: Cell::new(value), + } + } + + // these two methods are used by claim (see below) but can't be used from the RTFM application + #[inline(always)] + fn set(&self, value: u8) { + self.inner.set(value) + } + + #[inline(always)] + fn get(&self) -> u8 { + self.inner.get() + } +} + // TODO(MaybeUninit) Until core::mem::MaybeUninit is stabilized we use our own (inefficient) // implementation pub struct MaybeUninit { @@ -102,7 +127,7 @@ where #[inline(always)] pub unsafe fn claim( ptr: *mut T, - priority: &Cell, + priority: &Priority, ceiling: u8, nvic_prio_bits: u8, f: F, @@ -135,7 +160,7 @@ where #[inline(always)] pub unsafe fn claim( ptr: *mut T, - priority: &Cell, + priority: &Priority, ceiling: u8, _nvic_prio_bits: u8, f: F, diff --git a/tests/compiletest.rs b/tests/compiletest.rs index acc89546b1..58702eecdf 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -38,18 +38,16 @@ fn cfail() { let f = f.unwrap().path(); let name = f.file_stem().unwrap().to_str().unwrap(); - assert!( - Command::new("rustc") - .args(s.split_whitespace()) - .arg(f.display().to_string()) - .arg("-o") - .arg(td.path().join(name).display().to_string()) - .arg("-C") - .arg("linker=true") - .status() - .unwrap() - .success() - ); + assert!(Command::new("rustc") + .args(s.split_whitespace()) + .arg(f.display().to_string()) + .arg("-o") + .arg(td.path().join(name).display().to_string()) + .arg("-C") + .arg("linker=true") + .status() + .unwrap() + .success()); } config.target_rustcflags = Some(s);