phase-36: T2 — the oracle and the probe: tools/delever_oracle.py (4,284 recipes via make -n -W; a candidate compiled in place to a scratch object and compared with the fleet run's; calibrated 177/177 untouched + twin 32/32 + positive control DIFFERS) and tools/delever.py (positional rewrites per lever class, rung A strip-all + rung B greedy, headers serial after the TU phase); --probe on 283 distinct bodies: A identical 53/283, sites removed 561/1,309 (42.9 %), needed 748 (launder 77 %, barrier 82 %, pins 45 % — v0/v1 + a0–a3 dominant), 0.225 s per compile — T4 priced at ~94k compiles ≈ 0.5 h, the residue ≈ 81 % of bodies ≈ 1,800 distinct exemplars; dictionary + SETUP rows; kit corpus; the 🛑 block for T3

This commit is contained in:
Drew T
2026-09-09 02:03:04 -06:00
parent a1407bdfc1
commit 617f29e30c
23 changed files with 3133 additions and 18 deletions
+2
View File
@@ -479,10 +479,12 @@ unsloth_compiled_cache/
!/.run/P36/probe/*.md
!/.run/P36/probe/*.json
!/.run/P36/probe/*.py
!/.run/P36/probe/*.jsonl
!/.run/P36/delever/
/.run/P36/delever/*
!/.run/P36/delever/*.log
!/.run/P36/delever/*.json
/.run/P36/delever/recipes.json
!/.run/P36/delever/*.jsonl
!/.run/P36/delever/*.txt
!/.run/P36/permuter/
+6
View File
@@ -0,0 +1,6 @@
.venv/bin/python tools/tool_census.py --all
tool_census: 298 tool files (find == git ls-files) + 38 retired; classes {'LIVE': 238, 'ORPHAN': 30, 'REFERENCED': 30, 'RETIRED': 38}; dictionary rows 336
tool_census: wrote docs/tool-index.md
tool_census: wrote decomp-architect/tools/MANIFEST.md
tool_census: corpus materialised — 365 verbatim copies, 30 pointers, INDEX.md
kit-corpus wall=24.17 s
+1 -1
View File
@@ -229,7 +229,7 @@
}
},
"coverage_ok": true,
"elapsed_s": 35.0,
"elapsed_s": 40.0,
"generated": "2026-09-09",
"headers": 3181,
"levers_AB": {
+1 -1
View File
@@ -25,4 +25,4 @@ lever_census: 218 binaries · 4,121 TUs + 3,181 headers · coverage OK · unclas
src/shared/ov/func_80178004.h pins got 26 expected 26 OK
ov_SC03_006 func_80184034 bare-name pins got 3 expected 3 OK
engine_prelude.h asm sites (a macro definition only) got 0 expected 0 OK
elapsed 35.0 s
elapsed 40.0 s
+227
View File
@@ -0,0 +1,227 @@
{
"head": "a1407bdfc",
"stamp": "b44c5a8a3423",
"generated": "2026-09-09 01:50",
"aliases": [
"main",
"ov_SC03_014",
"ov_SC03_015",
"ov_SC04_011"
],
"objects": 177,
"identical": 177,
"failures": [],
"twin_checks": 32,
"twin_mismatch": [],
"positive_control": {
"obj": "build/src/800.o",
"verdict": "DIFFERS"
},
"per_alias": {
"main": {
"objects": 85,
"identical": 85,
"seconds": 6.707000000000002,
"mean_s": 0.079
},
"ov_SC03_014": {
"objects": 32,
"identical": 32,
"seconds": 4.815,
"mean_s": 0.15
},
"ov_SC03_015": {
"objects": 32,
"identical": 32,
"seconds": 4.541,
"mean_s": 0.142
},
"ov_SC04_011": {
"objects": 28,
"identical": 28,
"seconds": 3.943,
"mean_s": 0.141
}
},
"per_object_seconds": {
"build/src/800.o": 0.752,
"build/src/800_b.o": 0.07,
"build/src/800_b_2.o": 0.329,
"build/src/800_b_o0a.o": 0.074,
"build/src/800_c.o": 0.195,
"build/src/800b2.o": 0.078,
"build/src/apicard1.o": 0.061,
"build/src/apicard2.o": 0.069,
"build/src/apicard3.o": 0.076,
"build/src/apicard4.o": 0.066,
"build/src/apicard5.o": 0.086,
"build/src/apicard6.o": 0.072,
"build/src/apicard7.o": 0.081,
"build/src/boot.o": 0.08,
"build/src/gap.o": 0.071,
"build/src/libapi1.o": 0.07,
"build/src/libapi2.o": 0.063,
"build/src/libc2_1.o": 0.064,
"build/src/libc2_2.o": 0.059,
"build/src/libcd1.o": 0.069,
"build/src/libcd2.o": 0.065,
"build/src/libetc.o": 0.062,
"build/src/libgpu.o": 0.059,
"build/src/libgpu2.o": 0.067,
"build/src/libgs1.o": 0.057,
"build/src/libgs2.o": 0.064,
"build/src/libgs3.o": 0.057,
"build/src/libgs4.o": 0.064,
"build/src/libgs5.o": 0.057,
"build/src/libgs6.o": 0.077,
"build/src/libgs7.o": 0.061,
"build/src/libgs8.o": 0.059,
"build/src/libgte1.o": 0.055,
"build/src/libgte10.o": 0.065,
"build/src/libgte11.o": 0.063,
"build/src/libgte12.o": 0.061,
"build/src/libgte13.o": 0.056,
"build/src/libgte14.o": 0.058,
"build/src/libgte15.o": 0.071,
"build/src/libgte16.o": 0.063,
"build/src/libgte17.o": 0.063,
"build/src/libgte18.o": 0.061,
"build/src/libgte19.o": 0.063,
"build/src/libgte2.o": 0.061,
"build/src/libgte20.o": 0.066,
"build/src/libgte21.o": 0.058,
"build/src/libgte22.o": 0.063,
"build/src/libgte23.o": 0.064,
"build/src/libgte24.o": 0.057,
"build/src/libgte25.o": 0.061,
"build/src/libgte26.o": 0.068,
"build/src/libgte27.o": 0.062,
"build/src/libgte28.o": 0.06,
"build/src/libgte29.o": 0.068,
"build/src/libgte3.o": 0.069,
"build/src/libgte30.o": 0.069,
"build/src/libgte4.o": 0.065,
"build/src/libgte5.o": 0.065,
"build/src/libgte6.o": 0.061,
"build/src/libgte7.o": 0.058,
"build/src/libgte8.o": 0.059,
"build/src/libgte9.o": 0.068,
"build/src/libmcrd1.o": 0.082,
"build/src/libmcrd2.o": 0.064,
"build/src/libpad1.o": 0.068,
"build/src/libpad2.o": 0.066,
"build/src/sgap.o": 0.066,
"build/src/sgap_2.o": 0.067,
"build/src/sgap_3.o": 0.073,
"build/src/sgap_4.o": 0.078,
"build/src/sgap_5.o": 0.065,
"build/src/sgap_6.o": 0.064,
"build/src/sgap_8.o": 0.076,
"build/src/snd1.o": 0.073,
"build/src/snd10.o": 0.07,
"build/src/snd11.o": 0.071,
"build/src/snd12.o": 0.063,
"build/src/snd2.o": 0.072,
"build/src/snd3.o": 0.068,
"build/src/snd4.o": 0.073,
"build/src/snd5.o": 0.067,
"build/src/snd6.o": 0.069,
"build/src/snd7.o": 0.069,
"build/src/snd8.o": 0.063,
"build/src/snd9.o": 0.065,
"build/src/ov_SC03_014/ov_SC03_014.o": 0.141,
"build/src/ov_SC03_014/ov_SC03_014_after.o": 0.509,
"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.082,
"build/src/ov_SC03_014/ov_SC03_014_jr_80135A4C.o": 0.08,
"build/src/ov_SC03_014/ov_SC03_014_jr_80135D20.o": 0.126,
"build/src/ov_SC03_014/ov_SC03_014_jr_801380E0.o": 0.169,
"build/src/ov_SC03_014/ov_SC03_014_jr_8013C98C.o": 0.129,
"build/src/ov_SC03_014/ov_SC03_014_jr_8013F350.o": 0.097,
"build/src/ov_SC03_014/ov_SC03_014_jr_8013FFD8.o": 0.074,
"build/src/ov_SC03_014/ov_SC03_014_jr_80140608.o": 0.182,
"build/src/ov_SC03_014/ov_SC03_014_jr_8015444C.o": 0.086,
"build/src/ov_SC03_014/ov_SC03_014_jr_80154C24.o": 0.174,
"build/src/ov_SC03_014/ov_SC03_014_jr_801588CC.o": 0.102,
"build/src/ov_SC03_014/ov_SC03_014_jr_80159C84.o": 0.079,
"build/src/ov_SC03_014/ov_SC03_014_jr_8015A3C8.o": 0.075,
"build/src/ov_SC03_014/ov_SC03_014_jr_8015AE2C.o": 0.093,
"build/src/ov_SC03_014/ov_SC03_014_jr_8015C32C.o": 0.487,
"build/src/ov_SC03_014/ov_SC03_014_jr_8016AB6C.o": 0.267,
"build/src/ov_SC03_014/ov_SC03_014_jr_80171B4C.o": 0.102,
"build/src/ov_SC03_014/ov_SC03_014_jr_801734BC.o": 0.205,
"build/src/ov_SC03_014/ov_SC03_014_jr_801789AC.o": 0.064,
"build/src/ov_SC03_014/ov_SC03_014_jr_80178D40.o": 0.099,
"build/src/ov_SC03_014/ov_SC03_014_jr_8017A4AC.o": 0.066,
"build/src/ov_SC03_014/ov_SC03_014_jr_8017AE2C.o": 0.168,
"build/src/ov_SC03_014/ov_SC03_014_jr_8017EB7C.o": 0.193,
"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.059,
"build/src/ov_SC03_014/ov_SC03_014_o0c.o": 0.06,
"build/src/ov_SC03_014/ov_SC03_014_o0d.o": 0.053,
"build/src/ov_SC03_014/ov_SC03_014_o0e.o": 0.065,
"build/src/ov_SC03_015/ov_SC03_015.o": 0.12,
"build/src/ov_SC03_015/ov_SC03_015_after.o": 0.526,
"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.051,
"build/src/ov_SC03_015/ov_SC03_015_jr_80135A4C.o": 0.051,
"build/src/ov_SC03_015/ov_SC03_015_jr_80135D20.o": 0.116,
"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.113,
"build/src/ov_SC03_015/ov_SC03_015_jr_8013F350.o": 0.064,
"build/src/ov_SC03_015/ov_SC03_015_jr_8013FFD8.o": 0.06,
"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.07,
"build/src/ov_SC03_015/ov_SC03_015_jr_80154C24.o": 0.169,
"build/src/ov_SC03_015/ov_SC03_015_jr_801588CC.o": 0.085,
"build/src/ov_SC03_015/ov_SC03_015_jr_80159C84.o": 0.066,
"build/src/ov_SC03_015/ov_SC03_015_jr_8015A3C8.o": 0.068,
"build/src/ov_SC03_015/ov_SC03_015_jr_8015AE2C.o": 0.092,
"build/src/ov_SC03_015/ov_SC03_015_jr_8015C32C.o": 0.472,
"build/src/ov_SC03_015/ov_SC03_015_jr_8016AB6C.o": 0.259,
"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.201,
"build/src/ov_SC03_015/ov_SC03_015_jr_801789AC.o": 0.058,
"build/src/ov_SC03_015/ov_SC03_015_jr_80178D40.o": 0.1,
"build/src/ov_SC03_015/ov_SC03_015_jr_8017A4AC.o": 0.064,
"build/src/ov_SC03_015/ov_SC03_015_jr_8017AE2C.o": 0.164,
"build/src/ov_SC03_015/ov_SC03_015_jr_8017EB7C.o": 0.189,
"build/src/ov_SC03_015/ov_SC03_015_jr_80184440.o": 0.05,
"build/src/ov_SC03_015/ov_SC03_015_jr_801848E4.o": 0.278,
"build/src/ov_SC03_015/ov_SC03_015_o0b.o": 0.06,
"build/src/ov_SC03_015/ov_SC03_015_o0c.o": 0.061,
"build/src/ov_SC03_015/ov_SC03_015_o0d.o": 0.054,
"build/src/ov_SC03_015/ov_SC03_015_o0e.o": 0.069,
"build/src/ov_SC04_011/ov_SC04_011.o": 0.121,
"build/src/ov_SC04_011/ov_SC04_011_after.o": 0.423,
"build/src/ov_SC04_011/ov_SC04_011_jr_8012ACE0.o": 0.353,
"build/src/ov_SC04_011/ov_SC04_011_jr_80135888.o": 0.054,
"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.12,
"build/src/ov_SC04_011/ov_SC04_011_jr_801380E0.o": 0.152,
"build/src/ov_SC04_011/ov_SC04_011_jr_8013C98C.o": 0.109,
"build/src/ov_SC04_011/ov_SC04_011_jr_8013F350.o": 0.076,
"build/src/ov_SC04_011/ov_SC04_011_jr_8013FFD8.o": 0.065,
"build/src/ov_SC04_011/ov_SC04_011_jr_80140608.o": 0.172,
"build/src/ov_SC04_011/ov_SC04_011_jr_8015444C.o": 0.072,
"build/src/ov_SC04_011/ov_SC04_011_jr_80154C24.o": 0.169,
"build/src/ov_SC04_011/ov_SC04_011_jr_801588CC.o": 0.092,
"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.073,
"build/src/ov_SC04_011/ov_SC04_011_jr_8015AE2C.o": 0.093,
"build/src/ov_SC04_011/ov_SC04_011_jr_8015C32C.o": 0.353,
"build/src/ov_SC04_011/ov_SC04_011_jr_8016AB6C.o": 0.217,
"build/src/ov_SC04_011/ov_SC04_011_jr_80171B4C.o": 0.099,
"build/src/ov_SC04_011/ov_SC04_011_jr_801734BC.o": 0.164,
"build/src/ov_SC04_011/ov_SC04_011_jr_801789AC.o": 0.054,
"build/src/ov_SC04_011/ov_SC04_011_jr_80178D40.o": 0.085,
"build/src/ov_SC04_011/ov_SC04_011_jr_8017A4AC.o": 0.063,
"build/src/ov_SC04_011/ov_SC04_011_jr_8017AE2C.o": 0.093,
"build/src/ov_SC04_011/ov_SC04_011_jr_8017D494.o": 0.444,
"build/src/ov_SC04_011/ov_SC04_011_o0b.o": 0.048,
"build/src/ov_SC04_011/ov_SC04_011_o0c.o": 0.06
},
"ok": true,
"seconds": 2.3
}
+231
View File
@@ -0,0 +1,231 @@
01:56:54 probe: 285 bodies sampled from 19868 lever bodies (2340 distinct classes); seed 11
01:56:54 src/md_MAIN_003/md_MAIN_003_o0e.c:func_800D0B1C A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:54 src/md_MAIN_003/md_MAIN_003_o0c.c:func_800D0D6C A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:54 src/boot.c:func_80011380 A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:54 src/md_MAIN_011/md_MAIN_011.c:func_800CEF00 A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:54 src/md_MAIN_003/md_MAIN_003_o0e.c:func_800D0268 A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:54 src/boot.c:func_80011C8C A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:54 src/800_b.c:func_8002B0B4 A=DIFFERS B: removed 0 needed 5 compiles 6
01:56:54 src/md_MAIN_003/md_MAIN_003_jr_800D1E18.c:func_800D2A24 A=DIFFERS B: removed 0 needed 3 compiles 5
01:56:54 src/md_MAIN_021/md_MAIN_021.c:func_800CB0C8 A=DIFFERS B: removed 0 needed 3 compiles 4
01:56:54 src/boot.c:func_80010F80 A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:54 src/md_MAIN_026/md_MAIN_026.c:func_800CB900 A=DIFFERS B: removed 1 needed 1 compiles 3
01:56:54 src/800_c.c:func_800383A4 A=DIFFERS B: removed 0 needed 1 compiles 3
01:56:54 src/boot.c:func_80010A08 A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:54 src/md_MAIN_009/md_MAIN_009.c:func_800CD674 A=DIFFERS B: removed 2 needed 6 compiles 10
01:56:55 src/md_SC03_076/md_SC03_076_jr_801F218C.c:func_801F2348 A=DIFFERS B: removed 2 needed 1 compiles 4
01:56:55 src/md_MAIN_044/md_MAIN_044.c:func_800CCD2C A=DIFFERS B: removed 1 needed 1 compiles 4
01:56:55 src/md_SC03_135/md_SC03_135_jr_801E5358.c:func_801E559C A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:55 src/md_SC03_075/md_SC03_075.c:func_801F02D4 A=DIFFERS B: removed 1 needed 1 compiles 4
01:56:55 src/md_SC04_026/md_SC04_026.c:func_801E89D4 A=DIFFERS B: removed 1 needed 1 compiles 4
01:56:55 src/md_MAIN_001/md_MAIN_001.c:func_800CF290 A=DIFFERS B: removed 9 needed 4 compiles 15
01:56:55 src/md_MAIN_044/md_MAIN_044.c:func_800CD894 A=DIFFERS B: removed 0 needed 1 compiles 3
01:56:55 src/md_MAIN_003/md_MAIN_003.c:func_800CF3E8 A=DIFFERS B: removed 4 needed 8 compiles 14
01:56:55 src/md_MAIN_043/md_MAIN_043.c:func_800CD404 A=DIFFERS B: removed 7 needed 1 compiles 10
01:56:55 src/md_MAIN_013/md_MAIN_013.c:func_800CB138 A=DIFFERS B: removed 0 needed 13 compiles 15
01:56:55 src/md_SC05_026/md_SC05_026_jr_801F0748.c:func_801F0C4C A=DIFFERS B: removed 4 needed 2 compiles 8
01:56:55 src/800_c.c:func_800385C0 A=DIFFERS B: removed 0 needed 2 compiles 4
01:56:55 src/ov_MAIN_012/ov_MAIN_012_jr_8013F350.c:func_801418F8 A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:55 src/ov_MAIN_012/ov_MAIN_012_jr_8013C98C.c:func_8013D53C A=DIFFERS B: removed 0 needed 2 compiles 3
01:56:55 src/md_SC07_003/md_SC07_003.c:func_801A293C A=DIFFERS B: removed 1 needed 1 compiles 4
01:56:55 src/md_SC07_004/md_SC07_004.c:func_801AB818 A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:56 src/md_MAIN_003/md_MAIN_003.c:func_800CFEB4 A=DIFFERS B: removed 7 needed 4 compiles 13
01:56:56 src/800_b_2.c:func_8002EFF8 A=DIFFERS B: removed 1 needed 2 compiles 5
01:56:56 src/ov_MAIN_012/ov_MAIN_012_jr_8016AE5C.c:func_8016E9EC A=DIFFERS B: removed 0 needed 1 compiles 3
01:56:56 src/ov_SC01_004/ov_SC01_004_jr_8017E5B8.c:func_8017E5B8 A=DIFFERS B: removed 0 needed 2 compiles 3
01:56:56 src/ov_SC01_004/ov_SC01_004_jr_8017BE9C.c:func_8017DC38 A=DIFFERS B: removed 1 needed 3 compiles 6
01:56:56 src/ov_MAIN_012/ov_MAIN_012_jr_801380E0.c:func_8013B274 A=DIFFERS B: removed 4 needed 8 compiles 13
01:56:56 src/ov_SC01_004/ov_SC01_004_jr_8017E5B8.c:func_8017E804 A=DIFFERS B: removed 1 needed 5 compiles 8
01:56:56 src/800.c:func_80012E6C A=DIFFERS B: removed 0 needed 1 compiles 3
01:56:57 src/800_b_2.c:func_8002FF0C A=DIFFERS B: removed 0 needed 3 compiles 4
01:56:57 src/ov_SC01_000/ov_SC01_000_jr_8017BEBC.c:func_8017E4A0 A=DIFFERS B: removed 7 needed 2 compiles 11
01:56:57 src/md_SC07_004/md_SC07_004.c:func_801AE908 A=DIFFERS B: removed 0 needed 1 compiles 3
01:56:57 src/800_c.c:func_800391D4 A=DIFFERS B: removed 0 needed 10 compiles 12
01:56:57 src/ov_SC01_008/ov_SC01_008_jr_8017BE9C.c:func_8017E050 A=DIFFERS B: removed 0 needed 4 compiles 6
01:56:57 src/ov_SC01_084/ov_SC01_084_jr_80182A00.c:func_80183BD0 A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:57 src/ov_SC01_009/ov_SC01_009_jr_8017E590.c:func_8017FFD0 A=DIFFERS B: removed 3 needed 4 compiles 9
01:56:58 src/ov_SC01_084/ov_SC01_084_jr_80182A00.c:func_8018392C A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:58 src/ov_SC01_077/ov_SC01_077_jr_8017AE2C.c:func_8017B614 A=DIFFERS B: removed 0 needed 3 compiles 5
01:56:58 src/ov_SC01_084/ov_SC01_084_jr_80182A00.c:func_80183A7C A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:58 src/800_b_2.c:func_80034314 A=DIFFERS B: removed 2 needed 1 compiles 5
01:56:58 src/ov_MAIN_012/ov_MAIN_012.c:func_80130D48 A=DIFFERS B: removed 5 needed 4 compiles 11
01:56:58 src/ov_SC02_005/ov_SC02_005_jr_8017CF90.c:func_8018118C A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:58 src/ov_SC02_005/ov_SC02_005_jr_8017AE2C.c:func_8017C41C A=DIFFERS B: removed 2 needed 3 compiles 7
01:56:58 src/800_c.c:func_80038958 A=DIFFERS B: removed 5 needed 2 compiles 9
01:56:58 src/ov_SC01_080/ov_SC01_080_jr_8017AE2C.c:func_80180728 A=DIFFERS B: removed 0 needed 5 compiles 6
01:56:58 src/ov_MAIN_012/ov_MAIN_012_jr_8015A3C8.c:func_80162438 A=DIFFERS B: removed 2 needed 3 compiles 7
01:56:59 src/ov_SC01_000/ov_SC01_000_jr_8013C98C.c:func_8013D9B0 A=DIFFERS B: removed 9 needed 25 compiles 36
01:56:59 src/ov_SC02_011/ov_SC02_011_jr_8017AE2C.c:func_8017FD9C A=DIFFERS B: removed 0 needed 2 compiles 3
01:56:59 src/ov_SC02_005/ov_SC02_005_jr_8018EA04.c:func_8018FEA0 A=DIFFERS B: removed 5 needed 3 compiles 10
01:56:59 src/ov_SC02_005/ov_SC02_005_jr_80185E80.c:func_8018D270 A=DIFFERS B: removed 0 needed 2 compiles 4
01:56:59 src/ov_SC02_005/ov_SC02_005_jr_8018EA04.c:func_8018FA34 A=DIFFERS B: removed 0 needed 3 compiles 5
01:57:00 src/ov_SC01_077/ov_SC01_077.c:func_80140958 A=DIFFERS B: removed 7 needed 4 compiles 13
01:57:00 src/ov_SC02_021/ov_SC02_021_jr_8017ECFC.c:func_8017F1B4 A=DIFFERS B: removed 0 needed 1 compiles 3
01:57:00 src/ov_SC02_011/ov_SC02_011_jr_80183178.c:func_80187E08 A=DIFFERS B: removed 1 needed 4 compiles 7
01:57:00 src/ov_SC02_021/ov_SC02_021_jr_8017C294.c:func_8017DC10 A=DIFFERS B: removed 1 needed 3 compiles 6
01:57:00 src/md_SC07_004/md_SC07_004.c:func_801A2DC4 A=DIFFERS B: removed 2 needed 3 compiles 7
01:57:00 src/ov_SC02_011/ov_SC02_011_jr_8017AE2C.c:func_8017F67C A=DIFFERS B: removed 0 needed 2 compiles 4
01:57:00 src/ov_SC02_028/ov_SC02_028_jr_801884D8.c:func_8018AB34 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:00 src/ov_SC02_028/ov_SC02_028_jr_8017D898.c:func_80181264 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:00 src/ov_SC02_011/ov_SC02_011_jr_80188E3C.c:func_8018AB20 A=DIFFERS B: removed 1 needed 4 compiles 7
01:57:01 src/ov_SC02_026/ov_SC02_026_jr_8017C180.c:func_8017F6B0 A=DIFFERS B: removed 0 needed 2 compiles 3
01:57:01 src/ov_SC02_011/ov_SC02_011_jr_8017AE2C.c:func_8017D710 A=DIFFERS B: removed 0 needed 2 compiles 3
01:57:01 src/ov_SC02_011/ov_SC02_011_jr_80183178.c:func_80186868 A=DIFFERS B: removed 1 needed 4 compiles 7
01:57:01 src/ov_SC02_035/ov_SC02_035_jr_8017BEBC.c:func_80181B00 A=DIFFERS B: removed 1 needed 1 compiles 4
01:57:01 src/ov_SC02_041/ov_SC02_041_jr_8017BEBC.c:func_8018317C A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:01 src/ov_MAIN_012/ov_MAIN_012.c:func_8012956C A=DIFFERS B: removed 6 needed 4 compiles 12
01:57:02 src/ov_SC02_039/ov_SC02_039_jr_8017BEBC.c:func_8017DE00 A=DIFFERS B: removed 6 needed 4 compiles 12
01:57:02 src/ov_SC03_001/ov_SC03_001_jr_8017AE2C.c:func_80182058 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:02 src/ov_SC03_001/ov_SC03_001_jr_80189030.c:func_801898E4 A=DIFFERS B: removed 1 needed 5 compiles 8
01:57:02 src/ov_SC03_001/ov_SC03_001_jr_8018B8DC.c:func_8018BCD4 A=DIFFERS B: removed 1 needed 4 compiles 7
01:57:03 src/ov_SC02_026/ov_SC02_026_jr_8017C180.c:func_8017E3AC A=DIFFERS B: removed 4 needed 1 compiles 7
01:57:03 src/ov_SC03_002/ov_SC03_002_jr_8017D604.c:func_8017EA54 A=DIFFERS B: removed 2 needed 3 compiles 7
01:57:03 src/800_b_2.c:func_8003388C A=DIFFERS B: removed 2 needed 15 compiles 19
01:57:03 src/ov_SC02_011/ov_SC02_011_jr_80183178.c:func_80187CE8 A=DIFFERS B: removed 4 needed 4 compiles 10
01:57:03 src/ov_SC03_013/ov_SC03_013_jr_8017C730.c:func_8017DD98 A=DIFFERS B: removed 1 needed 1 compiles 4
01:57:04 src/ov_SC02_011/ov_SC02_011_jr_80183178.c:func_80188250 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:04 src/ov_SC03_001/ov_SC03_001_jr_8017AE2C.c:func_8017BF50 A=DIFFERS B: removed 3 needed 1 compiles 6
01:57:04 src/800_c.c:func_80038A58 A=DIFFERS B: removed 26 needed 9 compiles 37
01:57:04 src/ov_SC03_011/ov_SC03_011_jr_80140608.c:func_80140958 A=DIFFERS B: removed 7 needed 4 compiles 13
01:57:04 src/ov_SC03_024/ov_SC03_024_jr_80183BF8.c:func_80185E38 A=DIFFERS B: removed 1 needed 4 compiles 7
01:57:04 src/ov_SC03_028/ov_SC03_028_jr_8017DF98.c:func_80180AC4 A=DIFFERS B: removed 0 needed 2 compiles 4
01:57:05 src/ov_SC03_024/ov_SC03_024_jr_8017DF84.c:func_80182108 A=DIFFERS B: removed 1 needed 3 compiles 6
01:57:05 src/ov_SC03_028/ov_SC03_028_jr_801887A8.c:func_80189AF4 A=DIFFERS B: removed 0 needed 3 compiles 5
01:57:05 src/800.c:func_800242D0 A=DIFFERS B: removed 6 needed 5 compiles 13
01:57:05 src/800_c.c:func_800384A8 A=DIFFERS B: removed 0 needed 1 compiles 3
01:57:05 src/md_SC07_004/md_SC07_004.c:func_801AB694 A=DIFFERS B: removed 5 needed 1 compiles 8
01:57:06 src/ov_SC03_006/ov_SC03_006_jr_8017AE2C.c:func_80185BD4 A=DIFFERS B: removed 3 needed 2 compiles 7
01:57:06 src/ov_SC03_030/ov_SC03_030_jr_8017AE2C.c:func_80180960 A=DIFFERS B: removed 0 needed 3 compiles 5
01:57:06 src/ov_SC03_105/ov_SC03_105_jr_8017C8D0.c:func_8017E180 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:06 src/ov_SC03_029/ov_SC03_029_jr_8017FF7C.c:func_80180B84 A=DIFFERS B: removed 4 needed 1 compiles 7
01:57:06 src/ov_SC03_105/ov_SC03_105_jr_80186DDC.c:func_80187928 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:06 src/ov_SC03_097/ov_SC03_097_jr_8017D898.c:func_80181CF4 A=DIFFERS B: removed 1 needed 4 compiles 7
01:57:06 src/ov_SC03_092/ov_SC03_092_jr_80181694.c:func_8018478C A=DIFFERS B: removed 0 needed 8 compiles 10
01:57:06 src/ov_SC03_108/ov_SC03_108_jr_8017F83C.c:func_8017FD64 A=DIFFERS B: removed 0 needed 2 compiles 3
01:57:07 src/ov_SC03_028/ov_SC03_028_jr_80185C14.c:func_80186368 A=DIFFERS B: removed 17 needed 3 compiles 22
01:57:07 src/ov_SC03_105/ov_SC03_105_jr_80181C84.c:func_80183A98 A=DIFFERS B: removed 0 needed 5 compiles 6
01:57:07 src/ov_SC03_121/ov_SC03_121_jr_8017E1D0.c:func_8017F374 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:07 src/ov_SC03_112/ov_SC03_112_jr_8017C294.c:func_8017D174 A=DIFFERS B: removed 1 needed 3 compiles 5
01:57:07 src/ov_SC03_094/ov_SC03_094_jr_8017BEBC.c:func_80181448 A=DIFFERS B: removed 2 needed 4 compiles 8
01:57:07 src/ov_SC04_015/ov_SC04_015_jr_8017AE2C.c:func_8017DBAC A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:08 src/md_SC07_004/md_SC07_004.c:func_801A395C A=DIFFERS B: removed 1 needed 2 compiles 5
01:57:08 src/ov_SC04_018/ov_SC04_018_jr_80188E1C.c:func_80189EA0 A=DIFFERS B: removed 1 needed 1 compiles 3
01:57:08 src/ov_SC04_018/ov_SC04_018_jr_801878E8.c:func_80188778 A=DIFFERS B: removed 2 needed 4 compiles 8
01:57:08 src/ov_SC02_037/ov_SC02_037_jr_8017AE2C.c:func_8017F2B4 A=DIFFERS B: removed 6 needed 17 compiles 25
01:57:08 src/ov_SC04_020/ov_SC04_020_jr_8017D604.c:func_8018087C A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:08 src/ov_SC03_113/ov_SC03_113_jr_8017C294.c:func_8017F394 A=DIFFERS B: removed 5 needed 1 compiles 8
01:57:08 src/md_SC07_004/md_SC07_004.c:func_801A7CDC A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:08 src/ov_SC03_107/ov_SC03_107_jr_801789AC.c:func_8017BEBC A=DIFFERS B: removed 0 needed 7 compiles 8
01:57:09 src/ov_SC01_077/ov_SC01_077.c:func_8013D9B0 A=DIFFERS B: removed 9 needed 25 compiles 36
01:57:09 src/ov_SC05_001/ov_SC05_001_jr_8017BEBC.c:func_80182258 A=DIFFERS B: removed 0 needed 1 compiles 3
01:57:09 src/ov_SC04_016/ov_SC04_016_jr_8017BEBC.c:func_8017DA50 A=DIFFERS B: removed 2 needed 8 compiles 12
01:57:09 src/ov_SC05_003/ov_SC05_003_jr_8017BEBC.c:func_801815A0 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:09 src/ov_SC03_113/ov_SC03_113_jr_8017C294.c:func_8017E178 A=DIFFERS B: removed 0 needed 2 compiles 3
01:57:09 src/ov_SC05_005/ov_SC05_005_jr_8017D898.c:func_801816A8 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:09 src/ov_SC04_015/ov_SC04_015_jr_8017AE2C.c:func_8017BF70 A=DIFFERS B: removed 3 needed 1 compiles 6
01:57:09 src/ov_SC05_010/ov_SC05_010_jr_80180F84.c:func_801814AC A=DIFFERS B: removed 1 needed 1 compiles 3
01:57:09 src/ov_SC01_077/ov_SC01_077.c:func_801418F8 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:09 src/ov_SC05_008/ov_SC05_008_jr_8017AE2C.c:func_8017E6A4 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:09 src/ov_SC05_002/ov_SC05_002_jr_8017BEBC.c:func_8017F200 A=DIFFERS B: removed 5 needed 1 compiles 8
01:57:09 src/ov_SC05_003/ov_SC05_003_jr_8017BEBC.c:func_801812AC A=DIFFERS B: removed 1 needed 1 compiles 3
01:57:09 src/ov_SC03_124/ov_SC03_124_jr_80188544.c:func_80188C68 A=DIFFERS B: removed 5 needed 19 compiles 26
01:57:10 src/ov_SC06_013/ov_SC06_013_jr_8017BEBC.c:func_8017DE14 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:10 src/ov_SC06_006/ov_SC06_006_jr_8017DB90.c:func_8017E550 A=DIFFERS B: removed 0 needed 3 compiles 4
01:57:10 src/ov_SC06_016/ov_SC06_016_jr_801816DC.c:func_80181C78 A=DIFFERS B: removed 0 needed 1 compiles 3
01:57:10 src/ov_SC06_011/ov_SC06_011_jr_8017EEEC.c:func_8017F3C8 A=DIFFERS B: removed 4 needed 1 compiles 7
01:57:10 src/ov_SC06_008/ov_SC06_008_jr_8017C294.c:func_8017D73C A=DIFFERS B: removed 0 needed 2 compiles 3
01:57:10 src/ov_SC06_018/ov_SC06_018_jr_80187AEC.c:func_8018A688 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:10 src/ov_SC04_011/ov_SC04_011_jr_8017D494.c:func_801835B0 A=DIFFERS B: removed 0 needed 3 compiles 4
01:57:10 src/ov_SC06_011/ov_SC06_011_jr_8017BEBC.c:func_8017E5D0 A=DIFFERS B: removed 4 needed 3 compiles 9
01:57:10 src/ov_SC05_010/ov_SC05_010_jr_80181CDC.c:func_80183D5C A=DIFFERS B: removed 9 needed 1 compiles 12
01:57:10 src/ov_SC06_016/ov_SC06_016_jr_8017C8D0.c:func_801810A4 A=DIFFERS B: removed 2 needed 3 compiles 6
01:57:11 src/ov_SC06_000/ov_SC06_000_jr_8017AE2C.c:func_80182464 A=DIFFERS B: removed 0 needed 2 compiles 4
01:57:11 src/ov_SC06_024/ov_SC06_024_jr_80186F00.c:func_8018D674 A=DIFFERS B: removed 0 needed 1 compiles 3
01:57:11 src/ov_SC03_107/ov_SC03_107_jr_801789AC.c:func_8017CF48 A=DIFFERS B: removed 2 needed 4 compiles 8
01:57:11 src/ov_SC06_029/ov_SC06_029_jr_801898CC.c:func_8018A180 A=COMPILE-ERROR B: removed 0 needed 6 compiles 7
01:57:11 src/ov_SC06_018/ov_SC06_018_jr_8017C24C.c:func_801811DC A=DIFFERS B: removed 0 needed 1 compiles 3
01:57:12 src/ov_SC06_024/ov_SC06_024_jr_8017BEBC.c:func_8017FA1C A=DIFFERS B: removed 1 needed 3 compiles 6
01:57:12 src/ov_SC06_030/ov_SC06_030_jr_8017F65C.c:func_80180FF4 A=DIFFERS B: removed 0 needed 10 compiles 12
01:57:12 src/ov_SC06_033/ov_SC06_033_jr_8018D98C.c:func_8018D98C A=DIFFERS B: removed 0 needed 2 compiles 3
01:57:12 src/ov_SC06_018/ov_SC06_018_jr_8017C24C.c:func_80185DD8 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:12 src/ov_SC06_018/ov_SC06_018_jr_80187AEC.c:func_8018A974 A=DIFFERS B: removed 2 needed 3 compiles 7
01:57:12 src/ov_SC06_032/ov_SC06_032_jr_801902EC.c:func_801902EC A=DIFFERS B: removed 4 needed 9 compiles 14
01:57:12 src/ov_SC06_033/ov_SC06_033_jr_8018C2D8.c:func_8018C2D8 A=DIFFERS B: removed 4 needed 8 compiles 13
01:57:12 src/ov_SC06_032/ov_SC06_032_jr_801919A0.c:func_801919A0 A=REFUSED B: removed 4 needed 10 compiles 14
01:57:13 src/ov_SC07_000/ov_SC07_000_jr_8017F8B8.c:func_80181BA8 A=DIFFERS B: removed 1 needed 4 compiles 7
01:57:13 src/ov_SC07_007/ov_SC07_007_jr_8013C98C.c:func_8013D53C A=DIFFERS B: removed 0 needed 2 compiles 3
01:57:13 src/ov_SC06_016/ov_SC06_016_jr_8017C8D0.c:func_801813B0 A=DIFFERS B: removed 0 needed 12 compiles 13
01:57:13 src/ov_SC07_007/ov_SC07_007_jr_8017BEBC.c:func_8018044C A=DIFFERS B: removed 0 needed 1 compiles 3
01:57:13 src/resident/resident.c:func_800CFE60 A=DIFFERS B: removed 1 needed 2 compiles 5
01:57:13 src/ov_SC07_002/ov_SC07_002_jr_8017C8D0.c:func_80185FB0 A=DIFFERS B: removed 0 needed 2 compiles 4
01:57:13 src/ov_SC07_010/ov_SC07_010_jr_8017AE2C.c:func_80180480 A=DIFFERS B: removed 0 needed 1 compiles 3
01:57:13 src/resident/resident_jr_800D00E4.c:func_800D0488 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:13 src/resident/resident_jr_800D128C.c:func_800D1984 A=DIFFERS B: removed 0 needed 1 compiles 3
01:57:14 src/ov_SC06_032/ov_SC06_032_jr_80182890.c:func_8018BA4C A=DIFFERS B: removed 3 needed 2 compiles 7
01:57:14 src/ov_SC06_000/ov_SC06_000_jr_8017AE2C.c:func_801835B0 A=DIFFERS B: removed 0 needed 7 compiles 8
01:57:14 src/ov_SC06_018/ov_SC06_018_jr_8017C24C.c:func_8017DB20 A=DIFFERS B: removed 2 needed 1 compiles 5
01:57:14 src/resident/resident_jr_800D00E4.c:func_800D0E30 A=DIFFERS B: removed 2 needed 3 compiles 7
01:57:14 src/resident/resident_jr_800D128C.c:func_800D128C A=DIFFERS B: removed 0 needed 3 compiles 4
01:57:14 src/ov_SC07_007/ov_SC07_007_jr_8017BEBC.c:func_8018122C A=DIFFERS B: removed 1 needed 1 compiles 4
01:57:14 src/resident/resident_jr_800D00E4.c:func_800D0214 A=DIFFERS B: removed 1 needed 1 compiles 4
01:57:14 src/resident/resident_jr_800D128C.c:func_800D1658 A=DIFFERS B: removed 0 needed 3 compiles 5
01:57:14 src/resident/resident_jr_800D00E4.c:func_800D02D0 A=DIFFERS B: removed 0 needed 1 compiles 3
01:57:14 src/ov_SC04_011/ov_SC04_011_jr_8017D494.c:func_8018E428 A=DIFFERS B: removed 1 needed 4 compiles 7
01:57:14 src/resident/resident_jr_800D00E4.c:func_800D0D7C A=DIFFERS B: removed 0 needed 1 compiles 3
01:57:14 src/resident/resident_jr_800D128C.c:func_800D2D10 A=DIFFERS B: removed 0 needed 1 compiles 3
01:57:14 src/ov_SC07_010/ov_SC07_010_jr_80140608.c:func_80140958 A=DIFFERS B: removed 7 needed 4 compiles 13
01:57:15 src/resident/resident_jr_800D128C.c:func_800D2650 A=DIFFERS B: removed 1 needed 4 compiles 7
01:57:15 src/resident/resident_jr_800D128C.c:func_800D1B80 A=DIFFERS B: removed 0 needed 1 compiles 3
01:57:15 src/ov_SC07_006/ov_SC07_006_jr_8017BEBC.c:func_80182F3C A=DIFFERS B: removed 6 needed 6 compiles 13
01:57:15 src/ov_SC07_006/ov_SC07_006_jr_80171B4C.c:func_80176734 A=DIFFERS B: removed 1 needed 11 compiles 14
01:57:16 src/ov_SC04_011/ov_SC04_011_jr_8017D494.c:func_8018315C A=DIFFERS B: removed 0 needed 2 compiles 4
01:57:29 src/800.c:func_800226C0 A=DIFFERS B: removed 31 needed 14 compiles 47
01:57:30 src/800.c:func_80020DA4 A=DIFFERS B: removed 0 needed 2 compiles 3
01:57:34 src/800.c:func_8002AF70 A=DIFFERS B: removed 2 needed 3 compiles 7
01:57:37 src/800.c:func_800241C0 A=DIFFERS B: removed 3 needed 3 compiles 8
01:57:38 src/800.c:func_80016A7C A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:45 src/800.c:func_80023570 A=DIFFERS B: removed 9 needed 4 compiles 15
01:57:47 src/800.c:func_8001A114 A=DIFFERS B: removed 0 needed 2 compiles 3
01:57:53 src/800.c:func_80016224 A=DIFFERS B: removed 3 needed 6 compiles 11
01:57:54 src/800.c:func_80025A30 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:55 src/800.c:func_8001EA14 A=DIFFERS B: removed 0 needed 1 compiles 3
01:58:13 src/800.c:func_800215F4 A=DIFFERS B: removed 29 needed 7 compiles 38
01:58:19 src/800.c:func_8002AC00 A=DIFFERS B: removed 4 needed 5 compiles 11
01:58:25 src/800.c:func_8001BADC A=DIFFERS B: removed 5 needed 3 compiles 10
01:58:26 src/800.c:func_800273F4 A=DIFFERS B: removed 0 needed 1 compiles 2
01:58:30 src/800.c:func_80015208 A=DIFFERS B: removed 3 needed 1 compiles 6
01:58:35 src/800.c:func_8001BBBC A=DIFFERS B: removed 4 needed 4 compiles 10
01:58:39 src/800.c:LoaderInitFileTable A=DIFFERS B: removed 0 needed 7 compiles 9
01:58:40 src/shared/ov/func_8012A860.h:func_8012A860 A=DIFFERS B: removed 0 needed 1 compiles 3
01:58:41 src/shared/ov/func_8012C2D0.h:func_8012C2D0 A=DIFFERS B: removed 0 needed 2 compiles 3
01:58:44 src/shared/ov/func_8012DBD0.h:func_8012DBD0 A=DIFFERS B: removed 8 needed 4 compiles 14
01:58:46 src/shared/ov/func_80135004__0f8f8d0b.h:func_80135004 A=DIFFERS B: removed 4 needed 4 compiles 10
01:58:48 src/shared/ov/func_80135168__bad0a585.h:func_80135168 A=DIFFERS B: removed 0 needed 5 compiles 7
01:58:48 src/shared/ov/func_80135A4C__e0129286.h:func_80135A4C A=DIFFERS B: removed 0 needed 3 compiles 5
01:58:49 src/shared/ov/func_80135EB0__c9438c12.h:func_80135EB0 A=DIFFERS B: removed 0 needed 6 compiles 8
01:58:50 src/shared/ov/func_80136334__6e7f4730.h:func_80136334 A=DIFFERS B: removed 4 needed 4 compiles 10
01:58:51 src/shared/ov/func_8013B274__28562738.h:func_8013B274 A=DIFFERS B: removed 4 needed 8 compiles 13
01:58:51 src/shared/ov/func_8013D178__421ba659.h:func_8013D178 A=DIFFERS B: removed 0 needed 1 compiles 3
01:58:52 src/shared/ov/func_80140608__2fb5c201.h:func_80140608 A=DIFFERS B: removed 1 needed 1 compiles 4
01:58:53 src/shared/ov/func_801415C0.h:func_801415C0 A=DIFFERS B: removed 3 needed 2 compiles 7
01:58:53 src/shared/ov/func_80141B90.h:func_80141B90 A=DIFFERS B: removed 0 needed 1 compiles 2
01:58:54 src/shared/ov/func_80145A2C__530ca2ff.h:func_80145A2C A=DIFFERS B: removed 0 needed 1 compiles 2
01:58:54 src/shared/ov/func_801497A8.h:func_801497A8 A=DIFFERS B: removed 0 needed 1 compiles 2
01:58:55 src/shared/ov/func_8014DD8C.h:func_8014DD8C A=DIFFERS B: removed 0 needed 2 compiles 3
01:58:56 src/shared/ov/func_8014F74C.h:func_8014F74C A=DIFFERS B: removed 0 needed 1 compiles 2
01:58:58 src/shared/ov/func_801536DC.h:func_801536DC A=DIFFERS B: removed 2 needed 1 compiles 5
01:59:00 src/shared/ov/func_8015B6F4.h:func_8015B6F4 A=DIFFERS B: removed 1 needed 3 compiles 6
01:59:02 src/shared/ov/func_801612B8.h:func_801612B8 A=DIFFERS B: removed 2 needed 1 compiles 5
01:59:05 src/shared/ov/func_80162438__e113e304.h:func_80162438 A=DIFFERS B: removed 2 needed 3 compiles 7
01:59:09 src/shared/ov/func_80166054.h:func_80166054 A=DIFFERS B: removed 5 needed 5 compiles 12
01:59:11 src/shared/ov/func_80166F58__3728db8a.h:func_80166F58 A=DIFFERS B: removed 2 needed 2 compiles 6
01:59:12 src/shared/ov/func_8016DF5C__a8a4aea4.h:func_8016DF5C A=DIFFERS B: removed 1 needed 2 compiles 5
01:59:13 src/shared/ov/func_801759D8.h:func_801759D8 A=DIFFERS B: removed 1 needed 1 compiles 3
01:59:16 src/shared/ov/func_80176734__261d72d2.h:func_80176734 A=DIFFERS B: removed 1 needed 11 compiles 14
01:59:18 src/shared/ov/func_801778A8.h:func_801778A8 A=DIFFERS B: removed 3 needed 2 compiles 7
01:59:23 src/shared/ov/func_80177B5C__879ede92.h:func_80177B5C A=DIFFERS B: removed 8 needed 15 compiles 25
01:59:24 src/shared/ov/func_8017CF6C__4771362e.h:func_8017CF6C A=DIFFERS B: removed 0 needed 1 compiles 3
01:59:24 src/shared/ov/func_8017D0BC__4f23330c.h:func_8017D0BC A=DIFFERS B: removed 0 needed 1 compiles 3
01:59:24 src/shared/ov/func_8017E98C__94ae7b9b.h:func_8017E98C A=DIFFERS B: removed 0 needed 1 compiles 3
01:59:25 src/shared/ov/func_80181538__27797037.h:func_80181538 A=DIFFERS B: removed 0 needed 2 compiles 4
01:59:25 src/shared/ov/func_80188028.h:func_80188028 A=DIFFERS B: removed 0 needed 1 compiles 2
File diff suppressed because one or more lines are too long
+264
View File
@@ -0,0 +1,264 @@
01:56:54 probe: 285 bodies sampled from 19868 lever bodies (2340 distinct classes); seed 11
01:56:54 src/md_MAIN_003/md_MAIN_003_o0e.c:func_800D0B1C A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:54 src/md_MAIN_003/md_MAIN_003_o0c.c:func_800D0D6C A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:54 src/boot.c:func_80011380 A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:54 src/md_MAIN_011/md_MAIN_011.c:func_800CEF00 A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:54 src/md_MAIN_003/md_MAIN_003_o0e.c:func_800D0268 A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:54 src/boot.c:func_80011C8C A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:54 src/800_b.c:func_8002B0B4 A=DIFFERS B: removed 0 needed 5 compiles 6
01:56:54 src/md_MAIN_003/md_MAIN_003_jr_800D1E18.c:func_800D2A24 A=DIFFERS B: removed 0 needed 3 compiles 5
01:56:54 src/md_MAIN_021/md_MAIN_021.c:func_800CB0C8 A=DIFFERS B: removed 0 needed 3 compiles 4
01:56:54 src/boot.c:func_80010F80 A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:54 src/md_MAIN_026/md_MAIN_026.c:func_800CB900 A=DIFFERS B: removed 1 needed 1 compiles 3
01:56:54 src/800_c.c:func_800383A4 A=DIFFERS B: removed 0 needed 1 compiles 3
01:56:54 src/boot.c:func_80010A08 A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:54 src/md_MAIN_009/md_MAIN_009.c:func_800CD674 A=DIFFERS B: removed 2 needed 6 compiles 10
01:56:55 src/md_SC03_076/md_SC03_076_jr_801F218C.c:func_801F2348 A=DIFFERS B: removed 2 needed 1 compiles 4
01:56:55 src/md_MAIN_044/md_MAIN_044.c:func_800CCD2C A=DIFFERS B: removed 1 needed 1 compiles 4
01:56:55 src/md_SC03_135/md_SC03_135_jr_801E5358.c:func_801E559C A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:55 src/md_SC03_075/md_SC03_075.c:func_801F02D4 A=DIFFERS B: removed 1 needed 1 compiles 4
01:56:55 src/md_SC04_026/md_SC04_026.c:func_801E89D4 A=DIFFERS B: removed 1 needed 1 compiles 4
01:56:55 src/md_MAIN_001/md_MAIN_001.c:func_800CF290 A=DIFFERS B: removed 9 needed 4 compiles 15
01:56:55 src/md_MAIN_044/md_MAIN_044.c:func_800CD894 A=DIFFERS B: removed 0 needed 1 compiles 3
01:56:55 src/md_MAIN_003/md_MAIN_003.c:func_800CF3E8 A=DIFFERS B: removed 4 needed 8 compiles 14
01:56:55 src/md_MAIN_043/md_MAIN_043.c:func_800CD404 A=DIFFERS B: removed 7 needed 1 compiles 10
01:56:55 src/md_MAIN_013/md_MAIN_013.c:func_800CB138 A=DIFFERS B: removed 0 needed 13 compiles 15
01:56:55 src/md_SC05_026/md_SC05_026_jr_801F0748.c:func_801F0C4C A=DIFFERS B: removed 4 needed 2 compiles 8
01:56:55 src/800_c.c:func_800385C0 A=DIFFERS B: removed 0 needed 2 compiles 4
01:56:55 src/ov_MAIN_012/ov_MAIN_012_jr_8013F350.c:func_801418F8 A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:55 src/ov_MAIN_012/ov_MAIN_012_jr_8013C98C.c:func_8013D53C A=DIFFERS B: removed 0 needed 2 compiles 3
01:56:55 src/md_SC07_003/md_SC07_003.c:func_801A293C A=DIFFERS B: removed 1 needed 1 compiles 4
01:56:55 src/md_SC07_004/md_SC07_004.c:func_801AB818 A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:56 src/md_MAIN_003/md_MAIN_003.c:func_800CFEB4 A=DIFFERS B: removed 7 needed 4 compiles 13
01:56:56 src/800_b_2.c:func_8002EFF8 A=DIFFERS B: removed 1 needed 2 compiles 5
01:56:56 src/ov_MAIN_012/ov_MAIN_012_jr_8016AE5C.c:func_8016E9EC A=DIFFERS B: removed 0 needed 1 compiles 3
01:56:56 src/ov_SC01_004/ov_SC01_004_jr_8017E5B8.c:func_8017E5B8 A=DIFFERS B: removed 0 needed 2 compiles 3
01:56:56 src/ov_SC01_004/ov_SC01_004_jr_8017BE9C.c:func_8017DC38 A=DIFFERS B: removed 1 needed 3 compiles 6
01:56:56 src/ov_MAIN_012/ov_MAIN_012_jr_801380E0.c:func_8013B274 A=DIFFERS B: removed 4 needed 8 compiles 13
01:56:56 src/ov_SC01_004/ov_SC01_004_jr_8017E5B8.c:func_8017E804 A=DIFFERS B: removed 1 needed 5 compiles 8
01:56:56 src/800.c:func_80012E6C A=DIFFERS B: removed 0 needed 1 compiles 3
01:56:57 src/800_b_2.c:func_8002FF0C A=DIFFERS B: removed 0 needed 3 compiles 4
01:56:57 src/ov_SC01_000/ov_SC01_000_jr_8017BEBC.c:func_8017E4A0 A=DIFFERS B: removed 7 needed 2 compiles 11
01:56:57 src/md_SC07_004/md_SC07_004.c:func_801AE908 A=DIFFERS B: removed 0 needed 1 compiles 3
01:56:57 src/800_c.c:func_800391D4 A=DIFFERS B: removed 0 needed 10 compiles 12
01:56:57 src/ov_SC01_008/ov_SC01_008_jr_8017BE9C.c:func_8017E050 A=DIFFERS B: removed 0 needed 4 compiles 6
01:56:57 src/ov_SC01_084/ov_SC01_084_jr_80182A00.c:func_80183BD0 A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:57 src/ov_SC01_009/ov_SC01_009_jr_8017E590.c:func_8017FFD0 A=DIFFERS B: removed 3 needed 4 compiles 9
01:56:58 src/ov_SC01_084/ov_SC01_084_jr_80182A00.c:func_8018392C A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:58 src/ov_SC01_077/ov_SC01_077_jr_8017AE2C.c:func_8017B614 A=DIFFERS B: removed 0 needed 3 compiles 5
01:56:58 src/ov_SC01_084/ov_SC01_084_jr_80182A00.c:func_80183A7C A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:58 src/800_b_2.c:func_80034314 A=DIFFERS B: removed 2 needed 1 compiles 5
01:56:58 src/ov_MAIN_012/ov_MAIN_012.c:func_80130D48 A=DIFFERS B: removed 5 needed 4 compiles 11
01:56:58 src/ov_SC02_005/ov_SC02_005_jr_8017CF90.c:func_8018118C A=DIFFERS B: removed 0 needed 1 compiles 2
01:56:58 src/ov_SC02_005/ov_SC02_005_jr_8017AE2C.c:func_8017C41C A=DIFFERS B: removed 2 needed 3 compiles 7
01:56:58 src/800_c.c:func_80038958 A=DIFFERS B: removed 5 needed 2 compiles 9
01:56:58 src/ov_SC01_080/ov_SC01_080_jr_8017AE2C.c:func_80180728 A=DIFFERS B: removed 0 needed 5 compiles 6
01:56:58 src/ov_MAIN_012/ov_MAIN_012_jr_8015A3C8.c:func_80162438 A=DIFFERS B: removed 2 needed 3 compiles 7
01:56:59 src/ov_SC01_000/ov_SC01_000_jr_8013C98C.c:func_8013D9B0 A=DIFFERS B: removed 9 needed 25 compiles 36
01:56:59 src/ov_SC02_011/ov_SC02_011_jr_8017AE2C.c:func_8017FD9C A=DIFFERS B: removed 0 needed 2 compiles 3
01:56:59 src/ov_SC02_005/ov_SC02_005_jr_8018EA04.c:func_8018FEA0 A=DIFFERS B: removed 5 needed 3 compiles 10
01:56:59 src/ov_SC02_005/ov_SC02_005_jr_80185E80.c:func_8018D270 A=DIFFERS B: removed 0 needed 2 compiles 4
01:56:59 src/ov_SC02_005/ov_SC02_005_jr_8018EA04.c:func_8018FA34 A=DIFFERS B: removed 0 needed 3 compiles 5
01:57:00 src/ov_SC01_077/ov_SC01_077.c:func_80140958 A=DIFFERS B: removed 7 needed 4 compiles 13
01:57:00 src/ov_SC02_021/ov_SC02_021_jr_8017ECFC.c:func_8017F1B4 A=DIFFERS B: removed 0 needed 1 compiles 3
01:57:00 src/ov_SC02_011/ov_SC02_011_jr_80183178.c:func_80187E08 A=DIFFERS B: removed 1 needed 4 compiles 7
01:57:00 src/ov_SC02_021/ov_SC02_021_jr_8017C294.c:func_8017DC10 A=DIFFERS B: removed 1 needed 3 compiles 6
01:57:00 src/md_SC07_004/md_SC07_004.c:func_801A2DC4 A=DIFFERS B: removed 2 needed 3 compiles 7
01:57:00 src/ov_SC02_011/ov_SC02_011_jr_8017AE2C.c:func_8017F67C A=DIFFERS B: removed 0 needed 2 compiles 4
01:57:00 src/ov_SC02_028/ov_SC02_028_jr_801884D8.c:func_8018AB34 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:00 src/ov_SC02_028/ov_SC02_028_jr_8017D898.c:func_80181264 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:00 src/ov_SC02_011/ov_SC02_011_jr_80188E3C.c:func_8018AB20 A=DIFFERS B: removed 1 needed 4 compiles 7
01:57:01 src/ov_SC02_026/ov_SC02_026_jr_8017C180.c:func_8017F6B0 A=DIFFERS B: removed 0 needed 2 compiles 3
01:57:01 src/ov_SC02_011/ov_SC02_011_jr_8017AE2C.c:func_8017D710 A=DIFFERS B: removed 0 needed 2 compiles 3
01:57:01 src/ov_SC02_011/ov_SC02_011_jr_80183178.c:func_80186868 A=DIFFERS B: removed 1 needed 4 compiles 7
01:57:01 src/ov_SC02_035/ov_SC02_035_jr_8017BEBC.c:func_80181B00 A=DIFFERS B: removed 1 needed 1 compiles 4
01:57:01 src/ov_SC02_041/ov_SC02_041_jr_8017BEBC.c:func_8018317C A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:01 src/ov_MAIN_012/ov_MAIN_012.c:func_8012956C A=DIFFERS B: removed 6 needed 4 compiles 12
01:57:02 src/ov_SC02_039/ov_SC02_039_jr_8017BEBC.c:func_8017DE00 A=DIFFERS B: removed 6 needed 4 compiles 12
01:57:02 src/ov_SC03_001/ov_SC03_001_jr_8017AE2C.c:func_80182058 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:02 src/ov_SC03_001/ov_SC03_001_jr_80189030.c:func_801898E4 A=DIFFERS B: removed 1 needed 5 compiles 8
01:57:02 src/ov_SC03_001/ov_SC03_001_jr_8018B8DC.c:func_8018BCD4 A=DIFFERS B: removed 1 needed 4 compiles 7
01:57:03 src/ov_SC02_026/ov_SC02_026_jr_8017C180.c:func_8017E3AC A=DIFFERS B: removed 4 needed 1 compiles 7
01:57:03 src/ov_SC03_002/ov_SC03_002_jr_8017D604.c:func_8017EA54 A=DIFFERS B: removed 2 needed 3 compiles 7
01:57:03 src/800_b_2.c:func_8003388C A=DIFFERS B: removed 2 needed 15 compiles 19
01:57:03 src/ov_SC02_011/ov_SC02_011_jr_80183178.c:func_80187CE8 A=DIFFERS B: removed 4 needed 4 compiles 10
01:57:03 src/ov_SC03_013/ov_SC03_013_jr_8017C730.c:func_8017DD98 A=DIFFERS B: removed 1 needed 1 compiles 4
01:57:04 src/ov_SC02_011/ov_SC02_011_jr_80183178.c:func_80188250 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:04 src/ov_SC03_001/ov_SC03_001_jr_8017AE2C.c:func_8017BF50 A=DIFFERS B: removed 3 needed 1 compiles 6
01:57:04 src/800_c.c:func_80038A58 A=DIFFERS B: removed 26 needed 9 compiles 37
01:57:04 src/ov_SC03_011/ov_SC03_011_jr_80140608.c:func_80140958 A=DIFFERS B: removed 7 needed 4 compiles 13
01:57:04 src/ov_SC03_024/ov_SC03_024_jr_80183BF8.c:func_80185E38 A=DIFFERS B: removed 1 needed 4 compiles 7
01:57:04 src/ov_SC03_028/ov_SC03_028_jr_8017DF98.c:func_80180AC4 A=DIFFERS B: removed 0 needed 2 compiles 4
01:57:05 src/ov_SC03_024/ov_SC03_024_jr_8017DF84.c:func_80182108 A=DIFFERS B: removed 1 needed 3 compiles 6
01:57:05 src/ov_SC03_028/ov_SC03_028_jr_801887A8.c:func_80189AF4 A=DIFFERS B: removed 0 needed 3 compiles 5
01:57:05 src/800.c:func_800242D0 A=DIFFERS B: removed 6 needed 5 compiles 13
01:57:05 src/800_c.c:func_800384A8 A=DIFFERS B: removed 0 needed 1 compiles 3
01:57:05 src/md_SC07_004/md_SC07_004.c:func_801AB694 A=DIFFERS B: removed 5 needed 1 compiles 8
01:57:06 src/ov_SC03_006/ov_SC03_006_jr_8017AE2C.c:func_80185BD4 A=DIFFERS B: removed 3 needed 2 compiles 7
01:57:06 src/ov_SC03_030/ov_SC03_030_jr_8017AE2C.c:func_80180960 A=DIFFERS B: removed 0 needed 3 compiles 5
01:57:06 src/ov_SC03_105/ov_SC03_105_jr_8017C8D0.c:func_8017E180 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:06 src/ov_SC03_029/ov_SC03_029_jr_8017FF7C.c:func_80180B84 A=DIFFERS B: removed 4 needed 1 compiles 7
01:57:06 src/ov_SC03_105/ov_SC03_105_jr_80186DDC.c:func_80187928 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:06 src/ov_SC03_097/ov_SC03_097_jr_8017D898.c:func_80181CF4 A=DIFFERS B: removed 1 needed 4 compiles 7
01:57:06 src/ov_SC03_092/ov_SC03_092_jr_80181694.c:func_8018478C A=DIFFERS B: removed 0 needed 8 compiles 10
01:57:06 src/ov_SC03_108/ov_SC03_108_jr_8017F83C.c:func_8017FD64 A=DIFFERS B: removed 0 needed 2 compiles 3
01:57:07 src/ov_SC03_028/ov_SC03_028_jr_80185C14.c:func_80186368 A=DIFFERS B: removed 17 needed 3 compiles 22
01:57:07 src/ov_SC03_105/ov_SC03_105_jr_80181C84.c:func_80183A98 A=DIFFERS B: removed 0 needed 5 compiles 6
01:57:07 src/ov_SC03_121/ov_SC03_121_jr_8017E1D0.c:func_8017F374 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:07 src/ov_SC03_112/ov_SC03_112_jr_8017C294.c:func_8017D174 A=DIFFERS B: removed 1 needed 3 compiles 5
01:57:07 src/ov_SC03_094/ov_SC03_094_jr_8017BEBC.c:func_80181448 A=DIFFERS B: removed 2 needed 4 compiles 8
01:57:07 src/ov_SC04_015/ov_SC04_015_jr_8017AE2C.c:func_8017DBAC A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:08 src/md_SC07_004/md_SC07_004.c:func_801A395C A=DIFFERS B: removed 1 needed 2 compiles 5
01:57:08 src/ov_SC04_018/ov_SC04_018_jr_80188E1C.c:func_80189EA0 A=DIFFERS B: removed 1 needed 1 compiles 3
01:57:08 src/ov_SC04_018/ov_SC04_018_jr_801878E8.c:func_80188778 A=DIFFERS B: removed 2 needed 4 compiles 8
01:57:08 src/ov_SC02_037/ov_SC02_037_jr_8017AE2C.c:func_8017F2B4 A=DIFFERS B: removed 6 needed 17 compiles 25
01:57:08 src/ov_SC04_020/ov_SC04_020_jr_8017D604.c:func_8018087C A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:08 src/ov_SC03_113/ov_SC03_113_jr_8017C294.c:func_8017F394 A=DIFFERS B: removed 5 needed 1 compiles 8
01:57:08 src/md_SC07_004/md_SC07_004.c:func_801A7CDC A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:08 src/ov_SC03_107/ov_SC03_107_jr_801789AC.c:func_8017BEBC A=DIFFERS B: removed 0 needed 7 compiles 8
01:57:09 src/ov_SC01_077/ov_SC01_077.c:func_8013D9B0 A=DIFFERS B: removed 9 needed 25 compiles 36
01:57:09 src/ov_SC05_001/ov_SC05_001_jr_8017BEBC.c:func_80182258 A=DIFFERS B: removed 0 needed 1 compiles 3
01:57:09 src/ov_SC04_016/ov_SC04_016_jr_8017BEBC.c:func_8017DA50 A=DIFFERS B: removed 2 needed 8 compiles 12
01:57:09 src/ov_SC05_003/ov_SC05_003_jr_8017BEBC.c:func_801815A0 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:09 src/ov_SC03_113/ov_SC03_113_jr_8017C294.c:func_8017E178 A=DIFFERS B: removed 0 needed 2 compiles 3
01:57:09 src/ov_SC05_005/ov_SC05_005_jr_8017D898.c:func_801816A8 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:09 src/ov_SC04_015/ov_SC04_015_jr_8017AE2C.c:func_8017BF70 A=DIFFERS B: removed 3 needed 1 compiles 6
01:57:09 src/ov_SC05_010/ov_SC05_010_jr_80180F84.c:func_801814AC A=DIFFERS B: removed 1 needed 1 compiles 3
01:57:09 src/ov_SC01_077/ov_SC01_077.c:func_801418F8 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:09 src/ov_SC05_008/ov_SC05_008_jr_8017AE2C.c:func_8017E6A4 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:09 src/ov_SC05_002/ov_SC05_002_jr_8017BEBC.c:func_8017F200 A=DIFFERS B: removed 5 needed 1 compiles 8
01:57:09 src/ov_SC05_003/ov_SC05_003_jr_8017BEBC.c:func_801812AC A=DIFFERS B: removed 1 needed 1 compiles 3
01:57:09 src/ov_SC03_124/ov_SC03_124_jr_80188544.c:func_80188C68 A=DIFFERS B: removed 5 needed 19 compiles 26
01:57:10 src/ov_SC06_013/ov_SC06_013_jr_8017BEBC.c:func_8017DE14 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:10 src/ov_SC06_006/ov_SC06_006_jr_8017DB90.c:func_8017E550 A=DIFFERS B: removed 0 needed 3 compiles 4
01:57:10 src/ov_SC06_016/ov_SC06_016_jr_801816DC.c:func_80181C78 A=DIFFERS B: removed 0 needed 1 compiles 3
01:57:10 src/ov_SC06_011/ov_SC06_011_jr_8017EEEC.c:func_8017F3C8 A=DIFFERS B: removed 4 needed 1 compiles 7
01:57:10 src/ov_SC06_008/ov_SC06_008_jr_8017C294.c:func_8017D73C A=DIFFERS B: removed 0 needed 2 compiles 3
01:57:10 src/ov_SC06_018/ov_SC06_018_jr_80187AEC.c:func_8018A688 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:10 src/ov_SC04_011/ov_SC04_011_jr_8017D494.c:func_801835B0 A=DIFFERS B: removed 0 needed 3 compiles 4
01:57:10 src/ov_SC06_011/ov_SC06_011_jr_8017BEBC.c:func_8017E5D0 A=DIFFERS B: removed 4 needed 3 compiles 9
01:57:10 src/ov_SC05_010/ov_SC05_010_jr_80181CDC.c:func_80183D5C A=DIFFERS B: removed 9 needed 1 compiles 12
01:57:10 src/ov_SC06_016/ov_SC06_016_jr_8017C8D0.c:func_801810A4 A=DIFFERS B: removed 2 needed 3 compiles 6
01:57:11 src/ov_SC06_000/ov_SC06_000_jr_8017AE2C.c:func_80182464 A=DIFFERS B: removed 0 needed 2 compiles 4
01:57:11 src/ov_SC06_024/ov_SC06_024_jr_80186F00.c:func_8018D674 A=DIFFERS B: removed 0 needed 1 compiles 3
01:57:11 src/ov_SC03_107/ov_SC03_107_jr_801789AC.c:func_8017CF48 A=DIFFERS B: removed 2 needed 4 compiles 8
01:57:11 src/ov_SC06_029/ov_SC06_029_jr_801898CC.c:func_8018A180 A=COMPILE-ERROR B: removed 0 needed 6 compiles 7
01:57:11 src/ov_SC06_018/ov_SC06_018_jr_8017C24C.c:func_801811DC A=DIFFERS B: removed 0 needed 1 compiles 3
01:57:12 src/ov_SC06_024/ov_SC06_024_jr_8017BEBC.c:func_8017FA1C A=DIFFERS B: removed 1 needed 3 compiles 6
01:57:12 src/ov_SC06_030/ov_SC06_030_jr_8017F65C.c:func_80180FF4 A=DIFFERS B: removed 0 needed 10 compiles 12
01:57:12 src/ov_SC06_033/ov_SC06_033_jr_8018D98C.c:func_8018D98C A=DIFFERS B: removed 0 needed 2 compiles 3
01:57:12 src/ov_SC06_018/ov_SC06_018_jr_8017C24C.c:func_80185DD8 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:12 src/ov_SC06_018/ov_SC06_018_jr_80187AEC.c:func_8018A974 A=DIFFERS B: removed 2 needed 3 compiles 7
01:57:12 src/ov_SC06_032/ov_SC06_032_jr_801902EC.c:func_801902EC A=DIFFERS B: removed 4 needed 9 compiles 14
01:57:12 src/ov_SC06_033/ov_SC06_033_jr_8018C2D8.c:func_8018C2D8 A=DIFFERS B: removed 4 needed 8 compiles 13
01:57:12 src/ov_SC06_032/ov_SC06_032_jr_801919A0.c:func_801919A0 A=REFUSED B: removed 4 needed 10 compiles 14
01:57:13 src/ov_SC07_000/ov_SC07_000_jr_8017F8B8.c:func_80181BA8 A=DIFFERS B: removed 1 needed 4 compiles 7
01:57:13 src/ov_SC07_007/ov_SC07_007_jr_8013C98C.c:func_8013D53C A=DIFFERS B: removed 0 needed 2 compiles 3
01:57:13 src/ov_SC06_016/ov_SC06_016_jr_8017C8D0.c:func_801813B0 A=DIFFERS B: removed 0 needed 12 compiles 13
01:57:13 src/ov_SC07_007/ov_SC07_007_jr_8017BEBC.c:func_8018044C A=DIFFERS B: removed 0 needed 1 compiles 3
01:57:13 src/resident/resident.c:func_800CFE60 A=DIFFERS B: removed 1 needed 2 compiles 5
01:57:13 src/ov_SC07_002/ov_SC07_002_jr_8017C8D0.c:func_80185FB0 A=DIFFERS B: removed 0 needed 2 compiles 4
01:57:13 src/ov_SC07_010/ov_SC07_010_jr_8017AE2C.c:func_80180480 A=DIFFERS B: removed 0 needed 1 compiles 3
01:57:13 src/resident/resident_jr_800D00E4.c:func_800D0488 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:13 src/resident/resident_jr_800D128C.c:func_800D1984 A=DIFFERS B: removed 0 needed 1 compiles 3
01:57:14 src/ov_SC06_032/ov_SC06_032_jr_80182890.c:func_8018BA4C A=DIFFERS B: removed 3 needed 2 compiles 7
01:57:14 src/ov_SC06_000/ov_SC06_000_jr_8017AE2C.c:func_801835B0 A=DIFFERS B: removed 0 needed 7 compiles 8
01:57:14 src/ov_SC06_018/ov_SC06_018_jr_8017C24C.c:func_8017DB20 A=DIFFERS B: removed 2 needed 1 compiles 5
01:57:14 src/resident/resident_jr_800D00E4.c:func_800D0E30 A=DIFFERS B: removed 2 needed 3 compiles 7
01:57:14 src/resident/resident_jr_800D128C.c:func_800D128C A=DIFFERS B: removed 0 needed 3 compiles 4
01:57:14 src/ov_SC07_007/ov_SC07_007_jr_8017BEBC.c:func_8018122C A=DIFFERS B: removed 1 needed 1 compiles 4
01:57:14 src/resident/resident_jr_800D00E4.c:func_800D0214 A=DIFFERS B: removed 1 needed 1 compiles 4
01:57:14 src/resident/resident_jr_800D128C.c:func_800D1658 A=DIFFERS B: removed 0 needed 3 compiles 5
01:57:14 src/resident/resident_jr_800D00E4.c:func_800D02D0 A=DIFFERS B: removed 0 needed 1 compiles 3
01:57:14 src/ov_SC04_011/ov_SC04_011_jr_8017D494.c:func_8018E428 A=DIFFERS B: removed 1 needed 4 compiles 7
01:57:14 src/resident/resident_jr_800D00E4.c:func_800D0D7C A=DIFFERS B: removed 0 needed 1 compiles 3
01:57:14 src/resident/resident_jr_800D128C.c:func_800D2D10 A=DIFFERS B: removed 0 needed 1 compiles 3
01:57:14 src/ov_SC07_010/ov_SC07_010_jr_80140608.c:func_80140958 A=DIFFERS B: removed 7 needed 4 compiles 13
01:57:15 src/resident/resident_jr_800D128C.c:func_800D2650 A=DIFFERS B: removed 1 needed 4 compiles 7
01:57:15 src/resident/resident_jr_800D128C.c:func_800D1B80 A=DIFFERS B: removed 0 needed 1 compiles 3
01:57:15 src/ov_SC07_006/ov_SC07_006_jr_8017BEBC.c:func_80182F3C A=DIFFERS B: removed 6 needed 6 compiles 13
01:57:15 src/ov_SC07_006/ov_SC07_006_jr_80171B4C.c:func_80176734 A=DIFFERS B: removed 1 needed 11 compiles 14
01:57:16 src/ov_SC04_011/ov_SC04_011_jr_8017D494.c:func_8018315C A=DIFFERS B: removed 0 needed 2 compiles 4
01:57:29 src/800.c:func_800226C0 A=DIFFERS B: removed 31 needed 14 compiles 47
01:57:30 src/800.c:func_80020DA4 A=DIFFERS B: removed 0 needed 2 compiles 3
01:57:34 src/800.c:func_8002AF70 A=DIFFERS B: removed 2 needed 3 compiles 7
01:57:37 src/800.c:func_800241C0 A=DIFFERS B: removed 3 needed 3 compiles 8
01:57:38 src/800.c:func_80016A7C A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:45 src/800.c:func_80023570 A=DIFFERS B: removed 9 needed 4 compiles 15
01:57:47 src/800.c:func_8001A114 A=DIFFERS B: removed 0 needed 2 compiles 3
01:57:53 src/800.c:func_80016224 A=DIFFERS B: removed 3 needed 6 compiles 11
01:57:54 src/800.c:func_80025A30 A=DIFFERS B: removed 0 needed 1 compiles 2
01:57:55 src/800.c:func_8001EA14 A=DIFFERS B: removed 0 needed 1 compiles 3
01:58:13 src/800.c:func_800215F4 A=DIFFERS B: removed 29 needed 7 compiles 38
01:58:19 src/800.c:func_8002AC00 A=DIFFERS B: removed 4 needed 5 compiles 11
01:58:25 src/800.c:func_8001BADC A=DIFFERS B: removed 5 needed 3 compiles 10
01:58:26 src/800.c:func_800273F4 A=DIFFERS B: removed 0 needed 1 compiles 2
01:58:30 src/800.c:func_80015208 A=DIFFERS B: removed 3 needed 1 compiles 6
01:58:35 src/800.c:func_8001BBBC A=DIFFERS B: removed 4 needed 4 compiles 10
01:58:39 src/800.c:LoaderInitFileTable A=DIFFERS B: removed 0 needed 7 compiles 9
01:58:40 src/shared/ov/func_8012A860.h:func_8012A860 A=DIFFERS B: removed 0 needed 1 compiles 3
01:58:41 src/shared/ov/func_8012C2D0.h:func_8012C2D0 A=DIFFERS B: removed 0 needed 2 compiles 3
01:58:44 src/shared/ov/func_8012DBD0.h:func_8012DBD0 A=DIFFERS B: removed 8 needed 4 compiles 14
01:58:46 src/shared/ov/func_80135004__0f8f8d0b.h:func_80135004 A=DIFFERS B: removed 4 needed 4 compiles 10
01:58:48 src/shared/ov/func_80135168__bad0a585.h:func_80135168 A=DIFFERS B: removed 0 needed 5 compiles 7
01:58:48 src/shared/ov/func_80135A4C__e0129286.h:func_80135A4C A=DIFFERS B: removed 0 needed 3 compiles 5
01:58:49 src/shared/ov/func_80135EB0__c9438c12.h:func_80135EB0 A=DIFFERS B: removed 0 needed 6 compiles 8
01:58:50 src/shared/ov/func_80136334__6e7f4730.h:func_80136334 A=DIFFERS B: removed 4 needed 4 compiles 10
01:58:51 src/shared/ov/func_8013B274__28562738.h:func_8013B274 A=DIFFERS B: removed 4 needed 8 compiles 13
01:58:51 src/shared/ov/func_8013D178__421ba659.h:func_8013D178 A=DIFFERS B: removed 0 needed 1 compiles 3
01:58:52 src/shared/ov/func_80140608__2fb5c201.h:func_80140608 A=DIFFERS B: removed 1 needed 1 compiles 4
01:58:53 src/shared/ov/func_801415C0.h:func_801415C0 A=DIFFERS B: removed 3 needed 2 compiles 7
01:58:53 src/shared/ov/func_80141B90.h:func_80141B90 A=DIFFERS B: removed 0 needed 1 compiles 2
01:58:54 src/shared/ov/func_80145A2C__530ca2ff.h:func_80145A2C A=DIFFERS B: removed 0 needed 1 compiles 2
01:58:54 src/shared/ov/func_801497A8.h:func_801497A8 A=DIFFERS B: removed 0 needed 1 compiles 2
01:58:55 src/shared/ov/func_8014DD8C.h:func_8014DD8C A=DIFFERS B: removed 0 needed 2 compiles 3
01:58:56 src/shared/ov/func_8014F74C.h:func_8014F74C A=DIFFERS B: removed 0 needed 1 compiles 2
01:58:58 src/shared/ov/func_801536DC.h:func_801536DC A=DIFFERS B: removed 2 needed 1 compiles 5
01:59:00 src/shared/ov/func_8015B6F4.h:func_8015B6F4 A=DIFFERS B: removed 1 needed 3 compiles 6
01:59:02 src/shared/ov/func_801612B8.h:func_801612B8 A=DIFFERS B: removed 2 needed 1 compiles 5
01:59:05 src/shared/ov/func_80162438__e113e304.h:func_80162438 A=DIFFERS B: removed 2 needed 3 compiles 7
01:59:09 src/shared/ov/func_80166054.h:func_80166054 A=DIFFERS B: removed 5 needed 5 compiles 12
01:59:11 src/shared/ov/func_80166F58__3728db8a.h:func_80166F58 A=DIFFERS B: removed 2 needed 2 compiles 6
01:59:12 src/shared/ov/func_8016DF5C__a8a4aea4.h:func_8016DF5C A=DIFFERS B: removed 1 needed 2 compiles 5
01:59:13 src/shared/ov/func_801759D8.h:func_801759D8 A=DIFFERS B: removed 1 needed 1 compiles 3
01:59:16 src/shared/ov/func_80176734__261d72d2.h:func_80176734 A=DIFFERS B: removed 1 needed 11 compiles 14
01:59:18 src/shared/ov/func_801778A8.h:func_801778A8 A=DIFFERS B: removed 3 needed 2 compiles 7
01:59:23 src/shared/ov/func_80177B5C__879ede92.h:func_80177B5C A=DIFFERS B: removed 8 needed 15 compiles 25
01:59:24 src/shared/ov/func_8017CF6C__4771362e.h:func_8017CF6C A=DIFFERS B: removed 0 needed 1 compiles 3
01:59:24 src/shared/ov/func_8017D0BC__4f23330c.h:func_8017D0BC A=DIFFERS B: removed 0 needed 1 compiles 3
01:59:24 src/shared/ov/func_8017E98C__94ae7b9b.h:func_8017E98C A=DIFFERS B: removed 0 needed 1 compiles 3
01:59:25 src/shared/ov/func_80181538__27797037.h:func_80181538 A=DIFFERS B: removed 0 needed 2 compiles 4
01:59:25 src/shared/ov/func_80188028.h:func_80188028 A=DIFFERS B: removed 0 needed 1 compiles 2
# probe — 285 bodies sampled, 283 judged (seed 11, 2026-09-09 01:59, wall 151.4 s)
- rung A (strip-all) identical: **53 / 283** (register dropped) · 53 with `register` kept
- lever-free after rung B: **53 / 283**
- sites: 1309 usable · removed 561 (42.9 %) · needed 748
- needed by kind: {'pin': 360, 'launder': 154, 'instruction': 21, 'barrier': 127, 'register': 8, 'keepalive': 36, 'cast': 26, 'decl-body': 16}
- needed by detail (top): {'launder:': 123, 'barrier:': 82, 'pin:$2': 80, 'pin:$4': 50, 'pin:$3': 46, 'barrier:memory': 45, 'pin:$5': 37, 'keepalive:': 36, 'pin:$16': 27, 'launder:out-only': 20, 'cast:volatile s32 *': 18, 'pin:$17': 17, 'pin:$6': 15, 'instruction:addiu': 14, 'decl-body:': 14, 'pin:$0': 13, 'pin:$19': 13, 'pin:$8': 11, 'pin:$7': 10, 'pin:$9': 8, 'register:': 8, 'pin:$14': 5, 'pin:$21': 5, 'launder:LAUNDER_8018A180': 5, 'pin:$10': 4, 'pin:$12': 4, 'pin:$18': 4, 'cast:volatile s16 *': 4, 'instruction:addu': 4, 'pin:$20': 4}
- removed by kind: {'instruction': 3, 'pin': 438, 'decl-body': 11, 'register': 4, 'launder': 45, 'barrier': 27, 'keepalive': 8, 'cast': 25}
- refused (rewrite not applicable): {'instruction `sll` has no C spelling in the table': 1, 'instruction `addu` has no C spelling in the table': 1, 'instruction `li` has no C spelling in the table': 1, 'instruction `nop` has no C spelling in the table': 1, 'macro-carried instruction is residue': 2, 'no statement end for the macro use': 3}
- compiles 1660 · mean 5.87 per body · 0.225 s per compile
| kind | bodies | A identical | A (register kept) | lever-free after B | sites | removed | needed | compiles | s/compile |
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|
| main | 40 | 6 | 6 | 6 | 272 | 146 | 126 | 328 | 0.386 |
| resident | 16 | 4 | 4 | 4 | 31 | 9 | 22 | 53 | 0.06 |
| md | 29 | 2 | 2 | 2 | 121 | 56 | 65 | 161 | 0.139 |
| shared | 40 | 7 | 7 | 7 | 164 | 65 | 99 | 216 | 0.211 |
| ov | 158 | 34 | 34 | 34 | 721 | 285 | 436 | 902 | 0.195 |
| feature | bodies | lever-free |
|---|---:|---:|
| zero | 17 | 1 |
| init | 32 | 7 |
| instr | 10 | 1 |
| cast | 22 | 1 |
| register | 12 | 3 |
| launder | 68 | 4 |
| barrier | 78 | 9 |
| keepalive | 27 | 1 |
| volatile | 40 | 11 |
**Pricing (R41; population 16,064 lever bodies / 55,913 removable sites):** T4 ≈ 94,226 compiles ≈ 5.89 CPU-h ≈ 0.49 h wall at 12 workers; residue after the mechanical ladder ≈ 13,055 bodies / 31,950 sites (the sample's rates applied to the population).
probe wall=160.86 s
+188
View File
@@ -0,0 +1,188 @@
{
"generated": "2026-09-09 01:59",
"seed": 11,
"sampled": 285,
"judged": 283,
"not_judged": {
"NOTHING-USABLE": 2
},
"rung_A_identical": 53,
"rung_A_keep_register_identical": 53,
"lever_free_after_B": 53,
"sites": {
"total": 1309,
"removed": 561,
"needed": 748,
"pct_removed": 42.9
},
"needed_by_kind": {
"pin": 360,
"launder": 154,
"instruction": 21,
"barrier": 127,
"register": 8,
"keepalive": 36,
"cast": 26,
"decl-body": 16
},
"needed_by_detail": {
"launder:": 123,
"barrier:": 82,
"pin:$2": 80,
"pin:$4": 50,
"pin:$3": 46,
"barrier:memory": 45,
"pin:$5": 37,
"keepalive:": 36,
"pin:$16": 27,
"launder:out-only": 20,
"cast:volatile s32 *": 18,
"pin:$17": 17,
"pin:$6": 15,
"instruction:addiu": 14,
"decl-body:": 14,
"pin:$0": 13,
"pin:$19": 13,
"pin:$8": 11,
"pin:$7": 10,
"pin:$9": 8,
"register:": 8,
"pin:$14": 5,
"pin:$21": 5,
"launder:LAUNDER_8018A180": 5,
"pin:$10": 4,
"pin:$12": 4,
"pin:$18": 4,
"cast:volatile s16 *": 4,
"instruction:addu": 4,
"pin:$20": 4
},
"removed_by_kind": {
"instruction": 3,
"pin": 438,
"decl-body": 11,
"register": 4,
"launder": 45,
"barrier": 27,
"keepalive": 8,
"cast": 25
},
"refused": {
"instruction `sll` has no C spelling in the table": 1,
"instruction `addu` has no C spelling in the table": 1,
"instruction `li` has no C spelling in the table": 1,
"instruction `nop` has no C spelling in the table": 1,
"macro-carried instruction is residue": 2,
"no statement end for the macro use": 3
},
"by_kind": {
"main": {
"bodies": 40,
"A_identical": 6,
"A_keep_register_identical": 6,
"lever_free": 6,
"sites": 272,
"removed": 146,
"needed": 126,
"compiles": 328,
"mean_s_per_compile": 0.386
},
"resident": {
"bodies": 16,
"A_identical": 4,
"A_keep_register_identical": 4,
"lever_free": 4,
"sites": 31,
"removed": 9,
"needed": 22,
"compiles": 53,
"mean_s_per_compile": 0.06
},
"md": {
"bodies": 29,
"A_identical": 2,
"A_keep_register_identical": 2,
"lever_free": 2,
"sites": 121,
"removed": 56,
"needed": 65,
"compiles": 161,
"mean_s_per_compile": 0.139
},
"shared": {
"bodies": 40,
"A_identical": 7,
"A_keep_register_identical": 7,
"lever_free": 7,
"sites": 164,
"removed": 65,
"needed": 99,
"compiles": 216,
"mean_s_per_compile": 0.211
},
"ov": {
"bodies": 158,
"A_identical": 34,
"A_keep_register_identical": 34,
"lever_free": 34,
"sites": 721,
"removed": 285,
"needed": 436,
"compiles": 902,
"mean_s_per_compile": 0.195
}
},
"flags": {
"zero": {
"bodies": 17,
"lever_free": 1
},
"init": {
"bodies": 32,
"lever_free": 7
},
"instr": {
"bodies": 10,
"lever_free": 1
},
"cast": {
"bodies": 22,
"lever_free": 1
},
"register": {
"bodies": 12,
"lever_free": 3
},
"launder": {
"bodies": 68,
"lever_free": 4
},
"barrier": {
"bodies": 78,
"lever_free": 9
},
"keepalive": {
"bodies": 27,
"lever_free": 1
},
"volatile": {
"bodies": 40,
"lever_free": 11
}
},
"compiles": 1660,
"mean_compiles_per_body": 5.87,
"mean_s_per_compile": 0.225,
"wall_s": 151.4,
"population": {
"bodies": 16064,
"sites": 55913
},
"pricing": {
"T4_compiles_est": 94226,
"T4_cpu_hours_est": 5.89,
"T4_wall_hours_at_16_est": 0.49,
"residue_bodies_est": 13055,
"residue_sites_est": 31950
}
}
+32
View File
@@ -0,0 +1,32 @@
# probe — 285 bodies sampled, 283 judged (seed 11, 2026-09-09 01:59, wall 151.4 s)
- rung A (strip-all) identical: **53 / 283** (register dropped) · 53 with `register` kept
- lever-free after rung B: **53 / 283**
- sites: 1309 usable · removed 561 (42.9 %) · needed 748
- needed by kind: {'pin': 360, 'launder': 154, 'instruction': 21, 'barrier': 127, 'register': 8, 'keepalive': 36, 'cast': 26, 'decl-body': 16}
- needed by detail (top): {'launder:': 123, 'barrier:': 82, 'pin:$2': 80, 'pin:$4': 50, 'pin:$3': 46, 'barrier:memory': 45, 'pin:$5': 37, 'keepalive:': 36, 'pin:$16': 27, 'launder:out-only': 20, 'cast:volatile s32 *': 18, 'pin:$17': 17, 'pin:$6': 15, 'instruction:addiu': 14, 'decl-body:': 14, 'pin:$0': 13, 'pin:$19': 13, 'pin:$8': 11, 'pin:$7': 10, 'pin:$9': 8, 'register:': 8, 'pin:$14': 5, 'pin:$21': 5, 'launder:LAUNDER_8018A180': 5, 'pin:$10': 4, 'pin:$12': 4, 'pin:$18': 4, 'cast:volatile s16 *': 4, 'instruction:addu': 4, 'pin:$20': 4}
- removed by kind: {'instruction': 3, 'pin': 438, 'decl-body': 11, 'register': 4, 'launder': 45, 'barrier': 27, 'keepalive': 8, 'cast': 25}
- refused (rewrite not applicable): {'instruction `sll` has no C spelling in the table': 1, 'instruction `addu` has no C spelling in the table': 1, 'instruction `li` has no C spelling in the table': 1, 'instruction `nop` has no C spelling in the table': 1, 'macro-carried instruction is residue': 2, 'no statement end for the macro use': 3}
- compiles 1660 · mean 5.87 per body · 0.225 s per compile
| kind | bodies | A identical | A (register kept) | lever-free after B | sites | removed | needed | compiles | s/compile |
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|
| main | 40 | 6 | 6 | 6 | 272 | 146 | 126 | 328 | 0.386 |
| resident | 16 | 4 | 4 | 4 | 31 | 9 | 22 | 53 | 0.06 |
| md | 29 | 2 | 2 | 2 | 121 | 56 | 65 | 161 | 0.139 |
| shared | 40 | 7 | 7 | 7 | 164 | 65 | 99 | 216 | 0.211 |
| ov | 158 | 34 | 34 | 34 | 721 | 285 | 436 | 902 | 0.195 |
| feature | bodies | lever-free |
|---|---:|---:|
| zero | 17 | 1 |
| init | 32 | 7 |
| instr | 10 | 1 |
| cast | 22 | 1 |
| register | 12 | 3 |
| launder | 68 | 4 |
| barrier | 78 | 9 |
| keepalive | 27 | 1 |
| volatile | 40 | 11 |
**Pricing (R41; population 16,064 lever bodies / 55,913 removable sites):** T4 ≈ 94,226 compiles ≈ 5.89 CPU-h ≈ 0.49 h wall at 12 workers; residue after the mechanical ladder ≈ 13,055 bodies / 31,950 sites (the sample's rates applied to the population).
+2
View File
@@ -69,6 +69,8 @@ tools/decompme_replica.sh P4 ADAPT reproduce an external reference toolchain loc
tools/dedup_extend.py P6 ADAPT extend an existing code-share registry to newly onboarded binaries Extends the existing share registry to newly onboarded binaries repo config/src layout SUPERSEDED tools/share_body.py
tools/dedup_integrate.py P6 ADAPT validate a code-share registry byte-honestly, failing closed on signature drift Byte-honesty validator for the code-share registry; fails closed on signature drift repo config registry path LIVE
tools/dedup_propagate.py P6 ADAPT lift one matched body into a shared macro and instantiate it everywhere Lifts one matched body into a shared macro and instantiates it at every duplicate site repo shared-header/src layout SUPERSEDED tools/share_body.py
tools/delever.py P10 ADAPT take register pins, asm statements, volatile and register levers out of matched C, byte-gated per site The Phase-36 de-lever engine: positional rewrites per lever class (pin -> plain declaration, barrier/launder/keep-alive deleted, a hand-placed instruction -> its C, the zero-register variable -> 0, volatile/register dropped), rung A strip-all and rung B greedy per body through delever_oracle, headers serialized after the TU phase; --probe runs a stratified sample of distinct bodies and prices the campaign with denominators the census site records, the oracle LIVE
tools/delever_oracle.py P10 ADAPT judge one translation-unit edit by the bytes of its object in under a second The Phase-36 fast byte oracle: every object's exact build command captured once via make -n -W (the Makefile's own recipe, pad stage and -O0 overrides included), a candidate compiled in place with -o/-MF redirected to scratch and compared with the fleet run's object; --calibrate proves 100 % equality untouched, twin == primary and a positive control before any verdict is trusted the Makefile's object rules, the twin rule LIVE
tools/demacroize.py P6 ADAPT escape a shared-header declaration conflict locally by unmacroizing one body Per-binary local escape from a shared-header declaration conflict by unmacroizing one body repo shared-header layout FROZEN tools/share_body.py
tools/diff_autopsy.sh P7 ADAPT find which words diverge when the whole-binary gate says different but not where Reproduces exactly what the gate saw, then decodes the diverging words and restores the tree repo src/build paths STILL-NEEDED find WHICH words diverge when the whole-binary gate says DIFF but not where
tools/diff_regions.py P7 ADAPT classify where a remapped sibling's compiled bytes diverge from its target Classifies where a remapped member's compiled bytes diverge from its target repo build paths STILL-NEEDED classify where a mechanically remapped sibling's compiled bytes diverge from its target
1 path phase portability need what adapts status successor_or_product
69 tools/dedup_extend.py P6 ADAPT extend an existing code-share registry to newly onboarded binaries Extends the existing share registry to newly onboarded binaries repo config/src layout SUPERSEDED tools/share_body.py
70 tools/dedup_integrate.py P6 ADAPT validate a code-share registry byte-honestly, failing closed on signature drift Byte-honesty validator for the code-share registry; fails closed on signature drift repo config registry path LIVE
71 tools/dedup_propagate.py P6 ADAPT lift one matched body into a shared macro and instantiate it everywhere Lifts one matched body into a shared macro and instantiates it at every duplicate site repo shared-header/src layout SUPERSEDED tools/share_body.py
72 tools/delever.py P10 ADAPT take register pins, asm statements, volatile and register levers out of matched C, byte-gated per site The Phase-36 de-lever engine: positional rewrites per lever class (pin -> plain declaration, barrier/launder/keep-alive deleted, a hand-placed instruction -> its C, the zero-register variable -> 0, volatile/register dropped), rung A strip-all and rung B greedy per body through delever_oracle, headers serialized after the TU phase; --probe runs a stratified sample of distinct bodies and prices the campaign with denominators the census site records, the oracle LIVE
73 tools/delever_oracle.py P10 ADAPT judge one translation-unit edit by the bytes of its object in under a second The Phase-36 fast byte oracle: every object's exact build command captured once via make -n -W (the Makefile's own recipe, pad stage and -O0 overrides included), a candidate compiled in place with -o/-MF redirected to scratch and compared with the fleet run's object; --calibrate proves 100 % equality untouched, twin == primary and a positive control before any verdict is trusted the Makefile's object rules, the twin rule LIVE
74 tools/demacroize.py P6 ADAPT escape a shared-header declaration conflict locally by unmacroizing one body Per-binary local escape from a shared-header declaration conflict by unmacroizing one body repo shared-header layout FROZEN tools/share_body.py
75 tools/diff_autopsy.sh P7 ADAPT find which words diverge when the whole-binary gate says different but not where Reproduces exactly what the gate saw, then decodes the diverging words and restores the tree repo src/build paths STILL-NEEDED find WHICH words diverge when the whole-binary gate says DIFF but not where
76 tools/diff_regions.py P7 ADAPT classify where a remapped sibling's compiled bytes diverge from its target Classifies where a remapped member's compiled bytes diverge from its target repo build paths STILL-NEEDED classify where a mechanically remapped sibling's compiled bytes diverge from its target
@@ -1,7 +1,9 @@
# Gen3 handoff — where Gen2 ends, what comes next, and the levers already in hand
> **Written at the Gen2 exit (Phase 33, 2026-09-07).** Gen2's contract is met and published: 218 binaries rebuild
> byte-identical from C ([`docs/verification.md`](verification.md)), every game-code function in every binary is C, the
> byte-identical from C ([`docs/verification.md`](verification.md)), every game-code function in every binary is C (*corrected
> 2026-09-09, Phase 36 T1b: fourteen were whole-body assembly inside C shells, invisible to the file-scope detector — listed
> DECOMPILE-NOW / UNCERTAIN in the manifest*), the
> repository is public with its full rewritten history. This page is the seed for Gen3's first plan — it names the
> owner's stated next intent, gives the starter census derived from the tree (with the commands, so the next session
> re-derives rather than trusts), states the one invariant that must survive every Gen3 edit, and inventories the
+4 -2
View File
@@ -8,8 +8,8 @@
> its platform SDK need the marked adaptation. The last table lists the tools that are project-only in code (their *shape* is a task;
> their code does not transfer). *TODO(platform): the MIPS and PlayStation SDK hard-codes are the ones another platform replaces first.*
>
> **Coverage:** 296 tool files in scope (submodules, vendored and downloaded code excluded), of which 296 live rows
> below; per phase: P1 2 · P2 26 · P3 17 · P4 9 · P5 26 · P6 51 · P7 20 · P8 86 · P9 27 · P10 16 · PROJECT-ONLY 16. Superseded tools appear only as pointers to their successor (30 pointer rows); one-offs are omitted. Table rows in all: 326 (the installer checks its copy against this figure).
> **Coverage:** 298 tool files in scope (submodules, vendored and downloaded code excluded), of which 298 live rows
> below; per phase: P1 2 · P2 26 · P3 17 · P4 9 · P5 26 · P6 51 · P7 20 · P8 86 · P9 27 · P10 18 · PROJECT-ONLY 16. Superseded tools appear only as pointers to their successor (30 pointer rows); one-offs are omitted. Table rows in all: 328 (the installer checks its copy against this figure).
## P1 — extraction + manifest
@@ -333,12 +333,14 @@
| `lint_symbol_refs.py` | flag address-named references whose address now has a curated name | Flags address-named references in committed sources whose address now has a curated name | repo symbol/src paths |
| `uniquify_type.py` | give each conflicting camp of a same-named type its own name | Gives each conflicting camp of a same-named type its own name so every camp becomes liftable | repo src layout |
| `verbatim_check.py` | guard that every inline-assembly body still reproduces its target bytes | Regression guard that every inline-assembly body still reproduces its target bytes | repo src layout |
| `delever_oracle.py` | judge one translation-unit edit by the bytes of its object in under a second | The Phase-36 fast byte oracle: every object's exact build command captured once via make -n -W (the Makefile's own recipe, pad stage and -O0 overrides included), a candidate compiled in place with -o/-MF redirected to scratch and compared with the fleet run's object; --calibrate proves 100 % equality untouched, twin == primary and a positive control before any verdict is trusted | the Makefile's object rules, the twin rule |
| `share_census.py` | measure duplicate function bodies across the fleet and assert one source per unique function | The census of byte-identical function classes across every binary with their source forms and verdicts, plus the S1 invariant check with its exception ledger and a fixture self-test | repo paths, the registry and signature schemas |
| `ghidra_apply_symbols.sh` | mirror the curated symbol file into the analysis database with a real save | Mirrors the curated symbol file into the analysis program headlessly, with a real save | repo symbol path, project name |
| `frozen.py` | refuse, from one place, the command line of a tool the project has frozen | The one refusal a FROZEN tool prints from its main(): the tool, the successor and why; imports never exit (libraries stay usable) | nothing |
| `verbatim_target_s.py` | regenerate a splitter-format target disassembly for a function no longer stubbed | Regenerates a splitter-format target disassembly for a function that is no longer a stub; --gas emits the assemblable gas-syntax form ($-registers, .L labels, noreorder) that a web diff service accepts as a pasted target | repo build/asm layout |
| `share_body_cycle.sh` | run the share-body batch cycle unattended: batch, verify by exit code, log, commit, periodic clean fleet check | The Phase-35 T5 driver for bucket new: per batch share_body --apply --bucket new --batches 1 --label new<k>, the gated N/N line read, the phase log entry appended, the bank committed the moment it is green, the clean fleet run every N batches — stops on the first red | repo paths, the phase-log format, the fleet count from config/check.*.sha |
| `share_body.py` | share one byte-identical function class across its binaries through an include-at-site header, gated per binary | The Phase-35 successor of dedup_propagate + dedup_extend for the include-at-site form: exemplar by majority text, the header written once, every private copy replaced by the include at its position, per-binary byte gate with object comparison, bisect on red, the registry appended or extended by text, the exception ledger on refusal; --plan / --apply --bucket extend|new | repo paths, the registry and signature schemas |
| `delever.py` | take register pins, asm statements, volatile and register levers out of matched C, byte-gated per site | The Phase-36 de-lever engine: positional rewrites per lever class (pin -> plain declaration, barrier/launder/keep-alive deleted, a hand-placed instruction -> its C, the zero-register variable -> 0, volatile/register dropped), rung A strip-all and rung B greedy per body through delever_oracle, headers serialized after the TU phase; --probe runs a stratified sample of distinct bodies and prices the campaign with denominators | the census site records, the oracle |
| `verbatim_to_stub.py` | turn an inline-assembly body back into a stub the toolchain can reach | Turns an inline-assembly body back into an include-assembly stub | repo src/asm layout |
## PROJECT-ONLY — project-only in code (the shape is a task; the code does not transfer)
@@ -0,0 +1,608 @@
#!/usr/bin/env python3
"""delever.py — take the compiler-forcing constructs ("levers") out of matched C, byte-gated per translation unit
(Phase 36; the probe engine at T2, the campaign tool at T3).
tools/delever.py --probe --sample 150 [--seed 1] [-j 12] # T2: rung A + rung B on a stratified sample of DISTINCT bodies
# -> .run/P36/probe/{probe_results.jsonl,probe_summary.json,probe_table.md}
THE LADDER PER BODY (deterministic, zero tokens):
rung A strip-all — every removable site of the body rewritten at once (pin -> plain declaration; barrier/launder/keep-alive ->
deleted; a hand-placed instruction -> its C; `$0` pin -> its variable's uses replaced by 0; volatile -> dropped; register ->
dropped), one compile, the object compared with the fleet run's (tools/delever_oracle.py). Measured twice: with the `register`
keyword dropped from the pin (the target form) and with it kept (A').
rung B greedy — if A differs: from the untouched text, the sites are re-applied one at a time (asm statements first, then pins,
volatile, register); a site whose removal keeps the object identical is REMOVED, one that changes it is NEEDED.
The verdict of a body = the set of NEEDED sites; a body with none is lever-free. Never touched: GTE ops, the manifest's verbatim
bodies, an asm-body (a whole routine — the manifest's DECOMPILE rows are another task's work), file-scope declarations.
EVERY EDIT IS POSITIONAL on the raw text (line/col from the census), the file is written in place for the compile and restored
from the in-memory snapshot right after (never `git checkout`, R102); a site whose raw token does not match the census is REFUSED
(R43), never guessed.
"""
import argparse
import collections
import json
import os
import pathlib
import random
import re
import sys
import time
from concurrent.futures import ThreadPoolExecutor
REPO = pathlib.Path(__file__).resolve().parent.parent
sys.path.insert(0, str(REPO / "tools"))
import delever_oracle as oracle # noqa: E402
import lever_census as lc # noqa: E402
import share_census as sc # noqa: E402
SITES = REPO / ".run" / "P36" / "census" / "lever_sites.jsonl"
PROBE = REPO / ".run" / "P36" / "probe"
REMOVABLE = {("A", "pin"), ("B", "barrier"), ("B", "launder"), ("B", "keepalive"), ("B", "instruction"),
("C", "cast"), ("C", "decl-body"), ("D", "register")}
ORDER = {"barrier": 0, "launder": 1, "keepalive": 2, "instruction": 3, "pin": 4, "cast": 5, "decl-body": 6, "register": 7}
STRLIT = lc.STRLIT
class Refuse(Exception):
pass
# ----------------------------------------------------------------------------------------------------------------------
# text helpers (positions on the RAW text; comments masked to equal-length spaces so a ';' in a comment never ends a statement)
# ----------------------------------------------------------------------------------------------------------------------
def same_len_mask(raw):
return sc._TOK.sub(lambda m: re.sub(r"[^\n]", " ", m.group(0)) if m.group(0)[:2] in ("/*", "//") else m.group(0), raw)
def line_starts(text):
ls = [0]
for ln in text.split("\n"):
ls.append(ls[-1] + len(ln) + 1)
return ls
def stmt_end(m, pos):
"""index just past the ';' that ends the statement starting at pos (paren depth 0, string literals skipped); -1 if none."""
depth, i, n = 0, pos, len(m)
while i < n:
c = m[i]
if c == '"':
mm = STRLIT.match(m, i)
i = mm.end() if mm else i + 1
continue
if c == "(":
depth += 1
elif c == ")":
depth -= 1
elif c == ";" and depth <= 0:
return i + 1
elif c == "}" and depth == 0:
return -1
i += 1
return -1
def ws_after(m, pos):
i = pos
while i < len(m) and m[i] in " \t":
i += 1
return i
# ----------------------------------------------------------------------------------------------------------------------
# the rewrite of one site -> [(start, end, replacement)] on the raw text
# ----------------------------------------------------------------------------------------------------------------------
ASM_HEAD = re.compile(r"(?:__asm__|__asm|asm)\s*(?:__volatile__|volatile)?\s*\(")
PIN_CLAUSE = re.compile(r"\s*(?:__asm__|__asm|asm)\s*\(\s*\"[^\"]*\"\s*\)")
INSTR_TABLE = {
# mnemonic -> (arity, C template) using OUT / IN1 / IN2 / IMM; the operands come from the asm's "=r"(..) / "r"(..) lists
"addu": ("OUT = IN1;", "zero"), "move": ("OUT = IN1;", None), "addiu": ("OUT = IN1 + IMM;", None), "sll": ("OUT = IN1 << IMM;", None),
"srl": ("OUT = (u32)IN1 >> IMM;", None), "and": ("OUT = IN1 & IN2;", None), "andi": ("OUT = IN1 & IMM;", None),
"lui": ("OUT = IMM << 16;", None), "li": ("OUT = IMM;", None), "lw": ("OUT = *(s32 *)((s32)IN1 + IMM);", None),
"lh": ("OUT = *(s16 *)((s32)IN1 + IMM);", None), "la": ("OUT = (s32)&SYM;", None),
}
def parse_asm_operands(inner):
"""(template, outputs, inputs) — outputs/inputs as [(constraint, expr)]; the asm's own parser rules (lever_census)."""
s = inner.strip()
parts, i, n = [], 0, len(s)
while i < n:
if s[i] in " \t\r\n":
i += 1
continue
mm = STRLIT.match(s, i)
if not mm:
break
parts.append(mm.group(0)[1:-1])
i = mm.end()
rest = s[i:].strip()
secs = lc._split_sections(rest[1:]) if rest.startswith(":") else []
def ops(sec):
out = []
for mm in re.finditer(r'"([^"]*)"\s*\(', sec):
c = lc._paren_span(sec, mm.end() - 1)
out.append((mm.group(1), sec[mm.end():c].strip()))
return out
outs = ops(secs[0]) if len(secs) > 0 else []
ins = ops(secs[1]) if len(secs) > 1 else []
return "".join(parts), outs, ins
def instruction_to_c(inner, indent):
"""The C statement for a hand-placed single-instruction asm, or None (residue)."""
tmpl, outs, ins = parse_asm_operands(inner)
t = re.sub(r"\\[nt]", " ", tmpl).strip()
if len(outs) != 1:
return None
out_expr = outs[0][1]
allops = [e for _, e in outs] + [e for _, e in ins]
def resolve(tok):
mm = re.fullmatch(r"%(\d+)", tok)
if mm:
k = int(mm.group(1))
return allops[k] if k < len(allops) else None
return None
IMM = r"-?(?:0x[0-9A-Fa-f]+|\d+)"
# two-instruction address / constant materializations (one C expression each)
mm = re.fullmatch(r"lui\s+%0\s*,\s*%%hi\((\w+)\)\s*;?\s*addiu\s+%0\s*,\s*%0\s*,\s*%%lo\(\1\)", t)
if mm:
return f"{indent}{out_expr} = (s32)&{mm.group(1)};"
mm = re.fullmatch(r"lui\s+%0\s*,\s*(" + IMM + r")\s*;?\s*ori\s+%0\s*,\s*%0\s*,\s*(" + IMM + r")", t)
if mm:
return f"{indent}{out_expr} = ({mm.group(1)} << 16) | {mm.group(2)};"
if ";" in t or "\n" in t:
return None # other multi-instruction templates are residue
toks = [x for x in re.split(r"[\s,]+", t) if x]
if not toks:
return None
mnem = toks[0].lower()
if mnem not in INSTR_TABLE:
return None
templ, note = INSTR_TABLE[mnem]
refs = [x for x in toks[1:]]
if mnem == "addu":
if len(refs) != 3:
return None
if refs[2] in ("$zero", "$0"):
in1 = resolve(refs[1])
return None if in1 is None else f"{indent}{out_expr} = {in1};"
in1, in2 = resolve(refs[1]), resolve(refs[2])
return None if in1 is None or in2 is None else f"{indent}{out_expr} = (s32){in1} + (s32){in2};"
if mnem == "addiu" and len(refs) == 3 and refs[1] in ("$zero", "$0") and re.fullmatch(IMM, refs[2]):
return f"{indent}{out_expr} = {refs[2]};"
if mnem == "move":
if len(refs) != 2:
return None
in1 = resolve(refs[1])
return None if in1 is None else f"{indent}{out_expr} = {in1};"
if mnem == "la":
if len(refs) != 2 or not re.fullmatch(r"[A-Za-z_]\w*", refs[1]):
return None
return f"{indent}{out_expr} = (s32)&{refs[1]};"
if mnem in ("lw", "lh"):
mm = re.fullmatch(r"(-?\w+)\((%\d+)\)", refs[1]) if len(refs) == 2 else None
if not mm:
return None
base = resolve(mm.group(2))
return None if base is None else templ.replace("OUT", out_expr).replace("IN1", base).replace("IMM", mm.group(1)) and f"{indent}{templ.replace('OUT', out_expr).replace('IN1', base).replace('IMM', mm.group(1))}"
if mnem in ("addiu", "sll", "srl", "andi"):
if len(refs) != 3:
return None
in1 = resolve(refs[1])
if in1 is None or not re.fullmatch(IMM, refs[2]):
return None
return f"{indent}{templ.replace('OUT', out_expr).replace('IN1', '(s32)' + in1).replace('IMM', refs[2])}"
if mnem == "and":
if len(refs) != 3:
return None
in1, in2 = resolve(refs[1]), resolve(refs[2])
return None if in1 is None or in2 is None else f"{indent}{out_expr} = (s32){in1} & (s32){in2};"
if mnem in ("lui", "li"):
if len(refs) != 2 or not re.fullmatch(r"-?(?:0x[0-9A-Fa-f]+|\d+)", refs[1]):
return None
return f"{indent}{templ.replace('OUT', out_expr).replace('IMM', refs[1])}"
return None
def site_edits(raw, m, ls, site, keep_register=False):
"""[(start, end, replacement)] for one site, or raise Refuse. `m` = same_len_mask(raw)."""
pos = ls[site["line"] - 1] + site["col"] - 1
cls, kind = site["cls"], site["kind"]
indent_start = m.rfind("\n", 0, pos) + 1
indent = re.match(r"[ \t]*", raw[indent_start:pos]).group(0)
if cls == "A":
if not m.startswith("register", pos):
raise Refuse(f"token mismatch at {site['tu']}:{site['line']}: expected `register`")
e = stmt_end(m, pos)
if e < 0:
raise Refuse(f"no statement end for the pin at {site['tu']}:{site['line']}")
stmt = raw[pos:e]
if site.get("zero"):
# the zero-register variable: delete the declaration, replace every use in the body by 0
mm = re.search(r"([A-Za-z_]\w*)\s*(?:__asm__|__asm|asm)\s*\(", stmt)
if not mm:
raise Refuse("zero pin without a name")
name = mm.group(1)
edits = [(indent_start if raw[indent_start:pos].strip() == "" else pos, e + (1 if raw[e:e + 1] == "\n" and raw[indent_start:pos].strip() == "" else 0), "")]
b0, b1 = ls[site["fn_line"] - 1], ls[site["fn_end"]] - 1
for um in re.finditer(r"\b%s\b" % re.escape(name), m[b0:b1]):
s = b0 + um.start()
if s < pos or s >= e:
edits.append((s, s + len(name), "0"))
return edits
new = stmt if keep_register else re.sub(r"\bregister\b[ \t]*", "", stmt, count=1)
new = PIN_CLAUSE.sub("", new)
return [(pos, e, new)]
if cls == "B" and site.get("via"):
name = site["via"]
if not m.startswith(name, pos):
raise Refuse(f"token mismatch at {site['tu']}:{site['line']}: expected `{name}(`")
if kind not in ("barrier", "launder", "keepalive"):
raise Refuse(f"macro-carried {kind} is residue")
e = stmt_end(m, pos)
if e < 0:
raise Refuse("no statement end for the macro use")
whole_line = raw[indent_start:pos].strip() == "" and raw[e:ws_after(m, e)].strip() == "" and raw[ws_after(m, e):ws_after(m, e) + 1] == "\n"
return [(indent_start, ws_after(m, e) + 1, "")] if whole_line else [(pos, e, "")]
if cls == "B":
if not ASM_HEAD.match(m, pos):
raise Refuse(f"token mismatch at {site['tu']}:{site['line']}: expected an asm statement")
o = m.find("(", pos)
c = lc._paren_span(m, o)
e = stmt_end(m, pos)
if c < 0 or e < 0:
raise Refuse("unterminated asm statement")
if kind in ("barrier", "launder", "keepalive"):
new = ""
elif kind == "instruction":
new = instruction_to_c(m[o + 1:c] if '"' in m[o + 1:c] else raw[o + 1:c], "")
if new is None:
raise Refuse(f"instruction `{site['detail']}` has no C spelling in the table")
new = new.strip()
else:
raise Refuse(f"asm kind {kind} is not removable")
whole_line = raw[indent_start:pos].strip() == "" and raw[e:ws_after(m, e)].strip() == "" and raw[ws_after(m, e):ws_after(m, e) + 1] == "\n"
if new == "" and whole_line:
return [(indent_start, ws_after(m, e) + 1, "")]
return [(pos, e, new)]
if cls == "C":
if not m.startswith("volatile", pos):
raise Refuse(f"token mismatch at {site['tu']}:{site['line']}: expected `volatile`")
return [(pos, ws_after(m, pos + len("volatile")), "")]
if cls == "D":
if not m.startswith("register", pos):
raise Refuse(f"token mismatch at {site['tu']}:{site['line']}: expected `register`")
return [(pos, ws_after(m, pos + len("register")), "")]
raise Refuse(f"class {cls}/{kind} is not removable")
def apply_edits(raw, edits):
"""Splice non-overlapping edits (bottom-up). Overlap -> Refuse."""
es = sorted(edits, key=lambda x: (x[0], x[1]))
for a, b in zip(es, es[1:]):
if b[0] < a[1]:
raise Refuse(f"overlapping edits at {a[0]}..{a[1]} / {b[0]}..{b[1]}")
out = raw
for s, e, r in sorted(es, key=lambda x: -x[0]):
out = out[:s] + r + out[e:]
return out
# ----------------------------------------------------------------------------------------------------------------------
# the population
# ----------------------------------------------------------------------------------------------------------------------
def load_sites():
return [json.loads(l) for l in SITES.read_text().splitlines() if l.strip()]
def bodies_from_sites(sites):
bodies = {}
for s in sites:
if not s.get("fn"):
continue
b = bodies.setdefault((s["tu"], s["fn"]), dict(tu=s["tu"], fn=s["fn"], fn_line=s["fn_line"], fn_end=s["fn_end"], nhash=s.get("nhash"),
aliases=s["aliases"], header=s.get("header", False), sites=[], frozen=[]))
if (s["cls"], s["kind"]) in REMOVABLE:
b["sites"].append(s)
else:
b["frozen"].append(s)
return bodies
def kind_of(tu):
return "shared" if tu.startswith("src/shared/") else "ov" if tu.startswith("src/ov_") else "md" if tu.startswith("src/md_") \
else "resident" if tu.startswith("src/resident/") else "main"
def includers():
"""header rel -> [TU rel] from the #include lines of every TU (the recipe to compile a header's body is an includer's)."""
inc = collections.defaultdict(list)
for p in sorted((REPO / "src").rglob("*.c")):
rel = p.relative_to(REPO).as_posix()
text = p.read_text(errors="surrogateescape")
for mm in sc.INCLUDE_LINE.finditer(sc.mask_text(text)):
inc[os.path.normpath(os.path.join(os.path.dirname(rel), mm.group(1)))].append(rel)
return inc
def features(b):
ks = collections.Counter(s["kind"] for s in b["sites"])
n = len(b["sites"])
mix = "pins-only" if set(ks) <= {"pin"} else "asm-only" if not (set(ks) & {"pin", "cast", "decl-body", "register"}) else "mixed"
return dict(kind=kind_of(b["tu"]), n=n, bucket=("1" if n == 1 else "2-4" if n <= 4 else "5-9" if n <= 9 else "10+"), mix=mix,
zero=any(s.get("zero") for s in b["sites"]), init=any(s.get("init") for s in b["sites"] if s["kind"] == "pin"),
instr=any(s["kind"] == "instruction" for s in b["sites"]), cast=any(s["kind"] == "cast" for s in b["sites"]),
register=any(s["kind"] == "register" for s in b["sites"]), volatile=any(s["cls"] == "C" for s in b["sites"]),
launder=any(s["kind"] == "launder" for s in b["sites"]), barrier=any(s["kind"] == "barrier" for s in b["sites"]),
keepalive=any(s["kind"] == "keepalive" for s in b["sites"]))
def stratified_sample(bodies, n, seed):
rnd = random.Random(seed)
# one exemplar per normalized class
by_class = {}
for key, b in bodies.items():
if not b["sites"]:
continue
h = b["nhash"] or key
cur = by_class.get(h)
pref = {"main": 0, "resident": 1, "md": 2, "shared": 3, "ov": 4}[kind_of(b["tu"])]
if cur is None or pref < cur[0]:
by_class[h] = (pref, key)
pool = [bodies[k] for _, k in by_class.values()]
rnd.shuffle(pool)
# quotas scale with --sample (the shape of the population: main/resident/md are few but distinct, overlays are many)
base = {"main": 20, "resident": 16, "md": 15, "shared": 20, "ov": 79}
scale = n / sum(base.values())
quota = {k: max(3, int(round(v * scale))) for k, v in base.items()}
picked, seen = [], set()
for kind, q in quota.items():
cands = [b for b in pool if kind_of(b["tu"]) == kind]
# round-robin over (bucket, mix)
groups = collections.defaultdict(list)
for b in cands:
f = features(b)
groups[(f["bucket"], f["mix"])].append(b)
keys = sorted(groups)
i = 0
while len([b for b in picked if kind_of(b["tu"]) == kind]) < q and any(groups[k] for k in keys):
k = keys[i % len(keys)]
i += 1
if groups[k]:
b = groups[k].pop()
if (b["tu"], b["fn"]) not in seen:
picked.append(b)
seen.add((b["tu"], b["fn"]))
# the special features, topped up from the pool
for flag, want in (("zero", 10), ("init", 10), ("instr", 12), ("cast", 8), ("register", 5), ("launder", 8), ("keepalive", 6)):
have = sum(1 for b in picked if features(b)[flag])
for b in pool:
if have >= want:
break
if (b["tu"], b["fn"]) not in seen and features(b)[flag]:
picked.append(b)
seen.add((b["tu"], b["fn"]))
have += 1
return picked
# ----------------------------------------------------------------------------------------------------------------------
# the ladder on one body
# ----------------------------------------------------------------------------------------------------------------------
def run_body(b, recipe, write_path, raw, m, ls, log):
"""Returns the per-body result dict; the caller restores the file."""
sites = sorted(b["sites"], key=lambda s: (ORDER.get(s["kind"], 9), s["line"], s["col"]))
res = dict(tu=b["tu"], fn=b["fn"], nhash=b["nhash"], kind=kind_of(b["tu"]), aliases=b["aliases"], header=b["header"],
obj=recipe["obj"], n_sites=len(sites), sites=[dict(kind=s["kind"], cls=s["cls"], detail=s["detail"], line=s["line"]) for s in sites],
refused=[], compiles=0, seconds=0.0, features=features(b))
per_site, edits_all, edits_keep = {}, [], []
usable = []
for s in sites:
try:
ed = site_edits(raw, m, ls, s)
edk = site_edits(raw, m, ls, s, keep_register=True) if s["kind"] == "pin" else ed
per_site[id(s)] = (ed, edk)
usable.append(s)
edits_all += ed
edits_keep += edk
except Refuse as ex:
res["refused"].append(dict(kind=s["kind"], line=s["line"], why=str(ex)))
res["n_usable"] = len(usable)
if not usable:
res["verdict"] = "NOTHING-USABLE"
return res
def judge(edits, tag):
try:
cand = apply_edits(raw, edits)
except Refuse as ex:
return "REFUSED", str(ex)
if cand == raw:
return "REFUSED", "no-op edit (the candidate equals the original — an identical object would prove nothing, R37)"
v, dt, err = oracle.judge(recipe, cand, tag=tag, write_path=write_path)
res["compiles"] += 1
res["seconds"] += dt
return v, err
# rung A (register dropped) and A' (register kept)
vA, errA = judge(edits_all, "A")
res["rung_A"] = vA
res["rung_A_err"] = errA[:200] if vA == "COMPILE-ERROR" else ""
if any(s["kind"] == "pin" for s in usable):
vK, _ = judge(edits_keep, "Ak")
res["rung_A_keep_register"] = vK
else:
res["rung_A_keep_register"] = vA
if vA == "IDENTICAL":
res["removed"] = [dict(kind=s["kind"], detail=s["detail"], line=s["line"]) for s in usable]
res["needed"] = []
res["verdict"] = "LEVER-FREE"
return res
# rung B greedy
accepted, needed, removed = [], [], []
for s in usable:
ed, _ = per_site[id(s)]
v, err = judge(accepted + ed, "B")
if v == "IDENTICAL":
accepted += ed
removed.append(dict(kind=s["kind"], detail=s["detail"], line=s["line"]))
else:
needed.append(dict(kind=s["kind"], detail=s["detail"], line=s["line"], verdict=v, err=(err[:120] if v == "COMPILE-ERROR" else "")))
res["removed"], res["needed"] = removed, needed
res["verdict"] = "LEVER-FREE" if not needed else "RESIDUE"
log(f" {b['tu']}:{b['fn']} A={vA} B: removed {len(removed)} needed {len(needed)} compiles {res['compiles']}")
return res
def probe(sample_n, seed, jobs):
PROBE.mkdir(parents=True, exist_ok=True)
ok, why = oracle.calibration_current()
if not ok:
sys.exit(f"delever: the oracle's calibration is not current ({why}) — run tools/delever_oracle.py --calibrate first (R56)")
recipes = oracle.load_recipes()["recipes"]
sites = load_sites()
bodies = bodies_from_sites(sites)
inc = includers()
sample = stratified_sample(bodies, sample_n, seed)
log_f = open(PROBE / "probe_log.txt", "a")
def log(msg):
line = f"{time.strftime('%H:%M:%S')} {msg}"
print(line, flush=True)
log_f.write(line + "\n")
log_f.flush()
log(f"probe: {len(sample)} bodies sampled from {len(bodies)} lever bodies ({len({b['nhash'] for b in bodies.values() if b['sites']})} distinct classes); seed {seed}")
# group by the FILE that gets written (a TU, or a header) — one worker per file
by_file = collections.defaultdict(list)
for b in sample:
by_file[b["tu"]].append(b)
def work(tu):
out = []
if tu.endswith(".h"):
tus = [t for t in inc.get(tu, []) if ("build/" + t[:-2] + ".o") in recipes]
if not tus:
return [dict(tu=tu, fn=b["fn"], verdict="NO-INCLUDER-RECIPE") for b in by_file[tu]]
recipe = recipes["build/" + tus[0][:-2] + ".o"]
write_path = tu
else:
obj = "build/" + tu[:-2] + ".o"
if obj not in recipes:
return [dict(tu=tu, fn=b["fn"], verdict="NO-RECIPE") for b in by_file[tu]]
recipe = recipes[obj]
write_path = None
path = REPO / tu
raw = path.read_text(errors="surrogateescape")
m = same_len_mask(raw)
ls = line_starts(raw)
try:
for b in by_file[tu]:
out.append(run_body(b, recipe, write_path, raw, m, ls, log))
finally:
path.write_text(raw, errors="surrogateescape")
return out
results = []
t0 = time.time()
# phase 1: translation units, one worker per TU (no two candidates of one file at once, and no header is being edited);
# phase 2: shared headers, SERIAL — a header's edit reaches every includer, so nothing else may compile meanwhile
# (the dry run's three "compile errors" were two workers' candidates crossing inside one includer)
tu_files = sorted(f for f in by_file if not f.endswith(".h"))
hdr_files = sorted(f for f in by_file if f.endswith(".h"))
with ThreadPoolExecutor(max_workers=jobs) as ex:
for r in ex.map(work, tu_files):
results.extend(r)
for f in hdr_files:
results.extend(work(f))
wall = time.time() - t0
with open(PROBE / "probe_results.jsonl", "w") as f:
for r in results:
f.write(json.dumps(r) + "\n")
summary = summarize(results, bodies, wall, seed)
(PROBE / "probe_summary.json").write_text(json.dumps(summary, indent=1) + "\n")
(PROBE / "probe_table.md").write_text(render(summary) + "\n")
print(render(summary))
return summary
def summarize(results, bodies, wall, seed):
judged = [r for r in results if r.get("verdict") in ("LEVER-FREE", "RESIDUE")]
def pct(a, b):
return round(100.0 * a / b, 1) if b else None
by_kind = {}
for k in ("main", "resident", "md", "shared", "ov"):
rs = [r for r in judged if r["kind"] == k]
if not rs:
continue
by_kind[k] = dict(bodies=len(rs), A_identical=sum(1 for r in rs if r["rung_A"] == "IDENTICAL"),
A_keep_register_identical=sum(1 for r in rs if r.get("rung_A_keep_register") == "IDENTICAL"),
lever_free=sum(1 for r in rs if r["verdict"] == "LEVER-FREE"),
sites=sum(r["n_usable"] for r in rs), removed=sum(len(r["removed"]) for r in rs), needed=sum(len(r["needed"]) for r in rs),
compiles=sum(r["compiles"] for r in rs), mean_s_per_compile=round(sum(r["seconds"] for r in rs) / max(sum(r["compiles"] for r in rs), 1), 3))
needed_kinds = collections.Counter(n["kind"] for r in judged for n in r["needed"])
needed_detail = collections.Counter(f"{n['kind']}:{n['detail']}" for r in judged for n in r["needed"])
removed_kinds = collections.Counter(x["kind"] for r in judged for x in r["removed"])
refused = collections.Counter(x["why"].split(":")[0][:60] for r in results for x in r.get("refused", []))
sites_total = sum(r["n_usable"] for r in judged)
sites_needed = sum(len(r["needed"]) for r in judged)
flags = {}
for flag in ("zero", "init", "instr", "cast", "register", "launder", "barrier", "keepalive", "volatile"):
rs = [r for r in judged if r["features"].get(flag)]
flags[flag] = dict(bodies=len(rs), lever_free=sum(1 for r in rs if r["verdict"] == "LEVER-FREE"))
pop_bodies = sum(1 for b in bodies.values() if b["sites"])
pop_sites = sum(len(b["sites"]) for b in bodies.values())
mean_compiles = sum(r["compiles"] for r in judged) / max(len(judged), 1)
mean_s = sum(r["seconds"] for r in judged) / max(sum(r["compiles"] for r in judged), 1)
return dict(generated=time.strftime("%Y-%m-%d %H:%M"), seed=seed, sampled=len(results), judged=len(judged),
not_judged=collections.Counter(r.get("verdict") for r in results if r not in judged),
rung_A_identical=sum(1 for r in judged if r["rung_A"] == "IDENTICAL"),
rung_A_keep_register_identical=sum(1 for r in judged if r.get("rung_A_keep_register") == "IDENTICAL"),
lever_free_after_B=sum(1 for r in judged if r["verdict"] == "LEVER-FREE"),
sites=dict(total=sites_total, removed=sites_total - sites_needed, needed=sites_needed, pct_removed=pct(sites_total - sites_needed, sites_total)),
needed_by_kind=dict(needed_kinds), needed_by_detail=dict(needed_detail.most_common(30)), removed_by_kind=dict(removed_kinds),
refused=dict(refused), by_kind=by_kind, flags=flags,
compiles=sum(r["compiles"] for r in judged), mean_compiles_per_body=round(mean_compiles, 2), mean_s_per_compile=round(mean_s, 3),
wall_s=round(wall, 1),
population=dict(bodies=pop_bodies, sites=pop_sites),
pricing=dict(T4_compiles_est=int(pop_bodies * mean_compiles), T4_cpu_hours_est=round(pop_bodies * mean_compiles * mean_s / 3600, 2),
T4_wall_hours_at_16_est=round(pop_bodies * mean_compiles * mean_s / 3600 / 12, 2),
residue_bodies_est=int(pop_bodies * (1 - (sum(1 for r in judged if r["verdict"] == "LEVER-FREE") / max(len(judged), 1)))),
residue_sites_est=int(pop_sites * (sites_needed / max(sites_total, 1)))))
def render(s):
L = [f"# probe — {s['sampled']} bodies sampled, {s['judged']} judged (seed {s['seed']}, {s['generated']}, wall {s['wall_s']} s)", "",
f"- rung A (strip-all) identical: **{s['rung_A_identical']} / {s['judged']}** (register dropped) · {s['rung_A_keep_register_identical']} with `register` kept",
f"- lever-free after rung B: **{s['lever_free_after_B']} / {s['judged']}**",
f"- sites: {s['sites']['total']} usable · removed {s['sites']['removed']} ({s['sites']['pct_removed']} %) · needed {s['sites']['needed']}",
f"- needed by kind: {s['needed_by_kind']}", f"- needed by detail (top): {s['needed_by_detail']}",
f"- removed by kind: {s['removed_by_kind']}", f"- refused (rewrite not applicable): {s['refused']}",
f"- compiles {s['compiles']} · mean {s['mean_compiles_per_body']} per body · {s['mean_s_per_compile']} s per compile", "",
"| kind | bodies | A identical | A (register kept) | lever-free after B | sites | removed | needed | compiles | s/compile |", "|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|"]
for k, v in s["by_kind"].items():
L.append(f"| {k} | {v['bodies']} | {v['A_identical']} | {v['A_keep_register_identical']} | {v['lever_free']} | {v['sites']} | {v['removed']} | {v['needed']} | {v['compiles']} | {v['mean_s_per_compile']} |")
L += ["", "| feature | bodies | lever-free |", "|---|---:|---:|"]
for k, v in s["flags"].items():
L.append(f"| {k} | {v['bodies']} | {v['lever_free']} |")
p = s["pricing"]
L += ["", f"**Pricing (R41; population {s['population']['bodies']:,} lever bodies / {s['population']['sites']:,} removable sites):** "
f"T4 ≈ {p['T4_compiles_est']:,} compiles ≈ {p['T4_cpu_hours_est']} CPU-h ≈ {p['T4_wall_hours_at_16_est']} h wall at 12 workers; "
f"residue after the mechanical ladder ≈ {p['residue_bodies_est']:,} bodies / {p['residue_sites_est']:,} sites (the sample's rates applied to the population)."]
return "\n".join(L)
def main():
ap = argparse.ArgumentParser(description=__doc__.split("\n")[0])
ap.add_argument("--probe", action="store_true")
ap.add_argument("--sample", type=int, default=150)
ap.add_argument("--seed", type=int, default=1)
ap.add_argument("-j", "--jobs", type=int, default=12)
a = ap.parse_args()
if a.probe:
probe(a.sample, a.seed, a.jobs)
return
ap.print_help()
if __name__ == "__main__":
main()
@@ -0,0 +1,303 @@
#!/usr/bin/env python3
"""delever_oracle.py — the fast byte oracle for a single-translation-unit source edit (Phase 36 T2, 2026-09-09).
tools/delever_oracle.py --recipes [-j N] # capture every object's exact build command via `make -n -W` -> .run/P36/delever/recipes.json
tools/delever_oracle.py --calibrate ov_SC04_011 main # compile every TU of the named binaries UNTOUCHED: 100 % object equality with build/,
# twin == primary, then the POSITIVE control (a nop injected -> DIFFERS); exit 1 otherwise
tools/delever_oracle.py --status # is the calibration current for this HEAD / Makefile?
THE QUESTION IT ANSWERS: "does this candidate text for TU X still produce the byte-identical object?" — and only that. The whole-binary
gate (`make check BINARY=…`, and the clean fleet run, R22) remains the arbiter of every batch; this oracle is the inner loop that
decides one lever site at a time in ~0.3–5 s instead of ~90 s.
HOW (the design settled at gate 1 + the plan agent's review, R57: the instrument's own path is part of the instrument):
* the recipe of every object is the Makefile's OWN command, printed by `make -n -W <src> <obj> BINARY=<alias>` (0.04–0.15 s each;
captured once per Makefile/config state, ~4,300 objects at -j16 in ~30 s). It carries the per-object -O0 overrides, the
jtbl_rodata_pads stage of main/module objects, the reorder island, the twin rule (the primary's source into the twin's object).
tools/compile_only.py's re-typed pipeline omits the pad stage and is NOT used.
* a candidate compiles IN PLACE at the real path (objects embed the source path as an STT_FILE symbol) with `-o` and `-MF`
redirected to scratch — build/ is never written, so the baseline object of the fleet run stays the reference; the TU's text is
restored from the caller's in-memory snapshot (never `git checkout`, R102). Workers own whole TUs: no two candidates of one TU at once.
* verdict = whole-object byte equality with build/<obj> (R56: build/ must come from a clean fleet run of THIS tree — --calibrate proves it).
* CALIBRATION (R39/R57): every sampled TU compiled untouched reproduces its object byte for byte; a twin's object equals its primary's;
a deliberately altered body reports DIFFERS. `.run/P36/delever/calibration.json` records HEAD, the Makefile/config mtimes and the
per-object seconds (the scheduler's cost model); an --apply of the de-lever tool refuses without a current calibration.
"""
import argparse
import hashlib
import json
import os
import pathlib
import re
import subprocess
import sys
import time
from concurrent.futures import ThreadPoolExecutor
REPO = pathlib.Path(__file__).resolve().parent.parent
sys.path.insert(0, str(REPO / "tools"))
RUN = REPO / ".run" / "P36" / "delever"
RECIPES = RUN / "recipes.json"
CALIB = RUN / "calibration.json"
SCRATCH = RUN / "obj"
RECIPE_RE = re.compile(r"^set -o pipefail; (.*) -o (build/\S+\.o)$")
# ----------------------------------------------------------------------------------------------------------------------
# enumeration: (alias, src, obj) for every object of every binary — derived from the Makefile's own rules (R33)
# ----------------------------------------------------------------------------------------------------------------------
def src_dirs():
import corpus
return dict(corpus.src_dirs())
def twin_of_map():
import share_census
return share_census.twin_of_map()
def objects(aliases=None):
"""[(alias, src_rel, obj_rel)] — the pattern rule build/src/%.o: src/%.c, and for a twin the primary's sources renamed into the
twin's own object names (Makefile twin block)."""
import compile_only
dirs = src_dirs()
twins = twin_of_map()
out = []
for a in sorted(dirs):
if aliases and a not in aliases:
continue
prim = twins.get(a)
for p in compile_only.tus_of(a, dirs):
src = p.relative_to(REPO).as_posix()
if prim:
# src/<prim>/<prim><suffix>.c -> build/src/<twin>/<twin><suffix>.o
stem = p.name[:-2]
if not stem.startswith(prim):
continue
obj = f"build/src/{a}/{a}{stem[len(prim):]}.o"
else:
obj = "build/" + src[:-2] + ".o"
out.append((a, src, obj))
return out
def config_stamp():
files = [REPO / "Makefile"] + sorted((REPO / "config").glob("*.mk"))
h = hashlib.sha1()
for f in files:
st = f.stat()
h.update(f"{f.name}|{int(st.st_mtime)}|{st.st_size}\n".encode())
return h.hexdigest()[:12]
def head():
return subprocess.run(["git", "rev-parse", "--short", "HEAD"], cwd=REPO, capture_output=True, text=True).stdout.strip()
# ----------------------------------------------------------------------------------------------------------------------
# recipes
# ----------------------------------------------------------------------------------------------------------------------
def capture_recipe(alias, src, obj):
r = subprocess.run(["make", "-n", "-W", src, obj, f"BINARY={alias}"], cwd=REPO, capture_output=True, text=True)
if r.returncode != 0:
return None, f"make -n rc={r.returncode}: {r.stderr.strip()[:200]}"
lines = [ln for ln in r.stdout.splitlines() if ln.startswith("set -o pipefail;")]
if len(lines) != 1:
return None, f"expected ONE pipeline line, got {len(lines)}"
m = RECIPE_RE.match(lines[0])
if not m or m.group(2) != obj:
return None, f"unparsed recipe: {lines[0][:160]}"
return dict(alias=alias, src=src, obj=obj, pipeline=m.group(1)), None
def build_recipes(jobs, aliases=None):
objs = objects(aliases)
recipes, errors = {}, []
t0 = time.time()
with ThreadPoolExecutor(max_workers=jobs) as ex:
for (a, src, obj), (rec, err) in zip(objs, ex.map(lambda t: capture_recipe(*t), objs)):
if rec:
recipes[obj] = rec
else:
errors.append(f"{a} {src} {obj}: {err}")
return dict(stamp=config_stamp(), head=head(), generated=time.strftime("%Y-%m-%d %H:%M"), n=len(recipes),
seconds=round(time.time() - t0, 1), recipes=recipes, errors=errors)
def load_recipes(refresh=False, jobs=16):
if RECIPES.exists() and not refresh:
d = json.loads(RECIPES.read_text())
if d.get("stamp") == config_stamp():
return d
d = build_recipes(jobs)
RUN.mkdir(parents=True, exist_ok=True)
RECIPES.write_text(json.dumps(d, indent=1) + "\n")
return d
# ----------------------------------------------------------------------------------------------------------------------
# compile + compare
# ----------------------------------------------------------------------------------------------------------------------
def compile_obj(recipe, text=None, tag="x", write_path=None):
"""Compile the recipe's source (with `text` written IN PLACE first — to `write_path` when the edited file is a header the
source includes, else to the source itself; the CALLER restores it) to a scratch object. Returns (bytes | None, seconds, errors)."""
src = REPO / (write_path or recipe["src"])
obj = recipe["obj"]
out = SCRATCH / (obj[len("build/"):].replace("/", "__")[:-2] + f".{tag}.o")
out.parent.mkdir(parents=True, exist_ok=True)
dep = str(out)[:-2] + ".d"
pipeline = recipe["pipeline"]
pipeline = re.sub(r"-MF \S+", f"-MF {dep}", pipeline, count=1)
cmd = f"set -o pipefail; {pipeline} -o {out}"
if text is not None:
src.write_text(text, errors="surrogateescape")
t0 = time.time()
r = subprocess.run(["bash", "-c", cmd], cwd=REPO, capture_output=True, text=True)
dt = time.time() - t0
if r.returncode != 0 or not out.exists():
# gcc 2.7.2 prints errors without the word "error" and the assembler floods stderr with `$at` warnings: keep every
# non-warning line (R103), then the tail
errs = [ln for ln in r.stderr.splitlines()
if ln.strip() and not re.search(r"\bwarning:", ln, re.I) and not ln.startswith(("In file included", " " * 16))
and ": In function" not in ln]
return None, dt, ("\n".join(errs[:8]) or r.stderr[-400:])
data = out.read_bytes()
try:
out.unlink()
pathlib.Path(dep).unlink(missing_ok=True)
except OSError:
pass
return data, dt, ""
def baseline_bytes(obj):
p = REPO / obj
return p.read_bytes() if p.exists() else None
def judge(recipe, text, tag="x", write_path=None):
"""(verdict, seconds, err): verdict in IDENTICAL | DIFFERS | COMPILE-ERROR | NO-BASELINE."""
base = baseline_bytes(recipe["obj"])
if base is None:
return "NO-BASELINE", 0.0, ""
data, dt, err = compile_obj(recipe, text, tag, write_path)
if data is None:
return "COMPILE-ERROR", dt, err
return ("IDENTICAL" if data == base else "DIFFERS"), dt, ""
# ----------------------------------------------------------------------------------------------------------------------
# calibration
# ----------------------------------------------------------------------------------------------------------------------
def calibrate(aliases, jobs, recipes):
"""Every object of the named binaries compiled UNTOUCHED -> must equal build/; twin == primary; then the positive control."""
recs = [r for r in recipes["recipes"].values() if r["alias"] in aliases]
if not recs:
sys.exit(f"delever_oracle: no recipes for {aliases} (R43)")
twins = twin_of_map()
results = []
def one(r):
v, dt, err = judge(r, None, tag="cal")
return dict(alias=r["alias"], obj=r["obj"], verdict=v, seconds=round(dt, 3), err=err[-200:])
with ThreadPoolExecutor(max_workers=jobs) as ex:
results = list(ex.map(one, recs))
ok = sum(1 for x in results if x["verdict"] == "IDENTICAL")
bad = [x for x in results if x["verdict"] != "IDENTICAL"]
# twin == primary on the objects both build
twin_checks = []
for a in aliases:
prim = twins.get(a)
if not prim:
continue
for r in recipes["recipes"].values():
if r["alias"] == a:
pobj = r["obj"].replace(f"build/src/{a}/{a}", f"build/src/{prim}/{prim}")
same = baseline_bytes(r["obj"]) == baseline_bytes(pobj)
twin_checks.append(dict(twin=r["obj"], primary=pobj, equal=same))
twin_bad = [t for t in twin_checks if not t["equal"]]
# the positive control: a nop injected into a REAL function body (the first definition the shared scanner finds in the
# first recipe's source with a body of >= 3 lines) must DIFFER — a verifier that cannot fail is no verifier (R39/R53)
import share_census as sc
ctl = recs[0]
src = REPO / ctl["src"]
orig = src.read_text(errors="surrogateescape")
pos_verdict = "SKIPPED"
defs = [r for r in sc.scan_text(orig, ctl["src"], shared_defs=None) if r["form"] == "def" and r.get("nlines", 0) >= 3 and not r.get("empty")]
if defs:
d = defs[0]
line_starts = [0]
for ln in orig.split("\n"):
line_starts.append(line_starts[-1] + len(ln) + 1)
# at the END of the body (C89: a statement before the declarations is a parse error)
o = orig.rfind("}", line_starts[d["end"] - 1], line_starts[d["end"]])
if o >= 0:
cand = orig[:o] + ' __asm__("nop");\n' + orig[o:]
try:
pos_verdict, _, err = judge(ctl, cand, tag="ctl")
pos_verdict = pos_verdict if pos_verdict != "COMPILE-ERROR" else f"COMPILE-ERROR ({err[-100:]})"
finally:
src.write_text(orig, errors="surrogateescape")
by_alias = {}
for x in results:
d = by_alias.setdefault(x["alias"], dict(objects=0, identical=0, seconds=0.0))
d["objects"] += 1
d["identical"] += x["verdict"] == "IDENTICAL"
d["seconds"] += x["seconds"]
return dict(head=head(), stamp=config_stamp(), generated=time.strftime("%Y-%m-%d %H:%M"), aliases=sorted(aliases),
objects=len(results), identical=ok, failures=bad[:20], twin_checks=len(twin_checks), twin_mismatch=twin_bad[:10],
positive_control=dict(obj=ctl["obj"], verdict=pos_verdict),
per_alias={a: dict(v, mean_s=round(v["seconds"] / max(v["objects"], 1), 3)) for a, v in by_alias.items()},
per_object_seconds={x["obj"]: x["seconds"] for x in results},
ok=(not bad and not twin_bad and pos_verdict == "DIFFERS"))
def calibration_current():
if not CALIB.exists():
return False, "no calibration"
d = json.loads(CALIB.read_text())
if d.get("head") != head():
return False, f"calibrated at {d.get('head')}, HEAD is {head()}"
if d.get("stamp") != config_stamp():
return False, "the Makefile/config changed since the calibration"
return bool(d.get("ok")), ("ok" if d.get("ok") else "the calibration FAILED")
def main():
ap = argparse.ArgumentParser(description=__doc__.split("\n")[0])
ap.add_argument("--recipes", action="store_true", help="(re)capture every object's recipe")
ap.add_argument("--calibrate", nargs="*", help="binaries to calibrate on (e.g. ov_SC04_011 ov_SC03_015 main)")
ap.add_argument("--status", action="store_true")
ap.add_argument("-j", "--jobs", type=int, default=16)
a = ap.parse_args()
RUN.mkdir(parents=True, exist_ok=True)
if a.status:
ok, why = calibration_current()
print(f"delever_oracle: calibration {'CURRENT' if ok else 'STALE/MISSING'} — {why}")
sys.exit(0 if ok else 1)
if a.recipes:
d = load_recipes(refresh=True, jobs=a.jobs)
print(f"delever_oracle: {d['n']} recipes captured in {d['seconds']} s at -j{a.jobs}; errors {len(d['errors'])}")
for e in d["errors"][:10]:
print(" ", e)
sys.exit(1 if d["errors"] else 0)
if a.calibrate is not None:
recipes = load_recipes(jobs=a.jobs)
t0 = time.time()
d = calibrate(a.calibrate, a.jobs, recipes)
d["seconds"] = round(time.time() - t0, 1)
CALIB.write_text(json.dumps(d, indent=1) + "\n")
print(f"delever_oracle --calibrate {' '.join(d['aliases'])}: {d['identical']}/{d['objects']} objects byte-identical untouched; "
f"twin checks {d['twin_checks']} ({len(d['twin_mismatch'])} mismatch); positive control {d['positive_control']['verdict']} "
f"on {d['positive_control']['obj']}; {d['seconds']} s — {'OK' if d['ok'] else 'FAIL'}")
for a_, v in d["per_alias"].items():
print(f" {a_:14s} {v['identical']}/{v['objects']} identical, mean {v['mean_s']} s per object")
for f in d["failures"][:10]:
print(" FAIL", f["alias"], f["obj"], f["verdict"], f["err"][-120:])
sys.exit(0 if d["ok"] else 1)
ap.print_help()
if __name__ == "__main__":
main()
@@ -405,7 +405,8 @@ def walk_file(raw, rel, is_header):
d = span_of_line.get(ln)
flat = re.sub(r"\s+", " ", text).strip()
# a file-scope asm block keeps its whole template: the manifest match reads its `.globl`/`.ent` name
sites.append(dict(tu=rel, fn=(d["name"] if d else None), fn_line=(d["line"] if d else None), line=ln,
sites.append(dict(tu=rel, fn=(d["name"] if d else None), fn_line=(d["line"] if d else None), fn_end=(d["end"] if d else None),
nhash=(d["nhash"] if d else None), line=ln,
col=pos - masked.rfind("\n", 0, pos), cls=cls, kind=kind, detail=detail,
text=(flat if (cls == "B" and d is None) else flat[:200]), marked=marked(ln), scope=("body" if d else "file"), **kw))
+4 -2
View File
@@ -8,8 +8,8 @@
> its platform SDK need the marked adaptation. The last table lists the tools that are project-only in code (their *shape* is a task;
> their code does not transfer). *TODO(platform): the MIPS and PlayStation SDK hard-codes are the ones another platform replaces first.*
>
> **Coverage:** 296 tool files in scope (submodules, vendored and downloaded code excluded), of which 296 live rows
> below; per phase: P1 2 · P2 26 · P3 17 · P4 9 · P5 26 · P6 51 · P7 20 · P8 86 · P9 27 · P10 16 · PROJECT-ONLY 16. Superseded tools appear only as pointers to their successor (30 pointer rows); one-offs are omitted. Table rows in all: 326 (the installer checks its copy against this figure).
> **Coverage:** 298 tool files in scope (submodules, vendored and downloaded code excluded), of which 298 live rows
> below; per phase: P1 2 · P2 26 · P3 17 · P4 9 · P5 26 · P6 51 · P7 20 · P8 86 · P9 27 · P10 18 · PROJECT-ONLY 16. Superseded tools appear only as pointers to their successor (30 pointer rows); one-offs are omitted. Table rows in all: 328 (the installer checks its copy against this figure).
## P1 — extraction + manifest
@@ -333,12 +333,14 @@
| `lint_symbol_refs.py` | flag address-named references whose address now has a curated name | Flags address-named references in committed sources whose address now has a curated name | repo symbol/src paths |
| `uniquify_type.py` | give each conflicting camp of a same-named type its own name | Gives each conflicting camp of a same-named type its own name so every camp becomes liftable | repo src layout |
| `verbatim_check.py` | guard that every inline-assembly body still reproduces its target bytes | Regression guard that every inline-assembly body still reproduces its target bytes | repo src layout |
| `delever_oracle.py` | judge one translation-unit edit by the bytes of its object in under a second | The Phase-36 fast byte oracle: every object's exact build command captured once via make -n -W (the Makefile's own recipe, pad stage and -O0 overrides included), a candidate compiled in place with -o/-MF redirected to scratch and compared with the fleet run's object; --calibrate proves 100 % equality untouched, twin == primary and a positive control before any verdict is trusted | the Makefile's object rules, the twin rule |
| `share_census.py` | measure duplicate function bodies across the fleet and assert one source per unique function | The census of byte-identical function classes across every binary with their source forms and verdicts, plus the S1 invariant check with its exception ledger and a fixture self-test | repo paths, the registry and signature schemas |
| `ghidra_apply_symbols.sh` | mirror the curated symbol file into the analysis database with a real save | Mirrors the curated symbol file into the analysis program headlessly, with a real save | repo symbol path, project name |
| `frozen.py` | refuse, from one place, the command line of a tool the project has frozen | The one refusal a FROZEN tool prints from its main(): the tool, the successor and why; imports never exit (libraries stay usable) | nothing |
| `verbatim_target_s.py` | regenerate a splitter-format target disassembly for a function no longer stubbed | Regenerates a splitter-format target disassembly for a function that is no longer a stub; --gas emits the assemblable gas-syntax form ($-registers, .L labels, noreorder) that a web diff service accepts as a pasted target | repo build/asm layout |
| `share_body_cycle.sh` | run the share-body batch cycle unattended: batch, verify by exit code, log, commit, periodic clean fleet check | The Phase-35 T5 driver for bucket new: per batch share_body --apply --bucket new --batches 1 --label new<k>, the gated N/N line read, the phase log entry appended, the bank committed the moment it is green, the clean fleet run every N batches — stops on the first red | repo paths, the phase-log format, the fleet count from config/check.*.sha |
| `share_body.py` | share one byte-identical function class across its binaries through an include-at-site header, gated per binary | The Phase-35 successor of dedup_propagate + dedup_extend for the include-at-site form: exemplar by majority text, the header written once, every private copy replaced by the include at its position, per-binary byte gate with object comparison, bisect on red, the registry appended or extended by text, the exception ledger on refusal; --plan / --apply --bucket extend|new | repo paths, the registry and signature schemas |
| `delever.py` | take register pins, asm statements, volatile and register levers out of matched C, byte-gated per site | The Phase-36 de-lever engine: positional rewrites per lever class (pin -> plain declaration, barrier/launder/keep-alive deleted, a hand-placed instruction -> its C, the zero-register variable -> 0, volatile/register dropped), rung A strip-all and rung B greedy per body through delever_oracle, headers serialized after the TU phase; --probe runs a stratified sample of distinct bodies and prices the campaign with denominators | the census site records, the oracle |
| `verbatim_to_stub.py` | turn an inline-assembly body back into a stub the toolchain can reach | Turns an inline-assembly body back into an include-assembly stub | repo src/asm layout |
## PROJECT-ONLY — project-only in code (the shape is a task; the code does not transfer)
+2
View File
@@ -783,6 +783,8 @@ Every script under `tools/` (plus the two report make-targets), grouped by purpo
| | `tools/tool_census.py [--check | --manifest | --corpus | --all | --consumers FILE]` | **(P33.5 task 13.5)** The tools audit as a derived instrument: two independent enumerations of every tool file under `tools/` (`find` vs `git ls-files`, submodules/vendored/downloaded excluded — they must agree, R34); per tool the docstring line, its SETUP row, its CONSUMERS (Makefile/`.mk` targets, CI, the wave playbook, other tools by import or by name) and hence its class (LIVE · REFERENCED · ORPHAN); the AUTHORED facts live in `config/tool_dictionary.tsv` (phase · portability · the NEED the tool answers · what · what it hard-codes · the retirement verdict with its successor or product) with coverage asserted BOTH ways (R32 — a new tool without a row fails `--check`). Generates `docs/tool-index.md` (the need-keyed dictionary; KEEP-GEN), the kit's `tools/MANIFEST.md` (`--manifest`) and the three verbatim corpora `decomp-architect/corpus/tools/<phase>/` + `corpus/cookbook/` + `corpus/record/` (the how-to, the decision log, the accelerators, the retrospective, the story, the playbook, the effort map, the gen3 docs, the digest, every PhaseEnd — task 14.5) (`--corpus`; superseded tools as pointer files; sha1-equal to their sources). `--check` in `tools-health`; `make kit-corpus` = `--all`. `--consumers FILE` is the referrer census before any `git mv` of a tool. |
| | `tools/share_census.py` | **(P35 T1)** The fleet-wide census of byte-identical function classes and the S1 "one source per unique function" checker (`--check`, `--selftest`, `--scope`, `--strict-macros`, `--strict-text`); its ledger is `config/dedup_exceptions.tsv`; details in the P35 T1 section below. |
| | `tools/lever_census.py` | **(P36 T1)** The census of every compiler-forcing construct ("lever") in the fleet's C — register pins, asm statements by kind (barrier / launder / keepalive / instruction / GTE / verbatim-body), volatile levers, bare `register`, plus the deferred asm-label aliases, builtins and attributes — derived from `share_census`'s scanner with a per-token coverage assertion against the raw text, four known-true controls, the `// !FAKE:` marker split, `--check` (0 unmarked pins/asm) and `--check --strict` (0 pins, 0 asm outside the GTE header) gates, `--sites` (every site to `.run/P36/census/lever_sites.jsonl`, the delever ledger's input), `--selftest`. Evidence: `.run/P36/census/lever_census.{json,txt}`; `progress.py` publishes the `levers` block and a README sentence from it. |
| | `tools/delever_oracle.py` | **(P36 T2)** The fast byte oracle for a single-translation-unit edit: `--recipes` captures every object's exact build command through `make -n -W <src> <obj> BINARY=<alias>` (the Makefile's own pipeline — the jump-table pad stage, the per-object `-O0` overrides, the twin rule — into `.run/P36/delever/recipes.json`, regenerable in ~15 s); a candidate is compiled IN PLACE with `-o`/`-MF` redirected to scratch and its bytes compared with `build/`'s object from the fleet run (build/ is never written); `--calibrate <aliases>` proves 100 % equality untouched + twin == primary + a positive control (a nop injected at the end of a real body → DIFFERS) and writes `calibration.json` (HEAD, config stamp, per-object seconds); `--status`. Measured at T2: main 0.08–0.77 s per object, overlays ~0.14 s. |
| | `tools/delever.py` | **(P36 T2/T3)** The de-lever engine. Rewrites per lever class on the raw text at the census's positions (a token mismatch REFUSES, never guesses): pin → plain declaration (type/qualifiers/initializer kept; the `$0` zero-register variable's uses → 0), barrier / launder / keep-alive → deleted (via-macro `SHB(x)` too), a hand-placed instruction → its C (addu-$zero/move/la/lh/addiu/sll/srl/and/andi/lui/li/lw and the lui+addiu / lui+ori pairs), volatile / register → dropped; rung A strip-all then rung B greedy through `delever_oracle`, the file restored from the in-memory snapshot after every compile; TUs in parallel (one worker per file), shared headers SERIAL after the TU phase (a header edit reaches every includer). `--probe --sample N --seed S` runs the ladder on a stratified sample of DISTINCT bodies → `.run/P36/probe/{probe_results.jsonl,probe_summary.json,probe_table.md}` with the pricing (R41). |
| | `tools/kit_coverage.py` | **(P33.5 task 14.5)** The kit's DISTILLATION coverage: derives the rule population (every `- **R<n>` of DIGEST §3, asserted contiguous) and the hindsight population (every `## ` heading of `docs/accelerators.md` at numbered-item granularity — 58 at S92) and asserts each is cited by a `provenance:` line of the registry seed / the kernels OR dispositioned in `config/kit_coverage_map.tsv` (`G<id>` / `DK-<n>` / `FOLDED:G<id>` / `ENV` / `PA` / `SEED:<file>` / `KIT:<path>` / `RECORD` / `COOKBOOK` / `NOT-PORTABLE`; unknown ids refused, R43); counts with denominators (R41); rc 1 on any gap. In `tools-health` after `tool_census --check`. Its first run found 26 uncited rules and 21 uncited entries → three new kernels (DK-66–DK-68) and 41 authored dispositions. |
| | `decomp-architect/` (the day-one decomp kit) | **(P33.5 tasks 9–14)** The package a new matching-decomp project installs as Phase 0.5 on ProjectArchitect 2.0: `README.md` (the three steps), `intake.decomp.md` (ProjectArchitect's twelve items pre-answered + the phase ladder + the six readability inversions), `SETUP.md` (the installer, Step 0 contract … Step 10 verify + hard stop; `answers: <path>` for unattended runs), `decomp-architect.md` (the methodology), `templates/` (the firewall pack — `gitignore.decomp`, `firewall.txt`, `audit_public.template.py`, the planted fixture, `no-rom.template.yml` —, the READMEs, `pa-overlays.md`, `registry-E.decomp.md` G1–G67, the skeletons, `PLACEHOLDERS.md`, `layout-contract.md`), `corpus/decomp-kernels.md` (DK-1 … DK-80), the three dictionaries `corpus/tools/<phase>/` + `corpus/cookbook/` + `corpus/record/` (generated by `make kit-corpus`), `memory-seed/` (18), `tools/MANIFEST.md` (generated). **How it is checked:** `tools/kit_lint.py` (de-specialisation, placeholders, syntax, the gitignore-template diff) + `tools/tool_census.py --check` (the corpora equal their sources) + `tools/gitignore_template_check.py`, all in `tools-health`; the dry-run harness under `.run/P33.5/kit-dryrun/` (`answers.md`, `expected-manifest.txt`, `judge.py`, the install logs and verdicts of runs 1–5 — a kit change is re-verified by a resume on the last throwaway `repo/`, a fresh full run only when SETUP's steps change; the judge compares the real tree's dirty PATH SETS before/after). Wiki page: `docs/wiki/Start-a-new-decomp-project.md`. Split into its own repository after the flip. |
| **Verification** | `tools/verify_contract.sh` | **(P33 A5/C8) THE recorded contract run**: 00 tree · 01 check-env · 02 family_hseq · 03 `make clean && extract-all && check-all` · 04 sdk-dual (or a recorded SKIP) · 05 tools-health (zero `[warn]`) · 06 audit-frontier · 07 audit-disc · 08 report; one log per step ending `EXIT=<rc>`, abort on the first red (R53), every step asserted by its contract line (R49), `SUMMARY.md` generated → `.run/P33/verify/` (tracked evidence, quoted by `docs/verification.md` §2); step 00 ignores its own output dir. ≈14 min on 32 CPUs. |
+7 -5
View File
@@ -5,7 +5,7 @@ freshness). The derived columns come from the tree on every run; the authored on
dictionary, whose coverage is asserted both ways. Read it by NEED: find the phrase that matches what you are trying to do, then the tool,
then what proved it. The same data generates the day-one kit's manifest and its verbatim tool corpus.*
**Coverage:** 296 tool files in scope (submodules, vendored and downloaded code excluded; `find` and `git ls-files` agree) + 38 retired under `tools/sunset/`. Classes: LIVE 237 (a runtime consumer), REFERENCED 29 (a SETUP row only), ORPHAN 30 (neither) — of 296. Portability: PORTABLE 23, ADAPT 256, PROJECT-ONLY 17.
**Coverage:** 298 tool files in scope (submodules, vendored and downloaded code excluded; `find` and `git ls-files` agree) + 38 retired under `tools/sunset/`. Classes: LIVE 238 (a runtime consumer), REFERENCED 30 (a SETUP row only), ORPHAN 30 (neither) — of 298. Portability: PORTABLE 23, ADAPT 258, PROJECT-ONLY 17.
## P1 — extraction + manifest
@@ -85,7 +85,7 @@ then what proved it. The same data generates the day-one kit's manifest and its
| When you need to… | Tool | What it does | Proven by | Adapt | Class |
|---|---|---|---|---|---|
| answer from one derived model whether each function is open, matched or shared | `corpus.py` | The single derived model of the source tree: open, matched or shared, per function | Makefile, api_agent.py, aprop_autodraft.py, aprop_symfix.py (+66) | repo src/config layout | LIVE |
| answer from one derived model whether each function is open, matched or shared | `corpus.py` | The single derived model of the source tree: open, matched or shared, per function | Makefile, api_agent.py, aprop_autodraft.py, aprop_symfix.py (+67) | repo src/config layout | LIVE |
| assert every consumer knows about each newly onboarded binary | `audit_binaries.py` | Gate asserting every consumer knows about each newly onboarded binary | Makefile | repo config/registry paths | LIVE |
| assert every tracked source is plain text so text searches never skip it | `audit_text_sources.py` | Every tracked C source must be plain text, or text searches silently skip it | .github/workflows/no-rom.yml, Makefile | repo src paths | LIVE |
| check that the independent what-remains views agree with the corpus oracle | `audit_frontier.py` | Checks that the independent "what remains" views agree with the corpus oracle | Makefile | repo paths | LIVE |
@@ -294,7 +294,7 @@ then what proved it. The same data generates the day-one kit's manifest and its
| assert that the distilled kit cites or dispositions every rule and every hindsight entry of the source project (no silent gap in the distillation) | `kit_coverage.py` | Derives the rule and accelerator populations from the digest and the ledger, matches them against the kit's provenance lines, refuses an uncovered entry unless config/kit_coverage_map.tsv dispositions it | Makefile | repo digest/ledger/kit paths + the provenance citation forms | LIVE |
| build a progress timeline from the repository's own committed digests | `timeline.py` | Builds a progress timeline from the repository's own committed digests, with a self-check | Makefile | repo doc paths | LIVE |
| check that every relative link in the public docs resolves | `doc_links.py` | Checks that every relative link in the public docs resolves and the link policy holds | Makefile, wiki_render.py, wiki_sync.sh | repo doc paths | LIVE |
| compile every eligible translation unit with the pinned toolchain, using no derived bytes | `compile_only.py` | Compiles every eligible translation unit with the pinned toolchain, without any game bytes | .github/workflows/no-rom.yml, lever_census.py, macro_to_header.py, share_census.py | compiler triple, repo makefile parsing | LIVE |
| compile every eligible translation unit with the pinned toolchain, using no derived bytes | `compile_only.py` | Compiles every eligible translation unit with the pinned toolchain, without any game bytes | .github/workflows/no-rom.yml, delever_oracle.py, lever_census.py, macro_to_header.py (+1) | compiler triple, repo makefile parsing | LIVE |
| compute and publish progress metrics as report, machine data and per-binary breakdowns | `progress.py` | Computes and publishes the progress metrics as report, JSON and per-binary breakdowns | Makefile, docs/wave-playbook.md, api_agent.py, atlas.py (+33) | repo src/config layout | LIVE |
| convert progress metrics into an external progress-report schema | `objdiff_report.py` | Converts the progress JSON into the external progress-report schema | .github/workflows/progress.yml | external report schema | LIVE |
| emit the public ordinal-to-hash map and the private old-to-new map | `public_rewrite/build_commit_map.py` | Emits the public ordinal-to-new-hash map and the private old-to-new map | probe_github.sh, resolve_tokens.py | repo doc/scratch paths | LIVE |
@@ -322,19 +322,21 @@ then what proved it. The same data generates the day-one kit's manifest and its
|---|---|---|---|---|---|
| add per-site function-pointer casts so a draft can call a differently typed callee | `cast_call_sites.py` | Adds per-site function-pointer casts so a draft can call a differently typed callee | canon_sig_reconcile.py, cast_self_callers.py, family_sweep.py, gate_stage.py (+4) | repo src layout | LIVE |
| apply curated names and signatures inside the analysis tool and save | `ghidra_scripts/ApplySymbols.java` | The in-tool half of that mirror: apply curated names and signatures, save on exit | ghidra_apply_symbols.sh, ghidra_export_annotations.sh, ghidra_rebuild.sh, ExportAnnotations.java (+1) | none | LIVE |
| census every compiler-forcing construct in the C and gate the levers-off phase | `lever_census.py` | The Phase-36 census of lever sites (register pins, asm statements by kind, volatile levers, bare register, asm-label aliases, builtins, attributes; the whole-body hand-asm routines and the GTE ops set apart) derived from the shared-body scanner with a per-token coverage assertion against the raw text, four known-true controls, the !FAKE marker split, --check (0 unmarked pins/asm) and --strict (0 pins, 0 asm) gates, --sites for the delever ledger, a fixture self-test | progress.py, verbatim_check.py | repo paths, the shared-body scanner | LIVE |
| census every compiler-forcing construct in the C and gate the levers-off phase | `lever_census.py` | The Phase-36 census of lever sites (register pins, asm statements by kind, volatile levers, bare register, asm-label aliases, builtins, attributes; the whole-body hand-asm routines and the GTE ops set apart) derived from the shared-body scanner with a per-token coverage assertion against the raw text, four known-true controls, the !FAKE marker split, --check (0 unmarked pins/asm) and --strict (0 pins, 0 asm) gates, --sites for the delever ledger, a fixture self-test | delever.py, progress.py, verbatim_check.py | repo paths, the shared-body scanner | LIVE |
| convert a shared-body macro header into per-function plain-C headers included at each site | `macro_to_header.py` | The Phase-35 converter: every DEFINE_ macro body becomes a plain-C header under src/shared/<space>/ included at its site, the legacy name-parameterized headers converted, the registry text-edited, the macro header deleted; --plan / --apply / --finalize / --verify | share_body.py, tool_census.py | the macro form is this project's; a kit-born project shares headers from its first bank | LIVE |
| emit a function's target assembly as an inline-assembly body | `asm_verbatim.py` | Emits the file-scope inline-assembly body form from a disassembly file | docs/wave-playbook.md, draft_prechecks.py, gate_main.py, recover_route.py | repo asm layout | LIVE |
| extract inline-defined types and typedefs from a source file into a shared header | `build_engine_types.py` | Extracts inline-defined named types and typedefs from a source file into a shared header | inject_capped_externs.py, lift_types.py, uniquify_type.py | repo shared-header path | LIVE |
| flag address-named references whose address now has a curated name | `lint_symbol_refs.py` | Flags address-named references in committed sources whose address now has a curated name | .github/workflows/no-rom.yml, Makefile | repo symbol/src paths | LIVE |
| give each conflicting camp of a same-named type its own name | `uniquify_type.py` | Gives each conflicting camp of a same-named type its own name so every camp becomes liftable | — | repo src layout | REFERENCED |
| guard that every inline-assembly body still reproduces its target bytes | `verbatim_check.py` | Regression guard that every inline-assembly body still reproduces its target bytes | .github/workflows/no-rom.yml, lever_census.py, progress.py, verbatim_target_s.py (+1) | repo src layout | LIVE |
| measure duplicate function bodies across the fleet and assert one source per unique function | `share_census.py` | The census of byte-identical function classes across every binary with their source forms and verdicts, plus the S1 invariant check with its exception ledger and a fixture self-test | Makefile, audit_binaries.py, dedup_integrate.py, gen_harvest_targets.py (+6) | repo paths, the registry and signature schemas | LIVE |
| judge one translation-unit edit by the bytes of its object in under a second | `delever_oracle.py` | The Phase-36 fast byte oracle: every object's exact build command captured once via make -n -W (the Makefile's own recipe, pad stage and -O0 overrides included), a candidate compiled in place with -o/-MF redirected to scratch and compared with the fleet run's object; --calibrate proves 100 % equality untouched, twin == primary and a positive control before any verdict is trusted | delever.py | the Makefile's object rules, the twin rule | LIVE |
| measure duplicate function bodies across the fleet and assert one source per unique function | `share_census.py` | The census of byte-identical function classes across every binary with their source forms and verdicts, plus the S1 invariant check with its exception ledger and a fixture self-test | Makefile, audit_binaries.py, dedup_integrate.py, delever.py (+8) | repo paths, the registry and signature schemas | LIVE |
| mirror the curated symbol file into the analysis database with a real save | `ghidra_apply_symbols.sh` | Mirrors the curated symbol file into the analysis program headlessly, with a real save | — | repo symbol path, project name | REFERENCED |
| refuse, from one place, the command line of a tool the project has frozen | `frozen.py` | The one refusal a FROZEN tool prints from its main(): the tool, the successor and why; imports never exit (libraries stay usable) | aprop_autodraft.py, blocker_probe.py, canon_sig_reconcile.py, conform_decls.py (+10) | nothing | LIVE |
| regenerate a splitter-format target disassembly for a function no longer stubbed | `verbatim_target_s.py` | Regenerates a splitter-format target disassembly for a function that is no longer a stub; --gas emits the assemblable gas-syntax form ($-registers, .L labels, noreorder) that a web diff service accepts as a pasted target | decompme_replica.sh | repo build/asm layout | LIVE |
| run the share-body batch cycle unattended: batch, verify by exit code, log, commit, periodic clean fleet check | `share_body_cycle.sh` | The Phase-35 T5 driver for bucket new: per batch share_body --apply --bucket new --batches 1 --label new<k>, the gated N/N line read, the phase log entry appended, the bank committed the moment it is green, the clean fleet run every N batches — stops on the first red | — | repo paths, the phase-log format, the fleet count from config/check.*.sha | ORPHAN |
| share one byte-identical function class across its binaries through an include-at-site header, gated per binary | `share_body.py` | The Phase-35 successor of dedup_propagate + dedup_extend for the include-at-site form: exemplar by majority text, the header written once, every private copy replaced by the include at its position, per-binary byte gate with object comparison, bisect on red, the registry appended or extended by text, the exception ledger on refusal; --plan / --apply --bucket extend|new | aprop_autodraft.py, audit_binaries.py, auto_driver.py, blocker_probe.py (+18) | repo paths, the registry and signature schemas | LIVE |
| take register pins, asm statements, volatile and register levers out of matched C, byte-gated per site | `delever.py` | The Phase-36 de-lever engine: positional rewrites per lever class (pin -> plain declaration, barrier/launder/keep-alive deleted, a hand-placed instruction -> its C, the zero-register variable -> 0, volatile/register dropped), rung A strip-all and rung B greedy per body through delever_oracle, headers serialized after the TU phase; --probe runs a stratified sample of distinct bodies and prices the campaign with denominators | — | the census site records, the oracle | REFERENCED |
| turn an inline-assembly body back into a stub the toolchain can reach | `verbatim_to_stub.py` | Turns an inline-assembly body back into an include-assembly stub | — | repo src/asm layout | REFERENCED |
## PROJECT-ONLY — project-only in code (the shape is a task; the code does not transfer)
+48 -4
View File
@@ -47,9 +47,11 @@
in 15,667 bodies (2,215 distinct)** (+1: the DECOMPILE-NOW body is a lever); `progress.py` counts PERMANENT rows (27) with a dated
`corrections` entry; README prose + wiki Home + Where-next + gen3-standards + gen3-handoff updated as dated snapshots; `progress.py
--json --readme --check` fresh; `doc_links --strict` green.
- ☐ **T2** (Max) — The probe: the oracle (recipes via `make -n -W`, replayed to a scratch object, `-MF` to scratch), CALIBRATED (sampled
TUs untouched → 100 % object equality with `build/`; twin == primary; an altered body → DIFFERS), then rung A + B on ~150 stratified
distinct bodies; the measured table prices T4–T7 (R41). Verify: the probe table in this log with its command.
- ☑ **T2** (S97) — The probe. `tools/delever_oracle.py`: 4,284 recipes captured in 14 s; `--calibrate ov_SC04_011 ov_SC03_015 ov_SC03_014
main`: **177/177 objects byte-identical untouched; twin checks 32 (0 mismatch); positive control DIFFERS on build/src/800.o — OK**
(main 0.079 s per object mean, 0.77 s for `800.o`; overlays ~0.14 s). `tools/delever.py --probe --sample 300 --seed 11 -j 12`: **285
distinct bodies sampled, 283 judged, 1,660 compiles, 151 s wall; rung A identical 53/283; lever-free after rung B 53/283; sites 1,309
usable → removed 561 (42.9 %), needed 748 (57.1 %)**; the table is in the log entry below and in `.run/P36/probe/probe_table.md`.
- ☐ **T3** (Max design) — `tools/delever.py` + `tools/delever_cycle.sh`: rungs A/B/C, group testing per TU with per-symbol attribution
and the 2 % per-body control, shared headers greedy-on-one/verify-on-all (h_text on all), the in-memory snapshot + `inflight.json` as
the only restore, the ledger keyed alias+tu+function+address, refusals, progress logs, negative controls, selftest with decoys.
@@ -201,7 +203,49 @@ accumulate here as the phase produces them.**
file-scope detectors); 14 game functions (8 in main) were assembly in C shells all along. The README prose is corrected in this commit;
the decision log carries it at T9; Drew's word on the SDK/DECOMPILE/UNCERTAIN split is asked at the T1b report.
## 🛑 SESSION CHECKPOINT — S97 (2026-09-09): T0 ☑ T1 ☑ T1b ☑ (T1b's commit pending); NEXT = T2 (the probe: calibrate the object oracle, strip-all + greedy on ~150 distinct bodies, price T4–T7) at Max
- **S97 — T2 the oracle and the probe (R37).** *The oracle* (`tools/delever_oracle.py`): every object's exact build command captured once
through `make -n -W <src> <obj> BINARY=<alias>` (4,284 recipes, 14 s at -j16 — the Makefile's own pipeline with the jtbl pad stage of
main/module objects, the per-object `-O0` overrides, the twin rule); a candidate compiled IN PLACE (objects carry the source path as an
STT_FILE symbol) with `-o`/`-MF` redirected to scratch, the file restored from the in-memory snapshot after every compile; verdict =
whole-object byte equality with `build/`'s object of the T0 fleet run. **Calibration:** `177/177 objects byte-identical untouched;
twin checks 32 (0 mismatch); positive control DIFFERS on build/src/800.o; 2.3 s — OK` (`.run/P36/delever/calibration.json`; two
instrument findings on the way: a control injected BEFORE the declarations is a C89 parse error, so it goes at the end of the body;
gcc 2.7.2 reports errors without the word "error" and the assembler floods stderr with `$at` warnings — the error filter keeps
every non-warning line, R103). *The engine* (`tools/delever.py`): positional rewrites per class at the census's line/col on the raw
text (a token mismatch REFUSES); rung A strip-all (measured with `register` dropped and kept), rung B greedy; TUs in parallel (one
worker per file), **shared headers SERIAL after the TU phase** — the dry run's three "compile errors" were two workers' candidates
crossing inside one includer (a header edit reaches every includer: the campaign tool must never compile a TU while any header it
includes is being edited). A no-op edit is REFUSED (an identical object would prove nothing, R37).
**The probe (`--probe --sample 300 --seed 11 -j 12`; `.run/P36/probe/`):** 285 distinct bodies (one exemplar per normalized class,
stratified by binary kind × site count × class mix, topped up for the `$0`/initializer/instruction/cast/register/launder/keep-alive
features), 283 judged (2 NOTHING-USABLE), **1,660 compiles, 151 s wall, 0.225 s per compile, 5.87 compiles per body**.
| kind | bodies | A identical | A (register kept) | lever-free after B | sites | removed | needed | compiles | s/compile |
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|
| main | 40 | 6 | 6 | 6 | 272 | 146 | 126 | 328 | 0.386 |
| resident | 16 | 4 | 4 | 4 | 31 | 9 | 22 | 53 | 0.06 |
| md | 29 | 2 | 2 | 2 | 121 | 56 | 65 | 161 | 0.139 |
| shared | 40 | 7 | 7 | 7 | 164 | 65 | 99 | 216 | 0.211 |
| ov | 158 | 34 | 34 | 34 | 721 | 285 | 436 | 902 | 0.195 |
| **all** | **283** | **53** | **53** | **53** | **1,309** | **561 (42.9 %)** | **748 (57.1 %)** | **1,660** | **0.225** |
Needed by kind: pin 360 of 798 (45 %) · launder 154 of 199 (77 %) · barrier 127 of 154 (82 %) · keep-alive 36 of 44 · instruction 21
of 24 · cast 26 of 51 · decl-body volatile 16 of 27 · bare register 8 of 12. **Needed pins by register class: v0/v1 126 · a0–a3 112 ·
s0–s7 71 · t0–t9 36 · $0 13** (removed: s 147 · t 107 · v 90 · a 87). Residue bodies 230 of 283: 75 need only pins, 129 need at least
one asm statement. Bodies with a launder 4/68 lever-free, a barrier 9/78, a `$0` pin 1/17, a cast 1/22, an initializer pin 7/32.
`register` dropped vs kept: identical results (53 = 53) — the keyword is byte-neutral at -O2 on this sample. Instrument noise: rung A
1 COMPILE-ERROR + 1 REFUSED; 6 of 748 needed sites judged by a compile error (0.8 %); 9 rewrites refused (multi-instruction
templates, `mult`/`mfhi`, a bare `addu $v0,$zero,$zero` with no C operands, `RTP_SND` macro-carried instructions).
**Pricing (R41; population 16,064 lever bodies / 55,913 removable sites, 2,633 distinct classes by body hash):** T4 (rungs A+B fleet-wide)
≈ 94,000 compiles ≈ 5.9 CPU-h ≈ **0.5 h wall at 12 workers** — cheap, and it removes ≈ 24,000 sites (43 %) and frees ≈ 3,000 bodies
(19 %). **The residue ≈ 13,000 bodies / 32,000 sites ≈ 81 % of bodies — by distinct classes ≈ 1,800 exemplars** whose copies replay:
that is the campaign grind-to-zero prices (T6 recipes + permuter, then T7 waves). Where the residue sits: launders + barriers +
keep-alives (317 of 748 needed = 42 %) — the CSE/scheduling steers; v0/v1 + a0–a3 pins (238 = 32 %) — argument/return-register copies
(the §194-E/§17a ARGCOPY class); callee-saved pins (71 = 9 %) — the allocation-order class (R73, `alloc_table.py`); the rest casts,
volatile declarations, `$0` variables, instructions. **Design consequences for T3:** per-body compiles at 0.2 s make group testing
unnecessary (simplicity wins); run T4 first and publish the fall; rung R (T6) targets the three big classes in that order.
## 🛑 SESSION CHECKPOINT — S97 (2026-09-09): T0 ☑ T1 ☑ T1b ☑ T2 ☑ (T2's commit pending); NEXT = T3 (`tools/delever.py` the campaign tool: --plan/--apply, the ledger, exemplar/replay, batches + `delever_cycle.sh`, the `!FAKE` markers, refusals, selftest) at Max for the design
### 0. How to use this block
A fresh session reads CLAUDE.md's load order, replays this block verbatim, asks Drew for `/effort max` (T2 is Max; T3's finish and T4–T6
+608
View File
@@ -0,0 +1,608 @@
#!/usr/bin/env python3
"""delever.py — take the compiler-forcing constructs ("levers") out of matched C, byte-gated per translation unit
(Phase 36; the probe engine at T2, the campaign tool at T3).
tools/delever.py --probe --sample 150 [--seed 1] [-j 12] # T2: rung A + rung B on a stratified sample of DISTINCT bodies
# -> .run/P36/probe/{probe_results.jsonl,probe_summary.json,probe_table.md}
THE LADDER PER BODY (deterministic, zero tokens):
rung A strip-all — every removable site of the body rewritten at once (pin -> plain declaration; barrier/launder/keep-alive ->
deleted; a hand-placed instruction -> its C; `$0` pin -> its variable's uses replaced by 0; volatile -> dropped; register ->
dropped), one compile, the object compared with the fleet run's (tools/delever_oracle.py). Measured twice: with the `register`
keyword dropped from the pin (the target form) and with it kept (A').
rung B greedy — if A differs: from the untouched text, the sites are re-applied one at a time (asm statements first, then pins,
volatile, register); a site whose removal keeps the object identical is REMOVED, one that changes it is NEEDED.
The verdict of a body = the set of NEEDED sites; a body with none is lever-free. Never touched: GTE ops, the manifest's verbatim
bodies, an asm-body (a whole routine — the manifest's DECOMPILE rows are another task's work), file-scope declarations.
EVERY EDIT IS POSITIONAL on the raw text (line/col from the census), the file is written in place for the compile and restored
from the in-memory snapshot right after (never `git checkout`, R102); a site whose raw token does not match the census is REFUSED
(R43), never guessed.
"""
import argparse
import collections
import json
import os
import pathlib
import random
import re
import sys
import time
from concurrent.futures import ThreadPoolExecutor
REPO = pathlib.Path(__file__).resolve().parent.parent
sys.path.insert(0, str(REPO / "tools"))
import delever_oracle as oracle # noqa: E402
import lever_census as lc # noqa: E402
import share_census as sc # noqa: E402
SITES = REPO / ".run" / "P36" / "census" / "lever_sites.jsonl"
PROBE = REPO / ".run" / "P36" / "probe"
REMOVABLE = {("A", "pin"), ("B", "barrier"), ("B", "launder"), ("B", "keepalive"), ("B", "instruction"),
("C", "cast"), ("C", "decl-body"), ("D", "register")}
ORDER = {"barrier": 0, "launder": 1, "keepalive": 2, "instruction": 3, "pin": 4, "cast": 5, "decl-body": 6, "register": 7}
STRLIT = lc.STRLIT
class Refuse(Exception):
pass
# ----------------------------------------------------------------------------------------------------------------------
# text helpers (positions on the RAW text; comments masked to equal-length spaces so a ';' in a comment never ends a statement)
# ----------------------------------------------------------------------------------------------------------------------
def same_len_mask(raw):
return sc._TOK.sub(lambda m: re.sub(r"[^\n]", " ", m.group(0)) if m.group(0)[:2] in ("/*", "//") else m.group(0), raw)
def line_starts(text):
ls = [0]
for ln in text.split("\n"):
ls.append(ls[-1] + len(ln) + 1)
return ls
def stmt_end(m, pos):
"""index just past the ';' that ends the statement starting at pos (paren depth 0, string literals skipped); -1 if none."""
depth, i, n = 0, pos, len(m)
while i < n:
c = m[i]
if c == '"':
mm = STRLIT.match(m, i)
i = mm.end() if mm else i + 1
continue
if c == "(":
depth += 1
elif c == ")":
depth -= 1
elif c == ";" and depth <= 0:
return i + 1
elif c == "}" and depth == 0:
return -1
i += 1
return -1
def ws_after(m, pos):
i = pos
while i < len(m) and m[i] in " \t":
i += 1
return i
# ----------------------------------------------------------------------------------------------------------------------
# the rewrite of one site -> [(start, end, replacement)] on the raw text
# ----------------------------------------------------------------------------------------------------------------------
ASM_HEAD = re.compile(r"(?:__asm__|__asm|asm)\s*(?:__volatile__|volatile)?\s*\(")
PIN_CLAUSE = re.compile(r"\s*(?:__asm__|__asm|asm)\s*\(\s*\"[^\"]*\"\s*\)")
INSTR_TABLE = {
# mnemonic -> (arity, C template) using OUT / IN1 / IN2 / IMM; the operands come from the asm's "=r"(..) / "r"(..) lists
"addu": ("OUT = IN1;", "zero"), "move": ("OUT = IN1;", None), "addiu": ("OUT = IN1 + IMM;", None), "sll": ("OUT = IN1 << IMM;", None),
"srl": ("OUT = (u32)IN1 >> IMM;", None), "and": ("OUT = IN1 & IN2;", None), "andi": ("OUT = IN1 & IMM;", None),
"lui": ("OUT = IMM << 16;", None), "li": ("OUT = IMM;", None), "lw": ("OUT = *(s32 *)((s32)IN1 + IMM);", None),
"lh": ("OUT = *(s16 *)((s32)IN1 + IMM);", None), "la": ("OUT = (s32)&SYM;", None),
}
def parse_asm_operands(inner):
"""(template, outputs, inputs) — outputs/inputs as [(constraint, expr)]; the asm's own parser rules (lever_census)."""
s = inner.strip()
parts, i, n = [], 0, len(s)
while i < n:
if s[i] in " \t\r\n":
i += 1
continue
mm = STRLIT.match(s, i)
if not mm:
break
parts.append(mm.group(0)[1:-1])
i = mm.end()
rest = s[i:].strip()
secs = lc._split_sections(rest[1:]) if rest.startswith(":") else []
def ops(sec):
out = []
for mm in re.finditer(r'"([^"]*)"\s*\(', sec):
c = lc._paren_span(sec, mm.end() - 1)
out.append((mm.group(1), sec[mm.end():c].strip()))
return out
outs = ops(secs[0]) if len(secs) > 0 else []
ins = ops(secs[1]) if len(secs) > 1 else []
return "".join(parts), outs, ins
def instruction_to_c(inner, indent):
"""The C statement for a hand-placed single-instruction asm, or None (residue)."""
tmpl, outs, ins = parse_asm_operands(inner)
t = re.sub(r"\\[nt]", " ", tmpl).strip()
if len(outs) != 1:
return None
out_expr = outs[0][1]
allops = [e for _, e in outs] + [e for _, e in ins]
def resolve(tok):
mm = re.fullmatch(r"%(\d+)", tok)
if mm:
k = int(mm.group(1))
return allops[k] if k < len(allops) else None
return None
IMM = r"-?(?:0x[0-9A-Fa-f]+|\d+)"
# two-instruction address / constant materializations (one C expression each)
mm = re.fullmatch(r"lui\s+%0\s*,\s*%%hi\((\w+)\)\s*;?\s*addiu\s+%0\s*,\s*%0\s*,\s*%%lo\(\1\)", t)
if mm:
return f"{indent}{out_expr} = (s32)&{mm.group(1)};"
mm = re.fullmatch(r"lui\s+%0\s*,\s*(" + IMM + r")\s*;?\s*ori\s+%0\s*,\s*%0\s*,\s*(" + IMM + r")", t)
if mm:
return f"{indent}{out_expr} = ({mm.group(1)} << 16) | {mm.group(2)};"
if ";" in t or "\n" in t:
return None # other multi-instruction templates are residue
toks = [x for x in re.split(r"[\s,]+", t) if x]
if not toks:
return None
mnem = toks[0].lower()
if mnem not in INSTR_TABLE:
return None
templ, note = INSTR_TABLE[mnem]
refs = [x for x in toks[1:]]
if mnem == "addu":
if len(refs) != 3:
return None
if refs[2] in ("$zero", "$0"):
in1 = resolve(refs[1])
return None if in1 is None else f"{indent}{out_expr} = {in1};"
in1, in2 = resolve(refs[1]), resolve(refs[2])
return None if in1 is None or in2 is None else f"{indent}{out_expr} = (s32){in1} + (s32){in2};"
if mnem == "addiu" and len(refs) == 3 and refs[1] in ("$zero", "$0") and re.fullmatch(IMM, refs[2]):
return f"{indent}{out_expr} = {refs[2]};"
if mnem == "move":
if len(refs) != 2:
return None
in1 = resolve(refs[1])
return None if in1 is None else f"{indent}{out_expr} = {in1};"
if mnem == "la":
if len(refs) != 2 or not re.fullmatch(r"[A-Za-z_]\w*", refs[1]):
return None
return f"{indent}{out_expr} = (s32)&{refs[1]};"
if mnem in ("lw", "lh"):
mm = re.fullmatch(r"(-?\w+)\((%\d+)\)", refs[1]) if len(refs) == 2 else None
if not mm:
return None
base = resolve(mm.group(2))
return None if base is None else templ.replace("OUT", out_expr).replace("IN1", base).replace("IMM", mm.group(1)) and f"{indent}{templ.replace('OUT', out_expr).replace('IN1', base).replace('IMM', mm.group(1))}"
if mnem in ("addiu", "sll", "srl", "andi"):
if len(refs) != 3:
return None
in1 = resolve(refs[1])
if in1 is None or not re.fullmatch(IMM, refs[2]):
return None
return f"{indent}{templ.replace('OUT', out_expr).replace('IN1', '(s32)' + in1).replace('IMM', refs[2])}"
if mnem == "and":
if len(refs) != 3:
return None
in1, in2 = resolve(refs[1]), resolve(refs[2])
return None if in1 is None or in2 is None else f"{indent}{out_expr} = (s32){in1} & (s32){in2};"
if mnem in ("lui", "li"):
if len(refs) != 2 or not re.fullmatch(r"-?(?:0x[0-9A-Fa-f]+|\d+)", refs[1]):
return None
return f"{indent}{templ.replace('OUT', out_expr).replace('IMM', refs[1])}"
return None
def site_edits(raw, m, ls, site, keep_register=False):
"""[(start, end, replacement)] for one site, or raise Refuse. `m` = same_len_mask(raw)."""
pos = ls[site["line"] - 1] + site["col"] - 1
cls, kind = site["cls"], site["kind"]
indent_start = m.rfind("\n", 0, pos) + 1
indent = re.match(r"[ \t]*", raw[indent_start:pos]).group(0)
if cls == "A":
if not m.startswith("register", pos):
raise Refuse(f"token mismatch at {site['tu']}:{site['line']}: expected `register`")
e = stmt_end(m, pos)
if e < 0:
raise Refuse(f"no statement end for the pin at {site['tu']}:{site['line']}")
stmt = raw[pos:e]
if site.get("zero"):
# the zero-register variable: delete the declaration, replace every use in the body by 0
mm = re.search(r"([A-Za-z_]\w*)\s*(?:__asm__|__asm|asm)\s*\(", stmt)
if not mm:
raise Refuse("zero pin without a name")
name = mm.group(1)
edits = [(indent_start if raw[indent_start:pos].strip() == "" else pos, e + (1 if raw[e:e + 1] == "\n" and raw[indent_start:pos].strip() == "" else 0), "")]
b0, b1 = ls[site["fn_line"] - 1], ls[site["fn_end"]] - 1
for um in re.finditer(r"\b%s\b" % re.escape(name), m[b0:b1]):
s = b0 + um.start()
if s < pos or s >= e:
edits.append((s, s + len(name), "0"))
return edits
new = stmt if keep_register else re.sub(r"\bregister\b[ \t]*", "", stmt, count=1)
new = PIN_CLAUSE.sub("", new)
return [(pos, e, new)]
if cls == "B" and site.get("via"):
name = site["via"]
if not m.startswith(name, pos):
raise Refuse(f"token mismatch at {site['tu']}:{site['line']}: expected `{name}(`")
if kind not in ("barrier", "launder", "keepalive"):
raise Refuse(f"macro-carried {kind} is residue")
e = stmt_end(m, pos)
if e < 0:
raise Refuse("no statement end for the macro use")
whole_line = raw[indent_start:pos].strip() == "" and raw[e:ws_after(m, e)].strip() == "" and raw[ws_after(m, e):ws_after(m, e) + 1] == "\n"
return [(indent_start, ws_after(m, e) + 1, "")] if whole_line else [(pos, e, "")]
if cls == "B":
if not ASM_HEAD.match(m, pos):
raise Refuse(f"token mismatch at {site['tu']}:{site['line']}: expected an asm statement")
o = m.find("(", pos)
c = lc._paren_span(m, o)
e = stmt_end(m, pos)
if c < 0 or e < 0:
raise Refuse("unterminated asm statement")
if kind in ("barrier", "launder", "keepalive"):
new = ""
elif kind == "instruction":
new = instruction_to_c(m[o + 1:c] if '"' in m[o + 1:c] else raw[o + 1:c], "")
if new is None:
raise Refuse(f"instruction `{site['detail']}` has no C spelling in the table")
new = new.strip()
else:
raise Refuse(f"asm kind {kind} is not removable")
whole_line = raw[indent_start:pos].strip() == "" and raw[e:ws_after(m, e)].strip() == "" and raw[ws_after(m, e):ws_after(m, e) + 1] == "\n"
if new == "" and whole_line:
return [(indent_start, ws_after(m, e) + 1, "")]
return [(pos, e, new)]
if cls == "C":
if not m.startswith("volatile", pos):
raise Refuse(f"token mismatch at {site['tu']}:{site['line']}: expected `volatile`")
return [(pos, ws_after(m, pos + len("volatile")), "")]
if cls == "D":
if not m.startswith("register", pos):
raise Refuse(f"token mismatch at {site['tu']}:{site['line']}: expected `register`")
return [(pos, ws_after(m, pos + len("register")), "")]
raise Refuse(f"class {cls}/{kind} is not removable")
def apply_edits(raw, edits):
"""Splice non-overlapping edits (bottom-up). Overlap -> Refuse."""
es = sorted(edits, key=lambda x: (x[0], x[1]))
for a, b in zip(es, es[1:]):
if b[0] < a[1]:
raise Refuse(f"overlapping edits at {a[0]}..{a[1]} / {b[0]}..{b[1]}")
out = raw
for s, e, r in sorted(es, key=lambda x: -x[0]):
out = out[:s] + r + out[e:]
return out
# ----------------------------------------------------------------------------------------------------------------------
# the population
# ----------------------------------------------------------------------------------------------------------------------
def load_sites():
return [json.loads(l) for l in SITES.read_text().splitlines() if l.strip()]
def bodies_from_sites(sites):
bodies = {}
for s in sites:
if not s.get("fn"):
continue
b = bodies.setdefault((s["tu"], s["fn"]), dict(tu=s["tu"], fn=s["fn"], fn_line=s["fn_line"], fn_end=s["fn_end"], nhash=s.get("nhash"),
aliases=s["aliases"], header=s.get("header", False), sites=[], frozen=[]))
if (s["cls"], s["kind"]) in REMOVABLE:
b["sites"].append(s)
else:
b["frozen"].append(s)
return bodies
def kind_of(tu):
return "shared" if tu.startswith("src/shared/") else "ov" if tu.startswith("src/ov_") else "md" if tu.startswith("src/md_") \
else "resident" if tu.startswith("src/resident/") else "main"
def includers():
"""header rel -> [TU rel] from the #include lines of every TU (the recipe to compile a header's body is an includer's)."""
inc = collections.defaultdict(list)
for p in sorted((REPO / "src").rglob("*.c")):
rel = p.relative_to(REPO).as_posix()
text = p.read_text(errors="surrogateescape")
for mm in sc.INCLUDE_LINE.finditer(sc.mask_text(text)):
inc[os.path.normpath(os.path.join(os.path.dirname(rel), mm.group(1)))].append(rel)
return inc
def features(b):
ks = collections.Counter(s["kind"] for s in b["sites"])
n = len(b["sites"])
mix = "pins-only" if set(ks) <= {"pin"} else "asm-only" if not (set(ks) & {"pin", "cast", "decl-body", "register"}) else "mixed"
return dict(kind=kind_of(b["tu"]), n=n, bucket=("1" if n == 1 else "2-4" if n <= 4 else "5-9" if n <= 9 else "10+"), mix=mix,
zero=any(s.get("zero") for s in b["sites"]), init=any(s.get("init") for s in b["sites"] if s["kind"] == "pin"),
instr=any(s["kind"] == "instruction" for s in b["sites"]), cast=any(s["kind"] == "cast" for s in b["sites"]),
register=any(s["kind"] == "register" for s in b["sites"]), volatile=any(s["cls"] == "C" for s in b["sites"]),
launder=any(s["kind"] == "launder" for s in b["sites"]), barrier=any(s["kind"] == "barrier" for s in b["sites"]),
keepalive=any(s["kind"] == "keepalive" for s in b["sites"]))
def stratified_sample(bodies, n, seed):
rnd = random.Random(seed)
# one exemplar per normalized class
by_class = {}
for key, b in bodies.items():
if not b["sites"]:
continue
h = b["nhash"] or key
cur = by_class.get(h)
pref = {"main": 0, "resident": 1, "md": 2, "shared": 3, "ov": 4}[kind_of(b["tu"])]
if cur is None or pref < cur[0]:
by_class[h] = (pref, key)
pool = [bodies[k] for _, k in by_class.values()]
rnd.shuffle(pool)
# quotas scale with --sample (the shape of the population: main/resident/md are few but distinct, overlays are many)
base = {"main": 20, "resident": 16, "md": 15, "shared": 20, "ov": 79}
scale = n / sum(base.values())
quota = {k: max(3, int(round(v * scale))) for k, v in base.items()}
picked, seen = [], set()
for kind, q in quota.items():
cands = [b for b in pool if kind_of(b["tu"]) == kind]
# round-robin over (bucket, mix)
groups = collections.defaultdict(list)
for b in cands:
f = features(b)
groups[(f["bucket"], f["mix"])].append(b)
keys = sorted(groups)
i = 0
while len([b for b in picked if kind_of(b["tu"]) == kind]) < q and any(groups[k] for k in keys):
k = keys[i % len(keys)]
i += 1
if groups[k]:
b = groups[k].pop()
if (b["tu"], b["fn"]) not in seen:
picked.append(b)
seen.add((b["tu"], b["fn"]))
# the special features, topped up from the pool
for flag, want in (("zero", 10), ("init", 10), ("instr", 12), ("cast", 8), ("register", 5), ("launder", 8), ("keepalive", 6)):
have = sum(1 for b in picked if features(b)[flag])
for b in pool:
if have >= want:
break
if (b["tu"], b["fn"]) not in seen and features(b)[flag]:
picked.append(b)
seen.add((b["tu"], b["fn"]))
have += 1
return picked
# ----------------------------------------------------------------------------------------------------------------------
# the ladder on one body
# ----------------------------------------------------------------------------------------------------------------------
def run_body(b, recipe, write_path, raw, m, ls, log):
"""Returns the per-body result dict; the caller restores the file."""
sites = sorted(b["sites"], key=lambda s: (ORDER.get(s["kind"], 9), s["line"], s["col"]))
res = dict(tu=b["tu"], fn=b["fn"], nhash=b["nhash"], kind=kind_of(b["tu"]), aliases=b["aliases"], header=b["header"],
obj=recipe["obj"], n_sites=len(sites), sites=[dict(kind=s["kind"], cls=s["cls"], detail=s["detail"], line=s["line"]) for s in sites],
refused=[], compiles=0, seconds=0.0, features=features(b))
per_site, edits_all, edits_keep = {}, [], []
usable = []
for s in sites:
try:
ed = site_edits(raw, m, ls, s)
edk = site_edits(raw, m, ls, s, keep_register=True) if s["kind"] == "pin" else ed
per_site[id(s)] = (ed, edk)
usable.append(s)
edits_all += ed
edits_keep += edk
except Refuse as ex:
res["refused"].append(dict(kind=s["kind"], line=s["line"], why=str(ex)))
res["n_usable"] = len(usable)
if not usable:
res["verdict"] = "NOTHING-USABLE"
return res
def judge(edits, tag):
try:
cand = apply_edits(raw, edits)
except Refuse as ex:
return "REFUSED", str(ex)
if cand == raw:
return "REFUSED", "no-op edit (the candidate equals the original — an identical object would prove nothing, R37)"
v, dt, err = oracle.judge(recipe, cand, tag=tag, write_path=write_path)
res["compiles"] += 1
res["seconds"] += dt
return v, err
# rung A (register dropped) and A' (register kept)
vA, errA = judge(edits_all, "A")
res["rung_A"] = vA
res["rung_A_err"] = errA[:200] if vA == "COMPILE-ERROR" else ""
if any(s["kind"] == "pin" for s in usable):
vK, _ = judge(edits_keep, "Ak")
res["rung_A_keep_register"] = vK
else:
res["rung_A_keep_register"] = vA
if vA == "IDENTICAL":
res["removed"] = [dict(kind=s["kind"], detail=s["detail"], line=s["line"]) for s in usable]
res["needed"] = []
res["verdict"] = "LEVER-FREE"
return res
# rung B greedy
accepted, needed, removed = [], [], []
for s in usable:
ed, _ = per_site[id(s)]
v, err = judge(accepted + ed, "B")
if v == "IDENTICAL":
accepted += ed
removed.append(dict(kind=s["kind"], detail=s["detail"], line=s["line"]))
else:
needed.append(dict(kind=s["kind"], detail=s["detail"], line=s["line"], verdict=v, err=(err[:120] if v == "COMPILE-ERROR" else "")))
res["removed"], res["needed"] = removed, needed
res["verdict"] = "LEVER-FREE" if not needed else "RESIDUE"
log(f" {b['tu']}:{b['fn']} A={vA} B: removed {len(removed)} needed {len(needed)} compiles {res['compiles']}")
return res
def probe(sample_n, seed, jobs):
PROBE.mkdir(parents=True, exist_ok=True)
ok, why = oracle.calibration_current()
if not ok:
sys.exit(f"delever: the oracle's calibration is not current ({why}) — run tools/delever_oracle.py --calibrate first (R56)")
recipes = oracle.load_recipes()["recipes"]
sites = load_sites()
bodies = bodies_from_sites(sites)
inc = includers()
sample = stratified_sample(bodies, sample_n, seed)
log_f = open(PROBE / "probe_log.txt", "a")
def log(msg):
line = f"{time.strftime('%H:%M:%S')} {msg}"
print(line, flush=True)
log_f.write(line + "\n")
log_f.flush()
log(f"probe: {len(sample)} bodies sampled from {len(bodies)} lever bodies ({len({b['nhash'] for b in bodies.values() if b['sites']})} distinct classes); seed {seed}")
# group by the FILE that gets written (a TU, or a header) — one worker per file
by_file = collections.defaultdict(list)
for b in sample:
by_file[b["tu"]].append(b)
def work(tu):
out = []
if tu.endswith(".h"):
tus = [t for t in inc.get(tu, []) if ("build/" + t[:-2] + ".o") in recipes]
if not tus:
return [dict(tu=tu, fn=b["fn"], verdict="NO-INCLUDER-RECIPE") for b in by_file[tu]]
recipe = recipes["build/" + tus[0][:-2] + ".o"]
write_path = tu
else:
obj = "build/" + tu[:-2] + ".o"
if obj not in recipes:
return [dict(tu=tu, fn=b["fn"], verdict="NO-RECIPE") for b in by_file[tu]]
recipe = recipes[obj]
write_path = None
path = REPO / tu
raw = path.read_text(errors="surrogateescape")
m = same_len_mask(raw)
ls = line_starts(raw)
try:
for b in by_file[tu]:
out.append(run_body(b, recipe, write_path, raw, m, ls, log))
finally:
path.write_text(raw, errors="surrogateescape")
return out
results = []
t0 = time.time()
# phase 1: translation units, one worker per TU (no two candidates of one file at once, and no header is being edited);
# phase 2: shared headers, SERIAL — a header's edit reaches every includer, so nothing else may compile meanwhile
# (the dry run's three "compile errors" were two workers' candidates crossing inside one includer)
tu_files = sorted(f for f in by_file if not f.endswith(".h"))
hdr_files = sorted(f for f in by_file if f.endswith(".h"))
with ThreadPoolExecutor(max_workers=jobs) as ex:
for r in ex.map(work, tu_files):
results.extend(r)
for f in hdr_files:
results.extend(work(f))
wall = time.time() - t0
with open(PROBE / "probe_results.jsonl", "w") as f:
for r in results:
f.write(json.dumps(r) + "\n")
summary = summarize(results, bodies, wall, seed)
(PROBE / "probe_summary.json").write_text(json.dumps(summary, indent=1) + "\n")
(PROBE / "probe_table.md").write_text(render(summary) + "\n")
print(render(summary))
return summary
def summarize(results, bodies, wall, seed):
judged = [r for r in results if r.get("verdict") in ("LEVER-FREE", "RESIDUE")]
def pct(a, b):
return round(100.0 * a / b, 1) if b else None
by_kind = {}
for k in ("main", "resident", "md", "shared", "ov"):
rs = [r for r in judged if r["kind"] == k]
if not rs:
continue
by_kind[k] = dict(bodies=len(rs), A_identical=sum(1 for r in rs if r["rung_A"] == "IDENTICAL"),
A_keep_register_identical=sum(1 for r in rs if r.get("rung_A_keep_register") == "IDENTICAL"),
lever_free=sum(1 for r in rs if r["verdict"] == "LEVER-FREE"),
sites=sum(r["n_usable"] for r in rs), removed=sum(len(r["removed"]) for r in rs), needed=sum(len(r["needed"]) for r in rs),
compiles=sum(r["compiles"] for r in rs), mean_s_per_compile=round(sum(r["seconds"] for r in rs) / max(sum(r["compiles"] for r in rs), 1), 3))
needed_kinds = collections.Counter(n["kind"] for r in judged for n in r["needed"])
needed_detail = collections.Counter(f"{n['kind']}:{n['detail']}" for r in judged for n in r["needed"])
removed_kinds = collections.Counter(x["kind"] for r in judged for x in r["removed"])
refused = collections.Counter(x["why"].split(":")[0][:60] for r in results for x in r.get("refused", []))
sites_total = sum(r["n_usable"] for r in judged)
sites_needed = sum(len(r["needed"]) for r in judged)
flags = {}
for flag in ("zero", "init", "instr", "cast", "register", "launder", "barrier", "keepalive", "volatile"):
rs = [r for r in judged if r["features"].get(flag)]
flags[flag] = dict(bodies=len(rs), lever_free=sum(1 for r in rs if r["verdict"] == "LEVER-FREE"))
pop_bodies = sum(1 for b in bodies.values() if b["sites"])
pop_sites = sum(len(b["sites"]) for b in bodies.values())
mean_compiles = sum(r["compiles"] for r in judged) / max(len(judged), 1)
mean_s = sum(r["seconds"] for r in judged) / max(sum(r["compiles"] for r in judged), 1)
return dict(generated=time.strftime("%Y-%m-%d %H:%M"), seed=seed, sampled=len(results), judged=len(judged),
not_judged=collections.Counter(r.get("verdict") for r in results if r not in judged),
rung_A_identical=sum(1 for r in judged if r["rung_A"] == "IDENTICAL"),
rung_A_keep_register_identical=sum(1 for r in judged if r.get("rung_A_keep_register") == "IDENTICAL"),
lever_free_after_B=sum(1 for r in judged if r["verdict"] == "LEVER-FREE"),
sites=dict(total=sites_total, removed=sites_total - sites_needed, needed=sites_needed, pct_removed=pct(sites_total - sites_needed, sites_total)),
needed_by_kind=dict(needed_kinds), needed_by_detail=dict(needed_detail.most_common(30)), removed_by_kind=dict(removed_kinds),
refused=dict(refused), by_kind=by_kind, flags=flags,
compiles=sum(r["compiles"] for r in judged), mean_compiles_per_body=round(mean_compiles, 2), mean_s_per_compile=round(mean_s, 3),
wall_s=round(wall, 1),
population=dict(bodies=pop_bodies, sites=pop_sites),
pricing=dict(T4_compiles_est=int(pop_bodies * mean_compiles), T4_cpu_hours_est=round(pop_bodies * mean_compiles * mean_s / 3600, 2),
T4_wall_hours_at_16_est=round(pop_bodies * mean_compiles * mean_s / 3600 / 12, 2),
residue_bodies_est=int(pop_bodies * (1 - (sum(1 for r in judged if r["verdict"] == "LEVER-FREE") / max(len(judged), 1)))),
residue_sites_est=int(pop_sites * (sites_needed / max(sites_total, 1)))))
def render(s):
L = [f"# probe — {s['sampled']} bodies sampled, {s['judged']} judged (seed {s['seed']}, {s['generated']}, wall {s['wall_s']} s)", "",
f"- rung A (strip-all) identical: **{s['rung_A_identical']} / {s['judged']}** (register dropped) · {s['rung_A_keep_register_identical']} with `register` kept",
f"- lever-free after rung B: **{s['lever_free_after_B']} / {s['judged']}**",
f"- sites: {s['sites']['total']} usable · removed {s['sites']['removed']} ({s['sites']['pct_removed']} %) · needed {s['sites']['needed']}",
f"- needed by kind: {s['needed_by_kind']}", f"- needed by detail (top): {s['needed_by_detail']}",
f"- removed by kind: {s['removed_by_kind']}", f"- refused (rewrite not applicable): {s['refused']}",
f"- compiles {s['compiles']} · mean {s['mean_compiles_per_body']} per body · {s['mean_s_per_compile']} s per compile", "",
"| kind | bodies | A identical | A (register kept) | lever-free after B | sites | removed | needed | compiles | s/compile |", "|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|"]
for k, v in s["by_kind"].items():
L.append(f"| {k} | {v['bodies']} | {v['A_identical']} | {v['A_keep_register_identical']} | {v['lever_free']} | {v['sites']} | {v['removed']} | {v['needed']} | {v['compiles']} | {v['mean_s_per_compile']} |")
L += ["", "| feature | bodies | lever-free |", "|---|---:|---:|"]
for k, v in s["flags"].items():
L.append(f"| {k} | {v['bodies']} | {v['lever_free']} |")
p = s["pricing"]
L += ["", f"**Pricing (R41; population {s['population']['bodies']:,} lever bodies / {s['population']['sites']:,} removable sites):** "
f"T4 ≈ {p['T4_compiles_est']:,} compiles ≈ {p['T4_cpu_hours_est']} CPU-h ≈ {p['T4_wall_hours_at_16_est']} h wall at 12 workers; "
f"residue after the mechanical ladder ≈ {p['residue_bodies_est']:,} bodies / {p['residue_sites_est']:,} sites (the sample's rates applied to the population)."]
return "\n".join(L)
def main():
ap = argparse.ArgumentParser(description=__doc__.split("\n")[0])
ap.add_argument("--probe", action="store_true")
ap.add_argument("--sample", type=int, default=150)
ap.add_argument("--seed", type=int, default=1)
ap.add_argument("-j", "--jobs", type=int, default=12)
a = ap.parse_args()
if a.probe:
probe(a.sample, a.seed, a.jobs)
return
ap.print_help()
if __name__ == "__main__":
main()
+303
View File
@@ -0,0 +1,303 @@
#!/usr/bin/env python3
"""delever_oracle.py — the fast byte oracle for a single-translation-unit source edit (Phase 36 T2, 2026-09-09).
tools/delever_oracle.py --recipes [-j N] # capture every object's exact build command via `make -n -W` -> .run/P36/delever/recipes.json
tools/delever_oracle.py --calibrate ov_SC04_011 main # compile every TU of the named binaries UNTOUCHED: 100 % object equality with build/,
# twin == primary, then the POSITIVE control (a nop injected -> DIFFERS); exit 1 otherwise
tools/delever_oracle.py --status # is the calibration current for this HEAD / Makefile?
THE QUESTION IT ANSWERS: "does this candidate text for TU X still produce the byte-identical object?" — and only that. The whole-binary
gate (`make check BINARY=…`, and the clean fleet run, R22) remains the arbiter of every batch; this oracle is the inner loop that
decides one lever site at a time in ~0.3–5 s instead of ~90 s.
HOW (the design settled at gate 1 + the plan agent's review, R57: the instrument's own path is part of the instrument):
* the recipe of every object is the Makefile's OWN command, printed by `make -n -W <src> <obj> BINARY=<alias>` (0.04–0.15 s each;
captured once per Makefile/config state, ~4,300 objects at -j16 in ~30 s). It carries the per-object -O0 overrides, the
jtbl_rodata_pads stage of main/module objects, the reorder island, the twin rule (the primary's source into the twin's object).
tools/compile_only.py's re-typed pipeline omits the pad stage and is NOT used.
* a candidate compiles IN PLACE at the real path (objects embed the source path as an STT_FILE symbol) with `-o` and `-MF`
redirected to scratch — build/ is never written, so the baseline object of the fleet run stays the reference; the TU's text is
restored from the caller's in-memory snapshot (never `git checkout`, R102). Workers own whole TUs: no two candidates of one TU at once.
* verdict = whole-object byte equality with build/<obj> (R56: build/ must come from a clean fleet run of THIS tree — --calibrate proves it).
* CALIBRATION (R39/R57): every sampled TU compiled untouched reproduces its object byte for byte; a twin's object equals its primary's;
a deliberately altered body reports DIFFERS. `.run/P36/delever/calibration.json` records HEAD, the Makefile/config mtimes and the
per-object seconds (the scheduler's cost model); an --apply of the de-lever tool refuses without a current calibration.
"""
import argparse
import hashlib
import json
import os
import pathlib
import re
import subprocess
import sys
import time
from concurrent.futures import ThreadPoolExecutor
REPO = pathlib.Path(__file__).resolve().parent.parent
sys.path.insert(0, str(REPO / "tools"))
RUN = REPO / ".run" / "P36" / "delever"
RECIPES = RUN / "recipes.json"
CALIB = RUN / "calibration.json"
SCRATCH = RUN / "obj"
RECIPE_RE = re.compile(r"^set -o pipefail; (.*) -o (build/\S+\.o)$")
# ----------------------------------------------------------------------------------------------------------------------
# enumeration: (alias, src, obj) for every object of every binary — derived from the Makefile's own rules (R33)
# ----------------------------------------------------------------------------------------------------------------------
def src_dirs():
import corpus
return dict(corpus.src_dirs())
def twin_of_map():
import share_census
return share_census.twin_of_map()
def objects(aliases=None):
"""[(alias, src_rel, obj_rel)] — the pattern rule build/src/%.o: src/%.c, and for a twin the primary's sources renamed into the
twin's own object names (Makefile twin block)."""
import compile_only
dirs = src_dirs()
twins = twin_of_map()
out = []
for a in sorted(dirs):
if aliases and a not in aliases:
continue
prim = twins.get(a)
for p in compile_only.tus_of(a, dirs):
src = p.relative_to(REPO).as_posix()
if prim:
# src/<prim>/<prim><suffix>.c -> build/src/<twin>/<twin><suffix>.o
stem = p.name[:-2]
if not stem.startswith(prim):
continue
obj = f"build/src/{a}/{a}{stem[len(prim):]}.o"
else:
obj = "build/" + src[:-2] + ".o"
out.append((a, src, obj))
return out
def config_stamp():
files = [REPO / "Makefile"] + sorted((REPO / "config").glob("*.mk"))
h = hashlib.sha1()
for f in files:
st = f.stat()
h.update(f"{f.name}|{int(st.st_mtime)}|{st.st_size}\n".encode())
return h.hexdigest()[:12]
def head():
return subprocess.run(["git", "rev-parse", "--short", "HEAD"], cwd=REPO, capture_output=True, text=True).stdout.strip()
# ----------------------------------------------------------------------------------------------------------------------
# recipes
# ----------------------------------------------------------------------------------------------------------------------
def capture_recipe(alias, src, obj):
r = subprocess.run(["make", "-n", "-W", src, obj, f"BINARY={alias}"], cwd=REPO, capture_output=True, text=True)
if r.returncode != 0:
return None, f"make -n rc={r.returncode}: {r.stderr.strip()[:200]}"
lines = [ln for ln in r.stdout.splitlines() if ln.startswith("set -o pipefail;")]
if len(lines) != 1:
return None, f"expected ONE pipeline line, got {len(lines)}"
m = RECIPE_RE.match(lines[0])
if not m or m.group(2) != obj:
return None, f"unparsed recipe: {lines[0][:160]}"
return dict(alias=alias, src=src, obj=obj, pipeline=m.group(1)), None
def build_recipes(jobs, aliases=None):
objs = objects(aliases)
recipes, errors = {}, []
t0 = time.time()
with ThreadPoolExecutor(max_workers=jobs) as ex:
for (a, src, obj), (rec, err) in zip(objs, ex.map(lambda t: capture_recipe(*t), objs)):
if rec:
recipes[obj] = rec
else:
errors.append(f"{a} {src} {obj}: {err}")
return dict(stamp=config_stamp(), head=head(), generated=time.strftime("%Y-%m-%d %H:%M"), n=len(recipes),
seconds=round(time.time() - t0, 1), recipes=recipes, errors=errors)
def load_recipes(refresh=False, jobs=16):
if RECIPES.exists() and not refresh:
d = json.loads(RECIPES.read_text())
if d.get("stamp") == config_stamp():
return d
d = build_recipes(jobs)
RUN.mkdir(parents=True, exist_ok=True)
RECIPES.write_text(json.dumps(d, indent=1) + "\n")
return d
# ----------------------------------------------------------------------------------------------------------------------
# compile + compare
# ----------------------------------------------------------------------------------------------------------------------
def compile_obj(recipe, text=None, tag="x", write_path=None):
"""Compile the recipe's source (with `text` written IN PLACE first — to `write_path` when the edited file is a header the
source includes, else to the source itself; the CALLER restores it) to a scratch object. Returns (bytes | None, seconds, errors)."""
src = REPO / (write_path or recipe["src"])
obj = recipe["obj"]
out = SCRATCH / (obj[len("build/"):].replace("/", "__")[:-2] + f".{tag}.o")
out.parent.mkdir(parents=True, exist_ok=True)
dep = str(out)[:-2] + ".d"
pipeline = recipe["pipeline"]
pipeline = re.sub(r"-MF \S+", f"-MF {dep}", pipeline, count=1)
cmd = f"set -o pipefail; {pipeline} -o {out}"
if text is not None:
src.write_text(text, errors="surrogateescape")
t0 = time.time()
r = subprocess.run(["bash", "-c", cmd], cwd=REPO, capture_output=True, text=True)
dt = time.time() - t0
if r.returncode != 0 or not out.exists():
# gcc 2.7.2 prints errors without the word "error" and the assembler floods stderr with `$at` warnings: keep every
# non-warning line (R103), then the tail
errs = [ln for ln in r.stderr.splitlines()
if ln.strip() and not re.search(r"\bwarning:", ln, re.I) and not ln.startswith(("In file included", " " * 16))
and ": In function" not in ln]
return None, dt, ("\n".join(errs[:8]) or r.stderr[-400:])
data = out.read_bytes()
try:
out.unlink()
pathlib.Path(dep).unlink(missing_ok=True)
except OSError:
pass
return data, dt, ""
def baseline_bytes(obj):
p = REPO / obj
return p.read_bytes() if p.exists() else None
def judge(recipe, text, tag="x", write_path=None):
"""(verdict, seconds, err): verdict in IDENTICAL | DIFFERS | COMPILE-ERROR | NO-BASELINE."""
base = baseline_bytes(recipe["obj"])
if base is None:
return "NO-BASELINE", 0.0, ""
data, dt, err = compile_obj(recipe, text, tag, write_path)
if data is None:
return "COMPILE-ERROR", dt, err
return ("IDENTICAL" if data == base else "DIFFERS"), dt, ""
# ----------------------------------------------------------------------------------------------------------------------
# calibration
# ----------------------------------------------------------------------------------------------------------------------
def calibrate(aliases, jobs, recipes):
"""Every object of the named binaries compiled UNTOUCHED -> must equal build/; twin == primary; then the positive control."""
recs = [r for r in recipes["recipes"].values() if r["alias"] in aliases]
if not recs:
sys.exit(f"delever_oracle: no recipes for {aliases} (R43)")
twins = twin_of_map()
results = []
def one(r):
v, dt, err = judge(r, None, tag="cal")
return dict(alias=r["alias"], obj=r["obj"], verdict=v, seconds=round(dt, 3), err=err[-200:])
with ThreadPoolExecutor(max_workers=jobs) as ex:
results = list(ex.map(one, recs))
ok = sum(1 for x in results if x["verdict"] == "IDENTICAL")
bad = [x for x in results if x["verdict"] != "IDENTICAL"]
# twin == primary on the objects both build
twin_checks = []
for a in aliases:
prim = twins.get(a)
if not prim:
continue
for r in recipes["recipes"].values():
if r["alias"] == a:
pobj = r["obj"].replace(f"build/src/{a}/{a}", f"build/src/{prim}/{prim}")
same = baseline_bytes(r["obj"]) == baseline_bytes(pobj)
twin_checks.append(dict(twin=r["obj"], primary=pobj, equal=same))
twin_bad = [t for t in twin_checks if not t["equal"]]
# the positive control: a nop injected into a REAL function body (the first definition the shared scanner finds in the
# first recipe's source with a body of >= 3 lines) must DIFFER — a verifier that cannot fail is no verifier (R39/R53)
import share_census as sc
ctl = recs[0]
src = REPO / ctl["src"]
orig = src.read_text(errors="surrogateescape")
pos_verdict = "SKIPPED"
defs = [r for r in sc.scan_text(orig, ctl["src"], shared_defs=None) if r["form"] == "def" and r.get("nlines", 0) >= 3 and not r.get("empty")]
if defs:
d = defs[0]
line_starts = [0]
for ln in orig.split("\n"):
line_starts.append(line_starts[-1] + len(ln) + 1)
# at the END of the body (C89: a statement before the declarations is a parse error)
o = orig.rfind("}", line_starts[d["end"] - 1], line_starts[d["end"]])
if o >= 0:
cand = orig[:o] + ' __asm__("nop");\n' + orig[o:]
try:
pos_verdict, _, err = judge(ctl, cand, tag="ctl")
pos_verdict = pos_verdict if pos_verdict != "COMPILE-ERROR" else f"COMPILE-ERROR ({err[-100:]})"
finally:
src.write_text(orig, errors="surrogateescape")
by_alias = {}
for x in results:
d = by_alias.setdefault(x["alias"], dict(objects=0, identical=0, seconds=0.0))
d["objects"] += 1
d["identical"] += x["verdict"] == "IDENTICAL"
d["seconds"] += x["seconds"]
return dict(head=head(), stamp=config_stamp(), generated=time.strftime("%Y-%m-%d %H:%M"), aliases=sorted(aliases),
objects=len(results), identical=ok, failures=bad[:20], twin_checks=len(twin_checks), twin_mismatch=twin_bad[:10],
positive_control=dict(obj=ctl["obj"], verdict=pos_verdict),
per_alias={a: dict(v, mean_s=round(v["seconds"] / max(v["objects"], 1), 3)) for a, v in by_alias.items()},
per_object_seconds={x["obj"]: x["seconds"] for x in results},
ok=(not bad and not twin_bad and pos_verdict == "DIFFERS"))
def calibration_current():
if not CALIB.exists():
return False, "no calibration"
d = json.loads(CALIB.read_text())
if d.get("head") != head():
return False, f"calibrated at {d.get('head')}, HEAD is {head()}"
if d.get("stamp") != config_stamp():
return False, "the Makefile/config changed since the calibration"
return bool(d.get("ok")), ("ok" if d.get("ok") else "the calibration FAILED")
def main():
ap = argparse.ArgumentParser(description=__doc__.split("\n")[0])
ap.add_argument("--recipes", action="store_true", help="(re)capture every object's recipe")
ap.add_argument("--calibrate", nargs="*", help="binaries to calibrate on (e.g. ov_SC04_011 ov_SC03_015 main)")
ap.add_argument("--status", action="store_true")
ap.add_argument("-j", "--jobs", type=int, default=16)
a = ap.parse_args()
RUN.mkdir(parents=True, exist_ok=True)
if a.status:
ok, why = calibration_current()
print(f"delever_oracle: calibration {'CURRENT' if ok else 'STALE/MISSING'} — {why}")
sys.exit(0 if ok else 1)
if a.recipes:
d = load_recipes(refresh=True, jobs=a.jobs)
print(f"delever_oracle: {d['n']} recipes captured in {d['seconds']} s at -j{a.jobs}; errors {len(d['errors'])}")
for e in d["errors"][:10]:
print(" ", e)
sys.exit(1 if d["errors"] else 0)
if a.calibrate is not None:
recipes = load_recipes(jobs=a.jobs)
t0 = time.time()
d = calibrate(a.calibrate, a.jobs, recipes)
d["seconds"] = round(time.time() - t0, 1)
CALIB.write_text(json.dumps(d, indent=1) + "\n")
print(f"delever_oracle --calibrate {' '.join(d['aliases'])}: {d['identical']}/{d['objects']} objects byte-identical untouched; "
f"twin checks {d['twin_checks']} ({len(d['twin_mismatch'])} mismatch); positive control {d['positive_control']['verdict']} "
f"on {d['positive_control']['obj']}; {d['seconds']} s — {'OK' if d['ok'] else 'FAIL'}")
for a_, v in d["per_alias"].items():
print(f" {a_:14s} {v['identical']}/{v['objects']} identical, mean {v['mean_s']} s per object")
for f in d["failures"][:10]:
print(" FAIL", f["alias"], f["obj"], f["verdict"], f["err"][-120:])
sys.exit(0 if d["ok"] else 1)
ap.print_help()
if __name__ == "__main__":
main()
+2 -1
View File
@@ -405,7 +405,8 @@ def walk_file(raw, rel, is_header):
d = span_of_line.get(ln)
flat = re.sub(r"\s+", " ", text).strip()
# a file-scope asm block keeps its whole template: the manifest match reads its `.globl`/`.ent` name
sites.append(dict(tu=rel, fn=(d["name"] if d else None), fn_line=(d["line"] if d else None), line=ln,
sites.append(dict(tu=rel, fn=(d["name"] if d else None), fn_line=(d["line"] if d else None), fn_end=(d["end"] if d else None),
nhash=(d["nhash"] if d else None), line=ln,
col=pos - masked.rfind("\n", 0, pos), cls=cls, kind=kind, detail=detail,
text=(flat if (cls == "B" and d is None) else flat[:200]), marked=marked(ln), scope=("body" if d else "file"), **kw))