phase12: merge 15 (654 bodies), C's finding-44 direction, and the 3-of-6 reachability expectation
This commit is contained in:
@@ -91,6 +91,7 @@
|
||||
0x80025070 0x800250AC src/func_80025070.c
|
||||
0x8002515C 0x80025198 src/func_8002515C.c
|
||||
0x800254B0 0x800254EC src/func_800254B0.c
|
||||
0x800256F0 0x80025758 src/func_800256F0.c
|
||||
0x800259A0 0x800259DC src/func_800259A0.c
|
||||
0x80025A2C 0x80025ADC src/func_80025A2C.c
|
||||
0x80025ADC 0x80025B64 src/func_80025ADC.c
|
||||
|
||||
|
@@ -848,3 +848,48 @@ watching the aggregate, and D's own conclusion is the re-rank criterion: not "wh
|
||||
`differing_bytes=0`. Per cookbook 50 the flag is diagnosis-only and never attaches to a claim row, so
|
||||
it stays a negative, but the row is now the cleanest post-pass/scheduler candidate in A's pool.
|
||||
* **A's `0x8010400C`** and **C's `0x80107874`/`0x800F6ED0`**: classified with mechanisms, no lever left.
|
||||
|
||||
### Worker C gives finding 44 its concrete direction, and the phase an expectation
|
||||
|
||||
**`0x800F7930` (96 B), residual 72 bytes, is a PURE REGISTER-ROLE SWAP** — the original starts
|
||||
`move t0,a0` (record pointer into `$8`, freeing `a0` for the flag, which has the shorter live range);
|
||||
every spelling keeps the record in `a0`. Four spellings tied. Recorded as finding 44 seen in the
|
||||
mirror, because the useful part is the mechanism:
|
||||
|
||||
> When the original is BRANCHY, an `&&`/`||` written on the **assignment** side gets BRANCHLESS
|
||||
> codegen — the `&&` spelling compiles to `sltu v0,zero,v0; negu v0,v0; andi t0,v0,0x5`, which is 74
|
||||
> differing bytes and not the original's shape. The INVERTED spelling
|
||||
> `flag = 5; if (a == 0 || b == 0) flag = 0;` restores the branchy form (a `beqz` with `li a0,5`
|
||||
> hoisted into its delay slot, then a `bnez` skipping the `move a0,zero`) and drops the residual to
|
||||
> the register swap alone.
|
||||
|
||||
Finding 44's entry states this paired rule abstractly; this is the measured direction and the spelling.
|
||||
|
||||
**`0x800256F0` (104 B) is the FIRST prio-1 row in this batch where the recorded mechanism WAS the
|
||||
mechanism.** The note said "the original RELOADS the sub-object field after the call", and the store
|
||||
after the call genuinely re-dereferences — `*(char *)(*(int *)((char *)a0 + 32) + 44) = 1;` — so the
|
||||
`lw v1,32(s0)` after the `jal` is REQUIRED because the call may have changed `a0->sub`. Hoisting the
|
||||
field into a local and reusing it gives 96 bytes, eight short, because cc1 then keeps it in `a0`
|
||||
across the call and the re-load vanishes. Merged as a DEPENDENT claim: its callee `func_800F95CC` is
|
||||
not a registered region and the two-argument prototype is inferred from the call sites (the
|
||||
`move a1,a0` in each call's delay slot is why both arguments are the same pointer).
|
||||
|
||||
**The expectation-setting number, and it is the most important one for the milestone projection.**
|
||||
Worker C's outcome tally across its last six prio-1 rows:
|
||||
|
||||
| residual kind | count | closable by spelling? |
|
||||
|---|---|---|
|
||||
| a cc1 behaviour (reorg duplication ×2, a loop shape cc1 never emits) | 3 | **no** |
|
||||
| a clobber or arity fact (post-call re-load, 6-parameter signature, named binding) | 3 | yes |
|
||||
| genuinely a register/spelling problem | 1 | yes |
|
||||
|
||||
**So roughly half of the remaining prio-1 pool may be unreachable by source work.** That is not a
|
||||
reason to stop — it is the number the milestone projection has to be made against, and it is why the
|
||||
`negatives-d-unattempted.tsv` band (74 rows nobody pushed to a floor) matters: those rows are
|
||||
unreachable-looking only because nobody has asked them the question with the current lever set.
|
||||
|
||||
**Also recorded, in the workers' favour and against my own instruction:** worker C noted that its
|
||||
offer to label the two buckets is now backed by six measured rows. The offer was still declined, for a
|
||||
precise reason worth stating — an OUTCOME tally of six finished rows is a measurement of the past,
|
||||
while the note-wording proxy I tested is a PREDICTOR for the remaining rows, and the predictor failed
|
||||
at 3/7. The tally is recorded as the tally; it does not re-rank anything.
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* func_800256F0 — 104 bytes at 0x800256F0..0x80025758
|
||||
*
|
||||
* PHASE 12 WORKER C. **A DEPENDENT CLAIM (charter rule 2): the callee `func_800F95CC` is not a
|
||||
* registered region**, so the two-argument prototype below is INFERRED from the call sites. Only
|
||||
* the call sites' register setup is reproduced.
|
||||
*
|
||||
* MATCH: candidate_bytes=104, differing_bytes=0, result=MATCH, exit 0 on the DEFAULT toolchain.
|
||||
*
|
||||
* The observed instructions are:
|
||||
* addiu sp,sp,-24
|
||||
* sw s0,16(sp)
|
||||
* move s0,a0 s0 = a0 (the argument must survive the calls)
|
||||
* bnez s0,0x8002570C if (a0 == 0)
|
||||
* sw ra,20(sp) (delay slot)
|
||||
* j 0x80025744
|
||||
* li v0,24 (delay slot) return 24;
|
||||
* 0C: lw a0,32(s0) sub = *(int *)((char *)a0 + 32)
|
||||
* nop
|
||||
* beqz a0,0x80025734 if (sub != 0) {
|
||||
* nop
|
||||
* jal 0x800F95CC
|
||||
* move a1,a0 (delay slot) func_800F95CC(sub, sub);
|
||||
* lw v1,32(s0) *** THE FIELD IS RE-LOADED HERE ***
|
||||
* li v0,1
|
||||
* j 0x80025740
|
||||
* sb v0,44(v1) (delay slot) *(char *)(*(int *)((char *)a0 + 32) + 44) = 1;
|
||||
* 34: move a0,s0 } else {
|
||||
* jal 0x800F95CC
|
||||
* move a1,a0 (delay slot) func_800F95CC(a0, a0);
|
||||
* 40: move v0,zero }
|
||||
* 44: lw ra,20(sp) return 0;
|
||||
* lw s0,16(sp)
|
||||
* addiu sp,sp,24
|
||||
* jr ra
|
||||
* nop
|
||||
*
|
||||
* THE LEVER IS THE RE-LOAD, and it is a CLOBBER fact rather than a spelling: the store after the
|
||||
* call dereferences the field AGAIN (`*(char *)(*(int *)((char *)a0 + 32) + 44) = 1;`), so the
|
||||
* `lw v1,32(s0)` after the `jal` is required — the call may have changed `a0->sub`. **Measured:
|
||||
* hoisting the loaded value into a local and reusing it (`sub` in both the call and the store)
|
||||
* gives candidate_bytes=96, eight bytes short**, because cc1 then keeps `sub` in `a0` across the
|
||||
* call and the re-load disappears. The recorded note ("LENGTH-MISMATCH 108 vs 104 ... the original
|
||||
* RELOADS the sub-object field after the call") names exactly this, and it is finding 170/178's
|
||||
* family — a value that must be re-derived because a call can clobber the source of the value —
|
||||
* reached from the STORE side rather than from an entry-block copy.
|
||||
*
|
||||
* The `move a1,a0` in each call's delay slot is why both arguments are the same pointer
|
||||
* (`func_800F95CC(sub, sub)` and `func_800F95CC(a0, a0)`): the compiler set `a0` first and the
|
||||
* second argument copies it. `return 24` on the null path is a literal from the instruction
|
||||
* stream, and `sw ra,20(sp)` in the first branch's delay slot is the frame setup scheduled there.
|
||||
*
|
||||
* LIMITS: the offsets (32 and 44) and the `char` store at +44 are read off the instruction
|
||||
* stream; the object types and the meaning of the flag and of the return value 24 are unknown, so
|
||||
* the body is written as explicit pointer arithmetic. The callee's arity and parameter types are
|
||||
* INFERRED. Only the compiled bytes are evidence.
|
||||
*/
|
||||
|
||||
extern void func_800F95CC(int a0, int a1);
|
||||
|
||||
int func_800256F0(int a0)
|
||||
{
|
||||
int sub;
|
||||
|
||||
if (a0 == 0)
|
||||
return 24;
|
||||
sub = *(int *)((char *)a0 + 32);
|
||||
if (sub != 0) {
|
||||
func_800F95CC(sub, sub);
|
||||
*(char *)(*(int *)((char *)a0 + 32) + 44) = 1;
|
||||
} else {
|
||||
func_800F95CC(a0, a0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* func_800A8920 — 100 bytes at 0x800A8920..0x800A8984
|
||||
*
|
||||
* Hypothesis, not a claim about meaning: an indexed lookup with a NULL guard. For an index below
|
||||
* 8 it forms a pointer into a table of 24-byte records at 0x8013BF78; otherwise the pointer is
|
||||
* NULL. It then runs an argument-less helper, and if the pointer is non-NULL it returns the low
|
||||
* byte of a second call's result, else 0.
|
||||
*
|
||||
* Original words:
|
||||
* 27BDFFE8 addiu sp,sp,-24
|
||||
* 2C820008 sltiu v0,a0,8 ; UNSIGNED compare
|
||||
* AFBF0014 sw ra,20(sp)
|
||||
* 14400003 bnez v0,0x800A893C ; index < 8 -> BRANCH INTO THE BODY
|
||||
* AFB00010 sw s0,16(sp) ; (delay)
|
||||
* 0802A255 j 0x800A8954 ; otherwise skip the body
|
||||
* 00008021 move s0,zero ; (delay) p = 0
|
||||
* $B: 00041040 sll v0,a0,0x1
|
||||
* 00441021 addu v0,v0,a0 ; v0 = index * 3
|
||||
* 000210C0 sll v0,v0,0x3 ; v0 = index * 24
|
||||
* 3C038014 lui v1,0x8014
|
||||
* 2463BF78 addiu v1,v1,-16520 ; v1 = 0x8013BF78 (the symbol form, `addiu`)
|
||||
* 00438021 addu s0,v0,v1 ; p = (index * 24) + base INDEX FIRST
|
||||
* $C: 0C02A089 jal 0x800A8224 ; the argument-less helper
|
||||
* 00000000 nop
|
||||
* 12000004 beqz s0,0x800A8970 ; p == 0 -> the shared exit
|
||||
* 00001021 move v0,zero ; (delay) return 0
|
||||
* 0C02A228 jal 0x800A88A0 ; second helper
|
||||
* 02002021 move a0,s0 ; (delay) its argument
|
||||
* 304200FF andi v0,v0,0xFF ; low byte
|
||||
* $E: 8FBF0014 lw ra,20(sp)
|
||||
* 8FB00010 lw s0,16(sp)
|
||||
* 27BD0018 addiu sp,sp,24
|
||||
* 03E00008 jr ra
|
||||
* 00000000 nop
|
||||
*
|
||||
* THREE SPELLINGS (108 / 108 / 100), AND BOTH HALVES OF THE ROW ARE MIRRORED-VERSUS-NATURAL
|
||||
* LAYOUT DECISIONS — the same family as cookbook 28/77, one per if-statement:
|
||||
*
|
||||
* (1) THE FIRST if/else MUST BE WRITTEN INVERTED WITH THE ARMS SWAPPED. The natural
|
||||
* `if (a0 < 8) p = <table>; else p = 0;` gives `beqz v0,<else>` with the table computation
|
||||
* inline (+8 bytes). `if (a0 >= 8) p = 0; else p = <table>;` reproduces the original's
|
||||
* `bnez v0,<body>` with `move s0,zero` in the following `j`'s delay slot — i.e. the short
|
||||
* path inline and the long path as the branch target, which is cookbook 132's stated
|
||||
* direction ("when the original's short path is the fall-through, invert the condition").
|
||||
* (2) THE TAIL MUST BE THE POSITIVE TEST: `if (p != 0) return func(p) & 0xff; return 0;`.
|
||||
* Written as `if (p == 0) return 0; return func(p) & 0xff;` cc1 out-lines the `return 0`
|
||||
* block, leaves a `nop` in the `beqz` delay slot and adds a `j` (+8 bytes, 108 total). Only
|
||||
* the positive spelling puts `move v0,zero` in the `beqz s0` DELAY SLOT, which is what the
|
||||
* original has.
|
||||
* Note both fixes are needed: (1) alone still gives 108 bytes.
|
||||
*
|
||||
* The pointer arithmetic is written with the STRIDE FIRST — `(char *)(a0 * 24) + (int)D_8013BF78`
|
||||
* — because that is the spelling that produces the original's INDEX-FIRST `addu s0,v0,v1`
|
||||
* (cookbook 22's third row). `sltiu` makes `a0` UNSIGNED (cookbook 48). The base is materialised
|
||||
* with `addiu`, i.e. the symbol form rather than a literal (cookbook 4/89).
|
||||
*
|
||||
* LIMITS: the function name, both callees, the table base, the record stride 24, the index bound
|
||||
* 8 and the low-byte mask are read from the instruction encodings; only the bytes are evidence.
|
||||
* Nothing establishes the table's layout or what the two helpers do. The table base is referenced
|
||||
* as an address-named symbol (`extern char D_8013BF78[]`) because no registry row names it.
|
||||
*/
|
||||
|
||||
extern char D_8013BF78[];
|
||||
|
||||
void func_800A8224(void);
|
||||
int func_800A88A0(char *);
|
||||
|
||||
int func_800A8920(unsigned int a0)
|
||||
{
|
||||
char *p;
|
||||
|
||||
if (a0 >= 8)
|
||||
p = 0;
|
||||
else
|
||||
p = (char *)(a0 * 24) + (int)D_8013BF78;
|
||||
func_800A8224();
|
||||
if (p != 0)
|
||||
return func_800A88A0(p) & 0xff;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* func_800FB758 — 112 bytes at 0x800FB758..0x800FB7C8
|
||||
*
|
||||
* A guarded teardown: if the "active" flag is set, run two conditional/one
|
||||
* unconditional helper calls, hand a one-byte parameter block to a fourth
|
||||
* helper, clear the flag and stamp a second global with -1.
|
||||
*
|
||||
* The observed instructions are:
|
||||
* lw v0,0x7ac(gp) if (D_801220E4 == 0) return; (before the frame)
|
||||
* addiu sp,sp,-0x20
|
||||
* beq v0,zero,epilogue
|
||||
* _sw ra,0x18(sp) (delay slot)
|
||||
* lw v0,0x7b0(gp) \
|
||||
* nop | if (D_801220E8 != 0) 0x800FBD80();
|
||||
* beq v0,zero,skip |
|
||||
* _nop |
|
||||
* jal 0x800FBD80 / (delay slot nop)
|
||||
* _nop
|
||||
* skip:
|
||||
* jal 0x80107AA0 \
|
||||
* _move a0,zero / 0x80107AA0(0)
|
||||
* lw a0,0x7d0(gp) \
|
||||
* jal 0x800F8B6C / 0x800F8B6C(D_80122108)
|
||||
* _nop
|
||||
* li a0,14 \
|
||||
* li v0,0x80 |
|
||||
* addiu a1,sp,0x10 | b = 128; 0x800F8CBC(14, &b);
|
||||
* jal 0x800F8CBC |
|
||||
* _sb v0,0x10(sp) / (the store is the call's delay slot)
|
||||
* li v0,-1
|
||||
* sw zero,0x7ac(gp) D_801220E4 = 0
|
||||
* lui at,0x8012 \
|
||||
* sw v0,0x2140(at) / *(int *)0x80122140 = -1
|
||||
* epilogue:
|
||||
* lw ra,0x18(sp)
|
||||
* nop <- the release is NOT here
|
||||
* jr ra
|
||||
* addiu sp,sp,0x20 <- IT IS IN THE JUMP DELAY SLOT
|
||||
*
|
||||
* REGION TOKEN: `maspsx=epilogue`, decided from the CANDIDATE's tail
|
||||
* (cookbook 147/165/180). This row is the one the negatives index recorded as
|
||||
* "rare-epilogue-order-35b; residual 6B at 0x800FB7BC", and it closes on the
|
||||
* FIRST spelling once the token is applied. It is SHAPE A of cookbook 144 — `lw ra`
|
||||
* immediately precedes the frame release, so the transform moves the release into
|
||||
* the jump slot AND inserts the load-delay `nop` after `lw ra`. That inserted `nop`
|
||||
* is the 6-byte residual the index recorded: the earlier attempts are all recorded
|
||||
* as compiler/maspsx switches (`--no-maspsx`, `--cc1 gcc-2.91.66-psx`,
|
||||
* `--cc1 gcc-2.8.1-psx`), i.e. the token is what was missing, not a compiler.
|
||||
*
|
||||
* THE LITERAL ADDRESS FOR D_80122140 IS DELIBERATE AND IS NOT A GP TRAP IN
|
||||
* REVERSE. `config/symbols.tsv` marks D_80122140 `gp`, but the original writes it
|
||||
* with an ABSOLUTE `lui at,0x8012` / `sw v0,0x2140(at)` pair. Spelling it as the
|
||||
* symbol would let the harness rewrite the access to `%gp_rel(...)($gp)` and the
|
||||
* row would fail; the literal keeps the absolute encoding. This is the per-SITE
|
||||
* gp-versus-absolute behaviour of cookbook 16/46 — the registry's marker is
|
||||
* per-SYMBOL, the original is per-access — and the harness rewrite is inert on a
|
||||
* literal by construction (cookbook 189's neighbourhood).
|
||||
*
|
||||
* The three other globals ARE read through `gp` (0x7ac / 0x7b0 / 0x7d0), so they
|
||||
* are spelled as symbols and are registered `gp`: 0x801220E4, 0x801220E8 and
|
||||
* 0x80122108.
|
||||
*
|
||||
* The frame is 32 bytes: the 16-byte o32 outgoing argument area, the one-byte
|
||||
* local handed to 0x800F8CBC at sp+0x10, `ra` at sp+0x18 and the alignment gap at
|
||||
* sp+0x14.
|
||||
*
|
||||
* LIMITS: the globals' names and widths, the helper signatures (only the argument
|
||||
* registers actually set are evidenced) and the meaning of the flag and of the
|
||||
* -1 stamp are hypotheses read off the instruction shape. Only the compiled bytes
|
||||
* are evidence.
|
||||
*/
|
||||
|
||||
extern int D_801220E4;
|
||||
extern int D_801220E8;
|
||||
extern int D_80122108;
|
||||
|
||||
void func_800FBD80(void);
|
||||
void func_80107AA0(int a0);
|
||||
void func_800F8B6C(int a0);
|
||||
void func_800F8CBC(int a0, char *a1);
|
||||
|
||||
void func_800FB758(void)
|
||||
{
|
||||
char b;
|
||||
|
||||
if (D_801220E4 == 0)
|
||||
return;
|
||||
|
||||
if (D_801220E8 != 0)
|
||||
func_800FBD80();
|
||||
|
||||
func_80107AA0(0);
|
||||
func_800F8B6C(D_80122108);
|
||||
|
||||
b = 128;
|
||||
func_800F8CBC(14, &b);
|
||||
|
||||
D_801220E4 = 0;
|
||||
*(int *)0x80122140 = -1;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
typedef struct {
|
||||
int f0;
|
||||
char pad[28];
|
||||
} Rec_801461C8;
|
||||
|
||||
extern Rec_801461C8 *D_801461C8;
|
||||
|
||||
void func_801092C0(int start, int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
D_801461C8[i + start].f0 = 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user