From de141dc221e4ca977418076ef2f8cb6a5b1f26c8 Mon Sep 17 00:00:00 2001 From: Drew T <50529377+Druthulu@users.noreply.github.com> Date: Wed, 5 Aug 2026 14:48:26 -0600 Subject: [PATCH] =?UTF-8?q?docs(phase-30=20S42):=20cookbook=20=C2=A7148=20?= =?UTF-8?q?=E2=80=94=20the=20loop.c=20hoisting=20threshold=20arithmetic,?= =?UTF-8?q?=20the=20MIN=5FEXPR=20clamp=20fold,=20the=20allocno-priority=20?= =?UTF-8?q?slider;=20and=20the=20x16=20claim=20corrected?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/cookbook-index.md | 21 ++++++++--- docs/matching-cookbook.md | 77 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 5 deletions(-) diff --git a/docs/cookbook-index.md b/docs/cookbook-index.md index 6698b659b..38ad29bc2 100644 --- a/docs/cookbook-index.md +++ b/docs/cookbook-index.md @@ -2,7 +2,7 @@ > **Generated by `tools/cookbook_index.py` — do not hand-edit** (R33). Regenerate after adding a cookbook section. > -> `docs/matching-cookbook.md` is ~716 KB / 419 sections. Grepping it blind is how three P30 wave-1 agents each "discovered" an idiom that was already written down. **Start here, then read the section.** A section appears under every symptom it addresses. +> `docs/matching-cookbook.md` is ~716 KB / 424 sections. Grepping it blind is how three P30 wave-1 agents each "discovered" an idiom that was already written down. **Start here, then read the section.** A section appears under every symptom it addresses. **How to use:** name what you SEE in the diff (a stolen delay slot, an extra `la`, a swapped register pair, a `conflicting types` error), find that symptom below, read those sections first. If nothing fits, THEN grind — and add a section when you win. @@ -41,7 +41,7 @@ - **§3-D.** — THE CROSS-JUMP RATCHET (the sharpest new trap — `func_80131340` L-C) L3501 - **§3-C.** — Inner-block declaration does NOT delay slot allocation — BYTE-REFUTED L10058 -### instruction scheduling (16) +### instruction scheduling (18) - **§3-T2** — Source statement order drives instruction scheduling L78 - **§3** — When a diff is pure scheduling → decomp-permuter (harness built, Phase 6) L107 @@ -59,8 +59,10 @@ - **§3-The** — attribution primitive (use this before calling anything a scheduling residual) L6050 - **§3-The** — scheduling rules (refining §135-2 and §135-4) L8902 - **Consequence** — for the family (a real scheduling decision) L10085 +- **§148** — The loop.c hoisting THRESHOLD is arithmetic you can compute, and the `?:` clamp that folds to MIN_EXPR (P30 S42, `func_8017C6F4`, 947 ins) L10098 +- **§3-A.** — `move_movables` hoists iff `threshold × savings × lifetime ≥ insn_count` — and you can read it L10104 -### register allocation & pins (35) +### register allocation & pins (36) - **§10** — Closing the regalloc/scheduling hard tail by hand (LZSS, Phase 7 session F — the full close) L835 - **Residual** — A — commutative `|`/`&`/`+` result lands in the wrong source-operand register L856 @@ -97,13 +99,14 @@ - **§3-The** — same swallow, twice more, in the integration spine L9699 - **§3-B.** — A `?:` on MEMORY operands costs ~16 bytes of invisible frame; on REGISTER operands, zero L10047 - **§3-D.** — A lone `$t8`/`$t9` in the target is RELOAD SCRATCH — reproduce the spill, don't pin the register L10063 +- **§3-C.** — A zero-byte ALLOCNO-PRIORITY slider L10137 ### CSE / redundancy / rematerialization (2) - **§46** — The `func_80178D40` crack (890 ins ×134, the heaviest core in the game): four LOOP-STRUCTURE levers cheap-Opus found by reading loop.c/jump.c/cse.c (Phase 26 session 8, 2026-07-13) L3306 - **§83d** — CSE's quantity budget is WHOLE-FUNCTION, so a local rewrite cannot fix a local symptom L6445 -### loops & induction variables (8) +### loops & induction variables (9) - **§3-T1** — Loop pointer: top-of-body for `addu` induction, not constant-folded `addiu` L71 - **§34** — The `func_80138ED0` giant crack: gcc-2.7.2's **3-qty sort bug** + the **zero-byte asm allocation toolkit** + the **giv-init fence** (Phase 24 T5; Opus→close=21, Fable5→MATCH ×134) L2447 @@ -113,6 +116,7 @@ - **§66d-1** — What transfers between giants is the LOOP, not the PIN L5273 - **§70** — The giv-init base register: walk the PARAMETER, not a copy of it (Phase 29 SESSION-18, `func_801777BC`) L5612 - **§145** — Three loop/combine levers from the S40 wave-2 drafters (16/16 match_one) L9917 +- **§148** — The loop.c hoisting THRESHOLD is arithmetic you can compute, and the `?:` clamp that folds to MIN_EXPR (P30 S42, `func_8017C6F4`, 947 ins) L10098 ### structs, block moves & memcpy (30) @@ -563,7 +567,7 @@ - **§147** — The three-stratum FRAME LAW, and four "stop searching" verdicts (P30 S42, `func_8017C294`, serial run) L10026 - **§3-C.** — Inner-block declaration does NOT delay slot allocation — BYTE-REFUTED L10058 -### (unbucketed — title matched no symptom vocabulary) (120) +### (unbucketed — title matched no symptom vocabulary) (122) - **§3-How** — to use this L30 - **§1** — Idiom catalog (asm pattern → C that produces it) L39 @@ -685,6 +689,8 @@ - **§144** — THE LITERAL'S SPELLING PICKS THE IMMEDIATE ENCODING (P30 S40 wave 1, `func_801822E0`) L9878 - **§3-Why** — a correct draft can read as an intrinsic wall L9978 - **§3-The** — rule L10013 +- **§3-B.** — `(v < 0x40) ? v : 0x3F` is folded to `MIN_EXPR` and expands to the WRONG SHAPE L10123 +- **§3-D.** — Reproduce the original's BUGS verbatim L10147 ## All sections, in order @@ -1108,3 +1114,8 @@ - **§3-D.** — A lone `$t8`/`$t9` in the target is RELOAD SCRATCH — reproduce the spill, don't pin the register L10063 - **§3-E.** — A `qty_compare` TIE is not spelling-reachable — recognise it and stop L10073 - **Consequence** — for the family (a real scheduling decision) L10085 +- **§148** — The loop.c hoisting THRESHOLD is arithmetic you can compute, and the `?:` clamp that folds to MIN_EXPR (P30 S42, `func_8017C6F4`, 947 ins) L10098 +- **§3-A.** — `move_movables` hoists iff `threshold × savings × lifetime ≥ insn_count` — and you can read it L10104 +- **§3-B.** — `(v < 0x40) ? v : 0x3F` is folded to `MIN_EXPR` and expands to the WRONG SHAPE L10123 +- **§3-C.** — A zero-byte ALLOCNO-PRIORITY slider L10137 +- **§3-D.** — Reproduce the original's BUGS verbatim L10147 diff --git a/docs/matching-cookbook.md b/docs/matching-cookbook.md index 1bed34c4f..39d5a48d5 100644 --- a/docs/matching-cookbook.md +++ b/docs/matching-cookbook.md @@ -10092,3 +10092,80 @@ all 16 in one pass. **Symptom lines for the index:** **"a mystery stack slot at the top of the frame"** · **"frame is a multiple of 16 too large"** · **"a lone $t8/$t9 in the target"** · **"exactly one register pair transposed"** · **"permuter and hand-search plateau at the same number"**. + +--- + +## §148 — The loop.c hoisting THRESHOLD is arithmetic you can compute, and the `?:` clamp that folds to MIN_EXPR (P30 S42, `func_8017C6F4`, 947 ins) + +Serial run #2. Reached **NEAR(63)** of 947 (frame `0x120` exact, `vars=232` exact, all opcodes, +immediates, stack offsets and branch targets correct; residual is one register rotation). Did not +bank. The levers below are the yield. + +### A. `move_movables` hoists iff `threshold × savings × lifetime ≥ insn_count` — and you can read it + +`loop.c:1631` sets **`threshold = 58`** for this MIPS config and **decrements it by 3 per movable +already moved** (`threshold -= 3`, loop.c:1719/1904). This is the first *quantitative* handle we have +on gcc-2.7.2 invariant motion. + +Measured here: the draft's prim loop was **513** RTL insns, so `&g.flag` (savings 3, lifetime 3 → +`58×3×3 = 522 ≥ 513`) got hoisted into a preheader register that the target recomputes **inline** — +costing a callee-saved register and cascading into 2 extra spills and **+96 instructions**. +Duplicating the `if (za < g.sz2) za = g.sz2;` tail into **both** arms of the `sz0>sz1` test (which the +target's cross-jump reveals) pushed the loop to **523** insns → `522 < 523` → **not hoisted** → the +register came back. + +**Symptom → lever:** an address (`&x`) hoisted into a loop-preheader register that the target +recomputes inline ⇒ **raise the loop's RTL insn count by ~10, or lower the movable's ref count.** +**Read it directly:** `cc1 -dL` writes `.i.loop`, which prints +`Loop from A to B: N real insns` and, per movable, `Insn K: regno R (life L), savings S +moved/not desirable`. Stop guessing which invariant moved — the dump names it. + +### B. `(v < 0x40) ? v : 0x3F` is folded to `MIN_EXPR` and expands to the WRONG SHAPE + +`fold-const.c:4948` — `A < C1 ? A : C2` with `C1 == C2+1` becomes `MIN(A,C2)`, which expands as +**copy-then-conditionally-overwrite** (`move t,v; slti; bnez; li`). `A <= C ? A : C` folds too (the +`A op B ? A : B` rule at 4907). + + (v < 0x40) ? v : 0x3F -> MIN_EXPR -> move/slti/bnez/li WRONG + (v > 0x3F) ? 0x3F : v -> no fold -> jumpifnot/store/j/store RIGHT + +Same `slti $v0,$v,0x40` is emitted either way — but the second keeps gcc's canonical branchy form. +**This single respelling took the draft from close 827 to close 63 and fixed all four clamps +instruction-for-instruction.** Corollary: `(v < 0) ? 0 : X` is safe — fold's "swap if arg1 is simpler" +rewrites it to `(v >= 0) ? X : 0`, which is exactly the target's `bltz`. + +### C. A zero-byte ALLOCNO-PRIORITY slider + + __asm__ ("" :: "r"(a), "r"(b)); /* emits nothing */ + +Priority is `floor_log2(n_refs)·n_refs·size / live_length`, and `REG_N_REFS` is incremented **by loop +depth** — so an empty asm with `"r"` inputs inside a loop adds `depth` references per operand while +emitting **no code**. Used here it flipped `{rowptr,y}` ↔ `{cx1,cy1}` for the last two callee-saved +registers. **This is the counterpart to §47's live-length slider: that one moves the DENOMINATOR, +this one moves the NUMERATOR.** + +### D. Reproduce the original's BUGS verbatim + +This variant's F3 arm bbox-tests the packet through **PolyFT3** offsets (pkt+8/+0x10/+0x18, stride 8) +while writing F3 xy at stride 4; the FT4 arm reads `tmpxy[3].vx` where `.vy` is meant. Both are +original-source copy/paste bugs. Matching means reproducing them. + +### ⚠️ E. A "these are all the same function" claim needs the DRAFT test, not a diff + +The run reported all sixteen 947-ins instances as one identical body (→ "one crack banks 15,152 ins"). +**Checked and it does not hold:** the draft scores **63** on `ov_SC03_126` but **340** on +`func_8017C59C` and `func_8017CF90` — with an *identical* first diff on both, i.e. those two match +each other but not the cracked one. That is consistent with the h_norm clustering (947×3, 947×2, plus +singletons): **several multi-instance groups, not one group of 16.** A normalized-stream diff can say +"same shape"; only *running the actual draft against the sibling's asm* says "same body". **Cheap +test, do it before scaling a ×N claim** (`sed s/func_A/func_B/` the draft and `match_one` it). + +### Tooling note + +`permuter_ils` cannot be aimed at this draft: `run_masked` reports *"Function … not found in +base.c"* because the `gte_*` `#define` block defeats `make_base_c`. **Demacroize first** — worth doing +generally, since any GTE-using draft hits it. + +**Symptom lines for the index:** **"an &address hoisted into a loop preheader"** · **"a clamp expands +as move-then-overwrite"** · **"MIN_EXPR"** · **"two callee-saved registers swapped"** · **"permuter +says function not found in base.c"**.