From 2f89688ca974944781878a74873801597c0b1f11 Mon Sep 17 00:00:00 2001 From: Hugo van der Wijst Date: Tue, 15 Jan 2019 22:42:50 -0800 Subject: [PATCH 01/10] Make builds reproducible This is done by using `BTreeMap`s and `BTreeSet`s to get deterministic ordering. Also updated the CI job to check reproducibility of all examples. --- .travis.yml | 5 ++++- ci/script.sh | 45 +++++++++++++++++++++++++++++++++---------- macros/src/analyze.rs | 4 ++-- macros/src/codegen.rs | 14 +++++++------- macros/src/syntax.rs | 36 +++++++++++++++++----------------- 5 files changed, 66 insertions(+), 38 deletions(-) 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..ab7f34b24b 100644 --- a/ci/script.sh +++ b/ci/script.sh @@ -81,20 +81,45 @@ main() { continue fi - if [ $ex != types ]; then - cargo run --example $ex --target $T | \ - diff -u ci/expected/$ex.run - + test_arm_example() { + local EXAMPLE=$1 + local TARGET=$2 + local BUILD_MODE=$3 + local FEATURES=$4 - cargo run --example $ex --target $T --release | \ - diff -u ci/expected/$ex.run - + if [ $BUILD_MODE = "release" ]; then + local RELEASE_FLAG="--release" + else + local RELEASE_FLAG="" + fi + + if [ -n "$FEATURES" ]; then + local FEATURES_FLAG="--features $FEATURES" + else + local FEATURES_FLAG="" + fi + local CARGO_FLAGS="--example $EXAMPLE --target $TARGET $RELEASE_FLAG $FEATURES_FLAG" + + cargo run $CARGO_FLAGS | diff -u ci/expected/$EXAMPLE.run - + arm-none-eabi-objcopy -O ihex target/$TARGET/$BUILD_MODE/examples/$EXAMPLE ${EXAMPLE}_1.hex + + # build again to ensure that the build is reproducable + cargo clean + cargo build $CARGO_FLAGS + arm-none-eabi-objcopy -O ihex target/$TARGET/$BUILD_MODE/examples/$EXAMPLE ${EXAMPLE}_2.hex + + # compare results of both builds + cmp ${EXAMPLE}_1.hex ${EXAMPLE}_2.hex + } + + if [ $ex != types ]; then + test_arm_example $ex $T "debug" "" + test_arm_example $ex $T "release" "" fi if [ $TARGET != thumbv6m-none-eabi ]; then - cargo run --features timer-queue --example $ex --target $T | \ - diff -u ci/expected/$ex.run - - - cargo run --features timer-queue --example $ex --target $T --release | \ - diff -u ci/expected/$ex.run - + test_arm_example $ex $T "debug" "timer-queue" + test_arm_example $ex $T "release" "timer-queue" 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/codegen.rs b/macros/src/codegen.rs index af3def6476..a96eaef99d 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -2,7 +2,7 @@ use proc_macro::TokenStream; use std::{ - collections::HashMap, + collections::{BTreeMap, HashMap}, sync::atomic::{AtomicUsize, Ordering}, time::{SystemTime, UNIX_EPOCH}, }; @@ -20,13 +20,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,7 +41,7 @@ struct Context { schedule_enum: Ident, // Task -> Alias (`fn`) schedule_fn: Aliases, - tasks: HashMap, + tasks: BTreeMap, // Alias (`struct` / `static mut`) timer_queue: Ident, } @@ -66,7 +66,7 @@ impl Default for Context { Context { #[cfg(feature = "timer-queue")] baseline: mk_ident(None), - dispatchers: HashMap::new(), + dispatchers: BTreeMap::new(), idle: mk_ident(Some("idle")), init: mk_ident(Some("init")), priority: mk_ident(None), @@ -74,7 +74,7 @@ impl Default for Context { resources: HashMap::new(), schedule_enum: mk_ident(None), schedule_fn: Aliases::new(), - tasks: HashMap::new(), + tasks: BTreeMap::new(), timer_queue: mk_ident(None), } } @@ -2034,7 +2034,7 @@ fn mk_ident(name: Option<&str>) -> Ident { } // `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, } From 4f193df0efbd45d5d86eccb472fc2312aec3ca94 Mon Sep 17 00:00:00 2001 From: Hugo van der Wijst Date: Tue, 15 Jan 2019 23:21:40 -0800 Subject: [PATCH 02/10] Speed up CI significantly. --- ci/script.sh | 87 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 35 deletions(-) diff --git a/ci/script.sh b/ci/script.sh index ab7f34b24b..6a15cc67d5 100644 --- a/ci/script.sh +++ b/ci/script.sh @@ -1,5 +1,36 @@ 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 ${EXAMPLE}_${FEATURES_STR}${BUILD_MODE}_${BUILD_NUM}.hex +} + + main() { local T=$TARGET @@ -81,45 +112,31 @@ main() { continue fi - test_arm_example() { - local EXAMPLE=$1 - local TARGET=$2 - local BUILD_MODE=$3 - local FEATURES=$4 - - if [ $BUILD_MODE = "release" ]; then - local RELEASE_FLAG="--release" - else - local RELEASE_FLAG="" - fi - - if [ -n "$FEATURES" ]; then - local FEATURES_FLAG="--features $FEATURES" - else - local FEATURES_FLAG="" - fi - local CARGO_FLAGS="--example $EXAMPLE --target $TARGET $RELEASE_FLAG $FEATURES_FLAG" - - cargo run $CARGO_FLAGS | diff -u ci/expected/$EXAMPLE.run - - arm-none-eabi-objcopy -O ihex target/$TARGET/$BUILD_MODE/examples/$EXAMPLE ${EXAMPLE}_1.hex - - # build again to ensure that the build is reproducable - cargo clean - cargo build $CARGO_FLAGS - arm-none-eabi-objcopy -O ihex target/$TARGET/$BUILD_MODE/examples/$EXAMPLE ${EXAMPLE}_2.hex - - # compare results of both builds - cmp ${EXAMPLE}_1.hex ${EXAMPLE}_2.hex - } - if [ $ex != types ]; then - test_arm_example $ex $T "debug" "" - test_arm_example $ex $T "release" "" + arm_example "run" $ex "debug" "" "1" + arm_example "run" $ex "release" "" "1" fi if [ $TARGET != thumbv6m-none-eabi ]; then - test_arm_example $ex $T "debug" "timer-queue" - test_arm_example $ex $T "release" "timer-queue" + arm_example "run" $ex "debug" "timer-queue" "1" + arm_example "run" $ex "release" "timer-queue" "1" + fi + done + + cargo clean + for ex in ${exs[@]}; do + if [ $ex != types ]; then + arm_example "build" $ex "debug" "" "2" + cmp ${ex}_debug_1.hex ${ex}_debug_2.hex + arm_example "build" $ex "release" "" "2" + cmp ${ex}_release_1.hex ${ex}_release_2.hex + fi + + if [ $TARGET != thumbv6m-none-eabi ]; then + arm_example "build" $ex "debug" "timer-queue" "2" + cmp ${ex}_timer-queue_debug_1.hex ${ex}_timer-queue_debug_2.hex + arm_example "build" $ex "release" "timer-queue" "2" + cmp ${ex}_timer-queue_release_1.hex ${ex}_timer-queue_release_2.hex fi done esac From be8a5e89b8c5763dc691bb748ba1da73a56cb2cc Mon Sep 17 00:00:00 2001 From: Hugo van der Wijst Date: Wed, 16 Jan 2019 17:36:55 -0800 Subject: [PATCH 03/10] Make identifiers deterministic. --- .gitignore | 1 + ci/script.sh | 6 +++ macros/src/codegen.rs | 120 +++++++++++++++++++----------------------- 3 files changed, 62 insertions(+), 65 deletions(-) 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/ci/script.sh b/ci/script.sh index 6a15cc67d5..40b03a7e38 100644 --- a/ci/script.sh +++ b/ci/script.sh @@ -125,6 +125,12 @@ main() { cargo clean for ex in ${exs[@]}; do + 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 ${ex}_debug_1.hex ${ex}_debug_2.hex diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index a96eaef99d..9eb875c770 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -3,8 +3,6 @@ use proc_macro::TokenStream; use std::{ collections::{BTreeMap, HashMap}, - sync::atomic::{AtomicUsize, Ordering}, - time::{SystemTime, UNIX_EPOCH}, }; use proc_macro2::Span; @@ -44,6 +42,8 @@ struct Context { tasks: BTreeMap, // Alias (`struct` / `static mut`) timer_queue: Ident, + // Generator of Ident names or suffixes + ident_gen: IdentGenerator, } struct Dispatcher { @@ -63,19 +63,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), + baseline: ident_gen.mk_ident(None), dispatchers: BTreeMap::new(), - idle: mk_ident(Some("idle")), - init: mk_ident(Some("init")), - priority: mk_ident(None), + idle: ident_gen.mk_ident(Some("idle")), + init: ident_gen.mk_ident(Some("init")), + priority: ident_gen.mk_ident(None), statics: Aliases::new(), resources: HashMap::new(), - schedule_enum: mk_ident(None), + schedule_enum: ident_gen.mk_ident(None), schedule_fn: Aliases::new(), tasks: BTreeMap::new(), - timer_queue: mk_ident(None), + timer_queue: ident_gen.mk_ident(None), + ident_gen, } } } @@ -164,13 +167,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); quote!( #resources @@ -236,7 +239,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); if let Some(Ownership::Shared { ceiling }) = analysis.ownerships.get(name) { items.push(mk_resource( ctxt, @@ -252,7 +255,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); let symbol = format!("{}::{}", name, alias); items.push( @@ -826,7 +829,7 @@ fn prelude( #(#cfgs)* pub #name: &'a #mut_ #name )); - let alias = mk_ident(None); + let alias = ctxt.ident_gen.mk_ident(None); items.push(quote!( #(#cfgs)* let #mut_ #alias = unsafe { @@ -843,7 +846,7 @@ fn prelude( #(#cfgs)* pub #name: rtfm::Exclusive<'a, #name> )); - let alias = mk_ident(None); + let alias = ctxt.ident_gen.mk_ident(None); items.push(quote!( #(#cfgs)* let #mut_ #alias = unsafe { @@ -910,7 +913,7 @@ fn prelude( } } - let alias = mk_ident(None); + let alias = ctxt.ident_gen.mk_ident(None); let unsafety = if needs_unsafe { Some(quote!(unsafe)) } else { @@ -971,7 +974,7 @@ fn prelude( continue; } - ctxt.schedule_fn.insert(task.clone(), mk_ident(None)); + ctxt.schedule_fn.insert(task.clone(), ctxt.ident_gen.mk_ident(None)); } items.push(quote!( @@ -1100,7 +1103,7 @@ fn exceptions(ctxt: &mut Context, app: &App, analysis: &Analysis) -> Vec 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); + let free_alias = ctxt.ident_gen.mk_ident(None); + let inputs_alias = ctxt.ident_gen.mk_ident(None); + let task_alias = ctxt.ident_gen.mk_ident(Some(&name.to_string())); let inputs = &task.inputs; @@ -1277,7 +1280,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), #[cfg(feature = "timer-queue")] scheduleds: scheduleds_alias, @@ -1362,8 +1365,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); + let enum_alias = ctxt.ident_gen.mk_ident(None); let capacity = mk_typenum_capacity(dispatcher.capacity, true); let variants = dispatcher @@ -1452,7 +1455,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); dispatchers.push(quote!( #(#attrs)* #[export_name = #symbol] @@ -1703,7 +1706,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,7 +1781,7 @@ 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); items.push(quote!( #[export_name = "SysTick"] #[doc(hidden)] @@ -1989,48 +1992,35 @@ 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 { + rng: rand::rngs::SmallRng, +} - let elapsed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); - - 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]; - - for (i, v) in seed.iter_mut().take(8).enumerate() { - *v = ((secs >> (i * 8)) & 0xFF) as u8 +impl IdentGenerator { + fn new() -> IdentGenerator { + IdentGenerator { rng: rand::rngs::SmallRng::seed_from_u64(0) } } - 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) + fn mk_ident(&mut self, name: Option<&str>) -> Ident { + let n; + let mut s = if let Some(name) = name { + n = 4; + format!("{}_", name) } else { - s.push(('0' as u8 + rng.gen::() % 10) as char) - } - } + n = 16; + String::new() + }; - Ident::new(&s, Span::call_site()) + for i in 0..n { + 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* From 82c533cbf43c56da0960de7b48e7a6d1cc8defd0 Mon Sep 17 00:00:00 2001 From: Hugo van der Wijst Date: Wed, 16 Jan 2019 17:57:54 -0800 Subject: [PATCH 04/10] Fix thumbv6 build. --- ci/script.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ci/script.sh b/ci/script.sh index 40b03a7e38..1bb0ae2edb 100644 --- a/ci/script.sh +++ b/ci/script.sh @@ -125,6 +125,11 @@ main() { 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 From a654d13eefa846b9dc7fcdc1ccf5af43fc33834e Mon Sep 17 00:00:00 2001 From: Hugo van der Wijst Date: Tue, 22 Jan 2019 15:23:16 -0800 Subject: [PATCH 05/10] Seed RNG with package name and prepend string to full random name. --- macros/src/codegen.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 9eb875c770..9fc0e271c4 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -1998,7 +1998,12 @@ struct IdentGenerator { impl IdentGenerator { fn new() -> IdentGenerator { - IdentGenerator { rng: rand::rngs::SmallRng::seed_from_u64(0) } + let crate_name = env!("CARGO_PKG_NAME"); + let seed = [0u8; 16]; + for (i, b) in crate_name.bytes().enumerate() { + seed[i%seed.len()].wrapping_add(b); + } + IdentGenerator { rng: rand::rngs::SmallRng::from_seed(seed) } } fn mk_ident(&mut self, name: Option<&str>) -> Ident { @@ -2007,8 +2012,9 @@ impl IdentGenerator { n = 4; format!("{}_", name) } else { + let crate_name = env!("CARGO_PKG_NAME").replace("-", "_").to_lowercase(); n = 16; - String::new() + format!("{}__internal__", crate_name) }; for i in 0..n { From 577d188f72398461650a4e76d81396bf0abea0f4 Mon Sep 17 00:00:00 2001 From: Hugo van der Wijst Date: Sun, 10 Feb 2019 15:25:33 -0800 Subject: [PATCH 06/10] Make generated names stable when sorting. --- ci/script.sh | 12 +++++++----- macros/src/codegen.rs | 35 ++++++++++++++++++++++++++++------- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/ci/script.sh b/ci/script.sh index 1bb0ae2edb..4b9cd22d39 100644 --- a/ci/script.sh +++ b/ci/script.sh @@ -27,13 +27,15 @@ arm_example() { else cargo $COMMAND $CARGO_FLAGS fi - arm-none-eabi-objcopy -O ihex target/$TARGET/$BUILD_MODE/examples/$EXAMPLE ${EXAMPLE}_${FEATURES_STR}${BUILD_MODE}_${BUILD_NUM}.hex + 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 @@ -138,16 +140,16 @@ main() { if [ $ex != types ]; then arm_example "build" $ex "debug" "" "2" - cmp ${ex}_debug_1.hex ${ex}_debug_2.hex + cmp ci/builds/${ex}_debug_1.hex ci/builds/${ex}_debug_2.hex arm_example "build" $ex "release" "" "2" - cmp ${ex}_release_1.hex ${ex}_release_2.hex + 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 ${ex}_timer-queue_debug_1.hex ${ex}_timer-queue_debug_2.hex + 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 ${ex}_timer-queue_release_1.hex ${ex}_timer-queue_release_2.hex + 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/codegen.rs b/macros/src/codegen.rs index 9fc0e271c4..e2e5ac44ad 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -3,6 +3,7 @@ use proc_macro::TokenStream; use std::{ collections::{BTreeMap, HashMap}, + time::{SystemTime, UNIX_EPOCH}, }; use proc_macro2::Span; @@ -1993,30 +1994,48 @@ fn mk_typenum_capacity(capacity: u8, power_of_two: bool) -> proc_macro2::TokenSt } struct IdentGenerator { + call_count: u32, rng: rand::rngs::SmallRng, } impl IdentGenerator { fn new() -> IdentGenerator { - let crate_name = env!("CARGO_PKG_NAME"); - let seed = [0u8; 16]; - for (i, b) in crate_name.bytes().enumerate() { - seed[i%seed.len()].wrapping_add(b); + let elapsed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); + + let secs = elapsed.as_secs(); + let nanos = elapsed.subsec_nanos(); + + 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 + } + + let rng = rand::rngs::SmallRng::from_seed(seed); + + IdentGenerator { + call_count: 0, + rng, } - IdentGenerator { rng: rand::rngs::SmallRng::from_seed(seed) } } fn mk_ident(&mut self, name: Option<&str>) -> Ident { let n; - let mut s = if let Some(name) = name { + let s = if let Some(name) = name { n = 4; format!("{}_", name) } else { let crate_name = env!("CARGO_PKG_NAME").replace("-", "_").to_lowercase(); - n = 16; + n = 4; format!("{}__internal__", crate_name) }; + let mut s = format!("{}{}_", s, self.call_count); + for i in 0..n { if i == 0 || self.rng.gen() { s.push(('a' as u8 + self.rng.gen::() % 25) as char) @@ -2025,6 +2044,8 @@ impl IdentGenerator { } } + self.call_count += 1; + Ident::new(&s, Span::call_site()) } } From cf05dc2c4b2bb702be4807867610dfedcc02d6f2 Mon Sep 17 00:00:00 2001 From: Hugo van der Wijst Date: Mon, 11 Feb 2019 11:54:55 -0800 Subject: [PATCH 07/10] Temporarily disable checking for reproducibility of debug builds. --- ci/script.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ci/script.sh b/ci/script.sh index 4b9cd22d39..7c14766b9e 100644 --- a/ci/script.sh +++ b/ci/script.sh @@ -139,15 +139,15 @@ main() { 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 "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 "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 From 2b8e743f35a69b9b09a4de4c346eb9015c6b45ea Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 16 Feb 2019 00:22:00 +0100 Subject: [PATCH 08/10] make debug builds reproducible --- ci/script.sh | 8 +- macros/src/codegen.rs | 233 ++++++++++++++++++++++++------------------ src/export.rs | 27 ++++- 3 files changed, 161 insertions(+), 107 deletions(-) diff --git a/ci/script.sh b/ci/script.sh index 7c14766b9e..4b9cd22d39 100644 --- a/ci/script.sh +++ b/ci/script.sh @@ -139,15 +139,15 @@ main() { 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 "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 "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 diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index e2e5ac44ad..1405c2f69a 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -68,17 +68,17 @@ impl Default for Context { Context { #[cfg(feature = "timer-queue")] - baseline: ident_gen.mk_ident(None), + baseline: ident_gen.mk_ident(None, false), dispatchers: BTreeMap::new(), - idle: ident_gen.mk_ident(Some("idle")), - init: ident_gen.mk_ident(Some("init")), - priority: ident_gen.mk_ident(None), + 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: ident_gen.mk_ident(None), + schedule_enum: ident_gen.mk_ident(None, false), schedule_fn: Aliases::new(), tasks: BTreeMap::new(), - timer_queue: ident_gen.mk_ident(None), + timer_queue: ident_gen.mk_ident(None, false), ident_gen, } } @@ -174,7 +174,7 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream { let assertions = assertions(app, analysis); - let main = ctxt.ident_gen.mk_ident(None); + let main = ctxt.ident_gen.mk_ident(None, false); quote!( #resources @@ -240,7 +240,7 @@ fn resources(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2: pub static #mut_ #name: #ty = #expr; )); - let alias = ctxt.ident_gen.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, @@ -256,7 +256,7 @@ fn resources(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2: ctxt.statics.insert(name.clone(), alias); } else { - let alias = ctxt.ident_gen.mk_ident(None); + let alias = ctxt.ident_gen.mk_ident(None, false); let symbol = format!("{}::{}", name, alias); items.push( @@ -409,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!(), @@ -419,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!(), @@ -434,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, @@ -567,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, } )); } @@ -586,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 { @@ -595,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"))] @@ -609,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, } )); } @@ -691,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; @@ -830,7 +838,8 @@ fn prelude( #(#cfgs)* pub #name: &'a #mut_ #name )); - let alias = ctxt.ident_gen.mk_ident(None); + // XXX is randomness required? + let alias = ctxt.ident_gen.mk_ident(None, true); items.push(quote!( #(#cfgs)* let #mut_ #alias = unsafe { @@ -847,7 +856,8 @@ fn prelude( #(#cfgs)* pub #name: rtfm::Exclusive<'a, #name> )); - let alias = ctxt.ident_gen.mk_ident(None); + // XXX is randomness required? + let alias = ctxt.ident_gen.mk_ident(None, true); items.push(quote!( #(#cfgs)* let #mut_ #alias = unsafe { @@ -914,7 +924,7 @@ fn prelude( } } - let alias = ctxt.ident_gen.mk_ident(None); + let alias = ctxt.ident_gen.mk_ident(None, false); let unsafety = if needs_unsafe { Some(quote!(unsafe)) } else { @@ -975,7 +985,8 @@ fn prelude( continue; } - ctxt.schedule_fn.insert(task.clone(), ctxt.ident_gen.mk_ident(None)); + ctxt.schedule_fn + .insert(task.clone(), ctxt.ident_gen.mk_ident(None, false)); } items.push(quote!( @@ -991,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)* ) @@ -1035,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()), @@ -1087,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!(), }; @@ -1096,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!(), @@ -1104,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!(), }; @@ -1176,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 = ctxt.ident_gen.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() } )); } @@ -1217,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 = ctxt.ident_gen.mk_ident(None); - let free_alias = ctxt.ident_gen.mk_ident(None); - let inputs_alias = ctxt.ident_gen.mk_ident(None); - let task_alias = ctxt.ident_gen.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; @@ -1281,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: ctxt.ident_gen.mk_ident(None), + spawn_fn: ctxt.ident_gen.mk_ident(None, false), #[cfg(feature = "timer-queue")] scheduleds: scheduleds_alias, @@ -1300,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!(), @@ -1329,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) } )); } @@ -1366,8 +1401,8 @@ fn dispatchers( let device = &app.args.device; for (level, dispatcher) in &analysis.dispatchers { - let ready_alias = ctxt.ident_gen.mk_ident(None); - let enum_alias = ctxt.ident_gen.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 @@ -1431,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"))] () => { @@ -1456,7 +1491,7 @@ fn dispatchers( let attrs = &dispatcher.attrs; let interrupt = &dispatcher.interrupt; let symbol = interrupt.to_string(); - let alias = ctxt.ident_gen.mk_ident(None); + let alias = ctxt.ident_gen.mk_ident(None, false); dispatchers.push(quote!( #(#attrs)* #[export_name = #symbol] @@ -1538,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; @@ -1587,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"))] @@ -1636,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> { @@ -1782,14 +1817,14 @@ fn timer_queue(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro .collect::>(); let logical_prio = analysis.timer_queue.priority; - let alias = ctxt.ident_gen.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| { @@ -1933,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, } )); @@ -1942,7 +1977,7 @@ fn mk_resource( items.push(quote!( #(#cfgs)* struct #struct_<'a> { - #priority: &'a core::cell::Cell, + #priority: &'a rtfm::export::Priority, } )); @@ -2017,35 +2052,31 @@ impl IdentGenerator { let rng = rand::rngs::SmallRng::from_seed(seed); - IdentGenerator { - call_count: 0, - rng, - } + IdentGenerator { call_count: 0, rng } } - fn mk_ident(&mut self, name: Option<&str>) -> Ident { - let n; + fn mk_ident(&mut self, name: Option<&str>, random: bool) -> Ident { let s = if let Some(name) = name { - n = 4; format!("{}_", name) } else { - let crate_name = env!("CARGO_PKG_NAME").replace("-", "_").to_lowercase(); - n = 4; - format!("{}__internal__", crate_name) + "__rtfm_internal_".to_string() }; - let mut s = format!("{}{}_", s, self.call_count); + let mut s = format!("{}{}", s, self.call_count); + self.call_count += 1; - for i in 0..n { - 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) + 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) + } } } - self.call_count += 1; - Ident::new(&s, Span::call_site()) } } diff --git a/src/export.rs b/src/export.rs index ed40b62164..0d7466144c 100644 --- a/src/export.rs +++ b/src/export.rs @@ -39,6 +39,29 @@ 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 +125,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 +158,7 @@ where #[inline(always)] pub unsafe fn claim( ptr: *mut T, - priority: &Cell, + priority: &Priority, ceiling: u8, _nvic_prio_bits: u8, f: F, From 7ce052be372ad5e3671e4f470ac552db075162fb Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 16 Feb 2019 00:26:07 +0100 Subject: [PATCH 09/10] cargo fmt --- macros/src/check.rs | 8 ++++---- src/export.rs | 4 +++- tests/compiletest.rs | 22 ++++++++++------------ 3 files changed, 17 insertions(+), 17 deletions(-) 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/src/export.rs b/src/export.rs index 0d7466144c..6eae65f280 100644 --- a/src/export.rs +++ b/src/export.rs @@ -47,7 +47,9 @@ pub struct Priority { impl Priority { #[inline(always)] pub unsafe fn new(value: u8) -> Self { - Priority { inner: Cell::new(value)} + Priority { + inner: Cell::new(value), + } } // these two methods are used by claim (see below) but can't be used from the RTFM application 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); From e5e54ee8f1b7afca614f642ee064a7f00a1f8548 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 16 Feb 2019 00:28:12 +0100 Subject: [PATCH 10/10] rebase fix --- macros/src/codegen.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 1405c2f69a..af09ad4286 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -364,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