Skip to content

fix(no_std): the census counters no longer cost the crate bare metal (Cortex-M4F, RV32) - #13

Merged
Ttimmahlax merged 1 commit into
mainfrom
fix/bare-metal-census-atomics
Sep 9, 2026
Merged

fix(no_std): the census counters no longer cost the crate bare metal (Cortex-M4F, RV32)#13
Ttimmahlax merged 1 commit into
mainfrom
fix/bare-metal-census-atomics

Conversation

@Ttimmahlax

Copy link
Copy Markdown
Contributor

Implements B1 of rusty_RTOS/docs/plans/build-me-bare.md: the census
counters were costing rusty_zstd both Kairos bare-metal targets.

The failure

cargo check -p rusty_zstd --no-default-features --features alloc \
  --target thumbv7em-none-eabihf          # 204 errors
  --target riscv32imac-unknown-none-elf   # the same

Every one was cannot find AtomicU64 in atomic, and every one was an
instrument
— reload counts, decode band histograms, kernel reach, walk exits,
copy bytes. Not one was a line of codec. Neither part has 64-bit atomics.

The plan estimated eight statics, reading a capped rustc report. The real count
is 427 fully-qualified uses plus six imports across sixteen files, which is
why this is one seam rather than a hundred #[cfg]s.

The fix

census64 is the single seam:

  • Where 64-bit atomics exist: pub use core::sync::atomic::AtomicU64. The
    same type, not a wrapper, so the public statics keep their published type and
    codegen cannot change.
  • Where they do not: a zero-sized stub. The statics leave BSS entirely and
    every fetch_add folds away.

Not portable-atomic, deliberately

It would keep the census readable on those parts and it is the other honest
answer. But on a core with no 64-bit atomic instruction it needs a
critical-section implementation, and a library that enables that feature
conscripts every downstream firmware's interrupt policy so that a diagnostic
counter can increment. The seam is one type wide, so a firmware that does want
the count can supply portable_atomic::AtomicU64 itself.

Zero costs nothing here, and says so

A counter that silently reads zero is the exact "stale instrument" trap this
crate's own rules warn about, so it is published rather than buried:
census64::CENSUS_LIVE is false on such a build. Zero there means "not
measurable on this target", never "measured zero".

The reverse mistake would be far worse — a stub selected on a hosted target
would make every count-based verdict fiction while every gate still passed — so
it is gated twice: a compile-time size check, and a unit test that asserts on
behaviour rather than on the constant (it counts to 42 and reads it back).

Evidence the shipping target is untouched

  • The asm board is identical in all thirty-two columns to the pre-change
    build, across every shipping kernel, fill and finder.
  • Identity gates unchanged: GOLD 2F6594F7EEDBD12B / 59,680,638 and LDM
    57BE83EA4E1199E8 / 57,796,847.
  • Tests, clippy -D warnings, fmt --check, the ASCII gate, wasm32 std and
    alloc, the profile feature, and the bench crate all clean. Cross-checked
    against x86_64-unknown-linux-gnu and aarch64-apple-darwin too.

Also here

  • CI gains both bare-metal rungs in the job that already guards the portable
    configurations, so this cannot rot.
  • The README's platform table gains both rows, with the CENSUS_LIVE caveat
    stated rather than implied.

Closing the loop upstream: once this releases, rusty_RTOS's gate-zstd can
move to the new pin and B1's kill test (cargo check -p gate-zstd --target thumbv7em-none-eabihf, exit 0 at a crates.io pin) passes.

🤖 Generated with Claude Code

`cargo check -p rusty_zstd --no-default-features --features alloc` failed with
204 errors on thumbv7em-none-eabihf (Cortex-M4F) and the same on
riscv32imac-unknown-none-elf. Every one was `cannot find AtomicU64 in atomic`,
and every one was an INSTRUMENT -- reload counts, decode band histograms,
kernel reach, walk exits, copy bytes. Not one was a line of codec. Neither
part has 64-bit atomics.

Found by rusty_RTOS's house gate, which wants the codec on those parts for
on-chip OTA payloads and trace capture. Its plan estimated eight statics from
a capped rustc report; the real count is 427 fully-qualified uses plus six
imports across sixteen files, which is why this is one seam rather than a
hundred `#[cfg]`s.

`census64` is that seam:

- Where 64-bit atomics EXIST it is `pub use core::sync::atomic::AtomicU64` --
  the same type, not a wrapper -- so the public statics keep their published
  type and codegen cannot change. Proved rather than asserted: the asm board
  over every shipping kernel, fill and finder is identical in all thirty-two
  columns to the pre-change build, and the identity gates still read GOLD
  2F6594F7EEDBD12B / 59,680,638 and LDM 57BE83EA4E1199E8 / 57,796,847.
- Where they do not, it is a zero-sized stub: the statics leave BSS and every
  fetch_add folds away.

Not `portable-atomic`, deliberately. It would keep the census readable there
and it is the other honest answer, but on a core with no 64-bit atomic
instruction it needs a critical-section implementation, and a library that
enables that conscripts every downstream firmware's interrupt policy so a
diagnostic counter can increment. The seam is one type wide, so a firmware
that wants the count can supply portable_atomic::AtomicU64 itself.

A zero there means "not measurable on this target", never "measured zero" --
the exact trap this crate's own rules warn about -- so it is published, not
buried: census64::CENSUS_LIVE is false on such a build. The REVERSE mistake
would be far worse, because a stub selected on a hosted target would make
every count-based verdict fiction while every gate still passed. Two guards:
a compile-time size check, and a unit test that asserts on BEHAVIOUR rather
than on the constant (it counts to 42 and reads it back).

CI gains both rungs in the job that already guards the portable
configurations, and the README's platform table gains both rows.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Ttimmahlax
Ttimmahlax merged commit 3c0e575 into main Sep 9, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants