phase11: ledger — the ceiling breaks three times, the content-free frame lever, sf3_merge fail-fast fixes

494 bodies / 503 regions. Three bodies above the old 244 B ceiling from two workers
(248/248/264 B). Cookbook 59 extended with the unreferenced-array-local mechanism
(cc1 gives unreferenced scalars no home but does allocate for unreferenced arrays),
the amended three-direction rule, the per-band cost table, the sf3_merge fail-fast
fixes, and the rank-instability broadcast.
This commit is contained in:
Christopher Williams
2026-09-24 09:01:06 -04:00
parent e7caec4443
commit f188708009
+69
View File
@@ -156,3 +156,72 @@ on it, exactly as the maspsx `#nop` fix did.
merely *READS* a callee-saved register neither written nor saved earlier finds 11 more rows (8 above the
ceiling), but the control fires on **2 of 493 registered regions**, so it is **not** disjoint and was
**not** implemented. Recorded as a candidate requiring refinement, not applied.
## Cycle 1 — the ceiling breaks three times, and a content-free frame lever
**494 distinct bodies / 503 regions** (from the 484 / 493 baseline). **Three bodies now sit above the old
244 B ceiling**, from two independent workers: `0x8009F6A0` (248 B, worker D), `0x80036134` (248 B,
worker C), `0x800308C4` (264 B, worker D — the new corpus maximum). The dispatch-artefact verdict is
settled **by result rather than by inference**.
### The lever that broke it, and its extension
**Finding 59, instance 1** (`0x8009F6A0`): the local buffer had to be `int t[3][4]`, only columns 0..2
used — **not** `int t[9]`. The **4-word row stride is byte-load-bearing**: it moves the 2nd and 3rd
triples to `0x10`/`0x20`, makes the frame **48 B instead of 40 B**, and leaves the unused `0x0C`/`0x1C`
slots the original shows. Four spellings: nine `short` locals (100 B, cc1 drops the sign extension), nine
`int` locals (100 B, right signedness, still no frame), `int t[9]` (248 B, **19 differing bytes, the
first at the frame adjustment itself**), then `int t[3][4]` → MATCH.
**Finding 59, instance 2** (`0x800308C4`, 264 B): **8 words from a match and all eight were the frame** —
the `addiu sp,sp,-96` prologue and its mirror epilogue, with **56 of 64 words already byte-identical**
including every call site, delay slot and register. The missing 8 bytes are an **unreferenced array
local**, and the mechanism is measured across five spellings:
| trailing declaration | vars | frame | result |
|---|---|---|---|
| (none) | 56 | 88 | DIFF, 8 words |
| `int pad0, pad1;` (2 unreferenced **scalars**) | 56 | 88 | DIFF — **cc1 gives unreferenced scalars no home** |
| `short t[12]` (one 24-byte array) | 56 | 88 | DIFF |
| `int pad[2]` (unreferenced 8-byte **array**) | 64 | **96** | **MATCH** |
| `short pad[4]` (unreferenced 8-byte **array**) | 64 | **96** | **MATCH** |
**cc1 allocates stack space for an unreferenced ARRAY local but not for an unreferenced SCALAR local.**
The 8 bytes are **size-load-bearing and content-free** — any 8-byte array reproduces them. Recorded as a
**limit of the reconstruction, not a recovered fact**, and marked as such in the file header.
**AMENDED RULE (cookbook 59): when the residual IS the frame, vary the local aggregate's declaration in
THREE directions — element type, row stride, AND total size.** Two instances, two different directions,
one diagnostic: *correct length + right instruction multiset and order + residual concentrated on the
frame adjustment and every sp-relative offset ⇒ suspect a local aggregate's shape, not the control flow.*
### Cost above the ceiling, measured by two workers
| band | worker C | worker D |
|---|---|---|
| >244 B | 1 row attempted, 1 matched, **11 compile attempts** | 2 rows attempted, 2 matched, **4 and 6 spellings** |
| ≤244 B | 2 attempts, 2 attempts, ~6 attempts (3 rows) | paired control pending |
Neither of D's matches was scheduler-bound, allocation-bound or harness-bound — both failed in exactly one
place and both were closed by a **declaration change**. That is the "how tractable" answer at n=3: **the
band is tractable, and the cost is real but not a grind.**
### Fail-fast fixes to `sf3_merge`, both triggered by real worker files
- A claim row with an unknown override key (`md5=...`) used to **merge silently** and only fail later at
the gate with `unknown override key 'md5'`. `sf3_merge` now **validates override keys at merge time**
and rejects the row with a message naming the valid keys and pointing at `report.tsv`. Generalised as
cookbook 62: **validate at the earliest stage that can see the error**, because a late failure is
diagnosed as a gate problem rather than a claim problem.
- A bare header row is now rejected with *"looks like a column HEADER"* rather than a confusing
`not a hex address: 'start'`.
- A lone `-` in the override column is accepted as "no overrides", matching the absent-value convention
the other tracked tables use.
Suite 237 → 246 tests. The candidate gate caught the bad row; the tracked registry was never touched.
### Operational note broadcast to all workers
**Ranks are not stable — key dispatch by address.** Worker D's row moved from rank 592 to 578 mid-session
because the worklist regenerates and the partitions refilter after every merge. **Addresses are stable;
ranks are not**, and rank-keyed scratch tooling drifts silently onto a different row.