Skip to content

Commit 6072a64

Browse files
committed
Load string indices with inline asm to save space.
1 parent 42f4012 commit 6072a64

File tree

3 files changed

+59
-3
lines changed

3 files changed

+59
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ We have several packages which live in this repository. Changes are tracked sepa
5252

5353
### [defmt-next]
5454

55+
- [#879]: Load string indices with inline asm to save space.
5556
* [#974]: Ensure typechecking is still performed on disabled log statement.
5657
* [#960]: Fix `Format` not accepting multiple helper attribute instances
5758
* [#937]: add support for `#[defmt(transparent)]` on `Format` derive

macros/build.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
11
fn main() {
22
println!("cargo:rerun-if-env-changed=DEFMT_LOG");
3+
4+
// Check rustc version
5+
let rustc = std::env::var("RUSTC").unwrap_or_else(|_| "rustc".to_string());
6+
let output = std::process::Command::new(rustc)
7+
.arg("--version")
8+
.output()
9+
.expect("failed to execute rustc");
10+
let version_str = String::from_utf8_lossy(&output.stdout);
11+
// rustc 1.91.0-beta.1 (1bffa2300 2025-09-15)
12+
let is_ge_1_91 = version_str
13+
.split_whitespace()
14+
.nth(1)
15+
.and_then(|v| {
16+
let mut parts = v.split('.');
17+
let major = parts.next()?.parse::<u32>().ok()?;
18+
let minor = parts.next()?.parse::<u32>().ok()?;
19+
Some((major, minor))
20+
})
21+
.map(|(major, minor)| major > 1 || (major == 1 && minor >= 91))
22+
.unwrap_or(false);
23+
if is_ge_1_91 {
24+
println!("cargo:rustc-cfg=rustc_ge_1_91");
25+
}
26+
println!("cargo:rustc-check-cfg=cfg(rustc_ge_1_91)");
327
}

macros/src/construct.rs

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,41 @@ pub(crate) fn interned_string(
4646
quote!({ #defmt_path::export::fetch_add_string_index() })
4747
} else {
4848
let var_item = static_variable(&var_name, string, tag, prefix);
49-
quote!({
50-
#var_item
49+
50+
// defmt string indices are 16 bits, which can be loaded with a single `movw`.
51+
// However, the compiler doesn't know that, so it generates `movw+movt` or `ldr rX, [pc, #offs]`
52+
// because it sees we're loading an address of a symbol, which could be any 32bit value.
53+
// This wastes space, so we load the value with asm manually to avoid this.
54+
let val_arm_optimized = quote!(
55+
let res: u16;
56+
unsafe { ::core::arch::asm!(
57+
"movw {res}, #:lower16:{y}",
58+
res = lateout(reg) res,
59+
y = sym #var_name,
60+
options(pure, nomem, nostack, preserves_flags)
61+
)};
62+
resss
63+
);
64+
let val_standard = quote!(
5165
&#var_name as *const u8 as u16
52-
})
66+
);
67+
68+
// using symbols with quotes in `asm!(sym)` only works in Rust 1.91+
69+
if cfg!(rustc_ge_1_91) {
70+
quote!({
71+
#var_item
72+
73+
#[cfg(target_arch = "arm")]
74+
{ #val_arm_optimized }
75+
#[cfg(not(target_arch = "arm"))]
76+
{ #val_standard }
77+
})
78+
} else {
79+
quote!({
80+
#var_item
81+
#val_standard
82+
})
83+
}
5384
};
5485

5586
quote!({

0 commit comments

Comments
 (0)