mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-10-05 00:47:54 -04:00
docs(cookbook): §352 CRITICAL — two identical zero-byte barriers merge with EACH OTHER, defeating their purpose
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 / 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.
|
||||
> `docs/matching-cookbook.md` is ~716 KB / 1003 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 (75)
|
||||
### instruction scheduling (76)
|
||||
|
||||
- **§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>
|
||||
@@ -177,6 +177,7 @@
|
||||
- **§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>
|
||||
- **§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>
|
||||
- **§352** — ⚠ TWO IDENTICAL `__asm__ __volatile__("")` BARRIERS **MERGE WITH EACH OTHER** — SPELL THE SECOND ONE DIFFERENTLY (P31 S67; byte-proven resident/func_800D128C, measured closeness 105 when they merged) <sub>L31486</sub>
|
||||
|
||||
### register allocation & pins (121)
|
||||
|
||||
@@ -302,7 +303,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 (39)
|
||||
### CSE / redundancy / rematerialization (40)
|
||||
|
||||
- **§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>
|
||||
@@ -343,6 +344,7 @@
|
||||
- **§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>
|
||||
- **§352** — ⚠ TWO IDENTICAL `__asm__ __volatile__("")` BARRIERS **MERGE WITH EACH OTHER** — SPELL THE SECOND ONE DIFFERENTLY (P31 S67; byte-proven resident/func_800D128C, measured closeness 105 when they merged) <sub>L31486</sub>
|
||||
|
||||
### loops & induction variables (39)
|
||||
|
||||
@@ -1119,7 +1121,7 @@
|
||||
- **§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>
|
||||
- **§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>
|
||||
|
||||
### process, measurement & doctrine (121)
|
||||
### process, measurement & doctrine (122)
|
||||
|
||||
- **§8e** — The jtbl ALIGNMENT LAW + the pad-spec filter — multi-table .rodata spans (Phase 29, byte-proven; `.run/probe_jtbl/verdict.md`) <sub>L549</sub>
|
||||
- **§3-The** — mechanism: game-code dedup is SOURCE-LEVEL, not an object swap (R-D1, the key lesson) <sub>L945</sub>
|
||||
@@ -1242,6 +1244,7 @@
|
||||
- **§337** — THE CC1-ONLY BLOCKER CLASS: `blocker_probe`'s STATIC ORACLE REPORTS "none" AND THE WHOLE-TU COMPILE STILL FAILS (P31 S67; ov_SC04_011/func_801827DC, md_MAIN_027/func_800CB4A4) <sub>L31157</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>
|
||||
- **§343** — `decl_prior`'s FLEET MAJORITY CAN BE WRONG ABOUT THE TRUE SIGNATURE — READ THE RIVALS, NOT JUST THE WINNER (P31 S67; measured on func_8012BD14 / func_8012D624 / func_80143C74) <sub>L31274</sub>
|
||||
- **§352** — ⚠ TWO IDENTICAL `__asm__ __volatile__("")` BARRIERS **MERGE WITH EACH OTHER** — SPELL THE SECOND ONE DIFFERENTLY (P31 S67; byte-proven resident/func_800D128C, measured closeness 105 when they merged) <sub>L31486</sub>
|
||||
|
||||
### (unbucketed — title matched no symptom vocabulary) (298)
|
||||
|
||||
@@ -2549,6 +2552,7 @@
|
||||
- **§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>
|
||||
- **§352** — ⚠ TWO IDENTICAL `__asm__ __volatile__("")` BARRIERS **MERGE WITH EACH OTHER** — SPELL THE SECOND ONE DIFFERENTLY (P31 S67; byte-proven resident/func_800D128C, measured closeness 105 when they merged) <sub>L31486</sub>
|
||||
|
||||
|
||||
---
|
||||
@@ -3563,3 +3567,4 @@ Notes routinely quote that as a section id. This table resolves it. Grep bait: `
|
||||
| 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 |
|
||||
| L31486 | §352 | ⚠ TWO IDENTICAL `__asm__ __volatile__("")` BARRIERS **MERGE WITH EACH OTHER** — SPELL THE |
|
||||
|
||||
@@ -31482,3 +31482,36 @@ var (+3), a non-bitfield COMPONENT_REF (+3 and an `$s0` spill), an m24/mFF pin p
|
||||
**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.
|
||||
|
||||
## §352 — ⚠ TWO IDENTICAL `__asm__ __volatile__("")` BARRIERS **MERGE WITH EACH OTHER** — SPELL THE SECOND ONE DIFFERENTLY (P31 S67; byte-proven resident/func_800D128C, measured closeness 105 when they merged)
|
||||
|
||||
**This corrects how §5a/§336 barriers must be USED.** Identical `ASM_INPUT` rtx are
|
||||
`rtx_renumbered_equal_p`, so `find_cross_jump` matches the two barriers *to each other* and folds
|
||||
the very arms they were placed to separate. Measured: closeness **105** with two identical `("")`
|
||||
barriers. Spell the second one differently — `__asm__ __volatile__("" ::: "memory")` — and they stop
|
||||
matching.
|
||||
|
||||
**Two more placement laws from the same function:**
|
||||
|
||||
* **THE BARRIER GOES BETWEEN THE CALL AND THE `val =`, NOT AFTER IT.** Placed after, `reorg`
|
||||
(`stop_search_p`) can no longer steal `li $s0,K` into the `j`'s delay slot and you get a `nop`.
|
||||
* **A ONE-INSTRUCTION TAIL CAN MERGE.** `jump.c:2402`'s CODE_LABEL clause (`--minimum; break`) drops
|
||||
the usual 2-insn floor to 1, so even a bare `case N: val = K; break;` arm merges — it needs a
|
||||
barrier too. Do not assume a single-instruction arm is safe.
|
||||
|
||||
**NEW LEVER — resurrect a switch-index copy** (`addu $v1,$s1,$zero` before the `sltiu`):
|
||||
`switch (ret)` never emits one (`expand_end_case` folds `ret - 0`), and a plain `sel = ret;` is
|
||||
canonicalised away by CSE. This works:
|
||||
|
||||
sel = ret;
|
||||
__asm__ __volatile__("" : "=r"(sel) : "0"(sel)); /* re-DEFINE so CSE cannot fold it back */
|
||||
switch (sel)
|
||||
|
||||
local-alloc then gives the single-block `sel` a caller-saved register while global-alloc must give
|
||||
`ret` a call-saved one — reproducing the target's copy. (A fourth pass steered by the re-tie; cf.
|
||||
§350's list.)
|
||||
|
||||
Two ordinary fixes worth the pattern: a clamp must be `if/else` plus a SEPARATE `val = val * 25;`
|
||||
because a `?:` const-folds `0x14*25` to 500; and the definition needed `(s32 arg0, s32 arg1)` with
|
||||
`(u8)`/`(s16)` casts INSIDE, because `resident.c:1693` already declares the prototype at file scope
|
||||
(gcc-2.7.2 rejects `+` asm constraints).
|
||||
|
||||
Reference in New Issue
Block a user