phase10: merge 14 + negatives reconciliation — 450 distinct bodies / 459 regions

+3 bodies from 447. Candidate gate MATCH before promotion.

Negatives reconciliation (the protocol step that protects worker findings from
being lost with ignored staging): imported 26 new negatives from all three
workers' staging into the tracked index. Index 140 -> 166 rows, address-ordered,
0 duplicates, 0 registered. excluded_recorded_negative 116 -> 142; worklist
1268 rows; 0 unregistered negatives survive into the worklist.

make check green: regions=459 disagreements=0 AGREE, c_regions=459
differing_bytes=0 MATCH, SHA-1 e173426c157384ebf1b6caf8c6fea18a85a14af9.
This commit is contained in:
Christopher Williams
2026-09-24 07:36:56 -04:00
parent c6d0ddd831
commit e4a1ae47ca
6 changed files with 1436 additions and 1077 deletions
+1048 -1077
View File
File diff suppressed because it is too large Load Diff
+26
View File
@@ -30,6 +30,7 @@
0x8001DC20 - near-match -
0x8001EAFC 12 deferred shared-block-not-a-function
0x80022E44 52 near-match adjacent-zero-store merge
0X800238BC 264 near-match candidate_bytes=264 (correct length) differing_bytes=5 first_difference=0x80023900. Structure solved: `if (arg == 0) { out->x=out->y=out->z=0; } else
0x800245D8 - near-match -
0x80024630 56 near-match stack-routed min (temp elided by optimizer)
0x8002515C - near-match -
@@ -40,13 +41,18 @@
0x80025EAC - near-match -
0x8002622C - near-match -
0x800266A8 68 near-match byte-replication alloc (first sll register; fresh-context re-spelling 3B; named-locals regresses)
0X800268F4 136 near-match differing_bytes=9 result=DIFF at 136 bytes with the shared-result spelling (r-268f4-shared): branch target 0x80026968 vs 0x80026964 and the delay slot
0X80026A04 152 near-match LENGTH-MISMATCH: `for (i=0;i<10;i++)` gives 136 bytes (34 words) vs 152; cc1 ROTATED the loop into a do-while with the test at the bottom, and dropped
0x80027BE8 - near-match -
0X80028750 244 near-match candidate_bytes=236 vs 244 (8 short) with `int r[4]; int s;` and with `int r[3]; int s;`. Structure understood: s = f(p0,q0)+f(p1,q1)+f(p2,q2); r[i] =
0x8002D014 - near-match -
0x8002D060 72 near-match -
0X8002E198 180 deferred 4 prologue callee-saved assignment: ORIGINAL copies a0->s2, a1->s3, a2->s1 and the func_800A8700 result->s0 (save order s2,s3,s1, then s0 in the jal d
0x80037984 - near-match -
0x800379E0 - near-match -
0x80039308 - near-match -
0x8003A5F4 80 near-match alloc+layout (j-to-done second guard; worker-C residual confirmed by coordinator)
0X8003A9C8 240 near-match candidate_bytes=216 vs 240 (24 short). `int d[3]; int t[3];` reproduces the original's 64-byte frame and its s0/s1/ra offsets exactly, so the array le
0x8003EB18 - near-match -
0x80042CE0 80 near-match paired-array geometry (stride 1428, single walked at-register)
0x80042D88 76 near-match strength-reduce (sra5+sll2 vs cc1 folded shift)
@@ -55,12 +61,16 @@
0x80042E68 - near-match -
0x80045540 56 near-match constant-materialisation-order
0x80045F00 - near-match -
0X80045FD8 120 deferred 4 register shift: original reloads a3->a0, a4->v0, and in the L1 block a4->a1; candidate gets a3->v0, a4->v0, a4->v1 (i.e. the allocator's choice star
0x800460AC 40 near-match constant-materialisation-order
0x80047468 72 near-match alloc (move-zero-in-delay + *76 strength-reduce; 2 coordinator spellings 76B; the *76 and pointer-slot table-lever patterns confirmed)
0x8004820C - near-match -
0x800496CC - near-match -
0X8004D7C8 220 near-match candidate_bytes=220 = CORRECT length, differing_bytes=7 first_difference=0x8004D7F4. Structure: `v = *(char**)(p+32); s1 = *(int*)(*(char**)(v+244)+8)
0x800518BC 88 near-match return-merge/sltiu (3 spellings: goto-shared 80B, combined cond 80B, two-exit if 80B; original keeps move-zero at separate target + j-to-shared-return; sltiu needs unsigned val which alone fixes the compare byte but not the 8-byte layout)
0x800582AC 64 near-match -
0X800589E8 192 near-match candidate_bytes=196 vs 192 with `V4 d; V4 r;` and with `int d[3]; int r[3];` (the array form gives the original's 64-byte... no: frame 64 with s1 also
0X80058AA8 248 near-match candidate_bytes=252 vs 248 (4 over, 121 differing bytes from one shifted instruction). Structure: `V4 d; V4 v; v = *(V4*)D_8010D138; func_80027ECC(&v,
0x8005DEF8 124 near-match register-tiebreak
0x80065494 80 near-match popcount scheduling (base in beqz delay slot)
0x800690E4 68 near-match loop-rotation+head-match-early-return (3 spellings: do/while arg-test-top 84B, while+conditional 76B, while-test 64B; the j/li early path and bnez-back-to-bne rotation are the residual)
@@ -87,19 +97,25 @@
0x8009C750 436 deferred trapping-arithmetic
0x8009F064 - near-match -
0x800A6658 88 near-match alloc+symbol-recompute (3 coordinator spellings; original recomputes lui/addu per iteration into two separate symbol arrays; cc1 pre-materializes base pointers — worker-A finding-8 CSE class)
0X800A6B38 104 near-match candidate_bytes=104 differing_bytes=6 result=DIFF at 0x800A6B56. Count right (26 words), structure right, EVERY instruction identical except that the
0x800A82D0 64 near-match -
0x800A86B4 - near-match -
0x800A8AEC - near-match -
0x800AAC44 - near-match -
0X800AB504 148 near-match candidate_bytes=172 vs 148 (24 over). Structure solved: recursive free of a linked record (`if (n) func_800AB504(n);`), then a counted loop of `count
0x800AC9D8 56 near-match constant-materialisation-order
0X800ACA10 184 near-match candidate_bytes=188 vs 184 (one extra instruction). Structure solved: `v = 0; for (i=0;i<9;i++) if (D_80116E3C[i] == 1) { v = D_80116E84[i]; break; }`
0x800B0B88 - near-match -
0x800B34A4 88 near-match alloc-tiebreak (bit-index/base register pair a0/a1 vs cc1 a0/v1; 2 spellings both 80B; original keeps base in a1 via direct +0x10 load)
0X800BC6EC 236 deferred 0 NOT attempted beyond disassembly. The body switches the STACK POINTER to the PSX scratchpad and back: `lui at,0x1f80` + `sw sp,1020(at)` (saves sp t
0X800C1424 152 near-match candidate_bytes=148 vs 152 (4 short) after moving `found = 0;` to AFTER the two guards (that change alone took 144 -> 148 and put `move s0,zero` in th
0x800C1E54 - near-match -
0x800C3514 88 near-match alloc+layout (priority selector; original: all stack loads hoisted, beqz+nop+li groups, sltu first test; cc1 interleaves with bnez — 2 coordinator spellings 84B)
0x800F3BB4 164 near-match global-ra-save guard (ra through D_8012A57C is CRT/library asm, not usable C; GTE $0..$7 block verified as the rotation/translation macros — gte_ldTRX/TRY/TRZ added to gtemac.h from this row)
0x800F4B54 - near-match -
0x800F4C08 - near-match -
0x800F50B0 - near-match -
0X800F6948 116 near-match maspsx=off: candidate_bytes=116 differing_bytes=4 first_difference=0x800F6954. maspsx on: candidate_bytes=120 (one extra nop: cc1 emits `addu sp,sp,40
0x800F6ED0 44 near-match cc1-scheduling
0x800F6F20 - near-match -
0x800F7610 - near-match -
@@ -108,24 +124,33 @@
0x800F88F0 - near-match -
0x800F8928 - near-match -
0x800F9134 - near-match -
0X800FB6D8 128 near-match differing_bytes=56 result=DIFF, 128 bytes both sides. Instruction MULTISET and register allocation are IDENTICAL (handler in s0, previous in v0, buffe
0X800FB758 112 near-match rare-epilogue-order-35b; residual 6B at 0x800FB7BC; tried default;--no-maspsx;--cc1 gcc-2.91.66-psx;--cc1 gcc-2.8.1-ps
0x800FBD80 - near-match -
0x800FBDC0 - near-match -
0x800FBF5C 56 near-match -
0x800FC280 - near-match -
0X800FCB4C 228 near-match two residuals, both named. (i) WITHOUT parentheses around the multiply-minus: `base + n*36 - 36` reassociates to `(base + n*36) - 36` -> 232 bytes (4
0x800FCC30 - near-match -
0x800FD220 88 near-match exit-duplication
0x800FDE54 - near-match -
0X800FE970 84 near-match differing_bytes=6 result=DIFF, 84 bytes both sides. EXACT residual: the last two words swap - candidate `lw ra,24(sp)` / `addiu sp,sp,32` / `jr ra` /
0x800FF43C 64 near-match two-epilogue
0x800FF47C 64 near-match reorg-thread-fill
0x800FF6DC 76 near-match -
0x800FFB74 72 near-match base-materialization (original: lui v0,0x8014 + addiu 0x5ac0 + addiu 0x678 in v0; cc1: lui v1 + one addiu or ori; gp stores need D_80122168/6C gp rows; 4 coordinator spellings)
0x800FFBBC 48 near-match reorg-thread-fill
0X800FFF60 140 near-match maspsx on + default -G0: candidate_bytes=148 (cc1 CSEs the address of 0x801221AC into s0: lui s0/ori s0/sw s0/lw s0). maspsx on + cc1=-G4: candidate_b
0x80100334 - near-match -
0x8010036C 56 near-match rare-epilogue-order (F21)
0X801003A4 148 near-match candidate_bytes=180 vs 148 (32 over). Structure read off the original: two calls, then a 24-iteration loop over 72-byte records at 0x80145AC0 where th
0x80100998 80 near-match -
0X80101764 212 near-match correct LENGTH (212) with `int one = 1;` used for `one << mask` and both loop comparisons, but 139 differing bytes. WITHOUT the `one` local: 216 bytes
0x80101C5C 32 blocked -
0x80101C7C - near-match -
0x80101E50 76 near-match custom-compare loop shape (back-up-mismatch path; cc1 rewrites to 52B)
0X80102A00 128 near-match candidate_bytes=124 vs 128 expected, ONE extra instruction in the original: the original has a `nop` at 0x80102A64 between `lw v0,-724(v0)` (0x80102A6
0X80102A80 132 near-match rare-epilogue-r1-r2-measured; residual 4B at 0x80102A88; tried default maspsx 136 vs 132 LENGTH-MISMATCH (reorder nop block
0x80102F58 - near-match -
0x80102FE4 - near-match -
0x80103AA4 - near-match -
@@ -141,6 +166,7 @@
0x801086B0 - near-match -
0x801092C0 52 near-match -
0x801097A0 - near-match -
0X8010A940 232 deferred 5 5 attempts. The LOOP is fully solved: `unsigned short v = 0xC000; int i = 23; unsigned short *p = &D_80121112; for (; i >= 0; i--) { *p = v; p--; }`
0x8010AA28 112 near-match maspsx mutual exclusion (finding 17 in its purest form: maspsx=off -> 2 differing bytes at 0x8010AA6C; maspsx on -> 124 bytes, three unfilled delay slots; maspsx 2.56 has no flag suppressing only the jump-slot nop)
0x8010B420 - near-match -
0x801BB450 - near-match -
1 # Syphon Filter 3 (USA) open negatives index.
30 0x8001DC20
31 0x8001EAFC
32 0x80022E44
33 0X800238BC
34 0x800245D8
35 0x80024630
36 0x8002515C
41 0x80025EAC
42 0x8002622C
43 0x800266A8
44 0X800268F4
45 0X80026A04
46 0x80027BE8
47 0X80028750
48 0x8002D014
49 0x8002D060
50 0X8002E198
51 0x80037984
52 0x800379E0
53 0x80039308
54 0x8003A5F4
55 0X8003A9C8
56 0x8003EB18
57 0x80042CE0
58 0x80042D88
61 0x80042E68
62 0x80045540
63 0x80045F00
64 0X80045FD8
65 0x800460AC
66 0x80047468
67 0x8004820C
68 0x800496CC
69 0X8004D7C8
70 0x800518BC
71 0x800582AC
72 0X800589E8
73 0X80058AA8
74 0x8005DEF8
75 0x80065494
76 0x800690E4
97 0x8009C750
98 0x8009F064
99 0x800A6658
100 0X800A6B38
101 0x800A82D0
102 0x800A86B4
103 0x800A8AEC
104 0x800AAC44
105 0X800AB504
106 0x800AC9D8
107 0X800ACA10
108 0x800B0B88
109 0x800B34A4
110 0X800BC6EC
111 0X800C1424
112 0x800C1E54
113 0x800C3514
114 0x800F3BB4
115 0x800F4B54
116 0x800F4C08
117 0x800F50B0
118 0X800F6948
119 0x800F6ED0
120 0x800F6F20
121 0x800F7610
124 0x800F88F0
125 0x800F8928
126 0x800F9134
127 0X800FB6D8
128 0X800FB758
129 0x800FBD80
130 0x800FBDC0
131 0x800FBF5C
132 0x800FC280
133 0X800FCB4C
134 0x800FCC30
135 0x800FD220
136 0x800FDE54
137 0X800FE970
138 0x800FF43C
139 0x800FF47C
140 0x800FF6DC
141 0x800FFB74
142 0x800FFBBC
143 0X800FFF60
144 0x80100334
145 0x8010036C
146 0X801003A4
147 0x80100998
148 0X80101764
149 0x80101C5C
150 0x80101C7C
151 0x80101E50
152 0X80102A00
153 0X80102A80
154 0x80102F58
155 0x80102FE4
156 0x80103AA4
166 0x801086B0
167 0x801092C0
168 0x801097A0
169 0X8010A940
170 0x8010AA28
171 0x8010B420
172 0x801BB450
+3
View File
@@ -218,12 +218,14 @@
0x8006F6BC 0x8006F6F4 src/func_8006F6BC.c
0x8006F930 0x8006F944 src/func_8006F930.c
0x8006F944 0x8006F95C src/func_8006F944.c
0x8006F9B4 0x8006FA78 src/func_8006F9B4.c
0x8006FA78 0x8006FAA0 src/func_8006FA78.c
0x8007049C 0x800704BC src/func_8007049C.c
0x80072BA8 0x80072BDC src/func_80072BA8.c
0x80073250 0x80073284 src/func_80073250.c
0x80073364 0x800733C4 src/func_80073364.c
0x800734A4 0x800734DC src/func_800734A4.c
0x80073F78 0x8007405C src/func_80073F78.c
0x80074C00 0x80074C74 src/func_80074C00.c
0x8007A404 0x8007A428 src/func_8007A404.c
0x8007A428 0x8007A4FC src/func_8007A428.c
@@ -331,6 +333,7 @@
0x800AE0F4 0x800AE10C src/func_800AE0F4.c
0x800AE4DC 0x800AE548 src/func_800AE4DC.c
0x800AE548 0x800AE574 src/func_800AE548.c
0x800AE6C0 0x800AE7A0 src/func_800AE6C0.c
0x800AF1FC 0x800AF20C src/func_800AF1FC.c
0x800AF20C 0x800AF260 src/func_800AF20C.c
0x800AFB1C 0x800AFB6C src/func_800AFB1C.c
1 # Code-region registry: one C region per matched function.
218 0x8006F6BC
219 0x8006F930
220 0x8006F944
221 0x8006F9B4
222 0x8006FA78
223 0x8007049C
224 0x80072BA8
225 0x80073250
226 0x80073364
227 0x800734A4
228 0x80073F78
229 0x80074C00
230 0x8007A404
231 0x8007A428
333 0x800AE0F4
334 0x800AE4DC
335 0x800AE548
336 0x800AE6C0
337 0x800AF1FC
338 0x800AF20C
339 0x800AFB1C
+111
View File
@@ -0,0 +1,111 @@
/* func_8006F9B4 — 0x8006F9B4..0x8006FA78 (196 bytes).
*
* Original words (objdump of the validated payload, little-endian):
* lui v0,0x8012 \
* lhu v0,13926(v0) / v0 = *(unsigned short *)0x80123666 (hoisted)
* addiu sp,sp,-24
* sw ra,20(sp)
* sw s0,16(sp)
* sw a0,2924(gp) D_801224A4 = a0
* andi v0,v0,0x10 v0 &= 0x10
* beqz v0,0x8006F9FC bit 4 clear -> use the argument unchanged
* _srl v0,a1,0x10 (delay slot) v0 = a1 >> 16 LOGICAL
* andi v0,v0,0xff v0 &= 0xff
* andi v1,a1,0xff00 \
* sra v1,v1,0x8 / v1 = (a1 & 0xff00) >> 8 ARITHMETIC
* addu v0,v0,v1 |
* andi v1,a1,0xff |
* addu v0,v0,v1 | v0 = hi + mid + lo
* sra v0,v0,0x2 | ARITHMETIC: the sum is signed
* j 0x8006FA00 |
* _sll s0,v0,0x8 (delay slot) s0 = avg << 8
* 0x8006F9FC:
* move s0,a1 s0 = a1
* 0x8006FA00:
* sw s0,2928(gp) D_801224A8 = s0
* sw zero,2932(gp) D_801224AC = 0
* jal 0x800F3E70
* _nop
* li a1,31
* subu a1,a1,v0 a1 = 31 - func_800F3E70()
* lui a0,0xff a0 = 0x00FF0000
* and v1,s0,a0 \ lane 0x00FF0000: (x - (x>>2)) >> sh, masked
* sra v0,v1,0x2 |
* subu v1,v1,v0 |
* srav v1,v1,a1 |
* and v1,v1,a0 /
* andi v0,s0,0xff00 \ lane 0x0000FF00: same shape
* sra a0,v0,0x2 |
* subu v0,v0,a0 |
* srav v0,v0,a1 |
* andi v0,v0,0xff00 /
* or v1,v1,v0 |
* andi v0,s0,0xff \ lane 0x000000FF: same shape
* sra a0,v0,0x2 |
* subu v0,v0,a0 |
* srav v0,v0,a1 |
* andi v0,v0,0xff /
* or v1,v1,v0
* sw v1,2936(gp) D_801224B0 = v1
* lw ra,20(sp) (epilogue)
* lw s0,16(sp)
* addiu sp,sp,24
* jr ra
* _nop
*
* Stores an argument, then either uses a second argument unchanged or replaces it
* with a byte-average shifted into the middle of the word, then rescales the three
* colour-style lanes of that value by a shift derived from a callee. The frame is
* 24 bytes = the 16-byte o32 outgoing argument area plus s0 at 16 and ra at 20.
*
* FOUR THINGS THE BYTES PIN DOWN:
*
* 1. THE LANES ARE 24-BIT: the mask constant is `lui a0,0xff` = 0x00FF0000, so the
* three lanes are 0x00FF0000 / 0x0000FF00 / 0x000000FF. I first wrote
* 0xFF000000 (a 32-bit top lane); that is a different `lui` immediate and shows
* up as a single-instruction residual.
* 2. THE AVERAGE IS UNSIGNED AND THE SCALE IS SIGNED — the same expression needs
* BOTH shift kinds: `srl v0,a1,0x10` (logical, so the shifted operand is
* unsigned) and `sra v0,v0,0x2` (arithmetic, so the SUM is signed). Written
* wholly unsigned the final shift comes out `srl` and differs by exactly ONE
* byte at 0x8006F9F0. The spelling that works casts the byte extraction to
* unsigned and then casts the sum back to int:
* `(int)(((unsigned)a1 >> 16 & 0xff) + ((a1 & 0xff00) >> 8) + (a1 & 0xff)) >> 2 << 8`.
* Assigning the sum to a signed local first compiles identically. Diagnostic:
* a 1-byte residual inside a shift is a SIGNEDNESS difference, not a scheduling
* one — compare the funct field (srl 0x02 vs sra 0x03, srlv 0x06 vs srav 0x07).
* 3. The per-lane scale is `(x - (x >> 2))`, i.e. subtract a quarter from itself,
* then shift right by a variable amount. Both halves are signed (`sra`/`srav`),
* which is consistent with `s0` being an int.
* 4. The shift amount is `31 - func_800F3E70()`, computed once and shared by all
* three lanes.
*
* LIMITS: what the three stored words mean, the bit-4 flag's meaning, why the
* average is shifted left by 8, and the callee's semantics are hypotheses read from
* the instruction shapes. The four globals are gp-relative (+2924/2928/2932/2936 =
* 0x801224A4/0x801224A8/0x801224AC/0x801224B0) and are NOT in the registry, so the
* harness resolved them from their name-encoded addresses. Only the compiled bytes
* are evidence.
*/
extern int D_801224A4, D_801224A8, D_801224AC, D_801224B0;
extern int func_800F3E70(void);
void func_8006F9B4(int a0, int a1)
{
int s0;
int sh;
D_801224A4 = a0;
if (*(unsigned short *)0x80123666 & 0x10) {
s0 = (int)((((unsigned)a1 >> 16) & 0xff) + ((a1 & 0xff00) >> 8) + (a1 & 0xff)) >> 2 << 8;
} else {
s0 = a1;
}
D_801224A8 = s0;
D_801224AC = 0;
sh = 31 - func_800F3E70();
D_801224B0 = (((s0 & 0xFF0000) - ((s0 & 0xFF0000) >> 2)) >> sh & 0xFF0000)
| (((s0 & 0xFF00) - ((s0 & 0xFF00) >> 2)) >> sh & 0xFF00)
| (((s0 & 0xFF) - ((s0 & 0xFF) >> 2)) >> sh & 0xFF);
}
+123
View File
@@ -0,0 +1,123 @@
/* func_80073F78 — 0x80073F78..0x8007405C (228 bytes).
*
* Original words (objdump of the validated payload, little-endian):
* addiu sp,sp,-48
* sw s2,40(sp)
* move s2,a0 s2 = argument 0
* sw s0,32(sp)
* move s0,a1 s0 = argument 1
* move a0,a2 \
* addiu a1,sp,16 / func_80010B14(argument2, &buf)
* sw s1,36(sp)
* sw ra,44(sp)
* jal 0x80010B14
* _move s1,a3 (delay slot) s1 = argument 3
* jal 0x8006F6BC
* _move a0,s0 (delay slot) func_8006F6BC(argument1)
* lui a0,0x8012 \
* lw a0,9144(a0) / a0 = *(int *)0x801223B8 (ABSOLUTE load)
* move a1,s1 a1 = argument 3
* andi a0,a0,0x1 a0 &= 1
* jal 0x8006F9B4
* _ori a0,a0,0x4 (delay slot) func_8006F9B4(a0 | 4, argument3)
* addiu a0,sp,16 a0 = &buf
* li a1,32767
* li v0,32
* sw v0,3056(gp) D_80122528 = 32
* jal 0x8006FAEC
* _move a2,zero (delay slot) func_8006FAEC(&buf, 32767, 0)
* li a0,8
* li a1,7
* jal 0x8006FB44
* _li a2,1 (delay slot) r = func_8006FB44(8, 7, 1)
* move a0,v0 a0 = r
* beqz a0,0x8007403C r == 0 -> return r
* _nop
* bltz s2,0x80074038 argument0 < 0 -> the shared tail
* _sll v0,s2,0x2 (delay slot) v0 = argument0 * 4
* lui v1,0x8012 \
* lw v1,7168(v1) / v1 = *(int *)0x80121C00 (ABSOLUTE load)
* _nop load-delay slot
* addu v0,v0,v1 v0 = table + argument0*4
* lw v0,0(v0) v0 = table[argument0]
* _nop load-delay slot
* lw v0,8(v0) v0 = *(int *)(v0 + 8)
* _nop load-delay slot
* lbu v0,11(v0) v0 = *(unsigned char *)(v0 + 11)
* _nop load-delay slot
* andi v0,v0,0x1 v0 &= 1
* beqz v0,0x80074038 bit clear -> the shared tail
* _li v0,-30 (delay slot) SIGN-EXTENDED -30
* sb v0,46(a0) *(signed char *)(r + 46) = -30
* 0x80074038:
* sh zero,36(a0) *(short *)(r + 36) = 0
* 0x8007403C:
* move v0,a0 return r
* lw ra,44(sp) (epilogue)
* lw s2,40(sp)
* lw s1,36(sp)
* lw s0,32(sp)
* addiu sp,sp,48
* jr ra
* _nop
*
* Setup-and-stamp routine: fills a local record, runs three setup calls, obtains an
* object from a fourth, and — only when the object is non-null — conditionally
* stamps two of its fields before returning it. The frame is 48 bytes = 16 (o32
* outgoing args) + the 16-byte record at sp+16 + the s0/s1/s2/ra quad at
* sp+32..44. Three arguments and the record address are live across the calls.
*
* THREE THINGS THE BYTES PIN DOWN:
*
* 1. THE STAMPED FIELD IS `signed char`, AND THAT CHANGES THE LOADED CONSTANT EVEN
* THOUGH THE STORED BYTE IS IDENTICAL. The original materialises `-30` as
* `addiu v0,zero,-30` (0xFFFFFFE2) and stores it with `sb`. Declaring the
* target `char *` makes cc1 load `226` (0x000000E2) instead — same byte stored,
* ONE differing byte in the instruction. Declaring it `signed char *` restores
* the sign-extended constant. Diagnostic: a 1-byte residual on a `li`/`addiu`
* immediate whose low byte is unchanged is a SIGNEDNESS difference in the
* destination type, not a value difference.
* 2. `0x80121C00` and `0x801223B8` are both LOADED absolutely (`lui`+`lw`), so both
* use the literal-address spelling. 0x80121C00 is the pointer table already
* indexed by claims 6, 18, 20, 22 and 24.
* 3. The two guards share ONE tail: `sh zero,36(r)` is reached from both the
* `argument0 < 0` branch and the bit-test branch, and the `beqz r` exit jumps
* past it. So the source has the shared statement AFTER the inner `if`, not
* duplicated in both arms.
*
* LIMITS: the record's layout, the meaning of the +46/+36 fields, the constants
* 32767 / 32 / 8 / 7 / 1 / 0 and the five callees' semantics are hypotheses read
* from the instruction shapes. `func_8006F9B4` is my own claim 21 (matched this
* cycle) and `func_8006FAEC` / `func_8006FB44` sit immediately after it. Only the
* compiled bytes are evidence.
*/
extern int D_80122528;
extern int func_80010B14(int a0, void *a1);
extern int func_8006F6BC(int a0);
extern void func_8006F9B4(int a0, int a1);
extern int func_8006FAEC(void *a0, int a1, int a2);
extern int func_8006FB44(int a0, int a1, int a2);
int func_80073F78(int a0, int a1, int a2, int a3)
{
int buf[4];
int r;
int v;
func_80010B14(a2, buf);
func_8006F6BC(a1);
func_8006F9B4((*(int *)0x801223B8 & 1) | 4, a3);
D_80122528 = 32;
func_8006FAEC(buf, 32767, 0);
r = func_8006FB44(8, 7, 1);
if (r != 0) {
if (a0 >= 0) {
v = *(int *)(*(int *)0x80121C00 + a0 * 4);
if (*(unsigned char *)(*(int *)(v + 8) + 11) & 1)
*(signed char *)(r + 46) = -30;
}
*(short *)(r + 36) = 0;
}
return r;
}
+125
View File
@@ -0,0 +1,125 @@
/* func_800AE6C0 — 0x800AE6C0..0x800AE7A0 (224 bytes).
*
* Original words (objdump of the validated payload, little-endian):
* addiu sp,sp,-32
* lui v0,0x8012 \
* lw v0,7168(v0) / v0 = *(int *)0x80121C00 (ABSOLUTE load)
* sll a1,a1,0x2 a1 = argument 1 * 4
* sw ra,28(sp)
* sw s2,24(sp)
* sw s1,20(sp)
* sw s0,16(sp)
* addu a1,a1,v0 a1 = table + argument1*4
* lw s0,0(a1) s0 = table[argument1]
* move s2,a0 s2 = argument 0
* lbu v0,0(s0) v0 = *(unsigned char *)p
* _nop load-delay slot
* andi v0,v0,0x20 v0 &= 0x20
* bnez v0,0x800AE784 bit 5 set -> epilogue
* _move s1,a2 (delay slot) s1 = argument 2
* lh v0,2(s0) v0 = *(short *)(p + 2)
* lui a0,0x8012 \
* lw a0,7164(a0) / a0 = *(int *)0x80121BFC (ABSOLUTE load)
* sll v1,v0,0x2 \ v1 = v0 * 76 (strength-reduced)
* addu v1,v1,v0 |
* sll v1,v1,0x2 |
* subu v1,v1,v0 |
* sll v1,v1,0x2 /
* addu v1,v1,a0 v1 = base + v0*76
* lh v1,72(v1) v1 = t = *(short *)(v1 + 72)
* li v0,-1
* beq v1,v0,0x800AE754 t == -1 -> the c = -1 arm
* _sll v0,v1,0x2 (delay slot) the byte arm's first instruction
* addu v0,v0,v1 \ v0 = t * 76
* sll v0,v0,0x2 |
* subu v0,v0,v1 |
* sll v0,v0,0x2 /
* addu v0,v0,a0 v0 = base + t*76
* lbu v1,42(v0) v1 = c = *(unsigned char *)(v0 + 42)
* j 0x800AE75C
* _li v0,73 (delay slot)
* 0x800AE754:
* li v1,-1 c = -1
* li v0,73
* 0x800AE75C:
* beq v1,v0,0x800AE770 c == 73 -> skip the call
* _move a0,s0 (delay slot)
* jal 0x800AE548
* _move a0,s0 (delay slot)
* 0x800AE770:
* jal 0x800AE4DC
* _move a1,s1 (delay slot) TWO arguments
* move a0,s2
* jal 0x800ADDE8
* _move a1,s0 (delay slot)
* 0x800AE784:
* lw ra,28(sp) (epilogue)
* lw s2,24(sp)
* lw s1,20(sp)
* lw s0,16(sp)
* addiu sp,sp,32
* jr ra
* _nop
*
* Looks up an object in a pointer table, bails out when a flag bit is set,
* resolves a second index through a 76-byte-strided record table, compares one
* byte against 73, and then runs two forwarding calls. The frame is 32 bytes = 16
* (o32 outgoing args) + s0/s1/s2 at sp+16/20/24 + ra at sp+28.
*
* FOUR THINGS THE BYTES PIN DOWN:
*
* 1. CONDITIONAL POLARITY, FOURTH INSTANCE — and the first where it decides which
* arm is the DELAY-SLOT FILLER. The source must be
* if (t != -1) c = <byte>; else c = -1;
* so that the byte arm is the fall-through and `c = -1` sits at the branch
* target. The natural `if (t == -1) c = -1; else c = <byte>;` emits
* `bne v1,v0, <else>` with the `c = -1` arm as the fall-through and comes out
* 4 bytes SHORT (220 vs 224) with the index computation no longer in the delay
* slot. This lever is now 4-for-4 across claims 14, 19, 22 and the claim-20
* family; treat "residual starts at a two-arm branch" as "try flipping the
* condition" FIRST.
* 2. The 76-byte stride appears TWICE, both strength-reduced as
* `((idx*5)*4 - idx)*4` — the same shape as claim 16, so both are
* `idx * 76` with a variable index.
* 3. `0x80121C00` and `0x80121BFC` are both LOADED ABSOLUTELY (`lui`+`lw`), so both
* use the literal-address spelling. `D_80121BFC` IS registry gp-marked (the same
* trap as claim 16, where it is also read absolutely), and `D_80121C00` is the
* pointer table already indexed by claims 6, 18, 20 and 22.
* 4. THE CALL SITE IS WIDER THAN THE CALLEE'S OWN PROTOTYPE. `0x800AE4DC` is
* registered as taking ONE argument, but this call sets a1 as well, so the
* caller-side declaration here takes two. A call site that materialises an
* argument the callee's body never reads is still real code and must be
* declared (same shape as the claim-4/claim-10 "call leaves a0 holding the
* parameter" cases, in reverse).
*
* LIMITS: the table's element type, the flag bit 5, the +72/+42 fields, the value
* 73 and the three callees' semantics are hypotheses read from the instruction
* shapes. `func_800AE4DC` is my own claim 8 (matched this cycle) — the row was
* matched from its disassembly, not from that source, and the two agree. Only the
* compiled bytes are evidence.
*/
extern int func_800AE548(int a0);
extern void func_800AE4DC(char *p, int a1);
extern void func_800ADDE8(int a0, int a1);
void func_800AE6C0(int a0, int a1, int a2)
{
int s0 = *(int *)(*(int *)0x80121C00 + a1 * 4);
int base;
int t;
int c;
if (*(unsigned char *)s0 & 0x20)
return;
base = *(int *)0x80121BFC;
t = *(short *)(base + *(short *)(s0 + 2) * 76 + 72);
if (t != -1)
c = *(unsigned char *)(base + t * 76 + 42);
else
c = -1;
if (c != 73)
func_800AE548(s0);
func_800AE4DC((char *)s0, a2);
func_800ADDE8(a0, s0);
}