Reproduce game weak data with a PCH - #130
Conversation
Report for GMSJ01 (9b62e09 - aecc701)📈 Matched code: 37.73% (+0.57%, +20624 bytes) ✅ 46 new matches
...and 16 more new matches 📈 42 improvements in unmatched items
...and 12 more improvements in unmatched items 📉 4 regressions in unmatched items
|
d8cac9d to
6a41574
Compare
2002949 to
a927788
Compare
533a056 to
f8aa717
Compare
|
Follow-up commit a7e5756 centralizes the common game compiler profile in cflags_game: -opt all,nostrength, -inline deferred, and -prefix SMS.mch. The PCH build and the three evidence-backed no-PCH object exceptions retain explicit flag lists; THPPlayer remains on its separate profile. Verified with a full build (mario.dol: OK), ninja baseline/changes_all (no regressions), Python syntax validation, and git diff --check. |
6854611 to
9b9140b
Compare
|
Edited: this comment predated the rebase. The commit is now Resolves the "Known edge case" that was in the description — Root cause. Retail's MWCC picks the owning TU by key function: the first non-inline virtual in declaration order. Ours was Second fix.
Codebase sweep. I checked for the same bug elsewhere by resolving every // NOTE: has to be defined not first to get the vtable
// to emit to the correct TU
virtual ~JKRArchive();My sweep had been reading the pre-sweep header on this branch. No follow-up PR is coming, and none is needed. |
eb2b73a to
ca3368d
Compare
|
Rebased onto current Three things changed since the last look: CI. The Description. The Results table was measured against a superseded baseline and the "Known edge case" section described the MapObjInit stand-ins, which Re-audited the PCH selection.
Per-unit across the 40: 23 improve, 17 no effect, 0 regress. Across the 696 non-PCH units: none would gain, and exactly three regress under the global build — The 17 inert units are kept deliberately: seven are still at 0% code and 0% data, so there is nothing for the PCH to act on yet, and two are already at 100% data. Happy to trim them to 23 if you would rather the list carry only units with present-day evidence — it makes no difference to the build either way. |
Retail puts __vt__14TMapObjGeneral, the getFlushTime weak body and the @32@ adjustor thunk in MapObjInit.o. We were emitting all three in MapObjGeneral.o, which is why MapObjInit.cpp still carried hand-written stand-in literals to make up the missing data. MWCC picks the owning TU by key function: the first non-inline virtual in declaration order. Ours was loadAfter (MapObjGeneral.cpp); retail's must have been initMapObj (MapObjInit.cpp). initMapObj is an override of a TMapObjBase virtual, so moving its declaration to the front of the list cannot change the vtable layout -- and it doesn't, the table is still 0x1dc. All three symbols move to MapObjInit.o together. getFlushTime also had an empty body, which is 4 bytes; retail is 8 and disassembles to `lwz r3, mNormalFlushTime@sda21; blr`, so give it the real body. That mirrors getLivingTime directly above it, matches 100%, and silences the "return value expected" warning. With the data now coming from the real vtable, dummy/dummy2/dummy3/dummy4 are no longer needed and are removed -- no fakematch left in this file. matched data 50.0071% -> 53.5928% matched functions 7921 -> 7923 MapObjInit data 0.389% -> 44.203% mario.dol: OK; check-changed-symbol-order.py passes on both units. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ca3368d to
aecc701
Compare
Summary
This reproduces the MWCC precompiled-header behavior responsible for the J3DJoint weak Vec literals appearing in
.datain retail game objects.extra_cflags=["-prefix SMS.mch"]-opt all,nostrengthand-inline deferredcentralized incflags_gameTMapObjGeneralvtable ownership, which removes the last stand-ins inMapObjInit.cppTMBindShadowBodyTWW references:
Results
Measured against
mainat the current rebase point, by toggling only the PCH:So the PCH is worth +0.57pp code, +4.73pp data, +27 functions.
build/GMSJ01/mario.dol: OK.Largest per-unit contributions:
NPC/NpcInitData+10280 data,Player/MarioInit+10016 code,MoveBG/Item+5088 data,Enemy/hinokuri2+3000 code / +1976 data,Enemy/gatekeeper+2584 data,Strategic/liveinterp+2140 code,MarioUtil/MathUtil+2096 data.Per-unit A/B across the 40: 23 improve, 17 no measurable effect, 0 regress. The 17 inert units are kept because they are premature rather than wrong — seven of them (
bombhei,bosswanwan,feetinv,killer,tinkoopa,ModelGate,MarNameRefGen_BossEnemy) are still at 0% code and 0% data, so there is nothing yet for the PCH to act on, and two (MapEventDolpic,MapEventSink) are already at 100% data.Selective PCH audit
The selection was re-verified on the current base with a three-way controlled experiment, changing only whether the PCH is applied:
cflags_gameGlobal is worse than selective (code 37.6024%, data 54.7896%, 8088 functions), which is what justifies keeping the list narrow rather than turning the PCH on project-wide.
Across the 696 units that do not receive the PCH, none would gain from it, and exactly three regress under the global build:
MarioUtil/RumbleDataStrategic/spcinterpMarioUtil/gd-reinit-gxThese are the same three no-PCH cases identified when the list was first assembled, so the boundary is unchanged.
No padding or source fakematch is used for any of these results.
MapObjGeneral vtable ownership
Retail's
MapObjInit.oowns__vt__14TMapObjGeneral, thegetFlushTimeweak body, and the@32@__dt__14TMapObjGeneraladjustor thunk. We were emitting all three inMapObjGeneral.o, and that missing.datais what the hand-written stand-ins inMapObjInit.cppwere compensating for.MWCC selects the owning TU by key function: the first non-inline virtual in declaration order. Ours was
loadAfter(MapObjGeneral.cpp); retail's must have beeninitMapObj(MapObjInit.cpp). BecauseinitMapObjoverrides aTMapObjBasevirtual, its slot is fixed by the base, so promoting its declaration cannot change the vtable layout — and does not, the table is still exactly0x1dc. All three symbols relocate together.getFlushTimealso had an empty body{ }(4 bytes, and the source of areturn value expectedwarning). Retail is 8 bytes and disassembles tolwz r3, mNormalFlushTime@sda21; blr; that static already existed, so the real body mirrorsgetLivingTime()above it and matches 100%.With real vtable data present,
dummy,dummy2,dummy3anddummy4are removed.MapObjInitdata goes 0.389% → 44.203%; the remainder is genuine unreconstructed content, not padding.A sweep of the rest of the codebase for the same bug — resolving every
__vt__owner in mario.MAP against the TU defining that class's first non-inline virtual — found no other mismatches (624 classes resolved of 1508 vtables).Validation
-prefix SMS.mchninjabuild and retail DOL checksumtools/check-changed-symbol-order.pypasses on all 10 changed.cppfiles