Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Resources initialized at runtime #37

Closed
japaric opened this issue Jul 23, 2017 · 10 comments
Closed

[RFC] Resources initialized at runtime #37

japaric opened this issue Jul 23, 2017 · 10 comments
Milestone

Comments

@japaric
Copy link
Collaborator

japaric commented Jul 23, 2017

Motivation

In the current version of RTFM every resource must be assigned an initial value
at compile time and in const / static context. This requirement can sometimes be
too strict given the current limitations of const evaluation (no loops, no
conditionals, etc.).

In other cases one may want to initialize a resource according to the outcome of
the init function. For instance one may want to store the frequency of the
processor, product of the initialization routine, in a variable; in the current
system this requires the programmer to manually keep a copy of the value (as a
literal) in the resources section of the app! macro, which is error prone.

The workaround for these issues is to use an Option for the resource and to
select None as the initial value. Then a value can be assigned to the resource
resource in the init function. The downside of this approach is that it
requires the program to unwrap or match the Option value whenever they
want to access the resource data. And, of course, there's no compile time
guarantee that the programmer won't forget to actually initialize the Option
resource to some value in init; forgetting to do so would cause a panic! at
runtime.

Design

We make the initial value of resources optional in the app! macro:

app! {
    resources: {
        static BUFFER: [u8; 32] = [0; 32];
        static FREQUENCY: u32;
        static FOO: Vec<u8>;
    },
}

When the initial value of any resource is omitted the signature of init will
be changed to force the programmer to assign a value to these resources during
the initialization phase:

fn init(p: init::Peripherals, r: init::Resources) -> init::ResourceValues {
    // .. initialize stuff like the heap allocator ..

    // These are the initial values of the "uninitialized" resources
    init::ResourceValues {
        FREQUENCY: frequency,
        FOO: vec![a, b, c],
    }
}

Apart from this change in init there's no difference between using resources
with different initialization strategies.

What goes on under the hood?

Under the hood the resource without initial values will actually be static
variables with type equal to Option and initialized to None. However, since
we know that the resources will be initialized in init and before any task
(or idle) can run we can expose them to the user without the Option wrapper,
plus we can apply unsafe optimizations like intrinsics::unreachable under the
hood to eliminate branches and avoid unwrapping.

Possible extensions

Optimizing the pre-main initialization of static variables

Since the runtime initialized resources will be represented as static
variables containing an Option they'll still be initialized before init by
the runtime even though those initial values won't be read. This useless
initialization can be optimized away with the help of linker script magic:

// runtime initialized resources
#[link_section = ".uninit"]
static mut FREQUENCY: Option<u32> = None;

#[link_section = ".uninit"]
static mut FOO: Option<Vec<u8>> = None;

// "normal" resources
static mut BUFFER: [u8; 32] = [0; 32];

static variables placed in the .uninit linker section won't be initialized
before init. This may require changes in cortex-m-rt to ensure that static
variables placed in the .uninit section end up having a valid address in the
RAM region though. cf. rust-embedded/cortex-m-rt#32

cc @cr1901

@pftbest
Copy link

pftbest commented Jul 23, 2017

Under the hood the resource without initial values will actually be static
variables with type equal to Option and initialized to None.

Then it's not going to be a zero cost abstraction. Option<u32> is actually 8 bytes in size.
Too bad that mem::zeroed() is not a const fn.

@perlindgren
Copy link
Collaborator

perlindgren commented Jul 23, 2017 via email

@japaric
Copy link
Collaborator Author

japaric commented Jul 23, 2017

Did mem::size_of become a const fn yet? If yes then we could use static mut FOO: [u8; size_of::<T>()] = [0; size_of::<T>()]; and cast &[u8; size_of::<T>()] into &T behind the hood. And pray that nothing explodes ... as in hope that LLVM won't generate UB.

@pftbest
Copy link

pftbest commented Jul 23, 2017

[u8; size_of::()] may not have a correct alignment, unfortunately.

@perlindgren
Copy link
Collaborator

perlindgren commented Jul 23, 2017 via email

@japaric
Copy link
Collaborator Author

japaric commented Jul 23, 2017

@pftbest we can use ([u8; mem::size_of::<T>()], [T; 0]) then. See bellow:

#![feature(const_fn)]

use std::mem;

static mut FOO: ([u8; mem::size_of::<u64>()], [u64; 0]) =
    ([0; mem::size_of::<u64>()], []);

fn main() {
    unsafe {
        println!("{}", mem::align_of_val(&FOO));
        println!("{}", mem::size_of_val(&FOO));
    }
}
$ cargo run
8
8

@pftbest
Copy link

pftbest commented Jul 24, 2017

@japaric, this might work. LLVM doesn't have a strict aliasing rules like in C, so if the size and alignment match I don't see why it would fail (yet).

But still it's a very ugly hack. Maybe we can ask lang team to make mem::zeroed const, until CTFE lands?

@japaric japaric added this to the v0.2.x milestone Jul 29, 2017
@jonas-schievink
Copy link
Contributor

👍 for this feature, it would be very useful to have!

Wouldn't it be possible to use a union for this? Something like this:

union LateResource {
    uninit: (),
    init: Foo,
}

// Resources:
static mut FOO: LateResource = LateResource { uninit: () };

And then after init was called:

let resource_values = init(...);
FOO = LateResource { init: resource_values.FOO };

@pftbest
Copy link

pftbest commented Sep 3, 2017

@jonas-schievink This is a great idea, thanks!

@jonas-schievink
Copy link
Contributor

I'd like to give this a shot if no one is working on this yet :)

andrewgazelka pushed a commit to andrewgazelka/cortex-m-rtic that referenced this issue Nov 3, 2021
This would allow running regular `cargo test` on a crates
that depend on this crate.

Depends on rtic-rs#37
andrewgazelka pushed a commit to andrewgazelka/cortex-m-rtic that referenced this issue Nov 3, 2021
Put global asm behind target cfg
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants