mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-10-01 15:43:06 -04:00
docs(cookbook): §417 - a pin blocks jump.c's select collapse; unpinning exposes cse skip-blocks
Byte-proven on ov_SC03_013/func_8017E6F4 (182 ins). A pinned select arm expands as ior+copy (two insns), so jump.c:728's `x=b; if(c) x=a;` collapse cannot fire. Unpinning lets it fire - and then cse_end_of_basic_block's skip_blocks path walks between the two D_80184D2C[idx] reads and CSEs the symbol_ref into $s3. The earlier agent read that as "unpinning costs +2" and restored the pin, which re-blocked jump.c and hid the cse behaviour: the function plateaued for three attempts on that misattribution. The law: when removing a lever MOVES the residual instead of shrinking it, the new residual is a different pass. Attribute it with a pass dump before re-adding the lever.
This commit is contained in:
@@ -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 / 1084 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 / 1085 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.
|
||||
|
||||
@@ -191,7 +191,7 @@
|
||||
- **§393** — ★★ — THE **BIRTHING BOOST**: A SINGLE-SET LOCAL IS SCHEDULED LAST; GIVE IT A SECOND SET (P31 S69; byte-proven ov_SC02_017/func_8017FCFC) <sub>L32543</sub>
|
||||
- **§3-B.** — THE SCHEDULER DIALS (the dominant residual family this wave) <sub>L33108</sub>
|
||||
|
||||
### register allocation & pins (129)
|
||||
### register allocation & pins (130)
|
||||
|
||||
- **§10** — Closing the regalloc/scheduling hard tail by hand (LZSS, Phase 7 session F — the full close) <sub>L854</sub>
|
||||
- **Residual** — A — commutative `|`/`&`/`+` result lands in the wrong source-operand register <sub>L875</sub>
|
||||
@@ -322,8 +322,9 @@
|
||||
- **§375** — AN `$a0`-`$a3` PIN USED LATE RELOCATES AN **EARLIER** OUTGOING-CALL USE OF THAT REGISTER (P31 S68; main/func_8005F0C8, 88 ins) <sub>L32042</sub>
|
||||
- **§3-C.** — REGISTER ALLOCATION FROM C, WITHOUT PINS <sub>L33124</sub>
|
||||
- **§410** — ★★★ — COPY THEN ACCUMULATE ON THE COPY: resolving the birthing-boost vs register-allocation dilemma (P31 S71; byte-proven `ov_SC04_015/func_8017EB78`, 98 ins) <sub>L33353</sub>
|
||||
- **§417** — ★★★ — A REGISTER PIN CAN BLOCK `jump.c`'s SELECT COLLAPSE, AND UNPINNING THEN EXPOSES A `cse` SKIP-BLOCKS MERGE (P31 S71; byte-proven `ov_SC03_013/func_8017E6F4`, 182 ins) <sub>L33605</sub>
|
||||
|
||||
### CSE / redundancy / rematerialization (44)
|
||||
### CSE / redundancy / rematerialization (45)
|
||||
|
||||
- **§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>L3347</sub>
|
||||
- **§83d** — CSE's quantity budget is WHOLE-FUNCTION, so a local rewrite cannot fix a local symptom <sub>L6486</sub>
|
||||
@@ -369,6 +370,7 @@
|
||||
- **§373** — ★★★ — THE **DEAD-RESET CSE-BREAKER**, THE PIN THAT BREAKS A sched2 ANTI-DEP, AND WHY AN `asm` CAN NEVER RAISE PRIORITY (P31 S68; byte-proven ov_SC06_010/func_8017E764, 438 ins, fable escalation closed 8 → 0) <sub>L31977</sub>
|
||||
- **§3-1.** — DEAD-RESET CSE-BREAKER — the zero-footprint replacement for a §195-I asm re-tie <sub>L31979</sub>
|
||||
- **§385** — ★★★ — THE **SCHED2 PRIORITY-DONOR ASM**: closing the "hoisted-invariant vs IV-init preheader swap" class (P31 S69; byte-proven main/func_80038A58, 347 ins, fable escalation 2 → 0) <sub>L32309</sub>
|
||||
- **§417** — ★★★ — A REGISTER PIN CAN BLOCK `jump.c`'s SELECT COLLAPSE, AND UNPINNING THEN EXPOSES A `cse` SKIP-BLOCKS MERGE (P31 S71; byte-proven `ov_SC03_013/func_8017E6F4`, 182 ins) <sub>L33605</sub>
|
||||
|
||||
### loops & induction variables (46)
|
||||
|
||||
@@ -2754,6 +2756,7 @@
|
||||
- **§414** — ★★★ — `parallel_gate` ON `main` IS A FALSE PASS, AND THE RULE WAS ALREADY WRITTEN DOWN (P31 S71) <sub>L33510</sub>
|
||||
- **§415** — ★★ — A FILE-SCOPE DECL MAKES gcc-2.7.2 MERGE THE TU'S LATER *BLOCK-SCOPE* EXTERNS INTO IT (P31 S71; byte-proven `ov_SC04_011/func_80180B24`, 215 ins) <sub>L33547</sub>
|
||||
- **§416** — ★★ — FOUR LEVERS FROM THE S71 OVERNIGHT LANE, none of which the cookbook held (P31 S71) <sub>L33572</sub>
|
||||
- **§417** — ★★★ — A REGISTER PIN CAN BLOCK `jump.c`'s SELECT COLLAPSE, AND UNPINNING THEN EXPOSES A `cse` SKIP-BLOCKS MERGE (P31 S71; byte-proven `ov_SC03_013/func_8017E6F4`, 182 ins) <sub>L33605</sub>
|
||||
|
||||
|
||||
---
|
||||
@@ -3850,3 +3853,4 @@ Notes routinely quote that as a section id. This table resolves it. Grep bait: `
|
||||
| L33510 | §414 | ★★★ — `parallel_gate` ON `main` IS A FALSE PASS, AND THE RULE WAS ALREADY WRITTEN DOWN (P3 |
|
||||
| L33547 | §415 | ★★ — A FILE-SCOPE DECL MAKES gcc-2.7.2 MERGE THE TU'S LATER *BLOCK-SCOPE* EXTERNS INTO IT |
|
||||
| L33572 | §416 | ★★ — FOUR LEVERS FROM THE S71 OVERNIGHT LANE, none of which the cookbook held (P31 S71) |
|
||||
| L33605 | §417 | ★★★ — A REGISTER PIN CAN BLOCK `jump.c`'s SELECT COLLAPSE, AND UNPINNING THEN EXPOSES A `c |
|
||||
|
||||
@@ -33601,3 +33601,30 @@ the width cost two instructions per COMPARE, here it changes the LOAD itself.)
|
||||
`ov_SC06_000` (an unrelated 11-ins function), on `ov_SC01_080` (two different functions), and on
|
||||
`ov_SC03_030`. In every case the real fuel was a **same-TU neighbour** (§194-E). The address-twin
|
||||
lead is worth checking and never worth trusting — law 1c exists for this.
|
||||
|
||||
## §417 ★★★ — A REGISTER PIN CAN BLOCK `jump.c`'s SELECT COLLAPSE, AND UNPINNING THEN EXPOSES A `cse` SKIP-BLOCKS MERGE (P31 S71; byte-proven `ov_SC03_013/func_8017E6F4`, 182 ins)
|
||||
|
||||
**Two compiler passes in a chain, and the first agent misattributed the second to the first.** The
|
||||
prior attempt sat at a 3-instruction residual with a `$3` pin on a select's result and concluded that
|
||||
"unpinning costs +2". Both halves were wrong in an instructive way.
|
||||
|
||||
**1. The pin blocked `jump.c`.** A PINNED select arm expands as `ior` + a copy — TWO insns — so
|
||||
`jump.c:728`'s `x = b; if (c) x = a;` collapse cannot fire, and reorg emits `bne`/`ori`/`move`.
|
||||
Removing the pin lets the collapse happen. **A hard-register pin is not neutral to the RTL
|
||||
optimisers: it changes the INSN COUNT of the pattern they pattern-match on.**
|
||||
|
||||
**2. Unpinning then exposed a different pass.** With every select collapsed, `cse`'s
|
||||
`cse_end_of_basic_block` **skip_blocks** path now walks from the first `D_80184D2C[idx]` read all the
|
||||
way to the second and CSEs the `symbol_ref` into `$s3` — the +2 the earlier agent blamed on the
|
||||
unpinning. The residual moved; it did not grow.
|
||||
|
||||
**The fix is to end the cse block, not to restore the pin:** put a REAL diamond between the two uses
|
||||
(a `vol` clamp written as `if`/`else`, whose IOR arm is uncollapsible), keep `register vol
|
||||
__asm__("$5")` so both arms land in `$a1`, and spell the absolute value as
|
||||
`if ((s16)w < 0) w = -w; e = w;`. Four compiles from there.
|
||||
|
||||
**The general law.** *When removing a lever moves the residual instead of shrinking it, the new
|
||||
residual is a DIFFERENT PASS — attribute it before re-adding the lever.* "Removing X costs +2" is
|
||||
almost always "removing X unblocked pass A, and pass B now fires"; re-adding X re-blocks A and hides
|
||||
B again, which is how a function plateaus for three attempts. The instrument is the pass dump
|
||||
(`-dL`, `-dj`, `-dS`), not another guess.
|
||||
|
||||
Reference in New Issue
Block a user