From 1fe48501360683bc3ad8a2718712bf7fc735c844 Mon Sep 17 00:00:00 2001 From: Drew T <50529377+Druthulu@users.noreply.github.com> Date: Sun, 26 Jul 2026 13:02:58 -0600 Subject: [PATCH] =?UTF-8?q?feat(phase-29):=20T1.1=20func=5F80183814=20roun?= =?UTF-8?q?d=201=20=E2=80=94=2099.3%=20structural,=20named=20lever;=20cook?= =?UTF-8?q?book=20=C2=A783?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - VERIFIED INDEPENDENTLY (R14): match_one reproduces DIFF 5127 vs 5122, LENGTH-DRIFT/+5. Agent did not over-claim; tree untouched. Difflib-aligned truth: 36/5122 structural (99.3%), 17/21 cases EXACT, args+locals BYTE-EXACT at 216B - §83a: on a LENGTH-DRIFT class match_one's mismatch count is NOT a progress signal — 4,622 and 36 describe the same draft (index-wise comparison smears every index after the delta) - §83b THE LEVER: the handoff's '35x repeated template' (which I passed on flagged UNVERIFIED) is TRUE and was the whole game — 2,625 of 5,122 ins (51%) from ONE parameterised 72-ins body. Three sub-levers: pointer walk (no strength-reduction under -G0), rand()%(u32) for divu, cast barrier vs combine - §83c TRAP: the inherited 'dead local' pad[32] is gcc's OWN SPILL AREA — removing it made the locals area byte-exact. §83e: two 'pure allocation' residuals were a copy-pointer walk -> zero (§80 again) - §83d THE STALL, cited: cse.c:8340 sizes the quantity table by WHOLE-FUNCTION pseudo count, so no per-case edit can move a function-global CSE fork. Next move = close the +5 (buys length parity AND perturbs max_reg), then re-run the do-not-re-buy table on the new base - no pins in the deliverable (diagnostic-only, table row 15) — agent self-reported unprompted - DECISION: round 2 QUEUED, not spent now — T0.2 (224,410-ins pool) outranks a ~0.04pp lever --- docs/matching-cookbook.md | 60 +++++++++++++++++++++++++++++++++++++ phase-ends/CURRENT_PHASE.md | 39 ++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/docs/matching-cookbook.md b/docs/matching-cookbook.md index 2a3bc4335..66648bcbe 100644 --- a/docs/matching-cookbook.md +++ b/docs/matching-cookbook.md @@ -6387,3 +6387,63 @@ declared it `(MATRIX2 *, SVECTOR2 *, SVECTOR2 *)`, the TU and the fleet canon us void *)` — **2,286 of 2,835 sites**. Conforming the draft's decl to the canon is byte-neutral (pointer args pass identically) and banked first try. **On a jr function, expect BOTH gates to have something to say: the carve chain answers the jump table, and §75a answers the declarations.** + +## §83 — The parameterised-repeat law, the spill-area trap, and why a per-case edit cannot move a per-case symptom (Phase 29 SESSION-20, `func_80183814` 5,122 ins, cold-ish start → 36 structural / 99.3%) + +The largest unmatched function in the project, a 21-case state machine. Round 1 reached **5,127 ins vs +5,122 target, 36 structurally-unmatched instructions under a register-blind mask (99.3% exact), 17 of +21 case bodies EXACT, args+locals byte-exact at 216 bytes** — not a match, and the residual is *one* +decision, not 36 problems. + +### §83a — READ THE HEADLINE NUMBER CORRECTLY: a LENGTH drift makes `match_one`'s count meaningless +`match_one` compares **strictly index-wise**. A +5 instruction delta shifts every later index, so the +tool reported **4,622 mismatched** where difflib-aligned comparison shows **36**. Those two numbers +describe the same draft. **When the class is `LENGTH-DRIFT`, the mismatch count is not a progress +signal — align first (difflib) and re-read, or you will abandon a 99.3% draft as a 10% one.** +(Counterpart to §78: there, a length drift *was* a register grant in disguise; here it inflates the +score. Both say the same thing — never read a length-drifted diff literally.) + +### §83b — THE LEVER: find the parameterised REPEAT before decoding case-by-case +Three callees (`func_8012EC04`/`func_8012F14C`/`func_8012C51C`) each appear **exactly 35 times** as +one **72-instruction body parameterised only by `(KIND, START, BOUND)`** — with the invariants +`member == KIND*4`, `array == &D_801F61C0[START]`, and `prev.BOUND == next.START`. **That is 2,625 of +5,122 instructions (51%) from a SINGLE definition.** Writing it once as a template and generating the +35 sites (harness: `.run/giants/s20_g14_*.py` — edit the template in one place, re-propagate to all +35) is the whole game on a function this size. +**Practice: on any large state machine, hunt for a parameterised repeated block BEFORE decoding case +bodies one at a time.** The tell is a repeated call triplet at a fixed stride with monotone constants. +*(A prior handoff asserted this repeat existed; it was carried forward flagged UNVERIFIED because it +appeared nowhere in the recon. It proved TRUE — but flagging it cost nothing and the discipline stands: +an unverified premise is a hypothesis to test first, not a foundation to build on.)* + +**Three sub-levers made the template byte-exact:** +1. **A running POINTER walk, not array indexing** — under `-G0` array indexing does not strength-reduce. +2. **`rand() % (u32)x`** to force `divu` (the signed spelling emits `div` + the sign-fixup dance). +3. **`(s32)((u8 *)p + (X + 0xC))`** — the cast barrier stops `combine` reassociating the offset. + +### §83c — TRAP: a "dead local" in a prior draft may be gcc's OWN spill area +The inherited recon modelled a 128-byte `s32 pad[32]` local and called it dead. **It is not a local at +all — it is gcc's spill area.** Declaring it explicitly *adds* 132 bytes and corrupts the layout; +**removing it made the 216-byte args+locals area byte-exact.** If a draft needs an unexplained dead +block to reach the target frame size, suspect the spill area before inventing a variable. (The frame +being 8 bytes over here is a *different* cause — two extra callee-saved registers, §83d.) + +### §83d — CSE's quantity budget is WHOLE-FUNCTION, so a local rewrite cannot fix a local symptom +The stall: in cases 0 and 3, gcc CSEs three `&D_8018E27C`-class address constants (+0x30) across two +call groups that share a basic block, consuming **2 callee-saved registers the target spends on real +variables** — so `a0` lands in `$s7` instead of `$s2` and every register downstream renames. +**Why no rewrite of case 0 moved it:** `cse.c:8340` sizes the quantity table as +`max(nsets*2, 500) + max_reg` — **gated by the whole-function pseudo count.** A per-case edit does not +change `max_reg`, so it cannot change a CSE decision, even one whose *symptom* is local. +**⇒ The lever for a function-global CSE fork is a function-global quantity change** — here, closing the ++5 length delta is expected to move `max_reg` and the fork together. **Diagnose the SCOPE of a +compiler decision (function-global vs block-local) before choosing where to edit.** +A diagnostic `a0 → $s2` pin halves the residual and makes the prologue byte-exact — which *confirms* +the diagnosis — but it is a hand-placed dial, not plausible source, and it does not fix the frame. It +belongs in the do-not-re-buy table, **not** in the deliverable (§72/§74). + +### §83e — §80 vindicated again, on the same day it was written +Two case residuals (6 and 11, 8 and 7 diffs) had been written off as "pure allocation". They were not: +the morph/lerp loop walks **copies** of its two input pointers (`pa = msa; pb = msb;`), not the +originals — and spelling that took both cases to **zero**. **A residual class you assigned on an +earlier base is a hypothesis, not a verdict** (§80). Re-run the negative list after the base moves. diff --git a/phase-ends/CURRENT_PHASE.md b/phase-ends/CURRENT_PHASE.md index 356b6264a..70248a91b 100644 --- a/phase-ends/CURRENT_PHASE.md +++ b/phase-ends/CURRENT_PHASE.md @@ -4386,3 +4386,42 @@ resolves. T0.1/T0.3 only read sigs/configs and write `.run/` + `docs/` ⇒ safe already PROVED -O0 members bank (9/9 on ov_SC07_010, R22-verified); the blocker is the *fleet-scale* carve's splat `%lo` re-disassembly sensitivity, deferred on ROI. **That is a build-infra task with a known shape, not a compiler wall** — worth re-pricing once the FREE subset is harvested. + +- **◐ 2026-07-26 (SESSION-20) — T1.1 `func_80183814` (5,122 ins, ov_SC07_006) ROUND 1: 99.3% + structural, NOT banked. The predicted shape, with a precise named next move. → cookbook §83.** + **Verified independently (R14), agent did NOT over-claim:** my own `match_one` re-run reproduces + `DIFF mine=5127 target=5122, 4622 mismatched`, class **LENGTH-DRIFT/+5**. Tree untouched + (`git status config/ src/` clean — the agent respected the no-build-tree constraint). + **The honest state, difflib-aligned:** **36 / 5,122 structurally unmatched (99.3% exact)** under a + register-blind mask · 1,201 register-sensitive · **args+locals byte-exact at 216 B** · frame 8 B over + (2 extra callee-saved regs) · **17 of 21 case bodies structurally EXACT** (case 20, 525 ins, perfect). + **⚠️ THE 4,622 IS AN ARTEFACT OF INDEX-WISE COMPARISON** — a +5 length delta smears every later + index. 4,622 and 36 describe the SAME draft. **On a LENGTH-DRIFT class, the mismatch count is not a + progress signal** (§83a) — this is exactly how a 99.3% draft gets mistaken for a 10% one. + **THE LEVER (and the vindication of a flag I raised):** the handoff's *"35× repeated template"* + claim — which I passed to the agent **flagged UNVERIFIED because it appeared nowhere in the recon** — + **is TRUE and was the whole game.** Three callees each appear exactly 35× as ONE 72-ins body + parameterised by `(KIND, START, BOUND)` (`member==KIND*4`, `array==&D_801F61C0[START]`, + `prev.BOUND==next.START`) = **2,625 of 5,122 ins (51%) from a single definition**. Flagging it cost + nothing and the discipline stands: an unverified premise is a hypothesis to TEST, not a foundation. + **TWO INHERITED-RECON CORRECTIONS (both R14-class):** (1) the `pad[32]` "dead local" is **gcc's own + spill area** — declaring it corrupts the layout; removing it made the locals area byte-exact (§83c); + (2) the recon's case 12 was **incomplete** (it cross-jumps into case 14's tail). + **THE STALL, WITH A SOURCE CITATION:** one global regalloc fork — gcc CSEs three `&D_8018E27C`-class + address constants across two call groups sharing a basic block, eating 2 callee-saved regs the target + spends on real variables. **`cse.c:8340` sizes the quantity table `max(nsets*2,500)+max_reg` — gated + by WHOLE-FUNCTION pseudo count**, which is why no local rewrite of case 0 ever moved it (§83d). + **⇒ NAMED NEXT MOVE (round 2):** find the ~5 spurious instructions (5127→5122). That buys length + parity **and** perturbs `max_reg` — the same lever — then **re-run the 15-row do-not-re-buy table on + the new base** (§80). Round-2 economics are measured-cheaper than round 1 (§79/SESSION-19). + **§80 VINDICATED THE DAY AFTER IT WAS WRITTEN:** cases 6 and 11 (8 and 7 diffs) had been written off + as "pure allocation"; they were a copy-pointer walk (`pa = msa; pb = msb;`) and went to **zero**. + **NO PINS IN THE DELIVERABLE.** A diagnostic `a0→$s2` pin halves the residual and makes the prologue + byte-exact (confirming the diagnosis) but is a hand-placed dial and does not fix the frame — recorded + as row 15 of the do-not-re-buy table, not shipped (§72/§74). **Agent self-reported this unprompted.** + Artifacts: `.run/giants/s20_func_80183814_b1.c` · `s20_80183814_report.md` (15-row table with BASES) + · 6 × `s20_g14_*.py` (regenerate the draft identically; edit the template once → all 35 sites). + Cost: 367,677 agent tokens / 115 tool-uses / ~41 min. + **DECISION: do NOT spend round 2 now.** Per the approved order this is a ~0.04pp lever while **T0.2 + is a measured probe on a 224,410-ins (12.5%) pool** — and T0.2 was blocked only by this agent holding + `asm/`. Round 2 is queued with its lever named; nothing is lost (§80 table + harness are on disk).