2.5 KiB
§275 — THE LEFTOVER-REGISTER READ
A function whose entry block consumes $v0/$v1 before ANY EARLIER INSTRUCTION IN THAT FUNCTION
writes it is reading the CALLER's last return value, not a missing rand()/helper call. (Later
jals are irrelevant and usually present — func_80181F54 has five. What matters is that no write to
the register precedes the read.) (P31 S60;
byte-proven func_80181F54, ov_SC03_104, wave bw, MATCH 57/57.)
MECHANISM. PSX/MIPS o32 does not clear $v0/$v1 across a jal-less function entry, and
gcc-2.7.2 gives every function a fresh allocation for its own values — it never treats an incoming
$v0 as meaningful unless the C body says so. When the ORIGINAL source read that stale value on
purpose — almost certainly a copy-paste from a sibling that genuinely called something with an s32
return, with the call itself dropped in the shipped game code — the only way to reproduce the bytes is
to declare a register-pinned local with no assignment and consume it immediately:
register s32 vin __asm__("$2"); /* never assigned — reads whatever is in $v0 at entry */
...
(s16)(((s16)vin) % 128)
THE TELL. The function's disassembly opens with an operation on $v0/$v1 (sll $v0,$v0,16,
an andi, a comparison, etc.) before any jal, lw, or argument-register use establishes a value for
it — the read has no producer anywhere in the function. Do NOT spend turns hunting for a dropped call
to explain it. Check a structurally-similar sibling first: if it genuinely has a jal at the analogous
slot (its own .s shows the call), that sibling's C cannot be copied verbatim for this target — only
its skeleton transfers, and the target's own .s (no jal anywhere, an entry-block $v0/$v1 read)
is the ground truth that this is the leftover-register class, not a missing-call defect. Cast the value
at each use exactly as the register width implies ((s16) for a halfword-looking use) — no promotion
instructions exist in the target because none were emitted for a value that was never loaded.
BOUNDARY. Distinct from §223's delay-slot corollary (which reads a value the SAME function's
caller-of-a-callee definitely set on purpose, one instruction earlier in the same function) and from
§176-A (a store's C statement dominating an upcoming call). This law applies only at a function's true
entry block, before any control transfer of its own, and only when no assignment to the consumed
register exists anywhere in the target .s.