mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-26 13:33:34 -04:00
137c418bc7
* gate 1 (all 64 across 33 binaries): 12 banked — main 11 + ov_SC07_006 1. * gate 2 tested "a bad draft kills its binary's good ones" by re-staging only the 25 that recover_integration --probe-only called MATCH in their real TU: 0 banked. An honest null — that probe compiles and diffs bytes but never LINKS or CARVES, so it is a third oracle with its own blind spot. * triage (25/25 accounted): CARVE-REFUSED 10, undefined-reference 4, DIFF 3, CC1-FAIL-no-diagnostic 2, PARSE 1; gate 1 adds 7 func-decl / 4 data-decl / 6 type-decl conflicts. * R37 probe of the carve class: 6 of 8 are one refusal — a subseg would host NON-CONTIGUOUS .rodata carves — whose named remedy is jr_isolate_all (§8b). tools/restage_matching.py — rebuild a gate plan from probe verdicts. tools/gate_triage.py — route a gate's verdicts to the lane each one names (R47).
94 lines
4.7 KiB
Python
94 lines
4.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Route a parallel_gate result set to the repair lane its OWN verdict names (§376/§378, R47).
|
|
|
|
A gate that banks 0 is not a codegen verdict. `match_one` compiles a draft ALONE; the TU it must
|
|
live in already carries declarations written for its call sites, and C rejects the pair. Each of
|
|
those refusals names its own fix, and the fixes are byte-neutral (a declaration emits no code), so
|
|
the whole-binary gate stays the sole arbiter (G3/P9).
|
|
|
|
conflicting types for `f` -> cast_self_callers / fix_arity_callers --any-proto (§376/§378)
|
|
too few|many arguments to `f` -> fix_arity_callers --any-proto (§376)
|
|
undefined reference to D_* -> aprop_symfix (stale seed symbol) (§171)
|
|
redeclared / previous decl -> the draft redeclares what the TU owns (§378c)
|
|
|
|
R47: consume EVERY verdict layer — the class alone is not the verdict, the diagnostic text is.
|
|
R41: every count is printed against the number of drafts staged.
|
|
"""
|
|
import argparse, collections, json, os, re, sys
|
|
|
|
LANES = [
|
|
('CARVE', re.compile(r'CARVE-REFUSED'), 'jtbl_carve: probe it — the '
|
|
'dominant sub-class is NON-CONTIGUOUS .rodata in one subseg, fixed by jr_isolate_all (§8b)'),
|
|
('NO-DIAG', re.compile(r'CC1-FAIL\(no-diagnostic\)'), 'the cc1 error was not captured — '
|
|
'recompile the TU to get it'),
|
|
('ARITY', re.compile(r'too (few|many) arguments to function'), '§376 fix_arity_callers --any-proto'),
|
|
('CONFLICT', re.compile(r'conflicting types for'), '§378 cast_self_callers / fix_arity_callers'),
|
|
('REDECL', re.compile(r'redeclared|previous declaration|previously declared'), '§378c draft redeclares a TU-owned decl'),
|
|
('UNDEF-D', re.compile(r'undefined reference to `?D_[0-9A-Fa-f]{8}'), '§171 aprop_symfix (stale seed symbol)'),
|
|
('UNDEF-FN', re.compile(r'undefined reference to `?func_[0-9A-Fa-f]{8}'), 'missing callee definition/decl'),
|
|
('UNDEF', re.compile(r'undefined reference to'), 'unresolved symbol'),
|
|
('IMPLICIT', re.compile(r'implicit declaration'), 'missing extern'),
|
|
('PARSE', re.compile(r'parse error|syntax error'), 'draft does not parse in-TU'),
|
|
('DIFF', re.compile(r'\bDIFF\b|closeness'), 'real codegen residual'),
|
|
]
|
|
|
|
|
|
def lane_of(text):
|
|
for name, rx, _ in LANES:
|
|
if rx.search(text):
|
|
return name
|
|
return 'OTHER'
|
|
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser(description=__doc__,
|
|
formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
ap.add_argument('--results', default='.run/pgate_results.json')
|
|
ap.add_argument('--plan', default=None, help='the gate plan, to assert the denominator (R32)')
|
|
ap.add_argument('--out', default='.run/gate_triage.json')
|
|
a = ap.parse_args()
|
|
|
|
res = json.load(open(a.results))
|
|
staged = None
|
|
if a.plan:
|
|
staged = 0
|
|
for job in json.load(open(a.plan)):
|
|
staged += len([f for f in os.listdir(job['drafts']) if f.endswith('.c')])
|
|
|
|
rows, banked = [], []
|
|
for r in res:
|
|
b = r['binary']
|
|
for fn in r.get('banked') or []:
|
|
banked.append((b, fn))
|
|
for v in r.get('verdicts') or []:
|
|
fn, _, text = v.partition('\t')
|
|
rows.append({'binary': b, 'fn': fn.strip(), 'lane': lane_of(text),
|
|
'verdict': text.strip()})
|
|
if not (r.get('verdicts') or r.get('banked')) and r.get('tail'):
|
|
rows.append({'binary': b, 'fn': '(binary)', 'lane': lane_of(r['tail']),
|
|
'verdict': r['tail'].strip()[-200:]})
|
|
|
|
seen = {(x['binary'], x['fn']) for x in rows} | set(banked)
|
|
print('== gate triage (R41) ==')
|
|
if staged is not None:
|
|
print('drafts staged : %d' % staged)
|
|
# R32: a verdict for every staged draft, or say how many went unaccounted.
|
|
print('accounted (banked+verdict): %d / %s' % (len(seen), staged))
|
|
print('banked : %d' % len(banked))
|
|
c = collections.Counter(x['lane'] for x in rows)
|
|
for lane, n in c.most_common():
|
|
why = next((w for nm, _, w in LANES if nm == lane), '')
|
|
print(' %-9s %4d %s' % (lane, n, why))
|
|
json.dump({'banked': banked, 'rows': rows, 'staged': staged},
|
|
open(a.out, 'w'), indent=1)
|
|
print('rows -> %s' % a.out)
|
|
for lane, _, _ in LANES:
|
|
ex = [x for x in rows if x['lane'] == lane][:3]
|
|
for x in ex:
|
|
print(' %-9s %-14s %-16s %s' % (lane, x['binary'], x['fn'], x['verdict'][:110]))
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|