tsb: third size pass — restructure to the oracle's shape
The second pass concluded the 168 B tricks->asm gap was per-call ABI
cost. Most of it was structure. Rebuilt around the oracle's own shape —
argless noinline primitives over a whole-loader call-saved register
protocol (g_addr in Y, count r16, window r7, direction latch r6), a
top-down erase_below whose loop tests against zero and hands callers
g_addr = 0 for free, bounded rx everywhere (a silent host unwinds to
the app from any state, as the oracle does), and a named tsb_app entry
that --pmem-wrap-around=32k relaxes to the wrapped rjmp:
tsb_asm 510 B in the 512 B section (oracle: 500), C++ except rx
and the page-store loop — the two routines whose remaining
cost is the calling convention itself (~30 asm lines, was
~280)
tsb_tricks 526 B, no assembly at all (was 666)
tsb_pure 836 B, still one readable function per command (was 842)
Every g_* update placement works around a GCC 16.1 wrong-code bug
(stores into global register variables deleted when only callees read
them — repro and rules in libavr dev/lessons.md). Also fixes two
latent hardware bugs all earlier tiers carried, masked by simavr's
zeroed register file: the crt-less entries never established
__zero_reg__ = 0, and the direction latch was read before written —
power-on registers are undefined.
All tiers full oracle feature parity, protocol tests green in both
libavr modes, .text byte-identical across modes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -44,24 +44,29 @@ endif()
|
||||
# its size; BOOTRST vectors a reset to its base) with -nostartfiles — a polled
|
||||
# loader has no use for the crt or the vector table. The naked entry sits in
|
||||
# .vectors, laid first, and runs. The boot base is FLASHEND+1 minus the section
|
||||
# size; the linker section-start and the source's boot_bytes agree.
|
||||
# size; the linker section-start and the source's boot_bytes agree. tsb_app is
|
||||
# the application's reset vector, pinned to 0 here so the loaders jump to a
|
||||
# named function; --pmem-wrap-around lets relaxation turn that absolute jump
|
||||
# into the wrapped rjmp AVR's modulo-flash PC actually executes.
|
||||
# All three implement the full oracle feature set (see oracle/README.md):
|
||||
# watchdog bail, one-wire half-duplex, config-page activation timeout, password
|
||||
# gate, emergency erase, config/flash/EEPROM read-write. They differ only in how,
|
||||
# and the size gradient is the cost of that "how" — see dev/lessons.md.
|
||||
# tsb_asm — dense inline asm for the loader core, libavr for every geometry/
|
||||
# baud/info constant and the stack bring-up: 498 B in the 512 B
|
||||
# section, beating the hand-written oracle (500 B) at its own
|
||||
# feature set. The core is asm because C++ cannot reach it under
|
||||
# 512 (the tricks tier is the proof).
|
||||
# tsb_tricks — compiler trickery, no asm: the hot page pointer is walked in Y
|
||||
# (adiw), flash/EEPROM paths share one runtime-flag body, every
|
||||
# single-call handler is always_inline'd into the no-prologue
|
||||
# reset entry, pages stream straight to SPM/EEPROM, and the
|
||||
# bring-up is the two reset-non-default registers only. 666 B in
|
||||
# the 1 KB section (BOOTSZ=10).
|
||||
# tsb_asm — the tricks tier's C++ with exactly two routines in asm (the
|
||||
# bounded rx and the page-store loop — the two whose remaining
|
||||
# cost is the C ABI itself): 510 B in the 512 B section the
|
||||
# hand-written 500 B oracle occupies. Everything else, from
|
||||
# bring-up to dispatch, is C++ on libavr.
|
||||
# tsb_tricks — no asm at all: the whole-loader register allocation lives in
|
||||
# global register variables (Y walks the page pointer), every
|
||||
# helper is a tiny noinline primitive placed by the
|
||||
# global-register store rules, pages stream straight to
|
||||
# SPM/EEPROM, and the bring-up is the two reset-non-default
|
||||
# registers only. 526 B in the 1 KB section (BOOTSZ=10) — 14
|
||||
# over the oracle's section, from 168 over at this tier's first
|
||||
# floor.
|
||||
# tsb_pure — pure idiomatic libavr, one function per command, TU-local
|
||||
# (internal linkage), streaming (no SRAM page buffer): 842 B in
|
||||
# (internal linkage), streaming (no SRAM page buffer): 836 B in
|
||||
# the 1 KB section.
|
||||
#
|
||||
# add_tsb_variant(<name> <boot-section-bytes>)
|
||||
@@ -70,7 +75,8 @@ function(add_tsb_variant name bytes)
|
||||
math(EXPR base_hex "${base_dec}" OUTPUT_FORMAT HEXADECIMAL)
|
||||
add_executable(${name} tsb/${name}.cpp)
|
||||
target_link_libraries(${name} PRIVATE libavr)
|
||||
target_link_options(${name} PRIVATE -nostartfiles -Wl,--section-start=.text=${base_hex})
|
||||
target_link_options(${name} PRIVATE -nostartfiles -Wl,--section-start=.text=${base_hex}
|
||||
-Wl,--defsym=tsb_app=0 -Wl,--pmem-wrap-around=32k)
|
||||
add_custom_command(TARGET ${name} POST_BUILD COMMAND ${CMAKE_SIZE} $<TARGET_FILE:${name}>)
|
||||
if(PROJECT_IS_TOP_LEVEL)
|
||||
add_test(NAME ${name}.size
|
||||
|
||||
Reference in New Issue
Block a user