mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-29 07:10:32 -04:00
dfa366c7c6
func_8017F510 (1,511 ins): EXACT length, frame 0x258 exact, byte-exact prologue AND epilogue,
identical sp-slot set, 99.5% register-masked structural / 93.3% byte-aligned, SYMS-OK, 97 divergent.
Not a match (G3). §71's callee-set lever delivered ~90% of the C and a first compile at 1528/1511.
- CORRECTNESS FINDING (qualifies §17): a local `register s32 x __asm__("$30")` pin produced a
seductive 1511 ins / 98.9% and was a MISCOMPILE -- gcc-2.7.2 ALSO allocated $s8 to an unrelated
live value. A pin is a HINT to the allocator, not a reservation. Never ship one without inspecting
the pinned register's defs. Banked work is safe by construction (the byte-gate rejects a
miscompile); the exposure is UN-GATED drafts. ACTION: behemoth-2's draft carries FIVE pins
($25 $17 $19 $20 $21) and must be re-checked before anyone builds on it.
- THE HONEST FIX was source-level: the +17 drift was live-range stretching from REUSING w/wz for the
vertex-word reads (spilling amb, 7 lw+nop pairs). Dedicated temps -> 1528->1511, 88%->99.6%, no pin.
- GIV RECORD ORDER (§70 family): part->prim must be read BEFORE part->nprim -- loop.c:combine_givs
walks bl->giv in REVERSE record order, so the last-recorded giv becomes the combined base.
- RESIDUAL 97 traced to ONE seed: c3 is $a2 in the target, $a3 in the draft; tp takes the other of
the pair and renames the whole 4-tail block. Fix c3 -> $a2 and ~93 should fall together.
- SPENT, byte-recorded: decl-order permutations, block-scoping, splitting/inlining tp, reusing f0,
vertex axis orders, and decomp-permuter (4,724 candidates, base 97, ZERO improvement -- a §3 hard
tail outside the C-randomisation space).
- b3_align.py / b3_pos.py supersede the b2_* aligners.
103 lines
3.5 KiB
Python
103 lines
3.5 KiB
Python
"""Shape-agnostic word-level masked sequence aligner (generalised from .run/giants/b2_*.py).
|
|
|
|
usage: python b3_align.py <obj.o> <target.s> [nhunks]
|
|
prints BOTH the register-MASKED (structural) and the register-KEPT (byte) aligned numbers.
|
|
"""
|
|
import re, subprocess, difflib, shutil, sys, collections
|
|
|
|
OBJ = sys.argv[1]
|
|
TGT = sys.argv[2]
|
|
NH = int(sys.argv[3]) if len(sys.argv) > 3 else 60
|
|
OD = [c for c in ["mips-linux-gnu-objdump", "mipsel-linux-gnu-objdump"] if shutil.which(c)][0]
|
|
out = subprocess.run([OD, "-drz", OBJ], capture_output=True, text=True).stdout
|
|
|
|
|
|
def words_mine():
|
|
w = []
|
|
for line in out.splitlines():
|
|
m = re.match(r'\s*[0-9a-f]+:\s+([0-9a-f]{8})\s', line)
|
|
if m:
|
|
w.append(int(m.group(1), 16))
|
|
elif 'R_MIPS_' in line and w and not isinstance(w[-1], tuple) and (w[-1] >> 26) not in (2, 3):
|
|
w[-1] = ('R', w[-1])
|
|
return w
|
|
|
|
|
|
def words_tgt():
|
|
w = []
|
|
for line in open(TGT):
|
|
m = re.match(r'\s*/\* \w+ [0-9A-F]{8} ([0-9A-F]{8}) \*/\s+(\S+)\s*(.*)', line)
|
|
if m:
|
|
b = bytes.fromhex(m.group(1))
|
|
v = int.from_bytes(b, 'little')
|
|
if '%hi(' in line or '%lo(' in line:
|
|
w.append(('R', v))
|
|
else:
|
|
w.append(v)
|
|
return w
|
|
|
|
|
|
def mask(x, keepimm=True):
|
|
rel = isinstance(x, tuple)
|
|
v = x[1] if rel else x
|
|
op = (v >> 26) & 0x3F
|
|
if op == 0 or op == 0x1C:
|
|
k = (op << 6) | (v & 0x3F) | (((v >> 6) & 0x1F) << 12)
|
|
elif op in (2, 3):
|
|
k = (op << 26)
|
|
elif op in (0x12,):
|
|
k = v & 0xFC1F07FF
|
|
elif op in (1, 4, 5, 6, 7):
|
|
k = (op << 20) | (((v >> 16) & 0x1F) << 8)
|
|
else:
|
|
k = (op << 20) | ((v & 0xFFFF) if (keepimm and not rel) else 0)
|
|
return (k, 'R' if rel else '')
|
|
|
|
|
|
def full(x):
|
|
rel = isinstance(x, tuple)
|
|
v = x[1] if rel else x
|
|
op = (v >> 26) & 0x3F
|
|
if rel:
|
|
return (v & 0xFFFF0000, 'R')
|
|
if op in (2, 3):
|
|
return (op << 26, '')
|
|
if op in (1, 4, 5, 6, 7):
|
|
return (v & 0xFFFF0000, '')
|
|
return (v, '')
|
|
|
|
|
|
wm, wt = words_mine(), words_tgt()
|
|
print("mine", len(wm), "target", len(wt))
|
|
|
|
mt = []
|
|
for line in out.splitlines():
|
|
m = re.match(r'\s*[0-9a-f]+:\s+[0-9a-f]{8}\s+(.*)', line)
|
|
if m:
|
|
mt.append(m.group(1).strip())
|
|
tt = []
|
|
for line in open(TGT):
|
|
m = re.match(r'\s*/\* \w+ [0-9A-F]{8} [0-9A-F]{8} \*/\s+(.*)', line)
|
|
if m:
|
|
tt.append(m.group(1).strip())
|
|
|
|
for name, fn, show in (("REGISTER-MASKED (structural: opcodes+imms+sp-offsets kept)", mask, True),
|
|
("FULL (registers KEPT; relocs + branch displacements masked)", full, False)):
|
|
a = [fn(x) for x in wm]
|
|
b = [fn(x) for x in wt]
|
|
sm = difflib.SequenceMatcher(None, a, b, autojunk=False)
|
|
ops = sm.get_opcodes()
|
|
eq = sum(i2 - i1 for t, i1, i2, j1, j2 in ops if t == 'equal')
|
|
bad = [o for o in ops if o[0] != 'equal']
|
|
print(name)
|
|
print(" aligned-identical %d / %d target ins = %.1f%%" % (eq, len(b), 100.0 * eq / len(b)))
|
|
print(" divergent hunks: %d total divergent target ins: %d"
|
|
% (len(bad), sum(o[4] - o[3] for o in bad)))
|
|
if show:
|
|
for t, i1, i2, j1, j2 in bad[:NH]:
|
|
print(" %-8s tgt[%d:%d] (%d) mine[%d:%d] (%d)" % (t, j1, j2, j2 - j1, i1, i2, i2 - i1))
|
|
for k in range(j1, min(j2, j1 + 6)):
|
|
print(" T %4d %s" % (k, tt[k] if k < len(tt) else '??'))
|
|
for k in range(i1, min(i2, i1 + 6)):
|
|
print(" M %4d %s" % (k, mt[k] if k < len(mt) else '??'))
|