Files
BFM-decomp/tools/permuter_ils.py
T
Drew T c106774776 feat(phase-24): T7 §G — 6/8 giants cracked, 4 ×134; fleet 65.75→65.91%
- MATCHED 6 reach-134 giants: func_801571C4 (permuter), func_8014EA4C/801372B0/
  801770E0/80176D94/80148094 (Fable5, sequential idiom-banking chain)
- 2 genuine walls stay INCLUDE_ASM (G4): func_80178004 (close=7), func_801412A8 (close=29)
- propagated x134: func_801571C4/8014EA4C/80176D94/80148094 (134 overlays byte-identical;
  dedup 1810 groups/0 failed; genuinely-clean make-clean+extract-all+check-all = 136/136)
- func_801372B0/801770E0 banked x1 (x134 follow-up: pin/asm + local-type self-containment gaps)
- tools: p16_permute.py comment-fix (unblocked the permuter fleet-wide) + permuter_ils.py (warm-restart ILS)
- knowledge: cookbook §37 + docs/gcc-2.7.2-map/t7g-giant-harvest.md
2026-07-07 20:20:43 -06:00

71 lines
3.3 KiB
Python

#!/usr/bin/env python3
"""permuter_ils.py — iterated-local-search wrapper over decomp-permuter (Phase 24 T7 §G).
A COLD `run_masked` plateaus at the base score over ~10k iters, but WARM-RESTARTING base.c from the
best byte-waypoint each cycle (fresh -j) descends where cold stalls (proven on func_80148094: 72 -> 36
over ~8 restarts; the big drops come from FRESH restarts, not continuing a plateaued run). The final
score-0 hit is a CANDIDATE — bank it only through the whole-binary byte-gate (harvest_verify), since
intermediate waypoints can be semantically divergent (the permuter rewrites stores for byte-proximity).
python3 tools/permuter_ils.py func_80148094 --draft .run/t7b/close/func_80148094.c \
--asm-subdir asm/ov_SC01_077/nonmatchings/ov_SC01_077 --klass REGALLOC --cycles 10 --secs 180 --j 12
On a score-0 winner -> .run/permuter-winners/<fn>.c (then winner_to_draft + gate whole-binary).
"""
import argparse, glob, os, re, shutil, sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import p16_permute as P
def best_waypoint(pd):
"""(score, dir) of the lowest-score output-<score>-<n>/source.c across ALL cycles; None if none.
output-0-* is the true byte-match; output-<N>-* are intermediate bests (masked-diff score N)."""
cands = []
for d in glob.glob(f"{pd}/output-*"):
m = re.match(r"output-(\d+)-", os.path.basename(d))
if m and os.path.exists(os.path.join(d, "source.c")):
cands.append((int(m.group(1)), d))
return sorted(cands)[0] if cands else None
def main():
ap = argparse.ArgumentParser()
ap.add_argument("fn")
ap.add_argument("--draft", required=True, help="seed C draft (externs + def)")
ap.add_argument("--asm-subdir", default=P.ASM)
ap.add_argument("--klass", default="REGALLOC")
ap.add_argument("--cycles", type=int, default=10)
ap.add_argument("--secs", type=int, default=180, help="per-cycle time box")
ap.add_argument("--j", type=int, default=12)
ap.add_argument("--winners", default=".run/permuter-winners")
a = ap.parse_args()
draft = open(a.draft).read()
pd = P.setup(a.fn, draft, asm_subdir=a.asm_subdir, klass=a.klass)
if not pd:
print("ILS setup FAILED (target .s didn't assemble?)"); sys.exit(1)
print(f"ILS {a.fn}: {a.cycles} cycles x {a.secs}s @ -j{a.j}, klass={a.klass}")
prev = None
for cyc in range(1, a.cycles + 1):
P.run_permuter(pd, a.secs, a.j) # writes output-*/ ; kills stragglers
bw = best_waypoint(pd)
if bw is None:
print(f" cycle {cyc}: no waypoint (no improvement over base yet)")
continue
score, d = bw
tag = " (unchanged)" if prev is not None and score >= prev else ""
print(f" cycle {cyc}: best score = {score}{tag} [{os.path.basename(d)}]")
if score == 0:
os.makedirs(a.winners, exist_ok=True)
dst = os.path.join(a.winners, a.fn + ".c")
shutil.copy(os.path.join(d, "source.c"), dst)
print(f" WINNER score 0 -> {dst} (gate whole-binary before banking)")
return
shutil.copy(os.path.join(d, "source.c"), f"{pd}/base.c") # warm restart
prev = score
print(f"ILS done: best={prev} (no score-0; seed for Fable5 or a longer run)")
if __name__ == "__main__":
main()