Files
BFM-decomp/cookbook/C0036.md
T

3.3 KiB
Raw Blame History

§33 — Automating the giant decl-reconcile: tools/reconcile_decls.py (the DATA analog of §20's cast_call_sites) + a fleet-majority type oracle (Phase 24 T7b, byte-proven on func_80129CF8)

The gap. A freshly-matched giant/wave draft byte-matches STANDALONE with its own guessed decls, but to bank ×1 (and then propagate ×134 via a shared DEFINE_func_* macro that EMBEDS its externs) its callee/data externs must be FLEET-CANONICAL, else conflicting types in-TU. cast_call_sites (§20) does this for callee FUNCTIONS; reconcile_decls does it for DATA symbols D_XXXX — the class the giants hit (they hoist global base ARRAYS, §32). It was the last manual step in the giant pipeline.

The oracle (fleet-majority type picker) — new; none existed (gen_harvest_targets read only engine_core.h + ONE overlay, first-seen-wins, never saw a fleet disagreement). reconcile_decls.canonical_data_map(): an engine_core.h macro decl is AUTHORITATIVE (the shared/propagated set every overlay co-instantiates — conform to it and the macro can't conflict per-overlay); else the plurality across ALL overlays + resident.c (tie → lexicographically-first spelling, deterministic). Verified 8/8 on func_80129CF8's symbols (--print-canon D_XXXX inspects one).

Byte-neutral cast taxonomy (the DRAFT's decl type = the intended access; cast every use to reproduce it under the CANONICAL storage decl → gcc folds the compile-time cast → identical opcode; the whole-binary byte-gate is the sole arbiter, fail-closed):

draft decl canonical decl → access →
array Ed[] array Ec[] (Ed≠Ec) Ec[] D_x → (Ed*)D_x; D_x[i] → ((Ed*)D_x)[i]
array Ed[] struct / scalar canon D_x → (Ed*)&D_x (address-of the object)
scalar Td scalar Tc (signed/width) Tc D_x → *(Td*)&D_x (forces the Td opcode, e.g. lbu under s8)
ptr P* D_x scalar Tc Tc D_x → (*(P **)&D_x) (cast-lvalue: store/read a pointer in the scalar slot)
Implementation: ONE re.sub per symbol with a replacement fn (captures the optional leading & / following [), single-pass so a symbol in several forms on one line can't double-wrap (the first draft double-wrapped the ptr-store — the two-sub read+write templates re-matched; collapsed to one pass).

Pipeline placement: canon_resident_calls → cast_call_sites → reconcile_decls → sig_unify → harvest_verify --chunk 1 (wired into gate_stage; idempotent / no-op on drafts without a data conflict → can't regress the wave). Standalone giant use: tools/reconcile_decls.py --overlay <ov> --src-file src/<ov>/<ov>_a.c --in <drafts> --out <drafts>-rc.

Byte-proof (T7b, no Fable5 needed): the full loose func_80129CF8 (intended decls s32 D_80126948[] … , un-cast accesses) → reconcile_decls (5 symbols reconciled) → swapped into the DEFINE_ macro → make build BINARY=ov_SC01_077 = BYTE-IDENTICAL d19c9580.

R14 — the tool is an AUTOMATION, not an "unlock": func_80129CF8 banks ×134 FREE via existing dedup_propagate --recover once reconciled (the "×134 wall" was a stale-asm misdiagnosis, §32). reconcile_decls' value is removing the manual ×1-reconcile step for every future giant / loose draft (the 6 sibling giants + the wave tail), not unblocking propagation.