mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-26 21:36:06 -04:00
docs(cookbook): §349 n_times_set>1 on a base pointer defeats both the invariant hoist and the address giv
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 / 999 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 / 1000 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.
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
- **§339** — A 2-CASE SWITCH OMITS THE LOW-BOUND RANGE TEST, SO THE PRESENCE OF `slti/bnez` BETWEEN THE `beq`s IS A **COUNT TELL** FOR A THIRD CASE NODE (P31 S67; byte-proven ov_SC02_005/func_80190538, 197 ins) <sub>L31181</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>
|
||||
|
||||
### instruction scheduling (73)
|
||||
### instruction scheduling (74)
|
||||
|
||||
- **§3-T2** — Source statement order drives instruction scheduling <sub>L78</sub>
|
||||
- **§3** — When a diff is pure scheduling → decomp-permuter (harness built, Phase 6) <sub>L107</sub>
|
||||
@@ -175,6 +175,7 @@
|
||||
- **§336** — THE §5a CROSS-JUMP BARRIER GOES AT THE *BOTTOM* OF THE TWIN, NOT THE TOP: `find_cross_jump` WALKS BACKWARD FROM THE CONVERGING JUMP (P31 S67; byte-proven ov_SC05_001/func_80183C9C) <sub>L31149</sub>
|
||||
- **§340** — §194-K COROLLARY: **FLIP THE FALSE EDGE YOU CANNOT DELETE.** A "scheduler" residual can be sched.c's ALIAS ORACLE emitting a FALSE true-dependence; source order chooses its DIRECTION (P31 S67; byte-proven ov_SC03_107/func_8017CF48, 10 -> 0 in one compile, zero bytes) <sub>L31209</sub>
|
||||
- **§341** — AN HImode STORE TEMP REWEIGHTS A sched2 TIE-BREAK THAT NO STATEMENT ORDER CAN REACH (P31 S67; byte-proven ov_SC03_006/func_801823B8, last 4 ins) <sub>L31243</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>
|
||||
|
||||
### register allocation & pins (121)
|
||||
|
||||
@@ -300,7 +301,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 (37)
|
||||
### CSE / redundancy / rematerialization (38)
|
||||
|
||||
- **§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>
|
||||
@@ -339,8 +340,9 @@
|
||||
- **§327** — A RANGE TEST MUST BE HImode: WITH `s32` + A `(u16)` CAST GCC *PROVES* THE MASK REDUNDANT AND DROPS THE `andi` (P31 S67; byte-proven ov_SC02_039/func_8017EC34, 154 ins) <sub>L31009</sub>
|
||||
- **§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>
|
||||
|
||||
### loops & induction variables (38)
|
||||
### loops & induction variables (39)
|
||||
|
||||
- **§3-T1** — Loop pointer: top-of-body for `addu` induction, not constant-folded `addiu` <sub>L71</sub>
|
||||
- **§34** — The `func_80138ED0` giant crack: gcc-2.7.2's **3-qty sort bug** + the **zero-byte asm allocation toolkit** + the **giv-init fence** (Phase 24 T5; Opus→close=21, Fable5→MATCH ×134) <sub>L2473</sub>
|
||||
@@ -380,6 +382,7 @@
|
||||
- **§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>
|
||||
- **§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>
|
||||
- **§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)
|
||||
|
||||
@@ -2539,6 +2542,7 @@
|
||||
- **§347-addendum** — A THIRD INSTANCE, AND THE SHARPEST STATEMENT OF THE RULE (md_MAIN_025/func_800CB300, 243 ins) <sub>L31372</sub>
|
||||
- **§343-addendum** — SECOND INSTANCE OF THE WRONG-MAJORITY DECL (same function) <sub>L31390</sub>
|
||||
- **§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>
|
||||
|
||||
|
||||
---
|
||||
@@ -3550,3 +3554,4 @@ Notes routinely quote that as a section id. This table resolves it. Grep bait: `
|
||||
| L31372 | §347-addendum | A THIRD INSTANCE, AND THE SHARPEST STATEMENT OF THE RULE (md_MAIN_025/func_800CB300, 243 i |
|
||||
| L31390 | §343-addendum | SECOND INSTANCE OF THE WRONG-MAJORITY DECL (same function) |
|
||||
| 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 |
|
||||
|
||||
@@ -31410,3 +31410,24 @@ held pointer. Matching that mix is a LENGTH fix, not a register fix — here it
|
||||
not work on constants** — cse will not fold the extra reference onto the existing constant pseudo,
|
||||
so every probe cost +1/+2 instructions. The §344 "add a reference to raise priority" trick applies to
|
||||
a biv or a live variable, NOT to a materialised constant.
|
||||
|
||||
## §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)
|
||||
|
||||
When the target rematerialises a stack-array base inside the loop (`addiu $a0,$sp,0x10` appearing per
|
||||
iteration rather than hoisted once), the lever is to make the base pointer **set more than once**:
|
||||
assign it AGAIN at the END of the inner loop body. `loop.c` treats a pseudo with `n_times_set > 1`
|
||||
as non-invariant, so it declines BOTH the invariant hoist and the address-giv formation, and the
|
||||
address is recomputed exactly where the target recomputes it.
|
||||
|
||||
This is the mirror of §347 lever 3-4 (declaration order and variable count steering allocation) and
|
||||
of §344 (reference count steering priority): the loop optimizers are driven by **counts and
|
||||
positions of assignments**, so an extra assignment is as much a dial as an extra reference.
|
||||
|
||||
Supporting fixes from the same function, all ordinary once the base was right: `(u8)` casts on the
|
||||
`/5` quotient and `%5` remainder (they produce the `andi 0xFF`), a temp `n = tbl[i][0]` so the `lh`
|
||||
precedes `jal rand`, a `row` pointer temp for the inner address, and increment order
|
||||
`off += 10, i++, q++`.
|
||||
|
||||
**Housekeeping caught by the same agent:** `.run/<wave>/SYS.md` does not exist for pool-style waves
|
||||
(only `packs/` and `cards.json`), so a pack that references `SYS.md` sends the agent to a missing
|
||||
file. Harmless but wasteful — the brief should name only what the wave dir actually contains.
|
||||
|
||||
Reference in New Issue
Block a user