phase9: merge B 2 + coordinator 1 — 351 regions / 342 distinct bodies

Worker B matched 0x800F6330 (correcting my stale negative: destination is
0x8011AA18 via the signed-displacement finding-4 trap) and 0x80090B7C
(18th/19th first-attempt matches this cycle). Coordinator absorbed
0x80025070 (conditional dispatch wrapper). B's correction of its own
void-vs-valued epilogue tell accepted (0x800FBDC0 counterexample; the
shared-epilogue hypothesis stands untested — recorded as observation, not
rule). Gate MATCH whole-binary SHA-1
e173426c157384ebf1b6caf8c6fea18a85a14af9.
This commit is contained in:
Christopher Williams
2026-09-24 01:42:16 -04:00
parent 64561c9eca
commit db93c35371
4 changed files with 138 additions and 0 deletions
+3
View File
@@ -60,6 +60,7 @@
0x80022FCC 0x80022FFC src/func_80022FCC.c
0x800230E4 0x8002311C src/func_800230E4.c
0x80024C14 0x80024C34 src/func_80024C14.c
0x80025070 0x800250AC src/func_80025070.c
0x80026180 0x800261C0 src/func_80026180.c
0x80026258 0x80026264 src/func_80026258.c
0x80026264 0x80026274 src/func_80026264.c
@@ -198,6 +199,7 @@
0x800900A0 0x800900CC src/func_800900A0.c
0x80090A44 0x80090A70 src/func_80090A44.c
0x80090B64 0x80090B7C src/func_80090B64.c
0x80090B7C 0x80090BB4 src/func_80090B7C.c
0x80090C8C 0x80090CAC src/func_80090C8C.c
0x8009107C 0x800910B0 src/func_8009107C.c
0x800912D4 0x800912FC src/func_800912D4.c
@@ -263,6 +265,7 @@
0x800F5200 0x800F521C src/func_800F5200.c
0x800F5AF8 0x800F5B40 src/func_800F5AF8.c
0x800F5B40 0x800F5B70 src/func_800F5B40.c
0x800F6330 0x800F6364 src/func_800F6330.c
0x800F6570 0x800F6594 src/func_800F6570.c
0x800F75D0 0x800F760C src/func_800F75D0.c
0x800F7990 0x800F79C0 src/func_800F7990.c
1 # Code-region registry: one C region per matched function.
60 0x80022FCC
61 0x800230E4
62 0x80024C14
63 0x80025070
64 0x80026180
65 0x80026258
66 0x80026264
199 0x800900A0
200 0x80090A44
201 0x80090B64
202 0x80090B7C
203 0x80090C8C
204 0x8009107C
205 0x800912D4
265 0x800F5200
266 0x800F5AF8
267 0x800F5B40
268 0x800F6330
269 0x800F6570
270 0x800F75D0
271 0x800F7990
+43
View File
@@ -0,0 +1,43 @@
/* func_80025070 — 0x80025070..0x800250AC (60 bytes).
*
* Dispatches to func_800250AC with three arguments where the second is
* selected by a null-conditional.
*
* Original words:
* 0x27BDFFE8 addiu sp,sp,-24
* 0xAFBF0010 sw ra,16(sp)
* 0x8C820020 lw v0,32(a0) ; v0 = *(int *)(a0 + 0x20)
* 0x00000000 nop
* 0x14400003 bnez v0,else ; if (v0 != 0) else
* 0x00A03021 move a2,a1 ; (delay) a2 = a1 (forward)
* 0x08009425 j call
* 0x00002821 move a1,zero ; (delay) a1 = 0
* else:
* 0x8C450020 lw a1,32(v0) ; a1 = *(int *)(v0 + 0x20)
* call:
* 0x0C00942B jal func_800250AC
* 0x00000000 nop
* 0x8FBF0010 lw ra,16(sp)
* 0x27BD0018 addiu sp,sp,24
* 0x03E00008 jr ra
* 0x00000000 nop
*
* Dataflow: obj2 = *(a0+0x20); if (obj2 == 0) { a2 = a1; a1 = 0; }
* else { a1 = *(obj2 + 0x20); } call with (a0, a1, a2). The a0 stays
* forwarded unchanged. The j-with-delay structure selects the argument.
*
* LIMITS: the object layout and callee role are hypotheses. Only the
* compiled bytes are evidence.
*/
void func_800250AC(int, int, int);
int func_80025070(int *a0, int a1)
{
int *obj2 = *(int **)((char *)a0 + 0x20);
if (obj2 == 0)
func_800250AC((int)a0, 0, a1);
else
func_800250AC((int)a0, *(int *)((char *)obj2 + 0x20), a1);
}
+43
View File
@@ -0,0 +1,43 @@
/* func_80090B7C — 0x80090B7C..0x80090BB4 (56 bytes).
*
* Original words:
* 27BDFFE8 addiu sp,sp,-24
* AFBF0010 sw ra,16(sp)
* 00042400 sll a0,a0,0x10
* 00051100 sll v0,a1,0x4
* 00451023 subu v0,v0,a1 v0 = a1*15
* 00021480 sll v0,v0,0x12 << 18
* 00042403 sra a0,a0,0x10 a0 = (short)a0
* 00022C03 sra a1,v0,0x10 >> 16
* 0C03FE00 jal 0x800FF800
* 24060003 _li a2,3 (delay slot) third argument = 3
* 8FBF0010 lw ra,16(sp)
* 27BD0018 addiu sp,sp,24
* 03E00000 jr ra
* 00000000 nop
*
* Narrows two values and forwards them with a constant third. The first is a
* plain sign-extension; the second is the interesting one.
*
* `a1 << 4; - a1; << 18; >> 16` looks like two unrelated operations but is one:
* `<< 18` is the fusion of a `<< 2` (from a multiply by 4) with a `<< 16` (from a
* 16-bit sign-extension), so the whole sequence is
* `((a1 * 15) << 2 << 16) >> 16` — that is, `(short)(a1 * 60)`. The `>> 16` is
* what remains of the cast after cc1 folded the other shift into it, which is why
* the emitted shift amount is 18 and not 2. Writing this as anything other than a
* multiply-then-cast changes the shift amounts and will not match.
*
* LIMITS: the multipliers 15 and 4, and the fused shift amount 18, are read from
* the instructions; that they express a single `* 60` is an arithmetic identity
* rather than source evidence, but it is the only reading that yields a shift of
* 18 in a 16-bit-narrowing context. The third argument's constant 3 is literal.
* The callee is named for its address and its signature is inferred from the three
* registers that are set.
*/
void func_800FF800(short a, short b, int c);
void func_80090B7C(int a0, int a1)
{
func_800FF800((short)a0, (short)(a1 * 60), 3);
}
+49
View File
@@ -0,0 +1,49 @@
/* func_800F6330 — 0x800F6330..0x800F6364 (52 bytes).
*
* Original words:
* 27BDFFE8 addiu sp,sp,-24
* AFBF0010 sw ra,16(sp)
* 0C03DD98 jal 0x800F7660
* 2404FFFF _li a0,-0x1 (delay slot)
* 244200F0 addiu v0,v0,240 result + 240
* 3C018012 lui at,0x8012
* AC22AA18 sw v0,-21992(at) D_8011AA18 = result + 240
* 3C018012 lui at,0x8012
* AC20AA1C sw zero,-21988(at) D_8011AA1C = 0
* 8FBF0010 lw ra,16(sp)
* 27BD0018 addiu sp,sp,24
* 03E00008 jr ra
* 00000000 nop
*
* Calls one routine with the sentinel -1, adds 240 to what it returns, publishes
* the sum and clears the neighbouring global. Nothing is returned.
*
* `lui at,0x8012` + `sw v0,%lo(at)` is the assembler's expansion of a **symbol
* store** through `$at` (cookbook finding 3: a store must not clobber its source
* register, so the macro uses `$at`), so both destinations are symbols, not
* computed addresses. The two `lui`s load the same high half for two adjacent
* words, which is why the `lui` is re-emitted rather than shared — cc1 does not
* hoist a macro-expanded constant across the store.
*
* LIMITS: the `-1` sentinel, the 240 offset and the two displacements are read
* from the instructions. **The destination addresses are 0x8011AA18 and
* 0x8011AA1C, not 0x8012AA18** — `lui at,0x8012` with a displacement the
* disassembler prints as **-21992** is the sign-adjusted `%lo` split (cookbook
* finding 4), so the effective address is 0x80120000 - 0x55E8. Naming the symbol
* from the `lui` immediate alone puts it 64 KB away and produces a silently wrong
* claim; that mistake was caught here only by recomputing the address. The two
* globals are adjacent words (`0xAA1C` is `0xAA18` + 4). That the pair is a
* counter and its reset is a guess from their adjacency and the zero store, not
* evidence: nothing in the body reads either one.
*/
extern int D_8011AA18;
extern int D_8011AA1C;
int func_800F7660(int arg);
void func_800F6330(void)
{
D_8011AA18 = func_800F7660(-1) + 240;
D_8011AA1C = 0;
}