add "nightly" feature

This commit is contained in:
Jorge Aparicio 2019-02-15 19:52:25 +01:00
parent c91b14bcd4
commit 88078e7770
5 changed files with 248 additions and 87 deletions

View file

@ -51,6 +51,7 @@ features = ["exit"]
version = "0.5.1" version = "0.5.1"
[features] [features]
nightly = ["cortex-m-rtfm-macros/nightly", "heapless/const-fn"]
timer-queue = ["cortex-m-rtfm-macros/timer-queue"] timer-queue = ["cortex-m-rtfm-macros/timer-queue"]
[target.x86_64-unknown-linux-gnu.dev-dependencies] [target.x86_64-unknown-linux-gnu.dev-dependencies]

View file

@ -27,4 +27,5 @@ default-features = false
version = "0.5.5" version = "0.5.5"
[features] [features]
timer-queue = [] timer-queue = []
nightly = []

View file

@ -283,7 +283,7 @@ fn resources(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2:
if let Some(Ownership::Shared { ceiling }) = analysis.ownerships.get(name) { if let Some(Ownership::Shared { ceiling }) = analysis.ownerships.get(name) {
if res.mutability.is_some() { if res.mutability.is_some() {
let ptr = if res.expr.is_none() { let ptr = if res.expr.is_none() {
quote!(unsafe { #alias.get_mut() }) quote!(unsafe { &mut *#alias.as_mut_ptr() })
} else { } else {
quote!(unsafe { &mut #alias }) quote!(unsafe { &mut #alias })
}; };
@ -494,8 +494,10 @@ fn post_init(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::Tok
// Enable and start the system timer // Enable and start the system timer
if !analysis.timer_queue.tasks.is_empty() { if !analysis.timer_queue.tasks.is_empty() {
let tq = &ctxt.timer_queue; let tq = &ctxt.timer_queue;
exprs.push(quote!(#tq.get_mut().syst.set_clock_source(rtfm::export::SystClkSource::Core))); exprs.push(
exprs.push(quote!(#tq.get_mut().syst.enable_counter())); quote!((*#tq.as_mut_ptr()).syst.set_clock_source(rtfm::export::SystClkSource::Core)),
);
exprs.push(quote!((*#tq.as_mut_ptr()).syst.enable_counter()));
} }
// Enable cycle counter // Enable cycle counter
@ -903,21 +905,21 @@ fn prelude(
)); ));
} }
} else { } else {
let method = if mut_.is_some() { let expr = if mut_.is_some() {
quote!(get_mut) quote!(&mut *#alias.as_mut_ptr())
} else { } else {
quote!(get_ref) quote!(&*#alias.as_ptr())
}; };
if exclusive { if exclusive {
exprs.push(quote!( exprs.push(quote!(
#(#cfgs)* #(#cfgs)*
#name: rtfm::Exclusive(#alias.#method()) #name: rtfm::Exclusive(#expr)
)); ));
} else { } else {
exprs.push(quote!( exprs.push(quote!(
#(#cfgs)* #(#cfgs)*
#name: #alias.#method() #name: #expr
)); ));
} }
} }
@ -1254,8 +1256,9 @@ fn tasks(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::Tok
let ty = tuple_ty(inputs); let ty = tuple_ty(inputs);
let capacity_lit = mk_capacity_literal(analysis.capacities[name]); let capacity = analysis.capacities[name];
let capacity_ty = mk_typenum_capacity(analysis.capacities[name], true); let capacity_lit = mk_capacity_literal(capacity);
let capacity_ty = mk_typenum_capacity(capacity, true);
let resource = mk_resource( let resource = mk_resource(
ctxt, ctxt,
@ -1263,7 +1266,11 @@ fn tasks(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::Tok
&free_alias, &free_alias,
quote!(rtfm::export::FreeQueue<#capacity_ty>), quote!(rtfm::export::FreeQueue<#capacity_ty>),
*analysis.free_queues.get(name).unwrap_or(&0), *analysis.free_queues.get(name).unwrap_or(&0),
quote!(#free_alias.get_mut()), if cfg!(feature = "nightly") {
quote!(&mut #free_alias)
} else {
quote!(#free_alias.get_mut())
},
app, app,
None, None,
); );
@ -1273,12 +1280,24 @@ fn tasks(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::Tok
() => { () => {
let scheduleds_symbol = format!("{}::SCHEDULED_TIMES::{}", name, scheduleds_alias); let scheduleds_symbol = format!("{}::SCHEDULED_TIMES::{}", name, scheduleds_alias);
quote!( if cfg!(feature = "nightly") {
#[doc = #scheduleds_symbol] let inits =
static mut #scheduleds_alias: (0..capacity).map(|_| quote!(rtfm::export::MaybeUninit::uninitialized()));
rtfm::export::MaybeUninit<[rtfm::Instant; #capacity_lit]> =
rtfm::export::MaybeUninit::uninitialized(); quote!(
) #[doc = #scheduleds_symbol]
static mut #scheduleds_alias:
[rtfm::export::MaybeUninit<rtfm::Instant>; #capacity_lit] =
[#(#inits),*];
)
} else {
quote!(
#[doc = #scheduleds_symbol]
static mut #scheduleds_alias:
rtfm::export::MaybeUninit<[rtfm::Instant; #capacity_lit]> =
rtfm::export::MaybeUninit::uninitialized();
)
}
} }
#[cfg(not(feature = "timer-queue"))] #[cfg(not(feature = "timer-queue"))]
() => quote!(), () => quote!(),
@ -1286,20 +1305,36 @@ fn tasks(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro2::Tok
let inputs_symbol = format!("{}::INPUTS::{}", name, inputs_alias); let inputs_symbol = format!("{}::INPUTS::{}", name, inputs_alias);
let free_symbol = format!("{}::FREE_QUEUE::{}", name, free_alias); let free_symbol = format!("{}::FREE_QUEUE::{}", name, free_alias);
items.push(quote!( if cfg!(feature = "nightly") {
// FIXME(MaybeUninit) MaybeUninit won't be necessary when core::mem::MaybeUninit let inits = (0..capacity).map(|_| quote!(rtfm::export::MaybeUninit::uninitialized()));
// stabilizes because heapless constructors will work in const context
#[doc = #free_symbol] items.push(quote!(
static mut #free_alias: rtfm::export::MaybeUninit< #[doc = #free_symbol]
static mut #free_alias: rtfm::export::FreeQueue<#capacity_ty> = unsafe {
rtfm::export::FreeQueue::new_sc()
};
#[doc = #inputs_symbol]
static mut #inputs_alias: [rtfm::export::MaybeUninit<#ty>; #capacity_lit] =
[#(#inits),*];
));
} else {
items.push(quote!(
#[doc = #free_symbol]
static mut #free_alias: rtfm::export::MaybeUninit<
rtfm::export::FreeQueue<#capacity_ty> rtfm::export::FreeQueue<#capacity_ty>
> = rtfm::export::MaybeUninit::uninitialized(); > = rtfm::export::MaybeUninit::uninitialized();
#[doc = #inputs_symbol]
static mut #inputs_alias: rtfm::export::MaybeUninit<[#ty; #capacity_lit]> =
rtfm::export::MaybeUninit::uninitialized();
));
}
items.push(quote!(
#resource #resource
#[doc = #inputs_symbol]
static mut #inputs_alias: rtfm::export::MaybeUninit<[#ty; #capacity_lit]> =
rtfm::export::MaybeUninit::uninitialized();
#scheduleds_static #scheduleds_static
)); ));
@ -1428,18 +1463,31 @@ fn dispatchers(
&ready_alias, &ready_alias,
ty.clone(), ty.clone(),
ceiling, ceiling,
quote!(#ready_alias.get_mut()), if cfg!(feature = "nightly") {
quote!(&mut #ready_alias)
} else {
quote!(#ready_alias.get_mut())
},
app, app,
None, None,
); );
if cfg!(feature = "nightly") {
data.push(quote!(
#[doc = #symbol]
static mut #ready_alias: #ty = unsafe { #e::ReadyQueue::new_sc() };
));
} else {
data.push(quote!(
#[doc = #symbol]
static mut #ready_alias: #e::MaybeUninit<#ty> = #e::MaybeUninit::uninitialized();
));
}
data.push(quote!( data.push(quote!(
#[allow(dead_code)] #[allow(dead_code)]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
enum #enum_alias { #(#variants,)* } enum #enum_alias { #(#variants,)* }
#[doc = #symbol]
static mut #ready_alias: #e::MaybeUninit<#ty> = #e::MaybeUninit::uninitialized();
#resource #resource
)); ));
@ -1462,9 +1510,14 @@ fn dispatchers(
#[cfg(feature = "timer-queue")] #[cfg(feature = "timer-queue")]
() => { () => {
let scheduleds = &task_.scheduleds; let scheduleds = &task_.scheduleds;
let scheduled = if cfg!(feature = "nightly") {
quote!(#scheduleds.get_unchecked(usize::from(index)).as_ptr())
} else {
quote!(#scheduleds.get_ref().get_unchecked(usize::from(index)))
};
baseline_let = quote!( baseline_let = quote!(
let baseline = let baseline = ptr::read(#scheduled);
ptr::read(#scheduleds.get_ref().get_unchecked(usize::from(index)));
); );
call = quote!(#alias(&baseline, #pats)); call = quote!(#alias(&baseline, #pats));
} }
@ -1475,12 +1528,24 @@ fn dispatchers(
} }
}; };
let (free_, input) = if cfg!(feature = "nightly") {
(
quote!(#free),
quote!(#inputs.get_unchecked(usize::from(index)).as_ptr()),
)
} else {
(
quote!(#free.get_mut()),
quote!(#inputs.get_ref().get_unchecked(usize::from(index))),
)
};
quote!( quote!(
#(#cfgs)* #(#cfgs)*
#enum_alias::#task => { #enum_alias::#task => {
#baseline_let #baseline_let
let input = ptr::read(#inputs.get_ref().get_unchecked(usize::from(index))); let input = ptr::read(#input);
#free.get_mut().split().0.enqueue_unchecked(index); #free_.split().0.enqueue_unchecked(index);
let (#pats) = input; let (#pats) = input;
#call #call
} }
@ -1492,6 +1557,11 @@ fn dispatchers(
let interrupt = &dispatcher.interrupt; let interrupt = &dispatcher.interrupt;
let symbol = interrupt.to_string(); let symbol = interrupt.to_string();
let alias = ctxt.ident_gen.mk_ident(None, false); let alias = ctxt.ident_gen.mk_ident(None, false);
let ready_alias_ = if cfg!(feature = "nightly") {
quote!(#ready_alias)
} else {
quote!(#ready_alias.get_mut())
};
dispatchers.push(quote!( dispatchers.push(quote!(
#(#attrs)* #(#attrs)*
#[export_name = #symbol] #[export_name = #symbol]
@ -1502,7 +1572,7 @@ fn dispatchers(
let _ = #device::interrupt::#interrupt; let _ = #device::interrupt::#interrupt;
rtfm::export::run(|| { rtfm::export::run(|| {
while let Some((task, index)) = #ready_alias.get_mut().split().1.dequeue() { while let Some((task, index)) = #ready_alias_.split().1.dequeue() {
match task { match task {
#(#arms)* #(#arms)*
} }
@ -1550,12 +1620,21 @@ fn spawn(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenSt
#[cfg(feature = "timer-queue")] #[cfg(feature = "timer-queue")]
() => { () => {
let scheduleds = &ctxt.tasks[name].scheduleds; let scheduleds = &ctxt.tasks[name].scheduleds;
quote!( if cfg!(feature = "nightly") {
ptr::write( quote!(
#scheduleds.get_mut().get_unchecked_mut(usize::from(index)), ptr::write(
#baseline, #scheduleds.get_unchecked_mut(usize::from(index)).as_mut_ptr(),
); #baseline,
) );
)
} else {
quote!(
ptr::write(
#scheduleds.get_mut().get_unchecked_mut(usize::from(index)),
#baseline,
);
)
}
} }
#[cfg(not(feature = "timer-queue"))] #[cfg(not(feature = "timer-queue"))]
() => quote!(), () => quote!(),
@ -1568,6 +1647,11 @@ fn spawn(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenSt
() => quote!(), () => quote!(),
}; };
let input = if cfg!(feature = "nightly") {
quote!(#inputs.get_unchecked_mut(usize::from(index)).as_mut_ptr())
} else {
quote!(#inputs.get_mut().get_unchecked_mut(usize::from(index)))
};
items.push(quote!( items.push(quote!(
#[inline(always)] #[inline(always)]
#(#cfgs)* #(#cfgs)*
@ -1581,7 +1665,7 @@ fn spawn(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenSt
use rtfm::Mutex; use rtfm::Mutex;
if let Some(index) = (#free { #priority }).lock(|f| f.split().1.dequeue()) { if let Some(index) = (#free { #priority }).lock(|f| f.split().1.dequeue()) {
ptr::write(#inputs.get_mut().get_unchecked_mut(usize::from(index)), (#pats)); ptr::write(#input, (#pats));
#scheduleds_write #scheduleds_write
#ready { #priority }.lock(|rq| { #ready { #priority }.lock(|rq| {
@ -1667,6 +1751,17 @@ fn schedule(ctxt: &Context, app: &App) -> proc_macro2::TokenStream {
let ty = tuple_ty(args); let ty = tuple_ty(args);
let pats = tuple_pat(args); let pats = tuple_pat(args);
let input = if cfg!(feature = "nightly") {
quote!(#inputs.get_unchecked_mut(usize::from(index)).as_mut_ptr())
} else {
quote!(#inputs.get_mut().get_unchecked_mut(usize::from(index)))
};
let scheduled = if cfg!(feature = "nightly") {
quote!(#scheduleds.get_unchecked_mut(usize::from(index)).as_mut_ptr())
} else {
quote!(#scheduleds.get_mut().get_unchecked_mut(usize::from(index)))
};
items.push(quote!( items.push(quote!(
#[inline(always)] #[inline(always)]
#(#cfgs)* #(#cfgs)*
@ -1680,11 +1775,8 @@ fn schedule(ctxt: &Context, app: &App) -> proc_macro2::TokenStream {
use rtfm::Mutex; use rtfm::Mutex;
if let Some(index) = (#free { #priority }).lock(|f| f.split().1.dequeue()) { if let Some(index) = (#free { #priority }).lock(|f| f.split().1.dequeue()) {
ptr::write(#inputs.get_mut().get_unchecked_mut(usize::from(index)), (#pats)); ptr::write(#input, (#pats));
ptr::write( ptr::write(#scheduled, instant);
#scheduleds.get_mut().get_unchecked_mut(usize::from(index)),
instant,
);
let nr = rtfm::export::NotReady { let nr = rtfm::export::NotReady {
instant, instant,
@ -1772,12 +1864,20 @@ fn timer_queue(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro
let cap = mk_typenum_capacity(analysis.timer_queue.capacity, false); let cap = mk_typenum_capacity(analysis.timer_queue.capacity, false);
let tq = &ctxt.timer_queue; let tq = &ctxt.timer_queue;
let symbol = format!("TIMER_QUEUE::{}", tq); let symbol = format!("TIMER_QUEUE::{}", tq);
items.push(quote!( if cfg!(feature = "nightly") {
#[doc = #symbol] items.push(quote!(
static mut #tq: #[doc = #symbol]
rtfm::export::MaybeUninit<rtfm::export::TimerQueue<#enum_, #cap>> = static mut #tq: rtfm::export::MaybeUninit<rtfm::export::TimerQueue<#enum_, #cap>> =
rtfm::export::MaybeUninit::uninitialized(); rtfm::export::MaybeUninit::uninitialized();
)); ));
} else {
items.push(quote!(
#[doc = #symbol]
static mut #tq:
rtfm::export::MaybeUninit<rtfm::export::TimerQueue<#enum_, #cap>> =
rtfm::export::MaybeUninit::uninitialized();
));
}
items.push(mk_resource( items.push(mk_resource(
ctxt, ctxt,
@ -1785,7 +1885,7 @@ fn timer_queue(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro
tq, tq,
quote!(rtfm::export::TimerQueue<#enum_, #cap>), quote!(rtfm::export::TimerQueue<#enum_, #cap>),
analysis.timer_queue.ceiling, analysis.timer_queue.ceiling,
quote!(#tq.get_mut()), quote!(&mut *#tq.as_mut_ptr()),
app, app,
None, None,
)); ));
@ -1842,36 +1942,32 @@ fn timer_queue(ctxt: &mut Context, app: &App, analysis: &Analysis) -> proc_macro
fn pre_init(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenStream { fn pre_init(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::TokenStream {
let mut exprs = vec![]; let mut exprs = vec![];
// FIXME(MaybeUninit) Because we are using a fake MaybeUninit we need to set the Option tag to if !cfg!(feature = "nightly") {
// Some; otherwise the get_ref and get_mut could result in UB. Also heapless collections can't // these are `MaybeUninit` arrays
// be constructed in const context; we have to initialize them at runtime (i.e. here). for task in ctxt.tasks.values() {
let inputs = &task.inputs;
exprs.push(quote!(#inputs.set(core::mem::uninitialized());))
}
// these are `MaybeUninit` arrays #[cfg(feature = "timer-queue")]
for task in ctxt.tasks.values() { for task in ctxt.tasks.values() {
let inputs = &task.inputs; let scheduleds = &task.scheduleds;
exprs.push(quote!(#inputs.set(core::mem::uninitialized());)) exprs.push(quote!(#scheduleds.set(core::mem::uninitialized());))
}
// these are `MaybeUninit` `ReadyQueue`s
for dispatcher in ctxt.dispatchers.values() {
let rq = &dispatcher.ready_queue;
exprs.push(quote!(#rq.set(rtfm::export::ReadyQueue::new_sc());))
}
// these are `MaybeUninit` `FreeQueue`s
for task in ctxt.tasks.values() {
let fq = &task.free_queue;
exprs.push(quote!(#fq.set(rtfm::export::FreeQueue::new_sc());))
}
} }
#[cfg(feature = "timer-queue")]
for task in ctxt.tasks.values() {
let scheduleds = &task.scheduleds;
exprs.push(quote!(#scheduleds.set(core::mem::uninitialized());))
}
// these are `MaybeUninit` `ReadyQueue`s
for dispatcher in ctxt.dispatchers.values() {
let rq = &dispatcher.ready_queue;
exprs.push(quote!(#rq.set(rtfm::export::ReadyQueue::new_sc());))
}
// these are `MaybeUninit` `FreeQueue`s
for task in ctxt.tasks.values() {
let fq = &task.free_queue;
exprs.push(quote!(#fq.set(rtfm::export::FreeQueue::new_sc());))
}
// end-of-FIXME
// Initialize the timer queue // Initialize the timer queue
if !analysis.timer_queue.tasks.is_empty() { if !analysis.timer_queue.tasks.is_empty() {
let tq = &ctxt.timer_queue; let tq = &ctxt.timer_queue;
@ -1881,10 +1977,15 @@ fn pre_init(ctxt: &Context, app: &App, analysis: &Analysis) -> proc_macro2::Toke
// Populate the `FreeQueue`s // Populate the `FreeQueue`s
for (name, task) in &ctxt.tasks { for (name, task) in &ctxt.tasks {
let fq = &task.free_queue; let fq = &task.free_queue;
let fq_ = if cfg!(feature = "nightly") {
quote!(#fq)
} else {
quote!(#fq.get_mut())
};
let capacity = analysis.capacities[name]; let capacity = analysis.capacities[name];
exprs.push(quote!( exprs.push(quote!(
for i in 0..#capacity { for i in 0..#capacity {
#fq.get_mut().enqueue_unchecked(i); #fq_.enqueue_unchecked(i);
} }
)) ))
} }

View file

@ -1,8 +1,10 @@
//! IMPLEMENTATION DETAILS. DO NOT USE ANYTHING IN THIS MODULE //! IMPLEMENTATION DETAILS. DO NOT USE ANYTHING IN THIS MODULE
#[cfg(not(debug_assertions))] #[cfg(all(not(feature = "nightly"), not(debug_assertions)))]
use core::hint; use core::hint;
use core::{cell::Cell, ptr, u8}; #[cfg(not(feature = "nightly"))]
use core::ptr;
use core::{cell::Cell, u8};
#[cfg(armv7m)] #[cfg(armv7m)]
use cortex_m::register::basepri; use cortex_m::register::basepri;
@ -64,17 +66,72 @@ impl Priority {
} }
} }
// TODO(MaybeUninit) Until core::mem::MaybeUninit is stabilized we use our own (inefficient) #[cfg(feature = "nightly")]
// implementation pub struct MaybeUninit<T> {
// we newtype so the end-user doesn't need `#![feature(maybe_uninit)]` in their code
inner: core::mem::MaybeUninit<T>,
}
#[cfg(feature = "nightly")]
impl<T> MaybeUninit<T> {
pub const fn uninitialized() -> Self {
MaybeUninit {
inner: core::mem::MaybeUninit::uninitialized(),
}
}
pub fn as_ptr(&self) -> *const T {
self.inner.as_ptr()
}
pub fn as_mut_ptr(&mut self) -> *mut T {
self.inner.as_mut_ptr()
}
pub fn set(&mut self, value: T) -> &mut T {
self.inner.set(value)
}
}
#[cfg(not(feature = "nightly"))]
pub struct MaybeUninit<T> { pub struct MaybeUninit<T> {
value: Option<T>, value: Option<T>,
} }
#[cfg(not(feature = "nightly"))]
impl<T> MaybeUninit<T> { impl<T> MaybeUninit<T> {
pub const fn uninitialized() -> Self { pub const fn uninitialized() -> Self {
MaybeUninit { value: None } MaybeUninit { value: None }
} }
pub fn as_ptr(&self) -> *const T {
if let Some(x) = self.value.as_ref() {
x
} else {
match () {
// Try to catch UB when compiling in release with debug assertions enabled
#[cfg(debug_assertions)]
() => unreachable!(),
#[cfg(not(debug_assertions))]
() => unsafe { hint::unreachable_unchecked() },
}
}
}
pub fn as_mut_ptr(&mut self) -> *mut T {
if let Some(x) = self.value.as_mut() {
x
} else {
match () {
// Try to catch UB when compiling in release with debug assertions enabled
#[cfg(debug_assertions)]
() => unreachable!(),
#[cfg(not(debug_assertions))]
() => unsafe { hint::unreachable_unchecked() },
}
}
}
pub unsafe fn get_ref(&self) -> &T { pub unsafe fn get_ref(&self) -> &T {
if let Some(x) = self.value.as_ref() { if let Some(x) = self.value.as_ref() {
x x

View file

@ -36,6 +36,7 @@
//! [`Instant`]: struct.Instant.html //! [`Instant`]: struct.Instant.html
//! [`Duration`]: struct.Duration.html //! [`Duration`]: struct.Duration.html
#![cfg_attr(feature = "nightly", feature(maybe_uninit))]
#![deny(missing_docs)] #![deny(missing_docs)]
#![deny(warnings)] #![deny(warnings)]
#![no_std] #![no_std]