Skip to content

New static mutable storage for the entry function #616

@jonathanpallant

Description

@jonathanpallant

We carefully ensure the #[entry] function cannot be called other than after a reset (and after the start-up code has initialised everything).

Currently we use this knowledge to 'safely' replace static mut FOO: T = T::new() with code that uses an unsafe block to create &mut T reference to the static, which is passed in as an argument.

That is:

#[cortex_m_rt::entry]
fn main() -> ! {
    static mut FOO: u32 = 123;

    loop {}
}

becomes:

#[doc(hidden)]
#[export_name = "main"]
pub unsafe extern "C" fn __cortex_m_rt_main_trampoline() {
    #[allow(static_mut_refs)]
    __cortex_m_rt_main({
        static mut FOO: u32 = 123;
        unsafe { &mut FOO }
    })
}
fn __cortex_m_rt_main(#[allow(non_snake_case)] FOO: &'static mut u32) -> ! {
    loop {}
}

It is widely accepted that this kind of sleight of hand is not good. However, RTIC shows us a syntax that could work:

#[task(local = [state: u32 = 0])]
async fn foo(c: foo::Context) {
    let old_state: u32 = *c.local.state;
    *c.local.state += 1;
}

So, what if we wrote a new macro, called entry_with_context:

#[cortex_m_rt::entry_with_context(context = [state: u32 = 0])]
fn main(c: main::Context) -> ! {
    let old_state: u32 = *c.state;
    *c.state += 1;

    loop {}
}

The advantage here is that is clear some 'magic' is happening to create a static resource and pass it to the main function.

It would expand to something like:

mod main {
    pub(crate) struct Context {
        state: &mut u32
    }
}

#[doc(hidden)]
#[export_name = "main"]
pub unsafe extern "C" fn __cortex_m_rt_main_trampoline() {
    static STATE: RacyCell<u32> = RacyCell::new(123);
    __cortex_m_rt_main(main::Context {
        state: unsafe { STATE.get_mut_ref() },
    })
}

fn main(c: main::Context) -> ! {
    let old_state: u32 = *c.state;
    *c.state += 1;

    loop {}
}

We don't even need to hide the actual fn main() function anymore, because you cannot call it without creating the context object.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions