Files
BFM-decomp/tools/oracle/reg_renumber_swap.sh
T
Drew T ce7780b91f feat(phase-29): T46 — reg_renumber-swap oracle built + validated; it REFUTES the func_80176734 framing
Item 1's remaining half. Oracle mechanized and reusable at tools/oracle/reg_renumber_swap.sh: break
at reload entry (cc1 unstripped: reg_renumber @0x82d4330, reload @0x815d4d7), swap two hard regs
across reg_renumber, finish the compile, re-score with masked_diff REUSED not reimplemented (R33).

NEGATIVE CONTROL: a no-op swap (31<->31) reproduces exactly the baseline 13 mismatches, so the
harness faithfully reproduces the pinned compile.

RESULT: both contested swaps are far WORSE — <-> (17 pseudos) = 345 mismatches +1 insn;
<-> (31 pseudos) = 97. Baseline 13.

WHY, AND IT REFUTES THE FRAMING: reading .greg for cluster B's own insn shows
  (set (reg/v:SI 6 a2) (plus:SI (reg/v:SI 5 a1) (const_int 60)))
— the destination is a HARD register, not a pseudo. reg_renumber only maps pseudos (>=
FIRST_PSEUDO_REGISTER = 68), so that value is structurally unreachable by this oracle. The draft has
NO register __asm__ pins (header says so, grep confirms), so  is hard because it is an incoming
PARAMETER register that local-alloc reused as a destination.

VERDICT for func_80176734 (51,198 ins): the residual is NOT global-allocation 2-colouring. It is the
LOCAL-alloc hard-reg reuse / tying class — combine_regs (2.7.2 local-alloc.c:1722) +
qty_phys_copy_sugg, i.e. regalloc.md K8/RC-4, whose lever is C-level LIFETIME SHAPING, not the
permuter and not reg_renumber. That also explains the flat permuter: it was mutating a dial that
does not control this residual.

MAP REFINEMENT OWED: §H presents the swap oracle as THE way to discriminate RC-6 from S3 in one gdb
run. It has an unstated PRECONDITION — the contested registers must be held by PSEUDOS. Check .greg
first; if they appear as (reg/v:SI N ...) with N < 68 they are already hard, and a coarse swap
returns a large meaningless number (345 here) that looks like a verdict and is not one.

Tree clean; nothing banked, nothing broken.
2026-07-28 14:53:47 -06:00

75 lines
2.8 KiB
Bash

#!/bin/bash
# swap_oracle.sh — the regalloc.md §H reg_renumber-swap oracle, mechanized.
#
# QUESTION IT ANSWERS: when a residual is "register identity AND an instruction reordering", you
# cannot tell from the diff whether the reorder is a SCHEDULING decision (S3 — C often cannot move
# it) or merely a CONSEQUENCE of the register grant (fix the regs and the schedule follows).
#
# METHOD: break at `reload` entry, swap two hard registers throughout `reg_renumber` (so every
# pseudo allocated to A gets B and vice versa), let compilation finish, and re-score the result with
# the SAME comparison match_one uses (masked_diff, reused not reimplemented — R33).
#
# diff count DROPS -> the residual is ALLOCATION; route to density/lifetime levers.
# diff count STAYS -> the reorder is not a consequence of these two registers.
#
# usage: swap_oracle.sh <hardA> <hardB> (MIPS: v0=2 v1=3 a0=4 a1=5 a2=6 a3=7)
set -u
REPO=/home/musashi/bfm-decomp
D=$REPO/.run/giants/fable_cd4/dumps_o734
A=$1; B=$2
cd "$D" || exit 1
cat > swap_${A}_${B}.gdb <<EOF
set pagination off
set confirm off
set height 0
set debuginfod enabled off
break *0x815d4d7
commands 1
silent
set \$rr = *(long *)0x082d4330
set \$mx = *(long *)0x082d3a10
set \$i = 0
set \$n = 0
while \$i < \$mx
set \$h = *(short *)(\$rr + \$i*2)
if \$h == $A
set *(short *)(\$rr + \$i*2) = $B
set \$n = \$n + 1
else
if \$h == $B
set *(short *)(\$rr + \$i*2) = $A
set \$n = \$n + 1
end
end
set \$i = \$i + 1
end
printf "SWAPPED %d pseudo(s) between hard %d and hard %d\n", \$n, $A, $B
continue
end
run -quiet -O2 -G0 -mips1 -mcpu=3000 -mgas -msoft-float -fgnu-linker t.i -o swap_${A}_${B}.s
quit
EOF
timeout 600 gdb -batch -x swap_${A}_${B}.gdb "$REPO/tools/bin/gcc-2.7.2-psx/cc1" 2>&1 | grep -E "^SWAPPED"
[ -s swap_${A}_${B}.s ] || { echo " (no output produced)"; exit 1; }
python3 "$REPO/tools/maspsx/maspsx.py" --aspsx-version=2.56 --expand-div < swap_${A}_${B}.s > swap_${A}_${B}.mas.s 2>/dev/null
# `as` must run from the REPO ROOT: the emitted .s carries a RELATIVE `.include "include/labels.inc"`.
( cd "$REPO" && mipsel-linux-gnu-as -march=r3000 -mtune=r3000 -no-pad-sections -O1 -G0 -Iinclude \
-o "$D/swap_${A}_${B}.o" "$D/swap_${A}_${B}.mas.s" ) 2>/dev/null \
|| { echo " (assemble failed)"; exit 1; }
cd "$REPO" && python3 - "$D/swap_${A}_${B}.o" "$A" "$B" <<'PY'
import sys, os
sys.path.insert(0, 'tools')
import masked_diff as M
obj, A, B = sys.argv[1], sys.argv[2], sys.argv[3]
tgt = 'asm/ov_SC01_077/nonmatchings/ov_SC01_077_jr_801734BC/func_80176734.s'
mine = M.insns_from_object(obj, 'func_80176734')
want = M.insns_from_s(tgt)
d = M.structured_diff(mine, want)
print(" swap $%s<->$%s : mine=%d ins, target=%d ins, %d mismatched (baseline 13)"
% (A, B, len(mine), len(want), len(d)))
PY