Files
BFM-decomp/cookbook/C0447.md
T

2.7 KiB

§399 ★★★ — FOUR LEVERS FROM THE FINAL S69 ROUND (P31 S69; each byte-proven, none previously in the cookbook)

(a) A fence BETWEEN two prologue loads, where source order does nothing. Two loads at the top of a function emitted in the wrong order, and reordering the C statements had no effect — the order is fixed before statement order matters. A __asm__ __volatile__("") placed between the two loads (§194-A) swapped them and closed the last 2 instructions. (byte-proven md_MAIN_013/func_800CB56C, 94 ins)

(b) SINK A CALL INTO BOTH ARMS and let cross_jump keep only the jal suffix. A warm start sat at 88 ins / closeness 86. Sinking func_8017E3E8 into both arms of a >= 0x400 test (so cross_jump merges only the shared jal tail, §193-C) took it to 92 ins / closeness 13; then §3-T2 field-order on the two zero-stores — deliberately NOT the neighbour's order — let each sh $zero fill an lhu load-delay slot and closed it. Note the direction: duplicate the call in source so the compiler merges it, rather than writing the merged form yourself. (byte-proven ov_SC07_001/func_8017EDC0, 92 ins)

(c) A $v0 → $a0 → $s3 DOUBLE COPY IS A TWO-PSEUDO TELL. Not a scheduling artifact — it means the source has two distinct variables: an SImode temp holding the call result (living in a caller-saved register for the first compare) and a separate HImode variable for the tail:

    s32 t = call();        /* SImode, caller-saved, feeds the compare */
    s16 ret = t;           /* HImode, $s3, survives to the tail       */

That split alone took 70 → 37. And §195-N precondition 5: nesting return 1 inside the if-chain with exactly ONE trailing return 0; blocks jump.c's store-flag transform (killing sltiu $v0,$v0,1), so reorg fills both delay slots with addu $v0,$zero,$zero — 18 → 0. (byte-proven ov_SC02_017/func_8018347C, 94 ins)

(d) THE RE-TIE AS A BIV KILLER. __asm__("" : "=r"(q) : "0"(q)) on a halfword walker gives q a second set, so n_times_set > 1 and loop.c refuses it as a biv — which kills the combined address giv that had biased the base to +2 and had sunk $s0's init into the preheader. combine_givs picks the last-discovered / lowest-offset giv, so denying it the biv is the whole lever. Measured alternatives: a volatile q was WORSE (8), a dead q[0] read stayed at 6. This is the third distinct use of the zero-byte re-tie in one session — §380 (un-hoist a move_movables invariant), §393 (kill the scheduler's birthing boost), and now §399d (deny a biv). One line, three passes: when a single-set pseudo is being treated specially, give it a second set. (byte-proven ov_SC07_001/func_8017E4DC, 92 ins)