Skip to content

savings floor: tft_graph 20,365 -> 20,135 B after #307's alarm-limits box, decoded, nothing leaked (issue #314) - #316

Merged
angeloINTJ merged 1 commit into
mainfrom
fix/savings-floor-tft-graph
Nov 4, 2026
Merged

angeloINTJ merged 1 commit into
mainfrom
fix/savings-floor-tft-graph

Conversation

@angeloINTJ

Copy link
Copy Markdown
Owner

The nightly savings job (.github/workflows/savings.yml, which no PR runs) has failed on main since 2026-11-01, always at 7b851812, on one line: tft_graph saves 20,135 B against a floor of 20,365 B. The job opened issue #314. This PR lowers that one floor to 20,135 B, after the drop was decoded symbol by symbol and the maintainer chose between the two ways out. No image changes.

What moved the number

Measured on this host per merge (pico_w_release and _sav_tft_graph_off, each tree's own tools/measure_savings.py, the linker fill read from the map). At 7b851812 all 16 features measure exactly what CI's run of 2026-11-03 printed, and the base .bin is main's byte for byte.

Tree used on / off fill on / off saved
ae87f6d6, the last green night 1,024,060 / 1,003,692 685 / 682 20,365
aaccce35, #307 (F3, the alarm-limits box) 1,024,164 / 1,004,020 687 / 682 20,139
96f503aa, #308 (the display alignment) 1,024,380 / 1,004,244 683 / 682 20,135
7b851812 (#311 and #312 change neither image) as 96f503aa 20,135

Nothing of the graph leaked: the code the switch removes whole grows 12 B, and the graph-named symbols left in the image without the graph are the same 4 (17 B) before and after. The drop is in always-on code the switch changes, -238 B net in 65 functions (45 shrink 328 B, 20 grow 90 B).

#307 put AlarmLimitsSnap _limitsSaved (68 B) in the middle of DisplayManager. Every member after it moves 68 B, and in the image without the graph those members sit near the start of the object, where the Cortex-M0+ builds an offset with movs + lsls or a literal word depending on its value. loopCore1( )'s most used member went from 0x4dc (no shifted 8-bit movs reaches it: 28 literal loads) to 0x520 = 164 << 3 (27 movs + lsls pairs, 4 B each). In the image with the graph the same member went from 0x1bcc to 0x1c14, both literal words, and the code barely moves.

Why the floor moves instead of the box

On scratch copies of 7b851812 (never committed), the box at the end of the class, or just before the UI driver's members, brings the saving back to 20,369 B and shrinks the graph-off image by 224 B. But the published images grow (.bin, B):

Image box at the end
pico_w_release +8
pico2_w_release +144 (+136 before the driver's members)
pico2_w_original +72
pico_w_test, pico_w_test_https, pico_wiretft_angulo +16
pico_w_asserts +8
pico_wiretft -32
pico_w_air, pico_w_alpha, pico_wire byte-equal (no TFT panel, no box)

The maintainer chose to lower the floor (2026-11-04): 224 B back for a variant no release publishes is not worth 8 B on pico_w_release and 144 B on pico2_w_release.

The change

  • tools/feature_savings.json: tft_graph 20,365 -> 20,135 B, written by measure_savings.py --only tft_graph --update (one line; RAM stays 2,232 B).
  • docs/analysis/ECONOMIA_DE_RECURSOS.md (Living): the header line and a new section with the tables, the decoding and what is not proven.
  • CHANGELOG.md, CHANGELOG.pt-BR.md: one paragraph at the end of Unreleased's Flash section, as v2.13.0's entry has for the previous drop.

Checks (host only, the shim's pio, under the build lock)

  • tools/measure_savings.py --check on e2fef36c before: rc 1, only tft_graph; after: rc 0, "16 recursos, nenhum abaixo do piso de economia." The other floors: 11 exact, tel_mqtt +2, web_history +4 and syslog +3 above, cli_full 4 B under (within the 8 B tolerance; new since 2026-10-30, not decoded).
  • tools/test_measure_savings.py: 76/76. tools/check_angulo.py: clean. git diff --check: clean.
  • ci_gates_local.py --raso: 76 of 77 steps pass; the one failure is the fuzz step, because this host's clang++ lacks libclang-cpp.so.18.1 (CI runs it); one step is CI only (the sanitizers).
  • Reviewed with a clean context: COM RESSALVAS. The change and the mechanism hold; the text findings are applied (the main one: a constructor counted twice by nm -S).
  • Not run on a board: no image changes.

Not done here

tools/feature_costs.json, the configurator's estimate, still holds the matrix of 2026-10-30, before #307, #308 and #311 changed the features' costs. AGENTS.md asks for measure_savings.py --matrix (70 builds) when a change moves a feature's size; that is its own change.

After the merge

Dispatch SIMUT savings watermark on main and close issue #314 by hand once it is green. This PR does not close it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Uy7K2qPR85KvekjCnFFFxZ

… box, decoded symbol by symbol, nothing leaked (issue #314)

The nightly savings job (.github/workflows/savings.yml) has failed on main
since 2026-11-01, always at 7b85181, on one line: tft_graph saves 20,135 B
against a floor of 20,365 B (230 B under, 222 B past FLASH_TOL). The job opened
issue #314 itself. No PR runs this gate, so the merges of 2026-10-31 did not
see it. Nothing of the graph leaked; this records the floor after the drop was
decoded and after the maintainer chose between the two ways out.

Measured per merge (pico_w_release and _sav_tft_graph_off, each tree's own
tools/measure_savings.py, the linker fill read from the map). At 7b85181 every
number of the 16 features equals the CI's run of 2026-11-03, and the base .bin
is main's byte for byte (sha256 a39bf356...):

  tree                          used on / off           fill on / off   saved
  ae87f6d (the last green)     1,024,060 / 1,003,692   685 / 682       20,365
  aaccce3 (#307, F3)           1,024,164 / 1,004,020   687 / 682       20,139
  96f503a (#308, alignment)    1,024,380 / 1,004,244   683 / 682       20,135
  7b85181 (#311 and #312 change neither image)  as 96f503a           20,135

#307 costs the image with the graph 104 B and the image without it 328 B; #308
takes 4 B more, within the tolerance. Per section and symbol (the map and nm -S,
ae87f6d -> aaccce3), all of it is in .text. The code the switch removes whole
grows 12 B in six functions (drawGraphDetailScreen 3,068 -> 3,076 B): the graph
still goes entirely. The always-on code the switch changes accounts for -238 B
net in 65 functions, 62 of DisplayManager and three of 4 B outside it
(AppManager::setup and two WebManager touch routes): 45 shrink by 328 B and 20
grow by 90 B. The largest are loopCore1 -56, handleTouch -28, pinSubmit -16,
drawAlarmEdit -16, showSettingsPassword -12 and drawMelodySelect -12; 29 move
-4 B and 16 move +4 B.

Why. #307 put AlarmLimitsSnap _limitsSaved (68 B) in the middle of
DisplayManager (the TFT block of src/DisplayManager.h), before _sharedState, so
every member after it moves 68 B. The graph package (5,872 B) also comes after
the box, and it is what tells the two images apart: with the switch off, the
members after it sit near the start of the object, where the Cortex-M0+ builds
an offset with movs + lsls or with a literal word depending on its value, and
moving 68 B changes that choice in dozens of functions. loopCore1's most used
member shows how. In the graph-off image it was at 0x4dc (1,244), which no
shifted 8-bit movs reaches: 28 literal loads and 4 pool words. #307 moved it to
0x520 (1,312 = 164 << 3), which the compiler builds with movs #164 and lsls #3
and no literal: the 28 loads became 27 pairs of 4 B each. Over the whole
function, 23 fewer literal loads and 24 more pairs (5,260 -> 5,300 B). In the
image with the graph the same member went from 0x1bcc to 0x1c14, both literal
words: there loopCore1 has 4 fewer literal loads and shrank 16 B (5,696 ->
5,680). The graph package between the two images is 5,872 B before #307
(0x1bcc - 0x4dc) and 5,876 B after (0x1c14 - 0x520). It is the effect
tools/arduino_pico_overrides/README.md measured on the web server (2026-10-14).

The experiment (scratch copies of 7b85181, never committed): the box moved to
the end of the class, or to just before the UI driver's members, brings the
saving back to 20,369 B and shrinks the graph-off image by 224 B, but the
published images grow (.bin, B):

  pico_w_release +8, pico2_w_release +144 (+136 before the driver's members),
  pico2_w_original +72, pico_w_test +16, pico_w_test_https +16,
  pico_wiretft_angulo +16, pico_w_asserts +8, pico_wiretft -32;
  pico_w_air, pico_w_alpha and pico_wire byte-equal (no TFT panel, no box).

On pico2_w_release the box at the end changes 31 functions, +144 B net, exactly
the .bin's growth (uiPass +32, handleTouch +26, uaMontarMenus +24, the
constructor +16; redrawTopPanel -14); on pico_w_release it changes 53 input
sections, +10 B of code and -2 B of linker fill, the image's 8 B.

The main column of that table equals the "bin" of tools/flash_budget.json for
every image that records one. The maintainer chose to lower the floor
(2026-11-04): 224 B back for a variant no release publishes is not worth 8 B on
pico_w_release and 144 B on pico2_w_release, the two published images with a
panel.

Floors, tools/measure_savings.py --check on e2fef36 before this commit:
tft_graph 20,135 < 20,365 (rc 1, the only failing line); 11 features measure
their floor exactly; tel_mqtt +2, web_history +4 and syslog +3 measure above it,
as on 2026-10-30; cli_full measures 4 B under it, within the tolerance (it
measured its floor exactly on 2026-10-30; the 4 B are new and were not decoded,
and 5 B more would fail the night). Only the
floor that must change changes: --only tft_graph --update rewrote one line of
tools/feature_savings.json. RAM saved stays 2,232 B.

docs/analysis/ECONOMIA_DE_RECURSOS.md (Living): the header line and a new
section, "O piso do tft_graph depois da caixa dos limites de alarme
(2026-11-04)", with the tables above and what is not proven: no member-by-member
map of DisplayManager (the C++ is built without -g), and the instruction count
made only in loopCore1 (handleTouch's mnemonics show the same swap, 31 fewer
ldr and 24 more movs and lsls each; the other 63 functions were not decoded).
CHANGELOG.md and CHANGELOG.pt-BR.md: one paragraph at the end of Unreleased's
Flash section, as the previous floor drop has in v2.13.0's.

Not done here: tools/feature_costs.json, the configurator's estimate, still
holds the matrix of 2026-10-30, before #307, #308 and #311 changed the
features' costs; AGENTS.md asks for measure_savings.py --matrix (70 builds)
when a change moves a feature's size. That is its own change.

Reviewed with a clean context (COM RESSALVAS): the floor change and the
mechanism hold. Of its eight findings, six were about the text and are applied
here, the main one a constructor counted twice by nm -S (its C1 and C2 symbols
are one body); the CHANGELOG paragraph is the seventh; the eighth asked whether
the night of 2026-11-04 also failed, and that run had not started when this was
written (the last one is 2026-11-03, 13:27 UTC).

Checks, host only, the shim's pio, under the build lock:
  tools/measure_savings.py --check before: rc 1, only tft_graph;
    after: rc 0, "16 recursos, nenhum abaixo do piso de economia."
  tools/test_measure_savings.py: 76/76 ok
  tools/check_angulo.py: 93 documents without emoji; git diff --check clean
  ci_gates_local.py --raso: 76 of 77 steps pass; the one failure is the fuzz
    step (this host's clang++ lacks libclang-cpp.so.18.1, as on every PR since
    2026-10-23; CI runs it); one step is CI only (sanitizers)
  src/ untouched: no image changes. Not run on a board (nothing to run).

Issue #314 stays open until the nightly job, dispatched on main after the
merge, is green.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uy7K2qPR85KvekjCnFFFxZ
@github-actions github-actions Bot added documentation Improvements or additions to documentation tools Build tools, scripts, and developer tooling labels Nov 4, 2026
@angeloINTJ
angeloINTJ merged commit 610d77d into main Nov 4, 2026
15 checks passed
@angeloINTJ
angeloINTJ deleted the fix/savings-floor-tft-graph branch November 4, 2026 11:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation tools Build tools, scripts, and developer tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant