1.9 KiB
§332 — THE maspsx la-IN-A-DELAY-SLOT GAP: gcc EMITS A SYMBOLIC ADDRESS LOAD AS ONE ATOMIC length-2 INSN, SO IT CAN NEVER FILL A JUMP DELAY SLOT — 6 FUNCTIONS FLEET-WIDE, NONE BANKABLE FROM C (P31 S67; byte-traced main/func_80062144, func_8005DBD8)
THE MECHANISM, traced to the compiler sources, not inferred. The target fills jal func_X's
delay slot with addiu $a1,$a1,%lo(D_80078CE0) — the second half of a la. gcc-2.7.2's
movsi_internal emits a symbolic address load as ONE atomic RTL insn of length 2 (the lui+addiu
macro pair; NO HIGH/LO_SUM define_split exists in this MIPS backend — verified in
tools/reference/gcc-2.7.2/mips.md, mips.c, reorg.c, and mips.md's own comment says so).
eligible_for_delay requires length == 1, so the define_delay gate categorically REFUSES it as a
filler. cc1 therefore leaves it under .set reorder/macro, and maspsx
(tools/maspsx/__init__.py:1054-1057) unconditionally emits a real nop after every branch/jump in
reorder mode. The retail split is genuine ASPSX macro-hopping — the assembler moving the la's
low half into the jal slot — which maspsx does not replicate.
BYTE-VERIFIED: running maspsx.py --aspsx-version=2.56 --expand-div over cc1's raw -dS output
reproduces the extra nop exactly. The C is not the problem.
THE COST OF NOT KNOWING THIS: func_80062144 reads closeness 29 and every one of those 29
mismatches is a single length-drift consequence of this one nop. A closeness-29 verdict looks like
a badly wrong draft and invites a redraft; it is a one-instruction toolchain gap.
POPULATION: 6 occurrences fleet-wide, NONE banked — asm/nonmatchings/800c2/func_80061FA8.s ×2,
libgte3 PopMatrix/PushMatrix, md_MAIN_034/func_800CB00C, main/func_80062144. They are
source-unreachable pending a NARROW maspsx patch (hop a length-2 macro's low half into an otherwise
empty delay slot). Until that patch exists: ledger them, never draft them.