phase12: merge 16 (657 bodies), three-worker per-site gp evidence, C's decline endorsed, A's cross-jump test
This commit is contained in:
@@ -106,6 +106,7 @@
|
||||
0x800262F8 0x80026304 src/func_800262F8.c
|
||||
0x800263A8 0x800263E8 src/func_800263A8.c
|
||||
0x80026650 0x800266A8 src/func_80026650.c
|
||||
0x800266A8 0x800266EC src/func_800266A8.c
|
||||
0x800267C0 0x800267CC src/func_800267C0.c
|
||||
0x800268C4 0x800268F4 src/func_800268C4.c
|
||||
0x80026C2C 0x80026C7C src/func_80026C2C.c
|
||||
@@ -470,6 +471,7 @@
|
||||
0x800A745C 0x800A74BC src/func_800A745C.c
|
||||
0x800A74BC 0x800A74D0 src/func_800A74BC.c
|
||||
0x800A8224 0x800A82D0 src/func_800A8224.c
|
||||
0x800A8920 0x800A8984 src/func_800A8920.c
|
||||
0x800A8B48 0x800A8B8C src/func_800A8B48.c
|
||||
0x800A9C24 0x800A9CC4 src/func_800A9C24.c
|
||||
0x800A9D58 0x800A9D90 src/func_800A9D58.c
|
||||
@@ -588,6 +590,7 @@
|
||||
0x800FB5D4 0x800FB5DC src/func_800FB5D4.c
|
||||
0x800FB5E4 0x800FB5FC src/func_800FB5E4.c
|
||||
0x800FB6C4 0x800FB6D8 src/func_800FB6C4.c
|
||||
0x800FB758 0x800FB7C8 src/func_800FB758.c maspsx=epilogue
|
||||
0x800FBDF8 0x800FBE04 src/func_800FBDF8.c
|
||||
0x800FBE04 0x800FBE84 src/func_800FB13C.c
|
||||
0x800FBE84 0x800FBF5C src/func_800FBE84.c
|
||||
|
||||
|
@@ -195,6 +195,7 @@ D_8012215C 0x8012215C gp
|
||||
D_80122160 0x80122160 gp
|
||||
D_80122164 0x80122164 gp
|
||||
D_80122168 0x80122168 gp
|
||||
D_8012216C 0x8012216C gp
|
||||
D_80122170 0x80122170 gp
|
||||
D_80122174 0x80122174 gp
|
||||
D_80122178 0x80122178 gp
|
||||
|
||||
|
@@ -893,3 +893,68 @@ offer to label the two buckets is now backed by six measured rows. The offer was
|
||||
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.
|
||||
|
||||
### The harness-class hypothesis in the index is wrong on TWO rows (worker C)
|
||||
|
||||
The index marks rows as `HARNESS ROW (finding-40 class)`, and **finding 40 is the `nopmarker`
|
||||
predicate**. Worker C measured both tokens on `0x800FBB20` (128 B): **`--honour-nop-marker` changes
|
||||
nothing (132 bytes either way), while `--fill-epilogue` takes it to 128 = the correct length.** The
|
||||
original ends `lw ra,28(sp) / lw s0,24(sp) / jr ra / addiu sp,sp,32` — the release IN the jump's slot
|
||||
— which is **shape B** of the epilogue transform (the `lw ra` is not immediately before the release;
|
||||
`lw s0,24(sp)` sits between them), so the release moves into the slot and the trailing nop is
|
||||
DROPPED. A previous session read it as a `nopmarker` row and stopped at 132.
|
||||
|
||||
That is the second row this session whose recorded harness class named the wrong token family, and
|
||||
its remaining residual is **1 byte**: at `0x800FBB4C` the original has `move a1,a0` (`0x00802821`,
|
||||
copying the register `move a0,zero` just set) where every spelling gives `move a1,zero`
|
||||
(`0x00002821`, re-materialised from `$zero`). The source passed the same value twice and the
|
||||
original cc1 reused `a0`; ours prefers `$zero` for a duplicated constant. Four spellings
|
||||
(`f(0,0)`; a named `int z = 0;` at top; the same inside the `if`; `char *z = 0;` twice) all give
|
||||
`move a1,zero`. **Untried lever, recorded for whoever takes it: a duplicated value that is NOT
|
||||
compile-time constant, so cc1 must keep it in `a0`.**
|
||||
|
||||
### Worker C declined `0x800F6948` on cost, and the judgement is endorsed
|
||||
|
||||
Not a classification — a **cost judgement, reported rather than taken**, and it is the right call:
|
||||
the row is a trampoline with **5 callees (one unregistered), a 9-argument signature whose 9th
|
||||
parameter is read from `56(sp)`, and its own outgoing stack argument area**, and a previous session's
|
||||
floor on it is `116 / differing_bytes=4`. Reconstructing five prototypes and a nine-parameter
|
||||
signature to chase 4 bytes, **at a dependency surface where a wrong prototype is invisible until the
|
||||
gate**, is a bad trade against two rows with independent structures — and C said so and asked rather
|
||||
than deciding silently or grinding. Recorded as `NOT ATTEMPTED, cost-declined` with the reason, so
|
||||
the row is not silently dropped. **This is the behaviour I want from all four workers: a cost
|
||||
judgement stated, with the evidence, and the call handed up.**
|
||||
|
||||
### Worker A adds a cheap predictive test to the cross-jump entry (cookbook 86)
|
||||
|
||||
`0x80107CCC` (76 B) was re-taken with the `goto` lever fully applied — its guard is solved — and it
|
||||
still resists: the original has **two inline `jr ra` return-0 blocks** (0xCDC and 0xD10) plus the
|
||||
found-case `jr ra` (0xCE4), and every spelling makes cc1 merge the two identical `return 0;`s into
|
||||
`j <shared epilogue>` (80 vs 76). Three levers tried and why each fails: two plain returns → shared;
|
||||
`goto zeroret;` + a label → the extra label restructures the head and breaks the guard (72/59);
|
||||
labels ordered to match the original's layout → 80. **The addition:** *the cross-jump needs **two
|
||||
identical return blocks** to fire.* That is exactly why the sibling `0x8002DF1C` **closed** with the
|
||||
same lever — it has only **one** `return 0;`. So **"does the original show two identical inline
|
||||
returns?" is the cheap test for whether the goto lever will suffice**, and it is now the entry's
|
||||
first line rather than something a worker finds after six spellings.
|
||||
|
||||
Also from A: `0x800FB758` (112 B) was closed by the `epilogue` token at the first spelling, and its
|
||||
recorded 6-byte residual *is* the inserted load-delay `nop` (shape A). **One encode note with a second
|
||||
instance in A's own claims: `D_80122140` is marked `gp` in the registry but the original writes it
|
||||
ABSOLUTELY (`lui at,0x8012 / sw v0,0x2140(at)`), so the source must use the LITERAL address** — the
|
||||
symbol spelling lets the harness rewrite it to `%gp_rel` and fails. That is cookbook 16/46's per-SITE
|
||||
behaviour, now with worked instances in three workers' partitions.
|
||||
|
||||
### Worker B's symbol request, verified from two independent directions before acting
|
||||
|
||||
B asked for `D_8012216C 0x8012216C gp` for row `0x800FFB74`, having verified it against a **scratch
|
||||
registry** (finding 90's documented technique) and **without touching `config/`** — the correct
|
||||
conduct for a shared tracked file. I verified it independently rather than taking the request:
|
||||
`0x800FFB74` reads **eight** gp globals (`gp+2096 .. gp+2128`), and the implied gp base from
|
||||
`D_8012216C` at `gp+2100` is **`0x80121938`**. That base is confirmed by a second, unrelated
|
||||
observation already on the record — worker C measured `D_801224DC` as `gp+0xBA4`, and
|
||||
`0x80121938 + 0xBA4 = 0x801224DC`. Two independent derivations, same base, so the marker is right and
|
||||
the symbol is added. Gate re-run with it (`c_regions=666, differing_bytes=0, MATCH`) and
|
||||
`make check` exit 0, because adding a symbol changes the environment every region is compiled in.
|
||||
|
||||
**Position: 657 bodies / 666 regions. Phase: 602 → 657 = +55.**
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
typedef struct {
|
||||
int f0;
|
||||
int f4;
|
||||
char pad[24];
|
||||
} Rec_801455F0;
|
||||
|
||||
extern Rec_801455F0 D_801455F0[8];
|
||||
extern int D_80122790;
|
||||
extern int D_80122794;
|
||||
|
||||
void func_800FBF5C(void)
|
||||
{
|
||||
Rec_801455F0 *p;
|
||||
int i;
|
||||
|
||||
i = 7;
|
||||
p = D_801455F0;
|
||||
p += 7;
|
||||
for (; i >= 0; i--) {
|
||||
p->f4 = 0;
|
||||
p--;
|
||||
}
|
||||
D_80122790 = (int)D_801455F0;
|
||||
D_80122794 = (int)D_801455F0;
|
||||
}
|
||||
+60
-6
@@ -1,15 +1,69 @@
|
||||
/*
|
||||
* func_801092C0 — 52 bytes at 0x801092C0..0x801092F4
|
||||
*
|
||||
* Clears the first field of `count` 32-byte records in the table at D_801461C8,
|
||||
* starting at record `start`. Matched on the SIXTH spelling, from the
|
||||
* un-attempted band (an index row whose attempt predates the current lever set).
|
||||
* FOUR separate levers were required and three of them are new:
|
||||
*
|
||||
* 1. THE PARAMETERS ARE UNSIGNED. `int start, int count` gives `blez a1` and a
|
||||
* SIGNED `slt`; `unsigned int` reproduces the original's `beqz a1` AND `sltu`.
|
||||
* The entry test alone is not enough to tell -- both forms test the count, and
|
||||
* the second instruction is what names the signedness.
|
||||
* 2. **`volatile` ON THE TABLE POINTER IS REQUIRED, AND FOR AN ALIAS REASON.**
|
||||
* With `extern Rec *D_801461C8;` cc1 HOISTS the pointer load out of the loop
|
||||
* (`lw a2,25032(a2)` before the loop, one instruction shorter inside it) because
|
||||
* the store's type (a pointer field of `Rec`) is provably a different type from
|
||||
* the global's own type. The original RELOADS it every iteration -- visible in
|
||||
* the original at 0x801092D0/D4 -- so the load must be un-movable. `volatile`
|
||||
* reproduces it exactly. This is a proof-by-emission, not a taste: the two
|
||||
* spellings differ by the load's POSITION, not by its instruction.
|
||||
* 3. **THE FRAME WAS A LOOP-SHAPE ARTIFACT (cookbook 19), on a FORWARD loop.**
|
||||
* Written as `for (i = 0; i < count; i++)` cc1 emits an unused
|
||||
* `addiu sp,sp,-8` / `addiu sp,sp,8` pair -- 64 bytes against 52. Cookbook 19
|
||||
* records this for a BACKWARD `for` on 0x800F7FB4 and warns that it does not
|
||||
* generalise; here it generalises to the guard + `do`/`while` form:
|
||||
* i = 0; if (count != 0) { do { ... } while (++i < count); }
|
||||
* which removes the frame. Note the `++i` must be the WHILE's operand, and the
|
||||
* body's index is the PRE-increment value -- the original increments `i` in the
|
||||
* middle of the body (0x801092CC) and tests the incremented value.
|
||||
* 4. **`p += i + start; p->p0 = 0;` AND NOT `p[i + start].p0 = 0;` -- the final
|
||||
* `addu` TARGETS THE BASE REGISTER.** With the subscript form cc1 writes the
|
||||
* address sum into the OFFSET's register (`addu v0,v0,v1`, store via v0) and the
|
||||
* branch delay slot gets filled with a repeated index computation (a rotated
|
||||
* loop). Accumulating into a pointer variable makes the sum's destination the
|
||||
* BASE register -- the original's `addu v1,v1,v0` and `sw zero,0(v1)` -- and the
|
||||
* store then falls into the branch delay slot, which is the original's shape.
|
||||
* The volatile in (2) also blocks the strength reduction that would otherwise
|
||||
* turn this into a single `p += 32` induction variable.
|
||||
*
|
||||
* LIMITS: `D_801461C8` is named for its address; it is used here as a pointer to a
|
||||
* 32-byte record whose FIRST field is a pointer, but the record's real type and the
|
||||
* meaning of the cleared field are NOT established by these bytes -- a 4-byte store
|
||||
* of zero at offset 0 is all the evidence there is, and the `char pad[28]` is a
|
||||
* stride, not a recovered layout. Whether the original source said `volatile` is
|
||||
* unknowable; what is knowable is that the load is un-movable in the original, and
|
||||
* `volatile` is the smallest lever that reproduces it here. One caller is not
|
||||
* identified, so the meaning of `start` relative to the table is not recovered.
|
||||
*/
|
||||
typedef struct {
|
||||
int f0;
|
||||
void *p0;
|
||||
char pad[28];
|
||||
} Rec_801461C8;
|
||||
|
||||
extern Rec_801461C8 *D_801461C8;
|
||||
extern Rec_801461C8 * volatile D_801461C8;
|
||||
|
||||
void func_801092C0(int start, int count)
|
||||
void func_801092C0(unsigned int start, unsigned int count)
|
||||
{
|
||||
int i;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
D_801461C8[i + start].f0 = 0;
|
||||
i = 0;
|
||||
if (count != 0) {
|
||||
do {
|
||||
Rec_801461C8 *p = D_801461C8;
|
||||
|
||||
p += i + start;
|
||||
p->p0 = 0;
|
||||
} while (++i < count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* func_8010AA28 — 112 bytes at 0x8010AA28..0x8010AA98
|
||||
*
|
||||
* Hypothesis, not a claim about meaning: a one-shot initialiser guarded by a flag global. On the
|
||||
* first call only it sets the guard, invokes a syscall wrapper, hands a fixed address to two
|
||||
* helpers, calls a fourth helper with a constant triple, stores that helper's result into a
|
||||
* second global and passes it on, then invokes a second syscall wrapper and returns. Every later
|
||||
* call does nothing.
|
||||
*
|
||||
* Original words:
|
||||
* 3C028012 lui v0,0x8012
|
||||
* 8C421118 lw v0,4376(v0) ; (L) v0 = D_80121118 (ABSOLUTE, 2 instructions)
|
||||
* 27BDFFE8 addiu sp,sp,-24
|
||||
* 14400014 bnez v0,0x8010AA88 ; already initialised -> the shared exit
|
||||
* AFBF0010 sw ra,16(sp) ; (delay)
|
||||
* 24020001 li v0,1
|
||||
* 3C018012 lui at,0x8012
|
||||
* 0C040FFB jal 0x80103FEC ; (1) an argument-less syscall wrapper
|
||||
* AC221118 sw v0,4376(at) ; (delay) D_80121118 = 1
|
||||
* 3C048011 lui a0,0x8011
|
||||
* 0C042AB0 jal 0x8010AAC0 ; (2)
|
||||
* 2484A388 addiu a0,a0,-23672 ; (delay) a0 = 0x8010A388 (`la` form)
|
||||
* 3C04F000 lui a0,0xF000
|
||||
* 34840009 ori a0,a0,0x9 ; a0 = 0xF0000009
|
||||
* 24050020 li a1,32
|
||||
* 24062000 li a2,8192
|
||||
* 0C042017 jal 0x8010805C ; (3)
|
||||
* 00003821 move a3,zero ; (delay) a3 = 0 <- FILLED SLOT, the whole row
|
||||
* 00402021 move a0,v0 ; a0 = the result
|
||||
* 3C018012 lui at,0x8012
|
||||
* 0C042005 jal 0x80108014 ; (4)
|
||||
* AC2410B0 sw a0,4272(at) ; (delay) D_801210B0 = a0
|
||||
* 0C040FF3 jal 0x80103FCC ; (5) a second argument-less syscall wrapper
|
||||
* 00000000 nop
|
||||
* $E: 8FBF0010 lw ra,16(sp)
|
||||
* 27BD0018 addiu sp,sp,24
|
||||
* 03E00008 jr ra
|
||||
* 00000000 nop
|
||||
*
|
||||
* ONE SPELLING, WITH THE REGION TOKEN `maspsx=moves` — AND THE TOKEN IS THE WHOLE ROW.
|
||||
*
|
||||
* The recorded class was "maspsx mutual exclusion" (cookbook 17/61) with the measurements
|
||||
* `maspsx=off -> 2 differing bytes` and `maspsx on -> 124 bytes, three unfilled delay slots`.
|
||||
* Both are the SAME exclusion seen from its two sides, and `maspsx=moves` is the mode that
|
||||
* satisfies both at once:
|
||||
* - DEFAULT (maspsx on): 124 bytes. maspsx emits its own unconditional `nop` after every jump
|
||||
* whose slot cc1 left empty, so the argument set-ups cc1 put before the `jal`s are never
|
||||
* moved into the slots (three slots stay unfilled, +12 bytes).
|
||||
* - `maspsx=off`: the LENGTH is right because GNU `as` reorder mode performs the fills — but
|
||||
* `as` expands cc1's `move` macro as `or` (funct 0x25) where the original has `addu`
|
||||
* (funct 0x21), so the two `move`s in this row come out 2 bytes wrong.
|
||||
* - `maspsx=moves`: the `move`->`addu` rewrite ONLY (no maspsx nop insertion), which leaves GNU
|
||||
* `as` in reorder mode to fill the jump slots exactly as ASPSX did. **112 bytes, 0 differing.**
|
||||
* This is the first row I know of where `maspsx=moves` has closed a region; the two `move`s it has
|
||||
* to rewrite here are `move a3,zero` (the 4th argument of call 3, which lands IN the `jal` slot)
|
||||
* and `move a0,v0` (the result forwarding between calls 3 and 4).
|
||||
*
|
||||
* TWO FURTHER FACTS READ OFF THE BYTES: `D_80121118` and `D_801210B0` are accessed ABSOLUTELY
|
||||
* (`lui`+`lw`, `lui at`+`sw`) and are address-named placeholders, not registry gp symbols; and the
|
||||
* fixed address passed to call 2 is materialised with `addiu` (the symbol/`la` form, cookbook
|
||||
* 4/89), so it is written as an address-named array.
|
||||
*
|
||||
* LIMITS: the function name, all five callees, the two globals, the constant triple
|
||||
* (0xF0000009, 32, 8192), the fixed address 0x8010A388 and the fact that calls 1 and 5 take no
|
||||
* arguments are read from the instruction encodings; only the bytes are evidence. Nothing about
|
||||
* what the initialiser sets up, or what the two syscall wrappers do, is established here.
|
||||
*/
|
||||
|
||||
extern int D_80121118;
|
||||
extern int D_801210B0;
|
||||
extern char D_8010A388[];
|
||||
|
||||
void func_80103FEC(void);
|
||||
void func_8010AAC0(char *);
|
||||
int func_8010805C(int, int, int, int);
|
||||
void func_80108014(int);
|
||||
void func_80103FCC(void);
|
||||
|
||||
void func_8010AA28(void)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (D_80121118 != 0)
|
||||
return;
|
||||
D_80121118 = 1;
|
||||
func_80103FEC();
|
||||
func_8010AAC0(D_8010A388);
|
||||
r = func_8010805C(0xF0000009, 32, 8192, 0);
|
||||
D_801210B0 = r;
|
||||
func_80108014(r);
|
||||
func_80103FCC();
|
||||
}
|
||||
Reference in New Issue
Block a user