Skip to content

[RFC] #[uninit] for truly uninitialized static variables #398

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

Open
japaric opened this issue Sep 9, 2018 · 6 comments
Open

[RFC] #[uninit] for truly uninitialized static variables #398

japaric opened this issue Sep 9, 2018 · 6 comments

Comments

@japaric
Copy link
Member

japaric commented Sep 9, 2018

Summary

The #[uninit] attribute will place static [mut] variables into an .uninit
section, located in RAM, that won't be initialized before main.

Motivation

Today, uninitialized (see MaybeUninit) static variables will be placed in
.bss, which means that'll be zeroed before main. This leads to unnecessary
work as leaving the memory uninitialized was the intended behavior.

Design

The #[uninit] attribute will have the following syntax.

#[uninit]
static mut FOO: u32 = {};

The initial value of uninit variables must be the placeholder {}. This
attribute will expand into:

#[link_section = ".uninit"]
static mut FOO: core::mem::MaybeUninit<u32> = core::mem::MaybeUninit::new();

#[uninit] composes with #[entry] and #[exception]; it can be used on
safe local static mut variables.

#[entry]
fn main() -> ! {
    #[uninit]
    static mut KEY: [u32; 8] = {};

    // ..

    // the name of this MaybeUninit method hasn't been decided but it writes
    // a value into the MaybeUninit and then returns a reference to the written
    // value
    let key: &'static mut [u32; 8] = KEY.insert(/* runtime value */);

    // ..
}

Implementation

Before this can be implemented, MaybeUninit must first land in the core
library. This feature will live behind a "nightly" (Cargo) feature until
MaybeUninit and its const constructor are stabilized.

Alternative syntax

Option A

#[uninit]
static mut FOO: u32 = ();

Option B

#[uninit]
static mut FOO: u32 = ..;

We can't omit the RHS of static mut because then the code won't parse and the
compiler will never reach the macro expansion phase.

Drawbacks

This is not a perfect solution.

For example, a heapless vector contains an uninitialized buffer when it's
constructed using the new method but it also contains a length field that must
be initialized to zero. Applying #[uninit] to such data structure means that
the vector will have to be initialized (assign 0 to its length field) at
runtime, which is not ergonomic. Not using #[uninit] means that the vector
length and its buffer will be zeroed, which is wasteful.

It's not possible to have partial initialization of static variables so this
is as good as it gets.

@therealprof
Copy link
Contributor

I guess I'm partial on this one as written down. On one hand it sounds like a useful feature to have, even if it just improves the ergonomics of static variables which is quite a boon. OTOH I don't buy into the expense argument of the zeroing; if wasting a few cycles once during initialisation is the biggest problem of your design you can be very proud of yourself... I'd rather highlight the ergonomics benefit than the slight performance improvement and by doing that your drawback also vanishes into thin air because it's actually a pro that you cannot abuse this to do partial initialisation.

Regarding syntax, I'd very much prefer the unit type:
static mut FOO: u32 = ();
over the other variants but I think I would like the never type even more (if that is a possibility!):
static mut FOO: u32 = !;

@korken89
Copy link
Contributor

korken89 commented Sep 9, 2018

+1 on @therealprof for me. The zeroing does not convince me, though ergonomics is indeed a good plus.

japaric referenced this issue in rust-embedded/cortex-m-rt Jun 19, 2019
that contains static variables that will not be initialized by the runtime

this implements only the linker section described in RFC #116 so that downstream
libraries / framework can leverage it without (a) forking this crate or (b)
requiring the end user to pass additional linker scripts to the linker when
building their crate.

This commit doesn't add the user interface described in RFC #116; instead it
documents how to use this linker section in the advanced section of the crate
level documentation
bors bot referenced this issue in rust-embedded/cortex-m-rt Jun 19, 2019
192: add a .uninit section r=therealprof a=japaric

that contains static variables that will not be initialized by the runtime

this implements only the linker section described in RFC #116 so that downstream
libraries / framework can leverage it without (a) forking this crate or (b)
requiring the end user to pass additional linker scripts to the linker when
building their crate.

This commit doesn't add the user interface described in RFC #116; instead it
documents how to use this linker section in the advanced section of the crate
level documentation

Co-authored-by: Jorge Aparicio <jorge@japaric.io>
@korken89
Copy link
Contributor

Was this RFC accepted or not? Churning out the implementation for this should go fast.

@korken89
Copy link
Contributor

I needed to brush up my proc macro knowledge, so I made a simple impl to do the expansion and type signature checking: https://github.com/rust-embedded/cortex-m-rt/tree/uninit_impl

@jonas-schievink
Copy link
Contributor

I'm not terribly excited about having yet another way in which the user-specified type gets changed to something else by a macro. IMO we should require an #[uninit] static to have type MaybeUninit<T>.

Also, the MaybeUninit::write API is still unstable, so at the moment there is no ergonomics win from this. In fact, it would be impossible to use this API from fully safe code.

@adamgreig adamgreig transferred this issue from rust-embedded/cortex-m-rt Jan 23, 2022
@cher-nov
Copy link

cher-nov commented Aug 3, 2022

OTOH I don't buy into the expense argument of the zeroing; if wasting a few cycles once during initialisation is the biggest problem of your design you can be very proud of yourself...

+1 on @therealprof for me. The zeroing does not convince me

Maybe you will find this article interesting:
https://www.researchgate.net/publication/221320635_Why_Nothing_Matters_The_Impact_of_Zeroing
http://users.cecs.anu.edu.au/~steveb/pubs/papers/zero-oopsla-2011.pdf (mirror)

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

5 participants