3.1 KiB
§396 ★★★ — SIX LEVERS FROM THE S69 SINGLETON ROUND, INCLUDING ONE THAT ONLY A COND_EXPR REACHES (P31 S69)
(a) A cross-jump tail-merge whose identical suffix is only slti; beqz CANNOT be fenced.
__asm__("") does not stop it (CORRECTED by C0510: a barrier placed between the beqz and the j does; byte-proven
ov_SC03_110/func_801800F0), and neither if/else nor &&/|| avoids it — all three merge and
lose 5 instructions. The abs guard must be a ternary inside the if condition:
if (d >= 0 ? d < 0x80 : -d < 0x80) /* emits bltz/slti/beqz/j-join/negu/slti/beqz, unmerged */
This is the SECOND independent discovery of the do_jump COND_EXPR mechanism in one session
(§314b found it on a different function, in a different binary, from a different warm start). Treat
it as a first-class lever, not a curiosity: when a guard's two arms share a two-instruction tail,
the ternary is the only construct that survives cross-jump.
(byte-proven ov_SC02_005/func_8018074C; §314b ov_SC04_002/func_8018691C)
(b) Let gcc cross-jump the arms — do not hand-merge them. A warm start with a hand-written
shared tail (goto done) plateaued with 12 "register mismatches". Rewriting it as four arms, each
writing all four variables, let gcc perform the merge itself and the register diffs vanished with
it — they were a CONSEQUENCE of the join, not separate defects. §162's surviving-copy rule (the LAST
copy survives) tells you which arm's spelling to match.
(byte-proven ov_SC06_018/func_8017D4DC)
(c) Commute a comparison to change which pseudo is born first. A $a1/$a2 REGALLOC-PERM closed
in two compiles by writing the test as *(s16 *)(a0+0xDE) > -lim instead of the natural order: it
births the 0xDE pseudo BEFORE lim's last use, so the two conflict and lim is pushed off $a1.
Same slt emitted; different allocation. Operand order is a lifetime dial.
(byte-proven ov_SC03_112/func_8017EE48)
(d) UNPINNING can be the lever. §76 allocno-class: splitting val = (8-q) << 8 into two sets on
ONE variable (making it a global allocno → $a1) plus removing the pins on t and q — so the
divide's hi>>8 temp stops stealing $a3 — took 5 → 0. Reach for unpinning when a pinned register
is being stolen by an unrelated temp.
(byte-proven ov_SC03_102/func_8018139C)
(e) When a file-scope typedef collides, move BOTH the type and the extern to BLOCK scope.
Renaming the type at file scope only trades conflicting types for <Blob> for
conflicting types for <the data symbol> (§65g). Putting the shape decl AND its
extern inside the function (§100), with a unique name, clears both. Watch the §184 textual-typedef
prescan trap — paraphrase the comment.
(byte-proven ov_SC02_005/func_80189B30; this is §378c's fifth variant, solved)
(f) A K&R old-style definition is an integration lever. When the TU's existing prototype is
(s32, s32) and the body wants s16 parameters, writing the definition K&R-style lets the s16
parameters PROMOTE to the TU's prototype — no decl edit, no cast, and the gate's "conflicting types"
disappears.
(byte-proven ov_SC05_018/func_80182CDC)