mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-26 13:33:34 -04:00
c4380c19e4
MEASURED (denominators in .run/S67_findings.md): * 193 of the 530 open functions ALREADY have a draft on disk (1,885 wave targets seen, 1,521 banked, 171 open-no-draft, 166 never drawn). Classified in their real TUs: 37 MATCH / 67 NEAR / 89 CC1-FAIL. * 159 open functions (30% of the frontier) reference a jump table; 96 are PLAN-REFUSED by build_carve (non-contiguous same-subseg .rodata), 75 non-main across 38 subsegs. Not a codegen wall and not a decl wall — carve plumbing. NEW * tools/strand_census.py — coverage-asserted census + rtu_match classifier + draft staging. Keys binary:fn (R48); classifies each pair once after merging every manifest's view. * tools/o0_detect.py — the -O0 prologue tell extracted from match_one (which parses argv at import and therefore cannot be imported). match_one re-exports it; ONE definition (R33). Wiring it into the classifier turned md_MAIN_003 from 8 NEAR (7 of them >20) into 6 MATCH. Negative-controlled both directions. * tools/scope_demote_drafts.py — §8d as an _xform-contract gate rung. NOT yet exercised. FIXED * jtbl_carve --probe now runs build_carve (a pure planner) and reports plan-refused. It previously called only island_probe, which answers a necessary-not-sufficient question — every blocked function probed "carveable", and S66 priced 32 of them as free on that. * blocker_probe.macro_scope selects the LAST #define per macro name, matching cpp. engine_core.h has 1,037 duplicate DEFINE_func_ names and 4 with DIFFERENT bodies. NOT VALIDATED — DO NOT SCALE * jr_isolate_all: two real defects fixed (carried types deduped by name; header-provided types no longer re-emitted) but ov_SC02_000 STILL fails the byte gate after them. Open lead: file_scope_types carries a block without its enclosing #if guard. 20 of 35 blocked overlays dry-run clean and that number means nothing until one round-trips. 0 functions banked this session. tools-health has ONE pre-existing cdecl defect (1 of 74,749 declarations, func_8017EE08_p55352/struct ZnRec) — cdecl.py and its inputs are byte-identical to HEAD, so it is not from this change. Knowledge banked: cookbook §322/§323/§323a/§323b, decision-log pivot, accelerators #13/#14.
50 lines
2.7 KiB
Python
50 lines
2.7 KiB
Python
#!/usr/bin/env python3
|
|
"""o0_detect.py — the -O0 prologue tell, in ONE place. (P31 S67)
|
|
|
|
Extracted verbatim from match_one.py so other tools can ask the same question without importing
|
|
match_one (which parses argv at import time and therefore cannot be imported at all). A SECOND
|
|
hand-written copy of this predicate would be a second oracle that can silently disagree with the
|
|
one the drafting path steers by — exactly what R33 forbids. match_one re-exports it, so its
|
|
behaviour is unchanged and there is still only one definition.
|
|
"""
|
|
import re
|
|
|
|
|
|
def detect_o0(spath):
|
|
"""Is the TARGET compiled -O0? Read it off its own prologue (cookbook §6/§18, byte-proven).
|
|
|
|
gcc-2.7.2 keeps a frame pointer at -O0 and omits it at -O2, so an -O0 function opens with
|
|
`sw $fp, N($sp)` + `addu $fp, $sp, $zero` (word 21F0A003) inside its first few instructions.
|
|
Requiring BOTH, and requiring them in the PROLOGUE, is what separates the tell from -O2 code
|
|
that merely uses $fp/$s8 as an ordinary allocatable callee-saved register — the distinction that
|
|
makes "$fp occurs in 311 files under asm/" the WRONG population (the real one is 167 files).
|
|
|
|
WHY AUTO. Nothing in the drafting path ever passed --o0: api_draft.match_one() (the oracle every
|
|
wave agent iterates against) builds a fixed argv without it. An agent handed an -O0 target was
|
|
therefore shown an -O2 compile of its own C and a mismatch on every instruction — feedback that
|
|
cannot converge, for a reason invisible in the diff. Detecting it from the bytes costs one file
|
|
read and needs no card field, no lane, and no agent instruction (R33: derive it, do not ask).
|
|
"""
|
|
try:
|
|
head, started = [], False
|
|
for ln in open(spath):
|
|
# START AT THE FUNCTION, NOT AT THE FILE. A migrated jump table or an .asciz blob is
|
|
# emitted into the same .s ahead of the code (`.section .rodata` first), so a naive
|
|
# "first 8 encoded lines" reads TABLE WORDS as the prologue and calls an -O0 function
|
|
# -O2 (measured on md_MAIN_011/func_800CF28C and func_800D04F4).
|
|
if re.match(r'\s*(glabel|dlabel|\w+:)\s', ln) and 'glabel' in ln:
|
|
started = True
|
|
continue
|
|
if not started:
|
|
continue
|
|
m = re.match(r'\s*/\* [0-9A-Fa-f]+ [0-9A-Fa-f]{8} ([0-9A-Fa-f]{8}) \*/\s*(\S.*)?', ln)
|
|
if m:
|
|
head.append((m.group(1).upper(), (m.group(2) or '').strip()))
|
|
if len(head) >= 8:
|
|
break
|
|
except OSError:
|
|
return False
|
|
setup = any(w == '21F0A003' or re.match(r'addu\s+\$fp,\s*\$sp,\s*\$zero', t) for w, t in head)
|
|
save = any(re.match(r'sw\s+\$fp,', t) for _w, t in head)
|
|
return setup and save
|