Skip to content

Rename FallBlockBfs to daObjKm2_Fall_Block_c and consolidate it into one TU - #2151

Merged
andrewboudreau merged 7 commits into
mainfrom
cpp/fallblockbfs-snake-tu
Sep 1, 2026
Merged

Rename FallBlockBfs to daObjKm2_Fall_Block_c and consolidate it into one TU#2151
andrewboudreau merged 7 commits into
mainfrom
cpp/fallblockbfs-snake-tu

Conversation

@andrewboudreau

Copy link
Copy Markdown
Collaborator

Independent of the #2130 chain — this branches off main.

FallBlockBfs, Bowser in the Fire Sea's falling block, moves to the spelling the
cartridge'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.md sec 1
(tools/tu_names.py).

coined name cartridge name new TU module / .text
FallBlockBfs daObjKm2_Fall_Block_c src/actors/d_a_obj_km2_fall_block.cpp ov045 0x02111d48..0x02111e60

Why the rename is load-bearing, not cosmetic

A coined class name reaches no symbol home. Its _ZTI/_ZTS/_ZTV records are emitted
under a mangling the config carries nowhere, so romdata_check can never word-compare
them 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's
key-function TU and claims the whole RTTI group as retail data.

Measured, not assumed, in extracted/overlays/overlay_0045.bin (ov045 base 0x021111a0,
the .text start: on line 1 of config/arm9/overlays/ov045/delinks.txt): the vtable
object's preamble at 0x021130ec is [offset-to-top 0, 0x021130a0], and
_ZTI21daObjKm2_Fall_Block_c at 0x021130a0 reads [0x0209a764, 0x021130b8, 0x02114874]
_ZTVN3abi20__si_class_type_infoE, a typeinfo name at 0x021130b8 whose bytes are the
string "21daObjKm2_Fall_Block_c", and _ZTI16daObjFallBlock_c, so the cartridge states
the direct base too. That base pointer lands in ov015, and coo_rows independently
resolved _ZTI16daObjFallBlock_c to ov015 0x02114874 — the same address, from the other
direction.

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 the 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 now carry the base's
s32 return type and are 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 no one-function file became the
key-function TU and emitted _ZTV. That was right 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 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 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], because this TU defines that vtable and mwccarm's
symbol addresses the object start rather than the slot array.

Verification

  • tools/rombuild.py -j16106/106 exact, 100.000000% of compared bytes, mismatching 0
  • ROM data from source: 570 — 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, 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 bytes
    past the table into the zero padding ahead of it; slots 32–34 read 0x00000000 in the
    ROM. Pre-existing config, not introduced here.
  • 13 RTTI symbols licensed as deadstrip-data, every canonical address checked against
    symbols.txt.
  • 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 an agent review
    found 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

545ba599d is the rename alone. tools/class_rename.py --with-derived did 22 edits and 8
renames; 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 rename
changed and would have failed the suite closed, and the manifest's linkerOutput, a
verbatim captured mwldarm transcript the tool rewrote into output the linker never
produced. Blind spot (d) — ROM-true names asserted at addresses symbols.txt contradicts
— is absent: all 7 changed symbols/actor_renames.tsv rows were cross-checked and
every address agrees.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FsXWgEM3Gtbqs6zEdwLMYV

andrewboudreau and others added 2 commits September 1, 2026 06:51
`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
@tangos-validator

tangos-validator Bot commented Sep 1, 2026

Copy link
Copy Markdown

✅ PR validation — Passed

Committed merge passes; 5 contributor credit reassignment(s) noted, not a blocker.

Full merge validation

Check Result
Committed test merge yes
Byte-verified functions 10,704 / 11,347 (94.33%, -4)
Byte-verified code bytes 1,993,536 / 2,211,124 (90.16%, -200)
Claimed, not byte-verified 513 functions, 112,144 bytes (+4)
Perfect source moves 0 R100
Enrolled ranges (delinks complete) 11,059 functions, 2,053,148 bytes (92.86%, +0) -- differs from byte-verified by +355
Contributor credit 0 added, 5 changed, 0 lost
Relocation check 23 checked; 23 VERIFIED
Port reference check 405 checked; 0 stale
Module fidelity 106/106 exact; 100.000000% compared bytes
Code linked from verified source 11,088 functions, 2,067,148 bytes (93.49%)
Module bytes from source 2,067,148 / 3,049,600 (67.8%); 811,492 (26.6%) are data no delink entry reaches
ROM data reproduced from source 576 symbol(s) exact, 164 partial, 5 differ

Contributor credit moved (5)

Function Source Before After
ov045:0x02111d48 src/_ZN12FallBlockBfsD1Ev.cpp -> src/actors/d_a_obj_km2_fall_block.cpp tangosdev github-actions[bot]
ov045:0x02111d98 src/_ZN12FallBlockBfsD0Ev.cpp -> src/actors/d_a_obj_km2_fall_block.cpp tangosdev github-actions[bot]
ov045:0x02111dfc src/_ZN12FallBlockBfs16CleanupResourcesEv.cpp -> src/actors/d_a_obj_km2_fall_block.cpp lunavyqo github-actions[bot]
ov045:0x02111e10 src/_ZN12FallBlockBfs13InitResourcesEv.cpp -> src/actors/d_a_obj_km2_fall_block.cpp lunavyqo github-actions[bot]
ov045:0x02111e24 src/FallBlockBfs_Spawn.c -> src/actors/d_a_obj_km2_fall_block.cpp tangosdev github-actions[bot]

Byte-verified means the range carries complete in a delinks.txt, so the ROM build compiled it and compared it to the cartridge. The 513 claimed functions have a src/ file named after the symbol with no NONMATCHING banner, and nothing compiles them -- dsd fills their addresses with the ROM's own bytes. Both together are the 11,217 this project calls matched.

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 detail

All 16 changed file(s) compile to the ROM byte-for-byte with correct relocation targets.

File Symbol Result Slots checked
src/_ZN11FallBlockWf13InitResourcesEv.cpp _ZN11FallBlockWf13InitResourcesEv ✅ verified 1
src/_ZN11FallBlockWf16CleanupResourcesEv.cpp _ZN11FallBlockWf16CleanupResourcesEv ✅ verified 1
src/_ZN11FallBlockWfD0Ev.cpp _ZN11FallBlockWfD0Ev ✅ verified 1
src/_ZN11FallBlockWfD1Ev.cpp _ZN11FallBlockWfD1Ev ✅ verified 1
src/_ZN16daObjFallBlock_c15OnHitByMegaCharER6Player.cpp _ZN16daObjFallBlock_c15OnHitByMegaCharER6Player ✅ verified 1
src/_ZN16daObjFallBlock_c4KillEv.cpp _ZN16daObjFallBlock_c4KillEv ✅ verified 1
src/_ZN16daObjFallBlock_c6RenderEv.cpp _ZN16daObjFallBlock_c6RenderEv ✅ verified 1
src/_ZN16daObjFallBlock_c8BehaviorEv.cpp _ZN16daObjFallBlock_c8BehaviorEv ✅ verified 1
src/_ZN16daObjFallBlock_cD0Ev.cpp _ZN16daObjFallBlock_cD0Ev ✅ verified 1
src/_ZN16daObjFallBlock_cD1Ev.cpp _ZN16daObjFallBlock_cD1Ev ✅ verified 1
src/actors/FallBlockBbh/_ZN12FallBlockBbh13InitResourcesEv.cpp _ZN12FallBlockBbh13InitResourcesEv ✅ verified 1
src/actors/FallBlockBbh/_ZN12FallBlockBbh16CleanupResourcesEv.cpp _ZN12FallBlockBbh16CleanupResourcesEv ✅ verified 1
src/actors/FallBlockBbh/_ZN12FallBlockBbhD0Ev.cpp _ZN12FallBlockBbhD0Ev ✅ verified 1
src/actors/FallBlockBbh/_ZN12FallBlockBbhD1Ev.cpp _ZN12FallBlockBbhD1Ev ✅ verified 1
src/actors/d_a_obj_km2_fall_block.cpp _ZN21daObjKm2_Fall_Block_cD1Ev + _ZN21daObjKm2_Fall_Block_cD0Ev + _ZN21daObjKm2_Fall_Block_c16CleanupResourcesEv + _ZN21daObjKm2_Fall_Block_c13InitResourcesEv + daObjKm2_Fall_Block_c_Spawn ✅ verified 5
src/actors/daObjFl_Fall_Block_c.cpp _ZN20daObjFl_Fall_Block_cD1Ev + _ZN20daObjFl_Fall_Block_cD0Ev + _ZN20daObjFl_Fall_Block_c16CleanupResourcesEv + _ZN20daObjFl_Fall_Block_c13InitResourcesEv ✅ verified 4

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.

andrewboudreau and others added 2 commits September 1, 2026 07:26
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
andrewboudreau and others added 3 commits September 1, 2026 10:23
…e-tu

# Conflicts:
#	attribution.json
#	notes/cpp-tu-current-state.md
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@andrewboudreau
andrewboudreau merged commit e5e84e8 into main Sep 1, 2026
11 checks passed
@andrewboudreau
andrewboudreau deleted the cpp/fallblockbfs-snake-tu branch September 1, 2026 17:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant