phase11: merge 22 + cookbook 99-102 — 538 bodies / 547 regions
Worker A's 0x800556E8 and 0x80055654 (both first/second attempt). Cookbook 99 is a DISPATCH rule, not a codegen one: take the row ADJACENT to one you just matched. The binary is laid out by translation unit, so neighbours share the author's habits. 3 for 3, all first or second attempt, and it beat both the size ranker and the LRS ranker. Cookbook 100 puts the three branch-shaped diagnostics side by side -- each maps a residual shape to exactly one cause and each is a glance rather than a spelling: branch displacement words only -> block NESTING (95) first few instructions, right length -> then/else ORDER of a single-statement arm whole prologue, same multiset -> declaration vs assignment order Vector copies are now confirmed on FIVE independent rows.
This commit is contained in:
@@ -204,6 +204,8 @@
|
||||
0x8005182C 0x80051864 src/func_8005182C.c
|
||||
0x80051864 0x800518BC src/func_80051864.c
|
||||
0x80052C98 0x80052CAC src/func_80052C98.c
|
||||
0x80055654 0x800556E8 src/func_80055654.c
|
||||
0x800556E8 0x8005584C src/func_800556E8.c
|
||||
0x8005584C 0x800558BC src/func_8005584C.c
|
||||
0x80057524 0x80057564 src/func_80057524.c
|
||||
0x80057564 0x800575C4 src/func_80057564.c
|
||||
|
||||
|
@@ -1514,3 +1514,53 @@ residency with a whole-struct write, or an escape cc1 cannot see through.
|
||||
**This is a genuinely open question and I am recording it as one.** Any worker who has hit
|
||||
"original spills everything, cc1 promotes" should report the recipe. Compare finding 64 (memory
|
||||
residence as a load-bearing property of finding 59's family).
|
||||
|
||||
### 99. TAKE THE ROW ADJACENT TO ONE YOU JUST MATCHED (worker A — 3 for 3)
|
||||
|
||||
Worker A's picking heuristic, and it beat both the size ranker and the LRS ranker outright:
|
||||
|
||||
> **The compiled binary is laid out by translation unit, so the neighbours of a matched row share
|
||||
> the author's habits** — same code family, same idioms, same sub-object layout.
|
||||
|
||||
matched 0x80036DA4 -> next: 0x80036B14 (1 attempt)
|
||||
matched 0x8005584C -> next: 0x800556E8 (1 attempt)
|
||||
matched 0x800556E8 -> next: 0x80055654 (2 attempts)
|
||||
|
||||
**3 for 3, all first or second attempt.** It costs one pass over the worklist: for each unclaimed
|
||||
row, compare its start/end against the claimed rows' ends/starts. **This composes with the
|
||||
redundancy ranker** — use redundancy to pick the *first* row in an area, then adjacency to harvest
|
||||
its neighbours.
|
||||
|
||||
### 100. THREE BRANCH-SHAPED DIAGNOSTICS, side by side (worker A)
|
||||
|
||||
These are the highest-value checks in the project because each is a *glance* rather than a
|
||||
spelling, and each maps a residual shape to exactly one cause:
|
||||
|
||||
| residual shape | cause | finding |
|
||||
|---|---|---|
|
||||
| a handful of **branch displacement** words, everything else identical | **block NESTING** is wrong | 95 |
|
||||
| the **first few instructions**, correct length | **then/else ORDER** of a single-statement arm | 100 |
|
||||
| the **whole prologue**, same instruction multiset | **declaration vs assignment** order | (0x8005E820) |
|
||||
|
||||
**The then/else case (`0x80055654`):** `if (a0 == D1) k = 0; else if (a0 == D2) k = 1; else k = -1;`
|
||||
gives the **CORRECT LENGTH (148) and 34 differing bytes, all in the first four instructions.** cc1
|
||||
lays the `k = 0` arm out first (fall-through) and jumps to `k = 1`; the original jumps *forward* to
|
||||
`k = 0` and keeps `k = 1` as the fall-through. **Spelling the outermost test INVERTED with the big
|
||||
block as the `then`** — `if (a0 != D1) { if (a0 == D2) k = 1; else k = -1; } else k = 0;` — is exact.
|
||||
|
||||
**So when a residual is confined to the branch DIRECTION of the first few instructions, try the
|
||||
`!=` spelling of the outermost test before touching anything else.**
|
||||
|
||||
### 101. Absolute value: spell it as a SWAPPED SUBTRACTION, not `-x` (worker A)
|
||||
|
||||
`x = a1[0] - a0[0]` matches; `x = -x` emits `negu` and **costs 8 bytes**. Also on that row:
|
||||
`y = ...` must be **assigned after** the x branch — an initialiser gives 124 B instead of 136.
|
||||
|
||||
### 102. Vector copies: FIVE independent confirmations
|
||||
|
||||
Struct-assignment vector copies (4 loads then 4 stores) are now confirmed on five independent rows:
|
||||
`0x8005584C`, `0x8009F798`, `0x80036DA4`, `0x80036B14`, `0x800556E8`. Treat it as a rule.
|
||||
|
||||
Companions, re-confirmed: **OR-chain `||` codegen** — every `bnez` jumps to the SAME forward target
|
||||
and the last term inverts to skip the body; `t.v[0]=t.v[1]=t.v[2]=*a3` reads one address three times
|
||||
with no CSE; and **byte offsets, not `int *` arithmetic** (`244(v0)` vs `p+244` = +976).
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* func_80068910 — 204 bytes at 0x80068910..0x800689DC
|
||||
*
|
||||
* Hypothesis, not a claim about meaning: seeds a 4-int vector from an argument, derives a
|
||||
* second vector as the 12-bit-shifted difference between two earlier vectors, copies the
|
||||
* result forward, and derives a third vector as an unshifted difference. It is a leaf with
|
||||
* no calls and a completely repetitive body, which is why it matched on the FIRST spelling.
|
||||
*
|
||||
* Original words (int-index form; byte offsets are 4x):
|
||||
* 8CA20000 lw v0,0(a1) ; *(V *)(a0+64) = *(V *)a1 -- 4 loads then 4 stores
|
||||
* 8CA30004 lw v1,4(a1)
|
||||
* 8CA20008 lw a2,8(a1)
|
||||
* 8CA3000C lw a3,12(a1)
|
||||
* ACA20040 sw v0,64(a0)
|
||||
* ACA30044 sw v1,68(a0)
|
||||
* ACA20048 sw a2,72(a0)
|
||||
* ACA3004C sw a3,76(a0)
|
||||
* 8C820040 lw v0,64(a0) ; a0[20] = a0[16] - a0[0]
|
||||
* 8C850000 lw a1,0(a0)
|
||||
* 8C830044 lw v1,68(a0)
|
||||
* 8C860004 lw a2,4(a0)
|
||||
* 00451023 subu v0,v0,a1
|
||||
* AC820050 sw v0,80(a0)
|
||||
* 8C820048 lw v0,72(a0) ; a0[21] = a0[17] - a0[1]
|
||||
* 8C850008 lw a1,8(a0)
|
||||
* 00660823 subu v1,v1,a2
|
||||
* AC830054 sw v1,84(a0)
|
||||
* 8C830050 lw v1,80(a0) ; a0[22] = a0[18] - a0[2]
|
||||
* 00450823 subu v0,v0,a1
|
||||
* AC820058 sw v0,88(a0)
|
||||
* 8C820054 lw v0,84(a0) ; a0[20] <<= 12
|
||||
* 00042300 sll v1,v1,0xc
|
||||
* AC830050 sw v1,80(a0)
|
||||
* 8C830058 lw v1,88(a0) ; a0[21] <<= 12, a0[22] <<= 12
|
||||
* 00042300 sll v0,v0,0xc
|
||||
* 00042300 sll v1,v1,0xc
|
||||
* AC820054 sw v0,84(a0)
|
||||
* AC830058 sw v1,88(a0)
|
||||
* 8C820050 lw v0,80(a0) ; *(V *)(a0+96) = *(V *)(a0+80)
|
||||
* ... AC82006C sw a2,108(a0)
|
||||
* 8C820060 lw v0,96(a0) ; a0[28] = a0[24] - a0[8]
|
||||
* 8C830020 lw v1,32(a0)
|
||||
* 8C850024 lw a1,36(a0)
|
||||
* 8C860028 lw a2,40(a0)
|
||||
* 00431023 subu v0,v0,v1
|
||||
* AC820070 sw v0,112(a0)
|
||||
* 8C820064 lw v0,100(a0) ; a0[29] = a0[25] - a0[9]
|
||||
* 8C830068 lw v1,104(a0) ; a0[30] = a0[26] - a0[10]
|
||||
* 00450823 subu v0,v0,a1
|
||||
* 00660823 subu v1,v1,a2
|
||||
* AC820074 sw v0,116(a0)
|
||||
* 24020001 li v0,1 ; return 1
|
||||
* 03E00008 jr ra
|
||||
* AC830078 sw v1,120(a0) ; (delay)
|
||||
*
|
||||
* BYTE-REQUIRED SHAPES:
|
||||
*
|
||||
* 1. **Both vector copies are STRUCT ASSIGNMENTS** (`*(struct V *)(a0 + 16) = *(struct V *)a1;`
|
||||
* and `*(struct V *)(a0 + 24) = *(struct V *)(a0 + 20);`). Sixth independent
|
||||
* confirmation of this lever. Four element stores interleave load/store and change the
|
||||
* bytes.
|
||||
* 2. **The shifts are three separate `<<= 12` statements on three separate elements**, not
|
||||
* a loop or a compound expression — cc1 emits `sll` per element in the original's order.
|
||||
* 3. **The elements are addressed as `a0[N]` with `int *`** so the offsets stay in the
|
||||
* 16-byte-stride layout (16/20/24/28 = the four vectors, plus the inputs at 0/8).
|
||||
*
|
||||
* LIMITS: the function name, the object layout (vectors at int offsets 16, 20, 24, 28 and
|
||||
* the inputs at 0 and 8) and the meaning of the 12-bit shift (a fixed-point conversion) are
|
||||
* hypotheses read from the instruction shape; only the bytes are evidence. The `V` struct is
|
||||
* used purely to obtain the 4-word block moves. No callee: this is a leaf and the harness
|
||||
* emitted the common epilogue itself.
|
||||
*/
|
||||
|
||||
struct V { int v[4]; };
|
||||
|
||||
int func_80068910(int *a0, int *a1)
|
||||
{
|
||||
*(struct V *)(a0 + 16) = *(struct V *)a1;
|
||||
a0[20] = a0[16] - a0[0];
|
||||
a0[21] = a0[17] - a0[1];
|
||||
a0[22] = a0[18] - a0[2];
|
||||
a0[20] <<= 12;
|
||||
a0[21] <<= 12;
|
||||
a0[22] <<= 12;
|
||||
*(struct V *)(a0 + 24) = *(struct V *)(a0 + 20);
|
||||
a0[28] = a0[24] - a0[8];
|
||||
a0[29] = a0[25] - a0[9];
|
||||
a0[30] = a0[26] - a0[10];
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* func_800FA5D8 — 132 bytes at 0x800FA5D8..0x800FA65C
|
||||
*
|
||||
* Byte-identical reconstruction of a framed dispatcher that reports a flag word
|
||||
* to a callee twice and conditionally publishes a single global.
|
||||
*
|
||||
* The observed instructions are:
|
||||
* addiu sp,sp,-32
|
||||
* sw s0,24(sp) the flag word is live across the call
|
||||
* move s0,a0
|
||||
* li a0,14 \ func_800F8B80(14, buf, 0)
|
||||
* addiu a1,sp,16 | buf is an 8-byte local at sp+16
|
||||
* move a2,zero /
|
||||
* sw ra,28(sp)
|
||||
* jal 0x800F8B80
|
||||
* sb s0,16(sp) (delay slot) buf[0] = flags
|
||||
* andi v0,s0,0x100
|
||||
* beqz v0,0x800FA63C (slot: andi v0,s0,0x20)
|
||||
* beqz v0,0x800FA61C (slot: li v0,1)
|
||||
* j 0x800FA624
|
||||
* sw zero,25152(at) (delay slot) D_80146240 = 0
|
||||
* lui at,0x8014 \
|
||||
* sw v0,25152(at) / D_80146240 = 1
|
||||
* lui a0,0x8010 \ func_800F8F9C(&D_80107458)
|
||||
* jal 0x800F8F9C |
|
||||
* addiu a0,a0,29784 / (delay slot)
|
||||
* lui a0,0x8010 \ func_800F8B6C(&D_800FA65C)
|
||||
* jal 0x800F8B6C |
|
||||
* addiu a0,a0,-22948 / (delay slot)
|
||||
* li a0,27 \ func_800F8B80(27, 0, 0)
|
||||
* move a1,zero |
|
||||
* jal 0x800F8B80 |
|
||||
* move a2,zero / (delay slot)
|
||||
* lw ra,28(sp)
|
||||
* lw s0,24(sp)
|
||||
* jr ra
|
||||
* addiu sp,sp,32
|
||||
*
|
||||
* Both `lui`/`addiu` pairs are the **symbol** address form (cookbook finding 5):
|
||||
* a literal address would materialise as `lui`+`ori`. The two constants are
|
||||
* therefore written as the addresses of address-named placeholder symbols
|
||||
* (`D_80107458`, `D_800FA65C`), which the implicit address-symbol rule resolves.
|
||||
* `0x800FA65C` is this region's own end, i.e. the entry of the next function.
|
||||
*
|
||||
* LIMITS: the function and global names are address placeholders reconstructed
|
||||
* from the disassembly; only the compiled bytes are evidence. The parameter is
|
||||
* an `int` because `andi` masks its full 32-bit value — the width of the flag
|
||||
* word beyond that is unproven. `buf` is declared `char[8]` because only byte 0
|
||||
* is stored and sp+16..23 is the whole local area; the callee's use of the rest
|
||||
* of it is not observable here. The three callees' signatures are inferred from
|
||||
* register usage alone.
|
||||
*/
|
||||
|
||||
extern int D_80146240;
|
||||
extern char D_80107458;
|
||||
extern char D_800FA65C;
|
||||
extern void func_800F8B80(int, void *, int);
|
||||
extern void func_800F8F9C(int);
|
||||
extern void func_800F8B6C(int);
|
||||
|
||||
void func_800FA5D8(int flags) {
|
||||
char buf[8];
|
||||
|
||||
buf[0] = flags;
|
||||
func_800F8B80(14, buf, 0);
|
||||
if (flags & 0x100) {
|
||||
if (flags & 0x20) {
|
||||
D_80146240 = 0;
|
||||
} else {
|
||||
D_80146240 = 1;
|
||||
}
|
||||
func_800F8F9C((int)&D_80107458);
|
||||
func_800F8B6C((int)&D_800FA65C);
|
||||
}
|
||||
func_800F8B80(27, 0, 0);
|
||||
}
|
||||
Reference in New Issue
Block a user