From 959e9c7808fdda4f1a67a2de8aa13520dc04616a Mon Sep 17 00:00:00 2001 From: Christopher Williams Date: Thu, 24 Sep 2026 09:16:31 -0400 Subject: [PATCH] =?UTF-8?q?phase11:=20ledger=20+=20CURRENT=5FPHASE=20at=20?= =?UTF-8?q?the=20compaction=20point=20=E2=80=94=20501=20bodies=20/=20510?= =?UTF-8?q?=20regions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Records worker B's oracle result overturning the post-pass framing (ASPSX does not fill delay slots; maspsx is faithful to it; the fills come from GNU as reorder mode and the only gap is one mnemonic), the new `maspsx=moves` mode, worker C's correction of a too-broad Phase 10 coordinator fix (now opt-in `maspsx=nopmarker`), the per-worker cost tables that show SHAPE not band is the variable, and the new findings 63-66 plus the named-locals family's fourth mechanism and the char[4] block-move recipe. Committed at the orchestrator's 70% context cap; compaction follows, safe because the ledger and CURRENT_PHASE are current. --- phase-ends/CURRENT_PHASE.md | 6 ++- phase-ends/logs/Phase11.md | 78 +++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/phase-ends/CURRENT_PHASE.md b/phase-ends/CURRENT_PHASE.md index 264713c..a7f80ff 100644 --- a/phase-ends/CURRENT_PHASE.md +++ b/phase-ends/CURRENT_PHASE.md @@ -1,6 +1,10 @@ # CURRENT_PHASE — Phase 11: Breaking the 244-Byte Ceiling -**Status:** ACTIVE (P11-T1 in progress) +**Status:** ACTIVE (cycle 1). **501 distinct bodies / 510 regions** from the 484 / 493 baseline. +Corpus maximum **700 B** (worker D's `0x8006BC74`). Two harness modes added this phase: +`maspsx=moves` (the `move`->`addu` rewrite with maspsx off, from worker B's ASPSX oracle result) +and `maspsx=nopmarker` (opt-in correction of a too-broad Phase 10 fix). See +`phase-ends/logs/Phase11.md` for the running ledger — it is the durable record. **Created:** 2026-09-24, after Phase 10 closure and developer approval of `phase-ends/Phase11_PLAN.md`. **Milestone:** **600 distinct matched bodies** from the Phase 10 close of 484 (**+116**), with **700 as a stretch**, measured on the orchestrator's clean whole-binary gate. diff --git a/phase-ends/logs/Phase11.md b/phase-ends/logs/Phase11.md index 0ab8ab6..9ea7172 100644 --- a/phase-ends/logs/Phase11.md +++ b/phase-ends/logs/Phase11.md @@ -225,3 +225,81 @@ Suite 237 → 246 tests. The candidate gate caught the bad row; the tracked regi **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. + +## Cycle 1 continued — the 700 B match, the `moves` mode, and a coordinator-fix correction + +**501 distinct bodies / 510 regions** (from 484 / 493). Corpus maximum **700 B** (worker D's +`0x8006BC74`), 2.9x the old 244 B ceiling and the first match in the 401–800 B band. + +### Worker B's oracle result OVERTURNED the post-pass framing + +Worker B ran all five SDK assemblers (ASPSX 2.56/2.67/2.79/2.81/2.86) and every supported option as a +read-only oracle and found that **ASPSX does NOT fill delay slots at all** — it produces maspsx's exact +shape. So **maspsx is faithful to ASPSX**, and the fills in the original did not come from ASPSX. That +kills the "model ASPSX's fill" framing the developer had authorized a post-pass for. + +**What actually fills the slots is GNU `as` in reorder mode — i.e. maspsx OFF** — and the only real gap is +**one mnemonic**: `as` expands cc1's `move` to `or` where ASPSX emits `addu`. + +**New harness mode `maspsx=moves`** = `maspsx=off` plus a single `move`→`addu` rewrite, letting `as` fill +exactly the slots cc1 left empty while cc1's own `.set noreorder` windows are preserved. Demonstrated: +`0x800FA5D8` gives **132 B / 0 differing MATCH** where default maspsx gives 148 LENGTH-MISMATCH. +Regression-verified at 510 regions with the mode off. Opt-in per region, because worker B measured the +counterexample `0x8002D2BC` — the *same* cc1 shape, but its original keeps the store before the `jr` with +a nop, so the original's assembler behaves differently in different files. + +### A coordinator fix from Phase 10 was TOO BROAD — corrected by a worker + +Worker C found, while characterising an above-ceiling row, that Phase 10's unconditional +"honour cc1's explicit `#nop` marker" is wrong for a **bare-symbol store consumer**: the store's own +`lui $at` expansion **fills the delay slot**, so the marker is **spurious**, and honouring it costs an +instruction (`0x800AFDBC`: the original is `lhu` / `lui at` / `sh` with no nop). But `0x80107C5C` +genuinely wants the nop (112 vs 108). + +Two rows wanting opposite behaviour from the same shape ⇒ **make it a per-region mode**: +`maspsx=nopmarker`, **default OFF**. Verified: `make check` green at 510 regions / 253 tests with it off; +`0x80107C5C` matches 112 B with the token and is 108 B LENGTH-MISMATCH without it. Patch regenerated and +verified; `SETUP.md` records the correction. + +**The pattern is worth naming:** a fix proven regression-free against the **corpus** can still be wrong +for an **unmatched** row, because the corpus only exercises the paths that already work. + +### Cost and shape — two workers, converging + +| source | band | rows | matched | cost | +|---|---|---|---|---| +| worker D | >244 B | 5 | **4 (80%)** | 2–7 spellings, including 700 B in 3 | +| worker D | ≤244 B (paired control) | 1 | 1 | first spelling | +| worker C | >244 B | 4 | 1 (25%) | median **11.5 attempts**, every failure ≤8 bytes | + +**The band is not the variable — the shape is.** D's rows have **repetitive bodies** (reset/init/copy), +where redundancy makes a large body cheap. C's three near-misses are **tie-break-dense** bodies where the +size is incidental. So: **repetitive bodies are cheap at any size; tie-break-dense bodies are expensive at +any size.** That is the refinement of finding 58, and it points at redundancy rather than size as the +ranking signal. + +### New findings + +- **Cookbook 63** — the **combiner constant-fold class**: a residual of exactly one `addiu` plus a + matching shift in *every* displacement of one base register means cc1 folded a constant offset the + original kept in a register; materialise the offset as a named local. Invisible to a mnemonic diff. +- **Cookbook 64** — finding 59's family has three load-bearing properties, one per instance: row stride, + total size, **memory residence**. +- **Cookbook 65** — the amendment to finding 41 is **measured**: the paired control matched first try, so + the lever set was the variable, not the band. And the two **largest** bodies were the **easiest**. +- **Cookbook 66** — a bounded negative with a **named direction** beats a blocked class. +- **Named-locals family, FOURTH mechanism, opposite polarity** (worker D, `0x800FF5A8`): name **two** + pointer locals both holding `a0` and both live across the call — the allocator cannot coalesce + overlapping live ranges, so a second callee-saved register appears. The recorded cases are "name a + local so cc1 does NOT reload"; this is "name a local so cc1 MUST keep a second copy". +- **The `char[4]` struct-copy loop is the block-move idiom** (worker C): an explicit + `for (i=0;i<40;i++) dst[i] = src[i];` over a 4-byte `char[4]` struct yields `lwl`/`lwr` + `swl`/`swr` + with **no runtime check**, while a 160-byte struct assignment makes cc1 emit a runtime alignment test + plus two loops (344 B, +96). +- **A local holding a constant is not free** (worker C): naming `127` forced an `s1` save and grew the + frame 48 → 56. + +### Orchestrator note + +Context reached the 70% cap and was **compacted** rather than stopped, per the phase policy — safe because +the ledger, `CURRENT_PHASE.md`, the tracked registries and the negatives index are all current.