Files
BFM-decomp/tools/test_o0_detect.py
T
Drew T f574f3c80e feat(match_one): derive the opt level from the target — the -O0 oracle was never wired up
--o0 has existed since P29 and NOTHING ever passed it: api_draft.match_one(), the
oracle every wave agent iterates against, builds a fixed argv without it. Every agent
handed an -O0 target was 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, and
it hit even the 51 functions already sitting in -O0 objects.

match_one now decides from the target (R33), with two oracles because neither alone
is sufficient (R34): the -O0 frame-pointer prologue in the target's own bytes, OR the
subseg being an object the Makefile compiles -O0. boot/start.s is built -O0 with no
ordinary prologue; an -O0 function stranded in an -O2 subseg has the prologue but
cannot bank — and match_one now says so instead of letting an agent chase it.

The prologue scan is anchored at glabel, not the top of the file: two md_MAIN_011
.s files open with a migrated jump table / .asciz blob and a naive scan read table
words as the prologue.

tools/test_o0_detect.py: 14,400 .s scanned, 167/167 signature files covered, 0 false
positives outside -O0 objects, both rodata-first files detected.

Census correction in the design doc: the handoff's refutation of md_MAIN_003/011 is
itself wrong (16 and 21 -O0 functions, byte-verified), and the 311 '$fp mentions'
figure over-counts because $fp is $s8, allocatable at -O2. True population: 167
files, of which 116 / 14,148 ins are -O0 functions the build compiles -O2 — double
the atlas estimate, with x2/x3/x4 sibling replication.
2026-08-24 11:15:33 -06:00

99 lines
4.1 KiB
Python

#!/usr/bin/env python3
"""test_o0_detect.py — negative control for match_one's -O0 auto-detection (P31 S59).
WHY IT EXISTS. `match_one --o0` has always existed, and nothing in the drafting path ever passed
it: `api_draft.match_one()` — the oracle every wave agent iterates against — builds a fixed argv
without it. An agent handed an -O0 target was 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. S59 made the
opt level auto-detected. This is the control on that detector.
THE DETECTION LAW (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).
THREE CHECKS:
NC-1 COVERAGE — every .s under asm/ that carries the -O0 signature anywhere is detected as -O0,
either by its prologue or by its subseg being an -O0 object. A miss compiles an -O0 target
at -O2, which is the whole defect.
NC-2 NO FALSE POSITIVES OUTSIDE THE -O0 OBJECTS — a file the signature grep does NOT find may
only be called -O0 if it lives in a subseg the Makefile compiles -O0 (`boot`, `*_o0*`).
Calling an -O2 function -O0 is the same defect with the sign flipped.
NC-3 THE PROLOGUE RULE IS ANCHORED AT THE FUNCTION — the two md_MAIN_011 files whose .s opens
with a migrated jump table / .asciz blob must still be detected. They are the reason the
scan starts at `glabel`, not at the top of the file.
Run: .venv/bin/python tools/test_o0_detect.py
"""
import glob
import os
import re
import subprocess
import sys
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Import detect_o0 without executing match_one's CLI (it parses argv at import time).
_SRC = open(os.path.join(REPO, "tools/match_one.py")).read()
_FN = _SRC[_SRC.index("def detect_o0"):_SRC.index("_sub = os.path.basename")]
_NS = {"re": re, "os": os}
exec(_FN, _NS)
detect_o0 = _NS["detect_o0"]
FAIL = []
def sub_is_o0(path):
"""Does the BUILD compile this subseg -O0? (Makefile:697/702/716/724)"""
sub = os.path.basename(os.path.dirname(path))
return ("_o0" in sub) or sub == "boot"
def main():
os.chdir(REPO)
files = glob.glob("asm/**/*.s", recursive=True)
grep = set(subprocess.run(
["bash", "-c", "grep -rl 'addu .*\\$fp, *\\$sp\\|21F0A003' asm/ --include=*.s"],
capture_output=True, text=True).stdout.split())
print(f"R32 denominator: {len(files)} .s files under asm/")
print(f"carrying the -O0 signature anywhere in the file: {len(grep)}")
detected = {p for p in files if detect_o0(p) or sub_is_o0(p)}
missed = sorted(grep - detected)
print(f"detected as -O0 (prologue OR -O0 subseg): {len(detected)}")
# NC-1
if missed:
FAIL.append(f"NC-1 COVERAGE: {len(missed)} signature-carrying files not detected: {missed[:5]}")
print(f"NC-1 coverage : {len(grep) - len(missed)}/{len(grep)} signature files detected")
# NC-2
fp = sorted(p for p in detected - grep if not sub_is_o0(p))
if fp:
FAIL.append(f"NC-2 FALSE POSITIVES: {len(fp)} files called -O0 with no signature and no -O0 subseg: {fp[:5]}")
extra_in_o0 = len(detected - grep) - len(fp)
print(f"NC-2 false positives : 0 outside -O0 objects ({extra_in_o0} inside them, which the "
f"Makefile does compile -O0)")
# NC-3
anchored = ["asm/md_MAIN_011/nonmatchings/md_MAIN_011/func_800CF28C.s",
"asm/md_MAIN_011/nonmatchings/md_MAIN_011/func_800D04F4.s"]
for p in anchored:
if not os.path.exists(p):
print(f"NC-3 skipped ({p} absent — asm/ not extracted?)")
break
if not detect_o0(p):
FAIL.append(f"NC-3 {p}: rodata-first .s not detected — the scan is not anchored at glabel")
else:
print(f"NC-3 rodata-first : {len(anchored)}/{len(anchored)} detected (scan anchored at glabel)")
if FAIL:
print("\nFAILED:")
for f in FAIL:
print(" -", f)
return 1
print("\nALL CHECKS PASS")
return 0
if __name__ == "__main__":
sys.exit(main())