diff --git a/CHANGELOG.md b/CHANGELOG.md index cb1b58d5..662226a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,6 +66,7 @@ We have several packages which live in this repository. Changes are tracked sepa * [#972] Fix logic bug in env_filter * [#1007] `defmt`: impl `Format` for `core::fmt::Error` * [#983] Add `Format` implementation for core::num::Wrapping +* [#879] Load string indices with inline asm to save space. ### [defmt-v1.0.1] (2025-04-01) diff --git a/macros/build.rs b/macros/build.rs index 772d48b4..63953668 100644 --- a/macros/build.rs +++ b/macros/build.rs @@ -1,3 +1,27 @@ fn main() { println!("cargo:rerun-if-env-changed=DEFMT_LOG"); + + // Check rustc version + let rustc = std::env::var("RUSTC").unwrap_or_else(|_| "rustc".to_string()); + let output = std::process::Command::new(rustc) + .arg("--version") + .output() + .expect("failed to execute rustc"); + let version_str = String::from_utf8_lossy(&output.stdout); + // rustc 1.91.0-beta.1 (1bffa2300 2025-09-15) + let is_ge_1_91 = version_str + .split_whitespace() + .nth(1) + .and_then(|v| { + let mut parts = v.split('.'); + let major = parts.next()?.parse::().ok()?; + let minor = parts.next()?.parse::().ok()?; + Some((major, minor)) + }) + .map(|(major, minor)| major > 1 || (major == 1 && minor >= 91)) + .unwrap_or(false); + if is_ge_1_91 { + println!("cargo:rustc-cfg=rustc_ge_1_91"); + } + println!("cargo:rustc-check-cfg=cfg(rustc_ge_1_91)"); } diff --git a/macros/src/construct.rs b/macros/src/construct.rs index 3548338f..8dc49ba8 100644 --- a/macros/src/construct.rs +++ b/macros/src/construct.rs @@ -46,10 +46,41 @@ pub(crate) fn interned_string( quote!({ #defmt_path::export::fetch_add_string_index() }) } else { let var_item = static_variable(&var_name, string, tag, prefix); - quote!({ - #var_item + + // defmt string indices are 16 bits, which can be loaded with a single `movw`. + // However, the compiler doesn't know that, so it generates `movw+movt` or `ldr rX, [pc, #offs]` + // because it sees we're loading an address of a symbol, which could be any 32bit value. + // This wastes space, so we load the value with asm manually to avoid this. + let val_arm_optimized = quote!( + let res: u16; + unsafe { ::core::arch::asm!( + "movw {res}, #:lower16:{y}", + res = lateout(reg) res, + y = sym #var_name, + options(pure, nomem, nostack, preserves_flags) + )}; + res + ); + let val_standard = quote!( &#var_name as *const u8 as u16 - }) + ); + + // using symbols with quotes in `asm!(sym)` only works in Rust 1.91+ + if cfg!(rustc_ge_1_91) { + quote!({ + #var_item + + #[cfg(target_arch = "arm")] + { #val_arm_optimized } + #[cfg(not(target_arch = "arm"))] + { #val_standard } + }) + } else { + quote!({ + #var_item + #val_standard + }) + } }; quote!({