Skip to content

Commit ed00f50

Browse files
bors[bot]Rahix
authored andcommitted
Merge #380
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]>
1 parent 6a650bc commit ed00f50

File tree

2 files changed

+26
-10
lines changed

2 files changed

+26
-10
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1818
- The `inline-asm` feature no longer requires a nightly Rust compiler, but
1919
does require Rust 1.59 or above.
2020

21+
### Fixed
22+
- Fixed `singleton!()` statics sometimes ending up in `.data` instead of `.bss` (#364, #380).
23+
(Backported from upcoming 0.8 release).
24+
2125
## [v0.7.4] - 2021-12-31
2226

2327
### Added

src/macros.rs

+22-10
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,12 @@ macro_rules! iprintln {
3030
/// `None` variant the caller must ensure that the macro is called from a function that's executed
3131
/// at most once in the whole lifetime of the program.
3232
///
33-
/// # Note
33+
/// # Notes
3434
/// This macro is unsound on multi core systems.
3535
///
36+
/// For debuggability, you can set an explicit name for a singleton. This name only shows up the
37+
/// the debugger and is not referencable from other code. See example below.
38+
///
3639
/// # Example
3740
///
3841
/// ``` no_run
@@ -50,32 +53,41 @@ macro_rules! iprintln {
5053
/// fn alias() -> &'static mut bool {
5154
/// singleton!(: bool = false).unwrap()
5255
/// }
56+
///
57+
/// fn singleton_with_name() {
58+
/// // A name only for debugging purposes
59+
/// singleton!(FOO_BUFFER: [u8; 1024] = [0u8; 1024]);
60+
/// }
5361
/// ```
5462
#[macro_export]
5563
macro_rules! singleton {
56-
(: $ty:ty = $expr:expr) => {
64+
($name:ident: $ty:ty = $expr:expr) => {
5765
$crate::interrupt::free(|_| {
58-
static mut VAR: Option<$ty> = None;
66+
// this is a tuple of a MaybeUninit and a bool because using an Option here is
67+
// problematic: Due to niche-optimization, an Option could end up producing a non-zero
68+
// initializer value which would move the entire static from `.bss` into `.data`...
69+
static mut $name: (::core::mem::MaybeUninit<$ty>, bool) =
70+
(::core::mem::MaybeUninit::uninit(), false);
5971

6072
#[allow(unsafe_code)]
61-
let used = unsafe { VAR.is_some() };
73+
let used = unsafe { $name.1 };
6274
if used {
6375
None
6476
} else {
6577
let expr = $expr;
6678

6779
#[allow(unsafe_code)]
6880
unsafe {
69-
VAR = Some(expr)
70-
}
71-
72-
#[allow(unsafe_code)]
73-
unsafe {
74-
VAR.as_mut()
81+
$name.1 = true;
82+
$name.0 = ::core::mem::MaybeUninit::new(expr);
83+
Some(&mut *$name.0.as_mut_ptr())
7584
}
7685
}
7786
})
7887
};
88+
(: $ty:ty = $expr:expr) => {
89+
$crate::singleton!(VAR: $ty = $expr)
90+
};
7991
}
8092

8193
/// ``` compile_fail

0 commit comments

Comments
 (0)