docs(cookbook): §374/§375 — two more bounds on the register-pin lever

§374 a register __asm__($30) reservation is NOT honoured by move_movables under
pressure, and the corruption is SILENT (the build succeeds) -- audit the raw
objdump register uses before trusting a pinned build that compiles.
§375 an $a0-$a3 pin used LATE relocates an EARLIER outgoing-call use of that same
register ~26 slots early, identically across three structural variants; argument
pins are not local the way $s pins are.

With §368 and §373 these now form a usable four-way rule for when a pin helps,
when it fights the allocator, when it is ignored entirely, and when it acts at a
distance.
This commit is contained in:
Drew T
2026-08-31 22:26:02 -06:00
parent 5c317dbe5f
commit a22d5646f6
2 changed files with 37 additions and 2 deletions
+8 -2
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 / 1028 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 / 1030 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.
@@ -185,7 +185,7 @@
- **§3-2.** — A REGISTER PIN THAT DELETES A sched2 ANTI-DEPENDENCE <sub>L31997</sub>
- **§3-3.** — HARD FACT FOR THE SCHEDULING MAP — an `asm` ALWAYS has priority 1 <sub>L32010</sub>
### register allocation & pins (125)
### register allocation & pins (127)
- **§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>
@@ -312,6 +312,8 @@
- **§368** — ★★★ — THE **RELOAD-REMAT CONSTANT**: REACH A REGISTER NO PIN CAN REACH (P31 S68; ov_SC03_105/func_80187A30, 339 ins, fable escalation closed 8 → 0 in ONE edit) <sub>L31792</sub>
- **§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-2.** — A REGISTER PIN THAT DELETES A sched2 ANTI-DEPENDENCE <sub>L31997</sub>
- **§374** — A `register … __asm__("$30")` RESERVATION IS **NOT HONOURED** BY `move_movables` (P31 S68; main/func_80015608, 86 ins) <sub>L32026</sub>
- **§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>
### CSE / redundancy / rematerialization (43)
@@ -2621,6 +2623,8 @@
- **§3-2.** — A REGISTER PIN THAT DELETES A sched2 ANTI-DEPENDENCE <sub>L31997</sub>
- **§3-3.** — HARD FACT FOR THE SCHEDULING MAP — an `asm` ALWAYS has priority 1 <sub>L32010</sub>
- **CROSS-REFERENCE** — TO §370 — checked and found INAPPLICABLE here, which is the point <sub>L32018</sub>
- **§374** — A `register … __asm__("$30")` RESERVATION IS **NOT HONOURED** BY `move_movables` (P31 S68; main/func_80015608, 86 ins) <sub>L32026</sub>
- **§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>
---
@@ -3661,3 +3665,5 @@ Notes routinely quote that as a section id. This table resolves it. Grep bait: `
| L31997 | §3-2. | A REGISTER PIN THAT DELETES A sched2 ANTI-DEPENDENCE |
| L32010 | §3-3. | HARD FACT FOR THE SCHEDULING MAP — an `asm` ALWAYS has priority 1 |
| L32018 | CROSS-REFERENCE | TO §370 — checked and found INAPPLICABLE here, which is the point |
| L32026 | §374 | A `register … __asm__("$30")` RESERVATION IS **NOT HONOURED** BY `move_movables` (P31 S68; |
| L32042 | §375 | AN `$a0`-`$a3` PIN USED LATE RELOCATES AN **EARLIER** OUTGOING-CALL USE OF THAT REGISTER ( |
+29
View File
@@ -32022,3 +32022,32 @@ cause was the birthing-boost → local-alloc → anti-dep chain above — and th
§370's claim is unchanged and still narrow: it applies to *simultaneously-ready same-priority leaves*.
The habit is the transferable part: check whether a recorded bound actually covers your case before
concluding the residual is unreachable.
## §374 — A `register … __asm__("$30")` RESERVATION IS **NOT HONOURED** BY `move_movables` (P31 S68; main/func_80015608, 86 ins)
Under ~9-way register pressure at -O2, gcc-2.7.2 **free-registers something into `$30`/`$fp` AFTER
the pin's own assignment**, silently corrupting whichever variable loses that fight (observed as
y / flags / size / tag / mode2 across configurations). Reproducible. `move_movables`' invariant-hoist
register SELECTION does not consult local register-asm reservations.
**THE CORRUPTION IS SILENT** — the build succeeds. Audit the raw objdump register uses by hand before
trusting a pinned build that "compiles fine".
**This is the fourth bound on the pin lever, and together they are a usable rule:**
* **§368** — a pin FIGHTS local-alloc when you want a different ALLOCATION (measured worse than none)
* **§373** — a pin WORKS to break a false sched2 ANTI-DEPENDENCE
* **§374** — a pin on `$30`/`$fp` is NOT HONOURED at all against `move_movables` under pressure
* **§375** — a pin on `$a0`-`$a3` has NON-LOCAL effects, backwards, through earlier calls
## §375 — AN `$a0`-`$a3` PIN USED LATE RELOCATES AN **EARLIER** OUTGOING-CALL USE OF THAT REGISTER (P31 S68; main/func_8005F0C8, 88 ins)
The loop body is oracle-proven byte-exact once the base is fenced and `p` is pinned to `$4`. But that
pin **hoists the earlier `func_8005C2C8` argument copy ~26 slots early**, identically across three
structural variants — block scope and declaration order make no difference.
**The rule:** a hard-register pin on an ARGUMENT register used LATE relocates any EARLIER outgoing-call
use of that same register to the earliest legal point. Argument-register pins are not local the way
`$s`-register pins are; they reach backwards past unrelated calls.
(The other residual on that function is §188's epilogue and is toolchain-unfixable — it is now in the
walls ledger. See the playbook §1b: check `wall_sweep` BEFORE escalating anything.)