mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-30 07:31:59 -04:00
phase-36: T7 toolify a1 — generator R15, the sink (agent a1's crack made mechanical; reproduces it from the pre-bank text at score 0)
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.
This commit is contained in:
+180
-180
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"head": "5a8bd1021",
|
||||
"head": "fd2433b6d",
|
||||
"stamp": "b44c5a8a3423",
|
||||
"generated": "2026-09-10 00:04",
|
||||
"generated": "2026-09-10 00:39",
|
||||
"aliases": [
|
||||
"main",
|
||||
"ov_SC03_014",
|
||||
@@ -21,207 +21,207 @@
|
||||
"main": {
|
||||
"objects": 85,
|
||||
"identical": 85,
|
||||
"seconds": 7.544000000000001,
|
||||
"mean_s": 0.089
|
||||
"seconds": 6.715,
|
||||
"mean_s": 0.079
|
||||
},
|
||||
"ov_SC03_014": {
|
||||
"objects": 32,
|
||||
"identical": 32,
|
||||
"seconds": 4.776,
|
||||
"mean_s": 0.149
|
||||
"seconds": 4.601000000000001,
|
||||
"mean_s": 0.144
|
||||
},
|
||||
"ov_SC03_015": {
|
||||
"objects": 32,
|
||||
"identical": 32,
|
||||
"seconds": 4.493000000000001,
|
||||
"mean_s": 0.14
|
||||
"seconds": 4.5489999999999995,
|
||||
"mean_s": 0.142
|
||||
},
|
||||
"ov_SC04_011": {
|
||||
"objects": 28,
|
||||
"identical": 28,
|
||||
"seconds": 3.912000000000001,
|
||||
"mean_s": 0.14
|
||||
"seconds": 3.94,
|
||||
"mean_s": 0.141
|
||||
}
|
||||
},
|
||||
"per_object_seconds": {
|
||||
"build/src/800.o": 0.814,
|
||||
"build/src/800_b.o": 0.134,
|
||||
"build/src/800_b_2.o": 0.367,
|
||||
"build/src/800_b_o0a.o": 0.11,
|
||||
"build/src/800_c.o": 0.246,
|
||||
"build/src/800b2.o": 0.107,
|
||||
"build/src/apicard1.o": 0.093,
|
||||
"build/src/apicard2.o": 0.095,
|
||||
"build/src/apicard3.o": 0.09,
|
||||
"build/src/apicard4.o": 0.09,
|
||||
"build/src/apicard5.o": 0.091,
|
||||
"build/src/apicard6.o": 0.097,
|
||||
"build/src/apicard7.o": 0.095,
|
||||
"build/src/boot.o": 0.109,
|
||||
"build/src/gap.o": 0.089,
|
||||
"build/src/libapi1.o": 0.094,
|
||||
"build/src/libapi2.o": 0.058,
|
||||
"build/src/libc2_1.o": 0.103,
|
||||
"build/src/800.o": 0.754,
|
||||
"build/src/800_b.o": 0.084,
|
||||
"build/src/800_b_2.o": 0.287,
|
||||
"build/src/800_b_o0a.o": 0.073,
|
||||
"build/src/800_c.o": 0.189,
|
||||
"build/src/800b2.o": 0.072,
|
||||
"build/src/apicard1.o": 0.066,
|
||||
"build/src/apicard2.o": 0.075,
|
||||
"build/src/apicard3.o": 0.077,
|
||||
"build/src/apicard4.o": 0.065,
|
||||
"build/src/apicard5.o": 0.071,
|
||||
"build/src/apicard6.o": 0.066,
|
||||
"build/src/apicard7.o": 0.083,
|
||||
"build/src/boot.o": 0.07,
|
||||
"build/src/gap.o": 0.07,
|
||||
"build/src/libapi1.o": 0.081,
|
||||
"build/src/libapi2.o": 0.054,
|
||||
"build/src/libc2_1.o": 0.064,
|
||||
"build/src/libc2_2.o": 0.058,
|
||||
"build/src/libcd1.o": 0.096,
|
||||
"build/src/libcd2.o": 0.067,
|
||||
"build/src/libetc.o": 0.071,
|
||||
"build/src/libgpu.o": 0.079,
|
||||
"build/src/libgpu2.o": 0.094,
|
||||
"build/src/libgs1.o": 0.068,
|
||||
"build/src/libgs2.o": 0.066,
|
||||
"build/src/libgs3.o": 0.063,
|
||||
"build/src/libgs4.o": 0.068,
|
||||
"build/src/libgs5.o": 0.062,
|
||||
"build/src/libgs6.o": 0.112,
|
||||
"build/src/libgs7.o": 0.068,
|
||||
"build/src/libcd1.o": 0.066,
|
||||
"build/src/libcd2.o": 0.056,
|
||||
"build/src/libetc.o": 0.063,
|
||||
"build/src/libgpu.o": 0.065,
|
||||
"build/src/libgpu2.o": 0.081,
|
||||
"build/src/libgs1.o": 0.059,
|
||||
"build/src/libgs2.o": 0.063,
|
||||
"build/src/libgs3.o": 0.077,
|
||||
"build/src/libgs4.o": 0.069,
|
||||
"build/src/libgs5.o": 0.05,
|
||||
"build/src/libgs6.o": 0.074,
|
||||
"build/src/libgs7.o": 0.059,
|
||||
"build/src/libgs8.o": 0.061,
|
||||
"build/src/libgte1.o": 0.062,
|
||||
"build/src/libgte10.o": 0.056,
|
||||
"build/src/libgte11.o": 0.06,
|
||||
"build/src/libgte12.o": 0.064,
|
||||
"build/src/libgte13.o": 0.066,
|
||||
"build/src/libgte14.o": 0.056,
|
||||
"build/src/libgte15.o": 0.071,
|
||||
"build/src/libgte16.o": 0.065,
|
||||
"build/src/libgte17.o": 0.069,
|
||||
"build/src/libgte18.o": 0.059,
|
||||
"build/src/libgte19.o": 0.063,
|
||||
"build/src/libgte2.o": 0.064,
|
||||
"build/src/libgte20.o": 0.064,
|
||||
"build/src/libgte21.o": 0.069,
|
||||
"build/src/libgte22.o": 0.07,
|
||||
"build/src/libgte1.o": 0.06,
|
||||
"build/src/libgte10.o": 0.067,
|
||||
"build/src/libgte11.o": 0.062,
|
||||
"build/src/libgte12.o": 0.061,
|
||||
"build/src/libgte13.o": 0.06,
|
||||
"build/src/libgte14.o": 0.067,
|
||||
"build/src/libgte15.o": 0.067,
|
||||
"build/src/libgte16.o": 0.067,
|
||||
"build/src/libgte17.o": 0.071,
|
||||
"build/src/libgte18.o": 0.065,
|
||||
"build/src/libgte19.o": 0.066,
|
||||
"build/src/libgte2.o": 0.065,
|
||||
"build/src/libgte20.o": 0.063,
|
||||
"build/src/libgte21.o": 0.061,
|
||||
"build/src/libgte22.o": 0.065,
|
||||
"build/src/libgte23.o": 0.065,
|
||||
"build/src/libgte24.o": 0.067,
|
||||
"build/src/libgte25.o": 0.086,
|
||||
"build/src/libgte26.o": 0.059,
|
||||
"build/src/libgte27.o": 0.071,
|
||||
"build/src/libgte28.o": 0.064,
|
||||
"build/src/libgte29.o": 0.071,
|
||||
"build/src/libgte3.o": 0.07,
|
||||
"build/src/libgte30.o": 0.072,
|
||||
"build/src/libgte4.o": 0.066,
|
||||
"build/src/libgte24.o": 0.064,
|
||||
"build/src/libgte25.o": 0.072,
|
||||
"build/src/libgte26.o": 0.067,
|
||||
"build/src/libgte27.o": 0.063,
|
||||
"build/src/libgte28.o": 0.063,
|
||||
"build/src/libgte29.o": 0.062,
|
||||
"build/src/libgte3.o": 0.068,
|
||||
"build/src/libgte30.o": 0.064,
|
||||
"build/src/libgte4.o": 0.063,
|
||||
"build/src/libgte5.o": 0.063,
|
||||
"build/src/libgte6.o": 0.066,
|
||||
"build/src/libgte7.o": 0.066,
|
||||
"build/src/libgte8.o": 0.065,
|
||||
"build/src/libgte9.o": 0.067,
|
||||
"build/src/libmcrd1.o": 0.091,
|
||||
"build/src/libmcrd2.o": 0.075,
|
||||
"build/src/libpad1.o": 0.07,
|
||||
"build/src/libpad2.o": 0.082,
|
||||
"build/src/sgap.o": 0.063,
|
||||
"build/src/sgap_2.o": 0.063,
|
||||
"build/src/sgap_3.o": 0.062,
|
||||
"build/src/sgap_4.o": 0.074,
|
||||
"build/src/sgap_5.o": 0.071,
|
||||
"build/src/sgap_6.o": 0.062,
|
||||
"build/src/sgap_8.o": 0.073,
|
||||
"build/src/snd1.o": 0.084,
|
||||
"build/src/snd10.o": 0.071,
|
||||
"build/src/snd11.o": 0.065,
|
||||
"build/src/snd12.o": 0.065,
|
||||
"build/src/snd2.o": 0.093,
|
||||
"build/src/snd3.o": 0.065,
|
||||
"build/src/snd4.o": 0.062,
|
||||
"build/src/snd5.o": 0.061,
|
||||
"build/src/snd6.o": 0.079,
|
||||
"build/src/snd7.o": 0.068,
|
||||
"build/src/snd8.o": 0.074,
|
||||
"build/src/snd9.o": 0.08,
|
||||
"build/src/ov_SC03_014/ov_SC03_014.o": 0.136,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_after.o": 0.505,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_8012ACE0.o": 0.394,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_80135888.o": 0.059,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_80135A4C.o": 0.062,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_80135D20.o": 0.127,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_801380E0.o": 0.16,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_8013C98C.o": 0.127,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_8013F350.o": 0.08,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_8013FFD8.o": 0.078,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_80140608.o": 0.183,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_8015444C.o": 0.07,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_80154C24.o": 0.171,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_801588CC.o": 0.1,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_80159C84.o": 0.072,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_8015A3C8.o": 0.072,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_8015AE2C.o": 0.1,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_8015C32C.o": 0.498,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_8016AB6C.o": 0.275,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_80171B4C.o": 0.101,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_801734BC.o": 0.213,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_801789AC.o": 0.061,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_80178D40.o": 0.106,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_8017A4AC.o": 0.074,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_8017AE2C.o": 0.159,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_8017EB7C.o": 0.212,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_80184440.o": 0.054,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_801848E4.o": 0.281,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_o0b.o": 0.061,
|
||||
"build/src/libgte6.o": 0.068,
|
||||
"build/src/libgte7.o": 0.064,
|
||||
"build/src/libgte8.o": 0.067,
|
||||
"build/src/libgte9.o": 0.066,
|
||||
"build/src/libmcrd1.o": 0.075,
|
||||
"build/src/libmcrd2.o": 0.068,
|
||||
"build/src/libpad1.o": 0.069,
|
||||
"build/src/libpad2.o": 0.069,
|
||||
"build/src/sgap.o": 0.075,
|
||||
"build/src/sgap_2.o": 0.088,
|
||||
"build/src/sgap_3.o": 0.059,
|
||||
"build/src/sgap_4.o": 0.075,
|
||||
"build/src/sgap_5.o": 0.065,
|
||||
"build/src/sgap_6.o": 0.06,
|
||||
"build/src/sgap_8.o": 0.072,
|
||||
"build/src/snd1.o": 0.07,
|
||||
"build/src/snd10.o": 0.059,
|
||||
"build/src/snd11.o": 0.069,
|
||||
"build/src/snd12.o": 0.062,
|
||||
"build/src/snd2.o": 0.072,
|
||||
"build/src/snd3.o": 0.064,
|
||||
"build/src/snd4.o": 0.066,
|
||||
"build/src/snd5.o": 0.06,
|
||||
"build/src/snd6.o": 0.071,
|
||||
"build/src/snd7.o": 0.066,
|
||||
"build/src/snd8.o": 0.069,
|
||||
"build/src/snd9.o": 0.064,
|
||||
"build/src/ov_SC03_014/ov_SC03_014.o": 0.134,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_after.o": 0.5,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_8012ACE0.o": 0.379,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_80135888.o": 0.06,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_80135A4C.o": 0.057,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_80135D20.o": 0.121,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_801380E0.o": 0.165,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_8013C98C.o": 0.12,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_8013F350.o": 0.079,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_8013FFD8.o": 0.07,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_80140608.o": 0.181,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_8015444C.o": 0.08,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_80154C24.o": 0.176,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_801588CC.o": 0.099,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_80159C84.o": 0.069,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_8015A3C8.o": 0.067,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_8015AE2C.o": 0.101,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_8015C32C.o": 0.449,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_8016AB6C.o": 0.257,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_80171B4C.o": 0.095,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_801734BC.o": 0.197,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_801789AC.o": 0.056,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_80178D40.o": 0.097,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_8017A4AC.o": 0.068,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_8017AE2C.o": 0.155,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_8017EB7C.o": 0.196,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_80184440.o": 0.053,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_jr_801848E4.o": 0.276,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_o0b.o": 0.059,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_o0c.o": 0.061,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_o0d.o": 0.058,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_o0e.o": 0.066,
|
||||
"build/src/ov_SC03_015/ov_SC03_015.o": 0.112,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_after.o": 0.469,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_8012ACE0.o": 0.382,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_80135888.o": 0.059,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_80135A4C.o": 0.057,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_80135D20.o": 0.112,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_801380E0.o": 0.144,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_8013C98C.o": 0.111,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_8013F350.o": 0.067,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_8013FFD8.o": 0.066,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_80140608.o": 0.18,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_8015444C.o": 0.071,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_80154C24.o": 0.171,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_o0d.o": 0.057,
|
||||
"build/src/ov_SC03_014/ov_SC03_014_o0e.o": 0.067,
|
||||
"build/src/ov_SC03_015/ov_SC03_015.o": 0.123,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_after.o": 0.504,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_8012ACE0.o": 0.393,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_80135888.o": 0.049,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_80135A4C.o": 0.054,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_80135D20.o": 0.121,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_801380E0.o": 0.148,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_8013C98C.o": 0.104,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_8013F350.o": 0.068,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_8013FFD8.o": 0.064,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_80140608.o": 0.168,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_8015444C.o": 0.065,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_80154C24.o": 0.167,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_801588CC.o": 0.09,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_80159C84.o": 0.068,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_8015A3C8.o": 0.071,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_8015AE2C.o": 0.093,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_8015C32C.o": 0.455,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_80159C84.o": 0.065,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_8015A3C8.o": 0.067,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_8015AE2C.o": 0.098,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_8015C32C.o": 0.449,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_8016AB6C.o": 0.27,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_80171B4C.o": 0.1,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_801734BC.o": 0.197,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_801789AC.o": 0.057,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_80178D40.o": 0.099,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_8017A4AC.o": 0.059,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_8017AE2C.o": 0.168,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_8017EB7C.o": 0.195,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_80184440.o": 0.06,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_801848E4.o": 0.274,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_o0b.o": 0.062,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_o0c.o": 0.058,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_o0d.o": 0.054,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_o0e.o": 0.062,
|
||||
"build/src/ov_SC04_011/ov_SC04_011.o": 0.126,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_after.o": 0.422,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_8012ACE0.o": 0.338,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_80135888.o": 0.05,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_80135A4C.o": 0.055,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_80171B4C.o": 0.101,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_801734BC.o": 0.194,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_801789AC.o": 0.061,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_80178D40.o": 0.098,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_8017A4AC.o": 0.063,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_8017AE2C.o": 0.171,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_8017EB7C.o": 0.198,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_80184440.o": 0.053,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_jr_801848E4.o": 0.279,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_o0b.o": 0.067,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_o0c.o": 0.061,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_o0d.o": 0.058,
|
||||
"build/src/ov_SC03_015/ov_SC03_015_o0e.o": 0.078,
|
||||
"build/src/ov_SC04_011/ov_SC04_011.o": 0.121,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_after.o": 0.419,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_8012ACE0.o": 0.355,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_80135888.o": 0.051,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_80135A4C.o": 0.053,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_80135D20.o": 0.116,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_801380E0.o": 0.155,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_8013C98C.o": 0.121,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_8013F350.o": 0.068,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_8013FFD8.o": 0.062,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_80140608.o": 0.175,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_8015444C.o": 0.068,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_80154C24.o": 0.158,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_801588CC.o": 0.086,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_80159C84.o": 0.063,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_8015A3C8.o": 0.068,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_8015AE2C.o": 0.094,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_8015C32C.o": 0.358,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_8016AB6C.o": 0.225,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_80171B4C.o": 0.089,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_801734BC.o": 0.172,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_801789AC.o": 0.053,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_80178D40.o": 0.087,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_8017A4AC.o": 0.064,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_8017AE2C.o": 0.101,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_8017D494.o": 0.432,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_o0b.o": 0.046,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_o0c.o": 0.06
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_801380E0.o": 0.142,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_8013C98C.o": 0.113,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_8013F350.o": 0.075,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_8013FFD8.o": 0.06,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_80140608.o": 0.179,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_8015444C.o": 0.07,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_80154C24.o": 0.163,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_801588CC.o": 0.093,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_80159C84.o": 0.066,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_8015A3C8.o": 0.067,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_8015AE2C.o": 0.091,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_8015C32C.o": 0.351,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_8016AB6C.o": 0.224,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_80171B4C.o": 0.09,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_801734BC.o": 0.175,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_801789AC.o": 0.056,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_80178D40.o": 0.092,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_8017A4AC.o": 0.065,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_8017AE2C.o": 0.11,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_jr_8017D494.o": 0.428,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_o0b.o": 0.049,
|
||||
"build/src/ov_SC04_011/ov_SC04_011_o0c.o": 0.066
|
||||
},
|
||||
"ok": true,
|
||||
"seconds": 2.3
|
||||
"seconds": 2.2
|
||||
}
|
||||
|
||||
@@ -3714,3 +3714,37 @@ register lever (accelerators (16)), and treat "PROVED" as "proved against this l
|
||||
35 minutes of state only in its transcript. (3) Build the disagreeing oracle before the batch runs, not after: the text oracle found
|
||||
in one run what the byte join could never see. (4) A tool's restore path is part of the tool (R57): `git checkout` on an uncommitted
|
||||
tree is a destroyer with a green exit code.
|
||||
|
||||
## P36 S101 (2026-09-09) — the levers phase pivots from search to reading: a scored engine, the head as the number, and one agent at a time
|
||||
|
||||
**Context and belief.** After T6 the residue was 33,427 sites in 12,048 bodies (1,755 text classes); the free recipe rung had measured
|
||||
as a replication engine (134/134 on a known shape, 0/300 on unknown ones, cookbook §454a) and the permuter as a random walk that
|
||||
reprints the source. Drew's plan for S101: build a compute-only guided engine ("a fancier permuter, focused on pin pulling and C shape
|
||||
matching") while a Fable agent reads gcc 2.7.2's source for the residual → move map, and defer the exemplar triage.
|
||||
|
||||
**What was built and what it measured.** Rung G (`tools/delever_search.py`): the candidate is scored by the function's instructions in
|
||||
the oracle's own scratch object against the fleet run's baseline object (an edit distance over the reloc-masked words — a positional
|
||||
count read one inlined temp as 43), the residual classified from the diff blocks, the move families ordered by the class from lane
|
||||
B's map and ranked round-robin, a beam composing two to five moves, every score-0 banked through the real recipe and propagated to the
|
||||
class. Six runs, no drafting tokens: g1 1/16, g2 1/16, g3 13/64 (1,516 bodies), g4s 3/38, g5 5/70, g6+g6b 110/400 (the tail) —
|
||||
33,427 → 30,358 sites, every bank gated 218/218. The generator registry grew from six moves to fourteen, each new one read off a
|
||||
residual with `--explain` and, where lane B's map named it, verified on bytes (a constant-operand swap is byte-neutral; the do-while
|
||||
lever is the ref weight in `.lreg`; the 16-bit copy does not reach a value whose known bits already fit).
|
||||
|
||||
**What failed, and why it looked right.** Wider search looked like the lever after g3 (nine of fifteen moved past g1's best when the
|
||||
beam widened); g5 then spent 112,216 compiles at three times the beam and eight times the budget for 62 bodies in small classes and
|
||||
closed none of the ~50 head classes. The instrument also lied twice before it was caught by its own controls: a per-tag scratch
|
||||
object let two workers read each other's objects (a byte-identical body scored 14,871), and the ledger's rung-R miss rows carried no
|
||||
text hash (301 bodies read as one class). Both were found by reading an artifact, not by a red check.
|
||||
|
||||
**The pivot.** Derived from the ledger at the end: 57 classes of ≥100 copies hold 7,318 of the 9,796 residue bodies, 17 within four
|
||||
instructions. Each is a reading and a generator, not a search. Drew's decision: run T7 as ONE AGENT AT A TIME on the head (he had
|
||||
approved a 57-agent wave and then reshaped it: "time clock isn't the issue, it's efficiency … hone our methodology each time one lands,
|
||||
in the hopes of learning enough to be able to tool crack the rest"), starting in a fresh session. The agent's loop scores its
|
||||
candidate without a tree write (`--try`), the coordinator banks and propagates, the move becomes a generator, the engine sweeps the
|
||||
rest for free — the harvest→toolify gate at the granularity of one crack.
|
||||
|
||||
**Hindsight (the better path).** Read the residual first, always: every generator that closed a class this session came from
|
||||
`--explain` on one body and a line of the compiler's source, and every hour of wider search bought less than the previous one. The
|
||||
engine's real value was not the search but the SCORE — a distance that turns a stall into a named shape — and the instrument that
|
||||
prints it. Build that on day one of a de-lever campaign, before any search.
|
||||
|
||||
@@ -2079,7 +2079,140 @@ def param_copies(text, tu, fn, d_):
|
||||
return out
|
||||
|
||||
|
||||
ALL_FAMILIES = ("R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10", "R12", "R13", "R14")
|
||||
def if_chains(masked, lo, hi):
|
||||
"""[(chain_lo, chain_hi, [(arm_lo, arm_hi)])] — every brace-form if / else-if / else chain whose lines lie in
|
||||
masked[lo:hi] at the body's own depth. `chain_hi` is the line index just past the chain's last `}`; an arm span is the
|
||||
lines strictly inside that arm's braces. Depth is counted on the MASKED text, so a brace in a string or a comment is
|
||||
not a brace. Chains that do not open a brace on the `if` line (a one-statement `if` without braces) are skipped: the
|
||||
sink rewrites arm bodies and needs somewhere to put the statement."""
|
||||
out = []
|
||||
i = lo
|
||||
while i < hi:
|
||||
s_ = masked[i].strip()
|
||||
if not (s_.startswith("if") and re.match(r"^if\s*\(", s_) and s_.endswith("{")):
|
||||
i += 1
|
||||
continue
|
||||
arms, depth, arm_start, j = [], 0, i + 1, i
|
||||
while j < hi:
|
||||
nxt = masked[j].strip()
|
||||
closes = masked[j].count("}")
|
||||
# the CLOSES are counted before the OPENS: on a `} else if (…) {` line the two net to zero, and a depth
|
||||
# counter that adds both at once never comes back to zero — the chain then looks like one unterminated arm.
|
||||
if j > i and depth - closes == 0: # this arm's `}` is on line j
|
||||
arms.append((arm_start, j))
|
||||
m = re.match(r"^\}\s*else\b(.*)$", nxt)
|
||||
if m and m.group(1).strip().endswith("{"): # `} else {` or `} else if (…) {`
|
||||
arm_start, depth, j = j + 1, 1, j + 1
|
||||
continue
|
||||
if nxt == "}" and j + 1 < hi and re.match(r"^else\b", masked[j + 1].strip()) \
|
||||
and masked[j + 1].strip().endswith("{"):
|
||||
arm_start, depth, j = j + 2, 1, j + 2 # the `else …{` on its own line
|
||||
continue
|
||||
break
|
||||
depth += masked[j].count("{") - closes
|
||||
j += 1
|
||||
if len(arms) >= 2:
|
||||
out.append((i, j + 1, arms))
|
||||
i = j + 1
|
||||
else:
|
||||
i += 1
|
||||
return out
|
||||
|
||||
|
||||
def sink_merges(text, tu, fn, d_):
|
||||
"""[(description, candidate text)] — R15: the statement AFTER an if/else chain sunk into every arm, and the variables
|
||||
it consumed deleted. `if (c) { v = e1; } else { v = e2; } w = f(v);` -> `if (c) { w = f(e1); } else { w = f(e2); }`.
|
||||
|
||||
T7 agent a1's crack of func_80156044 (2026-09-10, 130 bodies, the rank-1 head class six rung-G runs left at best 1).
|
||||
The mechanism is a REGISTER move, not a scheduling one: a variable set in every arm and read after the merge is a
|
||||
CROSS-BLOCK pseudo, so (i) local-alloc never makes a quantity for it (`local-alloc.c:472`, `next_qty` reset per block
|
||||
at `:517`) and each arm holds two quantities, which is `block_alloc`'s unrolled `case 2` at `local-alloc.c:1499-1502`
|
||||
— one `qty_compare` (`:1578-1596`), higher density first; sinking makes it a third block-local quantity and
|
||||
`case 3` at `:1491-1496` FALLS THROUGH into `case 2`, applying that comparison a second time and undoing its own
|
||||
exchange, so the two caller-saved colours swap; and (ii) while it is a global allocno it can inherit a copy
|
||||
preference from whatever the merge statement's result is passed to (`set_preference` `global.c:1535+`, merged by
|
||||
`expand_preferences` `global.c:781-825`, overriding first-fit at `global.c:1034-1067`) — sinking removes it from
|
||||
`global.c` entirely. Read the whole reading in `.run/P36/agents/ov_SC04_011__func_80156044/mechanism.md`.
|
||||
|
||||
Applicability is checked, not assumed: every consumed variable must be assigned exactly once in EVERY arm by a simple
|
||||
statement, must appear in the merge statement, and must occur nowhere else in the function (declaration + one
|
||||
assignment per arm + its uses in the merge statement is its whole census) — otherwise the rewrite would change what
|
||||
the code reads. The bytes remain the correctness proof (a rewrite that changes behaviour simply DIFFERS)."""
|
||||
lines = text.split("\n")
|
||||
masked = [sc.mask_text(l) for l in lines]
|
||||
lo, hi = d_["line"], d_["end"] - 1
|
||||
whole = sc.mask_text("\n".join(lines[d_["line"] - 1:d_["end"]]))
|
||||
out = []
|
||||
for c_lo, c_hi, arms in if_chains(masked, lo, hi):
|
||||
j = next((k for k in range(c_hi, hi) if masked[k].strip()), None)
|
||||
if j is None or not simple_stmt(masked[j]):
|
||||
continue
|
||||
merge_masked = masked[j]
|
||||
eq = re.search(r"(?<![=!<>+\-*/%&|^~])=(?!=)", merge_masked)
|
||||
if not eq: # only an assignment merges arm values
|
||||
continue
|
||||
rhs = merge_masked[eq.end():]
|
||||
# every variable assigned exactly once, by a simple statement, in EVERY arm
|
||||
per_arm = []
|
||||
for a_lo, a_hi in arms:
|
||||
got = {}
|
||||
for k in range(a_lo, a_hi):
|
||||
if not simple_stmt(masked[k]):
|
||||
continue
|
||||
m = re.match(r"^\s*([A-Za-z_]\w*)\s*=\s*(.+);\s*$", masked[k])
|
||||
if not m:
|
||||
continue
|
||||
if m.group(1) in got: # assigned twice in one arm: not a single value
|
||||
got[m.group(1)] = None
|
||||
else:
|
||||
got[m.group(1)] = k
|
||||
per_arm.append(got)
|
||||
common = set(k for k, v in per_arm[0].items() if v is not None)
|
||||
for g in per_arm[1:]:
|
||||
common &= set(k for k, v in g.items() if v is not None)
|
||||
names_ = sorted(v for v in common if re.search(r"(?<![\w.>])%s(?![\w])" % re.escape(v), rhs))
|
||||
if not names_:
|
||||
continue
|
||||
decl = {}
|
||||
for v in names_:
|
||||
# the variable's whole census: one declaration, one assignment per arm, and its uses in the merge statement
|
||||
uses = len(re.findall(r"(?<![\w.>])%s(?![\w])" % re.escape(v), whole))
|
||||
in_merge = len(re.findall(r"(?<![\w.>])%s(?![\w])" % re.escape(v), merge_masked))
|
||||
d_line = next((k for k in range(lo, hi)
|
||||
if is_decl_line(masked[k].strip())
|
||||
and re.search(r"(?<![\w.>])%s(?![\w])\s*(?:=|;|,)" % re.escape(v), masked[k])), None)
|
||||
if d_line is None or MULTI_DECL.match(masked[d_line]) or "=" in masked[d_line]:
|
||||
decl[v] = None # a shared or initialised declaration: leave it standing
|
||||
else:
|
||||
decl[v] = d_line
|
||||
if uses != len(arms) + in_merge + (1 if d_line is not None else 0):
|
||||
names_ = None
|
||||
break
|
||||
if not names_:
|
||||
continue
|
||||
drop = {decl[v] for v in names_ if decl[v] is not None} | {j}
|
||||
after = {} # line index -> the sunk statement to emit just after it
|
||||
for a_idx, (a_lo, a_hi) in enumerate(arms):
|
||||
stmt = lines[j].strip()
|
||||
for v in names_:
|
||||
k = per_arm[a_idx][v]
|
||||
expr = re.match(r"^\s*[A-Za-z_]\w*\s*=\s*(.+);\s*$", lines[k]).group(1).strip()
|
||||
stmt = re.sub(r"(?<![\w.>])%s(?![\w])" % re.escape(v), "(" + expr + ")", stmt)
|
||||
drop.add(k)
|
||||
tail = a_hi - 1 # the arm's LAST BODY line (a_hi is its `}`)
|
||||
ref = lines[tail] if lines[tail].strip() else lines[a_lo]
|
||||
after[tail] = ref[:len(ref) - len(ref.lstrip())] + stmt
|
||||
cand = []
|
||||
for k, l in enumerate(lines): # a dropped line may still be the one we append after
|
||||
if k not in drop:
|
||||
cand.append(l)
|
||||
if k in after:
|
||||
cand.append(after[k])
|
||||
out.append((f"sink @{j + 1} ({','.join(names_)})", "\n".join(cand)))
|
||||
return out
|
||||
|
||||
|
||||
ALL_FAMILIES = ("R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10", "R12", "R13", "R14", "R15")
|
||||
RUNG_R_FAMILIES = ("R2", "R3", "R4", "R5", "R6", "R7") # the free sweep's set (R8/R9 are the search engine's until measured)
|
||||
|
||||
|
||||
@@ -2184,6 +2317,9 @@ def recipe_candidates(text, tu, fn, names, limit=24, rng=None, cap=40, blocks=Tr
|
||||
if "R13" in fam:
|
||||
for desc, cand in reassociations(text, tu, fn, d_):
|
||||
out.append(("R13", desc, cand))
|
||||
if "R15" in fam:
|
||||
for desc, cand in sink_merges(text, tu, fn, d_):
|
||||
out.append(("R15", 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))
|
||||
@@ -2894,6 +3030,53 @@ def selftest():
|
||||
fail(f"remap_body produced {got!r} ({why})")
|
||||
if remap_body(exb, exa, "void func_80300000(void) { D_80400000 = 0; }")[0] is not None:
|
||||
fail("remap_body must refuse a sibling with a different token count")
|
||||
# R15, the sink (T7 agent a1's crack of func_80156044, 2026-09-10): the merge statement pushed into every arm and the
|
||||
# variables it consumed deleted. The three controls are the ones the applicability test exists for.
|
||||
SINKFIX = ("void func_80100000(int c) {\n"
|
||||
" s32 base;\n"
|
||||
" u8 *row;\n"
|
||||
"\n"
|
||||
" if (c) {\n"
|
||||
" base = 1;\n"
|
||||
" } else if (c == 2) {\n"
|
||||
" base = 2;\n"
|
||||
" } else {\n"
|
||||
" base = 3;\n"
|
||||
" }\n"
|
||||
" row = (u8 *)(base + 4);\n"
|
||||
" use(row);\n"
|
||||
"}")
|
||||
d15 = next(r for r in sc.scan_text(SINKFIX, "src/fx/s.c", shared_defs=None)
|
||||
if r["form"] == "def" and r["name"] == "func_80100000")
|
||||
got15 = sink_merges(SINKFIX, "src/fx/s.c", "func_80100000", d15)
|
||||
if len(got15) != 1 or "sink" not in got15[0][0] or "base" not in got15[0][0]:
|
||||
fail(f"R15 must find one sink in the three-arm fixture, got {[g[0] for g in got15]}")
|
||||
else:
|
||||
c15 = got15[0][1]
|
||||
want15 = ["row = (u8 *)((1) + 4);", "row = (u8 *)((2) + 4);", "row = (u8 *)((3) + 4);"]
|
||||
if not all(w in c15 for w in want15):
|
||||
fail(f"R15 must sink the merge statement into every arm: {c15!r}")
|
||||
if "s32 base;" in c15 or "base = 1;" in c15:
|
||||
fail("R15 must delete the consumed variable's declaration and its per-arm assignments")
|
||||
if c15.count("row = ") != 3 or "row = (u8 *)(base + 4);" in c15:
|
||||
fail("R15 must remove the merge statement itself")
|
||||
if "u8 *row;" not in c15:
|
||||
fail("R15 must keep the declaration of the variable the merge statement ASSIGNS")
|
||||
# control 1: a consumed variable read anywhere else is not sinkable (its census would not add up)
|
||||
if sink_merges(SINKFIX.replace(" use(row);", " use(row + base);"), "src/fx/s.c", "func_80100000",
|
||||
next(r for r in sc.scan_text(SINKFIX.replace(" use(row);", " use(row + base);"), "src/fx/s.c",
|
||||
shared_defs=None) if r["form"] == "def")):
|
||||
fail("R15 must refuse a variable that is read after the merge statement")
|
||||
# control 2: a variable not set in EVERY arm is not sinkable
|
||||
if sink_merges(SINKFIX.replace(" base = 2;\n", ""), "src/fx/s.c", "func_80100000",
|
||||
next(r for r in sc.scan_text(SINKFIX.replace(" base = 2;\n", ""), "src/fx/s.c",
|
||||
shared_defs=None) if r["form"] == "def")):
|
||||
fail("R15 must refuse a variable one arm does not set")
|
||||
# control 3: the brace walk itself — a `} else if (…) {` line nets to zero braces and must still close its arm
|
||||
ch = if_chains([sc.mask_text(l) for l in SINKFIX.split("\n")], d15["line"], d15["end"] - 1)
|
||||
if len(ch) != 1 or len(ch[0][2]) != 3:
|
||||
fail(f"if_chains must see three arms in the fixture, got {ch}")
|
||||
|
||||
# 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")
|
||||
|
||||
@@ -82,15 +82,18 @@ FAMILIES = {
|
||||
# lane B's map (`.run/P36/engine/residual_moves.md`, S101, gcc 2.7.2 source): the caller-saved swap is decided in
|
||||
# local-alloc's block_alloc/combine_regs by which dying pseudo the operand ties to — the temp inlined/introduced first; a
|
||||
# constant-operand commutative swap is undone by fold (fold-const.c) and only a var/var swap can reach the allocator.
|
||||
"REG-caller": ("R6", "R8", "R5", "R10", "R12", "R14", "R13", "R3", "R7", "R9", "R2", "R4"),
|
||||
# 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"),
|
||||
# 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", "R12", "R7", "R9", "R10", "R14", "R13", "R5"),
|
||||
"REG-mixed": ("R6", "R2", "R5", "R10", "R4", "R3", "R8", "R12", "R13", "R14", "R7", "R9"),
|
||||
"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"),
|
||||
# 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", "R6", "R8", "R3", "R7", "R5", "R13", "R9", "R10", "R2", "R4"),
|
||||
"COUNT": ("R12", "R14", "R15", "R6", "R8", "R3", "R7", "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", "R2", "R4"),
|
||||
"ORDER": ("R9", "R7", "R13", "R3", "R6", "R8", "R5", "R12", "R14", "R10", "R15", "R2", "R4"),
|
||||
"MIXED": dl.ALL_FAMILIES,
|
||||
"OTHER": dl.ALL_FAMILIES,
|
||||
}
|
||||
@@ -839,8 +842,11 @@ 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}")
|
||||
if family_key(c) != "REG-caller" or FAMILIES["REG-caller"][0] != "R6" or "R5" not in FAMILIES["REG-caller"][:3]:
|
||||
fail(f"REG-caller family wrong: {family_key(c)} {FAMILIES['REG-caller'][:3]}")
|
||||
# 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]}")
|
||||
# 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
File diff suppressed because one or more lines are too long
+184
-1
@@ -2079,7 +2079,140 @@ def param_copies(text, tu, fn, d_):
|
||||
return out
|
||||
|
||||
|
||||
ALL_FAMILIES = ("R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10", "R12", "R13", "R14")
|
||||
def if_chains(masked, lo, hi):
|
||||
"""[(chain_lo, chain_hi, [(arm_lo, arm_hi)])] — every brace-form if / else-if / else chain whose lines lie in
|
||||
masked[lo:hi] at the body's own depth. `chain_hi` is the line index just past the chain's last `}`; an arm span is the
|
||||
lines strictly inside that arm's braces. Depth is counted on the MASKED text, so a brace in a string or a comment is
|
||||
not a brace. Chains that do not open a brace on the `if` line (a one-statement `if` without braces) are skipped: the
|
||||
sink rewrites arm bodies and needs somewhere to put the statement."""
|
||||
out = []
|
||||
i = lo
|
||||
while i < hi:
|
||||
s_ = masked[i].strip()
|
||||
if not (s_.startswith("if") and re.match(r"^if\s*\(", s_) and s_.endswith("{")):
|
||||
i += 1
|
||||
continue
|
||||
arms, depth, arm_start, j = [], 0, i + 1, i
|
||||
while j < hi:
|
||||
nxt = masked[j].strip()
|
||||
closes = masked[j].count("}")
|
||||
# the CLOSES are counted before the OPENS: on a `} else if (…) {` line the two net to zero, and a depth
|
||||
# counter that adds both at once never comes back to zero — the chain then looks like one unterminated arm.
|
||||
if j > i and depth - closes == 0: # this arm's `}` is on line j
|
||||
arms.append((arm_start, j))
|
||||
m = re.match(r"^\}\s*else\b(.*)$", nxt)
|
||||
if m and m.group(1).strip().endswith("{"): # `} else {` or `} else if (…) {`
|
||||
arm_start, depth, j = j + 1, 1, j + 1
|
||||
continue
|
||||
if nxt == "}" and j + 1 < hi and re.match(r"^else\b", masked[j + 1].strip()) \
|
||||
and masked[j + 1].strip().endswith("{"):
|
||||
arm_start, depth, j = j + 2, 1, j + 2 # the `else …{` on its own line
|
||||
continue
|
||||
break
|
||||
depth += masked[j].count("{") - closes
|
||||
j += 1
|
||||
if len(arms) >= 2:
|
||||
out.append((i, j + 1, arms))
|
||||
i = j + 1
|
||||
else:
|
||||
i += 1
|
||||
return out
|
||||
|
||||
|
||||
def sink_merges(text, tu, fn, d_):
|
||||
"""[(description, candidate text)] — R15: the statement AFTER an if/else chain sunk into every arm, and the variables
|
||||
it consumed deleted. `if (c) { v = e1; } else { v = e2; } w = f(v);` -> `if (c) { w = f(e1); } else { w = f(e2); }`.
|
||||
|
||||
T7 agent a1's crack of func_80156044 (2026-09-10, 130 bodies, the rank-1 head class six rung-G runs left at best 1).
|
||||
The mechanism is a REGISTER move, not a scheduling one: a variable set in every arm and read after the merge is a
|
||||
CROSS-BLOCK pseudo, so (i) local-alloc never makes a quantity for it (`local-alloc.c:472`, `next_qty` reset per block
|
||||
at `:517`) and each arm holds two quantities, which is `block_alloc`'s unrolled `case 2` at `local-alloc.c:1499-1502`
|
||||
— one `qty_compare` (`:1578-1596`), higher density first; sinking makes it a third block-local quantity and
|
||||
`case 3` at `:1491-1496` FALLS THROUGH into `case 2`, applying that comparison a second time and undoing its own
|
||||
exchange, so the two caller-saved colours swap; and (ii) while it is a global allocno it can inherit a copy
|
||||
preference from whatever the merge statement's result is passed to (`set_preference` `global.c:1535+`, merged by
|
||||
`expand_preferences` `global.c:781-825`, overriding first-fit at `global.c:1034-1067`) — sinking removes it from
|
||||
`global.c` entirely. Read the whole reading in `.run/P36/agents/ov_SC04_011__func_80156044/mechanism.md`.
|
||||
|
||||
Applicability is checked, not assumed: every consumed variable must be assigned exactly once in EVERY arm by a simple
|
||||
statement, must appear in the merge statement, and must occur nowhere else in the function (declaration + one
|
||||
assignment per arm + its uses in the merge statement is its whole census) — otherwise the rewrite would change what
|
||||
the code reads. The bytes remain the correctness proof (a rewrite that changes behaviour simply DIFFERS)."""
|
||||
lines = text.split("\n")
|
||||
masked = [sc.mask_text(l) for l in lines]
|
||||
lo, hi = d_["line"], d_["end"] - 1
|
||||
whole = sc.mask_text("\n".join(lines[d_["line"] - 1:d_["end"]]))
|
||||
out = []
|
||||
for c_lo, c_hi, arms in if_chains(masked, lo, hi):
|
||||
j = next((k for k in range(c_hi, hi) if masked[k].strip()), None)
|
||||
if j is None or not simple_stmt(masked[j]):
|
||||
continue
|
||||
merge_masked = masked[j]
|
||||
eq = re.search(r"(?<![=!<>+\-*/%&|^~])=(?!=)", merge_masked)
|
||||
if not eq: # only an assignment merges arm values
|
||||
continue
|
||||
rhs = merge_masked[eq.end():]
|
||||
# every variable assigned exactly once, by a simple statement, in EVERY arm
|
||||
per_arm = []
|
||||
for a_lo, a_hi in arms:
|
||||
got = {}
|
||||
for k in range(a_lo, a_hi):
|
||||
if not simple_stmt(masked[k]):
|
||||
continue
|
||||
m = re.match(r"^\s*([A-Za-z_]\w*)\s*=\s*(.+);\s*$", masked[k])
|
||||
if not m:
|
||||
continue
|
||||
if m.group(1) in got: # assigned twice in one arm: not a single value
|
||||
got[m.group(1)] = None
|
||||
else:
|
||||
got[m.group(1)] = k
|
||||
per_arm.append(got)
|
||||
common = set(k for k, v in per_arm[0].items() if v is not None)
|
||||
for g in per_arm[1:]:
|
||||
common &= set(k for k, v in g.items() if v is not None)
|
||||
names_ = sorted(v for v in common if re.search(r"(?<![\w.>])%s(?![\w])" % re.escape(v), rhs))
|
||||
if not names_:
|
||||
continue
|
||||
decl = {}
|
||||
for v in names_:
|
||||
# the variable's whole census: one declaration, one assignment per arm, and its uses in the merge statement
|
||||
uses = len(re.findall(r"(?<![\w.>])%s(?![\w])" % re.escape(v), whole))
|
||||
in_merge = len(re.findall(r"(?<![\w.>])%s(?![\w])" % re.escape(v), merge_masked))
|
||||
d_line = next((k for k in range(lo, hi)
|
||||
if is_decl_line(masked[k].strip())
|
||||
and re.search(r"(?<![\w.>])%s(?![\w])\s*(?:=|;|,)" % re.escape(v), masked[k])), None)
|
||||
if d_line is None or MULTI_DECL.match(masked[d_line]) or "=" in masked[d_line]:
|
||||
decl[v] = None # a shared or initialised declaration: leave it standing
|
||||
else:
|
||||
decl[v] = d_line
|
||||
if uses != len(arms) + in_merge + (1 if d_line is not None else 0):
|
||||
names_ = None
|
||||
break
|
||||
if not names_:
|
||||
continue
|
||||
drop = {decl[v] for v in names_ if decl[v] is not None} | {j}
|
||||
after = {} # line index -> the sunk statement to emit just after it
|
||||
for a_idx, (a_lo, a_hi) in enumerate(arms):
|
||||
stmt = lines[j].strip()
|
||||
for v in names_:
|
||||
k = per_arm[a_idx][v]
|
||||
expr = re.match(r"^\s*[A-Za-z_]\w*\s*=\s*(.+);\s*$", lines[k]).group(1).strip()
|
||||
stmt = re.sub(r"(?<![\w.>])%s(?![\w])" % re.escape(v), "(" + expr + ")", stmt)
|
||||
drop.add(k)
|
||||
tail = a_hi - 1 # the arm's LAST BODY line (a_hi is its `}`)
|
||||
ref = lines[tail] if lines[tail].strip() else lines[a_lo]
|
||||
after[tail] = ref[:len(ref) - len(ref.lstrip())] + stmt
|
||||
cand = []
|
||||
for k, l in enumerate(lines): # a dropped line may still be the one we append after
|
||||
if k not in drop:
|
||||
cand.append(l)
|
||||
if k in after:
|
||||
cand.append(after[k])
|
||||
out.append((f"sink @{j + 1} ({','.join(names_)})", "\n".join(cand)))
|
||||
return out
|
||||
|
||||
|
||||
ALL_FAMILIES = ("R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10", "R12", "R13", "R14", "R15")
|
||||
RUNG_R_FAMILIES = ("R2", "R3", "R4", "R5", "R6", "R7") # the free sweep's set (R8/R9 are the search engine's until measured)
|
||||
|
||||
|
||||
@@ -2184,6 +2317,9 @@ def recipe_candidates(text, tu, fn, names, limit=24, rng=None, cap=40, blocks=Tr
|
||||
if "R13" in fam:
|
||||
for desc, cand in reassociations(text, tu, fn, d_):
|
||||
out.append(("R13", desc, cand))
|
||||
if "R15" in fam:
|
||||
for desc, cand in sink_merges(text, tu, fn, d_):
|
||||
out.append(("R15", 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))
|
||||
@@ -2894,6 +3030,53 @@ def selftest():
|
||||
fail(f"remap_body produced {got!r} ({why})")
|
||||
if remap_body(exb, exa, "void func_80300000(void) { D_80400000 = 0; }")[0] is not None:
|
||||
fail("remap_body must refuse a sibling with a different token count")
|
||||
# R15, the sink (T7 agent a1's crack of func_80156044, 2026-09-10): the merge statement pushed into every arm and the
|
||||
# variables it consumed deleted. The three controls are the ones the applicability test exists for.
|
||||
SINKFIX = ("void func_80100000(int c) {\n"
|
||||
" s32 base;\n"
|
||||
" u8 *row;\n"
|
||||
"\n"
|
||||
" if (c) {\n"
|
||||
" base = 1;\n"
|
||||
" } else if (c == 2) {\n"
|
||||
" base = 2;\n"
|
||||
" } else {\n"
|
||||
" base = 3;\n"
|
||||
" }\n"
|
||||
" row = (u8 *)(base + 4);\n"
|
||||
" use(row);\n"
|
||||
"}")
|
||||
d15 = next(r for r in sc.scan_text(SINKFIX, "src/fx/s.c", shared_defs=None)
|
||||
if r["form"] == "def" and r["name"] == "func_80100000")
|
||||
got15 = sink_merges(SINKFIX, "src/fx/s.c", "func_80100000", d15)
|
||||
if len(got15) != 1 or "sink" not in got15[0][0] or "base" not in got15[0][0]:
|
||||
fail(f"R15 must find one sink in the three-arm fixture, got {[g[0] for g in got15]}")
|
||||
else:
|
||||
c15 = got15[0][1]
|
||||
want15 = ["row = (u8 *)((1) + 4);", "row = (u8 *)((2) + 4);", "row = (u8 *)((3) + 4);"]
|
||||
if not all(w in c15 for w in want15):
|
||||
fail(f"R15 must sink the merge statement into every arm: {c15!r}")
|
||||
if "s32 base;" in c15 or "base = 1;" in c15:
|
||||
fail("R15 must delete the consumed variable's declaration and its per-arm assignments")
|
||||
if c15.count("row = ") != 3 or "row = (u8 *)(base + 4);" in c15:
|
||||
fail("R15 must remove the merge statement itself")
|
||||
if "u8 *row;" not in c15:
|
||||
fail("R15 must keep the declaration of the variable the merge statement ASSIGNS")
|
||||
# control 1: a consumed variable read anywhere else is not sinkable (its census would not add up)
|
||||
if sink_merges(SINKFIX.replace(" use(row);", " use(row + base);"), "src/fx/s.c", "func_80100000",
|
||||
next(r for r in sc.scan_text(SINKFIX.replace(" use(row);", " use(row + base);"), "src/fx/s.c",
|
||||
shared_defs=None) if r["form"] == "def")):
|
||||
fail("R15 must refuse a variable that is read after the merge statement")
|
||||
# control 2: a variable not set in EVERY arm is not sinkable
|
||||
if sink_merges(SINKFIX.replace(" base = 2;\n", ""), "src/fx/s.c", "func_80100000",
|
||||
next(r for r in sc.scan_text(SINKFIX.replace(" base = 2;\n", ""), "src/fx/s.c",
|
||||
shared_defs=None) if r["form"] == "def")):
|
||||
fail("R15 must refuse a variable one arm does not set")
|
||||
# control 3: the brace walk itself — a `} else if (…) {` line nets to zero braces and must still close its arm
|
||||
ch = if_chains([sc.mask_text(l) for l in SINKFIX.split("\n")], d15["line"], d15["end"] - 1)
|
||||
if len(ch) != 1 or len(ch[0][2]) != 3:
|
||||
fail(f"if_chains must see three arms in the fixture, got {ch}")
|
||||
|
||||
# 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")
|
||||
|
||||
+13
-7
@@ -82,15 +82,18 @@ FAMILIES = {
|
||||
# lane B's map (`.run/P36/engine/residual_moves.md`, S101, gcc 2.7.2 source): the caller-saved swap is decided in
|
||||
# local-alloc's block_alloc/combine_regs by which dying pseudo the operand ties to — the temp inlined/introduced first; a
|
||||
# constant-operand commutative swap is undone by fold (fold-const.c) and only a var/var swap can reach the allocator.
|
||||
"REG-caller": ("R6", "R8", "R5", "R10", "R12", "R14", "R13", "R3", "R7", "R9", "R2", "R4"),
|
||||
# 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"),
|
||||
# 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", "R12", "R7", "R9", "R10", "R14", "R13", "R5"),
|
||||
"REG-mixed": ("R6", "R2", "R5", "R10", "R4", "R3", "R8", "R12", "R13", "R14", "R7", "R9"),
|
||||
"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"),
|
||||
# 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", "R6", "R8", "R3", "R7", "R5", "R13", "R9", "R10", "R2", "R4"),
|
||||
"COUNT": ("R12", "R14", "R15", "R6", "R8", "R3", "R7", "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", "R2", "R4"),
|
||||
"ORDER": ("R9", "R7", "R13", "R3", "R6", "R8", "R5", "R12", "R14", "R10", "R15", "R2", "R4"),
|
||||
"MIXED": dl.ALL_FAMILIES,
|
||||
"OTHER": dl.ALL_FAMILIES,
|
||||
}
|
||||
@@ -839,8 +842,11 @@ 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}")
|
||||
if family_key(c) != "REG-caller" or FAMILIES["REG-caller"][0] != "R6" or "R5" not in FAMILIES["REG-caller"][:3]:
|
||||
fail(f"REG-caller family wrong: {family_key(c)} {FAMILIES['REG-caller'][:3]}")
|
||||
# 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]}")
|
||||
# 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":
|
||||
|
||||
Reference in New Issue
Block a user