search: 0 of 139 exemplars matched lever-free in 0.54 h (0 of 7,077 bodies behind them; 52,566 compiles) — NO-MATCH 136 · BANK-REFUSED 3
- the three refusals are the three fleet copies of func_80136824, each a real score 0 (R15 sink + R12 width + R14
param-width) blocked by a contract rather than by a bad body: the engine verifies the whole candidate text but hands
apply_body_core only the definition, and --propagate remaps that body to siblings, so a generator that edits lines
outside the definition can never bank. R14 is the only such generator.
- my first diagnosis was wrong and was discarded rather than shipped: I guessed the conflicting declaration was in a
shared header and built a 2,431-name index to refuse on, and the index said the function is not in it. Reading cmd_run
gave the real answer.
- two fixes: param_widths refuses outright when the TU declares the function anywhere but at its definition
(protos_outside_definition; the earlier R14 banks had no such prototype, so nothing that worked is lost), and the engine
names the condition itself with a new OUT-OF-BODY verdict instead of letting the bank die on a compiler error that reads
like a bad body. Controls both ways in the selftest.
- the steering measurement (R41): across s1-s4 the head's 57 classes have absorbed ~128,000 compiles for 6 closes, all of
them R15's and all in the first sweep. The head is resistant to every mechanical generator at this width; sweeps pay on
the tail and on targeted families, and the head is what agents are for.
- open by name for the types phase: func_80136824 (133 copies) has a real crack that needs its prototype widened with its
definition — the second measured case where a declaration, not codegen, stands between us and the bytes.
The third agent produced no bank and a precise refutation plus a generator, which is the deliverable the brief asks for.
- it refuted the a2 hypothesis on its own body: git grep returns 1,770 declarations in two forms, both (s32 a0), no (void)
anywhere, and the pin is on a local rather than the parameter. Three lever-free spellings reach the target's complete
register assignment, so the class is reachable from plain C.
- its best is score 2, class ORDER, 89/89 instructions, every register correct, one displaced bystander store. The target's
sw sits inside the lbu->addiu window and that position is forced: anti_dependence and true_dependence (sched.c:817/845)
both hold, so the store can neither hoist nor sink. Post-sched1 stream and reg_live_length then match ours, leaving
reg_n_refs — computed by flow on the pre-combine RTL — as the only remaining input.
- R18 moves one simple statement to each other position in its own block, up to six away. R9's adjacent swap is the special
case; the distance is the point. It costs no instruction where R7's LOOP notes are a full sched1 barrier and always cost
one displaced insn, so R18 is ranked ahead of R7 in every class.
- two wrong spellings before the known-true check passed: identifier-disjointness as a requirement offered three candidates
and none of them the agent's (it is only an ordering preference now — byte-identical output is the same program, so the
oracle is the whole correctness proof, which is R9's own footing), and a blank line counted as an obstacle, so the
generator never offered the very move it was written from. It now reproduces that body exactly: bystander @21->17.
- delever_pack.py now writes each trace candidate's residual class beside its score, from the agent's method note: a bare
number hid that a move had already turned this body's residual from REG into ORDER.
- delever --selftest OK (3 new controls incl. the nested-block refusal); delever_search --selftest OK; tool_census OK.
search: 1 of 80 exemplars matched lever-free in 0.89 h (6 of 486 bodies behind them; 28,042 compiles) — BUDGET 59 · NO-MATCH 18 · UNSTRIPPABLE 2 · MATCH 1
- func_801621CC by R6 inline lo + R7 do-while; delever --propagate: 5 of 5 sibling(s) banked, 0 refused.
- the figure that prices the draw is BUDGET 59 of 80: three quarters of the remaining bodies exhausted 400 compiles rather
than being refuted, so this family is sampled, not measured. s3 had already taken its cheap half (10 bodies).
- two UNSTRIPPABLE recorded by name: func_8017DC80 (a launder with 2 outputs), func_80181A4C (instruction lw has no C
spelling in the table).
- check-all: 218 passed, 0 failed of 218
lever_census --check: 29,527 pin/asm sites, 29,527 marked !FAKE, 0 UNMARKED — OK
- snapshot row 14. Loop economics two agents in (R41): 30,358 -> 29,527 sites, 9,747 -> 9,320 bodies; the agents' own two
bodies account for 255 of that and their toolified moves for the other 576, at zero drafting tokens.
- s2 (the head, with R16/R17 added): search: 0 of 140 exemplars matched lever-free in 0.24 h (0 of 7,085 bodies behind
them; 23,689 compiles). The two new generators closed nothing on the head; recorded as measured.
- agent a2's "284 constant-holder pins of 10,958" verified against the source rather than believed (R14), and my first
instrument was wrong (R40): asking R16 directly answered 33, because R16 only fires on a split declaration while most
pins carry their value as an initialiser. R3 converts one form to the other, so the reachable family is R3+R16.
Corrected, both figures derived: 537 constant-holder pin sites of 17,302, in 510 bodies (152 initialiser, 385 separate
assignment; by register $2 282, $20 136, $3 28).
- s3 drew exactly those 99 function names and closed 10 bodies before crashing with
AttributeError: 'Namespace' object has no attribute 'allow_residue'
in propagate — the a2 fix read the flag off the caller's namespace and the search engine builds its own Namespace for
that in-process call. Fixed with getattr(a, "allow_residue", False): a library must not assume its caller's namespace
shape (R43).
- the ten banks were real, proven by gating the tree the crash left:
check-all: 218 passed, 0 failed of 218
lever_census --check: 29,533 pin/asm sites, 29,533 marked !FAKE, 0 UNMARKED — OK
- by first move the ten are R15 x3, R6 x4, R7 x2, R9 x1 — the draw was right about the family even though R16 did not fire.
- snapshot row 13; s3 re-runs from the top with the fix.
- 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).
- two moves: delete `register s32 c40 __asm__("$3")` and its `c40 = 0x40;`, writing the literal at its four uses (byte-neutral
on its own — the pin was never doing the work); then swap the adjacent `f1e = 0x40;` and `f1a = 0x10;` so the 0x10 store
splits the run of 0x40 stores.
- the residual reads like cse/sched and the decision is local-alloc's. Dumps of the real TU in both orderings differ on one
.lreg line — `Register 76 used 5 times across 10 insns` -> `across 14 insns`, `Register 76 in 2.` -> `in 3.` — which is
find_free_reg's live-range scan at local-alloc.c:2109-2110: unswapped the two constants' ranges are disjoint and share $v0,
swapped they overlap and the first takes $v1.
- the $4 pin STAYS, and not as a lever: src/shared/ov/func_801687CC.h declares extern void func_80168828(void), so the
target's `move s1,a0` has no C source. An uninitialised local, a pointer-typed one, a split declaration and deleting it
outright all give the identical score-25 residual; both parameter forms are hard cc1 errors against that header. This is
the first measured pin that only a declaration fix can remove — one of the 51 conflicts P35 ledgered for the types phase.
The engine's score-1 text is a coincidence (its andi truncates garbage in $s1 and never reads $a0); not proposed.
- instrument fixed in the same change: --propagate refused all 124 siblings because the reshape deliberately keeps a lever.
The allowance is now derived from the exemplar's own banked text (its surviving !FAKE markers), and a sibling whose remap
would carry more levers than the exemplar is refused by name.
delever --propagate: 124 of 124 sibling(s) banked, 0 refused
- check-all: 218 passed, 0 failed of 218
lever_census --check: 29,572 pin/asm sites, 29,572 marked !FAKE, 0 UNMARKED — OK
- snapshot row 12; delever --selftest OK. The toolify (R16, the constant-run split) follows.
The harvest half of agent a1 paying off. With R15 in the registry the engine was swept over the rest of the residue head
(--only the 56 other head functions, which draws every body of those names: 147 exemplars judged).
search: 6 of 147 exemplars matched lever-free in 0.28 h (267 of 7,477 bodies behind them; 23,892 compiles) — NO-MATCH 141 · MATCH 6
- all six closes are R15, each in FOUR compiles: func_8013EB7C (126 + 7 copies) and func_8016DF5C (127 + 4 + 2 + 1) across
their fleet copies. Every propagation 0 refused. R15 also moved func_80136824 from 21 to 2 as a first move.
- the loop's economics with denominators (R41): one agent's reading (~221k tokens) bought 130 bodies directly and 267 more
for zero tokens — 397 bodies, 30,358 -> 29,697 sites (-661).
- check-all: 218 passed, 0 failed of 218
lever_census --check: 29,697 pin/asm sites, 29,697 marked !FAKE, 0 UNMARKED — OK
- snapshot row 11 (9,617 -> 9,350 bodies); the checkpoint headline and loop state refreshed (R101).
The harvest half of the one-at-a-time loop (R16): agent a1's reading of func_80156044 is now a move the engine can make
on any body, so the remaining head classes get it for free.
- R15 sinks the statement AFTER an if/else chain into every arm and deletes the variables it consumed:
`if (c) { v = e1; } else { v = e2; } w = f(v);` -> `if (c) { w = f(e1); } else { w = f(e2); }`.
- it is a REGISTER move, not a scheduling one. A value set in every arm and read after the merge is a cross-block pseudo
local-alloc never gives a quantity (local-alloc.c:472, next_qty reset at :517), so the arm holds two quantities and
takes block_alloc's unrolled case 2 (:1499-1502, qty_compare :1578-1596). Sinking makes it a third block-local
quantity, and case 3 (:1491-1496) falls through into case 2 and applies that comparison a second time, undoing its own
exchange — the two caller-saved colours swap. It also takes the value out of global.c, where set_preference
(global.c:1535+) had given it a copy preference through the merge result's argument copy.
- applicability is checked, never assumed: each consumed variable must be assigned exactly once in every arm by a simple
statement, appear in the merge statement, and occur nowhere else in the function.
- if_chains() counts a line's CLOSING braces before its opening ones. On a `} else if (...) {` line the two net to zero
and the first version's depth counter never closed the arm — the generator found 0 candidates on the very body it was
written from. Caught by running it on that known-true case before believing it.
- ranked third in REG-caller / REG-mixed / COUNT; the engine selftest's "R5 in the first three" assertion widened to
"R5 and R15 in the first four" rather than de-ranking the new move.
- verified: delever --selftest OK (3 new controls: a variable read after the merge, a variable one arm does not set, the
brace walk's three arms); delever_search --selftest OK; and the known-true check — R15 run on func_80156044's
pre-bank text emits the agent's crack and `--try` scores it
`score 0 (OTHER; mine 74 ins, target 74) — MATCH`.
- SETUP row rewritten (R21), kit corpus regenerated, tool_census --check OK (371 copies + 30 pointers, 0 gaps).
- no src/ change in this commit; the sweep of the other 56 head classes follows.
The first agent of the one-at-a-time T7 loop, on the rank-1 head class (130 copies, two NEEDED pins $2/$3). Six mechanical
rung-G runs had been stuck at best 1 on this body; the agent closed it at 0.
- the move is not a dial: the iVar3/puVar2 pair that every arm of the if/else chain set is deleted, and each arm gets the
whole address expression (puVar4 = &D_80192454 + (((u32)param_2) & 0xffff) * 0x14). Two locals fewer than the levered
text — shorter AND byte-identical, which is what this phase is for.
- mechanism, read from gcc 2.7.2's own source and confirmed in the .lreg/.greg dumps: scoping the base into the arm makes
the block hold THREE local quantities instead of two, and block_alloc's unrolled case 3 (local-alloc.c:1491-1496) falls
through into case 2 and applies qty_compare(0,1) a second time, undoing its own exchange — so the $2/$3 assignment flips
(density formula local-alloc.c:1578-1596); deleting the cross-block variable also takes it out of global.c, where
set_preference (global.c:1535+) had given it a copy preference for $5 through puVar4's argument copy, merged by
expand_preferences (global.c:781-825) and overriding first-fit at global.c:1034-1067.
- why the engine could not find it: the u16-width move six runs found is a local optimum that IMITATES the fix by
manufacturing the third quantity while paying an andi. No generator in the registry deletes a variable, so the search
could not leave that basin. That is the toolify target and it follows in its own commit (R16).
- verified before banking (--try: score 0, mine 74 ins, target 74 — MATCH), then
delever --apply-body: ... IDENTICAL on 1 object(s) (0.11 s) — KEPT, ledgered (rung E, a1)
delever --propagate: 129 of 129 sibling(s) banked, 0 refused
check-all: 218 passed, 0 failed of 218
lever_census --check: 30,098 pin/asm sites, 30,098 marked !FAKE, 0 UNMARKED — OK
audit_public: OK — 0 offenders among 11445 tracked paths
- snapshot row 10; the agent's body.c and mechanism.md kept in the pack as the reading behind the generator.
- delever --propagate: 132 + 130 + 131 + 132 siblings banked, 0 refused; each sibling's body is the exemplar's reshaped
text with ITS OWN func_/D_ addresses (the class is "identical modulo addresses", so the two old bodies' tokens
correspond one for one), judged on its own objects before it is kept
- the class key comes from the FIRST bank in a body's chain, not the last: a body that was reshaped and then tidied has
two rows, and the later row's before-hash describes a text only that body ever had (func_80163EC8 found 0 siblings
until this was fixed, then 132)
- R22 clean fleet: `check-all: 218 passed, 0 failed of 218`
- lever_census --check: 33,427 pin/asm sites, 33,427 marked, 0 UNMARKED — 664 sites gone this session (34,091 at S99's
open); lever_progress snapshot "T6 p1"
- delever_permute --bank: 4 winners applied through delever --apply-body, each IDENTICAL on its own object; the winner is
now TIDIED first (pycparser reprints a body it parsed: two-space indent and a corpse `;` where a statement was inlined
away) and the tidy is judged like any other candidate, so the source keeps the tree's shape; parenthesisation and brace
style are deliberately left to the formatting phase, over the whole tree at once
- a slip, named: --bank re-applied a permuter body over the ONE-LINE version rung R had already banked for
func_80135D20; the clean text was restored through the oracle (label d1fix) and --bank now skips any body the ledger
already calls LEVER-FREE
- --recipes is killable now: the oracle writes the candidate into the tree to compile it, so the original goes into
inflight.json first (P35's rule: a tool restores from its own snapshot) — a killed run had left a candidate in src/
- rung R's R6 generalised from "assigned once, read once" to "dead after one read" (the lever rung D actually found:
uVar5 is assigned in two branches and only one was inlined); it still does not reproduce that class's win, which is
recorded as an open item rather than papered over
- lever_census: 33,953 sites (was 33,957), 0 UNMARKED; lever_progress snapshot "T6 d1"; R22 218 passed, 0 failed of 218
The pin/hint removal's COUNT OVER TIME is a deliverable in its own right: the post-100% chart, the story's spine, a wiki
page, and the day-one rule the decomp-architect kit should carry. It is only ever counted by the phase that removes it, so
it has to be captured while the work happens — a census is a moment.
- docs/levers.md: the taxonomy (A pins · B1-B4 barriers/launders/keep-alives/hand-placed instructions · B5 GTE, not a
lever · C volatile · D bare register · E asm-label aliases · F builtins · G attributes); a GENERATED §2; how the levers
got in phase by phase (P18's pin toolkit took the wave close-rate 33% -> 56% -> 90%; the family engine then copied every
lever across up to 134 overlays, which is why 1,758 distinct bodies are 12,578); the ladder with each rung's measured
yield; §5 the prevent-vs-defer argument; §6 what the wiki, the kit and the story each take.
- tools/lever_progress.py: --snapshot appends a milestone row (the census's totals by class + HEAD) to
docs/lever-progress.tsv and re-renders the document; the campaign table is derived from the de-lever ledger on every
render and scored as state TRANSITIONS, so the rung that FINISHES a body gets the credit (counting first-rows-only
reported rung R's 134-body batch as zero); --check refuses a series that is not this tree's.
- the numbers, generated: 21,061 sites removed or rewritten across 17,119 bodies against 33,957 still standing — 38% of
the class A/B population came off with no understanding at all, which is the evidence behind §5's rule:
BAN THE SILENCE, NOT THE LEVER (a lever is allowed and is a marked, ledgered, published debt from the first bank, with a
one-compile bank-time trial; never block the byte gate on it).
- Reference-index row (doc_links OK), SETUP row, tool dictionary row, CURRENT_PHASE entry for the story.
- delever --recipes --label r1 --only func_80135D20 --cap 200: `recipes: 134 of 134 bodies closed lever-free
(134 NEEDED sites in the 134 drawn), 3243 compiles in 6.0 min`, every one `R7 do-while @<line>` — one line of source
per body, each judged on its own objects, markers scrubbed within the banked body's span
- R22 clean fleet: `check-all: 218 passed, 0 failed of 218`
- lever_census --check: 33,957 pin/asm sites (was 34,091 — the 134), 33,957 marked !FAKE, 0 UNMARKED — OK
- the rung-D campaign that named the shape: `permuter: 5 of 16 exemplars matched lever-free in 0.69 h (665 of 2,131
bodies behind them)`; the split is the starting distance (closed at 8/12/19/8/29 mismatched instructions, open at
25..276), and the first two campaigns were the instrument, not the population (cookbook §454)
THE INSTRUMENT (R40). Two campaigns returned "0 of 16" with a straight face; both were the harness. The target object had
been assembled from a DISASSEMBLY LISTING, which is a second toolchain with its own answers:
- objdump prints the pseudo-instruction `move` for `addu rX,rY,$zero`; gas assembles `move` as `or` — 24 wrong words in
one 234-instruction function, silently;
- a listing's %hi/%lo pairs come back RESOLVED with no relocation, while every candidate carries one, and the masked
scorer compares reloc operands.
The permuter therefore scored 28 for a body that IS byte-identical: score 0 was unreachable and every NO-MATCH was its own.
- tools/delever_permute.py: the target is now the tree's OWN (levered) body compiled by the build's tail into a
one-function object — the candidates' relocations by construction — and `match_one` must call that body a MATCH against
the ROM listing before the search starts (R34 keeps it from being circular). Base score for the tree's own body: 0.
- tools/p16_permute.py `setup(target_o=)` + tools/permuter_ils.py `--target-o` (defaults unchanged).
- `--positive-control TU FN`: perturb a matching body by one commutative swap, require the permuter back to 0.
- tools/verbatim_target_s.py --gas now VERIFIES itself: assemble, disassemble, compare word by word with the image,
`.word 0x…`-patch what does not reproduce (24 in that function), REFUSE what still disagrees. The listing is a public
artifact (decomp.me) and was wrong for every function containing a `move`.
THE PROFILE. The weight profile now comes from the register a needed pin names, not just the site kind: callee-saved
($16-$23) is an allocation-order residual -> regalloc; caller-saved ($2/$3/$4-$7) is not -> cse. Read from the bytes: the
residual on func_80163EC8 (`register … __asm__("$2")`) is `and v0,v1,v0` against `and v0,v0,v1` — the operand order of one
`&` — and the regalloc profile weights perm_commutative 2.0 while cse weights it 40.0.
RUNG R (tools/delever.py --recipes): the cookbook's byte-neutral shape recipes, mechanically, seeded with the body's
lever-free text — R2 the formerly-pinned declarations permuted, R4 one moved through the whole declaration run, R3 an
initializer split placed after the run (C89), R5 the operand order of one commutative operator (the caller-saved lever, and
the only recipe needing no pinned declaration). Identity control on both the splice and the oracle before any verdict;
markers scrubbed within the banked body's own span only; selftest cases on a fixture whose answers are known by hand.
Also: a threading race in the site cache published the empty dict before filling it and made a whole batch report
"no site in this TU"; the file-scope asm dropper took an asm-LABEL clause for a statement. SETUP + dictionary rows.
- tools/delever_permute.py: one exemplar per RESIDUE text class from the ledger (copies desc, needed asc) prepared as a
single-function TU (delever's rung-A rewrite; other definitions -> prototypes; shared-header includes -> their prototypes;
INCLUDE_ASM and file-scope asm dropped; the build's own CPPFLAGS through cpp -P), the target regenerated from the ROM image in
BOTH forms (--gas for target.o, splat for match_one), permuter_ils with the profile from the NEEDED kinds, a winner banked only
through delever --apply-body + the GTE re-fold. Scratch/winners keyed alias+fn (R48).
- the control (R39/R56, new): every attempt first requires the LEVERED body to be match_one MATCH against the regenerated target,
then records the lever-free body's distance. --calibrate --limit 12: 12 of 12 MATCH; starting distance min 8 / median 78 / max 276.
- delever: ("B","gte-lever") joins REMOVABLE — a direct statement's clobbers reset to its canonical set, a variant-macro use pointed
at the canonical macro whose name comes from the variant definition's SIGNATURE (gte_rt_m -> gte_rtv0tr, not Sony's gte_rt);
462 of 462 gte-lever sites now offered to the ladder, 0 before. gte_consolidate.canonical_match() is the one reader of the
canonical table (R33), direct_rewrite refactored onto it; both selftests green.
- p16_permute.setup(outdir=) + permuter_ils --pd: a scratch dir keyed by the caller, defaults unchanged.
- four harness defects found by running it: the splat listing is not assemblable (R98 in a second place); pycparser rejects
__attribute__ and the permuter then silently permutes nothing; include_asm.h injects a file-scope .include "labels.inc" that
collides with the permuter's own macro.inc; an asm-LABEL clause is not an asm statement (a bare scan ate one and left a headless
K&R body). SETUP row (R21), dictionary row (R87), .gitignore allowlist for the outcomes ledger.