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