Rename FallBlockBfs to daObjKm2_Fall_Block_c and consolidate it into one TU - #2151
Conversation
`FallBlockBfs` was a coined name. The cartridge carries the real one, and I measured it rather than trusting the config. In extracted/overlays/overlay_0045.bin, ov045 base 0x021111a0 (the `.text start:` on line 1 of config/arm9/overlays/ov045/delinks.txt): vtable preamble 0x021130ec [offset-to-top 0, 0x021130a0] _ZTI… 0x021130a0 [0x0209a764, 0x021130b8, 0x02114874] _ZTS bytes 0x021130b8 "21daObjKm2_Fall_Block_c" 0x0209a764 is _ZTVN3abi20__si_class_type_infoE (config/arm9/symbols.txt), so one base at subobject offset 0, and that base's _ZTI is 0x02114874 = _ZTI16daObjFallBlock_c — which lives in ov015, a cross-overlay reference. The rename is load-bearing, not cosmetic: a coined class reaches no symbol home, so its _ZTI/_ZTS/_ZTV records can never be word-compared against the cartridge and its key-function TU cannot declare a verifiable compiler_only_output. This commit is the rename ALONE; the TU consolidation follows separately. tools/class_rename.py --with-derived did 22 edits and 8 renames. Reviewed for its known blind spots, four of which were present and are corrected here: - tools/ is outside the tool's reach. tools/test_tubuild.py looked the entry up by id (`ov045/FallBlockBfs`), which the rename changed, so the suite would have failed closed; tools/tu_config.py names the class in prose. - The manifest's `linkerOutput` is a verbatim captured mwldarm transcript. The tool rewrote the function names inside it, fabricating output the linker never produced. Restored byte-for-byte. - notes/tu-reconstruction-pilot-2-report.md sec 11 is a historical record of what a concurrent session added under the then-current filename. Reverted, and the one repo-rooted path token in it reworded to name the surviving `src_tu/actors/` directory so check_dead_references stays green. - Two collapsed coined/ROM pairs became tautologies: a "(FallBlockBfs, ov045)" parenthetical in include/daObjFallBlock_c.h's descendant list, and three duplicated `class_names` cells in notes/data/c-cpp-classification.tsv. Blind spot (d) — ROM-true names attached to addresses symbols.txt contradicts — is ABSENT here. All 7 changed symbols/actor_renames.tsv rows were cross-checked against config/arm9/overlays/ov045/symbols.txt and every address agrees: 0x02111d48 D1, 0x02111d98 D0, 0x02111dfc CleanupResources, 0x02111e10 InitResources, 0x02111e24 Spawn, 0x021130d0 SpawnInfo, 0x021130f4 _ZTV. Verified: pytest tools/test_tubuild.py -k splice_refuses passes; check_dead_references reports no new dead references and no broken links. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FsXWgEM3Gtbqs6zEdwLMYV
…_obj_km2_fall_block.cpp
Five per-function objects collapse into one `complete` delinks span. The stem
follows the snake_case scheme in notes/tu-naming-and-swallowers.md sec 1
(tools/tu_names.py): daObjKm2_Fall_Block_c -> d_a_obj_km2_fall_block.
## The key function, and the trap this branch deliberately reverses
The destructor moves INLINE into the header. Written out-of-line, mwccarm
emits D0 before D1 -- the reverse of the cartridge's order, which objisolate
refuses for the whole TU -- plus a third D2 with no ROM home.
That only works while some other virtual keeps this TU as the key-function TU.
Both slot methods override daObjFallBlock_c's pure `virtual s32
InitResources() = 0` / `CleanupResources() = 0` pair, so they were already
virtual; they are now spelt with the base's `s32` return type and declared
plainly, and CleanupResources -- declared first -- is the key function.
notes/plan-cpp-conversion-queue.md sec 5 held this exact pair up as the
exemplar of declaring vtable-slot methods NON-virtual on purpose, so that no
one-function file became the key-function TU and emitted _ZTV. That was the
right call while the class was spread over five files. With one TU there is
exactly one place for the group to land, and the class stays unverifiable
against the cartridge until it does, so the trap is now the goal. That section
is rewritten to say so rather than left contradicting the tree.
## The bodies are the recovered ones, not the generator's shadow
tu_promote's starting point spelled all five bodies as extern-C free functions
over `void *self`, and its factory stored the leaf vtable TWICE. The ROM stores
the base's vptr and then the leaf's -- the base sub-object is constructed
first. The bodies here are the recovered C++ ones from the deleted
per-function sources.
The factory's second store is now `&_ZTV21daObjKm2_Fall_Block_c[2]`: this TU
defines that vtable, so mwccarm's symbol addresses the object start -- the
two-word [offset-to-top, typeinfo] preamble at 0x021130ec -- while symbols.txt's
symbol at 0x021130f4 is already the slot array. _ZTV16daObjFallBlock_c is not
defined here (ov098 owns it) and takes no bias.
## Verification
- tools/rombuild.py -j16 -- 106/106 exact, 100.000000% of compared bytes,
mismatching 0
- ROM data from source: 570 verified, UP one: _ZTV21daObjKm2_Fall_Block_c is a
record no source proved before
- romdata_check per-symbol, on a raw (non-isolated) compile:
_ZTI21daObjKm2_Fall_Block_c VERIFIED 12 bytes
_ZTS21daObjKm2_Fall_Block_c VERIFIED 24 bytes
_ZTV21daObjKm2_Fall_Block_c PARTIAL 128 of a 140-byte extent
The PARTIAL is an extent artifact, not a mismatch. I read the cartridge:
the table is 32 slots, 0x021130f4..0x02113174, and all 32 words are emitted
and match. The next symbol is data_ov045_02113180, in .bss, so the config's
extent runs 12 bytes past the table's end into the zero padding ahead of it.
Slots 32-34 read 0x00000000 in the ROM. This is the known _ZTV extent
overrun, and it is pre-existing config, not something this branch introduced.
- 13 RTTI symbols licensed as deadstrip-data, every canonical address checked
against symbols.txt. Two are cross-overlay: _ZTI16daObjFallBlock_c ov015
0x02114874 -- which independently confirms the base pointer I measured out of
overlay_0045.bin -- and _ZTS16daObjFallBlock_c ov015 0x0211488c.
- tiers_ratchet.py --check PASS 2591/2591; eligible 11096/11211;
check_src_tu_compiles 98/98; check_header_offsets spans 0x34c;
check_dead_references, layout_check, port_refcheck clean
- No config_tu/ tree exists for ov045, so the dangling-shadow defect that the
ov019 promotion hit does not reach this branch. Checked, not assumed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FsXWgEM3Gtbqs6zEdwLMYV
✅ PR validation — PassedCommitted merge passes; 5 contributor credit reassignment(s) noted, not a blocker. Full merge validation
Contributor credit moved (5)
Byte-verified means the range carries Warnings: contributor attribution changed, not a blocker (5 changed, 0 lost -- src/actors/d_a_obj_km2_fall_block.cpp: tangosdev -> github-actions[bot]; src/actors/d_a_obj_km2_fall_block.cpp: tangosdev -> github-actions[bot]; src/actors/d_a_obj_km2_fall_block.cpp: lunavyqo -> github-actions[bot]; +2 more); 5 address range(s) left the byte-verified set while enrolled totals held steady: ov045:0x02111d48-0x02111d98, ov045:0x02111d98-0x02111dfc, ov045:0x02111dfc-0x02111e10, ov045:0x02111e10-0x02111e24, ov045:0x02111e24-0x02111e60; 4 more function(s) now claim a match that nothing compiles; enroll them in a delinks.txt to have the ROM build check them. Per-file link-check detailAll 16 changed file(s) compile to the ROM byte-for-byte with correct relocation targets.
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. |
Restack onto a6c2ece. Two conflicts, both generated files, both resolved by the tree's own rules: attribution.json -- UNION. Disjoint key sets: this branch's five src/actors/d_a_obj_km2_fall_block.cpp entries and main's five daObjKm3_Kuruma_c ones. Either --ours or --theirs would silently drop five real records. Re-parsed as JSON after. notes/cpp-tu-current-state.md -- REGENERATED with `tools/cpp_tu_state.py --write-note` after staging both sides' authority inputs, not hand-merged. config/converted-baseline.json auto-merged; it is a SET ratchet and was not touched by hand. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FsXWgEM3Gtbqs6zEdwLMYV
…e-tu # Conflicts: # notes/cpp-tu-current-state.md
…e-tu # Conflicts: # attribution.json # notes/cpp-tu-current-state.md
…e-tu # Conflicts: # attribution.json
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Independent of the #2130 chain — this branches off
main.FallBlockBfs, Bowser in the Fire Sea's falling block, moves to the spelling thecartridge's own RTTI carries, and its five per-function objects collapse into one genuine
translation unit under the snake_case scheme in
notes/tu-naming-and-swallowers.mdsec 1(
tools/tu_names.py)..textFallBlockBfsdaObjKm2_Fall_Block_csrc/actors/d_a_obj_km2_fall_block.cpp0x02111d48..0x02111e60Why the rename is load-bearing, not cosmetic
A coined class name reaches no symbol home. Its
_ZTI/_ZTS/_ZTVrecords are emittedunder a mangling the config carries nowhere, so
romdata_checkcan never word-comparethem against the cartridge and the TU cannot declare a verifiable
compiler_only_output(
tools/tubuild.py apply_compiler_only_policy). Renamed, this TU is its class'skey-function TU and claims the whole RTTI group as retail data.
Measured, not assumed, in
extracted/overlays/overlay_0045.bin(ov045 base0x021111a0,the
.text start:on line 1 ofconfig/arm9/overlays/ov045/delinks.txt): the vtableobject's preamble at
0x021130ecis[offset-to-top 0, 0x021130a0], and_ZTI21daObjKm2_Fall_Block_cat0x021130a0reads[0x0209a764, 0x021130b8, 0x02114874]—
_ZTVN3abi20__si_class_type_infoE, a typeinfo name at0x021130b8whose bytes are thestring
"21daObjKm2_Fall_Block_c", and_ZTI16daObjFallBlock_c, so the cartridge statesthe direct base too. That base pointer lands in ov015, and
coo_rowsindependentlyresolved
_ZTI16daObjFallBlock_cto ov0150x02114874— the same address, from the otherdirection.
The trap this branch deliberately reverses
The destructor moves inline into the header: written out-of-line, mwccarm emits D0
before D1 — the reverse of the cartridge's order, which
objisolaterefuses for thewhole TU — plus a third
D2with no ROM home.That only works while some other virtual keeps the TU as the key-function TU. Both slot
methods override
daObjFallBlock_c's purevirtual s32 InitResources() = 0/CleanupResources() = 0pair, so they were already virtual; they now carry the base'ss32return type and are declared plainly, andCleanupResources— declared first — isthe key function.
notes/plan-cpp-conversion-queue.mdsec 5 held this exact pair up as the exemplar ofdeclaring vtable-slot methods non-virtual on purpose, so no one-function file became the
key-function TU and emitted
_ZTV. That was right while the class was spread over fivefiles. With one TU there is exactly one place for the group to land, and the class stays
unverifiable until it does. That section is rewritten to say so rather than left
contradicting the tree.
The bodies are the recovered ones, not the generator's shadow
tu_promote's starting point spelled all five bodies as extern-C free functions overvoid *self, and its factory stored the leaf vtable twice. The ROM stores the base'svptr and then the leaf's — the base sub-object is constructed first. The bodies here are
the recovered C++ ones from the deleted per-function sources. The factory's second store
is now
&_ZTV21daObjKm2_Fall_Block_c[2], because this TU defines that vtable and mwccarm'ssymbol addresses the object start rather than the slot array.
Verification
tools/rombuild.py -j16— 106/106 exact, 100.000000% of compared bytes, mismatching 0_ZTV21daObjKm2_Fall_Block_cis a record nosource proved before.
romdata_checkper-symbol, on a raw (non-isolated) compile:_ZTI21daObjKm2_Fall_Block_cVERIFIED 12 bytes,_ZTS21daObjKm2_Fall_Block_cVERIFIED 24 bytes,
_ZTV21daObjKm2_Fall_Block_cPARTIAL, 128 of a 140-byteextent.
The PARTIAL is an extent artifact, not a mismatch, and I read the cartridge to be sure:
the table is 32 slots,
0x021130f4..0x02113174, and all 32 words are emitted and match.The next symbol is
data_ov045_02113180, in.bss, so the config's extent runs 12 bytespast the table into the zero padding ahead of it; slots 32–34 read
0x00000000in theROM. Pre-existing config, not introduced here.
deadstrip-data, every canonical address checked againstsymbols.txt.tiers_ratchet.py --checkPASS 2591/2591 ·eligible11096/11211 ·check_src_tu_compiles98/98 ·check_header_offsetsspans0x34c·check_dead_references,layout_check,port_refcheckcleanconfig_tu/tree exists for ov045, so the dangling-shadow defect an agent reviewfound on the ov019 promotion cannot reach this branch. Checked, not assumed.
The rename commit is separate, and was reviewed for the tool's blind spots
545ba599dis the rename alone.tools/class_rename.py --with-deriveddid 22 edits and 8renames; four of its known blind spots were present and are corrected in that commit —
including
tools/test_tubuild.py, which looked the manifest entry up by the id the renamechanged and would have failed the suite closed, and the manifest's
linkerOutput, averbatim captured mwldarm transcript the tool rewrote into output the linker never
produced. Blind spot (d) — ROM-true names asserted at addresses
symbols.txtcontradicts— is absent: all 7 changed
symbols/actor_renames.tsvrows were cross-checked andevery address agrees.
🤖 Generated with Claude Code
https://claude.ai/code/session_01FsXWgEM3Gtbqs6zEdwLMYV