Files
BFM-decomp/cookbook/C0264.md
T

1.9 KiB

§243 — SPELL THE CSE BARRIER AS A REASSIGNED POINTER; AND THE nop-AFTER-EVERY-lh SIGNATURE (P31 S58b)

THE SIGNATURE. When a target reloads the same narrow global before every store, with an unfilled load-delay nop after each load, the source did not read the symbol directly and did not mark it volatile. It read through a pointer variable that is reassigned before each use — the address pseudo is redefined between loads, so cse cannot common them, and no opcode changes.

func_80189B74 (ov_SC06_029, 16/16). Measured alternatives on the same card:

spelling result
*(s16*)&D_x direct cast cse folds four reads to two loads + hoisted stores — 10 ins vs 16
volatile on the extern 24 ins, lhu+sll/sra — wrong width, wrong count
reassigned block-scope pointer MATCH 16/16

§176-B1 documents the narrow-symbol alias problem for schedule permutations; this is its inverse — a deliberate CSE barrier that costs zero bytes.

THE COMPLEMENTARY LEVER (same mechanism, opposite direction). When the target reloads a pointer-typed field after an intervening store, the alias is already conservative and plain sequential C reproduces it for free — do not hoist (func_8017EC00, ov_SC07_000, which also explains why: gcc cannot prove *(arg0+0x20)+0x2C does not alias arg0+0x20). And when the target reloads a global's address three times, the fix is the opposite again: take the address once into a local (func_801A7B08, md_SC07_004; func_8017DB44, ov_SC01_084 — where sw $s0/lw $s0 with no other $s0 use is the demand for a pointer value live across a call).

READING RULE. Count the reloads in the target and compare with the number of textual reads your draft has. Equal ⇒ leave it alone. Target has more ⇒ you need a barrier (reassigned pointer, or §244). Target has fewer ⇒ you need a name (§208/§253-adjacent).