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.