phase-36: T7 toolify a2 — generators R16 (constant holder inlined) and R17 (constant-run split), the directed form of a move R9 reached only by luck

- R16 writes a local whose only assignment is one integer literal at every use and deletes it. R6 stops at a temp read
  exactly once, so a holder read four times was invisible to the search and its whole family with it. Deleting it is
  byte-neutral alone but removes a quantity from the block, which is what lets the next move reach the allocator.
- R17 splits a run of consecutive same-literal assignments by moving the nearest differently-valued one into it, at each
  interior split point. find_free_reg's live-range scan (local-alloc.c:2109-2110): while the two constants' ranges are
  disjoint they share a caller-saved register; splitting makes the first live across the second and it takes another colour.
- known-true check: on the seed that keeps func_80168828's semantically-forced $4 pin, R16 then R17 reaches
  score 0 (OTHER; mine 108 ins, target 108) — MATCH at three of the six offered split points, in ten compiles where the
  blind search needed 2,271.
- the engine selftest's caller-saved assertion is now the ordering invariant (every targeted lever before every blind
  family) rather than a fixed window widened once per new generator.
- delever --selftest OK (4 new controls); delever_search --selftest OK; tool_census --check OK; SETUP row (R21).
This commit is contained in:
Drew T
2026-09-10 01:27:20 -06:00
parent 9c5ca46a2a
commit 975850ff84
5 changed files with 359 additions and 25 deletions
+171 -3
View File
@@ -2212,7 +2212,117 @@ def sink_merges(text, tu, fn, d_):
return out
ALL_FAMILIES = ("R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10", "R12", "R13", "R14", "R15")
INT_LIT = re.compile(r"^\s*(?:\(\s*[A-Za-z_][\w \t*]*\)\s*)?(0[xX][0-9A-Fa-f]+|\d+)\s*$")
def constant_holders(text, tu, fn, d_):
"""[(description, candidate text)] — R16: a local whose ONLY assignment is one integer literal, written at every use
and its declaration removed. R6 stops at a temp read exactly ONCE (the classic def-with-one-use); a constant holder is
read many times and R6 never offered it, so the whole family was invisible to the search.
T7 agent a2's crack of func_80168828 (2026-09-10): its `$3` pin held `0x40` and was read as the RHS of four stores.
Deleting the variable is byte-neutral BY ITSELF — the pin was never doing the work — but it removes a quantity from
the block, which is what lets the next move reach the allocator (`qty_compare`, `local-alloc.c:1579-1595`, through the
unrolled switch at `:1485-1512`). A pinned local holding one literal is a CONSTANT-HOLDER, not a register lever, and
the readable spelling of a constant is the constant."""
lines = text.split("\n")
lo, hi = d_["line"], d_["end"] - 1
masked = [sc.mask_text(l) for l in lines]
occ = collections.defaultdict(list)
for i in range(lo, hi):
for m in IDENT.finditer(masked[i]):
occ[m.group(1)].append(i)
out = []
for v, where in occ.items():
asg, decl, uses = [], [], []
for i in where:
st = masked[i].strip()
m = re.match(r"^(?:[A-Za-z_][\w \t]*[\s*]\s*\*?\s*)?%s\s*=(?!=)\s*(.+);\s*$" % re.escape(v), st)
if m:
asg.append((i, m.group(1)))
elif is_decl_line(st) and "=" not in st.split(";")[0]:
decl.append(i)
else:
uses.append(i)
if len(asg) != 1 or len(decl) != 1 or not uses:
continue
if MULTI_DECL.match(masked[decl[0]]): # a shared declaration line: removing it would take the others
continue
lit = INT_LIT.match(asg[0][1])
if not lit:
continue
# every use must be a plain read — never an address-of, a member/arrow base, or another assignment's target
if any(re.search(r"&\s*%s(?![\w])|(?<![\w.>])%s\s*(?:\.|->|\[|=(?!=))" % (re.escape(v), re.escape(v)),
masked[i]) for i in uses):
continue
cand = []
for i, l in enumerate(lines):
if i == decl[0] or i == asg[0][0]:
continue
cand.append(re.sub(r"(?<![\w.>])%s(?![\w])" % re.escape(v), lit.group(1), l) if i in uses else l)
out.append((f"const-holder {v}={lit.group(1)} x{len(uses)}", "\n".join(cand)))
return out
def constant_run_splits(text, tu, fn, d_):
"""[(description, candidate text)] — R17: a run of consecutive statements assigning the SAME integer literal, split by
moving the nearest differently-valued literal assignment into it, at each split point.
T7 agent a2 (2026-09-10), the directed form of a move R9 already contains but reaches by luck: the mechanical search
needed 2,271 compiles to find this swap in func_80168828, and R17 offers it in a handful. The decision is
`find_free_reg`'s live-range scan, `local-alloc.c:2109-2110`
(`for (ins = born_index; ins < dead_index; ins++) IOR_HARD_REG_SET (used, regs_live_at[ins])`): while the two constants'
ranges are disjoint they share one caller-saved register; splitting the run makes the first live across the second, the
intervals overlap and the second takes another colour. The discriminator in the dumps is the `.lreg` line `Register N
used K times across M insns` — M grows when the split lands."""
lines = text.split("\n")
lo, hi = d_["line"], d_["end"] - 1
masked = [sc.mask_text(l) for l in lines]
def lit_of(i):
if not simple_stmt(masked[i]):
return None
m = re.match(r"^\s*[^=]+=(?!=)\s*(.+);\s*$", masked[i])
if not m:
return None
g = INT_LIT.match(m.group(1))
return g.group(1) if g else None
out = []
i = lo
while i < hi:
k = lit_of(i)
if k is None:
i += 1
continue
j = i
while j + 1 < hi and lit_of(j + 1) == k:
j += 1
if j - i + 1 < 2: # a run is two or more stores of the same literal
i = j + 1
continue
donors = [d for d in (i - 1, j + 1) if lo <= d < hi and lit_of(d) is not None and lit_of(d) != k]
for d in donors:
for cut in range(i + 1, j + 1): # every interior split point of the run
order = [x for x in range(i, j + 1)]
seq = [lines[x] for x in order]
seq.insert(cut - i, lines[d])
cand = []
for x, l in enumerate(lines):
if x == d:
continue
if x == i:
cand.extend(seq)
elif i < x <= j:
continue
else:
cand.append(l)
out.append((f"const-split @{d + 1}->{cut + 1} ({lit_of(d)} into the {k} run)", "\n".join(cand)))
i = j + 1
return out
ALL_FAMILIES = ("R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10", "R12", "R13", "R14", "R15", "R16", "R17")
RUNG_R_FAMILIES = ("R2", "R3", "R4", "R5", "R6", "R7") # the free sweep's set (R8/R9 are the search engine's until measured)
@@ -2320,6 +2430,12 @@ def recipe_candidates(text, tu, fn, names, limit=24, rng=None, cap=40, blocks=Tr
if "R15" in fam:
for desc, cand in sink_merges(text, tu, fn, d_):
out.append(("R15", desc, cand))
if "R16" in fam:
for desc, cand in constant_holders(text, tu, fn, d_):
out.append(("R16", desc, cand))
if "R17" in fam:
for desc, cand in constant_run_splits(text, tu, fn, d_):
out.append(("R17", desc, cand))
if blocks and "R7" in fam: # last: one candidate per statement, so the targeted recipes go first
for desc, cand in block_wraps(text, tu, fn, d_):
out.append(("R7", desc, cand))
@@ -2585,7 +2701,13 @@ def propagate(a):
if a.only:
sibs = [k for k in sibs if any(o in k for o in a.only)]
sibs = sibs[:a.limit] if a.limit else sibs
print(f"delever --propagate: {tu}:{fn} -> {len(sibs)} sibling(s) of class {key[:12]}", flush=True)
# A reshape may deliberately leave a lever standing (agent a2, S102: one of func_80168828's two pins is forced by the
# shared header's `(void)` declaration and has no C source, the other was a constant-holder and came off). Its siblings
# inherit exactly that shape, so the allowance is DERIVED from the exemplar's own banked text — the number of surviving
# markers — and never simply asserted: a sibling that would carry MORE levers than the exemplar is refused below.
ex_levers = src_row["after_text"].count(FAKE)
print(f"delever --propagate: {tu}:{fn} -> {len(sibs)} sibling(s) of class {key[:12]}"
+ (f"; the exemplar keeps {ex_levers} marked lever(s), so its siblings may too" if ex_levers else ""), flush=True)
if not sibs:
return 0, 0, 0 # R68: an empty work list is a refusal, not a success (a tuple like every return — the
# bare `1` here killed run g4s's process after its real propagations, S101)
@@ -2611,7 +2733,13 @@ def propagate(a):
continue
# IN PROCESS (S101): a subprocess per sibling reloaded the recipes and the includer map every time — ~1.3 s of the
# ~1.5 s each sibling cost, ≈40 min for run g3's 1,503 siblings
ok_, line = apply_body_core(stu, sfn, body, a.label, src_row.get("rung") or "R", source=f"propagate:{tu}:{fn}")
if body.count(FAKE) > ex_levers:
print(f" {stu}:{sfn}: the remap left {body.count(FAKE)} lever(s) where the exemplar keeps {ex_levers} "
f"— SKIPPED", flush=True)
bad += 1
continue
ok_, line = apply_body_core(stu, sfn, body, a.label, src_row.get("rung") or "R", source=f"propagate:{tu}:{fn}",
allow_residue=(a.allow_residue or ex_levers > 0))
print(f" {line[:200]}", flush=True)
ok += ok_
bad += not ok_
@@ -3077,6 +3205,46 @@ def selftest():
if len(ch) != 1 or len(ch[0][2]) != 3:
fail(f"if_chains must see three arms in the fixture, got {ch}")
# R16 / R17 (T7 agent a2's crack of func_80168828, 2026-09-10): a constant holder inlined, then the run it fed split.
CFIX = ("void func_80100000(void) {\n"
" s32 c40;\n"
" s32 other;\n"
"\n"
" c40 = 0x40;\n"
" other = 0x10;\n"
" st(0) = c40;\n"
" st(1) = c40;\n"
" st(2) = c40;\n"
" st(3) = other;\n"
"}")
dC = next(r for r in sc.scan_text(CFIX, "src/fx/c.c", shared_defs=None)
if r["form"] == "def" and r["name"] == "func_80100000")
h16 = constant_holders(CFIX, "src/fx/c.c", "func_80100000", dC)
if len(h16) != 2 or not any(d.startswith("const-holder c40=0x40 x3") for d, _ in h16):
fail(f"R16 must inline a 3-use constant holder, got {[d for d, _ in h16]}")
else:
c16 = next(c for d, c in h16 if d.startswith("const-holder c40"))
if "s32 c40;" in c16 or "c40 = 0x40;" in c16 or c16.count("st(0) = 0x40;") != 1:
fail(f"R16 must delete the declaration and the assignment and write the literal: {c16!r}")
# control: a holder whose value is not a literal, and one written twice, are not constant holders
if any(d.startswith("const-holder") for d, _ in
constant_holders(CFIX.replace("c40 = 0x40;", "c40 = f();"), "src/fx/c.c", "func_80100000",
next(r for r in sc.scan_text(CFIX.replace("c40 = 0x40;", "c40 = f();"), "src/fx/c.c",
shared_defs=None) if r["form"] == "def"))
if d.startswith("const-holder c40")):
fail("R16 must refuse a holder whose single assignment is not an integer literal")
# R17 on the inlined text: the 0x10 store moved into the run of three 0x40 stores, at each interior split point
c16 = next(c for d, c in h16 if d.startswith("const-holder c40"))
d17 = next(r for r in sc.scan_text(c16, "src/fx/c.c", shared_defs=None) if r["form"] == "def")
r17 = [d for d, _ in constant_run_splits(c16, "src/fx/c.c", "func_80100000", d17) if "into the 0x40 run" in d]
if len(r17) != 2:
fail(f"R17 must offer both interior split points of a three-store run, got {r17}")
# control: a run of one store has no split
if constant_run_splits(CFIX.replace(" st(1) = c40;\n st(2) = c40;\n", ""), "src/fx/c.c", "func_80100000",
next(r for r in sc.scan_text(CFIX.replace(" st(1) = c40;\n st(2) = c40;\n", ""),
"src/fx/c.c", shared_defs=None) if r["form"] == "def")):
fail("R17 must refuse a run shorter than two statements")
# the oracle's crash classification on its real message forms (R103)
if not oracle.SIGNAL_LINE.search("bash: line 1: 3845091 Done mipsel-linux-gnu-cpp ...\n 3845092 Aborted (core dumped) | tools/bin/gcc-2.7.2-psx/cc1 -quiet\n"):
fail("SIGNAL_LINE must match bash's job-status block")
@@ -85,15 +85,17 @@ FAMILIES = {
# R15 (the sink) is the arm-scoped form of the same tie: a value set in every arm of an if/else chain is a CROSS-BLOCK
# pseudo local-alloc never gives a quantity, so the arm holds two quantities and takes block_alloc's unrolled case 2;
# sinking makes it three, and case 3 falls through into case 2 and undoes its own exchange (T7 agent a1, func_80156044).
"REG-caller": ("R6", "R8", "R15", "R5", "R10", "R12", "R14", "R13", "R3", "R7", "R9", "R2", "R4"),
"REG-caller": ("R6", "R16", "R8", "R15", "R17", "R5", "R10", "R12", "R14", "R13", "R3", "R7", "R9", "R2", "R4"),
# the s-bank order is global.c's allocno_compare (ref weight x live length), declaration order only on an exact tie
"REG-callee": ("R2", "R4", "R3", "R6", "R8", "R15", "R12", "R7", "R9", "R10", "R14", "R13", "R5"),
"REG-mixed": ("R6", "R2", "R15", "R5", "R10", "R4", "R3", "R8", "R12", "R13", "R14", "R7", "R9"),
"REG-callee": ("R2", "R4", "R3", "R6", "R16", "R8", "R15", "R12", "R7", "R9", "R17", "R10", "R14", "R13", "R5"),
"REG-mixed": ("R6", "R16", "R2", "R15", "R17", "R5", "R10", "R4", "R3", "R8", "R12", "R13", "R14", "R7", "R9"),
# a copy dies to cse's canon_reg or the local-alloc tie unless its destination changes MODE (the width); an address
# pseudo lives when a pointer local is used twice; a value named once is computed once; a short PARAMETER is extended in place
"COUNT": ("R12", "R14", "R15", "R6", "R8", "R3", "R7", "R5", "R13", "R9", "R10", "R2", "R4"),
"COUNT": ("R12", "R16", "R14", "R15", "R6", "R8", "R3", "R7", "R17", "R5", "R13", "R9", "R10", "R2", "R4"),
# statement order IS the schedule among equal-priority insns (rank_for_schedule's LUID tie-break); do-while is a barrier
"ORDER": ("R9", "R7", "R13", "R3", "R6", "R8", "R5", "R12", "R14", "R10", "R15", "R2", "R4"),
# R17 is the DIRECTED form of the run-split R9 reaches only by luck: agent a2 measured 2,271 compiles for R9 to find it
# in func_80168828 and R16+R17 reproduce the same close in ten.
"ORDER": ("R17", "R9", "R7", "R13", "R3", "R16", "R6", "R8", "R5", "R12", "R14", "R10", "R15", "R2", "R4"),
"MIXED": dl.ALL_FAMILIES,
"OTHER": dl.ALL_FAMILIES,
}
@@ -842,11 +844,14 @@ def selftest():
c = classify(mine, tgt)
if c["kind"] != "REG" or c["bank"] != "caller" or c["score"] != 3:
fail(f"REG-caller classification wrong: {c}")
# the temp move leads, and the two byte-proven caller-saved levers (R5 the commutative swap, R15 the sink) are drawn
# early — R15 joined the front at S102 when agent a1's crack showed the arm-scoped form of the same allocator tie.
if family_key(c) != "REG-caller" or FAMILIES["REG-caller"][0] != "R6" \
or not {"R5", "R15"} <= set(FAMILIES["REG-caller"][:4]):
fail(f"REG-caller family wrong: {family_key(c)} {FAMILIES['REG-caller'][:4]}")
# The invariant, not a fixed window (widening the window once per new generator hid what it was for): the temp move
# leads, and every TARGETED caller-saved lever — R5 the commutative swap, R15 the sink, R16 the constant holder —
# is drawn before the BLIND families that permute declarations or statements wholesale (R9, R2, R4).
caller = FAMILIES["REG-caller"]
targeted, blind = {"R5", "R15", "R16"}, {"R9", "R2", "R4"}
if family_key(c) != "REG-caller" or caller[0] != "R6" or not targeted <= set(caller) \
or max(caller.index(t) for t in targeted) > min(caller.index(b) for b in blind):
fail(f"REG-caller family wrong: {family_key(c)} {caller}")
# a callee-saved swap: addu s0,a0,zero vs addu s1,a0,zero
c = classify([_ins(0x00808021)], [_ins(0x00808821)])
if c["kind"] != "REG" or c["bank"] != "callee":
+1 -1
View File
File diff suppressed because one or more lines are too long
+157 -1
View File
@@ -2212,7 +2212,117 @@ def sink_merges(text, tu, fn, d_):
return out
ALL_FAMILIES = ("R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10", "R12", "R13", "R14", "R15")
INT_LIT = re.compile(r"^\s*(?:\(\s*[A-Za-z_][\w \t*]*\)\s*)?(0[xX][0-9A-Fa-f]+|\d+)\s*$")
def constant_holders(text, tu, fn, d_):
"""[(description, candidate text)] — R16: a local whose ONLY assignment is one integer literal, written at every use
and its declaration removed. R6 stops at a temp read exactly ONCE (the classic def-with-one-use); a constant holder is
read many times and R6 never offered it, so the whole family was invisible to the search.
T7 agent a2's crack of func_80168828 (2026-09-10): its `$3` pin held `0x40` and was read as the RHS of four stores.
Deleting the variable is byte-neutral BY ITSELF — the pin was never doing the work — but it removes a quantity from
the block, which is what lets the next move reach the allocator (`qty_compare`, `local-alloc.c:1579-1595`, through the
unrolled switch at `:1485-1512`). A pinned local holding one literal is a CONSTANT-HOLDER, not a register lever, and
the readable spelling of a constant is the constant."""
lines = text.split("\n")
lo, hi = d_["line"], d_["end"] - 1
masked = [sc.mask_text(l) for l in lines]
occ = collections.defaultdict(list)
for i in range(lo, hi):
for m in IDENT.finditer(masked[i]):
occ[m.group(1)].append(i)
out = []
for v, where in occ.items():
asg, decl, uses = [], [], []
for i in where:
st = masked[i].strip()
m = re.match(r"^(?:[A-Za-z_][\w \t]*[\s*]\s*\*?\s*)?%s\s*=(?!=)\s*(.+);\s*$" % re.escape(v), st)
if m:
asg.append((i, m.group(1)))
elif is_decl_line(st) and "=" not in st.split(";")[0]:
decl.append(i)
else:
uses.append(i)
if len(asg) != 1 or len(decl) != 1 or not uses:
continue
if MULTI_DECL.match(masked[decl[0]]): # a shared declaration line: removing it would take the others
continue
lit = INT_LIT.match(asg[0][1])
if not lit:
continue
# every use must be a plain read — never an address-of, a member/arrow base, or another assignment's target
if any(re.search(r"&\s*%s(?![\w])|(?<![\w.>])%s\s*(?:\.|->|\[|=(?!=))" % (re.escape(v), re.escape(v)),
masked[i]) for i in uses):
continue
cand = []
for i, l in enumerate(lines):
if i == decl[0] or i == asg[0][0]:
continue
cand.append(re.sub(r"(?<![\w.>])%s(?![\w])" % re.escape(v), lit.group(1), l) if i in uses else l)
out.append((f"const-holder {v}={lit.group(1)} x{len(uses)}", "\n".join(cand)))
return out
def constant_run_splits(text, tu, fn, d_):
"""[(description, candidate text)] — R17: a run of consecutive statements assigning the SAME integer literal, split by
moving the nearest differently-valued literal assignment into it, at each split point.
T7 agent a2 (2026-09-10), the directed form of a move R9 already contains but reaches by luck: the mechanical search
needed 2,271 compiles to find this swap in func_80168828, and R17 offers it in a handful. The decision is
`find_free_reg`'s live-range scan, `local-alloc.c:2109-2110`
(`for (ins = born_index; ins < dead_index; ins++) IOR_HARD_REG_SET (used, regs_live_at[ins])`): while the two constants'
ranges are disjoint they share one caller-saved register; splitting the run makes the first live across the second, the
intervals overlap and the second takes another colour. The discriminator in the dumps is the `.lreg` line `Register N
used K times across M insns` — M grows when the split lands."""
lines = text.split("\n")
lo, hi = d_["line"], d_["end"] - 1
masked = [sc.mask_text(l) for l in lines]
def lit_of(i):
if not simple_stmt(masked[i]):
return None
m = re.match(r"^\s*[^=]+=(?!=)\s*(.+);\s*$", masked[i])
if not m:
return None
g = INT_LIT.match(m.group(1))
return g.group(1) if g else None
out = []
i = lo
while i < hi:
k = lit_of(i)
if k is None:
i += 1
continue
j = i
while j + 1 < hi and lit_of(j + 1) == k:
j += 1
if j - i + 1 < 2: # a run is two or more stores of the same literal
i = j + 1
continue
donors = [d for d in (i - 1, j + 1) if lo <= d < hi and lit_of(d) is not None and lit_of(d) != k]
for d in donors:
for cut in range(i + 1, j + 1): # every interior split point of the run
order = [x for x in range(i, j + 1)]
seq = [lines[x] for x in order]
seq.insert(cut - i, lines[d])
cand = []
for x, l in enumerate(lines):
if x == d:
continue
if x == i:
cand.extend(seq)
elif i < x <= j:
continue
else:
cand.append(l)
out.append((f"const-split @{d + 1}->{cut + 1} ({lit_of(d)} into the {k} run)", "\n".join(cand)))
i = j + 1
return out
ALL_FAMILIES = ("R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10", "R12", "R13", "R14", "R15", "R16", "R17")
RUNG_R_FAMILIES = ("R2", "R3", "R4", "R5", "R6", "R7") # the free sweep's set (R8/R9 are the search engine's until measured)
@@ -2320,6 +2430,12 @@ def recipe_candidates(text, tu, fn, names, limit=24, rng=None, cap=40, blocks=Tr
if "R15" in fam:
for desc, cand in sink_merges(text, tu, fn, d_):
out.append(("R15", desc, cand))
if "R16" in fam:
for desc, cand in constant_holders(text, tu, fn, d_):
out.append(("R16", desc, cand))
if "R17" in fam:
for desc, cand in constant_run_splits(text, tu, fn, d_):
out.append(("R17", desc, cand))
if blocks and "R7" in fam: # last: one candidate per statement, so the targeted recipes go first
for desc, cand in block_wraps(text, tu, fn, d_):
out.append(("R7", desc, cand))
@@ -3089,6 +3205,46 @@ def selftest():
if len(ch) != 1 or len(ch[0][2]) != 3:
fail(f"if_chains must see three arms in the fixture, got {ch}")
# R16 / R17 (T7 agent a2's crack of func_80168828, 2026-09-10): a constant holder inlined, then the run it fed split.
CFIX = ("void func_80100000(void) {\n"
" s32 c40;\n"
" s32 other;\n"
"\n"
" c40 = 0x40;\n"
" other = 0x10;\n"
" st(0) = c40;\n"
" st(1) = c40;\n"
" st(2) = c40;\n"
" st(3) = other;\n"
"}")
dC = next(r for r in sc.scan_text(CFIX, "src/fx/c.c", shared_defs=None)
if r["form"] == "def" and r["name"] == "func_80100000")
h16 = constant_holders(CFIX, "src/fx/c.c", "func_80100000", dC)
if len(h16) != 2 or not any(d.startswith("const-holder c40=0x40 x3") for d, _ in h16):
fail(f"R16 must inline a 3-use constant holder, got {[d for d, _ in h16]}")
else:
c16 = next(c for d, c in h16 if d.startswith("const-holder c40"))
if "s32 c40;" in c16 or "c40 = 0x40;" in c16 or c16.count("st(0) = 0x40;") != 1:
fail(f"R16 must delete the declaration and the assignment and write the literal: {c16!r}")
# control: a holder whose value is not a literal, and one written twice, are not constant holders
if any(d.startswith("const-holder") for d, _ in
constant_holders(CFIX.replace("c40 = 0x40;", "c40 = f();"), "src/fx/c.c", "func_80100000",
next(r for r in sc.scan_text(CFIX.replace("c40 = 0x40;", "c40 = f();"), "src/fx/c.c",
shared_defs=None) if r["form"] == "def"))
if d.startswith("const-holder c40")):
fail("R16 must refuse a holder whose single assignment is not an integer literal")
# R17 on the inlined text: the 0x10 store moved into the run of three 0x40 stores, at each interior split point
c16 = next(c for d, c in h16 if d.startswith("const-holder c40"))
d17 = next(r for r in sc.scan_text(c16, "src/fx/c.c", shared_defs=None) if r["form"] == "def")
r17 = [d for d, _ in constant_run_splits(c16, "src/fx/c.c", "func_80100000", d17) if "into the 0x40 run" in d]
if len(r17) != 2:
fail(f"R17 must offer both interior split points of a three-store run, got {r17}")
# control: a run of one store has no split
if constant_run_splits(CFIX.replace(" st(1) = c40;\n st(2) = c40;\n", ""), "src/fx/c.c", "func_80100000",
next(r for r in sc.scan_text(CFIX.replace(" st(1) = c40;\n st(2) = c40;\n", ""),
"src/fx/c.c", shared_defs=None) if r["form"] == "def")):
fail("R17 must refuse a run shorter than two statements")
# the oracle's crash classification on its real message forms (R103)
if not oracle.SIGNAL_LINE.search("bash: line 1: 3845091 Done mipsel-linux-gnu-cpp ...\n 3845092 Aborted (core dumped) | tools/bin/gcc-2.7.2-psx/cc1 -quiet\n"):
fail("SIGNAL_LINE must match bash's job-status block")
+15 -10
View File
@@ -85,15 +85,17 @@ FAMILIES = {
# R15 (the sink) is the arm-scoped form of the same tie: a value set in every arm of an if/else chain is a CROSS-BLOCK
# pseudo local-alloc never gives a quantity, so the arm holds two quantities and takes block_alloc's unrolled case 2;
# sinking makes it three, and case 3 falls through into case 2 and undoes its own exchange (T7 agent a1, func_80156044).
"REG-caller": ("R6", "R8", "R15", "R5", "R10", "R12", "R14", "R13", "R3", "R7", "R9", "R2", "R4"),
"REG-caller": ("R6", "R16", "R8", "R15", "R17", "R5", "R10", "R12", "R14", "R13", "R3", "R7", "R9", "R2", "R4"),
# the s-bank order is global.c's allocno_compare (ref weight x live length), declaration order only on an exact tie
"REG-callee": ("R2", "R4", "R3", "R6", "R8", "R15", "R12", "R7", "R9", "R10", "R14", "R13", "R5"),
"REG-mixed": ("R6", "R2", "R15", "R5", "R10", "R4", "R3", "R8", "R12", "R13", "R14", "R7", "R9"),
"REG-callee": ("R2", "R4", "R3", "R6", "R16", "R8", "R15", "R12", "R7", "R9", "R17", "R10", "R14", "R13", "R5"),
"REG-mixed": ("R6", "R16", "R2", "R15", "R17", "R5", "R10", "R4", "R3", "R8", "R12", "R13", "R14", "R7", "R9"),
# a copy dies to cse's canon_reg or the local-alloc tie unless its destination changes MODE (the width); an address
# pseudo lives when a pointer local is used twice; a value named once is computed once; a short PARAMETER is extended in place
"COUNT": ("R12", "R14", "R15", "R6", "R8", "R3", "R7", "R5", "R13", "R9", "R10", "R2", "R4"),
"COUNT": ("R12", "R16", "R14", "R15", "R6", "R8", "R3", "R7", "R17", "R5", "R13", "R9", "R10", "R2", "R4"),
# statement order IS the schedule among equal-priority insns (rank_for_schedule's LUID tie-break); do-while is a barrier
"ORDER": ("R9", "R7", "R13", "R3", "R6", "R8", "R5", "R12", "R14", "R10", "R15", "R2", "R4"),
# R17 is the DIRECTED form of the run-split R9 reaches only by luck: agent a2 measured 2,271 compiles for R9 to find it
# in func_80168828 and R16+R17 reproduce the same close in ten.
"ORDER": ("R17", "R9", "R7", "R13", "R3", "R16", "R6", "R8", "R5", "R12", "R14", "R10", "R15", "R2", "R4"),
"MIXED": dl.ALL_FAMILIES,
"OTHER": dl.ALL_FAMILIES,
}
@@ -842,11 +844,14 @@ def selftest():
c = classify(mine, tgt)
if c["kind"] != "REG" or c["bank"] != "caller" or c["score"] != 3:
fail(f"REG-caller classification wrong: {c}")
# the temp move leads, and the two byte-proven caller-saved levers (R5 the commutative swap, R15 the sink) are drawn
# early — R15 joined the front at S102 when agent a1's crack showed the arm-scoped form of the same allocator tie.
if family_key(c) != "REG-caller" or FAMILIES["REG-caller"][0] != "R6" \
or not {"R5", "R15"} <= set(FAMILIES["REG-caller"][:4]):
fail(f"REG-caller family wrong: {family_key(c)} {FAMILIES['REG-caller'][:4]}")
# The invariant, not a fixed window (widening the window once per new generator hid what it was for): the temp move
# leads, and every TARGETED caller-saved lever — R5 the commutative swap, R15 the sink, R16 the constant holder —
# is drawn before the BLIND families that permute declarations or statements wholesale (R9, R2, R4).
caller = FAMILIES["REG-caller"]
targeted, blind = {"R5", "R15", "R16"}, {"R9", "R2", "R4"}
if family_key(c) != "REG-caller" or caller[0] != "R6" or not targeted <= set(caller) \
or max(caller.index(t) for t in targeted) > min(caller.index(b) for b in blind):
fail(f"REG-caller family wrong: {family_key(c)} {caller}")
# a callee-saved swap: addu s0,a0,zero vs addu s1,a0,zero
c = classify([_ins(0x00808021)], [_ins(0x00808821)])
if c["kind"] != "REG" or c["bank"] != "callee":