4.5 KiB
§137 — REGALLOC-PERM is a TWO-COMPILE ARITHMETIC PROBLEM, not a permuter job
Symptom key: match_one reports REGALLOC-PERM — a clean swap of two registers, everything
else byte-identical ($t8/$t9, $s0/$s1, …). Historically this class went to the permuter or was
ledgered "unsteerable". It is neither: gcc-2.7.2 decides it by an arithmetic priority you can read
out of the compiler's own dumps and then target deliberately.
The mechanism. global.c:allocno_compare ranks by
pri = (int)( floor_log2(R) * R / L * 1e4 * size )
where R = times the register is used and L = the live-range length in insns. Both come out of cc1's own dumps:
cc1 -dl -dg … # t.i.lreg : "Register N used R times across L insns"
# t.i.greg : ";; Register dispositions"
The method (measured on func_801833F0, 328 ins):
- Compile with
-dl -dgand read R and L for BOTH contenders and their ranked neighbours. - Evaluate
prifor each ⇒ you get the exact admissible priority WINDOW that flips the pair. Here the two contenders were ONE unit apart —vtx1297 vs the giv 1296 — with window (1228, 1296). - R and L are both forced by the emitted code, so source reordering does not move them
(measured twice: moving a load's source position changed nothing, because
Lis recomputed post-sched1). This is why source-level levers are a dead end for this class. - Place a zero-byte
__asm__ __volatile__("" :: "r"(v))(§17/§21 primitive) at the source point that makesLland inside the window. Five probed placements gave L = 190/194/196/219/233/258; only L=219 → pri 1232 worked.
Why this matters: it converts a class we have been routing to the permuter (a random search that this session banked 0 from) into a deterministic two-compile calculation. Try it before the permuter on any clean 2-register swap.
(Companion finding, same wave, func_8017BFEC: a 5-instruction head rotation that was invariant
across every legal statement permutation — 8 head orderings × 2 store forms all identical — is the
diagnostic signature of priority / birthing-boost, not LUID order. Fix: make the pseudo
single-set by splitting a variable reused in two blocks into two locals (sched.md §1.7/§S2,
sched.c:2469/2490). An earlier agent had ledgered this exact function DIFF/7 "not steerable from
this decomposition" after ~70 source variants — refuted. Invariance under source permutation is
information: it says the lever is not in the source order.)
§137a — A gate verdict has a TIMESTAMP; re-check it against the draft's mtime
A redraft agent this wave found its handed-over "genuine byte-DIFF" was stale by 28 minutes: the
gate ran at 14:53, the draft was rewritten at 15:22 by an earlier lane whose result had been cached,
and it was never re-gated. The agent ran match_one on the file as it stood (D3), got MATCH, and
spent its budget proving the file would BANK instead of re-deriving a function that was already done.
The rule: before acting on any failure verdict — yours or a prior wave's — compare the verdict's time against the draft file's mtime. If the file is newer, re-verify before redrafting. This is the same family as the Phase-29 finding that 77% of stored drafts had decayed, but the opposite direction: a stored verdict can be stale because the draft got better, not just worse.
Two offline oracles that agent built, both worth reusing (they close the gap match_one's
relocation mask leaves open, without running make):
- Full relocation RESOLVE — resolve your
.o's relocations against the target.s(D_<addr>/func_<addr>symbol values, HI16/LO16 paired addends) and compare all words. This sees the class the mask hides: a wrongjaltarget or a wrong%hi/%losymbol or addend. Result there: 0/227 diffs including every relocation. - Collateral check — objdump the whole TU with and without the splice and require every OTHER
function to emit identical words, allowing only
.text-relativejaddends shifted by exactly your function's size (0x38C there). That catches file-scope declaration damage the target function's own bytes cannot show.
Symptom line for the index: "match_one/rtu_match say MATCH but the whole-overlay SHA still
DIFFs" has exactly three causes — a stale verdict, a wrong jal/%hi/%lo target the mask hides,
or collateral from file-scope decls — and the two oracles above discriminate all three offline.