feat(vapor): overlay allocation for frame-local temporaries - #269
Conversation
Materialized view chains and string scratch used to be either permanent statics (a vt slot lives forever for one call's worth of work) or C-stack locals (21-33 B frames on the cc65/sdcc software stack). Both are wrong for the 8-bit targets: statics never share, stack frames cost code and cycles on every access, and on the NES the stack shares 2 KB with the pool, the computed views, and the shadow grid. Every temporary is now tagged with the generated function that owns it. Two temporaries share one static slot unless their owners can be live at the same time: same function, or one reachable from the other in the static call graph (helpers, computed accessors, keymap dispatch - an indirect dispatch edges to every action in the table). The subset forbids recursion and nothing runs from interrupts, so reachability is the whole liveness story. Greedy coloring assigns slots; placeholders substitute at emit time; the memory plan prints the overlay plan. Todo on NES: 8 frame-local temps collapse into 3 shared slots (51 B). Mutually-exclusive keymap actions share string scratch; the nested slice buffer interferes with its enclosing build and gets its own slot. Effects measured on the todo example: - NES: CODE 8806 -> 8666 B (-140 B ROM); >=42 B of vp_sb/vp_view frames leave the deepest stack path. The RAM/stack boundary moves up 32 B (funded by those 42 B, so stack margin grows by >=10 B). - GB: 11413 -> 11383 used ROM bytes (-30 B); SM83 stack relieved. - GBA: 9328 -> 9356 B (+28 B) - ARM stack addressing is cheap, so the slot indirection costs slightly more there; RAM impact is negligible. Full suite including three-console oracle-ROM parity: 71 tests, 7,398 assertions, 0 fail. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1b452c1 to
4c38c5a
Compare
|
Verdict: merging. Reviewed on a local checkout; the size table, the safety argument, and the NES memory-map change were each verified independently. All three measured claims reproduce exactly. NES CODE 8806 → 8666 B (relinked the build's kept genDir objects with The sharing invariant holds structurally. The three ways a shared static could go wrong were each checked: The NES boundary move is real and safe. On the branch, BSS ends at $0717 — inside the new $0720 limit — and the jsnes-emulated 31-press parity tape (draft editing, glyph commit, filter cycling, clear-completed) runs green over the new layout, so the deepest converted paths are exercised behaviorally, not just argued. Rebased onto main for you (force-pushed One non-blocking nit for a future pass: placeholder substitution is a regex over the whole C text, so an app string literal containing Thanks @lfkdsk — two compiler PRs in one week that both come with a soundness argument grounded in the subset's actual rules, measured byte tables, and tests that pin the interference cases. The owner/interference machinery reads like it will carry the lifetime-aware packing you sketched, and I'd be glad to see that follow-up. |
What
Materialized view chains and string scratch used to be either permanent statics (
static vp_view vt5lives forever for one call's worth of work) or C-stack locals (21–33 Bvp_sb/vp_viewframes on the cc65/sdcc software stack). Both are wrong for the 8-bit targets: statics never share, stack frames cost code and cycles on every access, and on the NES the stack shares 2 KB with the pool, the computed views, and the shadow grid.This PR compiles every frame-local temporary into a shared static overlay slot — the classic 6502/8051 overlay technique, driven by the compiler's own call graph.
How
Measured on the todo example
The structural point for the NES: string scratch and view materialization no longer scale the stack with app complexity — they scale a statically-known, deduplicated overlay region the memory plan accounts for byte-by-byte.
Tests
vapor/tests/overlay.test.ts: todo/NES slot inventory; mutually-exclusive keymap actions sharing one string slot; nested slice scratch interfering with its enclosing build; view temps splitting when an effect's chain reads through a computed's chain.vapor/DESIGN.md§3 documents the overlay model.This is also groundwork for the storage-allocation direction discussed around #267: the owner/interference machinery is the substrate a future lifetime-aware packer (field narrowing, bool bit-packing) would build on.
🤖 Generated with Claude Code