Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>
* [#879] Load string indices with inline asm to save space.

### [defmt-v1.0.1] (2025-04-01)

Expand Down
24 changes: 24 additions & 0 deletions macros/build.rs
Original file line number Diff line number Diff line change
@@ -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::<u32>().ok()?;
let minor = parts.next()?.parse::<u32>().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)");
}
37 changes: 34 additions & 3 deletions macros/src/construct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!(
Copy link
Contributor

Choose a reason for hiding this comment

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

Unfortunately this assembly code doesn't work on Arm architectures prior to ARMv6T2 (i.e. ARMv4T, ARMv5TE and ARMv6). See Godbolt.

You might need to use https://docs.rs/arm-targets to work out which Arm core is being targeted.

Copy link
Contributor

Choose a reason for hiding this comment

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

Or we could say "Yeah, but those targets have no users", merge it anyway and worry about fixing it if someone actually complains.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh no. It doesn't work on thumbv6m-none-eabi either....

https://rust.godbolt.org/z/c9jb9TK36

"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!({
Expand Down