Files
BFM-decomp/cookbook/C0288.md
T

12 KiB
Raw Blame History

§267 — ADDENDA HARVESTED FROM WAVES at/bh/bk/bl (P31 S59b)

Eleven amendments to existing sections, distilled from 165 byte-gate-banked harvest notes (145 of which were already covered — the harvest is mostly re-derivation, which is itself the finding). Each block names the section it amends, so a grep for that § finds the amendment too. Six of the eleven carry a fresh match_one A/B; the rest are artifact-verified. Sources and per-candidate dispositions: docs/sunset/distill/atbhbkbl.md.

ADD-1 → §231 addendum (also cross-ref from §195-D) — THE MASKED-jal "MISSING CALL" ILLUSION

A relocation-masked jal can render in match_one's diff pane as if the call were DELETED — the aligner shows the slot's instruction where you expect the jal, and a drafter reads "my call didn't emit" (several near-15/17 verdicts on func_80188770 were exactly this misdiagnosis, in both its wave notes). The call was always there; §195-D's mask_for returns 0 for every j/jal word. The 20-second probe: compile a one-line body f(p){g(p);} and diff — if the jal appears there, it was never missing in your draft either. Read the masked columns as alignment, not absence.

ADD-2 → §42a addendum — A SHARED CONSTANT NAMED IN A LOCAL ACROSS A jal IS AN ISO→TU DRIFT HAZARD; WRITE BARE LITERALS

§42a lever 4 documents const-load sinking past a call, but not this: a s32 k = 0x40; reused on both sides of a jal keeps a pseudo live across the call, giving global-alloc the choice between a callee-saved home and rematerialising li — a choice that is stable in isolation and can flip under the real TU's ambient pressure (the §42a iso-MATCH→real-TU class). Bare literals at each use delete the freedom. Cards: func_80180820 (ov_SC03_094 — the true twin func_80181644's banked C uses bare literals; the named-local draft iso-MATCHed and failed the gate) and func_800CB2CC (md_MAIN_027 — "passing literal 1 to both calls lets gcc schedule li s0,1 after the address calc; a named const got scheduled too early"). Honesty bound: iso-side both spellings can MATCH, so this is a bank-side rule; it cannot be A/B'd by match_one alone.

ADD-3 → §236, item 10 — THE UN-DELETED INCLUDE_ASM STUB IS A DUPLICATE DEFINITION

The tenth way a byte-perfect body fails the gate, and the only one that is pure splice mechanics: the C definition lands but the function's own INCLUDE_ASM(...) line is not removed — two definitions of one symbol, whole binary red, zero instruction diff. func_80180800 (ov_SC03_030) burned a full session on body levers before the stub was found; the shard-replay framing ("wrong in ONE place") points at the body when the wrong place is the line above it. Add to §236's PROCEDURE: grep the TU for INCLUDE_ASM.*<fn> after every splice (a lingering .s file in nonmatchings/ is normal and harmless — §238; the stub line is the killer).

ADD-4 → §225-3/-4 addendum — THE MIRROR ROW: VALUE-RETURN IN THE TAKEN ARM + TRAILING BARE return 0

§225-3's exemplar puts the constant on the early-return edges and the computed value on the fall-through. The mirror shape exists and has its own spelling: when the target's guard beqz jumps to the epilogue with the zero written in its own delay slot — counter-intuitively clobbering the just-tested register (andi $v0,0xFF ; beqz $v0 ; addu $v0,$zero,$zero) — the source is if (A != 0) { return <computed>; } return 0;: guard-positive, computed predicate in the taken arm, zero on the fall-through. A/B-proven on func_8017EF64 (ov_SC01_009, 24/24): rewriting it as the early-return ladder if (A == 0) return 0; return B == 0; drifts +2 — the beqz slot empties to nop and a trailing move v0,zero block appears. The wave note also byte-refuted (a) A && B as a value (boolean homed in $s0, +sw pair), (c) the ternary cond ? B : 0 (per §195-F it re-canonicalises), and (d) an accumulator local (homed callee-saved). Reading tell worth its own grep string: a delay-slot write that clobbers the branch's own tested register is the other path's return constant.

ADD-5 → §1/I1 addendum — THE INVERTED RANGE TEST: (u32)(x-lo) >= N WITH THE ZERO-ARM AS THE TRAILING else

I1 shows only the positive layout. When the target emits sltiu $v0,(v-lo),N ; bnez → <zero-store>, the source is the NEGATED range test with the in-range body as the trailing else: if ((u32)(v - 5) >= 0x4B5) { …out-of-range arms… } else { D = 0; }. A/B-proven on func_801E28D0 (md_SC03_135, 35/35): the natural if (in-range) D = 0; else … polarity drifts −4 with 22 mismatches (beqz layout, different join). This is §247's branch-count read applied to I1 — cite both.

ADD-6 → §172b-1 / §264 addendum — SHIFT-AS-TEST: (x << 16) != 0 TESTS THE LOW HALF WITHOUT TRUNCATING THE PSEUDO

A lone sll $v0,$sN,16 feeding only a beqz/bnez, while $sN itself stays RAW for a later sh, is not a cast and not an extend-pair: the source tests the low halfword as an expression, leaving the variable's mode alone: s32 v; … if ((v << 16) != 0) { …; store(v); }. Three-way A/B on func_801E2BE0 (md_SC03_135, 56/56): the (x<<16)!=0 spelling MATCHes; (u16)v != 0 emits andi v0,s0,0xffff (1 mismatch, OPCODE-MIXED at the tail test); declaring the local u16 also emits andi. File this beside the extend-tell LANE ALIASES block at §172b so the lane label greps to it.

ADD-7 → §256 addendum — THE SINGLE-GUARD GOTO: THEN-BLOCK OUT OF LINE AT THE TAIL

§256 covers goto-dispatch. The degenerate single-guard case exists and no structured spelling reaches it: the target lays the guard's then-body after the function's return flow, entered by one forward beq, with the inline else-chain ending in j .Lepilogue. The source is an explicit goto: if (v0 == -1) goto L1; …else-work…; return; L1: …then-work…;. A/B-proven on func_801822D8 (ov_SC01_009, 36/36, banked with the goto): the structured if/else-if inlines the then-body at the head — 25 mismatches, −1. (Its decrement is also a §252-reading instance: v0 = load − 1; store; if (v0 == -1) — but per §266, that statement split is byte-inert here; the goto is the load-bearing half.) The §162 "backward j into a sibling arm" law reads the same fact from the cross-jump side; this is the forward/out-of-line face.

ADD-8 → §245 addendum — THE RE-TIE BARRIER PINS CALL-ARG SETUP TO SOURCE ORDER (construct 6)

When call N+1's two argument addius must issue at exact early slots while cheaper independent inits (q = …; i = 0;) sink below them, no statement permutation works — sched1 re-ranks them freely. Birth each argument as a local immediately after call N and nail it with the zero-byte re-tie: a0v = (s32)loc; __asm__ __volatile__("" : "=r"(a0v) : "0"(a0v)); (one per argument), then pass f(a0v, a1v). The __volatile__ asm is scheduler-immovable (§257-8's fence effect, used constructively), so the two addius hold source position while the untied inits sink. A/B-proven on func_801AEB94 (md_SC07_004, 41/41, banked with the barriers): stripping the two re-ties gives SCHEDULE-REORDER/4 — the arg addius sink to slots 12/18, q/i rise to 10/11. Boundary: this is a placement lever (§245's rule); it moves nothing across a call and adds zero bytes.

ADD-9 → §213-3 / §217 addendum — ADJACENT PRE-CALL FRAME STORES ARE ONE AGGREGATE; THE UNESCAPED SCALAR NEIGHBOR IS DEAD-STORED

§213-3 reads interleaved sh $zero runs as adjacent-array initialisers. The escape-analysis corollary bites on call setup: sh $zero,0x18($sp) ; sh $v0,0x1A($sp) ahead of a call taking $sp+0x18 is a two-element s16 buf[2] whose whole address escapes. Spelled as two scalars with &sp18 passed, gcc dead-stores the neighbor whose address never escapes. A/B-proven on func_8018117C (ov_SC03_116, 34/34): the s16 buf[2] form MATCHes; s16 sp18, sp1A; … &sp18 drifts −2 — the sp1A = 0x20 store (and its li) vanish. Reading rule: adjacent stack stores feeding one escaping address = one aggregate, never sibling scalars.

ADD-10 → §237 addendum (arity-evidence paragraph) — AN la PAIR ABOVE THE PROLOGUE sw $ra IS AN OUTGOING-ARGUMENT MATERIALISATION

Add to the asm arity tells: a lui/addiu %hi/%lo(SYM) → $aN pair issued above sw $ra — while the incoming $a0 is never touched — declares SYM the callee's argument N+1 and the raw parameter argument 1, i.e. a two-arg call even when every visible decl says void f(). func_8017EB38 (ov_SC02_005, 21 ins): lui/addiu $a1,%hi/%lo(D_801E4C50) sits two instructions above sw $ra; the banked C is func_8017EDF8(a0, p) with the atlas row a bare ?. §217's mirror covers incoming stack args read above the save; this is the outgoing face.

ADD-11 → Cross-confirmation card block (per §259's standing instruction: confirmation, not news)

Append these card references to the named sections — each was independently re-derived and byte-banked in waves bh/bk/bl:

  • §209-addendum no-local row + §226-4 INVERSE ← func_80180EFC (ov_SC04_005, bl) — un-hoisted double textual read regenerated the addu copy AND the 0x20 frame in one edit; both entries were single-card, now cross-confirmed. Its sharpening stands: a guarded value stored once should not be given a name even when guard and use are textually adjacent.
  • §223-A/-B ← func_800CB61C (bl; the slot $v0 is the PRECEDING call's return), func_800CB234, func_80180F68 (bk; pre-call store fills the slot), func_801875A4 (bk; every slot store is a source store-before-call — see Refuted #1).
  • §234 ← func_8017EDC4 (u16→ori 0xff00 / s16→addiu -0x100), func_801803F0 (s16 record element → addiu -2), func_8017FF6C (0xC800 u16→ori), func_80181DC0 (negative chain constants spelled -0xB2 for addiu) — all bh.
  • §236-2 ← func_801819A0 (bh) — tu-typed extern s32 MUST be block-scoped because sibling functions carry block-scope s16 decls of the same symbol; store through *(s16*)&.
  • §236-1/-2 (fn-ptr tables) ← func_8017E448 (bk) — the conflict class on a function-pointer TABLE extern; fleet spells void (*D[])(void), dispatch passes nothing.
  • §238 ← ~14 fresh cards across bh/bk/bl (func_801808F0, func_80181090, func_80184500, func_801816D8, func_8017F4DC, func_801857F4, func_801802CC, func_8017EE80, func_8017F7F0, func_8017E940, …) — including the sharpened tells: re-confirm the mounted oracle target's glabel
    • ins count after any session resume; the task-header asm path outranks every replayed artifact.
  • §246-3 ← func_8017F724 (bh) — pointer-chase p += 6 IV split killed by the index-loop form; §1412's for-vs-do-while note supplies the loop shape.
  • §255 ← func_8018F194 (bh) — empty case 1: break; between {0,2} roots the tree on ==1.
  • §225-3 ← func_8017E7C4 (bh) — the SAME mixed construct at the opposite constant polarity: if (c1||c2||c3) return 1; return c4;, all three li v0,1 folds landing in the failing branches' delay slots (22/22).
  • §214-addendum-10 ← func_801830B4 (bk) — a MATCHED twin's C comment carried *24 while its own bytes compute *48; diff the twin's encodings, never its comments.
  • §252-related / §172b ← func_801914A0 (bh) — signed-short temp flips the post-decrement test from andi 0xffff to sll 16.
  • §165-17-correction (L19102) ← func_80181D04 (bh) — a pure bb0 swap around a masked shift fell to retyping the temp s32→u8; statement order was inert.
  • §20 pointer-var bullet / §243 ← func_8018208C, func_80180108, func_80181230 (bh) — the held-pointer lever on an indexed global, a global RMW, and an RMW-across-store respectively; on the last, the memory-clobber barrier and volatile (both directions) were measured FAILURES — the naming is the lever.
  • §257-2 ← func_801816C8 (bh) — now A/B-proven live: the $2 pin in a rand()-calling function is byte-inert (see §266).
  • §226-addendum data point ← func_8017D710 (bk) — an address-taken s32 frame_pad[2] measured 0x10, not 8; the address-taken pad-form table's size column is CEIL(size,8) + rounding, worth a row note.
  • §220-addendum × loop-form interaction ← func_8017DF88 (bl) — only the combination "plain a0 param + for loop" reproduces the delay-slot refill; "named s1 local + for" still misses by spill. The two dials compose; A/B them jointly.