From 73b706e6678857ea26e407a445c38215d77f7a42 Mon Sep 17 00:00:00 2001 From: Christopher Williams Date: Thu, 24 Sep 2026 09:51:10 -0400 Subject: [PATCH] =?UTF-8?q?phase11:=20merge=2022=20+=20cookbook=2099-102?= =?UTF-8?q?=20=E2=80=94=20538=20bodies=20/=20547=20regions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- config/regions.tsv | 2 + docs/MATCHING_COOKBOOK.md | 50 ++++++++++++++++++++++ src/func_80068910.c | 90 +++++++++++++++++++++++++++++++++++++++ src/func_800FA5D8.c | 76 +++++++++++++++++++++++++++++++++ 4 files changed, 218 insertions(+) create mode 100644 src/func_80068910.c create mode 100644 src/func_800FA5D8.c diff --git a/config/regions.tsv b/config/regions.tsv index 9885514..22dc2f8 100644 --- a/config/regions.tsv +++ b/config/regions.tsv @@ -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 diff --git a/docs/MATCHING_COOKBOOK.md b/docs/MATCHING_COOKBOOK.md index e9cca4d..a447331 100644 --- a/docs/MATCHING_COOKBOOK.md +++ b/docs/MATCHING_COOKBOOK.md @@ -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). diff --git a/src/func_80068910.c b/src/func_80068910.c new file mode 100644 index 0000000..7ded4af --- /dev/null +++ b/src/func_80068910.c @@ -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; +} diff --git a/src/func_800FA5D8.c b/src/func_800FA5D8.c new file mode 100644 index 0000000..d9d5100 --- /dev/null +++ b/src/func_800FA5D8.c @@ -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); +}