fix(phase-21): dedup_propagate skips -O0 overlay-local fns + gate_stage surfaces prop failures

The cont.4 root-cause fixes so future _a waves auto-realize ×134:
- dedup_propagate --auto-from now excludes *_o0.c (-O0) defs: -O0 codegen embeds per-overlay
  %lo data, so masked h_exact falsely reports reach-134 (§18/§20). One such straggler
  (func_8013C360) reverted 10 clean ×134 matches under the all-or-nothing batch gate (cont.4).
  Detected via find_site on the -O0 split file; --addr still forces them. (6 fns excluded.)
- gate_stage: capture dedup_propagate's exit; on a real byte-gate revert (not the benign
  'nothing to propagate' no-op) write .run/auto/last_propagate_error.log + add prop_error to
  the summary + warn. A swallowed revert previously hid the gain silently.
This commit is contained in:
Drew T
2026-06-24 21:32:05 -06:00
parent e30697a081
commit 04c5c9b47f
2 changed files with 32 additions and 3 deletions
+15 -1
View File
@@ -293,8 +293,22 @@ def main():
ssig = load_sig(src)
ctext = source_text(src) # scan main + split files for matched defs
reg = registered_addrs() # skip functions already shared (additive + resumable)
# -O0 split-file functions are OVERLAY-LOCAL (§18/§20, cont.4): -O0 codegen embeds per-overlay
# %lo data addresses, so their bytes diverge per overlay even though the relocation-MASKED
# h_exact falsely reports reach-134. Cross-overlay-propagating one fails the byte-gate and,
# under the all-or-nothing batch revert, poisons every CLEAN match in the batch (cont.4:
# func_8013C360 reverted 10 good ×134 matches). Never auto-propagate them — detect by def-site
# in the -O0 split file (*_o0.c). (--addr still forces them, for an explicit override.)
o0_skip = set()
for _p, _tag in overlay_files(src):
if _p.name.endswith("_o0.c"):
_o0txt = _p.read_text()
for _ad in ssig:
_s = find_site(_o0txt, src, _ad)
if _s and _s[0] == "def":
o0_skip.add(_ad)
for addr in sorted(ssig):
if addr in reg:
if addr in reg or addr in o0_skip:
continue
site = find_site(ctext, src, addr)
if not site or site[0] != "def": # only functions matched (inline def) in the source
+17 -2
View File
@@ -120,10 +120,24 @@ def _run_gate_locked(drafts, binary, src, asm, out, good_sha, propagate, source_
# 5 propagate the banked matches fleet-wide
propagated = 0
prop_error = None
if verified and propagate:
before = _dedup_group_count()
sh([PY, "tools/dedup_propagate.py", "--auto-from", binary, "--min-reach", "2"], timeout=3600)
pr = sh([PY, "tools/dedup_propagate.py", "--auto-from", binary, "--min-reach", "2"], timeout=3600)
propagated = max(0, _dedup_group_count() - before)
# Surface a REAL failure: dedup_propagate exits non-zero on a byte-gate revert (a false-reach
# straggler poisoned the all-or-nothing batch) — distinct from the benign "nothing to propagate"
# empty-plan no-op. A swallowed revert previously hid a real ×134 gain (cont.4); never again.
out = (pr.stdout or "") + (pr.stderr or "")
if pr.returncode != 0 and "nothing to propagate" not in out:
errlog = os.path.join(REPO, ".run/auto/last_propagate_error.log")
try:
open(errlog, "w").write(out)
except OSError:
pass
prop_error = [l for l in out.strip().splitlines() if l.strip()][-3:] or [f"exit {pr.returncode}"]
print(f"[gate] WARNING: dedup_propagate exited {pr.returncode} ({propagated} groups added); "
f"see {errlog}", file=sys.stderr)
# 6 log every non-match to the backlog (closeness + RESIDUAL class + best draft for the human).
# The drafter stamps `// @class: <gcc-quirk class>` and `// @stuck: <note>` into the draft (so the
@@ -171,7 +185,8 @@ def _run_gate_locked(drafts, binary, src, asm, out, good_sha, propagate, source_
commit_sha = sh(["git", "rev-parse", "--short", "HEAD"]).stdout.strip()
return {"drafts": len(draft_fns), "banked": len(verified), "propagated": propagated,
"near": near, "failed": failed, "fleet_pct": fp, "verified": verified, "commit": commit_sha}
"near": near, "failed": failed, "fleet_pct": fp, "verified": verified,
"prop_error": prop_error, "commit": commit_sha}
def main():