9.8 KiB
§52 — The WALKER-FAMILY skeleton: 6 regalloc-order levers + a deeply-characterized intrinsic wall (func_80178004, 165 ins ×134; Phase 26, Fable5, 2026-07-15)
func_80178004 is the exemplar of the regalloc-order residual class (12 reach-134 siblings share it: the
9 close=0 + 3 close-21+ cores). A ~93-min / 477k-token Fable5 pass drove a PIN-FREE draft to
structure-exact (163 vs 165 ins); residual = pure register identity — byte-verified (match_one 126/165
masked; the 126 is dominated by one $s0↔$s2 swap rippling ~90 lines). It did not byte-match: the wall
reduces to three compiler-internal integers and is (probably) intrinsic to vanilla gcc-2.7.2.
Correction (R14): the historic "pinned MATCH" was a myth — the pinned seed was NEVER a match (best
historic permuter score 5, pinned); this is the deepest state the function has reached. Artifacts + gdb
oracles under .run/fable_80178004/ (repro runorc.sh); Fable5-derived, headline byte-verified.
The 6 levers (the "walker-family skeleton" — apply to the 12 siblings; levers 1-5 retire ~half the residual)
- Mutated-parameter pointer. Declare the walking pointer AS the mutated parameter (
u32 *p=arg0, thenp += …). Anyp = (u32*)a0COPY leaves the initial value live in the param pseudo → cse1 rebases every store block onto constant offsets and DELETES the pointer-walk. The single biggest lever; transfers to every walker-style function. - No derived-base variable. Never declare the second pointer — write
p[1..3]directly and let loop.c mint a combined DEST_ADDR giv of the biv (anchor = last-recorded giv → the -8/-4/0 offsets; preheader init reads the biv reg). A source-levelqbiv makes loop.c reduce the (q-4,q-8) pair into an EXTRA pointer (loop.c:3824 worthwhile test). - In-loop constant remat. A constant the target recomputes in-loop must be a user variable assigned in
BOTH if/else arms (
n_times_set==2fails scan_loop's movable gate, loop.c:698-712 → stays in-loop). A bare literal is hoisted. - Split the OR across two statements.
t = …|0x4000; p[3] = t|0x6d160000;beats tree-level constant reassociation → in-loopori+ a hoisted lui-only temp. - Break biv-recognition. Interpose one statement between
n = i+1andi = n(basic_induction_var follows a copy only to the immediately-previous insn, loop.c:4862) → the target's literal counter survives instead of a<<16giv. - Diagnose allocation walls with gdb-on-cc1 FIRST.
peek2.gdb(allocno_order/find_reg) + a 3-integer oracle (reg_n_deaths/reg_n_refs) tells you in minutes whether a register identity is even reachable — before grinding C.
Why the wall is (probably) intrinsic
The walker is a block-local 1-death qty → local-alloc runs first and hands it the first free callee-saved
$s0 (local-alloc.c:472/2103; nothing can pre-occupy s0/s1 for a call-crossing qty). Force it global
(deaths≥2) and its priority floor_log2(44)*44/103 ≈ 21000 dominates → allocated first → pass-1 first-fit
$s0 again (global.c:924, callee prefs stripped for call-crossers). The target needs pri < 2400 ⇒ effective
refs ≤ ~10-12, but REG_N_REFS counts RTL mentions, fixed at 44 by the bytes, and every legal construct
only pushes it UP (each ruled out with file:line). One untested lever: instrument qty_n_refs (local-alloc
SUMS at tying, local-alloc.c:1869) vs flow's per-reg REG_N_REFS with peek2.gdb on tied-copy chains — if a
tie shape yields a low-ref global view of an s2-window value, the wall falls; else it's the ×1-pinned +
whole-binary-gate route.
Flywheel note (R16): even walled, this pass paid off — a cheap-Opus wave applying levers 1-5 should crack the siblings that are NOT at the intrinsic wall. Fable5 DISCOVERS the skeleton; cheap-Opus APPLIES it.
§52a — The regalloc sibling wave: new levers + two new wall classes (cheap-Opus applying §52, 2026-07-15)
Ran §52 as a 6-agent Opus wave over the regalloc-order reach-134 cluster: 2/6 banked ×134 (func_80171FFC,
func_801775E0 → 268 instances, R22 136/136), the other 4 = precisely-characterized walls. Banks and walls
each yielded a byte-verified, reusable lever:
New banking levers (each verified by a whole-binary bank):
- Pass the callee its real arguments —
f((s32)a0,(s32)a1), NOT the void-cast no-arg trick((T(*)(void))f)(). When params are saved to callee-saved regs before the first call, the void-cast form STRIPS the param pseudos' arg refs → shuffles the$s0/$s1/$s2order (RC-10 density). Passing the args naturally BOTH suppresses arg-setup moves (params already in place) AND preserves the density order. (func_80171FFC.) - Recompute a store-base in BOTH if/else arms (the §52-lever-3 analogue for a base pointer): keeps the
recomputed base live across the branch-merge so subsequent
p[k]address off it, not gcc address-CSE onto a still-live keeper base. (func_801775E0.) - Copy-chain direction: use the incoming param DIRECTLY as the persistent keeper (
keep = param+off) with a working copyw = param; gcc savesparam→$sK, chains$sJ=$sK. An explicit keeper var reverses it. (func_801775E0.) pp-declaration position drives the prologue schedule (RC-1/K1): declare a param-copy pseudo AFTER the one you want saved first — sched1 emits in pseudo-creation order. (func_801775E0.)- A
const/RTX_UNCHANGING_Ppre-call load frees a sched2 prologue save-order tie (RC-3):f(*(const s32*)(a0+off))— sched.c:true_dependence drops the load↔store deps sosw $s0/sw $rastop readying simultaneously → the descending-regno save tie staggers right. BOUNDARY: it can OVER-free (the load then hoists above the saves), so it fixes save-order but not always the whole function. (func_80131A34.)
Two NEW intrinsic-wall classes (byte-characterized, P9 — distinct from §52's flagship $s0 local-alloc wall):
- Caller-saved priority-first-fit wall. A long-lived block-local value (
$v0/$v1/$a0scratch) stored many times has LOWqty_comparepriority (floor_log2(refs)·refs·size/(death−birth)) → loses the low-reg first-fit to its SHORT-lived competitor. §31-B in-out-asm and §47 live-length split TIES only, never a priority GAP, and the competitor can't be lengthened without deleting an instruction the target keeps. Needs pins → ×1-only. (func_80169228, bounded to 3 register identities.) - Non-coalescing delay-slot copy wall. A value computed in
$vX, tested, then copied to$vYto survive a clobber in the branch delay slot needs two non-coalescing equal-valued pseudos. Every pure-Cy=xis destroyed by cse.ccanon_reg/make_regs_eqvhead-promotion or global-alloc coalescing; the only preserving forms are#APPasm (blocks reorg's delay-slot fill) or a$0-add (SIGABRTs cc1 in sibling TUs, §42e). Intrinsic pin-free/sweep-safe wall. (func_80177AD4, 1 instruction.)
Wave economics: ~⅓ of a fully-walled cluster cracks pin-free by applying the idiom; the rest wall on a small set of distinct, now-named mechanisms. Cracks bank ×134; walls become permuter seeds or documented dead-ends.
§52b — Sibling wave 2: more de-pin levers, a third wall class, and the match_one→whole-binary gap at scale (2026-07-15)
A second 6-agent Opus wave (armed with §52a) over the close=0 regalloc cluster: 3/6 banked ×134
(func_801379FC, func_801497A8, func_801495C4), 2 whole-binary-near, 1 new wall. Additional VERIFIED levers
(each proven by a whole-binary ×1 bank):
- Per-loop pseudos for a register role-swap — when the target uses pointer=$s0/index=$s2 in one loop and the
swapped roles in another, a single shared C var can't (one hard reg each). Declare SEPARATE per-loop locals
(
s32 p; s32 idx;inside each block); the swap falls out of K2 density (the ref-heavier value wins the low callee-saved per block). (func_801379FC.) - The RC-7 "second-set" dial —
u8 *s = SYM; __asm__("" : "=r"(s) : "0"(s));makesreg_n_sets(s)==2, failingupdate_equiv_regs' single-set gate → NOREG_EQUIV→ the value is NOT rematerialized at its use → it must hold a callee-saved reg across calls (matching a target that keeps a base in$s1). Zero bytes, sweep-safe (generic"=r"/"0", no$N— distinct from the §42e$0-add). (func_801497A8.) - Value-barriers dissolve the CSE-stack-address-common wall — when correct stack-slot order (RC-1 decl order)
forces a
&buf(sp+off) to be CSE-commoned across two calls into a call-crossing pseudo that steals a callee-saved reg, wrap each&bufuse in__asm__("" : "=r"(m) : "0"(&buf))so cse can't fold them → each rematerialized fresh at its call. STEERABLE (banked), not intrinsic. (func_801495C4; itsfunc_8014964Ctemplate used a register pin + 5 barriers — the pin was superfluous.)
A third intrinsic wall class (byte-characterized, P9): the symbol-address-base "wins-low-needs-high" wall
(dual of §52a's caller-saved-priority wall). A 2-instruction symbol-address base feeding its own N loads is the
densest block-local pseudo → first-fits the LOW reg ($v0), but the target needs it HIGH ($a1); making it
low-priority is structurally impossible (a base can't out-rank the loads it feeds), and the movstri form that
would place it high triggers the §52-flagship (plus $fp const) local-alloc theft (update_equiv_regs can't
rematerialize a non-CONSTANT_P source). Only a register pin resolves both → ×1-only. (func_8012B4B8, close=15.)
Process finding — the match_one→whole-binary gap at wave scale: ~half of the agents' match_one close=0 drafts do NOT bank whole-binary (isolated reloc-masked compile overstates; A10). The whole-binary gate is the sole arbiter — a match_one MATCH is a CANDIDATE, not a bank; budget the gate cycles.