mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-26 13:33:34 -04:00
86caa7dfdb
T36 CORRECTION (the important half). Building the cookbook sweep tool surfaced a defect in
tools/verify_map_findings.py, which I had already used to validate BOTH map audits:
- GNU C sources use FORM FEED (\f) page separators — loop.c 47, cse.c 36, reload1.c 27,
local-alloc.c 21. Python's splitlines() splits on \f; grep/sed do not. Every line number computed
after the first \f was shifted (up to 47 in loop.c), which is LARGER than the checker's own +/-40
window — precisely how a real quote gets reported FABRICATED.
- Re-run after the fix: T34 regalloc 27 OK/153 NEAR/0 FAB -> 180 OK/0/0. T35 four-file
47 OK/240 NEAR/12 FAB -> 299 OK/0/0. THE AGENTS' LINE NUMBERS WERE EXACT ALL ALONG. I had even
written the false "off by +2..+19" claim into the T35 agent prompt.
- MY DIAGNOSIS OF THE 12 WAS ALSO WRONG. I said agents pasted map prose into source_quote and
"verified" it by grepping — but I grepped claim_excerpt (which IS map prose) instead of
source_quote. The real source_quote was ` record_jump_equiv (insn, 0);` at cse.c:7511, a
correctly-located C line. Two stacked errors: a broken tool, then a check of the wrong field that
appeared to confirm it.
- Fixed: both tools use split("\n"); verify_map_findings.py documents the trap so it cannot return;
loop.md's "12 unverified" note is WITHDRAWN in place. Nothing was deleted on this basis (all 12
were CONFIRMED-status, none underpinned a refutation), and the T34/T35 upheld/overturned splits are
unaffected — those came from adversarial agents, not the checker.
T37 THE COOKBOOK SWEEP (what was asked for). New tools/sweep_citations.py puts the mechanical half of
a citation audit into zero-token tooling (offline-tooling-first): symbol-form cites are compared to
the real 2.7.2 definition line; file-form cites are localised to their enclosing function.
- matching-cookbook.md: 57 resolvable citations, all localisable. MIXED provenance but mostly sound —
materially better than the map files. loop.c:5556, local-alloc.c:1765/1795/1825, global.c:906/917/
924/1000, local-alloc.c:1021/1064, global.c:588/594, sched.c:820, expmed.c:556, jump.c:2131 all
land where the prose says. GENUINE MISS: expr.c:5535 is MIN/MAX optab code; the /s grant sites are
4577 and 4904.
- STATED LIMITATION: "lands in the right function" is weak for giants (expand_expr 4026->~6300,
jump_optimize 139->~2200). This is a CITATION sweep, not a claim audit — proportionate because the
cookbook's idioms are byte-proven and its cites are explanation. No idiom re-litigated.
Docs+tools only: no src/ or config/ touched; R22 not re-run and not claimed.
127 lines
5.6 KiB
Python
127 lines
5.6 KiB
Python
#!/usr/bin/env python3
|
|
"""sweep_citations.py — deterministically localise every gcc-source citation in a markdown doc.
|
|
|
|
WHY THIS IS A TOOL AND NOT AN AGENT WAVE. The codegen-map audits (T34/T35) established that our
|
|
docs cite `tools/reference/gcc-papermario/` = gcc **2.8.1**, while we build with **2.7.2**
|
|
(`tools/reference/gcc-2.7.2/`), and that the drift is large and NON-uniform (+27 in sched.c, +611 in
|
|
reload1.c) — large enough that a citation lands inside a DIFFERENT FUNCTION and every sentence built
|
|
on it still reads plausibly. Deciding *whether a claim is true* needs judgement. Deciding *where a
|
|
line number actually points* does not: it is a lookup, and a lookup belongs in zero-token tooling.
|
|
|
|
So this tool answers the mechanical half exactly, and leaves only genuine judgement for a human/agent:
|
|
|
|
symbol-form cite (`combine_regs:1825`) -> compare to the symbol's REAL 2.7.2 definition line.
|
|
Verdict is fully determined: OK / DRIFT(+N) / NO-SYMBOL.
|
|
file-form cite (`cse.c:8626`) -> report which function that line falls INSIDE in 2.7.2,
|
|
so a reader can see at a glance whether the doc's
|
|
surrounding prose is talking about that function at all.
|
|
|
|
A cite whose enclosing function differs from the symbol the prose names is the exact signature of the
|
|
"landed in a neighbouring function" failure — that is what to grep the report for.
|
|
|
|
python3 tools/sweep_citations.py docs/matching-cookbook.md [--src tools/reference/gcc-2.7.2]
|
|
"""
|
|
import argparse
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
DEF_RE = re.compile(r"^([a-z_][a-z0-9_]*)\s*\(") # K&R definition: name on its own line
|
|
# `sym:1234` or `file.c:1234` (optionally a range `1234-1250`)
|
|
CITE_RE = re.compile(r"([A-Za-z_][A-Za-z0-9_./-]*?)\s*:\s*(\d{2,5})(?:\s*-\s*\d{2,5})?")
|
|
SKIP_PREFIX = ("http", "https", "ftp", "Phase", "phase", "§")
|
|
|
|
|
|
def index_source(src):
|
|
"""(defs, lines) — defs: symbol -> [(file, line)] ; lines: file -> [text]."""
|
|
defs, lines = {}, {}
|
|
for root, _dirs, files in os.walk(src):
|
|
for f in files:
|
|
if not f.endswith((".c", ".h", ".md")):
|
|
continue
|
|
p = os.path.join(root, f)
|
|
rel = os.path.relpath(p, src)
|
|
try:
|
|
L = open(p, errors="replace").read().split("\n")
|
|
except Exception:
|
|
continue
|
|
lines[rel] = L
|
|
lines.setdefault(os.path.basename(rel), L) # allow bare "cse.c"
|
|
for i, ln in enumerate(L, 1):
|
|
m = DEF_RE.match(ln)
|
|
if m:
|
|
defs.setdefault(m.group(1), []).append((rel, i))
|
|
return defs, lines
|
|
|
|
|
|
def enclosing(lines, fname, line):
|
|
"""Name of the function whose K&R definition most recently precedes `line`."""
|
|
L = lines.get(fname) or lines.get(os.path.basename(fname))
|
|
if not L or not (1 <= line <= len(L)):
|
|
return None, len(L) if L else 0
|
|
best = None
|
|
for i in range(line - 1, -1, -1):
|
|
m = DEF_RE.match(L[i])
|
|
if m:
|
|
best = (m.group(1), i + 1)
|
|
break
|
|
return best, len(L)
|
|
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument("doc")
|
|
ap.add_argument("--src", default="tools/reference/gcc-2.7.2")
|
|
ap.add_argument("--only-problems", action="store_true")
|
|
a = ap.parse_args()
|
|
|
|
if not os.path.isdir(a.src):
|
|
print("no source tree at %s" % a.src)
|
|
sys.exit(2)
|
|
defs, lines = index_source(a.src)
|
|
|
|
rows, seen = [], set()
|
|
for n, raw in enumerate(open(a.doc, errors="replace").read().split("\n"), 1):
|
|
for m in CITE_RE.finditer(raw):
|
|
who, ln = m.group(1), int(m.group(2))
|
|
if who.startswith(SKIP_PREFIX) or who.isdigit():
|
|
continue
|
|
key = (who, ln)
|
|
if key in seen:
|
|
continue
|
|
seen.add(key)
|
|
is_file = who.endswith((".c", ".h", ".md")) or "/" in who
|
|
if is_file:
|
|
base = os.path.basename(who)
|
|
if base not in lines and base.replace(".md", "") not in lines:
|
|
continue
|
|
enc, total = enclosing(lines, base, ln)
|
|
if enc is None:
|
|
rows.append((n, who, ln, "OUT-OF-RANGE", "file has %d lines" % total))
|
|
else:
|
|
rows.append((n, who, ln, "IN-FN", "%s (defined :%d)" % (enc[0], enc[1])))
|
|
else:
|
|
if who not in defs:
|
|
continue # not a gcc symbol we can resolve; skip quietly
|
|
cands = defs[who]
|
|
near = min(cands, key=lambda c: abs(c[1] - ln))
|
|
d = ln - near[1]
|
|
verdict = "OK" if abs(d) <= 3 else "DRIFT %+d" % d
|
|
rows.append((n, who, ln, verdict, "%s real :%d" % (near[0], near[1])))
|
|
|
|
probs = [r for r in rows if r[3].startswith("DRIFT") or r[3] == "OUT-OF-RANGE"]
|
|
print("%s — %d resolvable citations" % (a.doc, len(rows)))
|
|
print(" symbol-form OK : %d" % len([r for r in rows if r[3] == "OK"]))
|
|
print(" symbol-form DRIFTED : %d" % len([r for r in rows if r[3].startswith("DRIFT")]))
|
|
print(" file-form localised : %d" % len([r for r in rows if r[3] == "IN-FN"]))
|
|
print(" out of range : %d" % len([r for r in rows if r[3] == "OUT-OF-RANGE"]))
|
|
print()
|
|
show = probs if a.only_problems else rows
|
|
print("%-6s %-30s %-7s %-12s %s" % ("mdline", "cite", "line", "verdict", "in real 2.7.2"))
|
|
for n, who, ln, v, note in show:
|
|
print("%-6d %-30s %-7d %-12s %s" % (n, who[:30], ln, v, note))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|