From 1a6a00970fd36cfd9e54516cdc5aea47401fd37c Mon Sep 17 00:00:00 2001 From: Christopher Williams Date: Thu, 24 Sep 2026 09:38:05 -0400 Subject: [PATCH] =?UTF-8?q?phase11:=20merge=2014=20+=20cookbook=2079-81=20?= =?UTF-8?q?=E2=80=94=20worker=20A's=20redundancy=20ranker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Worker A built a repetitiveness score (repeated 2/3/4-instruction opcode subsequences, normalised by body length) and produced the cleanest controlled comparison in the phase: 3 spellings on a 248 B repetitive row vs 9 failures on a 176 B tie-break-dense one. That converts 'prefer a repetitive body' from a hunch into a sortable number, so size is deprioritised as the ranking signal. Also recorded: a transposed temp array is byte-required (int m[3][4] used as m[c][r]) with an exact diagnostic -- right length + right instruction multiset + residual only on sp-relative offsets means the frame LAYOUT is wrong, not the code; and when the original stores the same slot twice, suspect two source statements rather than a scheduler quirk (GCC 2.7.2 has no DSE). --- config/regions.tsv | 1 + docs/MATCHING_COOKBOOK.md | 35 ++++++++++++++ src/func_8009C904.c | 96 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 src/func_8009C904.c diff --git a/config/regions.tsv b/config/regions.tsv index 43d88d3..19ad12d 100644 --- a/config/regions.tsv +++ b/config/regions.tsv @@ -359,6 +359,7 @@ 0x8009F3A8 0x8009F4B4 src/func_8009F3A8.c 0x8009F5AC 0x8009F6A0 src/func_8009F5AC.c 0x8009F6A0 0x8009F798 src/func_8009F6A0.c +0x8009F798 0x8009F890 src/func_8009F798.c 0x800A2F20 0x800A2F44 src/func_800A2F20.c 0x800A34E8 0x800A3540 src/func_800A34E8.c 0x800A45E0 0x800A466C src/func_8009E8D0.c diff --git a/docs/MATCHING_COOKBOOK.md b/docs/MATCHING_COOKBOOK.md index c0e046a..0d72ed3 100644 --- a/docs/MATCHING_COOKBOOK.md +++ b/docs/MATCHING_COOKBOOK.md @@ -1274,3 +1274,38 @@ off) shows the RTL order is stores-then-copy for worker A's GTE row, so that res And a byte-required operand order: `a0 == D_8012204C` matches where `D_8012204C == a0` does not (worker A, `0x800B62C8`, 2 bytes). + +### 79. REDUNDANCY-DENSITY IS THE RANKER, AND IT IS NOW MEASURABLE (worker A) + +Worker A built a repetitiveness score over its partition — **count of repeated 2/3/4-instruction +opcode subsequences, normalised by body length** — and used it to pick the densest mid-size +row. The result is the cleanest controlled comparison in the phase: + +| row | size | rep-density | outcome | +|---|---|---|---| +| `0x8009F798` | 248 B, 0 calls | **2.10** | **3 spellings → MATCH** | +| worker D's `0x8009F798`-sized row | 248 B | — | 4 spellings | +| `0x80017C6C` | 176 B | tie-break-dense | **9 spellings, 42 differing bytes, classified** | + +**3 spellings on a 248 B repetitive row against 9 failures on a 176 B tie-break-dense one.** +This is finding 68 made operational: it converts "prefer a repetitive body" from a hunch into a +number you can sort by. Size is deprioritised. + +### 80. A TRANSPOSED temp array, and the exact diagnostic (worker A) + +`0x8009F798`: the original's slot for matrix cell (r,c) is `4*r + 16*c` — a **16-byte stride on +the SECOND index** — so `int m[3][4]` must be written `m[c][r]`. Natural `m[r][c]` indexing gives +**CORRECT LENGTH (248) and 15 differing bytes, every one of them a stack offset.** + +**Diagnostic (cookbook 59's cleanest instance):** *right length + right instruction multiset + +residual only on sp-relative offsets ⇒ the frame LAYOUT is the error, not the code.* + +### 81. When the original stores the same slot TWICE, suspect TWO SOURCE STATEMENTS (worker A) + +`0x8009F798`: phase 1 stores the un-negated value into all 9 slots; phase 2 negates 6 of them and +stores **again** into the same slots. GCC 2.7.2 has no DSE, so both stores appear. Writing +`m[c][r] = -a0[3*r + c];` directly (one statement per cell) is **244 B LENGTH-MISMATCH** — the +negations then land immediately after their own loads. + +Same property as worker A's `buf[1]` finding in `0x80027CA0` (`buf[1] = a0[1]-a1[1];` then +`buf[1] = 0;`), now confirmed on a much larger body. **Not a scheduler quirk — a second statement.** diff --git a/src/func_8009C904.c b/src/func_8009C904.c new file mode 100644 index 0000000..db92f02 --- /dev/null +++ b/src/func_8009C904.c @@ -0,0 +1,96 @@ +/* + * func_8009C904 — 548 bytes at 0x8009C904..0x8009CB28 + * + * Hypothesis, not a claim about meaning: rotates three 3-component short vectors in + * place and offsets each result by a component of a shared vector. The body is the same + * 30-instruction block three times over (for a0[6], a0[7], a0[8]) with only the vector + * pointer changing, which is why it matched on the FIRST spelling. + * + * Per-block shape (s0 = a0, s1 = a1; buf is the 3-word scratch at sp+16): + * lw v0,24(s0) ; a0[6] <- reloaded for EVERY component read + * lh v0,0(v0) + * sw v0,16(sp) ; buf[0] = *(short *)(a0[6] + 0) + * lw v0,24(s0) + * lh v0,2(v0) + * sw v0,20(sp) ; buf[1] + * lw v0,24(s0) + * lh v0,4(v0) + * sw v0,24(sp) ; buf[2] + * move a0,s1 ; func_800F3C60(a1, buf, buf) + * addiu a1,sp,16 + * move a2,a1 + * jal 0x800F3C60 + * sw v0,24(sp) ; (delay) + * lw a0,16(sp) ; buf[0] += a1[5] + * lw v0,20(s1) + * addu a0,a0,v0 + * sw a0,16(sp) + * ... ; buf[1] += a1[6], buf[2] += a1[7] + * lw v0,24(s0) ; *(short *)(a0[6] + 0) = buf[0] + * sh a0,0(v0) + * ... ; +2 and +4 + * then the identical block for a0[7] (offset 28) and a0[8] (offset 32), then the common + * epilogue (`lw ra,40(sp); lw s1,36(sp); lw s0,32(sp); addiu sp,sp,48; jr ra; nop`). + * + * THREE THINGS ARE BYTE-REQUIRED: + * + * 1. **The vector pointer is re-read from `a0[i]` for every single component** — six + * `lw v0,24(s0)`-style loads per block (three for the reads, three for the writes). + * Binding it once into a local (`int *p = (int *)a0[6];`) hoists the load and changes + * the bytes. So the source indexes the array expression inline at every use. + * 2. **The scratch buffer is `int buf[3]`**, not `short buf[3]`: the components are loaded + * with `lh` but stored with `sw` into word-spaced slots at sp+16, sp+20, sp+24. + * 3. **`func_800F3C60` is called with the SAME buffer as its second and third argument** + * (`move a0,s1; addiu a1,sp,16; move a2,a1`) — an in-place transform. + * + * The 548-byte body matching first attempt, against a 176-byte body that resisted nine + * spellings, is a clean instance of the phase-11 rule that redundancy predicts matchability + * far better than size does. + * + * LIMITS: the function name, the callee, the array offsets (6/7/8 for the vectors and + * 5/6/7 for the offsets), the element types (short components, word scratch) and the whole + * "vector rotate" reading are hypotheses taken from the instruction shape; only the bytes + * are evidence. The three blocks are written out explicitly because the original is + * unrolled — there is no loop in the object. func_800F3C60 is not registered and is + * referenced by its address-named spelling. + */ + +void func_800F3C60(int *, int *, int *); + +void func_8009C904(int *a0, int *a1) +{ + int buf[3]; + + buf[0] = *(short *)(a0[6] + 0); + buf[1] = *(short *)(a0[6] + 2); + buf[2] = *(short *)(a0[6] + 4); + func_800F3C60(a1, buf, buf); + buf[0] += a1[5]; + buf[1] += a1[6]; + buf[2] += a1[7]; + *(short *)(a0[6] + 0) = buf[0]; + *(short *)(a0[6] + 2) = buf[1]; + *(short *)(a0[6] + 4) = buf[2]; + + buf[0] = *(short *)(a0[7] + 0); + buf[1] = *(short *)(a0[7] + 2); + buf[2] = *(short *)(a0[7] + 4); + func_800F3C60(a1, buf, buf); + buf[0] += a1[5]; + buf[1] += a1[6]; + buf[2] += a1[7]; + *(short *)(a0[7] + 0) = buf[0]; + *(short *)(a0[7] + 2) = buf[1]; + *(short *)(a0[7] + 4) = buf[2]; + + buf[0] = *(short *)(a0[8] + 0); + buf[1] = *(short *)(a0[8] + 2); + buf[2] = *(short *)(a0[8] + 4); + func_800F3C60(a1, buf, buf); + buf[0] += a1[5]; + buf[1] += a1[6]; + buf[2] += a1[7]; + *(short *)(a0[8] + 0) = buf[0]; + *(short *)(a0[8] + 2) = buf[1]; + *(short *)(a0[8] + 4) = buf[2]; +}