CONVERTED tier: stop scoring MMIO as a codegen trick, and TU promotions as deletions - #2007
Merged
Merged
Conversation
…ns as deletions Two defects in the CONVERTED-tier gate, both of the same species: the gate counted something other than what it says it counts. 1. tools/tiers.py scored hardware registers as match hacks `no_codegen_trick` ORed LAUNDER, VOLATILE and ASM, and VOLATILE was a bare `\bvolatile\b`. On a Nintendo DS the only way to reach VRAM, the geometry engine or the IPC/DMA/divider registers is a volatile-qualified pointer, so the criterion failed the code that had no alternative: src/_ZN8dScene_c22ResetHardwareRegistersEv.cpp 74 hits, all 0x0400xxxx src/_ZN2GX13SetBankForTexEt.cpp 25 hits, all VRAM banks src/_ZN3G2x12SetBGyAffineEPVtP9Matrix2x2iiii.cpp the block is a PARAMETER A reconstructed TU absorbing any of those inherits the failure whole, so it compounds with the TU work rather than sitting still. The shapes are separable by WHAT is volatile-qualified. MMIO qualifies the pointed-to type, so a `*` follows it. A match hack qualifies an OBJECT -- `volatile int li;`, `volatile Vector3 v;`, the `(s32)(volatile s32)rsc` round-trip, `Node *volatile arr[4]` where the pointer not the pointee is volatile. VOLATILE = re.compile(r"\bvolatile\b(?![\s\w:]*\*)") Files scoring a codegen trick: 655 -> 254. All 401 released were checked to be MMIO-only, and the 254 kept still contain every match-hack form -- the negative direction was measured, not assumed. CONVERTED 2,511 -> 2,568 functions (22.20% -> 22.71%); no_codegen_trick 10,623 -> 11,020. Additions only. 2. tools/tiers_ratchet.py reported a TU promotion as a vanished file A promotion consolidates N per-symbol src/_ZN....cpp files into the one src/actors/X.cpp they always were; git records N deletions plus one addition, and every one read as `GONE -- not a tracked source file any more`. Measured on PR #1882 (tu/inline-dtor-order, 9c6396c): 90 of 90 backslid paths were TU `legacy_source` entries whose TU is "status": "promoted" and whose `promoted_source` exists on the branch. Zero were real deletions. A GONE path is now resolved through the manifest (via tools/tu_manifest.py, never the files) and reported as a MOVE naming the absorbing file and what that file does with the five criteria. A promotion is NOT free. The criteria are file-wide, so a clean function merged into a file with one bad line loses its status, and that still exits 1. Only a move into a file that itself passes all five is silent. In practice a promotion lands in the failing case by construction -- a reconstructed TU must spell _ZN7fBase_cnwEj, _ZN8dActor_cC2Ev and _ZN8dActor_cD2Ev directly or its range will not link -- so no_mangled_refs can never pass for one. That is structural; the answer is --update --reason, not exempting mangled refs. Re-banked config/converted-baseline.json in the same commit: 1,957 -> 2,567, +610 / -0. Set-diffed against HEAD to confirm zero removals. tools/test_tiers.py pins both readings in both directions (29 tests) and converted-ratchet.yml now runs it, plus watches tu_manifest.py and config/tu_manifest.d/**. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WdCK1xgrJdiJzPCh3bAJfQ
✅ PR validation — PassedCommitted merge introduces no reconstruction or attribution regression. Full merge validation
Byte-verified means the range carries The private worker commits a test merge, builds the stock ROM profile, compares every executable module, measures matched and source-built code, checks contributor lineage, and verifies affected relocations. The mod profile is opt-in and is not part of this merge gate. |
This was referenced Aug 30, 2026
andrewboudreau
added a commit
that referenced
this pull request
Aug 30, 2026
Resolves three generated files that main regenerated wholesale: - config/converted-baseline.json: REGENERATED with the post-#2007 tiers.py (tools/tiers_ratchet.py --update), not resolved by side or key union. Keeping the branch's copy would have banked 1951 and silently lowered the ratchet floor by ~600 entries with every gate green. New count 2559 = main's real current CONVERTED score (2565) minus the six per-symbol files this PR's ov070/daKpFr_c promotion absorbs. - config/converted-backslide-exceptions.jsonl: the six daKpFr_c rows this branch already logged are kept as-is; two rows added for src/_ZN12daObjAbuku_cD0Ev.cpp and D1, which main's own ov002/daObjAbuku_c promotion (#1996) absorbed without re-banking. Both are classified "MOVED -- absorbed into src/actors/daObjAbuku_c.cpp" by the tool's own classify_missing(), and that file exists in this tree. - notes/cpp-tu-current-state.md: regenerated with tools/cpp_tu_state.py --write-note; --check-note is clean. No source file, delinks entry, or byte changed in this merge. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WdCK1xgrJdiJzPCh3bAJfQ
andrewboudreau
added a commit
that referenced
this pull request
Aug 30, 2026
Two conflicts, resolved without --ours/--theirs on either: * attribution.json -- key UNION of the two override maps. Verified first that neither side removed a key from the merge base and that the 5 keys both sides touched agree on their value, so the union is the whole 3-way merge, not a pick. 845 base -> 1011 ours + 852 theirs -> 1018. * config/converted-baseline.json -- taken from main VERBATIM at 2567. NOT regenerated and NOT lowered. This branch's tree scores 2483 under the post-#2007 tools/tiers.py, so re-banking here would write 2483 over main's 2567 and silently drop the floor. `--check` is therefore red on this branch, deliberately and loudly: all 78 backslides are `MOVED -- absorbed into ... by TU promotion`, zero deletions. See the PR discussion before re-banking.
andrewboudreau
added a commit
that referenced
this pull request
Aug 30, 2026
Carries origin/main through the restacked #2000. Same two generated files conflicted and are resolved the same way: - config/converted-baseline.json: REGENERATED with the post-#2007 tiers.py (tools/tiers_ratchet.py --update), never by side or key union. Count 2552 = the parent branch's 2559 minus the seven per-symbol files this PR's ov070/daKrpa_c promotion absorbs. All seven are classified "MOVED -- absorbed into src/actors/daKrpa_c.cpp" by classify_missing(), that file exists in this tree, and all seven already had rows in config/converted-backslide-exceptions.jsonl from this branch's own earlier run, so no duplicate rows were added. - notes/cpp-tu-current-state.md: regenerated with tools/cpp_tu_state.py --write-note; --check-note is clean. No source file, delinks entry, or byte changed in this merge. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WdCK1xgrJdiJzPCh3bAJfQ
andrewboudreau
added a commit
that referenced
this pull request
Aug 30, 2026
…het on main (#2011) Main has been out of sync since #1996 merged. `config/converted-baseline.json` banked `src/_ZN12daObjAbuku_cD0Ev.cpp` and `src/_ZN12daObjAbuku_cD1Ev.cpp`, which that PR deleted when it promoted the ov002/daObjAbuku_c translation unit. Nobody's checks were wrong; they just never overlapped. #1996's green ran against a base predating #2007's re-bank, which is what first banked those two files. By the time #1996 merged, the paths it deletes were in the baseline it never re-read. The next unrelated PR to touch src/** -- #1978, which has nothing to do with ov002 -- is the one that went red. The removal is legitimate and is banked with a reason, not reverted: MOVED -- absorbed into src/actors/daObjAbuku_c.cpp by TU promotion (ov002/daObjAbuku_c), which fails: No raw offset arithmetic; No unk_<off> fields; Calls things by real names, not mangled _Z A reconstructed TU must spell vague-linkage symbols directly (_ZN7fBase_cnwEj, _ZN8dActor_cC2Ev, _ZN8dActor_cD2Ev) or its range will not link, so `no_mangled_refs` structurally cannot pass for an absorbing file. Byte-match outranks readability. No source changed and no byte moved. Also adds a `push: [main]` trigger. The workflow's design note claimed staleness "only ever runs one way" -- permissive, never falsely red. TU promotion is the exception, because it REMOVES banked paths, and the note now says so. The trigger gates nothing (no branch protection; a red main is advisory) but it attributes the breakage to the merge that caused it instead of to whoever opens the next PR. tiers_ratchet --check PASS baseline 2565 current 2565 pytest tools/test_tiers.py 29 passed check_dead_references no new dead references Claude-Session: https://claude.ai/code/session_01WdCK1xgrJdiJzPCh3bAJfQ Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This was referenced Aug 30, 2026
Alberto12345678999
pushed a commit
to Alberto12345678999/sm64ds-decomp
that referenced
this pull request
Aug 30, 2026
…erences gate tangosdev#2007 (b6da506) landed prose in tools/tiers_ratchet.py and notes/converted-tier.md that names `src/actors/X.cpp` as a stand-in for "whichever file the promotion absorbed the symbols into". check_dead_references reads every repo-rooted path in prose as a real reference, so it read the stand-in as a rename that missed the prose and turned main red: FAIL: 2 prose reference(s) name a path that does not exist notes/converted-tier.md names `src/actors/X.cpp` tools/tiers_ratchet.py names `src/actors/X.cpp` Spelling it `src/actors/<Class>.cpp` fixes it at the source rather than suppressing it: GLOBBY already skips any reference containing `<>`, and a metavariable is what the sentence meant. The sibling stand-in in the same paragraphs, `src/_ZN....cpp`, has always been skipped for the same reason (GLOBBY also matches `...`) -- this makes the two consistent. The `src/actors/X.cpp` literals in tools/test_tiers.py are untouched: they are fixture values in code, not prose, and the gate never read them. check_dead_references no new dead references (133 unresolved, was 135) pytest tools/test_tiers.py 29 passed tiers_ratchet --check PASS baseline 2567 current 2567 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WdCK1xgrJdiJzPCh3bAJfQ
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.
Two defects in the CONVERTED-tier gate. Both are the same species — the gate counting something other than what it says it counts — and both are
tools/+ baseline only, with no source touched.Defect 1 —
tools/tiers.pyscored hardware registers as a codegen trickno_codegen_trickORsLAUNDER,VOLATILEandASM, andVOLATILEwas a bare\bvolatile\b. On a Nintendo DS the only way to reach VRAM, the geometry engine or the IPC/DMA/divider registers is avolatile-qualified pointer, so the criterion failed the code that had no alternative:volatilehitssrc/_ZN8dScene_c22ResetHardwareRegistersEv.cpp0x0400xxxxand VRAM bank registerssrc/_ZN2GX13SetBankForTexEt.cppsrc/_ZN3G2x12SetBGyAffineEPVtP9Matrix2x2iiii.cppThat compounds with the TU work rather than sitting still: the criteria are file-wide, so a reconstructed TU that absorbs any of those inherits the failure whole.
The two shapes are separable by what is volatile-qualified:
*follows it —*(volatile u32 *)0x4000400,volatile u16 *ime,volatile DMAChannelRegs *reg,f(volatile void *dst).volatile int li;,volatile Vector3 v;,volatile s32 zero = 0;, the(s32)(volatile s32)rscround-trip that demotes a local out of a register, andNode *volatile arr[4]where the pointer rather than the pointee is volatile.A negative lookahead rather than the equivalent greedy form on purpose:
[\s\w:]*\*would backtrack into the type name, stop mid-identifier and callvolatile u32 *pa scalar.Both directions were measured, not assumed — a
volatileregex that stops catching match hacks is a worse defect than the one being fixed.volatile int li;spill pad, everyvolatile Vector3 v;stack reserver, everyvolatile int dummy[4];frame filler, the(volatile s32)round-trip and theNode *volatileform.no_codegen_trick10,623 → 11,020; CONVERTED 2,511 → 2,568 functions, 22.20% → 22.71%. Additions only.Known conservative reading, documented at the regex rather than hidden:
typedef volatile u32 vu32;used only asvu32 *(4 files) still scores. Excluding typedefs opens a real evasion —typedef volatile int vi; vi dummy;would carry novolatileat the use site at all.Defect 2 —
tools/tiers_ratchet.pyreported a TU promotion as a vanished fileThe ratchet banks the SET of paths passing all five criteria and fails when a path leaves. A TU promotion consolidates N per-symbol
src/_ZN....cppfiles into the onesrc/actors/X.cppthey always were; git records that as N deletions plus one addition, so every one read as:Measured on PR #1882 (
tu/inline-dtor-order,9c6396c5f): 90 of 90 backslid paths were TUlegacy_sourceentries whose TU is"status": "promoted"and whosepromoted_sourceexists on the branch. Zero were real deletions. A gate whose entire output is false alarms trains people to re-bank without reading it.A
GONEpath is now resolved through the manifest — viatools/tu_manifest.py, never the files — and reported as a MOVE naming the absorbing file and what that file does with the five criteria:versus a genuine deletion, which still reads
GONE.A promotion is not free. The criteria are file-wide, so a clean function merged into a file with one bad line really does lose its status, and that case still exits 1. Only a move into a file that itself passes all five is silent (it is then an ordinary addition on the next
--update). Both halves are exercised on live data by the current manifest: the two_ZN7fBase_c9SceneNode*legacy paths absorbed intosrc/actors/ActorBase_SceneNode.cppare clean moves; the eight absorbed intosrc/actors/daObjPathLift_c.cppstill fail.In practice a promotion lands in the failing case by construction: a reconstructed TU must spell
_ZN7fBase_cnwEj,_ZN8dActor_cC2Evand_ZN8dActor_cD2Evdirectly or its range will not link, sono_mangled_refscan never pass for one. That is structural, not sloppiness, and it is not fixed by exempting mangled refs — byte-match outranks readability, and--update --reasonis where that trade gets a name against it.--checknow says so in its footer when a MOVE is among the failures.Re-banked in the same PR
Changing a criterion requires re-baking the baseline or CI goes red.
Zero removals — set-diffed against
HEAD, not trusted from the counter. The diff looks larger than +610 only because the previous file was not sorted andwrite_baselinesorts; the old"count": 1958was also stale against its own 1957-entry list and is now consistent.Verification
tools/rombuild.pyandtools/eligible.pywere deliberately not run: no source file is touched and the build directory is shared with other agents.Also in here
tools/test_tiers.py(new, 29 tests) pins both readings in both directions — MMIO must not score, a match hack still must; a promotion must read as a MOVE, and a move into a failing file must still fail the gate..github/workflows/converted-ratchet.ymlruns those tests, and now also triggers ontools/tu_manifest.pyandconfig/tu_manifest.d/**— a PR that only re-labels an entry"status": "promoted"changes what this gate says about an existing baseline.notes/converted-tier.mdgains a dated section for both, in the style of the existing writeup.🤖 Generated with Claude Code
https://claude.ai/code/session_01WdCK1xgrJdiJzPCh3bAJfQ