5.2 KiB
§66d — The permuter⇄reader loop: alternate a random search with a byte-verified idiom, and let residual_class decide whose turn it is (Phase 29 SESSION-17)
func_80177940 (101 ins, reach-138) was preserved as close=5 with the note "equal-priority
birthing_insn_p ties broken by INSN_LUID, not steerable from source order — swept all 6 assign
orders/pin combos". Both halves of that note were right, and it still banked, because the two halves
belong to different tools:
| residual | who closes it | why the other cannot |
|---|---|---|
| 4-ins scheduler tie (INSN_LUID order) | the permuter (5 → 1, 900 s) | a hand sweep enumerates source orders; the tie is broken by internal LUIDs, which randomized decl/statement churn moves and a human cannot address |
andi $a2,$v0,0xf vs addu $a2,$v0,$zero |
the reader | the permuter's mutation set adds masks/casts; it does not invent "delete this mask AND hard-pin the destination" |
| the register/schedule fallout of that fix (6 left) | the permuter (6 → 0, 1800 s) | ditto the first row |
The handoff signal is free and already computed. tools/match_one.py prints residual_class's
bucket on every run: [permuter] → hand it back to the search; [structural] → read it. Here it went
structural(OPCODE-MIXED) → after the idiom fix ADDRESSING [permuter] profile=cse
→ MATCH. Feed that profile straight to p16_permute --klass (permuter_weights.classify accepts a
profile name verbatim).
⚠️ Correction, same session, before this rule could mislead anyone: [structural] is a HINT, not a
veto. func_80177940 was structural(OPCODE-MIXED) at close=5 and the permuter still took it
5 → 1 — at low closeness a randomized search reaches ties the class label says nothing about, since
the label describes only the dominant residual. Read [structural] as "a search alone probably will
not finish this", not "do not run the search". Cheap policy: at LOW closeness run the search anyway (it
costs CPU, not tokens); at HIGH closeness read first, because a search that must invent a semantic
change will burn hours to plateau.
Diagnose from a byte-verified SIBLING, never from first principles. The fix came from
func_801778A8 — same family, same nibble walk, already banked byte-identical — which writes
nib = uVar1; (a plain copy) between two hard-pinned variables after the identical (x << 16) >> 28
shift pair. And test the naive reading first: deleting the redundant & 0xf alone collapsed the
copy (100 vs 101 ins, 52 mismatched), because gcc then reused one register. That failure is what proved
the target needs a distinct pinned register — the mask was never the point. A residual of one
instruction can still be a two-part fix.
§66d-1 — What transfers between giants is the LOOP, not the PIN
Applied to func_8014D820 (304 ins, close=33), whose residual is the mirror image (target holds the
lhu results in $v1, keeps $a0 live to fill the load-delay slot), the same move — pin the
reusable temp to the register the target uses — went 303 vs 304 ins, 285 mismatched: $v1 is needed
elsewhere in that function. §44's "each giant is its own class" holds at the level of the specific pin.
Its permuter run improved 33 → 27 and plateaued (seed kept for an ILS warm restart). Budget the loop
per giant; do not budget a pin.
§66d-2 — Two operational sharp edges
p16_permute.setupwipes.run/permuter/<fn>/. Re-running a target destroys the previous run'soutput-<n>-*dirs, best score included. Copy the bestsource.cout before re-running — the score-1 candidate here survived only because it had been copied to a run-local path first.run_permuter's cleanup used a globalpkill -f permuter/run_masked.py, which matches every concurrent run — so two permuters on a multi-core box silently killed each other the moment the first timed out, with no error anywhere. Now scoped to the run's own scratch dir (it is in argv). This is what makes grinding several giants at once safe, and it is likely why the giant queue had only ever been run one at a time.
§66d-3 — Read the ILS per-cycle SERIES, not its final best: a repeated score and a still-falling one look identical in a summary line and mean opposite things
Two permuter_ils runs, same session, same machine, same 8×240 s budget:
| function | per-cycle best | verdict |
|---|---|---|
func_8014D820 |
25, 25, 25, 25, 25, 25, 25, 25 | DONE. The warm restart has nothing left to find; more CPU is waste. Escalate to a reader / Fable5, or accept the stub. |
func_80140958 |
80, 75, 72, 69, 64, 62, 60, 59 | BUDGET-LIMITED. Monotone, never repeating, still dropping on the last cycle → buy more cycles. It is CPU, not tokens. |
ILS done: best=NN is the same line in both cases, and choosing on it alone gets both decisions wrong
— you stop the one that was still descending and keep paying for the one that had finished. The series
is already printed per cycle; the rule is: a repeat means stop, a fall means continue. (A first-cycle
drop followed by repeats — func_8014D820's 27→25 then ×7 — is the "one easy waypoint then done" shape,
which still means stop.)