Files
BFM-decomp/cookbook/C0428.md
T

1.6 KiB

§387 ★★ — SPLIT-FOLD DISPATCH CLOBBER: one switch case needs a reload, another must keep the fold (P31 S69; byte-proven main/func_80030F80, 343 ins, escalation 3 → 0)

When one case needs a cse-forced RELOAD of the switch-index load while another must keep FOLDING that same value into a call argument:

  • Do NOT put the invalidator in the case body — reorg.c's stop_search_p halts fill_slots_from_thread at ANY asm insn (asm_noperands >= 0), so branch-delay-slot hoists into that thread die (+1 length).
  • Do NOT volatile-cast the load — loop.c:339 init_recog_no_volatile fails recog on every DEST_ADDR giv rewrite of a volatile mem, so its address stays biv-based forever.

Instead: name the index in a local; load the dispatch byte into an s32 local (a u8 costs an andi); place a non-volatile __asm__("" ::: "memory") AFTER that lbu and BEFORE the switch — zero bytes, invalidates cse's memory table on every path, and sits outside every case's delay-slot thread. Never between a load and its first use: it eats the final-pass load-delay nop. Fold-cases then pass the named local; reload-cases re-read memory.

Side-effect law, and it is general: adding or removing pseudos flips razor-thin global-alloc ties among pre-loop invariant pointers — which one loses its callee-saved register and gets reload- rematerialised as inline lui/addiu. This is INVISIBLE under match_one's HI16/LO16 mask and surfaces as a bogus "schedule" diff at the remat site. Audit with objdump -r, and flip it back by swapping the two invariants' init order.