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

Improve singleton!() macro #380

Merged
merged 3 commits into from
Jan 4, 2022
Merged

Conversation

Rahix
Copy link
Contributor

@Rahix Rahix commented Jan 3, 2022

This PR addresses two shortcomings of the cortex_m::singleton!() macro, which I raised in #364. For review, I think it is best to look at the two commits implementing these changes individually.

I think this changeset should also be backported to 0.7.x where it applies cleanly and which is also the place where I tested it.

1. Fix singleton!() sometimes ending up in .data

The static is always initialized to a "zero" value with Option::None which means it should end up in .bss. However, if the enclosed type has a niche, Option::None can become a non-zero bitpattern which moves the whole singleton from .bss to .data. This is especially problematic when storing large buffers in the singleton!() as this starts eating lots of flash space unnecessarily.

To prevent this, I switched to using an explicit boolean flag instead. This is not quite as nice but at least there is no chance for the singleton!() to end up in .data...

For reference and as an example, the faulty behavior can be triggered with

cortex_m::singleton!(: Option<u32> = None)

(the inner option has a non-zero niche which the outer option will occupy)

2. Allow naming the static

Due to the static always being named VAR right now, all singleton!() instances end up having non-descriptive symbol names like __cortex_m_rt_main::{{closure}}::VAR which makes them hard to tell apart in a debugger or when looking at an objdump.

I added the ability to set an explicit name which end up becoming part of the symbol name. This does not affect Rust code at all - the new name is not visible anywhere. It works like this:

let value = singleton!(FOO_BUFFER: [u8; 1024] = [0u8; 1024]);

Of course the old syntax also still works and keeps the old behavior of calling the static VAR.

Fixes #364.

Right now, the `singleton!()` macro always creates a static named `VAR`.
This is annoying when digging through objdump output or digging into
memory with a debugger because it is hard to see what singleton you're
looking at when they are all called `<...>::{{closure}}::VAR`.

To make it a bit nicer, allow setting the name of the generated static
to some meaningful identifier which can then be cross-referenced in
debugger output, for example with

	singleton!(FOO_BUFFER: [u8; 1024] = [0u8; 1024]);

There is no other side-effects to this change - the identifier is never
visible to other code because it is still contained in the closure of
the macro.
@Rahix Rahix requested a review from a team as a code owner January 3, 2022 11:33
@Rahix Rahix force-pushed the improve-singleton-macro branch from 4de6d6f to a02bce1 Compare January 3, 2022 11:42
@TDHolmes
Copy link
Contributor

TDHolmes commented Jan 3, 2022

Can you add a CHANGELOG entry?

thejpster
thejpster previously approved these changes Jan 3, 2022
Copy link
Contributor

@thejpster thejpster left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also looks good to me - just the needs the changelog update as noted.

@Rahix
Copy link
Contributor Author

Rahix commented Jan 4, 2022

Added CHANGELOG entries. Should I send a second PR against the v0.7.x branch or will someone else take care of backports?

@adamgreig
Copy link
Member

Thanks, this looks good to me too. Would you mind adding a comment inline to the macro definition briefly mentioning why it's a tuple with a bool instead of an Option, to help anyone coming along in the future with a great idea for an optimisation?

I can take care of the backport once this is merged. I'll probably give it a little while though in case anything else comes along that can be included in a new 0.7 release since we only just pushed 0.7.4 a couple of days ago.

Rahix added 2 commits January 4, 2022 17:40
As detailed in rust-embedded#364, niche optimization of the
`Option` used in the `singleton!()` macro can lead to the initial value
of the static to contain non-zero bits.  This in turn leads to the whole
static being moved from `.bss` to `.data` which means it eats up flash
space for no reason.  Especially if the singleton stores a particularly
large type, this can be quite problematic.

Prevent this by using an explicit boolean flag instead of the `Option`
type.  This is not quite as nice but at least there is no chance for the
`singleton!()` to end up in `.data`...
@Rahix Rahix force-pushed the improve-singleton-macro branch from ea2bd25 to 632af2e Compare January 4, 2022 16:40
@Rahix
Copy link
Contributor Author

Rahix commented Jan 4, 2022

@adamgreig: Good idea, done.

Copy link
Member

@adamgreig adamgreig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

bors merge

@bors
Copy link
Contributor

bors bot commented Jan 4, 2022

Build succeeded:

@bors bors bot merged commit e0b93a0 into rust-embedded:master Jan 4, 2022
@Rahix Rahix deleted the improve-singleton-macro branch January 4, 2022 17:04
adamgreig pushed a commit that referenced this pull request May 15, 2022
380: Improve singleton!() macro r=adamgreig a=Rahix

This PR addresses two shortcomings of the `cortex_m::singleton!()` macro, which I raised in #364.  For review, I think it is best to look at the two commits implementing these changes individually.

I think this changeset should also be backported to `0.7.x` where it applies cleanly and which is also the place where I tested it.

The static is always initialized to a "zero" value with `Option::None` which means it should end up in `.bss`.  However, if the enclosed type has a niche, `Option::None` can become a non-zero bitpattern which moves the whole singleton from `.bss` to `.data`.  This is especially problematic when storing large buffers in the `singleton!()` as this starts eating lots of flash space unnecessarily.

To prevent this, I switched to using an explicit boolean flag instead.  This is not quite as nice but at least there is no chance for the `singleton!()` to end up in `.data`...

For reference and as an example, the faulty behavior can be triggered with

```rust
cortex_m::singleton!(: Option<u32> = None)
```

(the inner option has a non-zero niche which the outer option will occupy)

Due to the static always being named `VAR` right now, all `singleton!()` instances end up having non-descriptive symbol names like `__cortex_m_rt_main::{{closure}}::VAR` which makes them hard to tell apart in a debugger or when looking at an objdump.

I added the ability to set an explicit name which end up becoming part of the symbol name.  This does not affect Rust code at all - the new name is not visible anywhere.  It works like this:

```rust
let value = singleton!(FOO_BUFFER: [u8; 1024] = [0u8; 1024]);
```

Of course the old syntax also still works and keeps the old behavior of calling the static `VAR`.

Fixes #364.

Co-authored-by: Rahix <[email protected]>
adamgreig pushed a commit that referenced this pull request May 15, 2022
380: Improve singleton!() macro r=adamgreig a=Rahix

This PR addresses two shortcomings of the `cortex_m::singleton!()` macro, which I raised in #364.  For review, I think it is best to look at the two commits implementing these changes individually.

I think this changeset should also be backported to `0.7.x` where it applies cleanly and which is also the place where I tested it.

The static is always initialized to a "zero" value with `Option::None` which means it should end up in `.bss`.  However, if the enclosed type has a niche, `Option::None` can become a non-zero bitpattern which moves the whole singleton from `.bss` to `.data`.  This is especially problematic when storing large buffers in the `singleton!()` as this starts eating lots of flash space unnecessarily.

To prevent this, I switched to using an explicit boolean flag instead.  This is not quite as nice but at least there is no chance for the `singleton!()` to end up in `.data`...

For reference and as an example, the faulty behavior can be triggered with

```rust
cortex_m::singleton!(: Option<u32> = None)
```

(the inner option has a non-zero niche which the outer option will occupy)

Due to the static always being named `VAR` right now, all `singleton!()` instances end up having non-descriptive symbol names like `__cortex_m_rt_main::{{closure}}::VAR` which makes them hard to tell apart in a debugger or when looking at an objdump.

I added the ability to set an explicit name which end up becoming part of the symbol name.  This does not affect Rust code at all - the new name is not visible anywhere.  It works like this:

```rust
let value = singleton!(FOO_BUFFER: [u8; 1024] = [0u8; 1024]);
```

Of course the old syntax also still works and keeps the old behavior of calling the static `VAR`.

Fixes #364.

Co-authored-by: Rahix <[email protected]>
@adamgreig adamgreig mentioned this pull request May 15, 2022
bors bot added a commit that referenced this pull request May 31, 2022
441: Prepare for v0.7.5 r=newAM a=adamgreig

Currently with cortex-m 0.7.4 it's not possible for stable Rust users to enable the inline-asm feature, even though their compiler might support it, because of the `![cfg_attr(feature = "inline-asm", feature(asm))]` line. I propose a new 0.7.5 release that removes this line, which means users on stable Rust >=1.59 could enable the previously nightly-only feature to get stable inline asm.

I wouldn't enable the feature by default, because that would be a significant MSRV bump, but at least this way more users could enable it before we release 0.8 with the revamped and inline-only asm.

I've also backported the bugfix from #380 which I don't believe is a breaking change.

I haven't had a chance to test this yet so it would be great if someone could try it out and just make sure the inline-asm feature does work before merging.

Any thoughts on anything else worth backporting from 0.8?

Co-authored-by: Adam Greig <[email protected]>
Co-authored-by: bors[bot] <26634292+bors[bot]@users.noreply.github.com>
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

Successfully merging this pull request may close these issues.

singleton!()'s use of Option can have nasty optimization results
4 participants