phase10: merge 19 — 464 distinct bodies / 473 regions

+8 bodies (worker A claims 11-18, all first-attempt; 16 of A's 18 claims needed
no override at all). Candidate gate MATCH before promotion.

TWO NEW LEVERS (worker A):
1. A two-arm selection's POLARITY is byte-required. For a1 = (x<2) ? a3 : saved,
   the natural order and the ternary both emit beq v0,zero with the arms swapped
   (right length, 5 differing bytes). Writing the larger-than arm first
   (if (x >= 2) a1 = saved; else a1 = a3;) makes cc1 emit bne v0,zero with
   a1 = a3 in the BRANCH DELAY SLOT, so that assignment runs on both paths and is
   overwritten on the >=2 path -- which is the original exactly. Same family as the
   mirrored comparison load order; second polarity case in A's partition.
2. A NAMED BOOLEAN LOCAL forces the branchless compare. rec[6] = ((a3 & 0xff) != 0) << 1
   is branchy (92 vs 88); only naming the boolean first,
   int flag = (a3 & 0xff) != 0; then rec[6] = flag << 1;
   reproduces the original's andi / sltu / sll. Six spellings measured. cc1 will
   un-do a boolean you inline when the VALUE (not the branch) is what you need.

make check green: regions=473 AGREE, differing_bytes=0 MATCH, 237 tests OK.
Worklist 1241 rows; excluded_already_registered=473.
This commit is contained in:
Christopher Williams
2026-09-24 07:57:26 -04:00
parent d7b232a7d9
commit 82c65ed468
10 changed files with 1606 additions and 1058 deletions
+1050 -1058
View File
File diff suppressed because it is too large Load Diff
+8
View File
@@ -48,6 +48,7 @@
0x80017D48 0x80017D88 src/func_80017D48.c
0x80017D88 0x80017DD0 src/func_80017D88.c
0x80017DD0 0x80017DF0 src/func_80017DD0.c
0x80017DF0 0x80017E38 src/func_80017DF0.c
0x800182D4 0x800182F4 src/func_800182D4.c
0x80018384 0x800183B8 src/func_80018384.c
0x800183B8 0x800183EC src/func_800183B8.c
@@ -120,6 +121,7 @@
0x8002F2F8 0x8002F300 src/func_8002F2F8.c
0x8002F404 0x8002F450 src/func_8002F404.c
0x800301FC 0x8003022C src/func_800301FC.c
0x80030284 0x800302DC src/func_80030284.c
0x80030358 0x80030390 src/func_80030358.c
0x80031F2C 0x80031F78 src/func_80031F2C.c
0x80031F78 0x80031FC4 src/func_80031F78.c
@@ -143,6 +145,7 @@
0x8003B2F0 0x8003B320 src/func_8003B2F0.c
0x8003B320 0x8003B34C src/func_8003B320.c
0x8003CB8C 0x8003CBE4 src/func_8003CB8C.c
0x8003D310 0x8003D35C src/func_8003D310.c
0x800419D0 0x80041A24 src/func_800419D0.c
0x80041A24 0x80041A58 src/func_80041A24.c
0x80041FE4 0x80042088 src/func_80041FE4.c
@@ -158,6 +161,7 @@
0x800454C8 0x80045540 src/func_800454C8.c
0x800474B0 0x80047508 src/func_800474B0.c
0x80047984 0x80047A14 src/func_80047984.c
0x80048350 0x80048394 src/func_80048350.c
0x80048E20 0x80048E70 src/func_80048E20.c
0x800491FC 0x80049298 src/func_800491FC.c
0x80049298 0x800492E4 src/func_80049298.c gp=-D_80121BFC
@@ -211,6 +215,8 @@
0x80068F98 0x80068FA8 src/func_80068F98.c
0x800697A4 0x800697C4 src/func_800697A4.c
0x800697C4 0x800697FC src/func_800697C4.c
0x8006ADB0 0x8006AE04 src/func_8006ADB0.c
0x8006B184 0x8006B1CC src/func_8006B184.c
0x8006B1CC 0x8006B214 src/func_8006B1CC.c
0x8006B66C 0x8006B6BC src/func_8006B66C.c
0x8006B778 0x8006B7C0 src/func_8006B778.c
@@ -312,6 +318,7 @@
0x800A2F20 0x800A2F44 src/func_800A2F20.c
0x800A34E8 0x800A3540 src/func_800A34E8.c
0x800A45E0 0x800A466C src/func_8009E8D0.c
0x800A5180 0x800A5228 src/func_800A5180.c
0x800A5CC8 0x800A5CEC src/func_800A5CC8.c
0x800A5CEC 0x800A5D24 src/func_800A5CEC.c
0x800A623C 0x800A6268 src/func_800A623C.c
@@ -332,6 +339,7 @@
0x800AA56C 0x800AA59C src/func_800AA56C.c
0x800AC818 0x800AC85C src/func_800AC818.c
0x800AC85C 0x800AC884 src/func_800AC85C.c
0x800AC98C 0x800AC9D8 src/func_800AC98C.c
0x800ACAC8 0x800ACB34 src/func_800ACAC8.c
0x800ACC00 0x800ACC20 src/func_800ACC00.c
0x800AE0F4 0x800AE10C src/func_800AE0F4.c
1 # Code-region registry: one C region per matched function.
48 0x80017D48
49 0x80017D88
50 0x80017DD0
51 0x80017DF0
52 0x800182D4
53 0x80018384
54 0x800183B8
121 0x8002F2F8
122 0x8002F404
123 0x800301FC
124 0x80030284
125 0x80030358
126 0x80031F2C
127 0x80031F78
145 0x8003B2F0
146 0x8003B320
147 0x8003CB8C
148 0x8003D310
149 0x800419D0
150 0x80041A24
151 0x80041FE4
161 0x800454C8
162 0x800474B0
163 0x80047984
164 0x80048350
165 0x80048E20
166 0x800491FC
167 0x80049298
215 0x80068F98
216 0x800697A4
217 0x800697C4
218 0x8006ADB0
219 0x8006B184
220 0x8006B1CC
221 0x8006B66C
222 0x8006B778
318 0x800A2F20
319 0x800A34E8
320 0x800A45E0
321 0x800A5180
322 0x800A5CC8
323 0x800A5CEC
324 0x800A623C
339 0x800AA56C
340 0x800AC818
341 0x800AC85C
342 0x800AC98C
343 0x800ACAC8
344 0x800ACC00
345 0x800AE0F4
+69
View File
@@ -0,0 +1,69 @@
/*
* func_80017DF0 — 72 bytes at 0x80017DF0..0x80017E38
*
* Leaf that zeroes a stride-20 array whose element count is twice a gp-relative
* halfword.
*
* Original words:
* 27BDFFF8 addiu sp,sp,-8
* 9782000C lhu v0,12(gp) ; v0 = D_80121944 UNSIGNED halfword
* 00000000 nop
* 00021040 sll v0,v0,0x1 ; v0 *= 2 (the count)
* 1840000A blez v0,0x80017E2C ; count <= 0 -> done
* 00001821 addu v1,zero,zero ; (delay) i = 0
* 00402821 addu a1,v0,zero ; a1 = count
* 00002021 addu a0,zero,zero ; a0 = 0
* 3C018012 lui at,0x8012 ; (L)
* 00240821 addu at,at,a0 ; at = 0x80120000 + i*20
* AC203270 sw zero,12912(at) ; *(int *)(0x80123270 + i*20) = 0
* 24630001 addiu v1,v1,1 ; i++
* 0065102A slt v0,v1,a1 ; v0 = (i < count)
* 1440FFFA bne v0,zero,0x80017E10 ; loop
* 24840014 addiu a0,a0,20 ; (delay) i*20 += 20
* 27BD0008 addiu sp,sp,8 ; (END)
* 03E00008 jr ra
* 00000000 nop
*
* The base is materialised as `lui at,0x8012` plus a 12912 displacement, so the
* address is 0x80120000 + i*20 + 0x3270 = D_80123270 + i*20: the low half of the
* base is carried in the STORE's displacement, not in an `ori`. The `lui` is inside
* the loop (recomputed every iteration) rather than hoisted.
*
* TWO SPELLING REQUIREMENTS, both measured against this range (six spellings tried).
* (1) The base must be a 20-BYTE-ELEMENT ARRAY, `extern int D_80123270[][5]`, indexed
* as `D_80123270[i][0]`. With `char *` arithmetic (`(char *)D_80123270 + i * 20`)
* cc1 hoists the base with `lui`/`addiu` and strength-reduces the WHOLE address into
* a running pointer, losing the loop's `lui` and its 12912 displacement (64 bytes).
* With a 20-byte element type cc1 takes the symbol-plus-register path and emits
* exactly the original's `lui at,hi` / `addu at,at,index` / `sw zero,lo(at)`.
* (2) The count must be written INLINE in the loop condition, not in a named local:
* `for (i = 0; i < D_80121944 * 2; i++)` matches, while `int n = D_80121944 * 2;`
* first and then `i < n` gives the right address form but the wrong count register
* and a `beq`-zero test instead of `blez` (68 bytes).
*
* `i*20` is strength-reduced to an induction variable in `a0` while `i` itself stays
* in `v1` for the `slt` comparison, and the loop is rotated with `i++` in the test's
* delay slot and the pointer bump in the loop-back's delay slot.
*
* The frame is 8 bytes with no `ra` save: this is a LEAF that still adjusts `sp`,
* which is the shape the tier-1/tier-2 split calls a frame. `sp` is never used, so
* the adjustment exists only because cc1 reserved space it did not need.
*
* LIMITS: the function name, the count global, the array base, the stride 20, the
* element width 4 and the doubling are hypotheses read from the instruction shape;
* only the bytes are evidence. `lhu` fixes the count as UNSIGNED 16-bit.
* D_80121944 is gp-relative and carries a registry `gp` marker; D_80123270 is not in
* the registry and is referenced by an address-named spelling, which stays absolute
* as the original does.
*/
extern unsigned short D_80121944;
extern int D_80123270[][5];
void func_80017DF0(void)
{
int i;
for (i = 0; i < D_80121944 * 2; i++)
D_80123270[i][0] = 0;
}
+64
View File
@@ -0,0 +1,64 @@
/*
* func_80030284 — 88 bytes at 0x80030284..0x800302DC
*
* Accumulates three of a caller's fields into three consecutive words of a second
* structure, then calls a routine and returns its result masked to eight bits.
*
* Original words:
* 27BDFFE8 addiu sp,sp,-24
* AFBF0010 sw ra,16(sp)
* 8CA20008 lw v0,8(a1) ; v0 = a1[2]
* 8C83003C lw v1,60(a0) ; v1 = *(int *)(a0 + 60)
* 00000000 nop
* 00431021 addu v0,v0,v1 ; v0 += v1
* ACA20008 sw v0,8(a1) ; a1[2] = v0
* 8CA2000C lw v0,12(a1) ; v0 = a1[3]
* 8C830040 lw v1,64(a0) ; v1 = *(int *)(a0 + 64)
* 00000000 nop
* 00431021 addu v0,v0,v1
* ACA2000C sw v0,12(a1) ; a1[3] = v0
* 8CA20010 lw v0,16(a1) ; v0 = a1[4]
* 8C830044 lw v1,68(a0) ; v1 = *(int *)(a0 + 68)
* 00000000 nop
* 00431021 addu v0,v0,v1
* 0C00C000 jal 0x80030000
* ACA20010 sw v0,16(a1) ; (delay) a1[4] = v0
* 8FBF0010 lw ra,16(sp)
* 304200FF andi v0,v0,0xff ; v0 &= 0xff
* 03E00008 jr ra
* 27BD0018 addiu sp,sp,24 ; (delay) frame release
*
* The three accumulate-and-store pairs are a repeated `+=` over three consecutive
* words with three different source offsets (60, 64, 68 — i.e. 15, 16, 17 ints), so
* the source is three separate statements and not a loop (a loop would not unroll
* into this interleaving, and the third store is scheduled into the call's delay
* slot).
*
* The mask is applied AFTER the call and to `v0` directly, so the return is
* `func_80030000() & 0xff` and not a masked local. The call passes NO new arguments —
* the delay slot is used for the third store — so the callee is called with whatever
* `a0` and `a1` already hold, i.e. it takes no arguments that this body sets up.
*
* THE EPILOGUE IS THE `rare-real` SHAPE (cookbook findings 11/35): `lw ra,16(sp)` /
* `andi v0,v0,0xff` / `jr ra` / `addiu sp,sp,24`. The instruction between the `ra`
* load and the jump is a real instruction that does not read `ra`, so GNU `as` in
* reorder mode reproduces it with the default toolchain and no override — the same
* reachability condition as src/func_800833CC.c.
*
* LIMITS: the function name, the callee, the three source offsets, the three
* destination words and the 8-bit mask are hypotheses read from the instruction
* shape; only the bytes are evidence. Both structures are addressed with byte
* offsets because their element types are not recoverable. func_80030000 is not
* registered and is referenced by its address-named spelling.
*/
extern int func_80030000(void);
int func_80030284(int a0, int *a1)
{
a1[2] += *(int *)((char *)a0 + 60);
a1[3] += *(int *)((char *)a0 + 64);
a1[4] += *(int *)((char *)a0 + 68);
return func_80030000() & 0xff;
}
+63
View File
@@ -0,0 +1,63 @@
/*
* func_8003D310 — 76 bytes at 0x8003D310..0x8003D35C
*
* Three-way guarded call: returns unless the argument and two successive fields are
* all non-null, in which case it calls one routine with the original argument and a
* fixed data address.
*
* Original words:
* 27BDFFE8 addiu sp,sp,-24
* 1080000D beq a0,zero,0x8003D34C ; null argument -> return
* AFBF0010 sw ra,16(sp) ; (delay) save ra
* 8C82000C lw v0,12(a0) ; v0 = a0->field_0c
* 00000000 nop
* 10400009 beq v0,zero,0x8003D34C ; null -> return
* 00000000 nop
* 8C420160 lw v0,352(v0) ; v0 = v0->field_160
* 00000000 nop
* 10400005 beq v0,zero,0x8003D34C ; null -> return
* 00000000 nop
* 3C058011 lui a1,0x8011
* 24A56944 addiu a1,a1,26948 ; a1 = D_80116944 (a SYMBOL: `addiu`)
* 0C024DDA jal 0x80093768
* 00000000 nop ; (delay)
* 8FBF0010 lw ra,16(sp) ; (END)
* 27BD0018 addiu sp,sp,24
* 03E00008 jr ra
* 00000000 nop
*
* All three guards branch to the SAME shared epilogue and `sw ra` is scheduled into
* the first guard's delay slot, so the source is three `return;` statements, not a
* shared result local. The third test discards the loaded value (it is only compared
* with zero), so it is `if (*(int *)(p + 352) == 0) return;` and not an assignment.
*
* The `jal` delay slot is a `nop` and only `a1` is set up, so the call's first
* argument is whatever `a0` still holds — the incoming pointer, which is never
* overwritten. The call therefore passes the argument AND the fixed address.
*
* LIMITS: the function name, the callee, the two field offsets (12 and 352), the
* fixed address and the list structure are hypotheses read from the instruction
* shape; only the bytes are evidence. D_80116944 is a data address not in the symbol
* registry, so it is referenced by an address-named spelling and stays absolute as
* the original does; func_80093768 is not registered either. The function sets no
* result, so it is `void`.
*/
extern char D_80116944[];
extern void func_80093768(int a0, char *a1);
void func_8003D310(int a0)
{
int *p;
if (a0 == 0)
return;
p = *(int **)((char *)a0 + 12);
if (p == 0)
return;
if (*(int *)((char *)p + 352) == 0)
return;
func_80093768(a0, D_80116944);
}
+52
View File
@@ -0,0 +1,52 @@
/*
* func_80048350 — 68 bytes at 0x80048350..0x80048394
*
* Two-call thunk: allocates or looks up something with four arguments, then passes
* the narrowed result on with a fixed data address and a count.
*
* Original words:
* 27BDFFE8 addiu sp,sp,-24
* AFBF0010 sw ra,16(sp)
* 00803021 addu a2,a0,zero ; a2 = the argument
* 24040001 addiu a0,zero,1 ; a0 = 1
* 24050030 addiu a1,zero,48 ; a1 = 48
* 0C023E96 jal 0x8008FA58
* 00003821 addu a3,zero,zero ; (delay) func_8008FA58(1, 48, a0, 0)
* 3C048005 lui a0,0x8005
* 2484832C addiu a0,a0,-31956 ; a0 = func_8004832C (a SYMBOL: `addiu`)
* 2405000A addiu a1,zero,10 ; a1 = 10
* 00021400 sll v0,v0,0x10 ; \
* 0C00B42A jal 0x8002D0A8 / (short) narrowing of the first result
* 00023403 sra a2,v0,0x10 ; (delay) a2 = (short)result
* 8FBF0010 lw ra,16(sp)
* 27BD0018 addiu sp,sp,24
* 03E00008 jr ra
* 00000000 nop
*
* The second call sets only `a0`, `a1` and `a2`, so `a3` is whatever the first call
* left in it — the call takes THREE arguments. The `lui`/`addiu` pair for 0x8004832C
* is the symbol form (a literal would use `ori`), so that address is taken from a
* named symbol; it is a `prologue`-graded code candidate 0x24 bytes before this
* function and is referenced by its address-named spelling.
*
* The first result is narrowed to a SIGNED 16-bit value with the two-piece
* `sll 16` / `sra 16` pair before being passed on, so the second callee's third
* parameter is 16 bits wide.
*
* LIMITS: the function name, both callees, the four first-call constants (1, 48, 0),
* the second-call count 10, the data address and the narrowed width are hypotheses
* read from the instruction shape; only the bytes are evidence. Neither callee is
* registered; func_8002D0A8 is itself a worklist row and a family callee. The frame
* is the minimum for a single `ra` save plus a 16-byte outgoing argument area.
*/
extern int func_8008FA58(int a0, int a1, int a2, int a3);
extern void func_8002D0A8(int a0, int a1, int a2);
extern int func_8004832C(void);
void func_80048350(int a0)
{
int r = func_8008FA58(1, 48, a0, 0);
func_8002D0A8((int)func_8004832C, 10, (short)r);
}
+65
View File
@@ -0,0 +1,65 @@
/*
* func_8006ADB0 — 84 bytes at 0x8006ADB0..0x8006AE04
*
* Walks a singly-linked list from a global head and, per node, conditionally calls
* one routine before following a two-hop link to the next node.
*
* Original words:
* 8F840550 lw a0,1360(gp) ; a0 = D_80121E88 (hoisted above the frame)
* 27BDFFE8 addiu sp,sp,-24
* AFBF0014 sw ra,20(sp)
* 1080000C beq a0,zero,0x8006ADF0 ; empty -> done
* AFB00010 sw s0,16(sp) ; (delay) save s0
* 8C82000C lw v0,12(a0) ; (L) v0 = node->field_0c
* 00000000 nop
* 8C430160 lw v1,352(v0) ; v1 = v0->field_160
* 8C50018C lw s0,396(v0) ; s0 = v0->field_18c (next)
* 10600003 beq v1,zero,0x8006ADE4 ; flag clear -> skip the call
* 00000000 nop
* 0C02FFC8 jal 0x800BFF20
* 00000000 nop ; (delay) func_800BFF20(node)
* 02002021 addu a0,s0,zero ; (NEXT) node = next
* 1480FFF6 bne a0,zero,0x8006ADC4 ; more -> loop
* 00000000 nop
* 8FBF0014 lw ra,20(sp) ; (END)
* 8FB00010 lw s0,16(sp)
* 27BD0018 addiu sp,sp,24
* 03E00008 jr ra
* 00000000 nop
*
* Same list shape as src/func_8006B184.c — the same head global and the same two link
* offsets (12 for the hop, 396 for the next link) — with an extra conditional call
* on a third field (352) read from the SAME intermediate record. The two loads from
* that record are adjacent and in ascending offset order, so both come from one
* source expression's temporaries and not from two separate lookups.
*
* The `jal` delay slot is a `nop` and no argument is set up, so the callee receives
* whatever `a0` holds — the current node, which the loop never overwrites before the
* call. The head load is hoisted above the frame setup and the loop is a rotated
* `while` whose back edge branches to the body start.
*
* LIMITS: the function name, the callee, the head global, the three field offsets
* (12, 352, 396) and the list direction are hypotheses read from the instruction
* shape; only the bytes are evidence. D_80121E88 is gp-relative and carries a
* registry `gp` marker; func_800BFF20 is not registered and is referenced by its
* address-named spelling. The function sets no result, so it is `void`.
*/
extern int D_80121E88;
extern void func_800BFF20(int *node);
void func_8006ADB0(void)
{
int *node = (int *)D_80121E88;
while (node != 0) {
int *mid = *(int **)((char *)node + 12);
int *flag = *(int **)((char *)mid + 352);
int *next = *(int **)((char *)mid + 396);
if (flag != 0)
func_800BFF20(node);
node = next;
}
}
+58
View File
@@ -0,0 +1,58 @@
/*
* func_8006B184 — 72 bytes at 0x8006B184..0x8006B1CC
*
* Walks a singly-linked list from a global head, calling one routine per node and
* following a two-hop link to the next node.
*
* Original words:
* 8F840550 lw a0,1360(gp) ; a0 = D_80121E88 (hoisted above the frame)
* 27BDFFE8 addiu sp,sp,-24
* AFBF0014 sw ra,20(sp)
* 10800009 beq a0,zero,0x8006B1B8 ; empty -> done
* AFB00010 sw s0,16(sp) ; (delay) save s0
* 8C82000C lw v0,12(a0) ; (L) v0 = node->field_0c
* 00000000 nop
* 8C50018C lw s0,396(v0) ; s0 = v0->field_18c (two hops)
* 0C030B0C jal 0x800C2C30
* 00000000 nop ; (delay) func_800C2C30(node)
* 02002021 addu a0,s0,zero ; node = next
* 1480FFF9 bne a0,zero,0x8006B198 ; more -> loop
* 00000000 nop
* 8FBF0014 lw ra,20(sp) ; (END)
* 8FB00010 lw s0,16(sp)
* 27BD0018 addiu sp,sp,24
* 03E00008 jr ra
* 00000000 nop
*
* The head load is hoisted ABOVE the frame setup (the func_80042964 scheduling
* shape) and the loop is a rotated `while`: the entry guard jumps to the epilogue
* and the back edge branches to the BODY start, not to a test, with the next node
* moved into `a0` immediately before it.
*
* The `jal` delay slot is a `nop`, so the callee gets whatever `a0` already holds —
* which is the current node — and the call needs no argument setup of its own.
* `s0` holds the two-hop next pointer across that call, which is why the frame saves
* it; `a0` is the loop variable and is reassigned from `s0` after the call.
*
* LIMITS: the function name, the callee, the head global, the two link offsets
* (12 and 396) and the list direction are hypotheses read from the instruction
* shape; only the bytes are evidence. D_80121E88 is gp-relative and carries a
* registry `gp` marker; func_800C2C30 is not registered and is referenced by its
* address-named spelling. The function sets no result, so it is `void`.
*/
extern int D_80121E88;
extern void func_800C2C30(int *node);
void func_8006B184(void)
{
int *node = (int *)D_80121E88;
while (node != 0) {
int *mid = *(int **)((char *)node + 12);
int *next = *(int **)((char *)mid + 396);
func_800C2C30(node);
node = next;
}
}
+102
View File
@@ -0,0 +1,102 @@
/*
* func_800A5180 — 168 bytes at 0x800A5180..0x800A5228
*
* Walks a singly-linked list and, for each node whose halfword flag has one bit set
* and whose table-referenced record has another, issues an eight-argument call.
*
* Original words:
* 27BDFFD8 addiu sp,sp,-40
* AFBF0024 sw ra,36(sp)
* AFB00020 sw s0,32(sp)
* 8C90008C lw s0,140(a0) ; s0 = a0->field_8c (list head)
* 00000000 nop
* 1200001F beq s0,zero,0x800A5214 ; empty -> done
* 00000000 nop
* 8E020000 lw v0,0(s0) ; (L) v0 = *p
* 00000000 nop
* 84430014 lh v1,20(v0) ; v1 = (short)v0[10] SIGNED 16-bit
* 00000000 nop
* 30622000 andi v0,v1,0x2000 ; v0 = v1 & 0x2000
* 10400014 beq v0,zero,0x800A5204 ; clear -> next node
* 306203FF andi v0,v1,0x3ff ; (delay) index = v1 & 0x3ff
* 3C038012 lui v1,0x8012
* 8C631C00 lw v1,7168(v1) ; v1 = D_80121C00 (table base, ABSOLUTE)
* 00021080 sll v0,v0,0x2 ; index * 4
* 00431021 addu v0,v0,v1 ; table + index*4
* 8C430000 lw v1,0(v0) ; v1 = entry = table[index]
* 00000000 nop
* 90620024 lbu v0,36(v1) ; v0 = entry->byte_24
* 00000000 nop
* 30420040 andi v0,v0,0x40 ; v0 &= 0x40
* 10400009 beq v0,zero,0x800A5204 ; clear -> next node
* 24040009 addiu a0,zero,9 ; (delay) a0 = 9
* 24050002 addiu a1,zero,2 ; a1 = 2
* 84670002 lh a3,2(v1) ; a3 = (short)entry[1] SIGNED 16-bit
* 3406FFFE ori a2,zero,0xfffe ; a2 = 0xfffe (ORI: does not fit signed 16)
* AFA00010 sw zero,16(sp) ; \
* AFA00014 sw zero,20(sp) ; | outgoing argument area: the callee's
* AFA00018 sw zero,24(sp) ; | 5th..8th arguments
* 0C00AD82 jal 0x8002B608 ; |
* AFA0001C sw zero,28(sp) ; (delay) /
* 8E100008 lw s0,8(s0) ; (NEXT) s0 = p->field_8
* 00000000 nop
* 1600FFE3 bne s0,zero,0x800A519C ; more -> loop
* 00000000 nop
* 8FBF0024 lw ra,36(sp) ; (END)
* 8FB00020 lw s0,32(sp)
* 27BD0028 addiu sp,sp,40
* 03E00008 jr ra
* 00000000 nop
*
* The call takes EIGHT arguments: four in registers (9, 2, 0xfffe, a signed halfword
* from the table entry) and four zero words in the outgoing argument area at
* sp+0x10..sp+0x20. That area is what makes the frame 40 bytes — `s0` and `ra` sit
* at 0x20 and 0x24, so the locals between the argument area and the saves are
* exactly the four stack arguments, not scratch storage.
*
* The two guards both branch to the same "next node" block and the table load sits
* between them, so the source is a nested `if` (NOT one `&&` expression with the
* table load inline — see the correction note below). `0xFFFE` is materialised with
* `ori` rather than `addiu` because it does not fit a signed 16-bit immediate — the
* mirror of the literal-vs-symbol trap.
*
* TWO CORRECTIONS THAT WERE BYTE-REQUIRED, both caught by diffing rather than by
* reading. (1) The fourth call argument is `entry[1]` — a halfword from the TABLE
* ENTRY — not `q[1]`; the original's `lh a3,2(v1)` reads through the entry pointer,
* and `q` is already dead by then because `lbu v0,36(v1)` overwrote its register.
* (2) Both pointers are `char *`: with `int *` the `+ 36` byte offset scales to 144
* and the body comes out 12 bytes wrong. With the `q[1]` mistake the body was the
* right LENGTH and differed in 11 bytes of register fields, which is exactly the
* shape that reads as a register-allocation tie-break — the diff, not the
* mnemonics, is what exposed it.
*
* LIMITS: the function name, the callee, the table, the list offset 0x8c, the node
* offsets (0 for the record pointer, 8 for the link), the halfword offsets 20 and 2,
* the byte offset 36 and the masks 0x2000 / 0x3ff / 0x40 are hypotheses read from
* the instruction shape; only the bytes are evidence. Both `lh` loads are SIGNED
* 16-bit. func_8002B608 is this row's unregistered family callee (18 worklist rows)
* and its map is derived here: eight arguments, the first three constants, the
* fourth a signed halfword, and four zeros on the stack.
*/
extern int D_80121C00;
extern void func_8002B608(int a0, int a1, int a2, int a3,
int a4, int a5, int a6, int a7);
void func_800A5180(int a0)
{
int *p = *(int **)((char *)a0 + 140);
while (p != 0) {
int *q = *(int **)p;
int v = *(short *)((char *)q + 20);
if (v & 0x2000) {
char *entry = *(char **)(D_80121C00 + (v & 0x3ff) * 4);
if (*(unsigned char *)(entry + 36) & 0x40)
func_8002B608(9, 2, 0xfffe, *(short *)(entry + 2), 0, 0, 0, 0);
}
p = *(int **)((char *)p + 8);
}
}
+75
View File
@@ -0,0 +1,75 @@
/*
* func_800AC98C — 76 bytes at 0x800AC98C..0x800AC9D8
*
* Six-argument routine that reduces a difference between two stack-passed arguments
* and then selects one of two pointers before calling on.
*
* Original words:
* 27BDFFE8 addiu sp,sp,-24
* 00C04021 addu t0,a2,zero ; t0 = a2 (the third argument)
* 8FA6002C lw a2,44(sp) ; a2 = the 6th argument (entry sp+0x14)
* 8FA30028 lw v1,40(sp) ; v1 = the 5th argument (entry sp+0x10)
* AFBF0010 sw ra,16(sp)
* 10660002 beq v1,a2,0x800AC9AC ; equal -> no reduction
* 00C01021 addu v0,a2,zero ; (delay) v0 = the 6th argument
* 00C33023 subu a2,a2,v1 ; a2 = 6th - 5th
* 10C20004 beq a2,v0,0x800AC9C0 ; (L) unchanged -> skip the selection
* 28C20002 slti v0,a2,2 ; (delay) v0 = (a2 < 2)
* 14400002 bne v0,zero,0x800AC9C0 ; below 2 -> take a3
* 00E02821 addu a1,a3,zero ; (delay) a1 = a3
* 01002821 addu a1,t0,zero ; a1 = the saved third argument
* 0C03CC5C jal 0x800F3170 ; (L2)
* 00000000 nop ; (delay)
* 8FBF0010 lw ra,16(sp)
* 27BD0018 addiu sp,sp,24
* 03E00008 jr ra
* 00000000 nop
*
* The frame is adjusted BEFORE the stack arguments are read, so `40(sp)` and `44(sp)`
* are the entry frame's `16(sp)` and `20(sp)` — the FIFTH and SIXTH arguments. The
* function therefore takes six arguments, and only `a0`, `a1` and `a3` come in
* registers beyond the four.
*
* `v0` is loaded with the sixth argument in the first branch's DELAY SLOT, so it
* holds that value on both paths and the second test is `if (a2 == <sixth>) skip` —
* which is exactly the "no reduction happened" case.
*
* THE POLARITY IS BYTE-REQUIRED. The selection must be written
* `if (x >= 2) a1 = <saved third argument>; else a1 = a3;` — that is, with the
* LARGER-than arm first. Written the natural way round (`if (x < 2) a1 = a3; else ...`)
* cc1 emits `beq v0,zero` with the arms in the opposite positions (5 differing
* bytes), and the same is true of the `(x < 2) ? a3 : saved` ternary. With the
* `>= 2` spelling cc1 emits `bne v0,zero` and puts `a1 = a3` in the branch's delay
* slot, which is the original exactly: the branch skips the `a1 = t0` assignment, so
* `a1 = a3` runs on both paths and is overwritten on the `>= 2` path. Three
* spellings were compiled against this range.
*
* The call passes `a0`, the selected `a1`, the reduced `a2` and the incoming `a3`,
* and its delay slot is a `nop`.
*
* LIMITS: the function name, the callee, the six-argument shape, the `< 2` bound and
* the meaning of the two stack arguments are hypotheses read from the instruction
* shape; only the bytes are evidence. `t0` holds the third argument across the body
* because it must survive the reduction of `a2`. func_800F3170 is not registered and
* is referenced by its address-named spelling; it is this row's family callee.
*/
extern void func_800F3170(int a0, int a1, int a2, int a3);
void func_800AC98C(int a0, int a1, int a2, int a3, int fifth, int sixth)
{
int saved = a2;
int x = sixth;
if (fifth != sixth)
x -= fifth;
if (x != sixth) {
if (x >= 2)
a1 = saved;
else
a1 = a3;
}
func_800F3170(a0, a1, x, a3);
}