docs(cookbook): §351 /s is a per-access dial; the base-split spelling; a pin-induced sched1 residual with the remaining door named

This commit is contained in:
Drew T
2026-08-31 14:33:55 -06:00
parent bbddb26e68
commit b39db34b56
2 changed files with 39 additions and 4 deletions
+9 -4
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 / 1001 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 / 1002 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.
@@ -302,7 +302,7 @@
- **§344** — RAISE A BIV'S global_alloc PRIORITY WITH A ZERO-BYTE REFERENCE INSTEAD OF PINNING IT; PINNING THE COUNTER KILLS LSR ENTIRELY (P31 S67; byte-proven ov_SC03_121/func_80180E64, 222 ins) <sub>L31296</sub>
- **§347** — LOOP REGISTER ASSIGNMENT IS A **DECLARATION-ORDER + LIVE-RANGE** DIAL: FIVE COMPOSABLE LEVERS, 178 -> 0 (P31 S67; byte-proven ov_SC06_029/func_8017EF34, 243 ins) <sub>L31342</sub>
### CSE / redundancy / rematerialization (38)
### CSE / redundancy / rematerialization (39)
- **§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>
@@ -342,6 +342,7 @@
- **§328** — THE VOLATILE ALIAS MUST BE AN *OBJECT*, NOT A CAST: `*(volatile s32*)&sym` UNFOLDS %lo INTO A SEPARATE `addiu` (P31 S67; byte-proven ov_SC07_007/func_80181B8C, 156 ins, NEW LAW) <sub>L31024</sub>
- **§345** — A VOLATILE **STORE** EVICTS THE MEM FROM cse AND KEEPS `sh`; A VOLATILE **LOAD** BLOCKS combine AND DEGRADES `lh` INTO `lhu+sll+sra` (P31 S67; byte-proven ov_SC01_084/func_80181A7C) <sub>L31312</sub>
- **§349** — RE-ASSIGN A BASE POINTER AT THE END OF THE LOOP BODY TO MAKE `n_times_set > 1` — THAT KILLS BOTH loop.c's INVARIANT HOIST **AND** THE ADDRESS GIV, REPRODUCING A REMATERIALISED `addiu $aN,$sp,K` (P31 S67; byte-proven ov_SC06_029/func_801804C8, 255 ins) <sub>L31414</sub>
- **§351** — `/s` (MEM_IN_STRUCT_P) IS A DIAL YOU CHOOSE PER ACCESS: A COMPONENT_REF GRANTS IT AND LETS cse KEEP AN INDEX ACROSS THE STORE; A PLAIN CAST DENIES IT (P31 S67; ov_SC01_000/func_8017DD04, 186 -> 5) <sub>L31456</sub>
### loops & induction variables (39)
@@ -385,7 +386,7 @@
- **§348** — THE BASE SPELLING PICKS THE ADDRESSING MODE: A SYMBOL GIVES THE 3-INSN `lui/%lo` FORM, A POINTER VARIABLE GIVES THE 2-INSN `addu/lw` FORM (P31 S67; byte-proven ov_SC07_007/func_80182184, 264 -> 30 on this row alone) <sub>L31397</sub>
- **§349** — RE-ASSIGN A BASE POINTER AT THE END OF THE LOOP BODY TO MAKE `n_times_set > 1` — THAT KILLS BOTH loop.c's INVARIANT HOIST **AND** THE ADDRESS GIV, REPRODUCING A REMATERIALISED `addiu $aN,$sp,K` (P31 S67; byte-proven ov_SC06_029/func_801804C8, 255 ins) <sub>L31414</sub>
### structs, block moves & memcpy (80)
### structs, block moves & memcpy (81)
- **§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>
@@ -467,8 +468,9 @@
- **§321** — FILE-SCOPE DUPLICATE ANONYMOUS-STRUCT TYPEDEFS ARE A HARD ERROR; THE SAME TEXT AT BLOCK SCOPE IS A WARNING (P31 S66; byte-proven func_80180728, func_8017F9F8, func_8017E07C) <sub>L30845</sub>
- **§335** — AN `extern u16 A[]` READ AT A VARIABLE SUBSCRIPT ALLOCATES DEAD STACK TEMPS (~8B PER ACCESS) THAT INFLATE THE FRAME WHILE EMITTING ZERO EXTRA INSTRUCTIONS (P31 S67; byte-proven ov_SC06_025/func_8017EA74, closeness 141 -> 20) <sub>L31140</sub>
- **§346** — `c ? X : -X` TAKES expand_expr's COND_EXPR **SINGLETON** PATH (copy, then negate IN PLACE) — AN if/else STATEMENT GIVES THE TWO-ARM FORM (P31 S67; byte-proven ov_SC03_102/func_80180C38, closed the last instruction) <sub>L31322</sub>
- **§351** — `/s` (MEM_IN_STRUCT_P) IS A DIAL YOU CHOOSE PER ACCESS: A COMPONENT_REF GRANTS IT AND LETS cse KEEP AN INDEX ACROSS THE STORE; A PLAIN CAST DENIES IT (P31 S67; ov_SC01_000/func_8017DD04, 186 -> 5) <sub>L31456</sub>
### types, signedness & load/store width (89)
### types, signedness & load/store width (90)
- **§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>
@@ -559,6 +561,7 @@
- **§335** — AN `extern u16 A[]` READ AT A VARIABLE SUBSCRIPT ALLOCATES DEAD STACK TEMPS (~8B PER ACCESS) THAT INFLATE THE FRAME WHILE EMITTING ZERO EXTRA INSTRUCTIONS (P31 S67; byte-proven ov_SC06_025/func_8017EA74, closeness 141 -> 20) <sub>L31140</sub>
- **§342** — A `void *` PARAMETER CAST TO ITS REAL TYPE IN A LOCAL IS **NOT** BYTE-NEUTRAL WHEN A LATER PARAMETER ALSO NEEDS A CALLEE-SAVED REGISTER (P31 S67; byte-proven ov_SC07_002/func_80181394, NEW LAW) <sub>L31257</sub>
- **§345** — A VOLATILE **STORE** EVICTS THE MEM FROM cse AND KEEPS `sh`; A VOLATILE **LOAD** BLOCKS combine AND DEGRADES `lh` INTO `lhu+sll+sra` (P31 S67; byte-proven ov_SC01_084/func_80181A7C) <sub>L31312</sub>
- **§351** — `/s` (MEM_IN_STRUCT_P) IS A DIAL YOU CHOOSE PER ACCESS: A COMPONENT_REF GRANTS IT AND LETS cse KEEP AN INDEX ACROSS THE STORE; A PLAIN CAST DENIES IT (P31 S67; ov_SC01_000/func_8017DD04, 186 -> 5) <sub>L31456</sub>
### declarations, prototypes & K&R (107)
@@ -2545,6 +2548,7 @@
- **§348** — THE BASE SPELLING PICKS THE ADDRESSING MODE: A SYMBOL GIVES THE 3-INSN `lui/%lo` FORM, A POINTER VARIABLE GIVES THE 2-INSN `addu/lw` FORM (P31 S67; byte-proven ov_SC07_007/func_80182184, 264 -> 30 on this row alone) <sub>L31397</sub>
- **§349** — RE-ASSIGN A BASE POINTER AT THE END OF THE LOOP BODY TO MAKE `n_times_set > 1` — THAT KILLS BOTH loop.c's INVARIANT HOIST **AND** THE ADDRESS GIV, REPRODUCING A REMATERIALISED `addiu $aN,$sp,K` (P31 S67; byte-proven ov_SC06_029/func_801804C8, 255 ins) <sub>L31414</sub>
- **§350** — A ZERO-BYTE RE-TIE SETS `reg_n_sets=2`, WHICH KILLS sched1's `birthing_insn_p` LAUNCH_PRIORITY BOOST — THE MECHANISM BEHIND "MY ADDS ARE GLUED TO THEIR STORES" (P31 S67; byte-proven ov_SC04_011/func_80180B24, 215 ins) <sub>L31435</sub>
- **§351** — `/s` (MEM_IN_STRUCT_P) IS A DIAL YOU CHOOSE PER ACCESS: A COMPONENT_REF GRANTS IT AND LETS cse KEEP AN INDEX ACROSS THE STORE; A PLAIN CAST DENIES IT (P31 S67; ov_SC01_000/func_8017DD04, 186 -> 5) <sub>L31456</sub>
---
@@ -3558,3 +3562,4 @@ Notes routinely quote that as a section id. This table resolves it. Grep bait: `
| L31397 | §348 | THE BASE SPELLING PICKS THE ADDRESSING MODE: A SYMBOL GIVES THE 3-INSN `lui/%lo` FORM, A P |
| L31414 | §349 | RE-ASSIGN A BASE POINTER AT THE END OF THE LOOP BODY TO MAKE `n_times_set > 1` — THAT KILL |
| L31435 | §350 | A ZERO-BYTE RE-TIE SETS `reg_n_sets=2`, WHICH KILLS sched1's `birthing_insn_p` LAUNCH_PRIO |
| L31456 | §351 | `/s` (MEM_IN_STRUCT_P) IS A DIAL YOU CHOOSE PER ACCESS: A COMPONENT_REF GRANTS IT AND LETS |
+30
View File
@@ -31452,3 +31452,33 @@ not a `break` (it jumps to the epilogue); the else arm must store `0xE0` in BOTH
merges the `sh` at the join, using an `s16` local, which yields the un-coalesced `addu $v1,$v0,$zero`
and stops `reorg` stealing the decrement; struct-member spelling (`MEM_IN_STRUCT_P`) defeats a false
alias against an unrelated store; `s16 buf[3]` gives the 0x38 frame (§333).
## §351 — `/s` (MEM_IN_STRUCT_P) IS A DIAL YOU CHOOSE PER ACCESS: A COMPONENT_REF GRANTS IT AND LETS cse KEEP AN INDEX ACROSS THE STORE; A PLAIN CAST DENIES IT (P31 S67; ov_SC01_000/func_8017DD04, 186 -> 5)
Two accesses to the same packet, opposite requirements — and the spelling is what decides:
* the **tag word** must be a plain cast, `*(u32*)pkt = (*(u32*)pkt & 0xFF000000) | ot->addr;`.
Spelling it as a `P_TAG` COMPONENT_REF grants `/s`, which lets cse keep an unrelated index live
across the store — **−13 instructions**.
* the **OT side** must STAY a `P_TAG` bitfield, or the trailing `D_800A5E60 = pkt` stops floating.
So `/s` is not a property of the object, it is a property of **how you wrote that one access**. Cf.
§340, where `/s` was the escape that was *unreachable*; here it is the thing to avoid on one line and
keep on the next.
**Two more spellings from the same function:**
* `(u16)D_800B9A02` — NOT `*(u16*)&D_800B9A02`. The ADDR_EXPR form makes cse hold the base in a
register: 8 × `lui/lhu` collapses to 1 × `lui/addiu` + 4 × `lhu`.
* **The base-split (byte-proven, previously undocumented):** spell the FIRST read off the raw symbol
(`(s32)D_800AA60C + idx` → `lui $at / addu / lw %lo`), then assign `ob = D_800AA60C;` and route
every later reference through `ob` (→ `addu $x,$idx,$t2 / lw 0($x)`). Exactly the 3-instruction
shape §348 describes, applied deliberately per-reference.
**OPEN, with the door named (do not re-grind blindly):** the last 5 are a prologue `li`-block
SCHEDULE-REORDER *caused by the pins that fixed the registers*. Pinning `0x80` makes `(set (reg 7)
128)` a HARD-REG set, which `sched1` ranks by successor count (12 `sb` uses) and hoists to idx 10;
the target has all pseudos at sched1 and emits them in source/first-use order (`li $a3,0x80` at idx
14). Refuted: pinning the other 4 prologue constants (still 5), hand-writing the mask via a pinned
var (+3), a non-bitfield COMPONENT_REF (+3 and an `$s0` spill), an m24/mFF pin pair (+3).
**The only remaining door is the allocno-priority route** — make `0xFFFFFF` outrank `0x80` with NO
pin (lreg: refs 9/len 173 vs refs 13/len 320), which needs a zero-byte ref/live-length edit. That is
a §344-shaped problem and the next attempt should start there, not at the pins.