Files
BFM-decomp/cookbook/C0040.md
T

5.6 KiB
Raw Blame History

§37 — The T7 §G giant endgame: 6/8 cracked, meta-laws + transferable levers (Phase 24, 2026-07-07; FULL byte-verified detail + gcc-2.7.2 line cites in docs/gcc-2.7.2-map/t7g-giant-harvest.md)

Cracked 6 of 8 reach-134 §G giants — func_801571C4 (permuter), func_8014EA4C/func_801372B0/func_801770E0/func_80176D94/func_80148094 (Fable5) — plus 2 genuine walls: func_80178004 (biv-init emit-order, close=7) and func_801412A8 (allocation placement-knot, close=29), both stay INCLUDE_ASM. 4 propagated ×134 (fleet 65.75→65.91%, clean 136/136); 372B0/770E0 banked ×1 (blocked ×134 by the §A pin/asm + local-type self-containment gaps).

META-LAWS (audit BEFORE deep work):

  1. SIBLING-ISOMORPHISM (mandatory step-0): GPU-packet/addPrim builders come in FAMILIES. Diff your target .s MNEMONIC sequence vs already-MATCHED siblings (grep -oP '\*/\s+\K\S+'); an immediates-only diff ⇒ PORT the banked sibling verbatim + swap constants = one-shot (76D94 fell from 770E0 in ~15 min).
  2. "MUTUAL-EXCLUSION / RC-6 unsteerable" IS A TELL, NOT A VERDICT: "X must be early AND late, coupled through one priority number / every small edit moves 20+ insns" = a MISSING dep edge or an allocno TIE — real only in the broken graph. 770E0 (the named RC-6 exemplar) fell fully source-reachable.
  3. RTL DUMPS ARE NOT STRIPPED (corrects §34): the shipped cc1 with -dr -dj -dc -dl -dg emits full .rtl/.jump/.combine/.lreg/.greg — readable ground truth before gdb (.lreg refs/length, .greg alloc-order/conflicts/preferences, (use (reg:SI in .combine = phantom stack slots).

TRANSFERABLE LEVERS (byte-verified):

  • The /s-DEP LATTICE (load+store dual): force MEM_IN_STRUCT_P via a struct-member-at-offset-0 access — ((struct{u16 h;}*)&D_global)->h (load, 770E0) / ((struct{u32 w;}*)p)->w=… (store, 76D94) — to restore the missing store↔load dep edge (sched.c:820 drop clause needs one side /s+varying, the other non-/s+FIXED-address). SCOPE: inapplicable when all mem ops are register-addressed (412A8/48094).
  • Allocno-priority ref-boost (48094): __asm__("" :: "r"(v)); at the TOP of a block where v is already live-through → +1 flow-ref, 0 live-range, 0 bytes; crosses the floor_log2(refs) step in allocno_compare (global.c:588) → v wins the reg over a short block temp.
  • Coalescable-copy insn_count bump (78004): whole reg-file shifted by one hoisted loop const ⇒ move_movables' threshold at its >= boundary — a coalescable copy {u32 m=v; store=m|…;} bumps insn_count +1 → hoist refused; combine coalesces m away = 0 bytes. BANKABLE (vs a TU-wide global-register-var).
  • Const-register PIN cascades store order (770E0): store order is downstream of a const's register via sched2 anti-webs → pin the const (register u32 c __asm__("$6")=…), stores re-place free.
  • S2-kill re-tie (372B0): __asm__("":"=r"(v):"0"(v)) after last use → reg_n_sets==2 → no birthing boost → source order (single-set pins, INCL. hard-reg, DO boost — corrects §34). + S2 fire-tick via consumer store order.
  • qty_compare-TIE audit (412A8): dozens of "schedule" diffs often trace to ONE equal-priority allocno tie (find_free_reg, gdb) broken by qty/block-scan order — a window-temp reuse flips it. + multi-death block-var law (local-alloc.c:472: reg_n_deaths≠1 → global allocno → chaos).
  • CSE-dodge without a barrier: (u16)x (zero_extend) head vs x&0xffff (AND) tail hash differently → no cross-call CSE → no extra callee-save. Prefer over volatile-asm re-ties (a FULL sched barrier per sched.c ASM_OPERANDS).

BANKING PATTERNS (main + _a split): caller-extern reconcile (void→canonical return, 571C4); asm-label alias for sibling-decl signedness/proto conflicts (extern u16 X __asm__("D_x"), 372B0) — beats reconcile's *(u16*)&D_x cast whose address-of PERTURBS regalloc; canonical call-cast + anonymize the shared /s struct for isomorphic-sibling ports (76D94). ×134 blockers (§A gaps → tool fixes): register-asm pins + overlay-local named types fail dedup_propagate.compiles_standalone → bank ×1 (needs a pin/asm self-containment shim + a uniquely-renamed type-lift).

TOOLING (flywheel): fixed a silent permuter bug — tools/p16_permute.py header comments broke cpp → decomp-permuter no-op'd (0s) on EVERY commented draft (strip_c_comments); a SECOND silent no-op of the same class (Phase 26 session 6): a draft whose GTE ops are #defines CONTAINING __asm__ (the PsyQ inline_c.h convention — i.e. most renderer code) got its macro DEFINITIONS chewed up by hide_asm (which is built for __asm__ statements / register pins and scans to the nearest ;{}), swallowing the function itself → pycparser Function <fn> not found in base.c → permuter no-op (0s). Fix: cpp_expand_macros() pre-expands with cpp -P so each GTE op becomes an inline __asm__ statement hide_asm can carry — applied ONLY when a #define ... __asm__ is present, so macro-free drafts are byte-untouched. LESSON: the permuter reporting no match (0s) is a TOOLING failure signature, never a real search result — always confirm workers actually ran. p16_permute also gained --asm-subdir (it was hardcoded to ov_SC01_077's main object, so no core in another overlay/split object could be permuted at all); NEW tools/permuter_ils.py (warm-restart iterated-local-search — descends where a cold run plateaus: 48094 72→29). Escalation: model to close=2 by hand, THEN directed-permuter the residual (372B0's last lever fell at permuter iter 291; permuter is low-ROI at close=8).