Files
BFM-decomp/cookbook/C0265.md
T

2.4 KiB

§244 — volatile IS A COUNTING INSTRUMENT, A STORE-ORDER PIN, AND MUST SOMETIMES BE ASYMMETRIC (P31 S58b)

Four distinct uses, three of them not previously named.

1. THE RELOAD COUNT IS THE DECOMPILER'S OCCURRENCE COUNT. func_801814AC (ov_SC05_010, 113 ins): the target reloads lui/lw of D_801C7E30 before all six accesses; a non-volatile extern CSE'd them away (108 ins). The Ghidra seed's six DAT_801c7e30 occurrences were the reload count, not a cached local. A pointer global read once into a local but reloaded per-access implies volatile, not a struct-pointer parameter.

2. ASYMMETRIC volatile — ON ONE OF TWO STORES TO THE SAME ADDRESS. func_801815A0 (ov_SC03_001): the target has a dead sh $zero,0x2C followed by sh $v1(0xC006),0x2C. volatile on both or on neither collapses the pair (the scheduler keeps only the last store); volatile on the zero store only reproduces both. Same shape on func_801816A8 (ov_SC05_005) and func_80185B78's self-assign (§249).

3. volatile ON A POINTER AS A STORE-ORDER PIN. func_801ADD98 (md_SC07_004, 33/33): declaring both byte pointers volatile forces the literal sb→lbu read-back of each just-stored byte instead of forwarding from a cached register; with only one volatile gcc hoisted the other's stores (+2 ins). volatile-on-pointer is a store-order pin for interleaved write/read-back loops, not only a reload lever.

4. volatile AS A DECLARATION-ONLY FRAME PAD. func_801828E0 (ov_SC05_008): volatile s32 pad; reserves the stack slot without emitting code at -O2, where a plain unused local is DCE'd. Two of them give the 8-byte vars area (func_801814AC). See §258/§226 addendum for the full pad catalogue.

BOUNDARY — WHEN volatile IS THE WRONG TOOL. It punches load widths: on func_801AC9CC a volatile-punned local turned lh into lhu and broke the other half of a deliberate lh/lhu pair (the fix there was a zero-byte __asm__ __volatile__("" ::: "memory") barrier between the two reads — the cookbook documents that barrier as a cross-jump bailer and scheduling anchor, not as a cse load-merge blocker, which is what it is here). On func_8018F230 the same barrier broke a value-CSE between two reads of one global where volatile produced an "ugly lui/addiu address-object form". Reach for the barrier before the qualifier when the goal is duplicate loads of one width.