1.4 KiB
§386 ★★★ — A BYTE LOAD ON THE BIV BASE WAS BORN IN THE COMBINE PASS: SPELL IT AS A SHIFT-MASK, NEVER A DEREF (P31 S69; byte-proven main/func_80020598, 292 ins, escalation 1 → 0)
Diff tell: a one-row reg+imm residual where yours reads offset-from-giv and the target reads
offset-from-biv at the same address — and the twin expression in another arm IS giv-based in the
target.
Why no deref spelling works. A byte deref is a DEST_ADDR giv, and loop.c's combine_givs
folds it onto the reduced register unconditionally: identity or express_from, then
memory_address_p and equal MIPS ADDRESS_COST. There is no per-instance guard, so no temp, no
ordering and no spelling can split two identical (mult, add) givs.
The lever — defer the load's birth past loop.c:
b1 = (w >> 8) & 0xFF; /* NOT ((u8 *)p)[1] */
Post-loop combine then narrows zero_extend(lshiftrt(mem)) into an lbu at base+k off the
ORIGINAL register.
Two corollaries. (1) Temp-pointer escapes are impossible: cse1's find_best_addr tie-break at
equal ADDRESS_COST prefers the MORE complex address (it frees a register) and folds any same-ebb
temp back into (plus base k) before loop — and anything opaque enough to survive cse1 is equally
opaque to cse2/combine. (2) The narrowed spelling changes spill demand, so expect the §333/§334
dead-aggregate pad to need retuning (here pad[6] → pad[4] at frame 0x88).