2.1 KiB
§297 — ONE GIV SERVING MIXED-WIDTH LOADS AND A STORE NEEDS STRUCT-MEMBER SPELLING; CAST/INDEX SPELLINGS RE-SPLIT THE PSEUDO (P31 S61; wave m0a, func_800347C8, main, byte-proven 31/31)
The target shape (asm/nonmatchings/800/func_800347C8.s): an 0x54-stride record walk with TWO
givs — $a1 = base+0 (lhu 0x0($a1), the state read) and $v1 = base+0x16 with NEGATIVE
displacements: lhu -0x12($v1) (id @0x04), lhu -0x10($v1) (group @0x06), sb $t0,0x0($v1)
(active @0x16). The $v1 giv carries two u16 LOADS and one u8 STORE.
What re-split it, measured: a raw u8 *q walker made gcc spawn a THIRD giv rebased at +6 while
the store kept its own; casting the store back through the pointer re-split it again even after the
loads had unified. Only the MEMBER spelling held all three accesses on the single pseudo — banked
src/800.c:19086:
typedef struct {
u16 state; /* 0x00 */
u16 unk02;
u16 id; /* 0x04 */
u16 group; /* 0x06 */
...
u8 active; /* 0x16 */
u8 pad17[0x3D];
} Sl; /* 0x54 */
e = (Sl *)D_800A46E8;
for (i = 0; i < 8; i++, e++) {
if (e->state == 5 && e->id == (s16)arg0 &&
((arg0 >> 16) == 0 || (arg0 >> 16) == e->group)) {
e->active = 1;
}
}
The law. Mixed-width accesses (loads AND a store) that must ride ONE strength-reduced induction register must be spelled as MEMBER SELECTS off one record pointer. Pointer-arithmetic and cast spellings build distinct address trees per width, and combine_givs re-splits the pseudo.
Two companion dials on the same card, both banked: (1) BOTH latch increments belong in the
for-header (i++, e++ as the comma expression) — written as the last body statement, the pointer
bump emitted on the wrong side of the counter bump (a 2-insn SCHEDULE-REORDER the residual class
files as permuter fuel; the header spelling fixes it for free). (2) The packed parameter's halves:
(s16)arg0 promoted ONCE inline in the condition; arg0 >> 16 used RAW in two basic blocks by
naming the parameter itself — no local, since there is no cross-bb CSE to defeat.