phase9: merge B 4 + C 4 — 346 regions / 337 distinct bodies

Worker B: 0x8002E3A8 bare unused frame (16B of pure stack adjustment from a
removed local — reproduced by declaring char unused[16] and nothing else),
0x8010AAC0 pass-through with arg reorder, 0x8008FF58 six-arg rebuild,
0x800F79C0 virtual dispatch. Worker C: 0x800268C4 (D_ names), 0x800697C4,
0x800658FC (F20 displacement-fold lever), 0x800A5CC8 (overlap with the
coordinator's claim — C's registration stood). Gate MATCH whole-binary
SHA-1 e173426c157384ebf1b6caf8c6fea18a85a14af9.
This commit is contained in:
Christopher Williams
2026-09-24 01:34:54 -04:00
parent cd53723c52
commit 74785840c7
8 changed files with 291 additions and 0 deletions
+7
View File
@@ -67,6 +67,7 @@
0x800262EC 0x800262F8 src/func_800262E0.c
0x800262F8 0x80026304 src/func_800262F8.c
0x800267C0 0x800267CC src/func_800267C0.c
0x800268C4 0x800268F4 src/func_800268C4.c
0x80026C2C 0x80026C7C src/func_80026C2C.c
0x80026F14 0x80026F3C src/func_80026F14.c
0x800276B0 0x800276D4 src/func_800276B0.c
@@ -86,6 +87,7 @@
0x8002D5D0 0x8002D608 src/func_8002D5D0.c
0x8002DEB4 0x8002DF1C src/func_8002DEB4.c
0x8002E028 0x8002E070 src/func_8002E028.c
0x8002E3A8 0x8002E3B8 src/func_8002E3A8.c
0x8002E4C8 0x8002E4F0 src/func_8002E4C8.c
0x8002E7C4 0x8002E7E4 src/func_8002E7C4.c
0x8002F160 0x8002F1A4 src/func_8002F160.c
@@ -134,6 +136,7 @@
0x80058288 0x800582AC src/func_80058288.c
0x8005E3D0 0x8005E3F4 src/func_8005E3D0.c
0x8005ED6C 0x8005EDBC src/func_8005ED6C.c
0x800658FC 0x80065930 src/func_800658FC.c
0x80065930 0x80065980 src/func_80065930.c
0x80065B6C 0x80065B8C src/func_80065B6C.c
0x800668A8 0x800668F0 src/func_800668A8.c
@@ -147,6 +150,7 @@
0x80068F6C 0x80068F98 src/func_80068F6C.c
0x80068F98 0x80068FA8 src/func_80068F98.c
0x800697A4 0x800697C4 src/func_800697A4.c
0x800697C4 0x800697FC src/func_800697C4.c
0x8006B778 0x8006B7C0 src/func_8006B778.c
0x8006BC08 0x8006BC34 src/func_8006BC08.c
0x8006BC34 0x8006BC74 src/func_8006BC34.c
@@ -187,6 +191,7 @@
0x8008F4A0 0x8008F4AC src/func_8008F4A0.c
0x8008F4F4 0x8008F508 src/func_8008F4F4.c
0x8008F508 0x8008F530 src/func_8008F508.c
0x8008FF58 0x8008FF84 src/func_8008FF58.c
0x8008FF84 0x8008FFC4 src/func_8008FF84.c
0x80090028 0x80090048 src/func_80090028.c
0x80090048 0x80090058 src/func_80090048.c
@@ -259,6 +264,7 @@
0x800F6570 0x800F6594 src/func_800F6570.c
0x800F75D0 0x800F760C src/func_800F75D0.c
0x800F7990 0x800F79C0 src/func_800F7990.c
0x800F79C0 0x800F79F0 src/func_800F79C0.c
0x800F79F0 0x800F7A20 src/func_800F79F0.c
0x800F7A20 0x800F7A54 src/func_800F7A20.c
0x800F7A54 0x800F7A84 src/func_800F7A54.c
@@ -349,3 +355,4 @@
0x8010A888 0x8010A8B0 src/func_8010A888.c
0x8010A8B0 0x8010A8D8 src/func_8010A8B0.c
0x8010AAA0 0x8010AABC src/func_8010AAA0.c
0x8010AAC0 0x8010AAE4 src/func_8010AAC0.c
1 # Code-region registry: one C region per matched function.
67 0x800262EC
68 0x800262F8
69 0x800267C0
70 0x800268C4
71 0x80026C2C
72 0x80026F14
73 0x800276B0
87 0x8002D5D0
88 0x8002DEB4
89 0x8002E028
90 0x8002E3A8
91 0x8002E4C8
92 0x8002E7C4
93 0x8002F160
136 0x80058288
137 0x8005E3D0
138 0x8005ED6C
139 0x800658FC
140 0x80065930
141 0x80065B6C
142 0x800668A8
150 0x80068F6C
151 0x80068F98
152 0x800697A4
153 0x800697C4
154 0x8006B778
155 0x8006BC08
156 0x8006BC34
191 0x8008F4A0
192 0x8008F4F4
193 0x8008F508
194 0x8008FF58
195 0x8008FF84
196 0x80090028
197 0x80090048
264 0x800F6570
265 0x800F75D0
266 0x800F7990
267 0x800F79C0
268 0x800F79F0
269 0x800F7A20
270 0x800F7A54
355 0x8010A888
356 0x8010A8B0
357 0x8010AAA0
358 0x8010AAC0
+40
View File
@@ -0,0 +1,40 @@
/*
* func_800268C4 — 48 bytes at 0x800268C4..0x800268F4
*
* Guarded teardown: if a gp-relative state word is set, passes the ADDRESS of an
* adjacent gp-relative object to a callee and clears the state word.
*
* The observed instructions are:
* lw v0,540(gp) ; v0 = D_80121B54
* addiu sp,sp,-24
* beqz v0,0x800268E4 ; if (state == 0) skip
* sw ra,16(sp) ; save ra (delay slot)
* addiu a0,gp,536 ; a0 = &D_80121B50 <- gp-relative ADDRESS
* jal 0x80026E94
* nop
* sw zero,540(gp) ; D_80121B54 = 0
* E4: lw ra,16(sp)
* addiu sp,sp,24
* jr ra
* nop
*
* The callee receives the ADDRESS of a gp-relative object (`addiu a0,gp,536`),
* not a loaded value, so both symbols must be gp-marked in the registry: an
* address-shaped name that resolves implicitly loses the marker and the address
* materialises absolutely instead. See the F19 finding.
*
* LIMITS: the two gp offsets (536 = 0x218 and 540 = 0x21C) are hypotheses read
* from the instruction shape and are facts about this executable's gp layout.
* What the state word and the object mean is unknown and is not guessed here.
* Only the compiled bytes are evidence.
*/
extern int D_80121B50;
extern int D_80121B54;
void func_800268C4(void) {
if (D_80121B54 != 0) {
func_80026E94(&D_80121B50);
D_80121B54 = 0;
}
}
+37
View File
@@ -0,0 +1,37 @@
/* func_8002E3A8 — 0x8002E3A8..0x8002E3B8 (16 bytes).
*
* Original words:
* 27BDFFF0 addiu sp,sp,-16
* 27BD0010 addiu sp,sp,16
* 03E00008 jr ra
* 00000000 nop
*
* An empty function that nonetheless adjusts the stack by 16 bytes and puts it
* back — the whole body is a frame that is never used and never addresses
* anything.
*
* This is a **compiler artefact, not a body**: cc1 allocates the frame for a
* local object that the optimiser then removes, leaving the `sp` adjustment
* behind. A function written as truly empty (`void f(void) {}`) emits only
* `jr ra` / `nop`, so the source must have declared something with automatic
* storage that GCC dropped after the frame had been assigned — which is why the
* C below declares an unused local array rather than being an empty body. There is
* deliberately nothing else in it: adding any use of the local would emit a store
* and change the bytes.
*
* The frame is 16 bytes, so the dropped object was 16 bytes — a `char[16]`, an
* `int[4]`, or any aggregate of that size. Which one cannot be told from the
* bytes, because a removed local leaves no trace beyond its size.
*
* LIMITS: the reconstruction reproduces a *compiler behaviour* (leave the frame,
* drop the object) rather than a program statement. The declared size is chosen to
* make the frame come out at 16 and is an inference from the frame size alone, not
* evidence about the original's declaration. If a later reader finds a compiler
* that does not emit the frame for this spelling, the row is a compiler-behaviour
* negative and not a source problem.
*/
void func_8002E3A8(void)
{
char unused[16];
}
+48
View File
@@ -0,0 +1,48 @@
/*
* func_800658FC — 52 bytes at 0x800658FC..0x80065930
*
* Clears two 16-bit fields inside one element of a strided array reached through
* the argument, then calls a callee with the routine's own two arguments
* unchanged. The element offset is `index * 4 + 68`, computed before the frame is
* finished being set up.
*
* The observed instructions are:
* addiu sp,sp,-24
* sll v0,a1,0x2 ; index * 4
* sw ra,16(sp)
* lw v1,32(a0) ; v1 = p->ptr_20 (the array base)
* addiu v0,v0,68 ; index * 4 + 68
* addu v1,v1,v0 ; element = base + that (base first)
* sh zero,2(v1) ; element->half_02 = 0
* jal 0x80065794
* sh zero,0(v1) ; element->half_00 = 0 (delay slot)
* lw ra,16(sp)
* addiu sp,sp,24
* jr ra
* nop
*
* The callee is passed the routine's OWN arguments: `a0` and `a1` are never
* overwritten (only temporaries v0/v1 are used), so the call forwards `(p, index)`
* without any explicit argument setup.
*
* The element displacement must be folded into the POINTER, not into the two
* store displacements: the original emits `addiu v0,v0,68` (on the scaled index)
* then `addu` into the base, and both stores then use offset 0 and 2. Written as
* a flat `base + index * 4 + 68`, cc1 reassociates and emits the stores at
* displacements 68 and 70 instead, which is 16 bytes shorter (36 vs 52). Binding
* `index * 4 + 68` to its own local forces the original's shape.
*
* LIMITS: the array-base offset (0x20), the element stride (4), the element
* displacement (68) and the two cleared field offsets (0 and 2) are hypotheses
* read from the instruction shape; what the array holds is unknown and is not
* guessed here. Only the compiled bytes are evidence.
*/
void func_800658FC(char *p, int index) {
int offset = index * 4 + 68;
char *element = (char *)(*(int *)(p + 32) + offset);
*(short *)(element + 2) = 0;
*(short *)(element + 0) = 0;
func_80065794(p, index);
}
+37
View File
@@ -0,0 +1,37 @@
/*
* func_800697C4 — 56 bytes at 0x800697C4..0x800697FC
*
* Two pointer hops and a guarded call: reads a pointer out of a structure, then a
* second pointer out of that, and calls a callee with it only when it is
* non-null. The callee receives the pointer that was already in `a0`, so no
* argument setup is emitted.
*
* The observed instructions are:
* addiu sp,sp,-24
* sw ra,16(sp)
* lw v0,12(a0) ; v0 = p->ptr_0c
* nop
* lw a0,392(v0) ; a0 = v0->ptr_188
* nop
* beqz a0,0x800697EC ; if (q == 0) skip the call
* nop
* jal 0x800697A4
* nop
* EC: lw ra,16(sp)
* addiu sp,sp,24
* jr ra
* nop
*
* The `nop` after each load is maspsx's load-delay fill.
*
* LIMITS: the two pointer offsets (0x0c and 0x188) are hypotheses read from the
* instruction shape; what the structures are is unknown and is not guessed here.
* Only the compiled bytes are evidence.
*/
void func_800697C4(char *p) {
char *q = *(char **)(*(char **)(p + 12) + 392);
if (q != 0)
func_800697A4(q);
}
+41
View File
@@ -0,0 +1,41 @@
/* func_8008FF58 — 0x8008FF58..0x8008FF84 (44 bytes).
*
* Original words:
* 27BDFFE0 addiu sp,sp,-32
* 8FA20030 lw v0,48(sp) v0 = the incoming FIFTH argument
* AFA70010 sw a3,16(sp) outgoing stack argument 1 = the fourth argument
* 00003821 move a3,zero fourth register argument = 0
* AFBF0018 sw ra,24(sp)
* 0C023D83 jal 0x8008F60C
* AFA20014 _sw v0,20(sp) (delay slot) outgoing stack argument 2 = the fifth
* 8FBF0018 lw ra,24(sp)
* 27BD0020 addiu sp,sp,32
* 03E00008 jr ra
* 00000000 nop
*
* A six-argument call rebuilt from five: the callee's first three arguments pass
* through untouched, its fourth is forced to zero, and its fifth and sixth are the
* routine's own fourth and fifth, placed on the stack.
*
* The `lw v0,48(sp)` is the tell that the routine itself takes five arguments: the
* frame is 32 bytes, so the fifth argument sits at 0x30 in the caller's frame and
* the load reads it before the outgoing stack slots are written. Both outgoing
* stack values land at 0x10 and 0x14(sp), which are the caller's slots 5 and 6.
*
* The two stack stores are what make this a *shuffling* adapter rather than a
* pass-through: every register argument except `a3` is already correct, so only
* `a3` is overwritten and the two stack slots are filled.
*
* LIMITS: the frame size 32 and the incoming displacement 0x30 are read from the
* bytes; the arithmetic that places the fifth argument at 0x30 (frame 32 minus the
* 16-byte register-save area) is a consequence of the o32 convention rather than a
* source fact. The callee is named for its address and nothing establishes what the
* forced zero or the forwarded arguments mean.
*/
void func_8008F60C(int a0, int a1, int a2, int zero, int a4, int a5);
void func_8008FF58(int a0, int a1, int a2, int a3, int a4)
{
func_8008F60C(a0, a1, a2, 0, a3, a4);
}
+46
View File
@@ -0,0 +1,46 @@
/* func_800F79C0 — 0x800F79C0..0x800F79F0 (48 bytes).
*
* Original words:
* 3C028012 lui v0,0x8012
* 8C42FB4C lw v0,-1204(v0) v0 = D_8011FB4C (0x80120000 - 1204)
* 27BDFFE8 addiu sp,sp,-24
* AFBF0010 sw ra,16(sp)
* 8C420008 lw v0,8(v0) v0 = *(int *)(v0 + 8)
* 0040F809 jalr v0
* 00000000 _nop (delay slot)
* 8FBF0010 lw ra,16(sp)
* 27BD0018 addiu sp,sp,24
* 03E00008 jr ra
* 00000000 nop
*
* A virtual dispatch thunk: it reads a global pointer, fetches the function
* pointer stored at offset 8 of whatever it points to, and calls it with the
* incoming arguments untouched.
*
* The pointer load is hoisted **above** the frame setup — `lui`/`lw` precede
* `addiu sp,sp,-24` — which is the same scheduling seen in `0x8002E4C8`: cc1 moves
* a load that does not depend on `sp` ahead of the prologue. The indirect call
* carries a `nop` in its delay slot, so the target was not known and no
* independent instruction was available to fill it.
*
* The call site passes nothing: `a0`-`a3` are untouched, so the callee receives
* the caller's arguments unchanged and the thunk is transparent apart from the
* target it selects.
*
* `lui`+`lw` into the **same** register is the macro expansion of a symbol load
* (cookbook finding 2), so the global at 0x8011FB4C holds a pointer; whether it is
* an object whose offset 8 is a function pointer, or a table whose third entry is
* one, cannot be told from these bytes. Written here as an indirect call through
* the loaded value with the offset folded in, which is what the two `lw`s express.
*
* LIMITS: the displacement 8 and the frame layout are read from the bytes. The
* signature of the called function is entirely unconstrained — the thunk passes
* whatever it received, so no argument type can be inferred from this body.
*/
extern int D_8011FB4C;
void func_800F79C0(void)
{
(*(void (**)(void))(D_8011FB4C + 8))();
}
+35
View File
@@ -0,0 +1,35 @@
/* func_8010AAC0 — 0x8010AAC0..0x8010AAE4 (36 bytes).
*
* Original words:
* 27BDFFE8 addiu sp,sp,-24
* AFBF0010 sw ra,16(sp)
* 00802821 move a1,a0 second argument = the routine's first
* 0C03DE7C jal 0x800F79F0
* 24040004 _li a0,4 (delay slot) first argument = 4
* 8FBF0010 lw ra,16(sp)
* 27BD0018 addiu sp,sp,24
* 03E00008 jr ra
* 00000000 nop
*
* A one-call adapter that swaps the argument positions and pins the first: the
* routine's single parameter becomes the callee's **second** argument, and the
* callee's first argument is the constant 4. Nothing is returned.
*
* The `move a1,a0` is the whole content of the routine — there is no second
* parameter to forward, so this is the minimal case of the pass-through idiom:
* the incoming value is moved one register along and the freed register is loaded
* with a constant in the call's delay slot.
*
* LIMITS: that the freed register's constant is 4 rather than a symbol or a
* variable is read directly from `li a0,4`. The callee is named for its address
* and its signature is inferred from the two registers that are set; in
* particular whether it returns a value is invisible, since the result is not
* used, so the signature here is `void`.
*/
void func_800F79F0(int four, int arg);
void func_8010AAC0(int arg)
{
func_800F79F0(4, arg);
}