cookbook: findings 58-62 — the size-band amendment, the ceiling break, division_check, the ASPSX oracle rule
Finding 58 AMENDS finding 41, which was Phase 10's headline result and drove every dispatch decision. The "1-in-12 for 200-800 B" comparison was taken on a queue that had never been attempted (5 of 427 rows above 244 B ever tried, 1.2%; three in an excluded class; both non-excluded attempts near-matched). The first row attempted above the ceiling matched. The band's measured yield is a function of the lever set at the time of measurement, and the lever set grows -- RE-MEASURE a band before concluding it is exhausted, and never treat a band as closed at ~1% attempt coverage. Findings 59-62: the local-aggregate row-stride/element-size lever that broke the ceiling (int t[3][4] not int t[9]; frame 48 vs 40; residual concentrated on the frame adjustment); the division_check trapped class; the localisation of the maspsx/GNU-as mutual exclusion with the developer's ASPSX-as-oracle-only rule; and the fail-fast validation of region override keys in sf3_merge.
This commit is contained in:
@@ -919,3 +919,103 @@ that merely read one, so the count is a lower bound.
|
||||
- **One attempt on a named lever, then classify.** A harness classification backed by a proof is worth
|
||||
more than an assumption; the model case is `0x80023D40`, where cc1's duplicated `slti` in the `bgez`
|
||||
slot is **correct code**, so no source shape can prevent it.
|
||||
|
||||
## Phase 11 — amendments and new findings (2026-09-24)
|
||||
|
||||
### 58. AMENDMENT TO FINDING 41 (the size-band law) — the "1-in-12" figure was measured against a queue that had never been attempted
|
||||
|
||||
Finding 41's headline measurement was "≤200 B yields at 1–2 attempts per row against 1-in-12 for
|
||||
200–800 B", and it drove every dispatch decision in Phase 10. **Phase 11's Goal B census showed the
|
||||
comparison was not what it appeared to be.**
|
||||
|
||||
| | rows | ever attempted |
|
||||
|---|---|---|
|
||||
| >244 B | 427 | **5 (1.2%)** |
|
||||
|
||||
Three of those five are the already-excluded trapping class. **Both** attempted rows that are not in an
|
||||
excluded class reached **near-match** (4 and 5 differing bytes). The corpus's size profile — 456 of 493
|
||||
registered regions at ≤120 B, 0 above 244 B — is simply **a queue consumed from the small end**.
|
||||
|
||||
**The first row worker D attempted above the ceiling matched** (`0x8009F6A0`, 248 B), setting a new
|
||||
corpus maximum. So:
|
||||
|
||||
- **The 244-byte "ceiling" was a dispatch artefact, not a measured wall.** Nothing about 248 B was
|
||||
special.
|
||||
- **The 1-in-12 figure was taken on rows attempted mostly *before* the length-class levers existed** —
|
||||
cookbook 54's row-stride lever, the named-locals family, the two-arm branch family and the rest. A
|
||||
band's measured yield is a function of the lever set *at the time of measurement*, and the lever set
|
||||
grows. **Re-measure a band before concluding it is exhausted.**
|
||||
- The "soft" verdict is a claim about **dispatch**, not a promise of yield: the two near-misses above the
|
||||
ceiling still failed at 4–5 bytes, so per-row cost there is real.
|
||||
|
||||
*What survives from finding 41:* small bodies are still cheaper per attempt, and a worker should still
|
||||
start there. *What does not:* treating a size band as *closed* because nobody has matched in it. **A
|
||||
band with ~1% attempt coverage has not been measured at all.**
|
||||
|
||||
### 59. A local aggregate's row stride and element size are byte-load-bearing (extends finding 54)
|
||||
|
||||
`0x8009F6A0` (248 B) needed **`int t[3][4]`, only columns 0..2 used** — not `int t[9]`. The 4-word row
|
||||
stride moves the 2nd and 3rd triples to `0x10` and `0x20`, makes the frame **48 B instead of 40 B**, and
|
||||
leaves the unused `0x0C`/`0x1C` slots the original shows. Four spellings:
|
||||
|
||||
| spelling | result |
|
||||
|---|---|
|
||||
| nine `short` locals | 100 B LENGTH-MISMATCH — cc1 drops the sign extension (`lhu`+`subu`), no frame |
|
||||
| nine `int` locals | 100 B LENGTH-MISMATCH — right signedness (`lh`), still no frame |
|
||||
| `int t[9]` | 248 B, **19 differing bytes**, first difference **at the frame adjustment itself** |
|
||||
| `int t[3][4]` | **MATCH** |
|
||||
|
||||
**Diagnostic: correct length + right instruction multiset and order + residual concentrated on the FRAME
|
||||
ADJUSTMENT and every sp-relative offset ⇒ suspect a local aggregate's row stride / element size, not the
|
||||
control flow.** The element type is the other half of the lever — `short` locals let cc1 drop the sign
|
||||
extension, `int` locals keep it.
|
||||
|
||||
### 60. A new trapped class: compiler-generated division checks (`div` + `break`)
|
||||
|
||||
`break` **never** appears without `div` and `div` **never** appears without `break` — 75 worklist rows,
|
||||
0 exceptions. The shape is GCC's divmodsi4 with `MASK_CHECK_ZERO_DIV|MASK_CHECK_RANGE_DIV`: `div` /
|
||||
`bnez`+`break 7` (zero check) / `li at,-1` + `bne` + `lui at,0x8000` + `bne quotient,at` + `break 6`
|
||||
(range check) / `mflo`. **`break` cannot be produced from C.**
|
||||
|
||||
**Measured disjoint from the matched corpus: 0 of 493 registered regions contains a div, a rem or a
|
||||
break.** Same signature as finding 26's trapping class.
|
||||
|
||||
Compiler matrix: every available cc1 either emits a **bare** div with no check (open 2.5.7–2.91.66-psx,
|
||||
and Sony CC1PSX 4.0–4.5, which reject `-mcheck-zero-division`), or emits a check with a **different
|
||||
shape** (open 2.95.2-psx and CC1PSX 4.6: `mflo` *before* the check, scratch `$3`/`$4`, comparing the
|
||||
**divisor** against `0x80000000`; the original checks before `mflo`, uses `$at`, and compares the
|
||||
**quotient**). Not reproducible with this toolchain — the route is a cc1 build with those masks in
|
||||
`TARGET_DEFAULT`, a developer-owned toolchain decision. Implemented as the counted exclusion
|
||||
`division_check` (81 rows).
|
||||
|
||||
### 61. The maspsx / GNU-as mutual exclusion, localised — and the ASPSX oracle rule
|
||||
|
||||
**ASPSX 2.56 does BOTH the `move`→`addu` conversion and the macro-expansion delay-slot fill.** maspsx
|
||||
does the first only; GNU `as` reorder mode does the second only; and **the two cannot be combined** —
|
||||
maspsx must be in noreorder to supply its own nops, while `as` needs reorder *from the function start*
|
||||
and then orphans cc1's own slots. Measured on `0x800FA5D8` across five spellings: `maspsx=off` gives the
|
||||
correct length with all four fills and breaks all four `move` copies (`or` instead of `addu`, one byte
|
||||
each); `maspsx=noreordernop` gives the right length with 30 differing bytes; `.set reorder` from the
|
||||
function start over-fills to 144 B (reproducing finding 40); a per-site `.set reorder` window stays at
|
||||
148 B (confirming that a mid-function `.set reorder` does not re-enable the fill).
|
||||
|
||||
Worker B measured **42 rows (14%) in one partition** with this signature, so roughly **~170 across the
|
||||
worklist**. It is the *same* mutual exclusion that blocks the rare-epilogue class — **two phases of
|
||||
evidence converging on one root cause**, and the strongest argument that one post-pass closes both.
|
||||
|
||||
**THE ORACLE RULE (developer decision).** `tools/psyq/psyq4.0/psyq4.0/ASPSX.EXE` under `wibo` is the
|
||||
*exact* assembler that built the original, and it is tempting to use it as the build stage. **Do not.**
|
||||
It is a proprietary, git-ignored binary, so a build depending on it could not be reproduced by anyone
|
||||
else — and reproducibility is the point of a matching decompilation. Phase 10 hit the same trap with
|
||||
maspsx, and that fix worked *only* because maspsx is open source and patchable; ASPSX cannot be
|
||||
redistributed. **Use ASPSX as a read-only diagnostic ORACLE to characterise the behaviour, then carry
|
||||
that behaviour as tracked code.**
|
||||
|
||||
### 62. Fail fast on an invalid region row
|
||||
|
||||
A worker placed the source md5 in a claim row's 4th column. `sf3_merge` passed it through as a region
|
||||
override, so the row **merged** and only `sf3_match gate` failed later 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` for per-claim metadata. The lesson generalises:
|
||||
**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.
|
||||
|
||||
Reference in New Issue
Block a user