Files
BFM-decomp/cookbook/C0282.md
T

3.5 KiB
Raw Blame History

§261 — THE -O0 ORACLE: DERIVE THE OPT LEVEL FROM THE TARGET, AND $fp IS NOT THE TELL (P31 S59)

match_one --o0 existed for a year and nothing ever passed it. api_draft.match_one() — the oracle every wave agent iterates against — builds a fixed argv without it, so an agent handed an -O0 target was shown an -O2 compile of its own C and a mismatch on every instruction: feedback that cannot converge, for a reason that never appears in the diff. It hit even the functions already sitting in _o0 objects, where a match was otherwise bankable today.

DERIVE IT (R33), FROM TWO ORACLES (R34), BECAUSE NEITHER ALONE COVERS THE TREE:

  • the target's own prologue — sw $fp, N($sp) + addu $fp, $sp, $zero (21F0A003) inside the function's first instructions. gcc-2.7.2 keeps a frame pointer at -O0 and omits it at -O2.
  • the subseg's build flags — boot and every *_o0* object are compiled -O0 by the Makefile. boot/start.s is -O0 with no ordinary prologue; only this oracle sees it.

$fp MENTIONS ARE NOT THE POPULATION. $fp is $s8, an ordinary allocatable callee-saved register at -O2. Over 14,400 .s files: 311 mention $fp, 167 carry the -O0 prologue. Sizing a lane off the 311 over-counts by 1.9×. Anchor the prologue scan at glabel, not the top of the file — two md_MAIN_011 targets open with a migrated jump table / .asciz blob, and a naive "first 8 encoded lines" reads table words as the prologue and calls an -O0 function -O2.

A MATCH IS NOT A BANK FOR THIS CLASS. An -O0 function in an -O2 subseg cannot bank however perfect the body: the object's CC1FLAGS decide, and the Makefile's -O0 globs cover boot, ov_SC01_077_o0, src/ov_*/ov_*_o0.c and src/ov_*/ov_*_o0?.c — nothing matches src/md_*/. Of the 167, 51 are inside an -O0 object and 116 (14,148 ins) are stranded in -O2 subsegs, so match_one now says so on sight rather than letting an agent chase a body that can never land.

§261a — THE -O0 FRAME-RELOAD GRAMMAR: reload COUNT disambiguates the C spelling (P31 S59, byte-proven)

func_80184058 (131 ins, ov_SC03_014_o0c) banked on the FIRST compile — MATCH(131) then whole-binary BYTE-IDENTICAL — because at -O0 the number of frame reloads of a pointer is a fingerprint of the exact C spelling. Read it off the target before writing anything:

  • p->f += k (value unused) → 2 reloads of p (STORE base first, read base second), no copy.
  • --p->f / ++p->f → 3 reloads (the FIRST is dead), an addu rd,rs,$zero copy after the add, and — only when the value is used — sll 16; sra 16 on the STORED register (no re-load). So if (--p->f == 0) vs p->f -= 1; if (p->f == 0) (re-loads the field) vs p->f -= 1; (2 reloads, no copy) are all distinguishable from the asm alone.
  • s16 fields: lh at a plain read, but lhu inside a read-modify-write.
  • Overlapping s32/s16 (a 16.16 accumulator at 0xE0 whose high half is read as the s16 at 0xE2): spell the wide access through a SECOND struct view and cast the pointer — ((Ent_fx *)p)->unkE0 += 0x5CCCC; — the cast is free at -O0; no union needed.
  • §127a compounded: the decisive spellings (the call-arg marshaling, ++p->unk02) were lifted VERBATIM from banked siblings in the same _o0* TU. Read the TU's banked bodies first, always.

With the §261 auto-oracle + this grammar, an -O0 function is STRICTLY easier than -O2 of the same size: no scheduling, no regalloc steering, no permuter — every miss is a spelling miss, and the spelling is enumerable from the reload count.