tu: promote ov100/daObjPathLift_c to one translation unit - #1979
Conversation
Eight one-function sources and eight delinks entries become one .cpp and one .text span (0x02146d7c-0x02147328), the shape the original TU had. mwcc emits one .text section per function in reverse source order, so the reconstructed file is written highest-address-first; the destructor pair's D1/D0 order is the compiler's own. Merging the objects makes mwcc emit an extra vague-linkage _ZN7Vector3D1Ev into the combined object -- types.h gives Vector3 a deliberately empty inline destructor, so every TU that destroys one re-emits it. The cartridge keeps a single copy at arm9:0x020072c0 that src/_ZN7Vector3D1Ev.cpp owns, so the manifest licenses this object's copy with the deadstrip-duplicate disposition added in #1977: rombuild reads the cartridge's bytes at that address and objisolate refuses unless this copy is byte-identical STB_LOPROC output. Known coverage cost, stated plainly: the file is still a SHADOW TU. Its D0/D1 are free functions carrying @symbol markers that stamp the base vptr directly, not a real `class daObjPathLift_c`, so this object emits no _ZTV/_ZTI/_ZTS. The two per-function destructor files it replaces did emit them, and romdata_check compared 128 of 128 _ZTV15daObjPathLift_c bytes against the cartridge. After this commit those three data symbols leave the checked set entirely -- verified by diffing `rombuild --data-json` per symbol, not the summary, which counts records per object and falls on consolidation for unrelated reasons. No symbol changed verdict; three stopped being checked. 106/106 is blind to vtables, so no gate reports this. Restoring it means giving the TU a real class whose out-of-line destructor is the key function; that is the next step, not this one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QhhAeJwXBnfPp7B5DNjCwh
✅ PR validation — PassedCommitted merge introduces no reconstruction or attribution regression. Full merge validation
Byte-verified means the range carries Warnings: 8 address range(s) left the byte-verified set while enrolled totals held steady: ov100:0x02146d7c-0x02146dec, ov100:0x02146dec-0x02146e70, ov100:0x02146e70-0x0214700c, ov100:0x0214700c-0x02147054, ov100:0x02147054-0x021470a4, +3 more; 7 more function(s) now claim a match that nothing compiles; enroll them in a delinks.txt to have the ROM build check them. 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. |
The second half of #1878, now that #1977 has landed the tool half. Eight one-function
sources and eight
delinks.txtentries become one.cppand one.textspan — the shapethe original translation unit had.
src_tu/actors/daObjPathLift_c.cppmoves tosrc/actors/and is enrolled, so the ROMbuild now compiles all eight functions from one object and byte-compares the whole
0x5AC-byte span against the cartridge. mwcc emits one
.textsection per function in thereverse of source order, so the file is written highest-address-first; the D1/D0 pair's
relative order is the compiler's own.
Why this needed #1977
Combining the objects makes mwcc emit an extra vague-linkage
_ZN7Vector3D1Evinto themerged object —
include/types.hgivesVector3a deliberately empty inline destructor,so every TU that destroys one re-emits the 4-byte body. The cartridge keeps a single copy
at
arm9:0x020072c0thatsrc/_ZN7Vector3D1Ev.cppowns. The manifest licenses thisobject's copy with the
deadstrip-duplicatedisposition from #1977, which reads thecartridge's own bytes at that address and refuses unless this copy is byte-identical
STB_LOPROCoutput.Coverage cost, stated plainly
This file is still a shadow TU. Its
D0/D1are free functions carrying@symbolmarkers that stamp the base vptr directly (
*(void***)p = (void**)_ZTV10dBgActor_c;), nota real
class daObjPathLift_c. So this object emits no_ZTV/_ZTI/_ZTS.The two per-function destructor files it replaces did emit them, and
romdata_checkcompared 128 of 128
_ZTV15daObjPathLift_cbytes against the cartridge. After thiscommit those three data symbols leave the checked set entirely:
Measured by diffing
rombuild --data-jsonper symbol, not the summary line — that countsone record per data symbol per object, so it falls on consolidation for reasons that
have nothing to do with verification. No symbol changed verdict; three stopped being
checked.
romdata_checkis the only gate that looks at vtables and 106/106 is blind tothem, so no check in this pipeline reports this. It is called out here because it
would otherwise land silently.
This matches how the one already-promoted TU on this tree (
ActorBase_SceneNode) behaves —it emits no ROM-data records either.
Follow-up, already measured. The class here is real —
InitResources,Behavior,RenderandCleanupResourcesare all genuinedaObjPathLift_c::methods. Only thedestructor is shadowed, and restoring it to the real out-of-line
daObjPathLift_c::~daObjPathLift_c() {}thatsrc/_ZN15daObjPathLift_cD1Ev.cppalreadycarries on main makes both variants byte-match under 2004/b56 (
build_pin: D10x02146d7c/0x70, D0 0x02146dec/0x84) and brings
_ZTV/_ZTI/_ZTSstraight back. Thegenerated file's claim that this form "measured 999 differing words" is stale — it
predates
include/PathLift.hgetting its inline~PathLift() {}.It cannot ship in this PR because it needs a tools change first, and the validator
restores all of
tools/from base. With a real destructor the TU also emits_ZTV8PathLift,_ZN8PathLiftD1Ev/D0Evand the whole ancestor RTTI chain as vaguelinkage.
objisolate.derive_deadstrip's multi-symbol path refuses any non-.textcontent, and
deadstrip_planacceptsSTT_FUNConly — so the data half cannot belicensed. Licensing just the three extra functions fails closed on the entanglement:
_ZTV8PathLiftholds a relocation to the D0 body, so the vtable and the function have tobe licensable together. That is one more tools-then-manifest split, the same shape as
#1977 → this PR.
Test plan
python tools/rombuild.py -j 16→ 106/106 exact, 100.000000% of compared bytes;11,082 source-built functions, 11,082 reproducing, 0 mismatching. Enrolled sources
11,081 → 11,074 (−7: eight files collapse into one).
rombuild --data-jsondiffed per symbol against the base — 0 verdict changes, the3 losses above.
python tools/port_refcheck.py→ 405 checked, all references resolvepython tools/prepush_attribution.py --base origin/main --head HEAD→ 0 changed, 0 lostUnblocks the rest of the stack: #1880 → #1882 → #1884 → #1914 all restack behind this.