2.6 KiB
§216 — DISTINCT ADJACENT SCALARS vs ONE ARRAY: one lui per access is the tell, and the array decl is UNUSABLE (P31 S58)
THE TELL. Adjacent byte symbols, each accessed through its own %hi materialisation:
lui $v0, %hi(D_801EFEA0) ; lbu $v0, %lo(D_801EFEA0)($v0)
lui $v1, %hi(D_801EFEA1) ; lbu $v1, %lo(D_801EFEA1)($v1)
...
lui $at, %hi(D_801EFEA0) ; sb $v0, %lo(D_801EFEA0)($at)
lui $at, %hi(D_801EFEA1) ; sb $v1, %lo(D_801EFEA1)($at)
(✔ byte-checked: asm/ov_SC04_011/.../func_8018BB44.s — six independent luis in the first block,
plus addiu $a0, %lo(D_801EFEA3) at function entry as a real base pointer.)
THE LAW. An array declaration forces gcc to materialise ONE base register and address every
member off it. When the target shows an independent lui+%lo per byte, each byte must be a
DISTINCT SCALAR OBJECT in the C — a block-scoped extern u8 per address, with an explicit __asm__
name where the symbol is not spellable.
func_8018BB44 (ov_SC04_011, 49 ins) is the measured case: the card's authoritative
extern u8 D_801EFEA0[] was unusable — accessing through the array, or &arr[3], or arr[i], all
collapsed to one base pointer and 43 ins against the target's 49. Four separate scalar decls,
plus a separate u8 *flag = &scalar3; to force the $a0 = &D_801EFEA3 materialisation at entry,
matched. func_8018BA9C (ov_SC04_011, 10 ins) is the minimal version: three sbs to D_801EFEA0,
D_801EFEA1, D_801EFEA2, each with its own lui $at — folding them into D_801EFEA0[1]/[2]
would drop two lui+relocation pairs. It also reuses the TU's existing extern u8 D_801EFEA0[]
verbatim and indexes [0], which is codegen-identical to a scalar.
BOUNDARY — and it is a real one. The opposite direction is §183's array spelling, and it is just
as often correct: func_80186C70 (ov_SC02_005) needs extern T *D[] because the target
materialises the table base once via lui/addiu with no intervening load, and func_8017F3B0
(ov_SC02_017) needs D_8018E19A as an array so that the <<2 index rides its own single lui $at. Count the luis: N accesses / N luis ⇒ N scalars; N accesses / 1 base ⇒ one array.
AND THE THIRD CASE — the TU already spells it as a scalar. Several ov_SC04_011 cards
(func_80186908, func_80184FC4, func_80181868, func_80185C04) hit D_801EFC4C declared
file-scope as a scalar extern s32 while the target indexes it. The house escape is
((s32 *)&D_801EFC4C)[i] (the TU's own banked func_80185FAC uses exactly that), or the
__asm__-label alias per §200 — never retype the file-scope declaration.