Fix: main cannot build the ROM -- accept a vptr store already in the ROM's vtable convention - #1989
Merged
Merged
Conversation
main cannot build the ROM. `python tools/rombuild.py` on baa3d97 stops at step 3 with src/actors/daObjPathLift_c.cpp: isolate: compiler-only deadstrip refused: _ZTV10dBgActor_c: unexpected reloc type=2 addend=0 #1987 added a shape survey for the RTTI relocations a promoted TU's surviving code makes, and it accepted only ONE of the two legitimate spellings of a vptr store. mwcc's `_ZTV<C>` addresses the vtable OBJECT, so a store against a definition in the same object carries the 8-byte preamble skip and has to lose it once that definition is externalised. But a source may equally declare the ROM symbol itself -- `daObjPathLift_c.cpp:79` has extern int _ZTV10dBgActor_c[]; -- which is symbols.txt's convention, where the symbol IS the slot array. That store relocates with addend 0, there is nothing to correct, and `_apply` already leaves it alone because its rewrite is keyed on `addend >= VTABLE_PREAMBLE`. The survey refused it anyway, so the first promoted TU on main to inherit a base's vtable rather than own one took the whole build down with it. Both spellings are now accepted. Anything strictly between them is still refused rather than guessed at, which is the point of the survey. Two tests: one builds a class whose destructor stores an extern `_ZTVBase`, asserts the reference relocates at 0 and comes back out untouched; the other retargets a known-good addend to 4 and asserts the refusal still fires. python tools/rombuild.py -j 16 on this branch: 106/106 exact, 100.000000% of compared bytes, PASS. 99 tests pass across objisolate/rombuild/tubuild. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QhhAeJwXBnfPp7B5DNjCwh
✅ PR validation — Passednoverify: no source/build-data changes in this PR Each changed |
andrewboudreau
added a commit
that referenced
this pull request
Aug 30, 2026
Both come from the #1880 -> #1914 promotion chain, which bundled them with the manifest rows and sources they produce. The PR validator restores all of `tools/` from base, so no PR in that chain could ever test its own tool change -- every one of them is UNSTABLE for that reason alone. These are lifted out and land alone, off main, so the content PRs that follow are judged against tools that are already there. `tu_order_check.py` compiles a manifest TU once and reports, per licensed function, bytes and emission order. Production isolation places an object's `.text` into the spanning delink in emission order, so when a TU defines its destructor out of line and mwcc emits `D2, D0, D1` against the cartridge's `D1, D0`, the range does not link. The tool makes that visible before a promotion is attempted rather than after. `tu_promote.py` performs the mechanical half of a promotion and guesses at nothing: it collapses the per-function `delinks.txt` entries into one `complete` entry spanning the manifest's sections, `git mv`s the `src_tu/` source to its production path (R100, so credit follows), `git rm`s every `legacy_source`, rewrites the entry to `status: promoted`, and adds one `attribution.json` override per absorbed symbol. It compiles nothing -- the proof of a promotion is `rombuild.py` reporting 106/106 with `mismatching: 0` over the whole batch. The chain's `objisolate.py`/`rombuild.py`/`romdata_check.py` deltas are deliberately NOT carried: they are the predecessor of the deadstrip-data work that has since landed as #1977, #1987 and #1989, and main's versions supersede them. The remaining three files are comment-only, correcting paths that the promotion moves. Verified against main at c05617c, not against the chain's 350-commit-old base: `tu_promote --dry-run ov002/BlueCoinSwitch ov002/Exit ov045/PoleLift` resolves all three to their production paths, and refuses `ov100/daObjPathLift_c` as already promoted; `tu_order_check ov002/BlueCoinSwitch ov045/PoleLift` compiles both and reports the `D0, D1` + extra `D2` against the wanted `D1, D0`. Claude-Session: https://claude.ai/code/session_01QhhAeJwXBnfPp7B5DNjCwh Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
main cannot build the ROM
python tools/rombuild.py -j 16onbaa3d97eestops at step 3:Reproduced on a clean detached checkout of
origin/main. #1987 and #1979 are eachfine on their own; the break is in the pair. #1987 added a shape survey for the RTTI
relocations a promoted TU's surviving code makes, and #1979's promoted
ov100/daObjPathLift_cis the first source on main to exercise it.The cause
There are two legitimate spellings of a vptr store, and they differ by exactly one
vtable preamble. The survey accepted only one of them.
mwcc's
_ZTV<C>addresses the vtable object, so a store against a definition inthe same object carries the 8-byte preamble skip, and has to lose it once that
definition is externalised — that is the case #1987 was written for, and it is right.
But a source may equally declare the ROM symbol itself.
daObjPathLift_c.cpp:79:That is
symbols.txt's convention, where the symbol is the slot array. The storerelocates with addend 0, there is nothing to correct, and
_applyalready leaves italone — its rewrite is keyed on
addend >= VTABLE_PREAMBLE. The survey refused itanyway, so the first promoted TU to inherit a base's vtable rather than own one took
the whole build down.
This is not specific to
daObjPathLift_c. Every promoted TU whose class inherits itsvptr — the common case — spells it this way.
The fix
Accept both spellings. Anything strictly between them is still refused rather than
guessed at, which is the point of having a survey at all.
Two tests: one builds a class whose destructor stores an extern
_ZTVBase, asserts thereference relocates at 0 and comes back out untouched; the other retargets a known-good
addend to 4 and asserts the refusal still fires.
Verification
python tools/rombuild.py -j 16— 106/106 exact, 100.000000% of compared bytes,PASS; 446 verified / 239 partial / 15 differ from 7,798 object records.
pytest tools/test_objisolate.py tools/test_rombuild.py tools/test_tubuild.py—99 passed.
🤖 Generated with Claude Code