mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-27 14:02:04 -04:00
feat(phase-29 T3): the §53 has_mid_jr INTERLOCK + worker_wave -O0 build-step fix
- family_sweep §53 INTERLOCK (the Task-3 deliverable): a has_mid_jr family CANNOT bank through the carve-less --hseq path — its exemplar's own bank needed a jump-table carve. The sweep now SKIPS them LOUDLY, names the right tool (jtbl_family_bank), and states that a 0% from this path is a TOOL ARTIFACT, not a wall. Uses the manifest's has_mid_jr (family_hseq already derived it — R33, one oracle, shared with dedup_extend; no re-derivation). --allow-jr is the escape hatch. WHY: a silent 0% from the wrong tool is exactly what manufactured the Phase-26 "structural families ≈0% / don't template" doctrine and steered two phases of strategy (§53, Phase-28 T1). The interlock makes the omission impossible to misread. VERIFIED (live-fire + negative control, R32/R35): manifest carries 11 has_mid_jr families / 163 member-slots of 1418 sweepable; --band substantial -> skips 9 families / 155 slots (159 -> 150 families); --allow-jr -> 159 (does NOT skip). It correctly flags 0x8017bebc (n=115) = B2, the very family whose missing carve produced the ≈0% doctrine (0/8 -> 102/115 once carved). - worker_wave.js: pass --o0 to the match_one self-check for -O0 targets (new `o0` target flag) + an -O0 prompt block (§18/§18-P29: the %lo-fold residual and the array-of-struct crack). Without it an agent iterates a -O0 target against an -O2 compile and can NEVER match — the same wrong-build-step trap that produced the Phase-28 "~3%" swing number (Task 1). Exercised live by func_8013C414. - byte-neutral: tooling only, no src/config touched.
This commit is contained in:
@@ -19,6 +19,7 @@ compile and are logged for the decl-reconcile pass; they are NOT remap failures.
|
||||
import json, glob, re, subprocess, os, sys, shutil, collections, argparse
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
import family_remap as FR
|
||||
import family_hseq # §53 interlock — the ONE has_mid_jr oracle (R33, shared with dedup_extend)
|
||||
import canon_sig_reconcile as CSR # v3.2 (Phase-25 T7-M2 per-sibling re-reconcile, Q5-proven)
|
||||
from scope_data_externs import fix as scope_data_fix # §8d (Phase-26 session 8)
|
||||
|
||||
@@ -266,6 +267,27 @@ def hseq_sweep(a):
|
||||
os.path.join(REPO, a.reconcile_raw, f"func_{int(f['exemplar']['addr'], 16):08X}.c"))]
|
||||
if a.min_members > 1:
|
||||
fams = [f for f in fams if f["n_members"] >= a.min_members]
|
||||
|
||||
# ---- §53 INTERLOCK: a has_mid_jr family CANNOT bank through this carve-less path.
|
||||
# Its exemplar's own bank required a jump-table carve (jtbl_family_bank / the §8 rodata workflow);
|
||||
# sweeping it here just burns gate cycles and returns 0% — and a 0% from the WRONG tool is the exact
|
||||
# artifact that manufactured the Phase-26 "structural families ≈0% / don't template" doctrine and
|
||||
# steered two phases of strategy (§53, Phase-28 T1). So SKIP them LOUDLY and name the right tool,
|
||||
# rather than let the omission read as a wall. `has_mid_jr` comes from the manifest (family_hseq
|
||||
# already derived it off the exemplar's words — R33: one oracle, don't re-derive).
|
||||
# --allow-jr is the escape hatch (the whole-binary byte-gate remains the sole arbiter, G3/P9).
|
||||
jr_fams = [f for f in fams if f.get("has_mid_jr")]
|
||||
if jr_fams and not getattr(a, "allow_jr", False):
|
||||
fams = [f for f in fams if not f.get("has_mid_jr")]
|
||||
skipped_members = sum(f["n_members"] for f in jr_fams)
|
||||
print(f"[hseq] §53 INTERLOCK: skipping {len(jr_fams)} has_mid_jr family(ies) "
|
||||
f"({skipped_members} member-slots) — they need the jtbl carve, NOT this sweep.\n"
|
||||
f" route: tools/jtbl_family_bank.py <func> <from_ov> <from_addr> <members.json>\n"
|
||||
f" (a 0% from this path would be a TOOL artifact, not a wall — §53. "
|
||||
f"Override with --allow-jr if you mean it.)")
|
||||
for f in jr_fams[:8]:
|
||||
print(f" jr-family exemplar {f['exemplar']['addr']} band={f['band']} n={f['n_members']}")
|
||||
|
||||
fams.sort(key=lambda f: -f["byte_weight_templatable"])
|
||||
if a.limit:
|
||||
fams = fams[:a.limit]
|
||||
@@ -367,6 +389,10 @@ def main():
|
||||
ap.add_argument("--allow-pins", action="store_true",
|
||||
help="bypass the §42e pinned-exemplar skip: template WITH the register pins and let the "
|
||||
"whole-binary byte-gate arbitrate (some pinned families bank ×134 per-sibling, e.g. func_8017A4AC).")
|
||||
ap.add_argument("--allow-jr", action="store_true",
|
||||
help="§53 escape hatch: sweep has_mid_jr families through this carve-less path anyway "
|
||||
"(they normally need tools/jtbl_family_bank.py). The whole-binary byte-gate stays "
|
||||
"the sole arbiter, but expect ~0% — and do NOT read that 0% as a wall.")
|
||||
ap.add_argument("--fix-def-sig", action="store_true",
|
||||
help="T6: rewrite each member draft's DEF signature to the shared-header (engine_core.h) "
|
||||
"canonical decl, so a member forward-declared there with a different sig (e.g. s32* vs "
|
||||
|
||||
@@ -48,7 +48,21 @@ function drafterPrompt(t, draftDir) {
|
||||
// (asm/ov_SC01_077/nonmatchings/ov_SC01_077_a/<fn>.s). Derive the dir so the match_one
|
||||
// self-check finds the right .s; fall back to the main subdir if t.asm is absent.
|
||||
const asmSubdir = (t.asm && t.asm.indexOf('/') >= 0) ? t.asm.replace(/\/[^/]+$/, '') : ASM_SUBDIR
|
||||
return `Match ONE MIPS function for the Brave Fencer Musashi PS1 matching decompilation (overlay ov_SC01_077).
|
||||
// -O0 targets (the ov_SC01_077_o0 cluster / whale _o0b): the TU is compiled -O0 by a Makefile
|
||||
// target-specific CC1FLAGS, so match_one MUST self-check with --o0 or the agent iterates against
|
||||
// the WRONG build step (the Phase-29 Task-1 trap that manufactured the "~3%" swing number: a -O0
|
||||
// target compiled -O2 can NEVER match). §53/§54 law: reproduce every build step the target needs.
|
||||
const o0flag = t.o0 ? ' --o0' : ''
|
||||
const o0note = t.o0
|
||||
? `\n-O0 TARGET (this fn lives in an -O0-compiled split, ${asmSubdir}). Your match_one self-check MUST pass --o0
|
||||
(already included below). Read COOKBOOK §18 + §18-P29: this cluster's classic residual is the %lo-fold on
|
||||
indexed global access — our cc1 materializes the address (lui;addiu;addu;sw 0(reg)) where the original folds
|
||||
%lo (lui;addu idx;sw %lo(sym)(reg), 1 ins shorter). The CRACK (§18/T3a, byte-proven) is the ARRAY-OF-STRUCT
|
||||
idiom: \`extern Struct base[];\` with sizeof(Struct)==stride, then \`base[i].field\`. Do NOT write
|
||||
\`*(T*)(&sym + i*stride)\` — that materializes &sym. At -O0 there is no DCE, so an unused local reserves its
|
||||
var region (frame-pad induction, §42 lever 3) with zero body instructions.\n`
|
||||
: ''
|
||||
return `Match ONE MIPS function for the Brave Fencer Musashi PS1 matching decompilation (overlay ov_SC01_077).${o0note}
|
||||
GOAL: write C that the pinned compiler (gcc-2.7.2-psx -O2 -G0 -mips1 -mcpu=3000 -mgas -msoft-float -fgnu-linker + maspsx --aspsx-version=2.56 --expand-div) compiles to BYTE-IDENTICAL machine code.
|
||||
|
||||
TARGET: ${t.name} @ ${t.addr} — ${t.nins} instructions, class hint "${t.class}".${prior}${giant}
|
||||
@@ -80,7 +94,7 @@ PROCESS (you have Bash + Read):
|
||||
// @class: <one of: regalloc-order | schedule | remat | struct | iv-combine | loop-guard | loose-typing | plumbing | other>
|
||||
// @stuck: <one concrete line on the residual that remains, or "none — MATCH">
|
||||
3. Self-check (fast relocation-masked proxy for the byte-gate):
|
||||
.venv/bin/python tools/match_one.py ${t.name} --c ${draftDir}/${t.name}.c --asm-subdir ${asmSubdir}
|
||||
.venv/bin/python tools/match_one.py ${t.name} --c ${draftDir}/${t.name}.c --asm-subdir ${asmSubdir}${o0flag}
|
||||
- "MATCH (N ins)" => byte-identical (relocation-masked). You nailed it. Stop.
|
||||
- "N mismatched" => N instructions differ. Apply the toolkit, iterate to reduce N.
|
||||
4. Iterate a few times; KEEP THE BEST draft in the file (always leave a file, even if imperfect —
|
||||
|
||||
Reference in New Issue
Block a user