From 49fc465a514b9ba9cad2d3da48bf1a207a5c65d1 Mon Sep 17 00:00:00 2001 From: Drew T <50529377+Druthulu@users.noreply.github.com> Date: Sun, 16 Aug 2026 14:23:19 -0600 Subject: [PATCH] =?UTF-8?q?docs(phase-31):=20cookbook=20=C2=A7178=20+=20?= =?UTF-8?q?=C2=A7176j-2=20+=20=C2=A7176k=20=E2=80=94=20mine=20the=20wave-P?= =?UTF-8?q?=20journals,=20the=20repair-pass=20yield,=20two=20selector=20bu?= =?UTF-8?q?gs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit §178 — SIX LEVERS FROM THE WAVE-P JOURNALS, each byte-proven and source-cited. Four wave-P repair agents REFUTED the first pass's own diagnosis by dumping cc1 -dS/-da and reading gcc-2.7.2. The meta-finding leads the section: "REGALLOC-PERM" is this project's most over-diagnosed class -- in four functions the symptom was a register swap and the cause was in cse.c or sched.c, decided BEFORE allocation, which is exactly why pins and statement order all failed. A. The $0-add OPAQUE COPY defeats cse.c:826 make_regs_eqv (a PLUS is not a (set reg reg)), so the parm pseudo keeps its register. MATCH on first compile; 3 of 5 pins then became dead weight. B. A `return ` is a priority-1 hard-reg set that the BACKWARD list scheduler places FIRST in the block, making hard $v0 live across a temp's range. Lever: goto a shared return tail. C. birthing_insn_p (sched.c:2469) boosts only single-set destinations; splitting a 3-set temp boosts the insn and drags its feeder chain down. D. NEW IDIOM: a NARROW destination type blocks copy elision (SI->HI cannot be coalesced), so the copy survives at its source position -- one type change worth ~20 instructions. E. The ZERO-OFFSET ALIAS HOLE: memrefs_conflict_p's find_symbolic_term path is only reachable for offset-0 fields, so an offset-0 store silently loses its dependence and floats. F. MEM_IN_STRUCT_P asymmetry in true_dependence (sched.c:817): struct-varying vs scalar-fixed do not depend. Struct-vs-scalar externs are a scheduling decision, not cosmetics. G. Two modelling traps: `sw $a1,SYM($a0)` is ONE cc1 insn (the lui/addu/store triple is gas -G0 macro expansion, not cc1 output); and __asm__ __volatile__ with a memory clobber is a FULL barrier that also sinks address chains. Plus the exhaustion result: 2,240- and 5,040-variant statement-order sweeps moved nothing, because the schedule was DAG-determined. When order does not matter, look for an alias or set-count property, not a permutation. §176j-2 — THE REPAIR PASS, MEASURED: 12 of 39 recovered / 579 ins, taking wave Q from 51 matches (3,631 ins) to 64 (4,245). Closeness must be COUNTED, not read off the first differing index (my first measurement reported six "closeness 0" drafts that were actually truncated). §176k — two silent selector bugs: ranking gate groups by MEMBER COUNT collapses a wide band to the smallest functions when the gate cost is per-slate (60 cards/2,604 ins chosen where 46/4,829 were available); and a selector that globs its own output counts the previous attempt as spent (pool 106 -> 46). Any derive-from-disk rule must exclude the artifact it is about to produce. --- docs/cookbook-index.md | 86 ++++++++++++++++---------- docs/matching-cookbook.md | 124 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 177 insertions(+), 33 deletions(-) diff --git a/docs/cookbook-index.md b/docs/cookbook-index.md index b1b474abc..2929cbeab 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 / 544 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 / 554 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. @@ -42,10 +42,10 @@ - **§3-C.** — Inner-block declaration does NOT delay slot allocation — BYTE-REFUTED L10065 - **§162** — CROSS-JUMP DIRECTION: the surviving copy is always the LATER one, so a BACKWARD `j` into a sibling arm is a source `goto` (P30 S48) L11263 - **§162** — The cross-jump "CALL veto" is a COUNT law, not a CALL law (BOUNDS §88a; P30 S48, `func_80189540`) L11299 -- **§177** — 🔴 THE EPILOGUE RETURN-DELAY SLOT IS DECIDED BY YOUR SAVED-REGISTER SET, NOT BY SCHEDULING L17119 -- **§176-A** — "SCHEDULE / DELAY-SLOT / LENGTH-DRIFT ±1" ⇒ check STATEMENT ORDER around the call first L17182 +- **§177** — 🔴 THE EPILOGUE RETURN-DELAY SLOT IS DECIDED BY YOUR SAVED-REGISTER SET, NOT BY SCHEDULING L17156 +- **§176-A** — "SCHEDULE / DELAY-SLOT / LENGTH-DRIFT ±1" ⇒ check STATEMENT ORDER around the call first L17306 -### instruction scheduling (23) +### instruction scheduling (24) - **§3-T2** — Source statement order drives instruction scheduling L78 - **§3** — When a diff is pure scheduling → decomp-permuter (harness built, Phase 6) L107 @@ -67,9 +67,10 @@ - **§3-A.** — `move_movables` hoists iff `threshold × savings × lifetime ≥ insn_count` — and you can read it L10148 - **§16Z** — SHARPENS *(sharpens §37 "the /s-DEP LATTICE", §136-13, §136-14, §16Xy, §162q, `gcc-2.7.2-map/sched.md` §64)* L14337 - **§175** — A CALLER-SAVED REGISTER PIN CAN BE A CORRECTNESS BUG, NOT JUST A SCHEDULING CHOICE (P31 wave H, 2026-08-15) L16709 -- **§177** — 🔴 THE EPILOGUE RETURN-DELAY SLOT IS DECIDED BY YOUR SAVED-REGISTER SET, NOT BY SCHEDULING L17119 -- **§176-A** — "SCHEDULE / DELAY-SLOT / LENGTH-DRIFT ±1" ⇒ check STATEMENT ORDER around the call first L17182 -- **§176-C** — 🔴 WALL REFUTATION: a hard-register pin CANNOT schedule around a call, because of a genuine gcc-2.7.2 bug L17246 +- **§177** — 🔴 THE EPILOGUE RETURN-DELAY SLOT IS DECIDED BY YOUR SAVED-REGISTER SET, NOT BY SCHEDULING L17156 +- **§3-B.** — A `return ` IS A PRIORITY-1 HARD-REG SET THE SCHEDULER PLACES FIRST (func_8001BE30, 92 ins) L17230 +- **§176-A** — "SCHEDULE / DELAY-SLOT / LENGTH-DRIFT ±1" ⇒ check STATEMENT ORDER around the call first L17306 +- **§176-C** — 🔴 WALL REFUTATION: a hard-register pin CANNOT schedule around a call, because of a genuine gcc-2.7.2 bug L17370 ### register allocation & pins (48) @@ -117,17 +118,17 @@ - **§156** — THE PREFERENCE-DONOR MERGE: cross-region variable reuse is what fills a0-a3, and a call-arg use in ONE region steers the fill in ALL of them (P30 S46 tier-3, `func_80186E24`, 611 ins: 236-off "S11 regalloc-order" → MATCH, zero new pins) L10769 - **§175** — A CALLER-SAVED REGISTER PIN CAN BE A CORRECTNESS BUG, NOT JUST A SCHEDULING CHOICE (P31 wave H, 2026-08-15) L16709 - **§176j** — STOPPING A WAVE MID-FLIGHT COSTS THE IN-FLIGHT TAIL (and how much is recoverable) L17093 -- **§177** — 🔴 THE EPILOGUE RETURN-DELAY SLOT IS DECIDED BY YOUR SAVED-REGISTER SET, NOT BY SCHEDULING L17119 -- **§176** — SEVEN LEVERS FROM THE P31 OVERNIGHT WAVES (2026-08-15): statement order, false regalloc, and the pin that fights back L17176 -- **§176-B** — "REGALLOC-PERM, 1-4 instructions off" ⇒ it is usually NOT register allocation L17206 -- **§176-C** — 🔴 WALL REFUTATION: a hard-register pin CANNOT schedule around a call, because of a genuine gcc-2.7.2 bug L17246 +- **§177** — 🔴 THE EPILOGUE RETURN-DELAY SLOT IS DECIDED BY YOUR SAVED-REGISTER SET, NOT BY SCHEDULING L17156 +- **§176** — SEVEN LEVERS FROM THE P31 OVERNIGHT WAVES (2026-08-15): statement order, false regalloc, and the pin that fights back L17300 +- **§176-B** — "REGALLOC-PERM, 1-4 instructions off" ⇒ it is usually NOT register allocation L17330 +- **§176-C** — 🔴 WALL REFUTATION: a hard-register pin CANNOT schedule around a call, because of a genuine gcc-2.7.2 bug L17370 ### CSE / redundancy / rematerialization (4) - **§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) L3313 - **§83d** — CSE's quantity budget is WHOLE-FUNCTION, so a local rewrite cannot fix a local symptom L6452 - **§153** — THE ADDRESS-REMATERIALISATION LAUNDER: a third zero-emission asm lever (P30 S43, `func_8018D98C`, 710 ins) L10463 -- **§176-D** — CSE-class levers used in reverse (two sharpenings of §153 and cse_expr §2) L17272 +- **§176-D** — CSE-class levers used in reverse (two sharpenings of §153 and cse_expr §2) L17396 ### loops & induction variables (10) @@ -142,7 +143,7 @@ - **§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) L10142 - **§171a** — THE MECHANICAL A-PROP DRAFT (P30 S50): 256 members banked with no agent in the loop L16417 -### structs, block moves & memcpy (37) +### structs, block moves & memcpy (38) - **§3-T2** — Source statement order drives instruction scheduling L78 - **§5** — Known hard-residual classes (instruction-identical, one byte-exact blocker) L199 @@ -180,9 +181,10 @@ - **§16Z** — SHARPENS *(sharpens §37 "the /s-DEP LATTICE", §136-13, §136-14, §16Xy, §162q, `gcc-2.7.2-map/sched.md` §64)* L14337 - **§3-The** — ADDRESS-CLASS TABLE: which load/store pairs even REACH the `/s` clause (P30 S48 wave 4, `func_80185B44`, ov_SC03_014) L14339 - **§176g** — SIZE A WAVE BY INSTRUCTIONS, NOT BY CARDS (P31 S52 — the adopted doctrine) L16927 -- **§176-B** — "REGALLOC-PERM, 1-4 instructions off" ⇒ it is usually NOT register allocation L17206 +- **§3-F.** — `MEM_IN_STRUCT_P` ASYMMETRY IN `true_dependence` (func_80037144, 124 ins) L17267 +- **§176-B** — "REGALLOC-PERM, 1-4 instructions off" ⇒ it is usually NOT register allocation L17330 -### types, signedness & load/store width (35) +### types, signedness & load/store width (36) - **§3-I1** — Unsigned range check: `(x - lo) < (hi-lo)` → `addiu`+`sltiu` L41 - **§3-I2** — Byte mask forces `andi` even after `lbu` L47 @@ -219,6 +221,7 @@ - **§3-C.** — PAC type 1 = the same payload class as type 4, just NOT compressed L10545 - **§155b** — check the TYPE your oracle returns before comparing against it (S45 p5) L10584 - **§3-B.** — Typedef handling — the only strategy that survives contact L16991 +- **§3-D.** — A NARROW TYPE BLOCKS COPY ELISION (func_8001D3FC — new idiom) L17251 ### declarations, prototypes & K&R (58) @@ -326,7 +329,7 @@ - **§127a** — §71 (sibling-first) is the strongest `-O0` lever, and it beats the index L8377 - **§132** — The `JR-PAIR-IN-ONE-O0-OBJECT` "wall" was TWO instrument defects: a merged-double span the carve could not see, and a truncated object no rule deleted (P30 S29, `func_8013B83C` + `func_8013BD74`) L8570 -### family propagation & sweeps (86) +### family propagation & sweeps (87) - **§8d** — Templating a body INTO a TU must not CHANGE its declaration environment — demote the carried data externs (Phase 26 session 8, byte-proven on `func_8015AE2C` ×133) L483 - **§11** — Cross-binary dedup & code-sharing (Phase 11 — "one match unlocks many") L908 @@ -414,6 +417,7 @@ - **§168** — THE COUSIN TIER (P30 S49, 2026-08-12): h_seq's exact-hash brittleness, measured — and the similarity map above it L16208 - **§171a** — THE MECHANICAL A-PROP DRAFT (P30 S50): 256 members banked with no agent in the loop L16417 - **§171b** — THREE CARRIES THE MECHANICAL DRAFT NEEDS (P30 S50, banking the top-reach families) L16467 +- **§3-G.** — TWO MODELLING TRAPS THAT COST THESE AGENTS SWEEPS OF HUNDREDS OF COMPILES L17273 ### integration / TU plumbing (41) @@ -575,9 +579,9 @@ - **§3-C2.** — RECONCILE BEFORE THE FIRST GATE — a parked draft gets HARDER to bank, not easier L17020 - **§3-D.** — The measured cost shape, and what to build next L17043 - **§176i** — WHAT A STATIC PRE-GATE CHECK CAN AND CANNOT PROVE (P31 S52, wave Q) L17061 -- **§176c** — MAIN (SLUS_007.26) CANNOT BE GATED INCREMENTALLY L17165 +- **§176c** — MAIN (SLUS_007.26) CANNOT BE GATED INCREMENTALLY L17289 -### process, measurement & doctrine (78) +### process, measurement & doctrine (79) - **§8e** — The jtbl ALIGNMENT LAW + the pad-spec filter — multi-table .rodata spans (Phase 29, byte-proven; `.run/probe_jtbl/verdict.md`) L530 - **§3-The** — mechanism: game-code dedup is SOURCE-LEVEL, not an object swap (R-D1, the key lesson) L926 @@ -655,10 +659,11 @@ - **§176g** — SIZE A WAVE BY INSTRUCTIONS, NOT BY CARDS (P31 S52 — the adopted doctrine) L16927 - **§176h** — THE BATCH-SUBSTITUTION HAZARD MAP (P31 S52): seven holes, three wrong fixes, one law L16965 - **§3-D.** — The measured cost shape, and what to build next L17043 -- **§176-C** — 🔴 WALL REFUTATION: a hard-register pin CANNOT schedule around a call, because of a genuine gcc-2.7.2 bug L17246 -- **§176-E** — Two cheap source spellings, both cc1-probed L17298 +- **§176j-2** — THE REPAIR PASS, MEASURED (do this instead of resuming) L17119 +- **§176-C** — 🔴 WALL REFUTATION: a hard-register pin CANNOT schedule around a call, because of a genuine gcc-2.7.2 bug L17370 +- **§176-E** — Two cheap source spellings, both cc1-probed L17422 -### (unbucketed — title matched no symptom vocabulary) (178) +### (unbucketed — title matched no symptom vocabulary) (183) - **§3-How** — to use this L30 - **§1** — Idiom catalog (asm pattern → C that produces it) L39 @@ -836,8 +841,13 @@ - **§176a** — THE VERIFICATION-LAYER LAWS (P31 overnight, 2026-08-15). What each check can and cannot prove. L16734 - **§176b** — BATCH-GATING MECHANICS (P31): what changes when N drafts land in ONE .c L16774 - **§3-C.** — The limit that remains (recorded, not solved) L17013 -- **§176-F** — Misdiagnosis triage: four residual verdicts that were lying L17320 -- **What** — is NOT banked here L17337 +- **§176k** — TWO SELECTOR BUGS THAT SILENTLY SHRINK A WAVE L17139 +- **§178** — SIX LEVERS MINED FROM THE WAVE-P JOURNALS (P31 S52), each byte-proven and source-cited L17202 +- **§3-A.** — THE `$0`-ADD OPAQUE COPY defeats `make_regs_eqv` (func_80033398, 93 ins) L17214 +- **§3-C.** — SINGLE-SET TEMPS GET THE BIRTHING BOOST (func_8001D3FC, 196 ins) L17243 +- **§3-E.** — THE ZERO-OFFSET ALIAS HOLE (func_80037028, 71 ins) L17258 +- **§176-F** — Misdiagnosis triage: four residual verdicts that were lying L17444 +- **What** — is NOT banked here L17461 ## All sections, in order @@ -1376,13 +1386,23 @@ - **§3-D.** — The measured cost shape, and what to build next L17043 - **§176i** — WHAT A STATIC PRE-GATE CHECK CAN AND CANNOT PROVE (P31 S52, wave Q) L17061 - **§176j** — STOPPING A WAVE MID-FLIGHT COSTS THE IN-FLIGHT TAIL (and how much is recoverable) L17093 -- **§177** — 🔴 THE EPILOGUE RETURN-DELAY SLOT IS DECIDED BY YOUR SAVED-REGISTER SET, NOT BY SCHEDULING L17119 -- **§176c** — MAIN (SLUS_007.26) CANNOT BE GATED INCREMENTALLY L17165 -- **§176** — SEVEN LEVERS FROM THE P31 OVERNIGHT WAVES (2026-08-15): statement order, false regalloc, and the pin that fights back L17176 -- **§176-A** — "SCHEDULE / DELAY-SLOT / LENGTH-DRIFT ±1" ⇒ check STATEMENT ORDER around the call first L17182 -- **§176-B** — "REGALLOC-PERM, 1-4 instructions off" ⇒ it is usually NOT register allocation L17206 -- **§176-C** — 🔴 WALL REFUTATION: a hard-register pin CANNOT schedule around a call, because of a genuine gcc-2.7.2 bug L17246 -- **§176-D** — CSE-class levers used in reverse (two sharpenings of §153 and cse_expr §2) L17272 -- **§176-E** — Two cheap source spellings, both cc1-probed L17298 -- **§176-F** — Misdiagnosis triage: four residual verdicts that were lying L17320 -- **What** — is NOT banked here L17337 +- **§176j-2** — THE REPAIR PASS, MEASURED (do this instead of resuming) L17119 +- **§176k** — TWO SELECTOR BUGS THAT SILENTLY SHRINK A WAVE L17139 +- **§177** — 🔴 THE EPILOGUE RETURN-DELAY SLOT IS DECIDED BY YOUR SAVED-REGISTER SET, NOT BY SCHEDULING L17156 +- **§178** — SIX LEVERS MINED FROM THE WAVE-P JOURNALS (P31 S52), each byte-proven and source-cited L17202 +- **§3-A.** — THE `$0`-ADD OPAQUE COPY defeats `make_regs_eqv` (func_80033398, 93 ins) L17214 +- **§3-B.** — A `return ` IS A PRIORITY-1 HARD-REG SET THE SCHEDULER PLACES FIRST (func_8001BE30, 92 ins) L17230 +- **§3-C.** — SINGLE-SET TEMPS GET THE BIRTHING BOOST (func_8001D3FC, 196 ins) L17243 +- **§3-D.** — A NARROW TYPE BLOCKS COPY ELISION (func_8001D3FC — new idiom) L17251 +- **§3-E.** — THE ZERO-OFFSET ALIAS HOLE (func_80037028, 71 ins) L17258 +- **§3-F.** — `MEM_IN_STRUCT_P` ASYMMETRY IN `true_dependence` (func_80037144, 124 ins) L17267 +- **§3-G.** — TWO MODELLING TRAPS THAT COST THESE AGENTS SWEEPS OF HUNDREDS OF COMPILES L17273 +- **§176c** — MAIN (SLUS_007.26) CANNOT BE GATED INCREMENTALLY L17289 +- **§176** — SEVEN LEVERS FROM THE P31 OVERNIGHT WAVES (2026-08-15): statement order, false regalloc, and the pin that fights back L17300 +- **§176-A** — "SCHEDULE / DELAY-SLOT / LENGTH-DRIFT ±1" ⇒ check STATEMENT ORDER around the call first L17306 +- **§176-B** — "REGALLOC-PERM, 1-4 instructions off" ⇒ it is usually NOT register allocation L17330 +- **§176-C** — 🔴 WALL REFUTATION: a hard-register pin CANNOT schedule around a call, because of a genuine gcc-2.7.2 bug L17370 +- **§176-D** — CSE-class levers used in reverse (two sharpenings of §153 and cse_expr §2) L17396 +- **§176-E** — Two cheap source spellings, both cc1-probed L17422 +- **§176-F** — Misdiagnosis triage: four residual verdicts that were lying L17444 +- **What** — is NOT banked here L17461 diff --git a/docs/matching-cookbook.md b/docs/matching-cookbook.md index 1f08c5880..44778c5f6 100644 --- a/docs/matching-cookbook.md +++ b/docs/matching-cookbook.md @@ -17116,6 +17116,43 @@ in-flight draft is near-matching, the tokens are already spent and stopping conv "nearly banked" into "needs a second, cheaper pass" — which is fine, but it is a deferral, not a saving. +### §176j-2 — THE REPAIR PASS, MEASURED (do this instead of resuming) + +Wave Q's 39 unfinished drafts were run through a repair-only workflow: one stage, no draft phase, +each agent handed **its own on-disk draft plus that draft's measured closeness**, with the prompt +opening `THIS IS A REPAIR, NOT A REWRITE`. Model routing deliberately cheap (6 haiku / 29 sonnet / +4 opus — opus only for the four >130-ins functions). + +**Result: 12 of 39 recovered, 579 instructions**, taking wave Q from 51 verified matches (3,631 ins) +to **64 (4,245 ins)**. Roughly a quarter of a stopped wave's tail comes back for a fraction of a +fresh wave's cost. + +Two calibration notes for next time: +- **Closeness must be counted, not read off the first differing index.** My first measurement + sorted by the index of the first mismatch and reported six drafts at "closeness 0"; they were + *truncated* drafts (agent stopped mid-write) that matched to instruction 35–48 and then simply + ended. Count the differing instructions. +- **The yield concentrates in the small-residual band.** Of the 12 recovered, most came from the + ≤15-differing-instruction band; the 35–40 band mostly stayed stuck (and much of what remained + turned out to be §177's epilogue rule, not per-function work at all). + +### §176k — TWO SELECTOR BUGS THAT SILENTLY SHRINK A WAVE + +Both found while building wave Q, both silent, both would have quietly cost instructions forever: + +1. **Ranking gate groups by MEMBER COUNT collapses a wide band to the smallest functions.** + `build_wave_atlas` ranked `(binary,TU)` groups by how many candidates they held — correct for + overlays, where each group costs its own rebuild. **For `main` the gate cost is per SLATE**, so + that ranking filled the wave from the biggest-by-count group, which is the smallest-by- + instruction one: measured **60 cards / 2,604 ins** selected when **46 cards / 4,829 ins** were + available. Fixed with `--rank mass`. *Whenever a selector ranks by a proxy, check the proxy + still means what it meant when the cost model was written.* +2. **A selector that globs its own output poisons itself.** Deriving the already-waved set from + `glob('.run/wave_*_cards.json')` matched the file the run was about to write, so re-running with + identical filters counted the previous attempt's cards as spent: candidate pool **106 → 46**. + Fixed by excluding the output path. *Any derive-from-disk rule (R33) must exclude the artifact + it is about to produce.* + ## §177 — 🔴 THE EPILOGUE RETURN-DELAY SLOT IS DECIDED BY YOUR SAVED-REGISTER SET, NOT BY SCHEDULING ### (P31 S52 — source-confirmed in `gcc-2.7.2/config/mips/mips.c`; eleven functions were stuck on it) @@ -17162,6 +17199,93 @@ banked, and they were all about to be written off as intrinsic. **A residual tha agents call "structural" is a signal to read the compiler, not to file a wall** (R17): the answer was forty lines of `mips.c` and it was already sitting in `tools/reference/gcc-2.7.2/`. +## §178 — SIX LEVERS MINED FROM THE WAVE-P JOURNALS (P31 S52), each byte-proven and source-cited + +Wave P's repair agents did something the campaign has rarely got: they read `cc1 -dS/-da` dumps and +then the gcc-2.7.2 source, and **four of them refuted the first pass's own diagnosis**. Every lever +below took a draft to MATCH; every one names the file and line that explains it. The recurring +meta-finding is stated first because it is worth more than any single lever: + +> **"REGALLOC-PERM" is the most over-diagnosed class in this project.** In four separate wave-P +> functions the visible symptom was a register swap and the actual cause was in `cse.c` or +> `sched.c`, decided *before* allocation — which is exactly why pins, declaration order and +> statement order all failed on them. When a pin sweep plateaus, stop pinning and dump the pass. + +### A. THE `$0`-ADD OPAQUE COPY defeats `make_regs_eqv` (func_80033398, 93 ins) +Symptom: `srl $s6,$s7,16` where the target has `srl $s6,$a0,16` — every pin combination left it +bit-identical. Cause (`cse.c:826`, `make_regs_eqv`): when the second pseudo of a copy pair outlives +the first and its live range escapes the cse block, it is head-promoted to `qty_first_reg`, and +`canon_reg` rewrites **every later use** of the parameter pseudo to it. No C spelling of a plain +copy escapes this. The lever: +```c +register s32 zr __asm__("$0"); +s7v = arg0 + zr; /* emits `addu $s7,$a0,$zero` — but the RTL is a PLUS, not (set reg reg) */ +s6v = arg0 >> 16; /* so the srl still reads the parm pseudo, which dies here */ +``` +Because it is a PLUS, `make_regs_eqv` never merges the quantities; local-alloc's copy suggestion +then hands the parm `$a0` and deletes the real copy as a no-op. **MATCH on the first compile.** +Bonus: with the opaque copy in place, 3 of the draft's 5 register pins became dead weight and were +removed — worth re-minimising pins after any cse-level fix. + +### B. A `return ` IS A PRIORITY-1 HARD-REG SET THE SCHEDULER PLACES FIRST (func_8001BE30, 92 ins) +Symptom: a clean `$v1`-for-`$v0` swap on throwaway temps; six levers failed. Cause, read from the +`-da` dumps: `(set (reg/i:SI 2 v0) (const_int 0))` — the `return 0` — is a hard-reg set with +`REG_N_SETS(reg 2) > 1` (the function has six return sites), so `birthing_insn_p`/`adjust_priority` +give it **no boost**; it sits at priority 1 while neighbours outrank it, and gcc-2.7.2's *backward* +list scheduler picks it last, i.e. **emits it FIRST in the block**. Hard `$v0` is then live across +the temp's whole range at local-alloc time, forcing the temp to `$v1`. sched2+dbr later move +`move $2,$0` into the `j` delay slot — which is why the SHAPE looked right while the register +stayed wrong. +**Lever: delete the hard-reg return set from the block.** Replace each in-block `return 0;` with +`goto L_ret0;` to one shared `L_ret0: return 0;` tail. The blocks then contain no set of hard `$v0`, +the temps take `$v0`, and dbr steals the shared `addu $v0,$zero,$zero` back into each delay slot. + +### C. SINGLE-SET TEMPS GET THE BIRTHING BOOST (func_8001D3FC, 196 ins) +`birthing_insn_p` (`sched.c:2469`) boosts an insn only when its destination has **exactly one +static set** (`reg_n_sets[dest]==1`, the discriminator at `:2490`). A three-set temp +(`ub = expr; u = ub; ub = ub + w - 1;`) gets no boost, so its whole chain is picked late and placed +early. Splitting off a genuinely single-set temp boosts the insn and **drags its feeder chain down +with it**. Byte-proven that the boost must land on the insn you care about: splitting one step +earlier reverted the schedule. + +### D. A NARROW TYPE BLOCKS COPY ELISION (func_8001D3FC — new idiom) +Once a temp is single-set, a plain same-mode `u = ut` copy is deleted by cse/coalescing, and `u` +*becomes* `ut` — which is what produced the "$a0↔$v1 swap" the first agent called irreducible. +Declaring the destination **narrower** (`u16 u;`) makes it an SI→HI mode-changing copy that cse +cannot propagate through and the allocator cannot coalesce, so **the copy survives at its source +position** and the entire register assignment falls into place. One type change, ~20 instructions. + +### E. THE ZERO-OFFSET ALIAS HOLE (func_80037028, 71 ins) +`sched.c:memrefs_conflict_p`, PLUS-vs-PLUS branch, falls through to `find_symbolic_term(x/y)` and +reports **no conflict when the two symbols differ** — but that path is only reachable for the plain +`(plus reg symbol_ref)` address form, i.e. **a field at offset 0**. A field at a non-zero offset is +`(plus reg (const (plus symbol N)))` and comes back conservative. Consequence: an offset-0 store +silently loses its dependence on later loads and floats to the bottom of the block. If a store at +offset 0 is scheduled wrongly, that is why — and giving the struct a non-zero-offset field to touch +restores the dependence. + +### F. `MEM_IN_STRUCT_P` ASYMMETRY IN `true_dependence` (func_80037144, 124 ins) +`sched.c:817` skips a dependence when `x` is `/s` with a **varying** address, non-QImode, while +`mem` is **non-`/s` at a fixed address**. So `D_800A463C[k].unk00` (struct, varying) does not +depend on plain scalar `D_8007622C` stores. Model your externs accordingly: struct-vs-scalar is a +scheduling decision, not cosmetics. + +### G. TWO MODELLING TRAPS THAT COST THESE AGENTS SWEEPS OF HUNDREDS OF COMPILES +1. **`sw $a1, D_80076244($a0)` is ONE cc1 insn** (symbol + scaled index). The `lui/addu/store` + triples you see in the `.s` are **gas `-G0` macro expansion, not cc1 output**. Model at the cc1 + level or you will chase a phantom. Only a struct array indexed by the slot produces that form; + six parallel `extern u8 D_800762xx[]` make every access a distinct `SYMBOL_REF` and change the + whole dependence graph. +2. **`__asm__ __volatile__("" ::: "memory")` is a FULL barrier** — it clobbers all pseudos, so it + sinks address chains below stores and can kill a delay-slot steal. When you only want *memory* + ordering, that is the wrong tool; an empty non-volatile `__asm__("")` was the one that worked as + a pure optimization barrier elsewhere (§B above, and the STORE_FLAG_VALUE fold defeat). + +**Statement-order sweeps are frequently worthless here and the agents proved it by exhaustion:** a +2,240-variant sweep and a 5,040-permutation sweep each moved nothing, because the schedule was +fully DAG-determined. When order does not matter, the answer is an *alias* or a *set-count* +property, not a permutation. + ## §176c — MAIN (SLUS_007.26) CANNOT BE GATED INCREMENTALLY main's `make extract` runs the EXE-only `psyq_integrate` + `ld_interleave` steps, which **rewrite the