From b39db34b56258dde87779d7bd4e84d63683a5822 Mon Sep 17 00:00:00 2001 From: Drew T <50529377+Druthulu@users.noreply.github.com> Date: Mon, 31 Aug 2026 14:33:55 -0600 Subject: [PATCH] =?UTF-8?q?docs(cookbook):=20=C2=A7351=20/s=20is=20a=20per?= =?UTF-8?q?-access=20dial;=20the=20base-split=20spelling;=20a=20pin-induce?= =?UTF-8?q?d=20sched1=20residual=20with=20the=20remaining=20door=20named?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/cookbook-index.md | 13 +++++++++---- docs/matching-cookbook.md | 30 ++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/docs/cookbook-index.md b/docs/cookbook-index.md index e4ab7ff90..9b6b91a0c 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 / 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) L31296 - **§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) L31342 -### 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) L3347 - **§83d** — CSE's quantity budget is WHOLE-FUNCTION, so a local rewrite cannot fix a local symptom L6486 @@ -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) L31024 - **§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) L31312 - **§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) L31414 +- **§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) L31456 ### 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) L31397 - **§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) L31414 -### structs, block moves & memcpy (80) +### structs, block moves & memcpy (81) - **§3-T2** — Source statement order drives instruction scheduling L78 - **§5** — Known hard-residual classes (instruction-identical, one byte-exact blocker) L199 @@ -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) L30845 - **§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) L31140 - **§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) L31322 +- **§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) L31456 -### types, signedness & load/store width (89) +### types, signedness & load/store width (90) - **§3-I1** — Unsigned range check: `(x - lo) < (hi-lo)` → `addiu`+`sltiu` L41 - **§3-I2** — Byte mask forces `andi` even after `lbu` L47 @@ -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) L31140 - **§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) L31257 - **§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) L31312 +- **§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) L31456 ### 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) L31397 - **§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) L31414 - **§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) L31435 +- **§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) L31456 --- @@ -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 | diff --git a/docs/matching-cookbook.md b/docs/matching-cookbook.md index 991181d75..bd6716339 100644 --- a/docs/matching-cookbook.md +++ b/docs/matching-cookbook.md @@ -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.