Files
BFM-decomp/tools/oracle_reorder.py
T

44 lines
2.9 KiB
Python

#!/usr/bin/env python3
"""oracle_reorder.py — decide whether a NEAR residual is a C defect or an ASSEMBLER artifact.
WHY (P31 S53, cookbook §188). The `jr $ra` + `addiu $sp` epilogue tail — and unfilled branch delay
slots generally — are produced by GNU `as` at `-O2`, NOT by cc1. The project pins `ASFLAGS -O1`
(Makefile:563) and maspsx force-emits `.set noreorder` after every `.ent`
(tools/maspsx/maspsx/__init__.py:856-859), so under the pinned triple that shape is UNREACHABLE from
any C source. A draft can therefore be perfectly correct and still sit at a stubborn residual.
THE DIAGNOSTIC: re-assemble the SAME draft two ways and compare.
maspsx + as -O1 (the pinned path) -> the residual you already see
bypass + as -O2 (maspsx skipped) -> if this is 0 diffs, THE C IS ALREADY RIGHT
A 0 in the second cell means: file IMMOVABLE, stop grinding, no C-level work can ever close it.
Measured on func_80061FA8 (target 103 ins): pinned 57 diffs/106 ins; bypass+-O2 0 diffs/103 ins.
Promoted verbatim from .run/wave_p31r/oracle61.py because a cookbook section may not cite evidence
that lives under a gitignored path (the verifier that found this flagged exactly that).
"""
"""Byte-exact oracle for the 0x8005D000-0x80062600 PsyQ island: cpp -> cc1 -> (move->addu) -> as -O2.
Reproduces the ORIGINAL assembler's reorder-mode delay-slot swapping, which maspsx (forced
`.set noreorder`) + the pinned `as -O1` cannot emit. Diagnostic only -- NOT the project gate."""
import subprocess,sys,re,os
sys.path.insert(0,'tools'); import masked_diff
CPP='mipsel-linux-gnu-cpp'; CC1='tools/bin/gcc-2.7.2-psx/cc1'; AS='mipsel-linux-gnu-as'
CPPFLAGS='-lang-c -Iinclude -undef -Wall -fno-builtin -Dmips -D__GNUC__=2 -D__OPTIMIZE__ -Dpsx -D_PSYQ -D_MIPSEL -D_LANGUAGE_C'.split()
CC1FLAGS='-quiet -O2 -G0 -mips1 -mcpu=3000 -mgas -msoft-float -fgnu-linker'.split()
ASFLAGS='-Iinclude -march=r3000 -mtune=r3000 -no-pad-sections -O2 -G0'.split()
cfile,fn,sub=sys.argv[1],sys.argv[2],sys.argv[3]
wd='.run/match/oracle61'; os.makedirs(wd,exist_ok=True)
s=masked_diff.strip_scalar_typedefs(open(cfile).read())
if '#include "common.h"' not in s: s='#include "common.h"\n'+s
open(wd+'/t.c','w').write(s)
p=subprocess.run([CPP]+CPPFLAGS+[wd+'/t.c'],capture_output=True)
assert not p.returncode,p.stderr.decode()[-1500:]
q=subprocess.run([CC1]+CC1FLAGS,input=p.stdout,capture_output=True)
assert not q.returncode,q.stderr.decode()[-2500:]
asm=re.sub(r'\tmove\t(\$\w+),(\$\w+)', lambda m:'\taddu\t%s,%s,$0'%(m.group(1),m.group(2)), q.stdout.decode())
r=subprocess.run([AS]+ASFLAGS+['-o',wd+'/t.o'],input=asm.encode(),capture_output=True)
assert not r.returncode,r.stderr.decode()[-1500:]
mine=masked_diff.insns_from_object(wd+'/t.o',fn); tgt=masked_diff.insns_from_s('%s/%s.s'%(sub,fn))
d=masked_diff.structured_diff(mine,tgt)
print('ORACLE %s mine=%d tgt=%d diffs=%d'%(fn,len(mine),len(tgt),len(d)))
for i,m,t in d[:15]: print(' ',i,m,'|',t)