Skip to content

Commit 68d7f83

Browse files
committed
macros: Don't use Option in singleton!()
As detailed in #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`...
1 parent 19125ac commit 68d7f83

File tree

1 file changed

+9
-8
lines changed

1 file changed

+9
-8
lines changed

src/macros.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,23 +63,24 @@ macro_rules! iprintln {
6363
macro_rules! singleton {
6464
($name:ident: $ty:ty = $expr:expr) => {
6565
$crate::interrupt::free(|_| {
66-
static mut $name: 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);
6771

6872
#[allow(unsafe_code)]
69-
let used = unsafe { $name.is_some() };
73+
let used = unsafe { $name.1 };
7074
if used {
7175
None
7276
} else {
7377
let expr = $expr;
7478

7579
#[allow(unsafe_code)]
7680
unsafe {
77-
$name = Some(expr)
78-
}
79-
80-
#[allow(unsafe_code)]
81-
unsafe {
82-
$name.as_mut()
81+
$name.1 = true;
82+
$name.0 = ::core::mem::MaybeUninit::new(expr);
83+
Some(&mut *$name.0.as_mut_ptr())
8384
}
8485
}
8586
})

0 commit comments

Comments
 (0)