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:
+1050
-1058
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user