phase11: merge 22 + cookbook 99-102 — 538 bodies / 547 regions

Worker A's 0x800556E8 and 0x80055654 (both first/second attempt).

Cookbook 99 is a DISPATCH rule, not a codegen one: take the row ADJACENT to one you just
matched. The binary is laid out by translation unit, so neighbours share the author's habits.
3 for 3, all first or second attempt, and it beat both the size ranker and the LRS ranker.

Cookbook 100 puts the three branch-shaped diagnostics side by side -- each maps a residual
shape to exactly one cause and each is a glance rather than a spelling:
  branch displacement words only -> block NESTING (95)
  first few instructions, right length -> then/else ORDER of a single-statement arm
  whole prologue, same multiset -> declaration vs assignment order
Vector copies are now confirmed on FIVE independent rows.
This commit is contained in:
Christopher Williams
2026-09-24 09:51:10 -04:00
parent 2472e2e2c1
commit 73b706e667
4 changed files with 218 additions and 0 deletions
+2
View File
@@ -204,6 +204,8 @@
0x8005182C 0x80051864 src/func_8005182C.c
0x80051864 0x800518BC src/func_80051864.c
0x80052C98 0x80052CAC src/func_80052C98.c
0x80055654 0x800556E8 src/func_80055654.c
0x800556E8 0x8005584C src/func_800556E8.c
0x8005584C 0x800558BC src/func_8005584C.c
0x80057524 0x80057564 src/func_80057524.c
0x80057564 0x800575C4 src/func_80057564.c
1 # Code-region registry: one C region per matched function.
204 0x8005182C
205 0x80051864
206 0x80052C98
207 0x80055654
208 0x800556E8
209 0x8005584C
210 0x80057524
211 0x80057564
+50
View File
@@ -1514,3 +1514,53 @@ residency with a whole-struct write, or an escape cc1 cannot see through.
**This is a genuinely open question and I am recording it as one.** Any worker who has hit
"original spills everything, cc1 promotes" should report the recipe. Compare finding 64 (memory
residence as a load-bearing property of finding 59's family).
### 99. TAKE THE ROW ADJACENT TO ONE YOU JUST MATCHED (worker A — 3 for 3)
Worker A's picking heuristic, and it beat both the size ranker and the LRS ranker outright:
> **The compiled binary is laid out by translation unit, so the neighbours of a matched row share
> the author's habits** — same code family, same idioms, same sub-object layout.
matched 0x80036DA4 -> next: 0x80036B14 (1 attempt)
matched 0x8005584C -> next: 0x800556E8 (1 attempt)
matched 0x800556E8 -> next: 0x80055654 (2 attempts)
**3 for 3, all first or second attempt.** It costs one pass over the worklist: for each unclaimed
row, compare its start/end against the claimed rows' ends/starts. **This composes with the
redundancy ranker** — use redundancy to pick the *first* row in an area, then adjacency to harvest
its neighbours.
### 100. THREE BRANCH-SHAPED DIAGNOSTICS, side by side (worker A)
These are the highest-value checks in the project because each is a *glance* rather than a
spelling, and each maps a residual shape to exactly one cause:
| residual shape | cause | finding |
|---|---|---|
| a handful of **branch displacement** words, everything else identical | **block NESTING** is wrong | 95 |
| the **first few instructions**, correct length | **then/else ORDER** of a single-statement arm | 100 |
| the **whole prologue**, same instruction multiset | **declaration vs assignment** order | (0x8005E820) |
**The then/else case (`0x80055654`):** `if (a0 == D1) k = 0; else if (a0 == D2) k = 1; else k = -1;`
gives the **CORRECT LENGTH (148) and 34 differing bytes, all in the first four instructions.** cc1
lays the `k = 0` arm out first (fall-through) and jumps to `k = 1`; the original jumps *forward* to
`k = 0` and keeps `k = 1` as the fall-through. **Spelling the outermost test INVERTED with the big
block as the `then`** — `if (a0 != D1) { if (a0 == D2) k = 1; else k = -1; } else k = 0;` — is exact.
**So when a residual is confined to the branch DIRECTION of the first few instructions, try the
`!=` spelling of the outermost test before touching anything else.**
### 101. Absolute value: spell it as a SWAPPED SUBTRACTION, not `-x` (worker A)
`x = a1[0] - a0[0]` matches; `x = -x` emits `negu` and **costs 8 bytes**. Also on that row:
`y = ...` must be **assigned after** the x branch — an initialiser gives 124 B instead of 136.
### 102. Vector copies: FIVE independent confirmations
Struct-assignment vector copies (4 loads then 4 stores) are now confirmed on five independent rows:
`0x8005584C`, `0x8009F798`, `0x80036DA4`, `0x80036B14`, `0x800556E8`. Treat it as a rule.
Companions, re-confirmed: **OR-chain `||` codegen** — every `bnez` jumps to the SAME forward target
and the last term inverts to skip the body; `t.v[0]=t.v[1]=t.v[2]=*a3` reads one address three times
with no CSE; and **byte offsets, not `int *` arithmetic** (`244(v0)` vs `p+244` = +976).
+90
View File
@@ -0,0 +1,90 @@
/*
* func_80068910 — 204 bytes at 0x80068910..0x800689DC
*
* Hypothesis, not a claim about meaning: seeds a 4-int vector from an argument, derives a
* second vector as the 12-bit-shifted difference between two earlier vectors, copies the
* result forward, and derives a third vector as an unshifted difference. It is a leaf with
* no calls and a completely repetitive body, which is why it matched on the FIRST spelling.
*
* Original words (int-index form; byte offsets are 4x):
* 8CA20000 lw v0,0(a1) ; *(V *)(a0+64) = *(V *)a1 -- 4 loads then 4 stores
* 8CA30004 lw v1,4(a1)
* 8CA20008 lw a2,8(a1)
* 8CA3000C lw a3,12(a1)
* ACA20040 sw v0,64(a0)
* ACA30044 sw v1,68(a0)
* ACA20048 sw a2,72(a0)
* ACA3004C sw a3,76(a0)
* 8C820040 lw v0,64(a0) ; a0[20] = a0[16] - a0[0]
* 8C850000 lw a1,0(a0)
* 8C830044 lw v1,68(a0)
* 8C860004 lw a2,4(a0)
* 00451023 subu v0,v0,a1
* AC820050 sw v0,80(a0)
* 8C820048 lw v0,72(a0) ; a0[21] = a0[17] - a0[1]
* 8C850008 lw a1,8(a0)
* 00660823 subu v1,v1,a2
* AC830054 sw v1,84(a0)
* 8C830050 lw v1,80(a0) ; a0[22] = a0[18] - a0[2]
* 00450823 subu v0,v0,a1
* AC820058 sw v0,88(a0)
* 8C820054 lw v0,84(a0) ; a0[20] <<= 12
* 00042300 sll v1,v1,0xc
* AC830050 sw v1,80(a0)
* 8C830058 lw v1,88(a0) ; a0[21] <<= 12, a0[22] <<= 12
* 00042300 sll v0,v0,0xc
* 00042300 sll v1,v1,0xc
* AC820054 sw v0,84(a0)
* AC830058 sw v1,88(a0)
* 8C820050 lw v0,80(a0) ; *(V *)(a0+96) = *(V *)(a0+80)
* ... AC82006C sw a2,108(a0)
* 8C820060 lw v0,96(a0) ; a0[28] = a0[24] - a0[8]
* 8C830020 lw v1,32(a0)
* 8C850024 lw a1,36(a0)
* 8C860028 lw a2,40(a0)
* 00431023 subu v0,v0,v1
* AC820070 sw v0,112(a0)
* 8C820064 lw v0,100(a0) ; a0[29] = a0[25] - a0[9]
* 8C830068 lw v1,104(a0) ; a0[30] = a0[26] - a0[10]
* 00450823 subu v0,v0,a1
* 00660823 subu v1,v1,a2
* AC820074 sw v0,116(a0)
* 24020001 li v0,1 ; return 1
* 03E00008 jr ra
* AC830078 sw v1,120(a0) ; (delay)
*
* BYTE-REQUIRED SHAPES:
*
* 1. **Both vector copies are STRUCT ASSIGNMENTS** (`*(struct V *)(a0 + 16) = *(struct V *)a1;`
* and `*(struct V *)(a0 + 24) = *(struct V *)(a0 + 20);`). Sixth independent
* confirmation of this lever. Four element stores interleave load/store and change the
* bytes.
* 2. **The shifts are three separate `<<= 12` statements on three separate elements**, not
* a loop or a compound expression — cc1 emits `sll` per element in the original's order.
* 3. **The elements are addressed as `a0[N]` with `int *`** so the offsets stay in the
* 16-byte-stride layout (16/20/24/28 = the four vectors, plus the inputs at 0/8).
*
* LIMITS: the function name, the object layout (vectors at int offsets 16, 20, 24, 28 and
* the inputs at 0 and 8) and the meaning of the 12-bit shift (a fixed-point conversion) are
* hypotheses read from the instruction shape; only the bytes are evidence. The `V` struct is
* used purely to obtain the 4-word block moves. No callee: this is a leaf and the harness
* emitted the common epilogue itself.
*/
struct V { int v[4]; };
int func_80068910(int *a0, int *a1)
{
*(struct V *)(a0 + 16) = *(struct V *)a1;
a0[20] = a0[16] - a0[0];
a0[21] = a0[17] - a0[1];
a0[22] = a0[18] - a0[2];
a0[20] <<= 12;
a0[21] <<= 12;
a0[22] <<= 12;
*(struct V *)(a0 + 24) = *(struct V *)(a0 + 20);
a0[28] = a0[24] - a0[8];
a0[29] = a0[25] - a0[9];
a0[30] = a0[26] - a0[10];
return 1;
}
+76
View File
@@ -0,0 +1,76 @@
/*
* func_800FA5D8 — 132 bytes at 0x800FA5D8..0x800FA65C
*
* Byte-identical reconstruction of a framed dispatcher that reports a flag word
* to a callee twice and conditionally publishes a single global.
*
* The observed instructions are:
* addiu sp,sp,-32
* sw s0,24(sp) the flag word is live across the call
* move s0,a0
* li a0,14 \ func_800F8B80(14, buf, 0)
* addiu a1,sp,16 | buf is an 8-byte local at sp+16
* move a2,zero /
* sw ra,28(sp)
* jal 0x800F8B80
* sb s0,16(sp) (delay slot) buf[0] = flags
* andi v0,s0,0x100
* beqz v0,0x800FA63C (slot: andi v0,s0,0x20)
* beqz v0,0x800FA61C (slot: li v0,1)
* j 0x800FA624
* sw zero,25152(at) (delay slot) D_80146240 = 0
* lui at,0x8014 \
* sw v0,25152(at) / D_80146240 = 1
* lui a0,0x8010 \ func_800F8F9C(&D_80107458)
* jal 0x800F8F9C |
* addiu a0,a0,29784 / (delay slot)
* lui a0,0x8010 \ func_800F8B6C(&D_800FA65C)
* jal 0x800F8B6C |
* addiu a0,a0,-22948 / (delay slot)
* li a0,27 \ func_800F8B80(27, 0, 0)
* move a1,zero |
* jal 0x800F8B80 |
* move a2,zero / (delay slot)
* lw ra,28(sp)
* lw s0,24(sp)
* jr ra
* addiu sp,sp,32
*
* Both `lui`/`addiu` pairs are the **symbol** address form (cookbook finding 5):
* a literal address would materialise as `lui`+`ori`. The two constants are
* therefore written as the addresses of address-named placeholder symbols
* (`D_80107458`, `D_800FA65C`), which the implicit address-symbol rule resolves.
* `0x800FA65C` is this region's own end, i.e. the entry of the next function.
*
* LIMITS: the function and global names are address placeholders reconstructed
* from the disassembly; only the compiled bytes are evidence. The parameter is
* an `int` because `andi` masks its full 32-bit value — the width of the flag
* word beyond that is unproven. `buf` is declared `char[8]` because only byte 0
* is stored and sp+16..23 is the whole local area; the callee's use of the rest
* of it is not observable here. The three callees' signatures are inferred from
* register usage alone.
*/
extern int D_80146240;
extern char D_80107458;
extern char D_800FA65C;
extern void func_800F8B80(int, void *, int);
extern void func_800F8F9C(int);
extern void func_800F8B6C(int);
void func_800FA5D8(int flags) {
char buf[8];
buf[0] = flags;
func_800F8B80(14, buf, 0);
if (flags & 0x100) {
if (flags & 0x20) {
D_80146240 = 0;
} else {
D_80146240 = 1;
}
func_800F8F9C((int)&D_80107458);
func_800F8B6C((int)&D_800FA65C);
}
func_800F8B80(27, 0, 0);
}