docs(phase-31): cookbook §178 + §176j-2 + §176k — mine the wave-P journals, the repair-pass yield, two selector bugs

§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 <const>` 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.
This commit is contained in:
Drew T
2026-08-16 14:23:19 -06:00
parent 4adb559a29
commit 49fc465a51
2 changed files with 177 additions and 33 deletions
+53 -33
View File
@@ -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 <sub>L10065</sub>
- **§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) <sub>L11263</sub>
- **§162** — The cross-jump "CALL veto" is a COUNT law, not a CALL law (BOUNDS §88a; P30 S48, `func_80189540`) <sub>L11299</sub>
- **§177** — 🔴 THE EPILOGUE RETURN-DELAY SLOT IS DECIDED BY YOUR SAVED-REGISTER SET, NOT BY SCHEDULING <sub>L17119</sub>
- **§176-A** — "SCHEDULE / DELAY-SLOT / LENGTH-DRIFT ±1" ⇒ check STATEMENT ORDER around the call first <sub>L17182</sub>
- **§177** — 🔴 THE EPILOGUE RETURN-DELAY SLOT IS DECIDED BY YOUR SAVED-REGISTER SET, NOT BY SCHEDULING <sub>L17156</sub>
- **§176-A** — "SCHEDULE / DELAY-SLOT / LENGTH-DRIFT ±1" ⇒ check STATEMENT ORDER around the call first <sub>L17306</sub>
### instruction scheduling (23)
### instruction scheduling (24)
- **§3-T2** — Source statement order drives instruction scheduling <sub>L78</sub>
- **§3** — When a diff is pure scheduling → decomp-permuter (harness built, Phase 6) <sub>L107</sub>
@@ -67,9 +67,10 @@
- **§3-A.** — `move_movables` hoists iff `threshold × savings × lifetime ≥ insn_count` — and you can read it <sub>L10148</sub>
- **§16Z** — SHARPENS *(sharpens §37 "the /s-DEP LATTICE", §136-13, §136-14, §16Xy, §162q, `gcc-2.7.2-map/sched.md` §64)* <sub>L14337</sub>
- **§175** — A CALLER-SAVED REGISTER PIN CAN BE A CORRECTNESS BUG, NOT JUST A SCHEDULING CHOICE (P31 wave H, 2026-08-15) <sub>L16709</sub>
- **§177** — 🔴 THE EPILOGUE RETURN-DELAY SLOT IS DECIDED BY YOUR SAVED-REGISTER SET, NOT BY SCHEDULING <sub>L17119</sub>
- **§176-A** — "SCHEDULE / DELAY-SLOT / LENGTH-DRIFT ±1" ⇒ check STATEMENT ORDER around the call first <sub>L17182</sub>
- **§176-C** — 🔴 WALL REFUTATION: a hard-register pin CANNOT schedule around a call, because of a genuine gcc-2.7.2 bug <sub>L17246</sub>
- **§177** — 🔴 THE EPILOGUE RETURN-DELAY SLOT IS DECIDED BY YOUR SAVED-REGISTER SET, NOT BY SCHEDULING <sub>L17156</sub>
- **§3-B.** — A `return <const>` IS A PRIORITY-1 HARD-REG SET THE SCHEDULER PLACES FIRST (func_8001BE30, 92 ins) <sub>L17230</sub>
- **§176-A** — "SCHEDULE / DELAY-SLOT / LENGTH-DRIFT ±1" ⇒ check STATEMENT ORDER around the call first <sub>L17306</sub>
- **§176-C** — 🔴 WALL REFUTATION: a hard-register pin CANNOT schedule around a call, because of a genuine gcc-2.7.2 bug <sub>L17370</sub>
### 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) <sub>L10769</sub>
- **§175** — A CALLER-SAVED REGISTER PIN CAN BE A CORRECTNESS BUG, NOT JUST A SCHEDULING CHOICE (P31 wave H, 2026-08-15) <sub>L16709</sub>
- **§176j** — STOPPING A WAVE MID-FLIGHT COSTS THE IN-FLIGHT TAIL (and how much is recoverable) <sub>L17093</sub>
- **§177** — 🔴 THE EPILOGUE RETURN-DELAY SLOT IS DECIDED BY YOUR SAVED-REGISTER SET, NOT BY SCHEDULING <sub>L17119</sub>
- **§176** — SEVEN LEVERS FROM THE P31 OVERNIGHT WAVES (2026-08-15): statement order, false regalloc, and the pin that fights back <sub>L17176</sub>
- **§176-B** — "REGALLOC-PERM, 1-4 instructions off" ⇒ it is usually NOT register allocation <sub>L17206</sub>
- **§176-C** — 🔴 WALL REFUTATION: a hard-register pin CANNOT schedule around a call, because of a genuine gcc-2.7.2 bug <sub>L17246</sub>
- **§177** — 🔴 THE EPILOGUE RETURN-DELAY SLOT IS DECIDED BY YOUR SAVED-REGISTER SET, NOT BY SCHEDULING <sub>L17156</sub>
- **§176** — SEVEN LEVERS FROM THE P31 OVERNIGHT WAVES (2026-08-15): statement order, false regalloc, and the pin that fights back <sub>L17300</sub>
- **§176-B** — "REGALLOC-PERM, 1-4 instructions off" ⇒ it is usually NOT register allocation <sub>L17330</sub>
- **§176-C** — 🔴 WALL REFUTATION: a hard-register pin CANNOT schedule around a call, because of a genuine gcc-2.7.2 bug <sub>L17370</sub>
### 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) <sub>L3313</sub>
- **§83d** — CSE's quantity budget is WHOLE-FUNCTION, so a local rewrite cannot fix a local symptom <sub>L6452</sub>
- **§153** — THE ADDRESS-REMATERIALISATION LAUNDER: a third zero-emission asm lever (P30 S43, `func_8018D98C`, 710 ins) <sub>L10463</sub>
- **§176-D** — CSE-class levers used in reverse (two sharpenings of §153 and cse_expr §2) <sub>L17272</sub>
- **§176-D** — CSE-class levers used in reverse (two sharpenings of §153 and cse_expr §2) <sub>L17396</sub>
### 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) <sub>L10142</sub>
- **§171a** — THE MECHANICAL A-PROP DRAFT (P30 S50): 256 members banked with no agent in the loop <sub>L16417</sub>
### structs, block moves & memcpy (37)
### structs, block moves & memcpy (38)
- **§3-T2** — Source statement order drives instruction scheduling <sub>L78</sub>
- **§5** — Known hard-residual classes (instruction-identical, one byte-exact blocker) <sub>L199</sub>
@@ -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)* <sub>L14337</sub>
- **§3-The** — ADDRESS-CLASS TABLE: which load/store pairs even REACH the `/s` clause (P30 S48 wave 4, `func_80185B44`, ov_SC03_014) <sub>L14339</sub>
- **§176g** — SIZE A WAVE BY INSTRUCTIONS, NOT BY CARDS (P31 S52 — the adopted doctrine) <sub>L16927</sub>
- **§176-B** — "REGALLOC-PERM, 1-4 instructions off" ⇒ it is usually NOT register allocation <sub>L17206</sub>
- **§3-F.** — `MEM_IN_STRUCT_P` ASYMMETRY IN `true_dependence` (func_80037144, 124 ins) <sub>L17267</sub>
- **§176-B** — "REGALLOC-PERM, 1-4 instructions off" ⇒ it is usually NOT register allocation <sub>L17330</sub>
### types, signedness & load/store width (35)
### types, signedness & load/store width (36)
- **§3-I1** — Unsigned range check: `(x - lo) < (hi-lo)` → `addiu`+`sltiu` <sub>L41</sub>
- **§3-I2** — Byte mask forces `andi` even after `lbu` <sub>L47</sub>
@@ -219,6 +221,7 @@
- **§3-C.** — PAC type 1 = the same payload class as type 4, just NOT compressed <sub>L10545</sub>
- **§155b** — check the TYPE your oracle returns before comparing against it (S45 p5) <sub>L10584</sub>
- **§3-B.** — Typedef handling — the only strategy that survives contact <sub>L16991</sub>
- **§3-D.** — A NARROW TYPE BLOCKS COPY ELISION (func_8001D3FC — new idiom) <sub>L17251</sub>
### declarations, prototypes & K&R (58)
@@ -326,7 +329,7 @@
- **§127a** — §71 (sibling-first) is the strongest `-O0` lever, and it beats the index <sub>L8377</sub>
- **§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`) <sub>L8570</sub>
### 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) <sub>L483</sub>
- **§11** — Cross-binary dedup & code-sharing (Phase 11 — "one match unlocks many") <sub>L908</sub>
@@ -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 <sub>L16208</sub>
- **§171a** — THE MECHANICAL A-PROP DRAFT (P30 S50): 256 members banked with no agent in the loop <sub>L16417</sub>
- **§171b** — THREE CARRIES THE MECHANICAL DRAFT NEEDS (P30 S50, banking the top-reach families) <sub>L16467</sub>
- **§3-G.** — TWO MODELLING TRAPS THAT COST THESE AGENTS SWEEPS OF HUNDREDS OF COMPILES <sub>L17273</sub>
### integration / TU plumbing (41)
@@ -575,9 +579,9 @@
- **§3-C2.** — RECONCILE BEFORE THE FIRST GATE — a parked draft gets HARDER to bank, not easier <sub>L17020</sub>
- **§3-D.** — The measured cost shape, and what to build next <sub>L17043</sub>
- **§176i** — WHAT A STATIC PRE-GATE CHECK CAN AND CANNOT PROVE (P31 S52, wave Q) <sub>L17061</sub>
- **§176c** — MAIN (SLUS_007.26) CANNOT BE GATED INCREMENTALLY <sub>L17165</sub>
- **§176c** — MAIN (SLUS_007.26) CANNOT BE GATED INCREMENTALLY <sub>L17289</sub>
### 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`) <sub>L530</sub>
- **§3-The** — mechanism: game-code dedup is SOURCE-LEVEL, not an object swap (R-D1, the key lesson) <sub>L926</sub>
@@ -655,10 +659,11 @@
- **§176g** — SIZE A WAVE BY INSTRUCTIONS, NOT BY CARDS (P31 S52 — the adopted doctrine) <sub>L16927</sub>
- **§176h** — THE BATCH-SUBSTITUTION HAZARD MAP (P31 S52): seven holes, three wrong fixes, one law <sub>L16965</sub>
- **§3-D.** — The measured cost shape, and what to build next <sub>L17043</sub>
- **§176-C** — 🔴 WALL REFUTATION: a hard-register pin CANNOT schedule around a call, because of a genuine gcc-2.7.2 bug <sub>L17246</sub>
- **§176-E** — Two cheap source spellings, both cc1-probed <sub>L17298</sub>
- **§176j-2** — THE REPAIR PASS, MEASURED (do this instead of resuming) <sub>L17119</sub>
- **§176-C** — 🔴 WALL REFUTATION: a hard-register pin CANNOT schedule around a call, because of a genuine gcc-2.7.2 bug <sub>L17370</sub>
- **§176-E** — Two cheap source spellings, both cc1-probed <sub>L17422</sub>
### (unbucketed — title matched no symptom vocabulary) (178)
### (unbucketed — title matched no symptom vocabulary) (183)
- **§3-How** — to use this <sub>L30</sub>
- **§1** — Idiom catalog (asm pattern → C that produces it) <sub>L39</sub>
@@ -836,8 +841,13 @@
- **§176a** — THE VERIFICATION-LAYER LAWS (P31 overnight, 2026-08-15). What each check can and cannot prove. <sub>L16734</sub>
- **§176b** — BATCH-GATING MECHANICS (P31): what changes when N drafts land in ONE .c <sub>L16774</sub>
- **§3-C.** — The limit that remains (recorded, not solved) <sub>L17013</sub>
- **§176-F** — Misdiagnosis triage: four residual verdicts that were lying <sub>L17320</sub>
- **What** — is NOT banked here <sub>L17337</sub>
- **§176k** — TWO SELECTOR BUGS THAT SILENTLY SHRINK A WAVE <sub>L17139</sub>
- **§178** — SIX LEVERS MINED FROM THE WAVE-P JOURNALS (P31 S52), each byte-proven and source-cited <sub>L17202</sub>
- **§3-A.** — THE `$0`-ADD OPAQUE COPY defeats `make_regs_eqv` (func_80033398, 93 ins) <sub>L17214</sub>
- **§3-C.** — SINGLE-SET TEMPS GET THE BIRTHING BOOST (func_8001D3FC, 196 ins) <sub>L17243</sub>
- **§3-E.** — THE ZERO-OFFSET ALIAS HOLE (func_80037028, 71 ins) <sub>L17258</sub>
- **§176-F** — Misdiagnosis triage: four residual verdicts that were lying <sub>L17444</sub>
- **What** — is NOT banked here <sub>L17461</sub>
## All sections, in order
@@ -1376,13 +1386,23 @@
- **§3-D.** — The measured cost shape, and what to build next <sub>L17043</sub>
- **§176i** — WHAT A STATIC PRE-GATE CHECK CAN AND CANNOT PROVE (P31 S52, wave Q) <sub>L17061</sub>
- **§176j** — STOPPING A WAVE MID-FLIGHT COSTS THE IN-FLIGHT TAIL (and how much is recoverable) <sub>L17093</sub>
- **§177** — 🔴 THE EPILOGUE RETURN-DELAY SLOT IS DECIDED BY YOUR SAVED-REGISTER SET, NOT BY SCHEDULING <sub>L17119</sub>
- **§176c** — MAIN (SLUS_007.26) CANNOT BE GATED INCREMENTALLY <sub>L17165</sub>
- **§176** — SEVEN LEVERS FROM THE P31 OVERNIGHT WAVES (2026-08-15): statement order, false regalloc, and the pin that fights back <sub>L17176</sub>
- **§176-A** — "SCHEDULE / DELAY-SLOT / LENGTH-DRIFT ±1" ⇒ check STATEMENT ORDER around the call first <sub>L17182</sub>
- **§176-B** — "REGALLOC-PERM, 1-4 instructions off" ⇒ it is usually NOT register allocation <sub>L17206</sub>
- **§176-C** — 🔴 WALL REFUTATION: a hard-register pin CANNOT schedule around a call, because of a genuine gcc-2.7.2 bug <sub>L17246</sub>
- **§176-D** — CSE-class levers used in reverse (two sharpenings of §153 and cse_expr §2) <sub>L17272</sub>
- **§176-E** — Two cheap source spellings, both cc1-probed <sub>L17298</sub>
- **§176-F** — Misdiagnosis triage: four residual verdicts that were lying <sub>L17320</sub>
- **What** — is NOT banked here <sub>L17337</sub>
- **§176j-2** — THE REPAIR PASS, MEASURED (do this instead of resuming) <sub>L17119</sub>
- **§176k** — TWO SELECTOR BUGS THAT SILENTLY SHRINK A WAVE <sub>L17139</sub>
- **§177** — 🔴 THE EPILOGUE RETURN-DELAY SLOT IS DECIDED BY YOUR SAVED-REGISTER SET, NOT BY SCHEDULING <sub>L17156</sub>
- **§178** — SIX LEVERS MINED FROM THE WAVE-P JOURNALS (P31 S52), each byte-proven and source-cited <sub>L17202</sub>
- **§3-A.** — THE `$0`-ADD OPAQUE COPY defeats `make_regs_eqv` (func_80033398, 93 ins) <sub>L17214</sub>
- **§3-B.** — A `return <const>` IS A PRIORITY-1 HARD-REG SET THE SCHEDULER PLACES FIRST (func_8001BE30, 92 ins) <sub>L17230</sub>
- **§3-C.** — SINGLE-SET TEMPS GET THE BIRTHING BOOST (func_8001D3FC, 196 ins) <sub>L17243</sub>
- **§3-D.** — A NARROW TYPE BLOCKS COPY ELISION (func_8001D3FC — new idiom) <sub>L17251</sub>
- **§3-E.** — THE ZERO-OFFSET ALIAS HOLE (func_80037028, 71 ins) <sub>L17258</sub>
- **§3-F.** — `MEM_IN_STRUCT_P` ASYMMETRY IN `true_dependence` (func_80037144, 124 ins) <sub>L17267</sub>
- **§3-G.** — TWO MODELLING TRAPS THAT COST THESE AGENTS SWEEPS OF HUNDREDS OF COMPILES <sub>L17273</sub>
- **§176c** — MAIN (SLUS_007.26) CANNOT BE GATED INCREMENTALLY <sub>L17289</sub>
- **§176** — SEVEN LEVERS FROM THE P31 OVERNIGHT WAVES (2026-08-15): statement order, false regalloc, and the pin that fights back <sub>L17300</sub>
- **§176-A** — "SCHEDULE / DELAY-SLOT / LENGTH-DRIFT ±1" ⇒ check STATEMENT ORDER around the call first <sub>L17306</sub>
- **§176-B** — "REGALLOC-PERM, 1-4 instructions off" ⇒ it is usually NOT register allocation <sub>L17330</sub>
- **§176-C** — 🔴 WALL REFUTATION: a hard-register pin CANNOT schedule around a call, because of a genuine gcc-2.7.2 bug <sub>L17370</sub>
- **§176-D** — CSE-class levers used in reverse (two sharpenings of §153 and cse_expr §2) <sub>L17396</sub>
- **§176-E** — Two cheap source spellings, both cc1-probed <sub>L17422</sub>
- **§176-F** — Misdiagnosis triage: four residual verdicts that were lying <sub>L17444</sub>
- **What** — is NOT banked here <sub>L17461</sub>
+124
View File
@@ -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 <const>` 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