Files
BFM-decomp/cookbook/C0045.md
T

28 KiB
Raw Blame History

§42 — The F-band ≤28 regalloc crack wave: register-pin/DENSITY levers beat the permuter (Phase 25 T7 F-band, 2026-07-10; Ultracode 9-worker wave, 4/9 banked byte-identical, 266 swept ×134)

The F-near ≤28 band (14 fns / ~1,393 ins, one ov_SC01_077 h_norm exemplar each) is regalloc-order-DOMINATED (9 of 14 = saved-register $sN allocation/ordering swaps). The permuter is structurally blind to this class: it mutates C source, and pycparser rejects register __asm__ (§5a/§17), so a pure $sN-allocation swap has no source-mutation reachable. Empirically proven this wave: permuter-ILS (regalloc-directed _REGALLOC weights, 8×120 s warm-restart) plateaued at base on EVERY regalloc fn (func_80134C20 stuck@3; schedule-class func_8017EF50 stuck@4, func_80168828 8→5) — 0 closed. A 9-worker Ultracode wave applying MANUAL §17/§31 levers cracked 7/9 to byte-0 in isolation, of which 4 banked byte-identical through the whole-binary gate.

The winning levers (all zero-runtime-code, semantics-preserving; full RTL in subagents/workflows/wf_0329d3c2-75c/):

  1. The §31 DENSITY lever (the workhorse for $sN races). To win a razor-thin saved-reg allocation, ADD a zero-byte dead-read __asm__ __volatile__("" :: "r"(v)); on the pseudo you want gcc to prefer — it bumps v's ref-count so the local-alloc density heuristic gives it the contested $sN. Calibrate the COUNT exactly (func_80134C20: ONE dead-read of the master reclaims $s5; TWO over-boost it into $s4 → 13-off). Proven: func_80134C20 (230, MATCH), func_801365B8 (155, 11→2).
  2. The opaque asm-COPY for a param live-range split. __asm__("addu %0,%1,$zero" : "=r"(copy) : "r"(orig)); (or the §17 in-place re-tie __asm__("" : "=r"(p) : "0"((T)p));) forces gcc to keep orig in its incoming arg reg for early reads while copy carries the later reg — reproducing the target's single-pseudo live-range split. Proven: func_8017B614 (RC-9 hoist-vs-remat, MATCH). CAVEAT: reorg.c forbids __asm__ in a delay slot, so an asm-copy that must fall in one lands a slot early (func_801365B8's irreducible 2-off).
  3. Frame-pad induction: s32 pad[2]; (void)&pad; — address-taken-then-discarded local defeats -O2 DCE, reserves 8 unused var_size bytes to match a target frame (0x20 vs 0x18), shifting every save offset; (void)&pad emits zero code. Proven: func_80141A60 (MATCH). CAVEAT: frame-pad is ov077-specific — its 133 h_norm siblings ALL byte-drift on remap (each sibling's natural frame differs) → frame-pad families are EXEMPLAR-ONLY, NOT ×134-sweepable.
  4. Array-initializer LUID shift: s32 a[2] = {x, y}; vs two a[0]=x; a[1]=y; reorders the const-materialization LUIDs → sched2 emits the callee-save stores before the const chain (matches a target prologue-weave, S7). Proven: func_80180F10 (MATCH).
  5. u16 zero-extend for a high-bit halfword store constant:* storing 0x8000+ through unsigned short * zero-extends → ori $r,$zero,0xFFF8 (opcode 0x34) vs short *'s sign-extend addiu/li -8 (0x24). func_80141A60.

DIRECT register T v __asm__("$21") pins OFTEN BACKFIRE on giants — they wreck the prologue save-birthing order and clobber the dead pinned regs (func_80134C20: direct pins = 97-off vs density = MATCH; func_80180F10: pin = 37-off vs array-init = MATCH). Reach for the DENSITY lever first; use hard pins only when the residual is a clean, uncontested-reg home (the dont-conclude-unsteerable memory still holds: try SOMETHING before declaring a wall, but density > pins on the giants).

Attrition — isolation-MATCH ≠ real-TU bank (reinforces §41b): 7 iso-MATCH → 4 banked, 3 real-TU byte-drift (compile OK, byte-differs). func_8017B614's drift = the T1 memcpy-builtin→call class (the sibling TU's extern memcpy disables the builtin, so the worker's inlined lwl/lwr block-move lowers to a CALL) → re-crack with field-by-field or explicit memcpy(x,y,8). func_801365B8 = a GENUINE irreducible cse-representative conflict → G4/INCLUDE_ASM candidate.

Tooling gotchas (each cost a false-fail cycle): (a) harvest_verify.py for a NON-resident binary MUST pass --out build/<bin>/<bin> — its build() removes+sha1s --out (default build/resident/resident), so an overlay run without it reports "final SHA None"/fail for EVERY draft even when byte-identical. (b) canon_sig_reconcile can't extract a def whose body has a fn-pointer cast ((s32(*)(...))func) — such a draft banks RAW (no reconcile) if its sig is already canonical (func_80180F10). (c) R22 clean-fleet: make clean nukes the WHOLE splat tree (asm/); make extract re-splits only the DEFAULT binary — you must make extract BINARY=$b for ALL 136, else 135 fail "can't open .s" (a build-infra false-fail, not a byte mismatch).

Wave economics: 9 xHigh workers ≈ 1.66 M subagent tokens → 4 banked + 266 swept ×134 = ~270 fleet fns. The ≤28 regalloc band is genuine frontier — budget ~40-50% bank-rate per wave, NOT the mechanical tiers' ~94%.

§42a addendum — wave 2 (residuals + 29-100 band): iso-MATCH ≠ real-TU bank, the memcpy→struct-assign fix, +5 levers (2026-07-10b)

THE #1 LESSON — a crack worker must verify against the RECONCILED REAL TU, not isolation. Wave 2's 14 workers produced 9 iso-MATCHes but only 4 banked — 5 iso-MATCHes DRIFTED in the real overlay TU (func_80136824/ 80164930/8014DD8C/8016C188/80168828). The ONE iso-drift fn that banked (func_8017B614) did so because its worker embedded the def into a scratch copy of the real split .c, compiled the WHOLE TU (builtins ON = the real condition), and objdump-compared to the isolation MATCH — catching the drift cause and fixing it. isolation match_one uses -Iinclude+prepended common.h; the real TU adds engine_core.h types, a memcpy decl, and the reconciled sig — any of which shifts codegen. Wave-3+ crack prompt MUST require: after iso-MATCH, splice into a scratch copy of src/ov_SC01_077/<split>.c, cc1 the TU, and confirm the target fn's bytes are identical modulo link relocation — THEN report MATCH. (Cheap: one extra TU compile per worker; converts ~50% real-TU attrition to near-0.)

The memcpy-builtin→CALL fix (extends the T1 class, byte-proven func_8017B614): a small fixed-size mem-copy written as memcpy(x,y,8) inlines to lwl/lwr/swl/swr in ISOLATION but lowers to a jal memcpy CALL in any TU that declares extern memcpy (a sibling triggers conflicting types for built-in function memcpy, disabling the builtin TU-wide) → byte-drift. FIX: typedef struct { u8 b[8]; } Blk8; *(Blk8*)dst = *(Blk8*)src; — struct-assign routes through emit_block_move (identical lwl/lwr/swl/swr bytes) but references NO memcpy SYMBOL, so it is immune to the builtin-disable. Mirrors the codebase's own family idiom (matched sibling func_8017B368 uses (*(SV4*)&D_x)=loc;). Verify with cc1 -fno-builtin: struct-assign still emits lwl/lwr; the memcpy draft emits jal memcpy.

Five lever refinements (wave-2 journal wf_dbadb86a-6b7):

  1. register int NOT register short for a pin whose value is already sign-extended (an lh result) — register int g __asm__("$6"); g = *(short*)p; pins to $a2 with no sll/sra penalty; register short re-adds the extend (func_8017EF50).
  2. Never density-dead-read a pseudo that is LIVE ACROSS A BLOCK — the __asm__("":: "r"(v)) adds a real instruction (count+1) and backfires; instead RESTRUCTURE the pseudo away (compute fresh at each use) (func_80136824).
  3. When density fails, use STATEMENT-BLOCK reordering for $v0/$v1 birth order — group the var you want in $v0 so it is first-born + dense; density dead-reads that must keep a var live past its consuming sll produce the wrong schedule (func_80164930).
  4. Birthing-boost coupling: a single-set const-load (li $v1,0x40) sinks to just before its EARLIEST-scheduled consumer, not to its C statement position — to move the load, reorder the CONSUMER store-block, not the assignment (func_80168828).
  5. for-init LUID ordering controls the delay slot — for (i=0, lim=0x19, p=P; i<N; i++) makes i=0 win the beqz delay slot and emits lim before the pointer lui/addiu; a plain pre-loop int lim=…; captures the delay slot instead (func_80164930).

Wave-2 economics: 14 workers ≈ 2.47 M tok → 4 banked + 399 swept = ~403 fleet fns. Bank-rate 4/9 iso-MATCH — LOWER than wave 1 (real-TU attrition), fixable by the real-TU-verify rule above. The 5 nears (func_80134A74 71→16, func_80133AB0 →28 aligned, func_8012FCC4 beqz/jal delay-swap, func_80185BA4 65, func_801670E4 70 "irreducible") are permuter-ILS fuel / G4 candidates.

§42b addendum — wave 3 (Max, 2026-07-10c): THE STALE-OBJECT GATE TRAP + the read-global &-cast drift + fix

THE #1 METHODOLOGY BUG (invalidated wave-2's "iso-drift" labels; fix ALL gates). A per-function real-TU check that does make build BINARY=<ov> >/dev/null 2>&1 and then runs asm-differ -o <fn> without checking the build exit code and without removing the split .o first will diff a STALE object whenever the build FAILS — reporting a phantom score 0 / "MATCH" for a draft that never compiled. Measured this wave: three wave-2 "iso-drift" fns (func_8016C188, func_80168828, func_80136824) read as score-0 on the first pass, then NOCOMPILE on a forced-clean pass (rm build/src/<ov>/<split>.o + exit-code check). Root cause of the false score: the stale .o from a prior good build survives the failed compile, and asm-differ -o happily diffs it. This is almost certainly why wave 2 mis-classified 5 fns as "iso-MATCH → drift" — several likely never compiled in the real TU at all. MANDATORY gate shape (now in .run/crack3/diff.sh): git checkout <split> → splice → rm build/src/<ov>/<split>.o → make build BINARY=<ov> and assert exit 0 → sha1sum the built binary vs config/check.<ov>.sha (the real whole-binary arbiter) → only THEN asm-differ -o for the diff view. Never trust a piped make build you didn't exit-check. (Compounds with the §42a --out gotcha — both produce false PASS/FAIL on overlays.)

The *(T*)&D_sym read-global drift (a canon_sig_reconcile defect) + the fix — byte-proven on func_80164930. canon_sig_reconcile rewrites an ambient-conflicting global access as *(u16*)&D_sym (cast-at-use, to dodge a type conflict). For a write-only global this is byte-neutral (lui at,%hi; sh v,%lo(at) — direct addressing). For a read (esp. read-modify-write) global it DRIFTS: &D_sym forces gcc to materialize the FULL address into a held register (lui a0,%hi; addiu a0,a0,%lo; lhu v0,0(a0)) instead of the target's direct lui v0,%hi; lhu v0,%lo(D_sym)(v0) — and it reuses that held reg for the store, shifting the whole schedule. The wall: the target read needs lhu (u16) but the ambient TU decl is s16; a block-scoped extern unsigned short D_sym inside the fn is a hard conflicting types ERROR in gcc-2.7.2 (cc1 exit 33, NOT a warning — signed/unsigned short mismatch). The fix: flip the file-scope decl to the exact type (extern s16 D_8018971C; → extern u16 D_8018971C;) — byte-neutral when the only other referencer is store-only (func_801647A4 stores = 0x80 → sh either way) — and reference the global directly (no *(T*)&). Result: whole-overlay d19c9580 BYTE-IDENTICAL, func_801647A4 unaffected. General rule for drafters/reconcile: a read global that needs a specific load width (lhu/lh) must be a direct-typed lvalue at file scope, never *(T*)&sym; align the whole TU on one type rather than casting at use. Sweep caveat: the file-scope-decl flip is per-TU, so family_sweep --reconcile must also flip each sibling's decl (or the sibling's caller must be an unmatched stub with no conflicting decl) — else siblings NOCOMPILE like the frame-pad class (§42 lever 3).

Wave-3 consequence: the wave-2 uc2_gate_* drafts are not reliable seeds — several NOCOMPILE (unreconciled callee externs conflicting with the TU canonical-sig layer, e.g. conflicting types for func_80015954) and the "iso-MATCH" labels were stale-object phantoms. Wave-3 targets must be re-reconciled + rigorously rebuilt per fn (the .run/crack3/ harness), not gated from the wave-2 artifacts. Confirmed banks this wave: func_80164930 (the read-global fix above).

§42c addendum — wave 3 (Max orchestrator + CORRECTED Ultracode fan-out, 2026-07-10c): the real-TU-faithful parallel harness (rtu_match) + 7/9 crack, ZERO iso-drift

THE TOOL that makes a reliable crack fan-out possible — tools/rtu_match.py (real-TU-faithful, parallel-safe). Wave-2 workers self-checked in ISOLATION (match_one), blind to in-TU decl/global-type/memcpy-builtin drift, so their iso-MATCHes drifted at the whole-binary gate (~50% attrition). FIX: compile the WHOLE split .c with the candidate spliced and INCLUDE_ASM neutralized (-DINCLUDE_ASM(a,b)= + -Isrc/<source> for the relative ../shared include) → masked-diff the fn. No asm/, no shared overlay build → many workers run in PARALLEL in per-fn temp dirs. Because gcc-2.7.2 -O2 compiles each global fn independently, the neutralized whole-TU compile reproduces the exact ambient context, so a rtu_match MATCH HOLDS at the whole-binary gate. Measured: 7 real-TU MATCHes → 7/7 banked byte-identical (individually + combined d19c9580), ZERO drift (vs wave-2's ~50%). Corrected fan-out = 9 xHigh workers ~1.27 M tok → 7 MATCH + 2 DIFF(→permuter). This is the reusable engine for the phase tail: reconcile-first

  • rtu_match-gated + the levers below. Supports //@EDIT old||new file-scope pre-edits.

DURABLE LEVERS from the 7 cracks (all rtu_match-byte-gated):

  1. Callee-ARITY unblocks a delay-slot "steal" (func_8012FCC4 — the "irreducible" that wasn't). A spurious extra register arg on a callee that is LIVE ACROSS the call blocks gcc reorg fill_slots_from_thread from sharing a downstream constant into a branch delay slot (reads as an irreducible ~3-off beqz/jal delay swap). Before conceding a delay-slot residual as irreducible, RE-DERIVE THE CALLEE ARITY FROM THE ASM: drop the bogus arg → the target schedule falls out of stock reorg, no barrier/pin/mutation.
  2. Pointer-holding global via *(T**)&sym → lui;lw %lo(load ptr)+lh off(ptr)(deref) (func_80136824). A file-scope extern u8 D_x that actually HOLDS a pointer: read as (*(s16**)&D_x)[i]. Byte-neutral vs the u8 decl.
  3. Array-decay CSE (func_80136824, the load-bearing extra): reading extern s32 D_x[] (ARRAY) as *(s16**)&D_x or D_x[0] makes gcc CSE the decayed BASE addr into a held reg (lui;addiu;lw 0(reg) reused) vs the target's per-use direct lui;lw %lo(sym). FIX: //@EDIT extern s32 D_x[];||extern s16 *D_x; (flip to a SCALAR POINTER). (Scalar u8 symbols fold %lo fine; only the array decays.)
  4. §17 zero-reg-copy x + zr for a delay-slot-SAFE live-range copy (func_80134A74): register u32 zr __asm__("$0"); y = x + zr; copies a pseudo with NO __asm__ op, so it CAN land in a branch delay slot (an __asm__ volatile copy cannot, and disrupts delay-fill → +1 ins). Use to hoist a masked value into a bnez delay slot / before a range-check.
  5. void→s32 flip for a discarded-return callee decl (func_8014DD8C): when a fn truly returns a value (addiu $v0,1) but a shared DEFINE_func_* macro in engine_core.h declares it extern void and the caller DISCARDS the return, flip that macro-internal extern void→s32 (byte-neutral fleet-wide; stops the void-decl DCE'ing the return). R22-confirm fleet neutrality. Precedent: §20 func_8014EE14.
  6. register-arg capture into a NORMAL pseudo for a callee-saved param (func_80168828, SWEEP-SAFE, no //@EDIT): to force incoming $a0 into a callee-saved reg (target addu $s1,$a0,$zero): declare the fn (void), then register s32 a0v __asm__("$4"); s32 param_1 = a0v;. The copy into a normal pseudo (live across calls) gets a callee-saved home. A direct register ... __asm__("$4") leaves it in call-clobbered $a0 (wrong frame → 100-off).
  7. Free-floating load temp for a scheduler hoist (func_8016C188): extracting an arg-load into its own statement (s32 t34 = *(s32*)(s1+0x34);) lets the scheduler hoist it early to fill a load-delay slot (vs pinned late by the call) — closed 63 mismatches at once.

block-extern-vs-definition is an ERROR, not a warning (func_80133AB0/8014DD8C): in gcc-2.7.2 a block-scope extern whose sig conflicts with the function's own DEFINITION hard-errors (cc1 exit 33). A TU that forward-decls the fn with a wrong/loose sig must be reconciled (match the def's sig; //@EDIT the caller decl when it discards the return or the arg is already the right width in-register). The dominant "reconcile-first" wall for the F-band exemplars.

The 2 DIFFs (permuter tier), seeds in .run/crack3/wave3/: func_801670E4 (70→48; block birth-order levers landed, "assign p/i late" shape from sibling func_8016A290) and func_80185BA4 (structurally 177/177, pure scheduler + caller-saved temp-numbering residual, no responsive C lever) — decomp-permuter fuel.

§42d addendum — wave 4 (rtu_match fan-out over the mapped frontier, 2026-07-10c): 24/26 MATCH, +5 durable levers

META-YIELD (validates the frontier-map "reconcile-first" bucket): a 26-worker rtu_match fan-out over the tractable-band draftable exemplars (the frontier map, docs/sunset/phase25-frontier-map.md) landed 24/26 MATCH (20 banked byte-identical, 2 permuter, 4 needing per-fn link/drift fixes). Confirmed: for the F-band exemplars, reconcile-first is often the WHOLE fix — several (func_80131B14) were byte-correct in the body and only their TU-canonical decl layer conflicted; strip/align the decls → MATCH with no schedule/regalloc grind. The engine = reconcile-first + rtu_match-gated + the §42/§42c/§42d levers.

NEW / generalized durable levers:

  1. Return-type flip goes BOTH ways (generalizes §42c #5). If a fn genuinely RETURNS a value but a discarding caller's decl says void, flip the decl void→s32/short (func_8014FE60, func_8016CF04) — the void decl DCE's the return computation. INVERSELY (func_8016DF5C): if a fn is effectively VOID (bare return;) but the draft declares it s32, flip s32→void — an s32 return keeps $v0 LIVE at the epilogue, blocking reorg's eager fall-through delay-slot steal (a single-instruction cascade). Read the asm: does $v0 carry a value out?
  2. Address-recompute-vs-CACHE — the unifying read-global rule (subsumes §42b read-global + §42c array-decay CSE). Taking &D_sym (via *(T*)&sym or a cached local ptr) makes gcc materialize the symbol address into ONE reg (lui;addiu) and CSE it across all uses → FEWER lui than a target that recomputes %hi/%lo per reference (direct global access). When the target shows a fresh lui $scratch,%hi; op %lo(sym) at EACH use, declare the global directly at the right type/scope (extern volatile unsigned short D_x; etc.) and reference it plainly — never &sym. When the target instead HOLDS the address in a reg across uses, cache it (T* p = ...;). Same root cause behind func_80164930, func_80136824, func_801418F8, func_80136334.
  3. The full-inline-asm TRAMPOLINE idiom (func_8014FBC0, the 22×1996 family). The scratchpad-stack-switch trampolines (func_8014F468/F6F4/FA04/FCFC/…) are hand-asm: the callee symbol AND the global live INSIDE the __asm__ string (%hi/%lo escaped as %%), so ZERO C externs are declared → nothing to reconcile. maspsx 2.56 auto-fills the jal delay slot with a nop (do NOT write an explicit post-jal nop). family_remap must substitute the callee/global symbols INSIDE the inline-asm string, not as C extern lines (the x134 sweep of an inline-asm family needs this — else siblings drop).
  4. memcpy→struct-assign, re-confirmed at scale (func_8017B238, §42a): the TU's file-scope extern memcpy disables the builtin → 8-byte moves lower to CALLs; model on the matched sibling's align-1 typedef struct{u8 b[8];} struct-assign (routes emit_block_move, zero memcpy ref). Pair with the register u8* __asm__("$16") + in-place re-tie pin to hold the src pointer across the moves.
  5. phantom-frame induction (func_80136334, §42-refined): a value live across BOTH arms of a branch makes gcc reserve a spill slot the no-frame twin lacks — induce the frame with s32 frame_pad[2]; (void)&frame_pad;.

Wave-4 economics: 26 workers ~2.36 M tok → 24 MATCH → 20 banked + swept ×134. Bank-rate 20/24 at the whole-binary gate (4 hit rtu-blind link-walls / drift — rtu_match is .text-only, §41b/§42b caveat; those need the whole-binary/link gate). The 2 permuter DIFFs: func_8012E364 (c=4), func_801549F8 (c=3, jtbl delay-slot).

§42e — propagating a CRACK ×134: the def-finder bug + the byte-drift residual (the "remap-fail" misdiagnosis)

Cracked F-band exemplars don't all propagate ×134 through family_sweep --reconcile — waves 3/4 dropped ~1,200 siblings. Diagnosis (a two-layer story; both matter for future sweeps):

  1. THE def-finder BUG (canon_sig_reconcile, fixed) — mislabeled "remap-fail". family_sweep's reconcile_remap returns None on ANY failure and the caller counts it as "remap-fail", but family_remap itself SUCCEEDS (verify with tools/family_remap.py --addr … --from … --to … — it pairs the symbols fine). The real None came from canon_sig_reconcile.reconcile raising "no definition of func_X found in draft": its def-finder regex required a leading \n (\n(<type> fn(...)){), but a raw draft whose //@EDIT header lines were stripped has the fn definition on line 1 → no match. FIX: \n → (?:^|\n) (also match a def at draft start). This alone fully recovered func_8014FE60 (133/133 siblings) once paired with its shared-header return-type flip.
  2. THE byte-drift residual (the genuine --edit-remap work). Families cracked with a file-scope //@EDIT (the array-decay pointer flip §42c#3, the no-proto flip) or a shared-header return-type flip (§42d#1) reconcile per sibling but BYTE-DRIFT, because those edits live OUTSIDE the function body that family_sweep remaps: the pointer/ no-proto //@EDIT targets per-overlay decls (must be symbol-remapped + applied per sibling), and the return-type flip targets the ONE shared engine_core.h macro (apply once, globally — like func_8016CF04/8014FE60). family_sweep carries neither. So a --edit-remap = {per-sibling: remap the exemplar's //@EDIT symbols and apply to the sibling split; once: apply any shared-header flip globally} recovers this class. func_8016DF5C/80136334/8013D9B0/80156044 are the backlog exemplars.

Forward rule (frontier-map leverage realism): a crack's ×134 is only free if its body is self-contained (no //@EDIT, no shared-header flip). Before counting a cracked family's ×134, note whether it carries out-of-body edits; if so it's exemplar+--edit-remap, not exemplar×134-free. LESSON (R14): trace a tool's real exception, not its summary label — "remap-fail" was a swallowed reconcile-throw two layers down.

BUILT + measured (Phase-25 task B, 2026-07-10): family_sweep --edit-remap MANIFEST (JSON: per family, edits = split-scope //@EDIT old||new in EXEMPLAR symbols, symbol-remapped per sibling via family_remap.symbol_map; ec_edits = once-global engine_core.h flips, byte-neutral). Per sibling it applies the remapped edits to the split

  • stages the family_remap body + gates the (overlay,split) group via plain harvest_verify. Orphaned edits from a failed sibling are byte-neutral (R22-checked). Manifest at .run/edit_remap_manifest.json.

THE CC1-CRASH WALL (the decisive R14 finding — only 2 of the 6 backlog families recovered): the whole-binary byte-gate is the sole arbiter, and it revealed that out-of-body-edit families split into two classes:

  • array-decay pointer-flip (extern s32 D_x[];→extern s16 *D_x;, a per-overlay symbol) — recovers cleanly ×134. func_80136824 + func_80136334 → 266/266 siblings banked byte-identical, 0 failed (2×133). Light register pressure; family_remap body + the remapped split-edit is sufficient (no reconcile, no extern injection).
  • register-pin-heavy (GTE 20-pin bodies func_8013D9B0/func_8016DF5C; an exotic register int zr __asm__("$0") zero-register pin func_80133AB0; the inline-asm trampoline func_80156044) — the original verdict here was "cc1-2.7.2 SIGABRTs compiling the sibling TU… ov077-TU-context-specific… NOT mechanically ×134-recoverable, stay exemplar-only (×1)." ⚠️ REFUTED — Phase-27 (Fable5 characterization, .run/giants/pin_crash_sigabrt.md). See the corrected verdict below; the pin-×1 ceiling was a STAGING-TOOL artefact, not a compiler wall, and it is fixed.

§42e-CORRECTION — the "pin-crash wall" is the extract_unit macro-drop, not the pins (Phase-27 T5 + SIGABRT characterization, 2026-07-15). The SIGABRT is real and now exactly located — gcc-2.7.2/sched.c:2725, create_reg_dead_note(): if (dead_notes == 0) abort();, a sched1 REG_DEAD-note conservation bug (flow places the pinned reg's death on the fall-through path; sched1's clobber-aware per-block recount demands a death note for a use-after-call in the CALL's block, whose harvested note-pool is empty → abort; backtrace abort ← create_reg_dead_note ← attach_deaths ← attach_deaths_insn ← schedule_block). But it was TRIGGERED by family_remap.extract_unit dropping the body's file-scope #define dependencies, not by any TU context:

  • Of the 4 "crash-walled" families only func_8013D9B0 ever genuinely SIGABRTed — and only because the dropped gte_* macros became implicit-declaration CALLS, putting its caller-saved pins into the fatal shape. The other three were exit-33 plumbing (a dropped multi-line typedef func_80133AB0; a dropped single-line typedef func_8016DF5C; the one-line-wrapper false-positive func_80156044) misfiled as crashes because the era one-big-split gate shared a TU compile with d9b0 and reported its Error-134 for all of them (the R14 lesson, recursed: one exit code folded three distinct failures into a phantom "universal SIGABRT").
  • Properly staged, all four compile CLEAN in sibling TUs: func_80133AB0 133/133 (today AND at the era commit), func_8013D9B0 133/133 (today, fleet-swept), df5c + x6044 spot-proven. T5's _carry_macros fixes (a) the #define drop; (b) multi-line typedefs route through the engine_types.h lift; (c) the one-line-wrapper false-positive is already fixed by the current comment-strip guard; (d) per-sibling decl flips are --edit-remap.
  • The fatal-pin predicate (checkable at DRAFT time, probe-matrix-proven): FATAL = a register T x __asm__("$N") pin where $N is caller-saved ($2–$15, $24, $25), the value is used after a CALL_INSN, and the post-call use has a branch-dependent use-then-conditionally-set shape. SAFE = callee-saved pins ($16–$23, $30) in any shape; caller-saved pins whose live range never crosses a call; use-only or single-level-conditional shapes; $0 pins. (12-line minimal repro + probe matrix in pin_crash_sigabrt.md; -fno-schedule-insns/-O1 suppresses it — a safe "is this the dead-notes bug?" probe, useless for matching.) So ov077 banked these pins precisely because, in its TU (macros present), no pin crossed a real call.
  • DIAGNOSTIC (R14, corrected): exit 134 + the create_reg_dead_note backtrace = this bug, always; exit 33 = ordinary decl/typedef plumbing. Distinguish them (T4 surfaces cc1 stderr; don't fold both into "cc1-crash").
  • Takeaway: the pin-×1 ceiling does NOT exist — P31's pin-propagation route is OPEN. Route pin-heavy families back to the mechanical family_sweep harvest (macros now carried); byte-identity per sibling is the byte-gate's question, but cc1-crash is no longer a barrier. (Array-decay pointer-flip families were never affected and still recover cleanly ×134.)