feat(phase-25): T5b batch-2 — def-side wall cracked mechanically (canon_sig_reconcile); +5 giants, 3x134 (fleet 72.18->72.29%)

- 29 giants drafted (Opus-xHigh wave), 16 R14-isolation-MATCH but 0 auto-banked: ALL
  blocked by the DEF-SIDE canonical-sig wall (Ghidra-typed draft sigs conflict with the
  engine_core.h canonical, which lives inside DEFINE_ macros so sig_unify can't reach it).
- NEW tools/canon_sig_reconcile.py: strip ambient-dup typedefs/externs -> rewrite def to
  the canonical sig -> cast changed params AT USE (never intermediate locals: a local adds
  a pseudo -> regalloc shift -> byte-diff, measured 70ff4748 != d19c9580). Byte-neutral.
- BANKED 5 giants mechanically: func_8013B274 func_80130D48 func_80167DBC (family_sweep
  x134) + func_8016DC20 func_8018514C (exemplar-only). ~404 new fn-defs.
- R22 clean-fleet 136/136 BYTE-IDENTICAL (from make clean + extract-all + check-all),
  dedup 1813/0, 0 NON_MATCHING (G4).
- 11 walls backlogged (non-identical ambient types SVEC/ApplyMatrixSV, macro-local data
  symbols D_*, u8 redef, byte-diff) + 13 nears (permuter-ILS/Fable5 fuel). Frontier +29.
- Cookbook §41 (the reconcile recipe + the at-use-cast-not-locals regalloc proof) +
  decision-log R31 (the "giant tier was plumbing not matching" pivot). R30/R31.
This commit is contained in:
Drew T
2026-07-09 20:56:35 -06:00
parent 00f81e1678
commit 20747321c4
274 changed files with 51102 additions and 3304 deletions
+208
View File
@@ -0,0 +1,208 @@
#!/usr/bin/env python3
"""Def-side-wall reconcile (Phase-25 T5b/T7 lever).
A drafter writes an isolation-MATCH giant body with Ghidra-derived TYPED parameters
(void func(u32 *a0, s16 *a2)). Placed in the real overlay TU it hits `conflicting types`:
the TU's callers reference the function through the CANONICAL signature declared in
src/shared/engine_core.h (extern void func(s32, s32, void*)) — or, absent that, through
gcc-2.7.2's implicit K&R `int func()` from a call site above the definition. The gate's
sig_unify can't reach the engine_core.h decl (it lives inside a DEFINE_ macro) so the
draft never banks.
This reconciles the DEF to the canonical signature, BYTE-NEUTRALLY:
1. strip the draft's redefinitions of types already in src/shared/engine_types.h
(identical-definition typedefs — a redef is a hard error in gcc-2.7.2 / C89);
2. rewrite the def signature to the canonical (return type + positional param types;
arity-grow adds unused params so an implicit-int caller with N args still matches);
3. cast each type-changed param AT ITS USES — never via an intermediate local, which
introduces a fresh pseudo and shifts register allocation (proven: cast-locals broke
func_8013B274, at-use casts banked it byte-identical).
The whole-binary harvest_verify byte-gate remains the sole arbiter (G3/P9) — a wrong
reconcile simply fails to bank. Canonical form comes from engine_core.h when present,
else the implicit-int-compatible `s32 func(s32,...)` (matches K&R `int func()`).
Usage:
tools/canon_sig_reconcile.py --fn func_8013B274 --draft .run/drafts-t5-batch2/func_8013B274.c \
--out .run/t5b2-recon/func_8013B274.c [--sig 'void func_8013B274(s32 a0, s32 a1, void *a2)']
"""
import re, os, argparse
EC = 'src/shared/engine_core.h'
ET = 'src/shared/engine_types.h'
def ambient_typedefs():
"""name -> normalized single-line 'typedef struct {...} name;' from engine_types.h."""
et = open(ET).read()
out = {}
for m in re.finditer(r'typedef\s+struct\s*\{[^}{]*\}\s*([A-Za-z_]\w*)\s*;', et):
out[m.group(1)] = re.sub(r'\s+', ' ', m.group(0)).strip()
return out
def canonical_from_engine_core(fn):
"""Return the canonical 'ret ... fn(params)' decl string from engine_core.h, or None."""
ec = open(EC).read()
m = re.search(r'extern\s+([^;]*?\b' + re.escape(fn) + r'\s*\(([^;]*)\))\s*;', ec)
return (re.sub(r'\s+', ' ', m.group(1)).strip()) if m else None
def split_params(param_str):
"""Split a C parameter list on top-level commas -> ['s32 a0', 's16 *a2', ...]."""
param_str = param_str.strip()
if param_str in ('', 'void'):
return []
out, depth, cur = [], 0, ''
for ch in param_str:
if ch in '([':
depth += 1
elif ch in ')]':
depth -= 1
if ch == ',' and depth == 0:
out.append(cur.strip()); cur = ''
else:
cur += ch
if cur.strip():
out.append(cur.strip())
return out
def parse_param(p):
"""'s16 *a2' -> ('s16 *', 'a2'). 'void' / unnamed -> (p, None)."""
m = re.match(r'^(.*?)([A-Za-z_]\w*)\s*$', p.strip())
if not m:
return (p.strip(), None)
typ, name = m.group(1).strip(), m.group(2)
# a bare type token with no name (e.g. 's32', 'void *') -> name is None
if typ == '' and name in ('void', 's32', 'u32', 'int', 's16', 'u16', 's8', 'u8', 'char', 'long'):
return (name, None)
return (typ, name)
def parse_sig(sig, fn):
"""'void func_X(s32 a0, s16 *a2)' -> (ret, [(type,name),...])."""
m = re.match(r'^(.*?)\b' + re.escape(fn) + r'\s*\((.*)\)\s*$', sig.strip(), re.S)
if not m:
raise ValueError(f'cannot parse sig: {sig!r}')
ret = m.group(1).strip()
params = [parse_param(p) for p in split_params(m.group(2))]
return ret, params
def cast_uses(body, name, orig_type):
"""Blanket-wrap EVERY body use of `name` in `((orig_type)name)`. This preserves the
original semantics of every use — pointer arithmetic (`name + 1` keeps orig stride),
index (`name[i]`), deref (`*name`), member (`name->f`), pass-through, AND already-cast
uses (`(P_TAG*)name` -> `(P_TAG*)((orig)name)`, same bits) — all byte-neutral (a cast
emits no code). Targeted patterns missed arithmetic (void*+1 != s16*+1); blanket is
correct and safe (\\b avoids substrings; the def line is in `head`, not `body`)."""
ot = orig_type.strip()
return re.sub(r'\b' + re.escape(name) + r'\b', '((' + ot + ')' + name + ')', body)
def strip_conflicting_externs(draft, ambient):
"""Remove the draft's redundant `extern` decls — the `conflicting types` source. The TU's
#includes (common.h + engine_core.h) and its per-TU canonical-sig layer already declare
these; the draft's Ghidra-typed re-declaration conflicts. Strip an extern when its symbol
is in `ambient` (declared by engine_core.h/engine_types.h or the target TU), and always
strip a `memcpy` extern (the TU's macros / gcc builtin provide it — a mismatched prototype
triggers `conflicting types for built-in memcpy`). A genuinely-draft-local extern (symbol
NOT ambient) is kept; the byte-gate is the sole arbiter (G3/P9)."""
def drop_fn(m):
return '' if m.group(1) in ambient else m.group(0)
# extern function decls (func_XXXX or memcpy/library)
draft = re.sub(r'^\s*extern\s+[^;\n]*?\b(func_[0-9A-Fa-f]+)\s*\([^;\n]*\)\s*;\s*\n',
drop_fn, draft, flags=re.M)
draft = re.sub(r'^\s*extern\s+[^;\n]*?\bmemcpy\s*\([^;\n]*\)\s*;\s*\n', '', draft, flags=re.M)
# extern DATA decls (one line may declare several: `extern s16 D_a, D_b;`)
def drop_data(m):
names = re.findall(r'\b(D_[0-9A-Fa-f]+)\b', m.group(0))
return '' if names and all(n in ambient for n in names) else m.group(0)
draft = re.sub(r'^\s*extern\s+[^;\n]*\bD_[0-9A-Fa-f]+[^;\n]*;\s*\n', drop_data, draft, flags=re.M)
return draft
def ambient_symbol_names(tu_path=None):
"""Every func_/D_ symbol declared by engine_core.h/engine_types.h (and the target TU, if
given) — the set whose draft re-declarations are redundant + conflict-prone."""
names = set()
for p in (EC, ET, tu_path):
if p and os.path.exists(p):
txt = open(p).read()
names |= set(re.findall(r'\b(func_[0-9A-Fa-f]+)\b', txt))
names |= set(re.findall(r'\b(D_[0-9A-Fa-f]+)\b', txt))
return names
def reconcile(fn, draft, canon_sig=None, tu_path=None):
amb = ambient_typedefs()
# 1) strip ambient-dup typedefs (identical definition only -> byte-neutral)
def strip_td(m):
name = m.group(1); norm = re.sub(r'\s+', ' ', m.group(0)).strip()
return '' if (name in amb and amb[name] == norm) else m.group(0)
draft = re.sub(r'typedef\s+struct\s*\{[^}{]*\}\s*([A-Za-z_]\w*)\s*;\n?', strip_td, draft)
# 1b) strip the draft's redundant externs (func/data/memcpy) that engine_core.h/the TU declares
draft = strip_conflicting_externs(draft, ambient_symbol_names(tu_path))
# locate the definition signature (ret fn(params) {)
dm = re.search(r'\n((?:[A-Za-z_][\w ]*?\*?\s*)\b' + re.escape(fn) + r'\s*\([^;{]*\))\s*\n?\{', draft)
if not dm:
raise ValueError(f'no definition of {fn} found in draft')
draft_sig = dm.group(1)
dret, dparams = parse_sig(draft_sig, fn)
# 2) determine canonical
canon_sig = canon_sig or canonical_from_engine_core(fn)
if not canon_sig:
# implicit-int-compatible fallback: s32 return + s32 params, keep draft arity
cret, cparams = 's32', [('s32', None)] * len(dparams)
else:
cret, cparams = parse_sig(canon_sig, fn)
# 3) build the new param list (keep DRAFT names; use CANONICAL types positionally),
# and record which positions changed type (need at-use casts).
new_params, changed = [], []
for i in range(max(len(dparams), len(cparams))):
dt, dn = dparams[i] if i < len(dparams) else (None, None)
ct, cn = cparams[i] if i < len(cparams) else (None, None)
if dt is None: # arity grow: canonical has an extra param
new_params.append(f'{ct} _unused{i}'); continue
if ct is None: # canonical shorter (rare) — keep draft param as-is
new_params.append(f'{dt} {dn}' if dn else dt); continue
ntd = re.sub(r'\s+', ' ', dt).strip()
ntc = re.sub(r'\s+', ' ', ct).strip()
if dn is None:
new_params.append(ct); continue
new_params.append(f'{ct} {dn}' if not ct.endswith('*') else f'{ct}{dn}')
if ntc != ntd:
changed.append((dn, dt))
new_sig = f'{cret} {fn}(' + ', '.join(new_params) + ')'
# split body from the def line so casts only touch the body
head, body = draft[:dm.start(1)], draft[dm.end(1):]
for name, orig_type in changed:
body = cast_uses(body, name, orig_type)
return head + new_sig + body
def main():
ap = argparse.ArgumentParser()
ap.add_argument('--fn', required=True)
ap.add_argument('--draft', required=True)
ap.add_argument('--out', required=True)
ap.add_argument('--sig', default=None, help='explicit canonical sig (else engine_core.h / implicit-int)')
ap.add_argument('--tu', default=None, help='target TU .c (its already-declared symbols are treated as ambient)')
a = ap.parse_args()
out = reconcile(a.fn, open(a.draft).read(), a.sig, a.tu)
os.makedirs(os.path.dirname(a.out), exist_ok=True)
open(a.out, 'w').write(out)
canon = a.sig or canonical_from_engine_core(a.fn) or '(implicit-int s32-form)'
print(f'{a.fn}: reconciled -> {a.out} [canonical: {canon}]')
if __name__ == '__main__':
main()