Files
BFM-decomp/cookbook/C0409.md
T

2.0 KiB

§368 ★★★ — THE RELOAD-REMAT CONSTANT: REACH A REGISTER NO PIN CAN REACH (P31 S68; ov_SC03_105/func_80187A30, 339 ins, fable escalation closed 8 → 0 in ONE edit)

THE TELL, and check it FIRST on any REGALLOC-LOCAL residual: the wrong-register rows ALSO have swapped operands on a commutative op — target mult $a3,$v0, yours mult $v0,$a3. A wrong-register row with swapped commutative operands is THIS idiom. It is not a pin case and not a scheduling tie. Reading the operand order IS the diagnostic.

Why a pin cannot work, and why an in-block constant cannot either. cse.c fold_rtx (~5284) forcibly swaps any operand with a known constant equivalent into position 2 — the reg-reg swap always validates — so an in-block r = K can NEVER be operand 1. And register __asm__ pins fight local-alloc instead of rerouting around it (measured WORSE here: 18 and 14, vs 8 unpinned).

The lever — a FUNCTION-SCOPE single-set local, assigned once at entry, used constant-first:

s32 cK;              /* function scope */
cK = K;              /* ONCE, at entry — a different basic block from the uses */
...
prod = cK * x;       /* constant FIRST */

Preconditions: cK's live range must cross calls and every callee-saved register must already be occupied, so global-alloc leaves it UNCOLORED. Reload then records reg_equiv_constant, DELETES the init insn (instruction-count-neutral — the extra statement is free) and REMATERIALIZES addiu <reload-reg>,$zero,K immediately before each use, picking the register by reload1.c order_regs_for_reload preference rather than local-alloc's ascending scan. That is how it reaches $a3 while $v1 is free.

Bonus, and why ONE edit fixed two things: the cross-block def blinds cse to the value, so the source operand order SURVIVES into the emitted instruction. Register and operand order are fixed together; adjacent product/mfhi pseudos follow into the same register.