4.1 KiB
§16 Guided hand-matching the struct-heavy core (Phase 17 — beats the §15 brute-force)
Phase 16 called the loose-typing wall "fundamental." Phase 17 disproves it for the majority. The wall is
a signature-CONSISTENCY problem, not a comprehension one — the §1 loop reconstructs correct bodies ~100% of
the time; the work is byte-closing + sig reconciliation. Full process: docs/sunset/hand-matching-process.md
(§1 loop, §2 idioms, §3a the 5-move signature-consistency playbook, §7 the Ultracode wave + canonical-sig wall).
New byte-idioms (Phase 17, §2 there):
- mask-local (defeats
lh→lhufold).*(s16*)f & (x & 0xFFFF)inline lets gcc fold the load tolhu- drop the
andi. Hoist the mask:s32 m = x & 0xFFFF; ... *(s16*)f & m→ gcc keepslh+ emitsandi.
- drop the
- shared-ret0 goto (cross-jump clustering + branch polarity). Two non-adjacent predicate tests the
original routes to ONE shared
return 0block → write both asgoto ret0;to a single trailingret0: return 0;. gcc then makesret0a labeled block reached by branches (right polarity) + schedules the next test's constant into the delay slot. A loneif(x)return 0;inlines (wrong polarity/reg). - v0↔v1 result/constant coalescing + the §10 hoist-vs-remat / phantom-frame quirks = the residual hard
tail — NOT a dead-end: §17 (CORRECTED) shows the call-crossing register-ORDER class is matchable with
register __asm__PINS + a scheduling barrier (byte-proven, func_8012B8E4), and array-decay cracks the hoist-vs-remat class. Hand-tier, but matchable. (Permuter can't help — it rejectsregister __asm__.)
Scaling = Ultracode wave (§12 pattern + §7): Ghidra pre-pass (DecompileFunctions.java, headless batch,
no /mcp) → parallel draft agents (m2c+Ghidra-C+asm+actor-struct+§3a, self-validate match_one) → whole-binary
gate (harvest_verify --chunk 1) → sig_unify recover → dedup_propagate --auto-from. Calibration (top-30):
60% match_one MATCH, 33% whole-binary (+0.47% fleet), 136/136.
THE CANONICAL-SIG LAYER (built Phase-17 session-4; NOT the ~2× lever it first looked like). The
match_one→whole-binary gap on the calibration's top-30 was SIG CONFLICTS (parallel agents declare shared
callees inconsistently → conflicting types in the one-big-TU; 100% compile-errors, 0 codegen). Fix = a
SURGICAL per-callee canonical-sig layer: tools/census_conflict_callees.py (the conflict predicate:
undeclared-stub callee with decl_sources = n_callers + is_target >= 2) + tools/derive_canonical_sigs.py
(byte-neutral s32 func_X(s32...), arity from Ghidra-C + asm read-before-write $a0-$a3) → a 20-extern
block at the TOP of ov_SC01_077.c (LOCAL, not engine_core.h — reach-1 names differ across overlays). (Both census_conflict_callees.py and derive_canonical_sigs.py were DELETED in Phase 26-A — R33; the fleet-canonical-sig approach was superseded by reconcile_tu's per-TU oracle. Historical record.)
gen_harvest_targets + sig_unify auto-read it; the gate pipeline is now draft → sig_unify (MANDATORY)
→ harvest_verify --chunk 1 (the accumulating baseline now carries the file-top block, so a raw draft's
guessed extern would clash without sig_unify). SIZING CORRECTION (R14): for the remaining 270, the
conflict wall is only 20 callees / 24 targets / 7% of wave reach — the "~2×" was the top-30's in-flight
conflicts, since resolved by banking. The real wall is the gcc-quirk tail, not sig conflicts — the 4
highest-reach circular targets are ALL §10-hoist / regalloc / layout-bound (0 closed by hand or permuter).
The layer makes a wave sig-clean; it does NOT unlock the quirk tail. → The match-% lever is understanding
gcc-2.7.2 (R17 compiler-source research, Phase 18), not more brute waves. Wave deferred; infra staged
(.run/harvest_wave_s4.js, 40 tractable reach-134 targets). See docs/sunset/hand-matching-process.md §8.
§17 (CORRECTED) answers it: the call-crossing register-ORDER class IS matchable — with register __asm__
pins + a scheduling barrier (byte-proven). The "brute waves won't help" point stands; HAND levers (pins) do.