Make identifiers deterministic.

This commit is contained in:
Hugo van der Wijst 2019-01-16 17:36:55 -08:00 committed by Jorge Aparicio
parent 4f193df0ef
commit be8a5e89b8
3 changed files with 62 additions and 65 deletions

1
.gitignore vendored
View file

@ -4,3 +4,4 @@
/book/*/book /book/*/book
/target /target
Cargo.lock Cargo.lock
*.hex

View file

@ -125,6 +125,12 @@ main() {
cargo clean cargo clean
for ex in ${exs[@]}; do 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 if [ $ex != types ]; then
arm_example "build" $ex "debug" "" "2" arm_example "build" $ex "debug" "" "2"
cmp ${ex}_debug_1.hex ${ex}_debug_2.hex cmp ${ex}_debug_1.hex ${ex}_debug_2.hex

View file

@ -3,8 +3,6 @@
use proc_macro::TokenStream; use proc_macro::TokenStream;
use std::{ use std::{
collections::{BTreeMap, HashMap}, collections::{BTreeMap, HashMap},
sync::atomic::{AtomicUsize, Ordering},
time::{SystemTime, UNIX_EPOCH},
}; };
use proc_macro2::Span; use proc_macro2::Span;
@ -44,6 +42,8 @@ struct Context {
tasks: BTreeMap<Ident, Task>, tasks: BTreeMap<Ident, Task>,
// Alias (`struct` / `static mut`) // Alias (`struct` / `static mut`)
timer_queue: Ident, timer_queue: Ident,
// Generator of Ident names or suffixes
ident_gen: IdentGenerator,
} }
struct Dispatcher { struct Dispatcher {
@ -63,19 +63,22 @@ struct Task {
impl Default for Context { impl Default for Context {
fn default() -> Self { fn default() -> Self {
let mut ident_gen = IdentGenerator::new();
Context { Context {
#[cfg(feature = "timer-queue")] #[cfg(feature = "timer-queue")]
baseline: mk_ident(None), baseline: ident_gen.mk_ident(None),
dispatchers: BTreeMap::new(), dispatchers: BTreeMap::new(),
idle: mk_ident(Some("idle")), idle: ident_gen.mk_ident(Some("idle")),
init: mk_ident(Some("init")), init: ident_gen.mk_ident(Some("init")),
priority: mk_ident(None), priority: ident_gen.mk_ident(None),
statics: Aliases::new(), statics: Aliases::new(),
resources: HashMap::new(), resources: HashMap::new(),
schedule_enum: mk_ident(None), schedule_enum: ident_gen.mk_ident(None),
schedule_fn: Aliases::new(), schedule_fn: Aliases::new(),
tasks: BTreeMap::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!(), () => 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 pre_init = pre_init(&ctxt, &app, analysis);
let assertions = assertions(app, analysis); let assertions = assertions(app, analysis);
let main = mk_ident(None); let main = ctxt.ident_gen.mk_ident(None);
quote!( quote!(
#resources #resources
@ -236,7 +239,7 @@ fn resources(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2:
pub static #mut_ #name: #ty = #expr; 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) { if let Some(Ownership::Shared { ceiling }) = analysis.ownerships.get(name) {
items.push(mk_resource( items.push(mk_resource(
ctxt, ctxt,
@ -252,7 +255,7 @@ fn resources(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2:
ctxt.statics.insert(name.clone(), alias); ctxt.statics.insert(name.clone(), alias);
} else { } else {
let alias = mk_ident(None); let alias = ctxt.ident_gen.mk_ident(None);
let symbol = format!("{}::{}", name, alias); let symbol = format!("{}::{}", name, alias);
items.push( items.push(
@ -826,7 +829,7 @@ fn prelude(
#(#cfgs)* #(#cfgs)*
pub #name: &'a #mut_ #name pub #name: &'a #mut_ #name
)); ));
let alias = mk_ident(None); let alias = ctxt.ident_gen.mk_ident(None);
items.push(quote!( items.push(quote!(
#(#cfgs)* #(#cfgs)*
let #mut_ #alias = unsafe { let #mut_ #alias = unsafe {
@ -843,7 +846,7 @@ fn prelude(
#(#cfgs)* #(#cfgs)*
pub #name: rtfm::Exclusive<'a, #name> pub #name: rtfm::Exclusive<'a, #name>
)); ));
let alias = mk_ident(None); let alias = ctxt.ident_gen.mk_ident(None);
items.push(quote!( items.push(quote!(
#(#cfgs)* #(#cfgs)*
let #mut_ #alias = unsafe { 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 { let unsafety = if needs_unsafe {
Some(quote!(unsafe)) Some(quote!(unsafe))
} else { } else {
@ -971,7 +974,7 @@ fn prelude(
continue; 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!( items.push(quote!(
@ -1100,7 +1103,7 @@ fn exceptions(ctxt: &mut Context, app: &App, analysis: &Analysis) -> Vec<proc_ma
let locals = mk_locals(&exception.statics, false); let locals = mk_locals(&exception.statics, false);
let symbol = ident.to_string(); let symbol = ident.to_string();
let alias = mk_ident(None); let alias = ctxt.ident_gen.mk_ident(None);
let unsafety = &exception.unsafety; let unsafety = &exception.unsafety;
quote!( quote!(
#module #module
@ -1179,7 +1182,7 @@ fn interrupts(
}; };
let locals = mk_locals(&interrupt.statics, false); let locals = mk_locals(&interrupt.statics, false);
let alias = mk_ident(None); let alias = ctxt.ident_gen.mk_ident(None);
let symbol = ident.to_string(); let symbol = ident.to_string();
let unsafety = &interrupt.unsafety; let unsafety = &interrupt.unsafety;
scoped.push(quote!( scoped.push(quote!(
@ -1213,10 +1216,10 @@ fn tasks(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::Tok
// first pass to generate buffers (statics and resources) and spawn aliases // first pass to generate buffers (statics and resources) and spawn aliases
for (name, task) in &app.tasks { for (name, task) in &app.tasks {
#[cfg(feature = "timer-queue")] #[cfg(feature = "timer-queue")]
let scheduleds_alias = mk_ident(None); let scheduleds_alias = ctxt.ident_gen.mk_ident(None);
let free_alias = mk_ident(None); let free_alias = ctxt.ident_gen.mk_ident(None);
let inputs_alias = mk_ident(None); let inputs_alias = ctxt.ident_gen.mk_ident(None);
let task_alias = mk_ident(Some(&name.to_string())); let task_alias = ctxt.ident_gen.mk_ident(Some(&name.to_string()));
let inputs = &task.inputs; let inputs = &task.inputs;
@ -1277,7 +1280,7 @@ fn tasks(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::Tok
alias: task_alias, alias: task_alias,
free_queue: free_alias, free_queue: free_alias,
inputs: inputs_alias, inputs: inputs_alias,
spawn_fn: mk_ident(None), spawn_fn: ctxt.ident_gen.mk_ident(None),
#[cfg(feature = "timer-queue")] #[cfg(feature = "timer-queue")]
scheduleds: scheduleds_alias, scheduleds: scheduleds_alias,
@ -1362,8 +1365,8 @@ fn dispatchers(
let device = &app.args.device; let device = &app.args.device;
for (level, dispatcher) in &analysis.dispatchers { for (level, dispatcher) in &analysis.dispatchers {
let ready_alias = mk_ident(None); let ready_alias = ctxt.ident_gen.mk_ident(None);
let enum_alias = mk_ident(None); let enum_alias = ctxt.ident_gen.mk_ident(None);
let capacity = mk_typenum_capacity(dispatcher.capacity, true); let capacity = mk_typenum_capacity(dispatcher.capacity, true);
let variants = dispatcher let variants = dispatcher
@ -1452,7 +1455,7 @@ fn dispatchers(
let attrs = &dispatcher.attrs; let attrs = &dispatcher.attrs;
let interrupt = &dispatcher.interrupt; let interrupt = &dispatcher.interrupt;
let symbol = interrupt.to_string(); let symbol = interrupt.to_string();
let alias = mk_ident(None); let alias = ctxt.ident_gen.mk_ident(None);
dispatchers.push(quote!( dispatchers.push(quote!(
#(#attrs)* #(#attrs)*
#[export_name = #symbol] #[export_name = #symbol]
@ -1703,7 +1706,7 @@ fn schedule(ctxt: &Context, app: &App) -> proc_macro2::TokenStream {
quote!(#(#items)*) 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; let tasks = &analysis.timer_queue.tasks;
if tasks.is_empty() { if tasks.is_empty() {
@ -1778,7 +1781,7 @@ fn timer_queue(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::T
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let logical_prio = analysis.timer_queue.priority; let logical_prio = analysis.timer_queue.priority;
let alias = mk_ident(None); let alias = ctxt.ident_gen.mk_ident(None);
items.push(quote!( items.push(quote!(
#[export_name = "SysTick"] #[export_name = "SysTick"]
#[doc(hidden)] #[doc(hidden)]
@ -1989,29 +1992,16 @@ fn mk_typenum_capacity(capacity: u8, power_of_two: bool) -> proc_macro2::TokenSt
quote!(rtfm::export::consts::#ident) quote!(rtfm::export::consts::#ident)
} }
fn mk_ident(name: Option<&str>) -> Ident { struct IdentGenerator {
static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); 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
} }
for (i, v) in seed.iter_mut().skip(8).take(4).enumerate() { impl IdentGenerator {
*v = ((nanos >> (i * 8)) & 0xFF) as u8 fn new() -> IdentGenerator {
} IdentGenerator { rng: rand::rngs::SmallRng::seed_from_u64(0) }
for (i, v) in seed.iter_mut().skip(12).enumerate() {
*v = ((count >> (i * 8)) & 0xFF) as u8
} }
fn mk_ident(&mut self, name: Option<&str>) -> Ident {
let n; let n;
let mut s = if let Some(name) = name { let mut s = if let Some(name) = name {
n = 4; n = 4;
@ -2021,17 +2011,17 @@ fn mk_ident(name: Option<&str>) -> Ident {
String::new() String::new()
}; };
let mut rng = rand::rngs::SmallRng::from_seed(seed);
for i in 0..n { for i in 0..n {
if i == 0 || rng.gen() { if i == 0 || self.rng.gen() {
s.push(('a' as u8 + rng.gen::<u8>() % 25) as char) s.push(('a' as u8 + self.rng.gen::<u8>() % 25) as char)
} else { } else {
s.push(('0' as u8 + rng.gen::<u8>() % 10) as char) 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* // `once = true` means that these locals will be called from a function that will run *once*
fn mk_locals(locals: &BTreeMap<Ident, Static>, once: bool) -> proc_macro2::TokenStream { fn mk_locals(locals: &BTreeMap<Ident, Static>, once: bool) -> proc_macro2::TokenStream {