mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-27 14:02:04 -04:00
phase-36: T5 (header step) — the GTE consolidation: tools/gte_consolidate.py (signatures by the build's own maspsx→as tail: bytes + operand counts + clobbers; one canonical text per signature under Sony's names, Sony's clobbers canonical even when every definition carries the steer; lever variants <name>_m tried as canonical first; direct statements → canonical calls; --sweep), include/gte_inline.h (50 macros for 9,102 definitions) included from common.h — R22 218/218; the census's gte-lever class + per-TU definition count + strict gate; the cycle's MODE=gte; dictionary + SETUP rows; kit corpus; log entry
This commit is contained in:
@@ -489,6 +489,12 @@ unsloth_compiled_cache/
|
||||
/.run/P36/delever/includers_cache.json
|
||||
!/.run/P36/delever/*.jsonl
|
||||
!/.run/P36/delever/*.txt
|
||||
!/.run/P36/gte/
|
||||
/.run/P36/gte/*
|
||||
!/.run/P36/gte/*.json
|
||||
!/.run/P36/gte/*.log
|
||||
!/.run/P36/gte/*.txt
|
||||
/.run/P36/gte/inventory.json
|
||||
!/.run/P36/permuter/
|
||||
/.run/P36/permuter/*
|
||||
!/.run/P36/permuter/*.log
|
||||
|
||||
@@ -0,0 +1,476 @@
|
||||
clean: removed build/, expected/, and the regenerated splat tree (asm/, assets/, undefined_*_auto.txt).
|
||||
disc-extract: up to date — extracted/retail/ matches the committed manifest (sha1 e5c6019249dec3c71bb2cf40658a78439dddb037)
|
||||
splat 0.41.0 (powered by spimdisasm 1.41.0)
|
||||
|
||||
0%| | 0/1096 [00:00<?, ?it/s]
|
||||
Loading symbols (symbols.us): 0%| | 0/1096 [00:00<?, ?it/s]
|
||||
Loading symbols (symbols.us): 100%|██████████| 1096/1096 [00:00<00:00, 227696.13it/s]
|
||||
|
||||
0%| | 0/47 [00:00<?, ?it/s]
|
||||
Loading symbols (symbols.us.ram): 0%| | 0/47 [00:00<?, ?it/s]
|
||||
Loading symbols (symbols.us.ram): 100%|██████████| 47/47 [00:00<00:00, 633865.88it/s]
|
||||
|
||||
0%| | 0/2 [00:00<?, ?it/s]
|
||||
Scanning header: 0%| | 0/2 [00:00<?, ?it/s]
|
||||
Scanning main: 0%| | 0/2 [00:00<?, ?it/s]
|
||||
Rodata segment '800' may belong to the text segment '800'
|
||||
Based on the usage from the function LzssDecodeSector to the symbol jtbl_80072A38
|
||||
|
||||
The rodata segment '800' has jumptables that are not aligned properly file-wise, indicating one or more likely file split.
|
||||
File split suggestions for this segment will follow in config yaml format:
|
||||
- [0x6324C, .rodata]
|
||||
- [0x63388, .rodata]
|
||||
- [0x633FC, .rodata]
|
||||
|
||||
|
||||
Rodata segment '800_b' may belong to the text segment '800_b'
|
||||
Based on the usage from the function func_8002B0B4 to the symbol jtbl_80072E44
|
||||
|
||||
Rodata segment '800_b_2' may belong to the text segment '800_b_2'
|
||||
Based on the usage from the function func_8002DC68 to the symbol jtbl_80072F3C
|
||||
|
||||
The rodata segment '800_b_2' has jumptables that are not aligned properly file-wise, indicating one or more likely file split.
|
||||
File split suggestions for this segment will follow in config yaml format:
|
||||
- [0x63920, .rodata]
|
||||
|
||||
|
||||
Rodata segment '800_c' may belong to the text segment '800_c'
|
||||
Based on the usage from the function func_80035270 to the symbol jtbl_800732A0
|
||||
|
||||
Scanning main: 100%|██████████| 2/2 [00:01<00:00, 1.37it/s]
|
||||
Scanning main: 100%|██████████| 2/2 [00:01<00:00, 1.37it/s]
|
||||
|
||||
0%| | 0/2 [00:00<?, ?it/s]
|
||||
Splitting header: 0%| | 0/2 [00:00<?, ?it/s]
|
||||
Splitting main: 0%| | 0/2 [00:00<?, ?it/s]
|
||||
Splitting main: 100%|██████████| 2/2 [00:01<00:00, 1.47it/s]
|
||||
Splitting main: 100%|██████████| 2/2 [00:01<00:00, 1.47it/s]
|
||||
|
||||
0%| | 0/2 [00:00<?, ?it/s]
|
||||
Linker script header: 0%| | 0/2 [00:00<?, ?it/s]
|
||||
Linker script main: 0%| | 0/2 [00:00<?, ?it/s]
|
||||
Linker script main: 100%|██████████| 2/2 [00:00<00:00, 90.06it/s]
|
||||
Split 340 KB (82.28%) in defined segments
|
||||
header: 2 KB (0.50%) 1 split, 0 cached
|
||||
c: 338 KB (81.78%) 85 split, 0 cached
|
||||
unknown: 0 B (0.00%) from unknown bin files
|
||||
ld_interleave --order: .main island = 8 pieces [53198.data.o, 800.o, 63470.data.o, 800_b.o, 800_b_2.o, 63940.data.o, 800_c.o, 63C4C.data.o]; pre=0 text=85 empties=166 bss=85
|
||||
[ OK ] ov_SC05_000
|
||||
[ OK ] resident
|
||||
[ OK ] ov_SC01_000
|
||||
[ OK ] ov_SC04_000
|
||||
[ OK ] ov_SC06_000
|
||||
[ OK ] ov_SC01_006
|
||||
[ OK ] ov_SC01_005
|
||||
[ OK ] ov_SC01_077
|
||||
[ OK ] ov_SC02_003
|
||||
[ OK ] ov_SC04_018
|
||||
[ OK ] ov_SC04_019
|
||||
[ OK ] ov_SC02_000
|
||||
[ OK ] ov_SC03_002
|
||||
[ OK ] ov_SC01_001
|
||||
[ OK ] ov_SC03_006
|
||||
[ OK ] ov_SC01_004
|
||||
[ OK ] ov_SC03_001
|
||||
[ OK ] ov_SC01_008
|
||||
[ OK ] ov_SC01_074
|
||||
[ OK ] ov_SC07_000
|
||||
[ OK ] ov_SC02_004
|
||||
[ OK ] ov_SC02_015
|
||||
[ OK ] ov_SC02_021
|
||||
[ OK ] ov_SC01_080
|
||||
[ OK ] ov_SC01_084
|
||||
[ OK ] ov_SC01_009
|
||||
[ OK ] ov_SC02_016
|
||||
[ OK ] ov_SC02_031
|
||||
[ OK ] ov_SC02_005
|
||||
[ OK ] ov_SC02_026
|
||||
[ OK ] ov_SC02_039
|
||||
[ OK ] ov_SC02_027
|
||||
[ OK ] ov_SC03_003
|
||||
[ OK ] ov_SC02_028
|
||||
[ OK ] ov_SC02_035
|
||||
[ OK ] ov_SC02_011
|
||||
[ OK ] ov_SC02_041
|
||||
[ OK ] ov_SC02_017
|
||||
[ OK ] ov_SC03_012
|
||||
[ OK ] ov_SC03_010
|
||||
[ OK ] ov_SC03_011
|
||||
[ OK ] ov_SC03_013
|
||||
[ OK ] ov_SC03_023
|
||||
[ OK ] ov_SC03_007
|
||||
[ OK ] ov_SC03_024
|
||||
[ OK ] ov_SC03_089
|
||||
[ OK ] ov_SC03_014
|
||||
[ OK ] ov_SC03_029
|
||||
[ OK ] ov_SC03_015
|
||||
[ OK ] ov_SC03_092
|
||||
[ OK ] ov_SC03_031
|
||||
[ OK ] ov_SC03_090
|
||||
[ OK ] ov_SC03_028
|
||||
[ OK ] ov_SC03_095
|
||||
[ OK ] ov_SC03_096
|
||||
[ OK ] ov_SC03_030
|
||||
[ OK ] ov_SC03_093
|
||||
[ OK ] ov_SC03_094
|
||||
[ OK ] ov_SC03_091
|
||||
[ OK ] ov_SC03_097
|
||||
[ OK ] ov_SC03_101
|
||||
[ OK ] ov_SC03_098
|
||||
[ OK ] ov_SC03_108
|
||||
[ OK ] ov_SC03_109
|
||||
[ OK ] ov_SC03_110
|
||||
[ OK ] ov_SC03_102
|
||||
[ OK ] ov_SC03_100
|
||||
[ OK ] ov_SC03_115
|
||||
[ OK ] ov_SC03_105
|
||||
[ OK ] ov_SC03_113
|
||||
[ OK ] ov_SC03_112
|
||||
[ OK ] ov_SC03_099
|
||||
[ OK ] ov_SC03_104
|
||||
[ OK ] ov_SC03_103
|
||||
[ OK ] ov_SC03_114
|
||||
[ OK ] ov_SC03_111
|
||||
[ OK ] ov_SC03_116
|
||||
[ OK ] ov_SC03_126
|
||||
[ OK ] ov_SC03_121
|
||||
[ OK ] ov_SC03_117
|
||||
[ OK ] ov_SC04_003
|
||||
[ OK ] ov_SC04_008
|
||||
[ OK ] ov_SC03_125
|
||||
[ OK ] ov_SC03_118
|
||||
[ OK ] ov_SC04_010
|
||||
[ OK ] ov_SC04_006
|
||||
[ OK ] ov_SC04_002
|
||||
[ OK ] ov_SC03_119
|
||||
[ OK ] ov_SC04_007
|
||||
[ OK ] ov_SC04_004
|
||||
[ OK ] ov_SC04_005
|
||||
[ OK ] ov_SC03_124
|
||||
[ OK ] ov_SC04_009
|
||||
[ OK ] ov_SC04_012
|
||||
[ OK ] ov_SC04_021
|
||||
[ OK ] ov_SC04_016
|
||||
[ OK ] ov_SC05_006
|
||||
[ OK ] ov_SC04_015
|
||||
[ OK ] ov_SC05_002
|
||||
[ OK ] ov_SC05_009
|
||||
[ OK ] ov_SC05_007
|
||||
[ OK ] ov_SC05_001
|
||||
[ OK ] ov_SC05_008
|
||||
[ OK ] ov_SC04_011
|
||||
[ OK ] ov_SC05_003
|
||||
[ OK ] ov_SC04_020
|
||||
[ OK ] ov_SC05_005
|
||||
[ OK ] ov_SC05_004
|
||||
[ OK ] ov_SC05_011
|
||||
[ OK ] ov_SC05_010
|
||||
[ OK ] ov_SC05_019
|
||||
[ OK ] ov_SC06_008
|
||||
[ OK ] ov_SC06_015
|
||||
[ OK ] ov_SC06_013
|
||||
[ OK ] ov_SC06_010
|
||||
[ OK ] ov_SC06_014
|
||||
[ OK ] ov_SC06_016
|
||||
[ OK ] ov_SC06_011
|
||||
[ OK ] ov_SC05_017
|
||||
[ OK ] ov_SC05_018
|
||||
[ OK ] ov_SC06_018
|
||||
[ OK ] ov_SC06_006
|
||||
[ OK ] ov_SC06_027
|
||||
[ OK ] ov_SC06_020
|
||||
[ OK ] ov_SC06_024
|
||||
[ OK ] ov_SC06_025
|
||||
[ OK ] ov_SC06_022
|
||||
[ OK ] ov_SC07_008
|
||||
[ OK ] md_MAIN_013
|
||||
[ OK ] md_MAIN_014
|
||||
[ OK ] ov_SC07_001
|
||||
[ OK ] md_MAIN_015
|
||||
[ OK ] ov_SC07_002
|
||||
[ OK ] ov_SC06_030
|
||||
[ OK ] md_MAIN_016
|
||||
[ OK ] md_MAIN_017
|
||||
[ OK ] md_MAIN_018
|
||||
[ OK ] ov_SC07_009
|
||||
[ OK ] md_MAIN_020
|
||||
[ OK ] md_MAIN_019
|
||||
[ OK ] md_MAIN_021
|
||||
[ OK ] md_MAIN_022
|
||||
[ OK ] md_MAIN_023
|
||||
[ OK ] md_MAIN_024
|
||||
[ OK ] md_MAIN_025
|
||||
[ OK ] md_MAIN_026
|
||||
[ OK ] md_MAIN_027
|
||||
[ OK ] md_MAIN_028
|
||||
[ OK ] md_MAIN_029
|
||||
[ OK ] md_MAIN_030
|
||||
[ OK ] ov_SC06_033
|
||||
[ OK ] ov_SC06_032
|
||||
[ OK ] md_MAIN_031
|
||||
[ OK ] md_MAIN_033
|
||||
[ OK ] md_MAIN_035
|
||||
[ OK ] md_MAIN_034
|
||||
[ OK ] md_MAIN_032
|
||||
[ OK ] md_MAIN_036
|
||||
[ OK ] md_MAIN_037
|
||||
[ OK ] ov_SC07_011
|
||||
[ OK ] ov_SC06_029
|
||||
[ OK ] md_MAIN_039
|
||||
[ OK ] md_MAIN_038
|
||||
[ OK ] md_MAIN_042
|
||||
[ OK ] md_MAIN_043
|
||||
[ OK ] md_MAIN_040
|
||||
[ OK ] md_MAIN_044
|
||||
[ OK ] md_MAIN_041
|
||||
[ OK ] md_MAIN_045
|
||||
[ OK ] md_MAIN_047
|
||||
[ OK ] ov_SC07_010
|
||||
[ OK ] md_MAIN_046
|
||||
[ OK ] md_MAIN_008
|
||||
[ OK ] md_MAIN_001
|
||||
[ OK ] md_MAIN_011
|
||||
[ OK ] ov_MAIN_012
|
||||
[ OK ] md_SC03_078
|
||||
[ OK ] md_SC03_073
|
||||
[ OK ] md_SC03_074
|
||||
[ OK ] md_SC03_075
|
||||
[ OK ] md_SC03_077
|
||||
[ OK ] ov_SC03_107
|
||||
[ OK ] md_SC03_076
|
||||
[ OK ] md_SC03_079
|
||||
[ OK ] md_SC03_132
|
||||
[ OK ] md_SC03_133
|
||||
[ OK ] md_SC03_137
|
||||
[ OK ] md_SC03_134
|
||||
[ OK ] md_SC03_136
|
||||
[ OK ] ov_SC07_007
|
||||
[ OK ] md_SC03_135
|
||||
[ OK ] md_SC04_025
|
||||
[ OK ] md_SC03_138
|
||||
[ OK ] md_MAIN_003
|
||||
[ OK ] md_SC04_024
|
||||
[ OK ] md_SC04_026
|
||||
[ OK ] md_SC04_029
|
||||
[ OK ] md_SC04_027
|
||||
[ OK ] md_SC04_028
|
||||
[ OK ] ov_SC07_006
|
||||
[ OK ] md_SC05_028
|
||||
[ OK ] md_SC05_024
|
||||
[ OK ] md_SC04_030
|
||||
[ OK ] md_SC05_023
|
||||
[ OK ] md_SC05_027
|
||||
[ OK ] md_SC05_025
|
||||
[ OK ] md_MAIN_007
|
||||
[ OK ] md_SC05_029
|
||||
[ OK ] md_SC03_053
|
||||
[ OK ] md_MAIN_009
|
||||
[ OK ] md_SC05_026
|
||||
[ OK ] md_SC03_056
|
||||
[ OK ] md_SC03_054
|
||||
[ OK ] ov_SC02_037
|
||||
[ OK ] md_SC02_009
|
||||
[ OK ] md_SC07_003
|
||||
[ OK ] md_SC07_004
|
||||
extract-all: 217 extracted, 0 failed of 217 (+ main, serial)
|
||||
[ OK ] resident
|
||||
[ OK ] ov_SC05_000
|
||||
[ OK ] ov_SC01_005
|
||||
[ OK ] ov_SC01_006
|
||||
[ OK ] ov_SC03_002
|
||||
[ OK ] ov_SC04_000
|
||||
[ OK ] ov_SC01_001
|
||||
[ OK ] ov_SC01_000
|
||||
[ OK ] ov_SC03_006
|
||||
[ OK ] ov_SC04_018
|
||||
[ OK ] ov_SC04_019
|
||||
[ OK ] ov_SC06_000
|
||||
[ OK ] ov_SC03_001
|
||||
[ OK ] ov_SC01_077
|
||||
[ OK ] ov_SC02_000
|
||||
[ OK ] ov_SC02_003
|
||||
[ OK ] ov_SC07_000
|
||||
[ OK ] ov_SC01_004
|
||||
[ OK ] ov_SC02_004
|
||||
[ OK ] ov_SC01_008
|
||||
[ OK ] ov_SC01_080
|
||||
[ OK ] ov_SC01_074
|
||||
[ OK ] ov_SC01_009
|
||||
[ OK ] ov_SC01_084
|
||||
[ OK ] ov_SC02_016
|
||||
[ OK ] ov_SC02_015
|
||||
[ OK ] ov_SC02_011
|
||||
[ OK ] ov_SC02_021
|
||||
[ OK ] ov_SC02_026
|
||||
[ OK ] ov_SC02_005
|
||||
[ OK ] ov_SC02_017
|
||||
[ OK ] ov_SC02_039
|
||||
[ OK ] ov_SC02_031
|
||||
[ OK ] ov_SC02_028
|
||||
[ OK ] ov_SC02_027
|
||||
[ OK ] ov_SC02_041
|
||||
[ OK ] ov_SC03_003
|
||||
[ OK ] ov_SC02_035
|
||||
[ OK ] ov_SC03_010
|
||||
[ OK ] ov_SC03_012
|
||||
[ OK ] ov_SC03_011
|
||||
[ OK ] ov_SC03_013
|
||||
[ OK ] ov_SC03_007
|
||||
[ OK ] ov_SC03_023
|
||||
[ OK ] ov_SC03_014
|
||||
[ OK ] ov_SC03_015
|
||||
[ OK ] ov_SC03_031
|
||||
[ OK ] ov_SC03_030
|
||||
[ OK ] ov_SC03_024
|
||||
[ OK ] ov_SC03_029
|
||||
[ OK ] ov_SC03_089
|
||||
[ OK ] ov_SC03_028
|
||||
[ OK ] ov_SC03_092
|
||||
[ OK ] ov_SC03_095
|
||||
[ OK ] ov_SC03_090
|
||||
[ OK ] ov_SC03_091
|
||||
[ OK ] ov_SC03_093
|
||||
[ OK ] ov_SC03_096
|
||||
[ OK ] ov_SC03_094
|
||||
[ OK ] main
|
||||
[ OK ] ov_SC03_097
|
||||
[ OK ] ov_SC03_098
|
||||
[ OK ] ov_SC03_100
|
||||
[ OK ] ov_SC03_099
|
||||
[ OK ] ov_SC03_101
|
||||
[ OK ] ov_SC03_102
|
||||
[ OK ] ov_SC03_108
|
||||
[ OK ] ov_SC03_103
|
||||
[ OK ] ov_SC03_109
|
||||
[ OK ] ov_SC03_113
|
||||
[ OK ] ov_SC03_110
|
||||
[ OK ] ov_SC03_111
|
||||
[ OK ] ov_SC03_104
|
||||
[ OK ] ov_SC03_114
|
||||
[ OK ] ov_SC03_112
|
||||
[ OK ] ov_SC03_105
|
||||
[ OK ] ov_SC03_115
|
||||
[ OK ] ov_SC03_116
|
||||
[ OK ] ov_SC03_117
|
||||
[ OK ] ov_SC03_126
|
||||
[ OK ] ov_SC03_121
|
||||
[ OK ] ov_SC03_125
|
||||
[ OK ] ov_SC04_003
|
||||
[ OK ] ov_SC03_118
|
||||
[ OK ] ov_SC04_008
|
||||
[ OK ] ov_SC04_004
|
||||
[ OK ] ov_SC04_002
|
||||
[ OK ] ov_SC03_119
|
||||
[ OK ] ov_SC03_124
|
||||
[ OK ] ov_SC04_006
|
||||
[ OK ] ov_SC04_005
|
||||
[ OK ] ov_SC04_009
|
||||
[ OK ] ov_SC04_007
|
||||
[ OK ] ov_SC04_010
|
||||
[ OK ] ov_SC04_012
|
||||
[ OK ] ov_SC04_016
|
||||
[ OK ] ov_SC04_021
|
||||
[ OK ] ov_SC05_006
|
||||
[ OK ] ov_SC04_015
|
||||
[ OK ] ov_SC04_011
|
||||
[ OK ] ov_SC05_002
|
||||
[ OK ] ov_SC05_003
|
||||
[ OK ] ov_SC04_020
|
||||
[ OK ] ov_SC05_004
|
||||
[ OK ] ov_SC05_007
|
||||
[ OK ] ov_SC05_001
|
||||
[ OK ] ov_SC05_005
|
||||
[ OK ] ov_SC05_009
|
||||
[ OK ] ov_SC05_008
|
||||
[ OK ] ov_SC05_010
|
||||
[ OK ] ov_SC05_011
|
||||
[ OK ] ov_SC05_019
|
||||
[ OK ] ov_SC06_010
|
||||
[ OK ] ov_SC05_018
|
||||
[ OK ] ov_SC06_011
|
||||
[ OK ] ov_SC06_008
|
||||
[ OK ] ov_SC06_013
|
||||
[ OK ] ov_SC06_015
|
||||
[ OK ] ov_SC06_006
|
||||
[ OK ] ov_SC06_014
|
||||
[ OK ] ov_SC06_016
|
||||
[ OK ] ov_SC05_017
|
||||
[ OK ] ov_SC06_020
|
||||
[ OK ] ov_SC06_018
|
||||
[ OK ] ov_SC06_022
|
||||
[ OK ] ov_SC06_024
|
||||
[ OK ] ov_SC06_025
|
||||
[ OK ] ov_SC06_027
|
||||
[ OK ] md_MAIN_013
|
||||
[ OK ] md_MAIN_014
|
||||
[ OK ] ov_SC07_009
|
||||
[ OK ] ov_SC07_008
|
||||
[ OK ] ov_SC07_001
|
||||
[ OK ] md_MAIN_015
|
||||
[ OK ] ov_SC06_030
|
||||
[ OK ] md_MAIN_017
|
||||
[ OK ] md_MAIN_018
|
||||
[ OK ] md_MAIN_016
|
||||
[ OK ] md_MAIN_019
|
||||
[ OK ] md_MAIN_021
|
||||
[ OK ] md_MAIN_023
|
||||
[ OK ] md_MAIN_020
|
||||
[ OK ] md_MAIN_022
|
||||
[ OK ] md_MAIN_024
|
||||
[ OK ] md_MAIN_026
|
||||
[ OK ] md_MAIN_025
|
||||
[ OK ] ov_SC07_002
|
||||
[ OK ] md_MAIN_027
|
||||
[ OK ] md_MAIN_028
|
||||
[ OK ] ov_SC06_029
|
||||
[ OK ] md_MAIN_029
|
||||
[ OK ] md_MAIN_031
|
||||
[ OK ] md_MAIN_030
|
||||
[ OK ] md_MAIN_033
|
||||
[ OK ] ov_SC06_032
|
||||
[ OK ] md_MAIN_032
|
||||
[ OK ] md_MAIN_035
|
||||
[ OK ] md_MAIN_034
|
||||
[ OK ] md_MAIN_037
|
||||
[ OK ] md_MAIN_038
|
||||
[ OK ] ov_SC07_006
|
||||
[ OK ] md_MAIN_040
|
||||
[ OK ] md_MAIN_036
|
||||
[ OK ] ov_SC07_007
|
||||
[ OK ] md_MAIN_039
|
||||
[ OK ] md_MAIN_041
|
||||
[ OK ] ov_SC07_010
|
||||
[ OK ] md_MAIN_042
|
||||
[ OK ] md_MAIN_043
|
||||
[ OK ] md_MAIN_047
|
||||
[ OK ] md_MAIN_046
|
||||
[ OK ] md_MAIN_001
|
||||
[ OK ] md_MAIN_045
|
||||
[ OK ] md_MAIN_008
|
||||
[ OK ] md_MAIN_044
|
||||
[ OK ] md_MAIN_011
|
||||
[ OK ] md_SC03_073
|
||||
[ OK ] md_SC03_075
|
||||
[ OK ] md_SC03_074
|
||||
[ OK ] md_SC03_079
|
||||
[ OK ] md_SC03_078
|
||||
[ OK ] md_SC03_132
|
||||
[ OK ] ov_MAIN_012
|
||||
[ OK ] md_SC03_077
|
||||
[ OK ] md_SC03_076
|
||||
[ OK ] md_SC07_003
|
||||
[ OK ] md_SC03_134
|
||||
[ OK ] md_SC03_133
|
||||
[ OK ] ov_SC07_011
|
||||
[ OK ] md_SC03_137
|
||||
[ OK ] md_SC04_024
|
||||
[ OK ] md_SC03_136
|
||||
[ OK ] md_SC04_025
|
||||
[ OK ] ov_SC06_033
|
||||
[ OK ] md_SC03_138
|
||||
[ OK ] md_SC04_026
|
||||
[ OK ] md_SC03_135
|
||||
[ OK ] md_SC05_023
|
||||
[ OK ] md_MAIN_003
|
||||
[ OK ] md_SC04_029
|
||||
[ OK ] md_SC04_030
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,13 +24,13 @@
|
||||
"gte_stsv": 145,
|
||||
"gte_stsxy": 361,
|
||||
"gte_stsxy3": 191,
|
||||
"gte_stsxy3_f3": 61,
|
||||
"gte_stsxy3c": 94,
|
||||
"gte_stsz3": 123,
|
||||
"gte_stsz4": 126,
|
||||
"gte_stszotz": 113,
|
||||
"lhu": 70,
|
||||
"lw": 80,
|
||||
"lwc2": 435,
|
||||
"lhu": 66,
|
||||
"lwc2": 432,
|
||||
"mfc2": 61,
|
||||
"swc2": 449
|
||||
},
|
||||
@@ -56,7 +56,9 @@
|
||||
"asm-body/direct": 13,
|
||||
"barrier/direct": 4576,
|
||||
"barrier/via-macro": 2,
|
||||
"gte/direct": 1140,
|
||||
"gte-lever/direct": 49,
|
||||
"gte-unsigned/direct": 3,
|
||||
"gte/direct": 1088,
|
||||
"gte/via-macro": 5751,
|
||||
"instruction/direct": 1690,
|
||||
"instruction/via-macro": 22,
|
||||
@@ -89,7 +91,9 @@
|
||||
"kinds": {
|
||||
"asm-body": 13,
|
||||
"barrier": 4578,
|
||||
"gte": 6891,
|
||||
"gte": 6839,
|
||||
"gte-lever": 49,
|
||||
"gte-unsigned": 3,
|
||||
"instruction": 1712,
|
||||
"keepalive": 2000,
|
||||
"launder": 4676,
|
||||
@@ -232,18 +236,27 @@
|
||||
}
|
||||
},
|
||||
"coverage_ok": true,
|
||||
"elapsed_s": 39.0,
|
||||
"elapsed_s": 50.8,
|
||||
"generated": "2026-09-09",
|
||||
"head": "f087282a3",
|
||||
"gte_levers": {
|
||||
"direct": 49,
|
||||
"marked": 0,
|
||||
"sites": 49,
|
||||
"unmarked": 49,
|
||||
"unsigned": 3,
|
||||
"via_macro": 0,
|
||||
"what": "GTE ops whose clobbers exceed the canonical macro's (a scheduling steer): levers for T7, outside the headline number"
|
||||
},
|
||||
"head": "917c68afd",
|
||||
"headers": 3181,
|
||||
"levers_AB": {
|
||||
"asm": 12979,
|
||||
"bodies": 12501,
|
||||
"distinct_bodies": 1728,
|
||||
"asm": 13028,
|
||||
"bodies": 12518,
|
||||
"distinct_bodies": 1745,
|
||||
"marked": 33625,
|
||||
"pins": 20646,
|
||||
"sites": 33625,
|
||||
"unmarked": 0,
|
||||
"sites": 33674,
|
||||
"unmarked": 49,
|
||||
"what": "register pins + asm statements excluding GTE ops: the classes the phase drives to 0"
|
||||
},
|
||||
"macro_definitions": {
|
||||
@@ -388,6 +401,17 @@
|
||||
"count": 0,
|
||||
"sample": []
|
||||
},
|
||||
"per_tu_asm_macro_definitions": {
|
||||
"by_kind": {
|
||||
"barrier": 1,
|
||||
"gte": 9102,
|
||||
"instruction": 9,
|
||||
"launder": 428
|
||||
},
|
||||
"gte_variants": 0,
|
||||
"total": 9540,
|
||||
"what": "asm-bearing #define blocks outside the GTE header: 0 at the close (T5 deletes the canonical duplicates, T7 the variants)"
|
||||
},
|
||||
"pins": {
|
||||
"bare_name": 25,
|
||||
"init": 2776,
|
||||
|
||||
@@ -6,24 +6,26 @@ lever_census: 218 binaries · 4,121 TUs + 3,181 headers · coverage OK · unclas
|
||||
coverage attribute raw 76 = live 76 + macro-block 0 + comment/dead 0
|
||||
class sites in-bodies file-scope bodies distinct TUs marked unmarked kinds
|
||||
A pins 20646 20646 0 10329 1330 2580 20646 0 {'pin': 20646}
|
||||
B asm 22552 22539 13 9758 1054 2289 13155 9397 {'gte': 6891, 'barrier': 4578, 'launder': 4676, 'instruction': 1712, 'keepalive': 2000, 'asm-body': 13, 'verbatim-body': 2682}
|
||||
B asm 22552 22539 13 9758 1054 2289 13155 9397 {'gte': 6839, 'barrier': 4578, 'gte-lever': 49, 'gte-unsigned': 3, 'launder': 4676, 'instruction': 1712, 'keepalive': 2000, 'asm-body': 13, 'verbatim-body': 2682}
|
||||
C volatile 1722 1570 152 628 97 612 14 1708 {'decl-body': 58, 'decl-file': 152, 'cast': 1510, 'param': 2}
|
||||
D register 50 50 0 47 47 6 0 50 {'register': 50}
|
||||
E asm-label 7428 1005 6423 737 117 2080 0 7428 {'asm-label': 7428}
|
||||
F builtin 445 445 0 428 27 302 0 445 {'builtin': 445}
|
||||
G attribute 76 1 75 1 1 40 0 76 {'attribute': 76}
|
||||
UNION A–D: 15,955 bodies · 2,023 distinct (addresses normalized) · 394 multi-copy classes holding 14,326 bodies · by kind {'ov': 15258, 'resident': 12, 'main': 180, 'md': 182, 'shared': 323}
|
||||
THE PHASE'S NUMBER (pins + asm statements, GTE excluded): 33,625 sites in 12,501 bodies (1,728 distinct) · marked !FAKE 33,625 · UNMARKED 0
|
||||
UNION A–D: 15,955 bodies · 2,023 distinct (addresses normalized) · 394 multi-copy classes holding 14,326 bodies · by kind {'ov': 15258, 'shared': 323, 'main': 180, 'md': 182, 'resident': 12}
|
||||
THE PHASE'S NUMBER (pins + asm statements, GTE excluded): 33,674 sites in 12,518 bodies (1,745 distinct) · marked !FAKE 33,625 · UNMARKED 49
|
||||
orphan !FAKE markers (no pin/asm site on the line nor below): 0
|
||||
GTE levers (clobbers beyond the canonical macro's): 49 sites (0 via a variant macro, 49 direct) · marked 0 · UNMARKED 49 · unsigned GTE statements 3
|
||||
per-TU asm macro definitions outside the GTE header: 9,540 {'gte': 9102, 'launder': 428, 'instruction': 9, 'barrier': 1} (GTE variants 0)
|
||||
pins: 20,646 · $0 1,321 · $sp 13 · with initializer 2,776 · volatile-qualified 134 · bare-name 25 · spellings {'__asm__': 20544, 'asm': 102}
|
||||
whole-body asm routines in C shells, manifest PERMANENT (hand asm, NOT levers): 22 routines · 2,682 sites (2,660 private copies + 22 shared headers); asm-bodies NOT permanent (levers): 13 site(s) ['func_8001E378:DECOMPILE-NOW', 'func_80020F34:DECOMPILE-NOW', 'func_800249F0:DECOMPILE-NOW', 'func_80025CBC:DECOMPILE-NOW', 'func_80026514:UNCERTAIN', 'func_800268D0:UNCERTAIN', 'func_80027058:DECOMPILE-NOW', 'func_80027200:DECOMPILE-NOW', 'func_800CBA44:DECOMPILE-NOW', 'func_8017D810:DECOMPILE-NOW', 'func_8017E26C:UNCERTAIN', 'func_80184440:DECOMPILE-NOW', 'func_801A3BCC:DECOMPILE-NOW']
|
||||
asm kinds: {'asm-body/direct': 13, 'barrier/direct': 4576, 'barrier/via-macro': 2, 'gte/direct': 1140, 'gte/via-macro': 5751, 'instruction/direct': 1690, 'instruction/via-macro': 22, 'keepalive/direct': 2000, 'launder/direct': 4236, 'launder/via-macro': 440, 'verbatim-body/direct': 2682}
|
||||
asm kinds: {'asm-body/direct': 13, 'barrier/direct': 4576, 'barrier/via-macro': 2, 'gte/direct': 1088, 'gte/via-macro': 5751, 'gte-lever/direct': 49, 'gte-unsigned/direct': 3, 'instruction/direct': 1690, 'instruction/via-macro': 22, 'keepalive/direct': 2000, 'launder/direct': 4236, 'launder/via-macro': 440, 'verbatim-body/direct': 2682}
|
||||
instruction mnemonics: {'addu': 966, 'move': 406, 'la': 142, 'lh': 134, 'addiu': 22, 'RTP_SND': 22, '.section': 7, 'lui': 4, 'and': 2, 'mult': 1, 'mfhi': 1, 'sll': 1, 'li': 1, 'lw': 1, 'nop': 1, 'srl': 1}
|
||||
gte mnemonics: {'gte_ldv0': 653, 'swc2': 449, 'lwc2': 435, 'gte_stsxy': 361, 'gte_rtps': 356, 'gte_stflg': 319, 'gte_rtpt': 289, 'gte_stsxy3': 191, 'gte_ldrgb': 162, 'gte_ldv3c': 159, 'gte_rt': 155, 'gte_SetRotMatrix': 150, 'gte_SetTransMatrix': 145, 'gte_stsv': 145, 'gte_stlvnl': 143, 'gte_ldv3': 140, 'gte_ldIR0z': 133, 'gte_ldIRGB': 133, 'gte_dpcl': 133, 'gte_stORGB': 133, 'gte_mvmva0': 131, 'gte_stsz4': 126, 'gte_stsz3': 123, 'gte_stszotz': 113, 'gte_nclip': 96, 'gte_stopz': 96, 'gte_stsxy3c': 94, 'lw': 80, 'lhu': 70, 'mfc2': 61}
|
||||
gte mnemonics: {'gte_ldv0': 653, 'swc2': 449, 'lwc2': 432, 'gte_stsxy': 361, 'gte_rtps': 356, 'gte_stflg': 319, 'gte_rtpt': 289, 'gte_stsxy3': 191, 'gte_ldrgb': 162, 'gte_ldv3c': 159, 'gte_rt': 155, 'gte_SetRotMatrix': 150, 'gte_SetTransMatrix': 145, 'gte_stsv': 145, 'gte_stlvnl': 143, 'gte_ldv3': 140, 'gte_ldIR0z': 133, 'gte_ldIRGB': 133, 'gte_dpcl': 133, 'gte_stORGB': 133, 'gte_mvmva0': 131, 'gte_stsz4': 126, 'gte_stsz3': 123, 'gte_stszotz': 113, 'gte_nclip': 96, 'gte_stopz': 96, 'gte_stsxy3c': 94, 'lhu': 66, 'mfc2': 61, 'gte_stsxy3_f3': 61}
|
||||
asm-bearing macro definitions: 9,540 (338 names, 32 with >1 text) kinds {'gte': 9102, 'launder': 428, 'instruction': 9, 'barrier': 1}
|
||||
controls (R39):
|
||||
src/800.c func_800226C0 pins got 14 expected 45 N-A
|
||||
src/shared/ov/func_80178004.h pins got 8 expected 26 N-A
|
||||
ov_SC03_006 func_80184034 bare-name pins got 0 expected 3 N-A
|
||||
engine_prelude.h asm sites (a macro definition only) got 0 expected 0 OK
|
||||
elapsed 39.0 s
|
||||
elapsed 50.8 s
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,142 @@
|
||||
{
|
||||
"lw $12, 0( $4 );lw $13, 4( $4 );ctc2 $12, $0;ctc2 $13, $1;lw $12, 8( $4 );lw $13, 12( $4 );lw $14, 16( $4 );ctc2 $12, $2;ctc2 $13, $3;ctc2 $14, $4": "00008c8c04008d8c0000cc480008cd4808008c8c0c008d8c10008e8c0010cc480018cd480020ce48",
|
||||
"lhu $12, 0( $4 );lhu $13, 6( $4 );lhu $14, 12( $4 );mtc2 $12, $9;mtc2 $13, $10;mtc2 $14, $11": "00008c9406008d940c008e9400488c4800508d4800588e48",
|
||||
"nop;nop;mvmva 1, 0, 3, 3, 0": "000000000000000012e0494a",
|
||||
"mfc2 $12, $9;mfc2 $13, $10;mfc2 $14, $11;sh $12, 0( $4 );sh $13, 6( $4 );sh $14, 12( $4 )": "00480c4800500d4800580e4800008ca406008da40c008ea4",
|
||||
"lw $12, 0($4);lw $13, 4($4);ctc2 $12, $0;ctc2 $13, $1;lw $12, 8($4);lw $13, 12($4);lw $14, 16($4);ctc2 $12, $2;ctc2 $13, $3;ctc2 $14, $4": "00008c8c04008d8c0000cc480008cd4808008c8c0c008d8c10008e8c0010cc480018cd480020ce48",
|
||||
"lhu $12, 0($4);lhu $13, 6($4);lhu $14, 12($4);mtc2 $12, $9;mtc2 $13, $10;mtc2 $14, $11": "00008c9406008d940c008e9400488c4800508d4800588e48",
|
||||
"nop; nop; mvmva 1, 0, 3, 3, 0": "000000000000000012e0494a",
|
||||
"mfc2 $12, $9;mfc2 $13, $10;mfc2 $14, $11;sh $12, 0($4);sh $13, 6($4);sh $14, 12($4)": "00480c4800500d4800580e4800008ca406008da40c008ea4",
|
||||
"lwc2 $0, 0( $4 );lwc2 $1, 4( $4 )": "000080c8040081c8",
|
||||
"lwc2 $0, 0( $4 );lwc2 $1, 4( $4 );lwc2 $2, 0( $5 );lwc2 $3, 4( $5 );lwc2 $4, 0( $6 );lwc2 $5, 4( $6 )": "000080c8040081c80000a2c80400a3c80000c4c80400c5c8",
|
||||
"nop;nop;rtps": "00000000000000000100184a",
|
||||
"nop;nop;rtpt": "00000000000000003000284a",
|
||||
"nop;nop;avsz4": "00000000000000002e00684b",
|
||||
"swc2 $14, 0( $4 )": "00008ee8",
|
||||
"swc2 $12, 0( $4 );swc2 $13, 0( $5 );swc2 $14, 0( $6 )": "00008ce80000ade80000cee8",
|
||||
"swc2 $7, 0( $4 )": "000087e8",
|
||||
"cfc2 $12, $31;nop;sw $12, 0( $4 )": "00f84c480000000000008cac",
|
||||
"nop;nop;mvmva 1, 0, 0, 0, 0": "00000000000000001200484a",
|
||||
"swc2 $25, 0( $4 );swc2 $26, 4( $4 );swc2 $27, 8( $4 )": "000099e804009ae808009be8",
|
||||
"lwc2 $6, 0( $4 )": "000086c8",
|
||||
"nop;nop;nccs": "00000000000000001b04084b",
|
||||
"swc2 $22, 0( $4 )": "000096e8",
|
||||
"lw $12, 20( $4 );lw $13, 24( $4 );ctc2 $12, $5;lw $14, 28( $4 );ctc2 $13, $6;ctc2 $14, $7": "14008c8c18008d8c0028cc481c008e8c0030cd480038ce48",
|
||||
"lwc2 $0, 0( $4 );lwc2 $1, 4( $4 );lwc2 $2, 0( $5 );lwc2 $3, 4( $5 );lwc2 $4, 0( $5 );lwc2 $5, 4( $5 )": "000080c8040081c80000a2c80400a3c80000a4c80400a5c8",
|
||||
"swc2 $12, 0( $4 )": "00008ce8",
|
||||
"swc2 $13, 0( $4 )": "00008de8",
|
||||
"swc2 $19, 0( $4 )": "000093e8",
|
||||
"mfc2 $12, $19;nop;sra $12, $12, 2;sw $12, 0( $4 )": "00980c480000000083600c0000008cac",
|
||||
"lwc2 $0, 0( $5 );lwc2 $1, 4( $5 );nop;nop;mvmva 1, 0, 0, 3, 0;swc2 $25, 0( $4 );swc2 $26, 4( $4 );swc2 $27, 8( $4 )": "0000a0c80400a1c800000000000000001260484a000099e804009ae808009be8",
|
||||
"nop;nop;mvmva 1, 0, 0, 3, 0": "00000000000000001260484a",
|
||||
"lhu $13, 4( $4 );lhu $12, 0( $4 );sll $13, $13, 16;or $12, $12, $13;mtc2 $12, $0;lwc2 $1, 8( $4 )": "04008d9400008c94006c0d0025608d0100008c48080081c8",
|
||||
"lb $13, 1( $4 );lb $12, 0( $4 );sll $13, $13, 16;sll $12, $12, 16;srl $12, $12, 16;or $12, $12, $13;lb $13, 2( $4 );mtc2 $12, $0;mtc2 $13, $1": "01008d8000008c80006c0d0000640c0002640c0025608d0102008d8000008c4800088d48",
|
||||
"lwc2 $0, 0($4)\nlwc2 $1, 4($4)\n": "000080c8040081c8",
|
||||
"nop\nnop\nmvmva 1, 0, 0, 0, 0\n": "00000000000000001200484a",
|
||||
"swc2 $25, 0($4)\nswc2 $26, 4($4)\nswc2 $27, 8($4)\n": "000099e804009ae808009be8",
|
||||
"cfc2 $12, $31\nnop\nsw $12, 0($4)\n": "00f84c480000000000008cac",
|
||||
"nop;nop;nclip": "00000000000000000600404b",
|
||||
"swc2 $24, 0( $4 )": "000098e8",
|
||||
"lw $12, 20($4);lw $13, 24($4);ctc2 $12, $5;lw $14, 28($4);ctc2 $13, $6;ctc2 $14, $7": "14008c8c18008d8c0028cc481c008e8c0030cd480038ce48",
|
||||
"swc2 $12, 8( $4 );swc2 $13, 12( $4 );swc2 $14, 16( $4 )": "08008ce80c008de810008ee8",
|
||||
"nop;nop;avsz3": "00000000000000002d00584b",
|
||||
"cfc2 $12, $31;nop;sw $12, 0( $4 )": "00f84c480000000000008cac",
|
||||
"swc2 $12, 8( $4 );swc2 $13, 16( $4 );swc2 $14, 24( $4 )": "08008ce810008de818008ee8",
|
||||
"swc2 $12, 8( $4 );swc2 $13, 16( $4 );swc2 $14, 24( $4 )": "08008ce810008de818008ee8",
|
||||
"nop;nop;ncct": "00000000000000003f04184b",
|
||||
"swc2 $17, 0( $4 );swc2 $18, 0( $5 );swc2 $19, 0( $6 )": "000091e80000b2e80000d3e8",
|
||||
"swc2 $20, 4( $4 );swc2 $21, 12( $4 );swc2 $22, 20( $4 )": "040094e80c0095e8140096e8",
|
||||
"lwc2 $0, 0($4);lwc2 $1, 4($4)": "000080c8040081c8",
|
||||
"lwc2 $0, 0($4);lwc2 $1, 4($4);lwc2 $2, 0($5);lwc2 $3, 4($5);lwc2 $4, 0($6);lwc2 $5, 4($6)": "000080c8040081c80000a2c80400a3c80000c4c80400c5c8",
|
||||
"lwc2 $6, 0($4)": "000086c8",
|
||||
"nop;nop;cop2 0x0280030": "00000000000000003000284a",
|
||||
"nop;nop;cop2 0x0180001": "00000000000000000100184a",
|
||||
"nop;nop;cop2 0x1400006": "00000000000000000600404b",
|
||||
"nop;nop;cop2 0x168002e": "00000000000000002e00684b",
|
||||
"nop;nop;cop2 0x118043f": "00000000000000003f04184b",
|
||||
"nop;nop;cop2 0x108041b": "00000000000000001b04084b",
|
||||
"cfc2 $12, $31;nop;sw $12, 0($4)": "00f84c480000000000008cac",
|
||||
"swc2 $24, 0($4)": "000098e8",
|
||||
"swc2 $7, 0($4)": "000087e8",
|
||||
"swc2 $14, 0($4)": "00008ee8",
|
||||
"swc2 $22, 0($4)": "000096e8",
|
||||
"swc2 $12, 8($4);swc2 $13, 16($4);swc2 $14, 24($4)": "08008ce810008de818008ee8",
|
||||
"swc2 $20, 4($4);swc2 $21, 12($4);swc2 $22, 20($4)": "040094e80c0095e8140096e8",
|
||||
"swc2 $16, 0($4);swc2 $17, 0($5);swc2 $18, 0($6);swc2 $19, 0($7)": "000090e80000b1e80000d2e80000f3e8",
|
||||
"nop;nop;.word 0x4A280030": "00000000000000003000284a",
|
||||
"nop;nop;.word 0x4B400006": "00000000000000000600404b",
|
||||
"nop;nop;.word 0x4B58002D": "00000000000000002d00584b",
|
||||
"nop;nop;.word 0x4B08041B": "00000000000000001b04084b",
|
||||
"swc2 $16, 0( $4 );swc2 $17, 0( $5 );swc2 $18, 0( $6 );swc2 $19, 0( $7 )": "000090e80000b1e80000d2e80000f3e8",
|
||||
"swc2 $20, 0( $4 );swc2 $21, 0( $5 );swc2 $22, 0( $6 )": "000094e80000b5e80000d6e8",
|
||||
"swc2 $12, 8( $4 );swc2 $13, 20( $4 );swc2 $14, 32( $4 )": "08008ce814008de820008ee8",
|
||||
"nop;nop;.word 0x4a180001": "00000000000000000100184a",
|
||||
"nop;nop;.word 0x4a280030": "00000000000000003000284a",
|
||||
"nop;nop;.word 0x4b400006": "00000000000000000600404b",
|
||||
"nop;nop;.word 0x4b68002e": "00000000000000002e00684b",
|
||||
"mfc2 $12, $9\nmfc2 $13, $10\nmfc2 $14, $11\nsh $12, 0($4)\nsh $13, 2($4)\nsh $14, 4($4)\n": "00480c4800500d4800580e4800008ca402008da404008ea4",
|
||||
"lw $12, 0($4)\nlw $13, 4($4)\nctc2 $12, $0\nctc2 $13, $1\nlw $12, 8($4)\nlw $13, 12($4)\nlw $14, 16($4)\nctc2 $12, $2\nctc2 $13, $3\nctc2 $14, $4\n": "00008c8c04008d8c0000cc480008cd4808008c8c0c008d8c10008e8c0010cc480018cd480020ce48",
|
||||
"nop\nnop\nmvmva 1, 0, 0, 3, 0\n": "00000000000000001260484a",
|
||||
"lwc2 $0, 0($4)\nlwc2 $1, 4($4)": "000080c8040081c8",
|
||||
"nop\nnop\nrtps": "00000000000000000100184a",
|
||||
"lw $12, 20($4)\nlw $13, 24($4)\nctc2 $12, $5\nlw $14, 28($4)\nctc2 $13, $6\nctc2 $14, $7\n": "14008c8c18008d8c0028cc481c008e8c0030cd480038ce48",
|
||||
"lhu $12, 0($4)\nlhu $13, 6($4)\nlhu $14, 12($4)\nmtc2 $12, $9\nmtc2 $13, $10\nmtc2 $14, $11\nnop\nnop\nmvmva 1, 0, 3, 3, 0\nmfc2 $12, $9\nmfc2 $13, $10\nmfc2 $14, $11\nsh $12, 0($4)\nsh $13, 6($4)\nsh $14, 12($4)\n": "00008c9406008d940c008e9400488c4800508d4800588e48000000000000000012e0494a00480c4800500d4800580e4800008ca406008da40c008ea4",
|
||||
"lhu $13, 4($4)\nlhu $12, 0($4)\nsll $13, $13, 16\nor $12, $12, $13\nmtc2 $12, $0\nlwc2 $1, 8($4)\nnop\nnop\nmvmva 1, 0, 0, 0, 0\nswc2 $25, 0($4)\nswc2 $26, 4($4)\nswc2 $27, 8($4)\n": "04008d9400008c94006c0d0025608d0100008c48080081c800000000000000001200484a000099e804009ae808009be8",
|
||||
"lwc2 $0, 0($4)\nlwc2 $1, 4($4)\nlwc2 $2, 0($5)\nlwc2 $3, 4($5)\nlwc2 $4, 0($6)\nlwc2 $5, 4($6)\n": "000080c8040081c80000a2c80400a3c80000c4c80400c5c8",
|
||||
"nop\nnop\nrtpt": "00000000000000003000284a",
|
||||
"nop\nnop\navsz4": "00000000000000002e00684b",
|
||||
"swc2 $12, 0($4)\nswc2 $13, 0($5)\nswc2 $14, 0($6)": "00008ce80000ade80000cee8",
|
||||
"cfc2 $12, $31\nnop\nsw $12, 0($4)": "00f84c480000000000008cac",
|
||||
"mtc2 $0, $8": "00408048",
|
||||
"lwc2 $28, 0($4)": "00009cc8",
|
||||
"nop\n\nnop\n\ndpcl": "00000000000000002900684a",
|
||||
"swc2 $29, 0($4)": "00009de8",
|
||||
"lwc2 $0, 0( $4 );lwc2 $1, 4( $4 );lwc2 $2, 8( $4 );lwc2 $3, 12( $4 );lwc2 $4, 16( $4 );lwc2 $5, 20( $4 )": "000080c8040081c8080082c80c0083c8100084c8140085c8",
|
||||
"swc2 $12, 0( $4 );swc2 $13, 4( $4 );swc2 $14, 8( $4 )": "00008ce804008de808008ee8",
|
||||
"swc2 $12, 0( $4 );swc2 $13, 0( $5 )": "00008ce80000ade8",
|
||||
"nop;nop;.word 0x4A480012": "00000000000000001200484a",
|
||||
"lw $12, 0( $4 );lw $13, 4( $4 );ctc2 $12, $0;ctc2 $13, $1;lw $12, 8( $4 );lw $13, 12( $4 );lw $14, 16( $4 );ctc2 $12, $2;ctc2 $13, $3;ctc2 $14, $4;lw $12, 20( $4 );lw $13, 24( $4 );ctc2 $12, $5;lw $14, 28( $4 );ctc2 $13, $6;ctc2 $14, $7": "00008c8c04008d8c0000cc480008cd4808008c8c0c008d8c10008e8c0010cc480018cd480020ce4814008c8c18008d8c0028cc481c008e8c0030cd480038ce48",
|
||||
"lhu $12, 0( $4 );lhu $13, 6( $4 );lhu $14, 12( $4 );mtc2 $12, $9;mtc2 $13, $10;mtc2 $14, $11;nop;nop;mvmva 1, 0, 3, 3, 0;mfc2 $12, $9;mfc2 $13, $10;mfc2 $14, $11;sh $12, 0( $4 );sh $13, 6( $4 );sh $14, 12( $4 )": "00008c9406008d940c008e9400488c4800508d4800588e48000000000000000012e0494a00480c4800500d4800580e4800008ca406008da40c008ea4",
|
||||
"lhu $13, 4( $4 );lhu $12, 0( $4 );sll $13, $13, 16;or $12, $12, $13;mtc2 $12, $0;lwc2 $1, 8( $4 );nop;nop;mvmva 1, 0, 0, 0, 0;swc2 $25, 0( $4 );swc2 $26, 4( $4 );swc2 $27, 8( $4 )": "04008d9400008c94006c0d0025608d0100008c48080081c800000000000000001200484a000099e804009ae808009be8",
|
||||
"lw $12, 0($4);lw $13, 4($4);ctc2 $12, $0;ctc2 $13, $1;lw $12, 8($4);lw $13, 12($4);lw $14, 16($4);ctc2 $12, $2;ctc2 $13, $3;ctc2 $14, $4;addiu $2, $sp, 16;lhu $12, 0($2);lhu $13, 6($2);lhu $14, 12($2);mtc2 $12, $9;mtc2 $13, $10;mtc2 $14, $11;nop;nop;mvmva 1, 0, 3, 3, 0;mfc2 $12, $9;mfc2 $13, $10;mfc2 $14, $11;sh $12, 0($4);sh $13, 6($4);sh $14, 12($4);addiu $2, $sp, 18;lhu $12, 0($2);lhu $13, 6($2);lhu $14, 12($2);mtc2 $12, $9;mtc2 $13, $10;mtc2 $14, $11;nop;nop;mvmva 1, 0, 3, 3, 0;addiu $2, $4, 2;mfc2 $12, $9;mfc2 $13, $10;mfc2 $14, $11;sh $12, 0($2);sh $13, 6($2);sh $14, 12($2);addiu $2, $sp, 20;lhu $12, 0($2);lhu $13, 6($2);lhu $14, 12($2);mtc2 $12, $9;mtc2 $13, $10;mtc2 $14, $11;nop;nop;mvmva 1, 0, 3, 3, 0;addiu $2, $4, 4;mfc2 $12, $9;mfc2 $13, $10;mfc2 $14, $11;sh $12, 0($2);sh $13, 6($2);sh $14, 12($2)": "00008c8c04008d8c0000cc480008cd4808008c8c0c008d8c10008e8c0010cc480018cd480020ce481000a22700004c9406004d940c004e9400488c4800508d4800588e48000000000000000012e0494a00480c4800500d4800580e4800008ca406008da40c008ea41200a22700004c9406004d940c004e9400488c4800508d4800588e48000000000000000012e0494a0200822400480c4800500d4800580e4800004ca406004da40c004ea41400a22700004c9406004d940c004e9400488c4800508d4800588e48000000000000000012e0494a0400822400480c4800500d4800580e4800004ca406004da40c004ea4",
|
||||
"lwc2 $9, 0($4)\n\nlwc2 $10, 4($4)\n\nlwc2 $11, 8($4)": "000089c804008ac808008bc8",
|
||||
"nop\n\nnop\n\nsqr 0": "00000000000000002804a04a",
|
||||
"swc2 $25, 0($4)\n\nswc2 $26, 4($4)\n\nswc2 $27, 8($4)": "000099e804009ae808009be8",
|
||||
"lw $12, 0($4);lw $13, 4($4);ctc2 $12, $0;ctc2 $13, $1;lw $12, 8($4);lw $13, 12($4);lw $14, 16($4);ctc2 $12, $2;ctc2 $13, $3;ctc2 $14, $4;lhu $12, 0($5);lhu $13, 6($5);lhu $14, 12($5);mtc2 $12, $9;mtc2 $13, $10;mtc2 $14, $11;nop;nop;mvmva 1, 0, 3, 3, 0;mfc2 $12, $9;mfc2 $13, $10;mfc2 $14, $11;sh $12, 0($4);sh $13, 6($4);sh $14, 12($4);addiu $2, $sp, 50;lhu $12, 0($2);lhu $13, 6($2);lhu $14, 12($2);mtc2 $12, $9;mtc2 $13, $10;mtc2 $14, $11;nop;nop;mvmva 1, 0, 3, 3, 0;addiu $2, $sp, 18;mfc2 $12, $9;mfc2 $13, $10;mfc2 $14, $11;sh $12, 0($2);sh $13, 6($2);sh $14, 12($2);addiu $2, $sp, 52;lhu $12, 0($2);lhu $13, 6($2);lhu $14, 12($2);mtc2 $12, $9;mtc2 $13, $10;mtc2 $14, $11;nop;nop;mvmva 1, 0, 3, 3, 0;addiu $2, $sp, 20;mfc2 $12, $9;mfc2 $13, $10;mfc2 $14, $11;sh $12, 0($2);sh $13, 6($2);sh $14, 12($2)": "00008c8c04008d8c0000cc480008cd4808008c8c0c008d8c10008e8c0010cc480018cd480020ce480000ac940600ad940c00ae9400488c4800508d4800588e48000000000000000012e0494a00480c4800500d4800580e4800008ca406008da40c008ea43200a22700004c9406004d940c004e9400488c4800508d4800588e48000000000000000012e0494a1200a22700480c4800500d4800580e4800004ca406004da40c004ea43400a22700004c9406004d940c004e9400488c4800508d4800588e48000000000000000012e0494a1400a22700480c4800500d4800580e4800004ca406004da40c004ea4",
|
||||
"lw $12, 0($5);lw $13, 4($5);ctc2 $12, $0;ctc2 $13, $1;lw $12, 8($5);lw $13, 12($5);lw $14, 16($5);ctc2 $12, $2;ctc2 $13, $3;ctc2 $14, $4;lhu $12, 0($4);lhu $13, 6($4);lhu $14, 12($4);mtc2 $12, $9;mtc2 $13, $10;mtc2 $14, $11;nop;nop;mvmva 1, 0, 3, 3, 0;mfc2 $12, $9;mfc2 $13, $10;mfc2 $14, $11;sh $12, 0($5);sh $13, 6($5);sh $14, 12($5);addiu $2, $sp, 50;lhu $12, 0($2);lhu $13, 6($2);lhu $14, 12($2);mtc2 $12, $9;mtc2 $13, $10;mtc2 $14, $11;nop;nop;mvmva 1, 0, 3, 3, 0;addiu $2, $sp, 18;mfc2 $12, $9;mfc2 $13, $10;mfc2 $14, $11;sh $12, 0($2);sh $13, 6($2);sh $14, 12($2);addiu $2, $sp, 52;lhu $12, 0($2);lhu $13, 6($2);lhu $14, 12($2);mtc2 $12, $9;mtc2 $13, $10;mtc2 $14, $11;nop;nop;mvmva 1, 0, 3, 3, 0;addiu $2, $sp, 20;mfc2 $12, $9;mfc2 $13, $10;mfc2 $14, $11;sh $12, 0($2);sh $13, 6($2);sh $14, 12($2)": "0000ac8c0400ad8c0000cc480008cd480800ac8c0c00ad8c1000ae8c0010cc480018cd480020ce4800008c9406008d940c008e9400488c4800508d4800588e48000000000000000012e0494a00480c4800500d4800580e480000aca40600ada40c00aea43200a22700004c9406004d940c004e9400488c4800508d4800588e48000000000000000012e0494a1200a22700480c4800500d4800580e4800004ca406004da40c004ea43400a22700004c9406004d940c004e9400488c4800508d4800588e48000000000000000012e0494a1400a22700480c4800500d4800580e4800004ca406004da40c004ea4",
|
||||
"lw $12, 0($4);lw $13, 4($4);ctc2 $12, $0;ctc2 $13, $1;lw $12, 8($4);lw $13, 12($4);lw $14, 16($4);ctc2 $12, $2;ctc2 $13, $3;ctc2 $14, $4;": "00008c8c04008d8c0000cc480008cd4808008c8c0c008d8c10008e8c0010cc480018cd480020ce48",
|
||||
"lhu $12, 0($4);lhu $13, 6($4);lhu $14, 12($4);mtc2 $12, $9;mtc2 $13, $10;mtc2 $14, $11;": "00008c9406008d940c008e9400488c4800508d4800588e48",
|
||||
"mfc2 $12, $9;mfc2 $13, $10;mfc2 $14, $11;sh $12, 0($4);sh $13, 6($4);sh $14, 12($4);": "00480c4800500d4800580e4800008ca406008da40c008ea4",
|
||||
".set\nnoreorder\nlw $12, 0($4)\nlw $13, 4($4)\nctc2 $12, $0\nctc2 $13, $1\nlw $12, 8($4)\nlw $13, 12($4)\nlw $14, 16($4)\nctc2 $12, $2\nctc2 $13, $3\nctc2 $14, $4\nlwc2 $0, 0($5)\nlwc2 $1, 4($5)\nnop\nnop\nmvmva 1, 0, 0, 3, 0\nswc2 $25, 0($6)\nswc2 $26, 4($6)\nswc2 $27, 8($6)\n.set\nreorder\n": "ERROR: {standard input}:36: Error: unrecognized opcode `reorder'",
|
||||
"swc2 $8, 0( $4 )": "000088e8",
|
||||
"lw $12, 0($4);lw $13, 4($4);ctc2 $12, $0;ctc2 $13, $1;lw $12, 8($4);lw $13, 12($4);lw $14, 16($4);ctc2 $12, $2;ctc2 $13, $3;ctc2 $14, $4;addiu $2, $sp, 0x30;lhu $12, 0($2);lhu $13, 6($2);lhu $14, 12($2);mtc2 $12, $9;mtc2 $13, $10;mtc2 $14, $11;nop;nop;mvmva 1, 0, 3, 3, 0;mfc2 $12, $9;mfc2 $13, $10;mfc2 $14, $11;sh $12, 0($4);sh $13, 6($4);sh $14, 12($4);addiu $2, $sp, 0x32;lhu $12, 0($2);lhu $13, 6($2);lhu $14, 12($2);mtc2 $12, $9;mtc2 $13, $10;mtc2 $14, $11;nop;nop;mvmva 1, 0, 3, 3, 0;addiu $2, $sp, 0x12;mfc2 $12, $9;mfc2 $13, $10;mfc2 $14, $11;sh $12, 0($2);sh $13, 6($2);sh $14, 12($2);addiu $2, $sp, 0x34;lhu $12, 0($2);lhu $13, 6($2);lhu $14, 12($2);mtc2 $12, $9;mtc2 $13, $10;mtc2 $14, $11;nop;nop;mvmva 1, 0, 3, 3, 0;addiu $2, $sp, 0x14;mfc2 $12, $9;mfc2 $13, $10;mfc2 $14, $11;sh $12, 0($2);sh $13, 6($2);sh $14, 12($2);": "00008c8c04008d8c0000cc480008cd4808008c8c0c008d8c10008e8c0010cc480018cd480020ce483000a22700004c9406004d940c004e9400488c4800508d4800588e48000000000000000012e0494a00480c4800500d4800580e4800008ca406008da40c008ea43200a22700004c9406004d940c004e9400488c4800508d4800588e48000000000000000012e0494a1200a22700480c4800500d4800580e4800004ca406004da40c004ea43400a22700004c9406004d940c004e9400488c4800508d4800588e48000000000000000012e0494a1400a22700480c4800500d4800580e4800004ca406004da40c004ea4",
|
||||
"lw $12, 0($4);lw $13, 4($4);ctc2 $12, $0;ctc2 $13, $1;lw $12, 8($4);lw $13, 12($4);lw $14, 16($4);ctc2 $12, $2;ctc2 $13, $3;ctc2 $14, $4;lw $12, 20($4);lw $13, 24($4);ctc2 $12, $5;lw $14, 28($4);ctc2 $13, $6;ctc2 $14, $7": "00008c8c04008d8c0000cc480008cd4808008c8c0c008d8c10008e8c0010cc480018cd480020ce4814008c8c18008d8c0028cc481c008e8c0030cd480038ce48",
|
||||
"lhu $12, 0($4)\nlhu $13, 6($4)\nlhu $14, 12($4)\nmtc2 $12, $9\nmtc2 $13, $10\nmtc2 $14, $11\nnop\nnop\nmvmva 1, 0, 3, 3, 0\n": "00008c9406008d940c008e9400488c4800508d4800588e48000000000000000012e0494a",
|
||||
"mfc2 $12, $9\nmfc2 $13, $10\nmfc2 $14, $11\nsh $12, 0($4)\nsh $13, 6($4)\nsh $14, 12($4)\n": "00480c4800500d4800580e4800008ca406008da40c008ea4",
|
||||
"lhu $13, 4($4)\nlhu $12, 0($4)\nsll $13, $13, 16\nor $12, $12, $13\nmtc2 $12, $0\nlwc2 $1, 8($4)\nnop\nnop\nmvmva 1, 0, 0, 0, 0\n": "04008d9400008c94006c0d0025608d0100008c48080081c800000000000000001200484a",
|
||||
"swc2 $8, 0($4)": "000088e8",
|
||||
"mfc2 $12, $19;nop;sra $12, $12, 2;sw $12, 0($4)": "00980c480000000083600c0000008cac",
|
||||
"lhu $12, 0( $4 );lhu $13, 6( $4 );lhu $14, 12( $4 );mtc2 $12, $9;mtc2 $13, $10;mtc2 $14, $11;nop;nop;mvmva 1, 0, 3, 3, 0": "00008c9406008d940c008e9400488c4800508d4800588e48000000000000000012e0494a",
|
||||
"lwc2 $9, 0($4)\nlwc2 $10, 4($4)\nlwc2 $11, 8($4)\nnop\nnop\nsqr 0\n": "000089c804008ac808008bc800000000000000002804a04a",
|
||||
"lwc2 $0, 0($4)\nlwc2 $1, 4($4)\nnop\nnop\nmvmva 1, 0, 0, 0, 0\n": "000080c8040081c800000000000000001200484a",
|
||||
"lwc2 $0, 0($4)\nlwc2 $1, 4($4)\nnop\nnop\nmvmva 1, 0, 0, 3, 0\n": "000080c8040081c800000000000000001260484a",
|
||||
"lw $12, 0($4)\nlw $13, 4($4)\nctc2 $12, $0\nctc2 $13, $1\nlw $12, 8($4)\nlw $13, 12($4)\nlw $14, 16($4)\nctc2 $12, $2\nctc2 $13, $3\nctc2 $14, $4\nlw $12, 20($4)\nlw $13, 24($4)\nctc2 $12, $5\nlw $14, 28($4)\nctc2 $13, $6\nctc2 $14, $7\n": "00008c8c04008d8c0000cc480008cd4808008c8c0c008d8c10008e8c0010cc480018cd480020ce4814008c8c18008d8c0028cc481c008e8c0030cd480038ce48",
|
||||
"lwc2 $0, 0($4)\n\nlwc2 $1, 4($4)": "000080c8040081c8",
|
||||
"nop\n\nnop\n\nrtps": "00000000000000000100184a",
|
||||
"cfc2 $12, $31\n\nnop\n\nsw $12, 0($4)": "00f84c480000000000008cac",
|
||||
"lw $12, 0($2);lw $13, 4($2);ctc2 $12, $0;ctc2 $13, $1;lw $12, 8($2);lw $13, 12($2);lw $14, 16($2);ctc2 $12, $2;ctc2 $13, $3;ctc2 $14, $4;addiu $2, $sp, 0x10;lhu $12, 0($2);lhu $13, 6($2);lhu $14, 12($2);mtc2 $12, $9;mtc2 $13, $10;mtc2 $14, $11;nop;nop;mvmva 1, 0, 3, 3, 0;mfc2 $12, $9;mfc2 $13, $10;mfc2 $14, $11;sh $12, 0($2);sh $13, 6($2);sh $14, 12($2);addiu $2, $sp, 0x12;lhu $12, 0($2);lhu $13, 6($2);lhu $14, 12($2);mtc2 $12, $9;mtc2 $13, $10;mtc2 $14, $11;nop;nop;mvmva 1, 0, 3, 3, 0;addiu $2, $2, 0x2;mfc2 $12, $9;mfc2 $13, $10;mfc2 $14, $11;sh $12, 0($2);sh $13, 6($2);sh $14, 12($2);addiu $2, $sp, 0x14;lhu $12, 0($2);lhu $13, 6($2);lhu $14, 12($2);mtc2 $12, $9;mtc2 $13, $10;mtc2 $14, $11;nop;nop;mvmva 1, 0, 3, 3, 0;addiu $2, $2, 4;mfc2 $12, $9;mfc2 $13, $10;mfc2 $14, $11;sh $12, 0($2);sh $13, 6($2);sh $14, 12($2)": "00004c8c04004d8c0000cc480008cd4808004c8c0c004d8c10004e8c0010cc480018cd480020ce481000a22700004c9406004d940c004e9400488c4800508d4800588e48000000000000000012e0494a00480c4800500d4800580e4800004ca406004da40c004ea41200a22700004c9406004d940c004e9400488c4800508d4800588e48000000000000000012e0494a0200422400480c4800500d4800580e4800004ca406004da40c004ea41400a22700004c9406004d940c004e9400488c4800508d4800588e48000000000000000012e0494a0400422400480c4800500d4800580e4800004ca406004da40c004ea4",
|
||||
"lui $2,%%hi(D_800AF648);addiu $2,$2,%%lo(D_800AF648);lw $12,0($2);lw $13,4($2);ctc2 $12,$0;ctc2 $13,$1;lw $12,8($2);lw $13,12($2);lw $14,16($2);ctc2 $12,$2;ctc2 $13,$3;ctc2 $14,$4;lw $12,20($2);lw $13,24($2);ctc2 $12,$5;lw $14,28($2);ctc2 $13,$6;ctc2 $14,$7": "ERROR: {standard input}:10: Error: operand 3 must be constant `addiu $2,$2,%%lo(D_800AF648)'",
|
||||
"lwc2 $0,0($4);lwc2 $1,4($4);nop;nop;rtps;swc2 $14,0($4)": "000080c8040081c800000000000000000100184a00008ee8",
|
||||
"cfc2 $12,$31;nop;sw $12,0($4)": "00f84c480000000000008cac",
|
||||
"lwc2 $9, 0($sp)\nlwc2 $10, 4($sp)\nlwc2 $11, 8($sp)\nnop\nnop\nsqr 0\n": "0000a9cb0400aacb0800abcb00000000000000002804a04a",
|
||||
"swc2 $25, 0($sp)\nswc2 $26, 4($sp)\nswc2 $27, 8($sp)\n": "0000b9eb0400baeb0800bbeb",
|
||||
"lw $2, 0(%3);lw $3, 4(%3);ctc2 $2, $0;ctc2 $3, $1;lw $2, 8(%3);lw $3, 12(%3);lw $4, 16(%3);ctc2 $2, $2;ctc2 $3, $3;ctc2 $4, $4;lw $2, 20(%3);lw $3, 24(%3);ctc2 $2, $5;lw $4, 28(%3);ctc2 $3, $6;ctc2 $4, $7": "ERROR: {standard input}:10: Error: invalid operands `lw $4,28(%3)'",
|
||||
"lwc2 $0, 0($4);lwc2 $1, 4($4);nop;nop;rtps": "000080c8040081c800000000000000000100184a",
|
||||
"cfc2 $2, $31; nop; sw $2, 0($4)": "00f8424800000000000082ac",
|
||||
"lwc2 $9, 0( $4 );lwc2 $10, 4( $4 );lwc2 $11, 8( $4 );nop;nop;sqr 0;swc2 $25, 0( $4 );swc2 $26, 4( $4 );swc2 $27, 8( $4 )": "000089c804008ac808008bc800000000000000002804a04a000099e804009ae808009be8",
|
||||
"nop\nnop\nmvmva 1, 0, 0, 3, 0": "00000000000000001260484a",
|
||||
"mfc2 $12, $9\nmfc2 $13, $10\nmfc2 $14, $11\nsh $12, 0($4)\nsh $13, 2($4)\nsh $14, 4($4)": "00480c4800500d4800580e4800008ca402008da404008ea4",
|
||||
"swc2 $19, 0($4)": "000093e8",
|
||||
"mfc2 $12, $19\nnop\nsra $12, $12, 2\nsw $12, 0($4)\n": "00980c480000000083600c0000008cac",
|
||||
"lwc2 $9, 0($4)\nlwc2 $10, 4($4)\nlwc2 $11, 8($4)\nnop\nnop\nsqr 1\n": "000089c804008ac808008bc800000000000000002804a84a"
|
||||
}
|
||||
@@ -71,6 +71,7 @@ tools/dedup_integrate.py P6 ADAPT validate a code-share registry byte-honestly,
|
||||
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 and campaign tool: positional rewrites per lever class (pin -> plain declaration, barrier/keep-alive deleted, a launder deleted or turned into the assignment it is, a hand-placed instruction -> its C, the zero-register variable -> 0, a macro-carried site deleted/valued/refused by its macro's shape, volatile/register dropped); per body replay -> rung A strip-all -> rung B greedy through delever_oracle; the file is the write unit and its final compile through every recipe the proof; --plan/--apply batches (TUs parallel, exemplar files first; headers serial, includers parallel), the ledger keyed by tu+fn+addr with the body's nhash before/after, !FAKE markers on class A/B survivors, --redraw for bodies refused by an older tool, --restore from inflight.json, --scrub for orphan markers, --apply-body for T6/T7, --selftest with a stub oracle, --probe (T2) 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/gte_consolidate.py P10 ADAPT consolidate the GTE coprocessor inline-asm macros under Sony's names in one header and sweep dead lever macros The Phase-36 T5 tool: every asm-bearing macro definition and direct GTE statement SIGNED by the build's own maspsx→as tail (template bytes with operands bound to fixed registers, operand counts, clobbers); one canonical text per signature named by PsyQ's inline_c.h convention (Sony's file supplies names and clobber lists, never opcode words) written to include/gte_inline.h; per file the canonical duplicates deleted, private names renamed, a variant with extra clobbers tried as canonical and kept as a marked <name>_m lever only when the object differs, direct statements rewritten into canonical calls (a concatenation of two included); every file judged through every recipe; --sweep deletes dead asm-bearing macros and drops a compound macro's inner asm when byte-neutral Sony's inline_c.h path, the census sites, the oracle 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
|
||||
|
||||
|
@@ -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:** 299 tool files in scope (submodules, vendored and downloaded code excluded), of which 299 live rows
|
||||
> below; per phase: P1 2 · P2 26 · P3 17 · P4 9 · P5 26 · P6 51 · P7 20 · P8 86 · P9 27 · P10 19 · PROJECT-ONLY 16. Superseded tools appear only as pointers to their successor (30 pointer rows); one-offs are omitted. Table rows in all: 329 (the installer checks its copy against this figure).
|
||||
> **Coverage:** 300 tool files in scope (submodules, vendored and downloaded code excluded), of which 300 live rows
|
||||
> below; per phase: P1 2 · P2 26 · P3 17 · P4 9 · P5 26 · P6 51 · P7 20 · P8 86 · P9 27 · P10 20 · PROJECT-ONLY 16. Superseded tools appear only as pointers to their successor (30 pointer rows); one-offs are omitted. Table rows in all: 330 (the installer checks its copy against this figure).
|
||||
|
||||
## P1 — extraction + manifest
|
||||
|
||||
@@ -327,6 +327,7 @@
|
||||
| `cast_call_sites.py` | add per-site function-pointer casts so a draft can call a differently typed callee | Adds per-site function-pointer casts so a draft can call a differently typed callee | repo src layout |
|
||||
| `ghidra_scripts/ApplySymbols.java` | apply curated names and signatures inside the analysis tool and save | The in-tool half of that mirror: apply curated names and signatures, save on exit | none |
|
||||
| `lever_census.py` | census every compiler-forcing construct in the C and gate the levers-off phase | 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, 0 orphan markers) and --strict (0 pins, 0 asm) gates, --sites for the delever ledger, a fixture self-test | repo paths, the shared-body scanner |
|
||||
| `gte_consolidate.py` | consolidate the GTE coprocessor inline-asm macros under Sony's names in one header and sweep dead lever macros | The Phase-36 T5 tool: every asm-bearing macro definition and direct GTE statement SIGNED by the build's own maspsx→as tail (template bytes with operands bound to fixed registers, operand counts, clobbers); one canonical text per signature named by PsyQ's inline_c.h convention (Sony's file supplies names and clobber lists, never opcode words) written to include/gte_inline.h; per file the canonical duplicates deleted, private names renamed, a variant with extra clobbers tried as canonical and kept as a marked <name>_m lever only when the object differs, direct statements rewritten into canonical calls (a concatenation of two included); every file judged through every recipe; --sweep deletes dead asm-bearing macros and drops a compound macro's inner asm when byte-neutral | Sony's inline_c.h path, the census sites, the oracle |
|
||||
| `macro_to_header.py` | convert a shared-body macro header into per-function plain-C headers included at each site | 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 | the macro form is this project's; a kit-born project shares headers from its first bank |
|
||||
| `asm_verbatim.py` | emit a function's target assembly as an inline-assembly body | Emits the file-scope inline-assembly body form from a disassembly file | repo asm layout |
|
||||
| `build_engine_types.py` | extract inline-defined types and typedefs from a source file into a shared header | Extracts inline-defined named types and typedefs from a source file into a shared header | repo shared-header path |
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
# and src/ + the ledger/batch record/log + the census + the phase log are committed (R42).
|
||||
# Stops on the first red (a non-zero tool exit, a final N/M with N != M, a fleet run that is not 218/218) — the batch's files are then
|
||||
# still in place: inspect, then `tools/delever.py --restore` (never git checkout, R102). Stops cleanly when nothing is drawable.
|
||||
# Usage: [LABEL_PREFIX=t3_] [TASK=T4] [REDRAW="REFUSED NOTHING-USABLE"] tools/delever_cycle.sh START END [BATCH=300] [MODE=tus|headers] [ONLY="alias1 alias2 …"]
|
||||
# Usage: [LABEL_PREFIX=t3_] [TASK=T4] [REDRAW="REFUSED NOTHING-USABLE"] tools/delever_cycle.sh START END [BATCH=300] [MODE=tus|headers|gte] [ONLY="alias1 alias2 …"]
|
||||
# RUN IT DETACHED from the Claude Code harness (its low-memory guard kills a long BACKGROUND task — batch tus3 died mid-apply, S98):
|
||||
# setsid nohup bash -c 'TASK=T4 tools/delever_cycle.sh 3 12 300 tus; echo "cycle exit=$?"' > .run/P36/delever/cycle_<x>.log 2>&1 &
|
||||
# and watch the log (a tiny waiter: `until grep -q 'cycle exit=' <log>; do sleep 60; done`). A killed batch: `tools/delever.py --restore`.
|
||||
@@ -20,7 +20,12 @@ set -o pipefail
|
||||
cd "$(dirname "$0")/.." || exit 2
|
||||
START=$1; END=$2; BATCH=${3:-300}; MODE=${4:-tus}; ONLY=${5:-}
|
||||
[ -n "$START" ] && [ -n "$END" ] || { echo "usage: $0 START END [BATCH] [tus|headers] [ONLY]"; exit 2; }
|
||||
case "$MODE" in tus) HFLAG="";; headers) HFLAG="--headers";; *) echo "MODE must be tus or headers"; exit 2;; esac
|
||||
case "$MODE" in
|
||||
tus) HFLAG=""; TOOL="tools/delever.py --apply"; VPAT='delever: batch .*';;
|
||||
headers) HFLAG="--headers"; TOOL="tools/delever.py --apply"; VPAT='delever: batch .*';;
|
||||
gte) HFLAG=""; TOOL="tools/gte_consolidate.py --apply"; VPAT='gte_consolidate: batch .*';; # T5: the GTE consolidation per file
|
||||
*) echo "MODE must be tus, headers or gte"; exit 2;;
|
||||
esac
|
||||
ONLYFLAG=""; [ -n "$ONLY" ] && ONLYFLAG="--only $ONLY"
|
||||
REDRAWFLAG=""; [ -n "${REDRAW:-}" ] && REDRAWFLAG="--redraw $REDRAW" # e.g. REDRAW="REFUSED NOTHING-USABLE" after a tool fix
|
||||
FLEET=$(ls config/check.*.sha | wc -l)
|
||||
@@ -41,11 +46,11 @@ for k in $(seq "$START" "$END"); do
|
||||
[ -z "$(git status --short --porcelain src | grep '^??')" ] || { echo "cycle: untracked files under src/ (a dotfile probe?) — sweep them first"; exit 3; }
|
||||
$PY tools/delever_oracle.py --status >/dev/null 2>&1 || $PY tools/delever_oracle.py --calibrate ov_SC04_011 ov_SC03_015 ov_SC03_014 main -j 16 > .run/P36/delever/calibrate_${label}.log 2>&1 \
|
||||
|| { echo "cycle: calibration FAILED"; tail -5 .run/P36/delever/calibrate_${label}.log; exit 1; }
|
||||
{ /usr/bin/time -f "$label wall=%e s" $PY tools/delever.py --apply --batch "$BATCH" --label "$label" $HFLAG $ONLYFLAG $REDRAWFLAG -j 12; echo "exit=$?"; } > "$log" 2>&1
|
||||
{ /usr/bin/time -f "$label wall=%e s" $PY $TOOL --batch "$BATCH" --label "$label" $HFLAG $ONLYFLAG $REDRAWFLAG -j 12; echo "exit=$?"; } > "$log" 2>&1
|
||||
fi
|
||||
grep -q '^exit=0$' "$log" || { echo "cycle: batch $label — delever exited non-zero"; tail -4 "$log"; exit 1; }
|
||||
if grep -q 'nothing to do (no drawable file)' "$log"; then echo "cycle: nothing drawable at $label — done"; exit 0; fi
|
||||
vline=$(grep -oE 'delever: batch .*' "$log" | tail -1)
|
||||
if grep -qE 'nothing to do' "$log"; then echo "cycle: nothing drawable at $label — done"; exit 0; fi
|
||||
vline=$(grep -oE "$VPAT" "$log" | tail -1)
|
||||
[ -n "$vline" ] || { echo "cycle: batch $label — no verify line in $log (R32: an empty line is not a pass)"; tail -3 "$log"; exit 1; }
|
||||
final=$(echo "$vline" | grep -oE 'final [0-9]+/[0-9]+' | grep -oE '[0-9]+/[0-9]+')
|
||||
[ -n "$final" ] && [ "${final%/*}" = "${final#*/}" ] || { echo "cycle: batch $label — $vline"; exit 1; }
|
||||
|
||||
@@ -0,0 +1,922 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gte_consolidate.py — the GTE coprocessor asm consolidated under Sony's names, and the dead lever-macro sweep (Phase 36 T5).
|
||||
|
||||
tools/gte_consolidate.py --inventory # every asm-bearing macro definition + every direct GTE statement, SIGNED by the
|
||||
# build's own maspsx→as tail; the canonical table → .run/P36/gte/{inventory,canonical}.json
|
||||
tools/gte_consolidate.py --header # write include/gte_inline.h (one canonical macro per signature) + the include in common.h
|
||||
tools/gte_consolidate.py --apply --batch N --label L [--only …] [-j 12]
|
||||
# per file: canonical-duplicate definitions deleted, private/variant names renamed, direct
|
||||
# statements rewritten into canonical calls; every file judged through every recipe
|
||||
tools/gte_consolidate.py --sweep --label L [--only …] # dead asm-bearing macros deleted; a compound macro's inner asm dropped when byte-neutral
|
||||
tools/gte_consolidate.py --status | --selftest
|
||||
|
||||
A SIGNATURE is what the assembler makes of a template, not how it is spelled: the template with its operands bound to fixed registers
|
||||
(%0.. → $4 $5 $6 $7 for inputs, $2 $3 for outputs) is assembled through the SAME tail as the build (maspsx --aspsx-version=2.56 →
|
||||
mipsel-linux-gnu-as with include/macro.inc + include/gte_macros.inc, where `rtps`, `mvmva`, `sqr 0` are GAS macros), and the signature
|
||||
is (bytes, #outputs, #inputs). `nop;nop;rtps`, `nop;nop;.word 0x4a180001` and `nop;nop;cop2 0x0180001` are one signature. Sony's
|
||||
converted inline_c.h is the source of NAMES and of operand/clobber lists only — its `.word` opcode spellings assemble to nothing
|
||||
useful (`gte_rtps` there is `.word 0x0000007f`).
|
||||
|
||||
THE CANONICAL TABLE: definitions grouped by signature; the canonical CLOBBER set of a signature is Sony's for that name (a store macro
|
||||
legitimately clobbers "memory"), else the majority's; the canonical NAME is the most-used `gte_*` name in the group (Sony's convention),
|
||||
else a name derived from gte_macros.inc, else `gte_seq_<hash>`; the canonical TEXT is the majority definition text (byte-proven in the
|
||||
fleet — the header ships nothing that was not already in the tree). A definition with the canonical signature but EXTRA clobbers (the
|
||||
`"memory"` a drafter added to a load or a compute op to force the schedule) is a LEVER VARIANT: it stays per TU, renamed `<name>_m`, and
|
||||
its uses carry `// !FAKE:` markers for T7. A direct statement is rewritten into `NAME(args)` (or `NAME1(args); NAME2();` for the
|
||||
concatenation of two canonical macros) when bytes and clobbers agree; with extra clobbers it is a lever variant too (marked); a sequence
|
||||
that matches nothing is counted and left. Every file's final text is judged through every recipe of the file (tools/delever_oracle.py);
|
||||
the clean fleet run gates every batch (tools/delever_cycle.sh MODE=gte).
|
||||
"""
|
||||
import argparse
|
||||
import collections
|
||||
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"))
|
||||
import delever as dl # noqa: E402
|
||||
import delever_oracle as oracle # noqa: E402
|
||||
import lever_census as lc # noqa: E402
|
||||
import share_census as sc # noqa: E402
|
||||
|
||||
RUN = REPO / ".run" / "P36" / "gte"
|
||||
SIG_CACHE = RUN / "sig_cache.json"
|
||||
INVENTORY = RUN / "inventory.json"
|
||||
CANON = RUN / "canonical.json"
|
||||
LEDGER = REPO / ".run" / "P36" / "delever" / "ledger.jsonl"
|
||||
HEADER = "include/gte_inline.h"
|
||||
COMMON = "include/common.h"
|
||||
SONY = REPO / "tools/psyq/conv47/psyq-4_7-converted/include/inline_c.h"
|
||||
MASPSX = ".venv/bin/python tools/maspsx/maspsx.py --aspsx-version=2.56 --expand-div"
|
||||
AS = "mipsel-linux-gnu-as -Iinclude -march=r3000 -mtune=r3000 -no-pad-sections -O1 -G0"
|
||||
FAKE = dl.FAKE
|
||||
DEFINE_LINE = re.compile(r"^[ \t]*#[ \t]*define[ \t]+([A-Za-z_]\w*)(\([^)]*\))?[ \t]*(.*)$")
|
||||
_LOCK = dl._LOCK
|
||||
|
||||
|
||||
class Refuse(Exception):
|
||||
pass
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
# the signature engine
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
def parse_asm(inner):
|
||||
"""(template, outs, ins, clobbers) — delever's operand parser plus the clobber section."""
|
||||
tmpl, outs, ins = dl.parse_asm_operands(inner)
|
||||
s = inner.strip()
|
||||
i, n = 0, len(s)
|
||||
while i < n:
|
||||
if s[i] in " \t\r\n":
|
||||
i += 1
|
||||
continue
|
||||
mm = lc.STRLIT.match(s, i)
|
||||
if not mm:
|
||||
break
|
||||
i = mm.end()
|
||||
rest = s[i:].strip()
|
||||
secs = lc._split_sections(rest[1:]) if rest.startswith(":") else []
|
||||
clob = [m.group(1) for m in re.finditer(r'"([^"]*)"', secs[2])] if len(secs) > 2 else []
|
||||
return tmpl, outs, ins, sorted(set(clob))
|
||||
|
||||
|
||||
def bind_regs(tmpl, nout, nin):
|
||||
regs = ["$2", "$3"][:nout] + ["$4", "$5", "$6", "$7"][:nin]
|
||||
def sub(m):
|
||||
k = int(m.group(1))
|
||||
return regs[k] if k < len(regs) else m.group(0)
|
||||
t = re.sub(r"%(\d+)", sub, tmpl)
|
||||
t = re.sub(r"\\n|\\t", "\n", t)
|
||||
return t
|
||||
|
||||
|
||||
_sig_cache = None
|
||||
|
||||
|
||||
def _load_cache():
|
||||
global _sig_cache
|
||||
if _sig_cache is None:
|
||||
_sig_cache = json.loads(SIG_CACHE.read_text()) if SIG_CACHE.exists() else {}
|
||||
return _sig_cache
|
||||
|
||||
|
||||
def _save_cache():
|
||||
RUN.mkdir(parents=True, exist_ok=True)
|
||||
tmp = SIG_CACHE.with_suffix(".tmp")
|
||||
tmp.write_text(json.dumps(_sig_cache, indent=0))
|
||||
os.replace(tmp, SIG_CACHE)
|
||||
|
||||
|
||||
def asm_bytes(bound_template):
|
||||
"""hex of the .text bytes the build's tail makes of the template (cached by text); 'ERROR: …' when it does not assemble."""
|
||||
cache = _load_cache()
|
||||
key = bound_template
|
||||
if key in cache:
|
||||
return cache[key]
|
||||
d = RUN / "probe"
|
||||
d.mkdir(parents=True, exist_ok=True)
|
||||
tag = hashlib.sha1(key.encode()).hexdigest()[:10]
|
||||
s_path, o_path, b_path = d / f"{tag}.s", d / f"{tag}.o", d / f"{tag}.bin"
|
||||
s_path.write_text("\t.include \"include/macro.inc\"\n\t.include \"gte_macros.inc\"\n\t.set\tnoreorder\n\t.set\tnoat\n\t.text\n\t.align\t2\n"
|
||||
"\t.globl\tprobe\n\t.ent\tprobe\nprobe:\n" + bound_template + "\n\t.end\tprobe\n")
|
||||
cmd = f"set -o pipefail; cat {s_path} | {MASPSX} | {AS} -o {o_path} && mipsel-linux-gnu-objcopy -O binary -j .text {o_path} {b_path}"
|
||||
r = subprocess.run(["bash", "-c", cmd], cwd=REPO, capture_output=True, text=True)
|
||||
if r.returncode != 0 or not b_path.exists():
|
||||
res = "ERROR: " + (r.stderr.strip().splitlines() or ["?"])[-1][:120]
|
||||
else:
|
||||
res = b_path.read_bytes().hex()
|
||||
for p in (s_path, o_path, b_path):
|
||||
try:
|
||||
p.unlink()
|
||||
except OSError:
|
||||
pass
|
||||
cache[key] = res
|
||||
return res
|
||||
|
||||
|
||||
def signature(inner):
|
||||
"""dict(bytes, nout, nin, clob, tmpl, ok) for the text between an asm statement's parens."""
|
||||
tmpl, outs, ins, clob = parse_asm(inner)
|
||||
bound = bind_regs(tmpl, len(outs), len(ins))
|
||||
b = asm_bytes(bound)
|
||||
return dict(bytes=b, nout=len(outs), nin=len(ins), clob=clob, tmpl=tmpl, ok=not b.startswith("ERROR"),
|
||||
ins=[e for _, e in ins], outs=[e for _, e in outs])
|
||||
|
||||
|
||||
def set_clobbers(body, clob):
|
||||
"""the macro body with the asm statement's clobber section replaced by `clob` (removed when empty)."""
|
||||
m_asm = lc.ASM_KW.search(body)
|
||||
o = body.find("(", m_asm.end())
|
||||
c = lc._paren_span(body, o)
|
||||
inner = body[o + 1:c]
|
||||
# the sections: template, then up to three ':'-separated parts outside string literals
|
||||
s = inner
|
||||
i, n = 0, len(s)
|
||||
while i < n:
|
||||
if s[i] in " \t\r\n":
|
||||
i += 1
|
||||
continue
|
||||
mm = lc.STRLIT.match(s, i)
|
||||
if not mm:
|
||||
break
|
||||
i = mm.end()
|
||||
head, rest = s[:i], s[i:]
|
||||
secs = lc._split_sections(rest.strip()[1:]) if rest.strip().startswith(":") else []
|
||||
while len(secs) < 3:
|
||||
secs.append("")
|
||||
secs[2] = ", ".join(f'"{x}"' for x in clob) if clob else ""
|
||||
while secs and secs[-1].strip() == "":
|
||||
secs.pop()
|
||||
new_inner = head.rstrip() + ("".join(" : " + x.strip() for x in secs) if secs else "") + " "
|
||||
return body[:o + 1] + new_inner + body[c:]
|
||||
|
||||
|
||||
def sig_key(s):
|
||||
return f"{s['bytes']}|{s['nout']}|{s['nin']}"
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
# the inventory
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
def asm_inner(body_masked):
|
||||
"""the text between the parens of the FIRST asm statement in a macro body (None if none / more than one)."""
|
||||
ms = list(lc.ASM_KW.finditer(body_masked))
|
||||
if len(ms) != 1:
|
||||
return None
|
||||
o = body_masked.find("(", ms[0].end())
|
||||
c = lc._paren_span(body_masked, o) if o >= 0 else -1
|
||||
return body_masked[o + 1:c] if (o >= 0 and c > 0) else None
|
||||
|
||||
|
||||
def define_params(raw, l0):
|
||||
m = DEFINE_LINE.match(raw.split("\n")[l0 - 1])
|
||||
return (m.group(2) or "") if m else ""
|
||||
|
||||
|
||||
def sony_table():
|
||||
"""name -> (nout, nin, clobbers) from Sony's inline_c.h — the parts of it that are plain C text."""
|
||||
out = {}
|
||||
if not SONY.exists():
|
||||
return out
|
||||
raw = SONY.read_text(errors="surrogateescape")
|
||||
for (l0, l1, name, body) in lc.define_blocks(raw):
|
||||
inner = asm_inner(sc.mask_text(body) if "/*" in body else body)
|
||||
if inner is None:
|
||||
continue
|
||||
try:
|
||||
_, outs, ins, clob = parse_asm(inner)
|
||||
except Exception: # noqa: BLE001
|
||||
continue
|
||||
out[name] = (len(outs), len(ins), clob)
|
||||
return out
|
||||
|
||||
|
||||
def inc_names():
|
||||
"""bytes -> name for gte_macros.inc's single-op macros (assembled as `nop;nop;<macro>`), for signatures without a gte_* name."""
|
||||
raw = (REPO / "include/gte_macros.inc").read_text(errors="surrogateescape")
|
||||
out = {}
|
||||
for m in re.finditer(r"^\.macro\s+(\w+)\s*$", raw, re.M):
|
||||
b = asm_bytes(f"nop\nnop\n{m.group(1)}")
|
||||
if not b.startswith("ERROR"):
|
||||
out[f"{b}|0|0"] = m.group(1)
|
||||
return out
|
||||
|
||||
|
||||
def files_under_src():
|
||||
return [p for p in sorted((REPO / "src").rglob("*")) if p.suffix in (".c", ".h") and not p.name.startswith(".")]
|
||||
|
||||
|
||||
def inventory(jobs=12, quiet=False):
|
||||
"""definitions: [{tu, l0, l1, name, params, body, kind, sig}], direct: [{tu, line, col, inner, sig}], uses: Counter(name)."""
|
||||
sites = dl.load_sites()
|
||||
uses = collections.Counter(s["via"] for s in sites if s.get("via") and s["kind"] == "gte")
|
||||
direct_sites = [s for s in sites if s["kind"] == "gte" and not s.get("via")]
|
||||
defs, direct = [], []
|
||||
for p in files_under_src():
|
||||
raw = p.read_text(errors="surrogateescape")
|
||||
if "#define" not in raw and "asm" not in raw:
|
||||
continue
|
||||
rel = p.relative_to(REPO).as_posix()
|
||||
for (l0, l1, name, body) in lc.define_blocks(raw):
|
||||
kind = lc.macro_kind(body)
|
||||
if not kind:
|
||||
continue
|
||||
d = dict(tu=rel, l0=l0, l1=l1, name=name, params=define_params(raw, l0), body=body, kind=kind, sig=None)
|
||||
if kind == "gte":
|
||||
inner = asm_inner(sc.mask_text(body) if ("/*" in body or "//" in body) else body)
|
||||
if inner is not None:
|
||||
try:
|
||||
d["sig"] = signature(inner)
|
||||
except Exception as ex: # noqa: BLE001
|
||||
d["sig"] = dict(bytes=f"ERROR: {ex}"[:100], ok=False, nout=0, nin=0, clob=[], tmpl="", ins=[], outs=[])
|
||||
defs.append(d)
|
||||
by_tu = collections.defaultdict(list)
|
||||
for s in direct_sites:
|
||||
by_tu[s["tu"]].append(s)
|
||||
for tu, ss in by_tu.items():
|
||||
raw = (REPO / tu).read_text(errors="surrogateescape")
|
||||
m = dl.same_len_mask(raw)
|
||||
ls = dl.line_starts(raw)
|
||||
for s in ss:
|
||||
pos = ls[s["line"] - 1] + s["col"] - 1
|
||||
o = m.find("(", pos)
|
||||
c = lc._paren_span(m, o) if o >= 0 else -1
|
||||
if o < 0 or c < 0:
|
||||
continue
|
||||
inner = m[o + 1:c]
|
||||
try:
|
||||
sg = signature(inner)
|
||||
except Exception as ex: # noqa: BLE001
|
||||
sg = dict(bytes=f"ERROR: {ex}"[:100], ok=False, nout=0, nin=0, clob=[], tmpl="", ins=[], outs=[])
|
||||
direct.append(dict(tu=tu, line=s["line"], col=s["col"], fn=s.get("fn"), inner=inner, sig=sg, marked=s.get("marked", False)))
|
||||
_save_cache()
|
||||
inv = dict(generated=time.strftime("%Y-%m-%d %H:%M"), definitions=defs, direct=direct, uses=dict(uses))
|
||||
RUN.mkdir(parents=True, exist_ok=True)
|
||||
INVENTORY.write_text(json.dumps(inv))
|
||||
if not quiet:
|
||||
gte = [d for d in defs if d["kind"] == "gte"]
|
||||
print(f"gte_consolidate --inventory: {len(defs)} asm-bearing macro definitions ({len(gte)} GTE, {sum(1 for d in gte if d['sig'] and d['sig']['ok'])} signed), "
|
||||
f"{len(direct)} direct GTE statements ({sum(1 for d in direct if d['sig']['ok'])} signed), {sum(uses.values())} macro-carried GTE uses")
|
||||
return inv
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
# the canonical table
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
def canonical_table(inv, quiet=False):
|
||||
sony = sony_table()
|
||||
incn = inc_names()
|
||||
groups = collections.defaultdict(list)
|
||||
for d in inv["definitions"]:
|
||||
if d["kind"] == "gte" and d["sig"] and d["sig"]["ok"]:
|
||||
groups[sig_key(d["sig"])].append(d)
|
||||
table, variants, unsigned = {}, {}, [d for d in inv["definitions"] if d["kind"] == "gte" and not (d["sig"] and d["sig"]["ok"])]
|
||||
names_taken = set()
|
||||
for key, ds in sorted(groups.items(), key=lambda kv: -len(kv[1])):
|
||||
names = collections.Counter(d["name"] for d in ds)
|
||||
# candidates: the names with their address / variant suffixes stripped (`gte_stsxy01_80181E04` is Sony's gte_stsxy01;
|
||||
# `LDV0_DA34` is a private copy of gte_ldv0), Sony's spelling preferred, then the most-used gte_* spelling
|
||||
stripped = collections.Counter()
|
||||
for n, cnt in names.items():
|
||||
base = re.sub(r"(_[0-9A-Fa-f]{3,8}|X|_m|_A|_B)$", "", n)
|
||||
base = re.sub(r"_v[0-9a-f]{4}$", "", base)
|
||||
stripped[base] += cnt
|
||||
nout0, nin0 = ds[0]["sig"]["nout"], ds[0]["sig"]["nin"]
|
||||
# a Sony name is taken only when the operand counts agree with Sony's macro (a private compound that borrowed the name is not it)
|
||||
sony_named = [n for n, _ in stripped.most_common() if n in sony and (sony[n][0], sony[n][1]) == (nout0, nin0)]
|
||||
gte_names = [n for n, _ in stripped.most_common() if n.startswith("gte_")]
|
||||
name = (sony_named or gte_names or [None])[0]
|
||||
if name is None:
|
||||
base = incn.get(key)
|
||||
priv = stripped.most_common(1)[0][0]
|
||||
name = f"gte_{base}" if base else f"gte_{re.sub(r'[^A-Za-z0-9]', '', priv).lower()}"
|
||||
if name in sony and not sony_named:
|
||||
name = f"{name}_alt"
|
||||
while name in names_taken:
|
||||
name = f"{name}_alt"
|
||||
names_taken.add(name)
|
||||
clob_sets = collections.Counter(tuple(d["sig"]["clob"]) for d in ds)
|
||||
if name in sony:
|
||||
canon_clob = tuple(sony[name][2])
|
||||
else:
|
||||
canon_clob = clob_sets.most_common(1)[0][0]
|
||||
canon_defs = [d for d in ds if tuple(d["sig"]["clob"]) == canon_clob]
|
||||
synthesized = False
|
||||
if canon_defs:
|
||||
texts = collections.Counter((d["params"], re.sub(r"[ \t]+\n", "\n", d["body"]).strip()) for d in canon_defs)
|
||||
(params, body), _ = texts.most_common(1)[0]
|
||||
else:
|
||||
# nobody spells it with Sony's clobbers (every definition carries the steer): the canonical text is the majority body
|
||||
# with its clobber list set to Sony's — the steer must not become canonical by being universal
|
||||
texts = collections.Counter((d["params"], re.sub(r"[ \t]+\n", "\n", d["body"]).strip()) for d in ds)
|
||||
(params, body), _ = texts.most_common(1)[0]
|
||||
body = set_clobbers(body, list(canon_clob))
|
||||
synthesized = True
|
||||
table[key] = dict(name=name, params=params, body=body, clob=list(canon_clob), defs=len(canon_defs), files=len({d["tu"] for d in canon_defs}),
|
||||
synthesized=synthesized,
|
||||
names=dict(names), sony=(name in sony), nin=ds[0]["sig"]["nin"], nout=ds[0]["sig"]["nout"], tmpl=ds[0]["sig"]["tmpl"])
|
||||
for cs, cnt in clob_sets.items():
|
||||
if cs != canon_clob:
|
||||
extra = sorted(set(cs) - set(canon_clob))
|
||||
fewer = sorted(set(canon_clob) - set(cs))
|
||||
vname = f"{name}_m" if extra == ["memory"] and not fewer else f"{name}_v{hashlib.sha1('|'.join(cs).encode()).hexdigest()[:4]}"
|
||||
variants[f"{key}|{'|'.join(cs)}"] = dict(name=vname, of=name, key=key, clob=list(cs), extra=extra, fewer=fewer, defs=cnt,
|
||||
files=len({d["tu"] for d in ds if tuple(d["sig"]["clob"]) == cs}))
|
||||
out = dict(generated=time.strftime("%Y-%m-%d %H:%M"), canonical=table, variants=variants, unsigned=[dict(tu=d["tu"], name=d["name"], l0=d["l0"]) for d in unsigned])
|
||||
CANON.write_text(json.dumps(out, indent=1))
|
||||
if not quiet:
|
||||
print(f"gte_consolidate: canonical table — {len(table)} signatures ({sum(1 for t in table.values() if t['sony'])} with Sony's name), "
|
||||
f"{sum(t['defs'] for t in table.values())} canonical definitions; {len(variants)} lever variants holding {sum(v['defs'] for v in variants.values())} definitions; "
|
||||
f"{len(unsigned)} unsigned definitions")
|
||||
for v in sorted(variants.values(), key=lambda v: -v["defs"])[:12]:
|
||||
print(f" variant {v['name']:24s} of {v['of']:18s} defs {v['defs']:4d} files {v['files']:4d} extra {v['extra']} fewer {v['fewer']}")
|
||||
for u in unsigned[:8]:
|
||||
print(f" unsigned {u['tu']}:{u['l0']} {u['name']}")
|
||||
return out
|
||||
|
||||
|
||||
def render_header(canon):
|
||||
L = ["/* include/gte_inline.h — the GTE coprocessor macros under Sony's names, ONE definition per signature (Phase 36 T5).",
|
||||
" *",
|
||||
" * Every macro here is the majority spelling the tree already carried in its per-TU definitions (byte-proven in the fleet),",
|
||||
" * named by PsyQ's inline_c.h convention; the signature of each (the bytes the build's maspsx → as tail makes of the template,",
|
||||
" * its operand counts, its clobbers) was computed by tools/gte_consolidate.py, which also deleted the per-TU duplicates.",
|
||||
" * A definition that differs from the one here only by an extra clobber (a scheduling steer) lives on in its TU as <name>_m",
|
||||
" * and is a marked lever, not a second spelling. Generated: do not edit by hand — regenerate with `tools/gte_consolidate.py --header`.",
|
||||
" */", "#ifndef GTE_INLINE_H", "#define GTE_INLINE_H", ""]
|
||||
for key, t in sorted(canon["canonical"].items(), key=lambda kv: kv[1]["name"]):
|
||||
body_lines = t["body"].split("\n")
|
||||
L.append(f"/* {t['tmpl'][:90]}{' …' if len(t['tmpl']) > 90 else ''} ({t['defs']} definitions in {t['files']} files{'' if t['sony'] else '; not a PsyQ name'}) */")
|
||||
if len(body_lines) == 1:
|
||||
L.append(f"#define {t['name']}{t['params']} {body_lines[0]}")
|
||||
else:
|
||||
L.append(f"#define {t['name']}{t['params']} \\")
|
||||
for i, bl in enumerate(body_lines):
|
||||
L.append(f" {bl.strip()}" + (" \\" if i < len(body_lines) - 1 else ""))
|
||||
L.append("")
|
||||
L += ["#endif /* GTE_INLINE_H */", ""]
|
||||
return "\n".join(L)
|
||||
|
||||
|
||||
def write_header(canon):
|
||||
RUN.mkdir(parents=True, exist_ok=True)
|
||||
(REPO / HEADER).write_text(render_header(canon))
|
||||
c = REPO / COMMON
|
||||
t = c.read_text()
|
||||
if '#include "gte_inline.h"' not in t:
|
||||
t = t.replace("#endif /* COMMON_H */", '#include "gte_inline.h" /* Phase 36 T5: the GTE coprocessor macros, one definition each */\n#endif /* COMMON_H */', 1)
|
||||
c.write_text(t)
|
||||
print(f"gte_consolidate --header: {HEADER} written ({len(canon['canonical'])} macros); {COMMON} includes it")
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
# --apply: per file
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
def plan_files(inv, canon, only=None):
|
||||
"""tu -> dict(defs=[definitions to delete/rename], direct=[direct statements], renames={old: new})"""
|
||||
by_key = canon["canonical"]
|
||||
var_by = {v["key"] + "|" + "|".join(v["clob"]): v for v in canon["variants"].values()}
|
||||
plan = collections.defaultdict(lambda: dict(defs=[], direct=[], renames={}))
|
||||
for d in inv["definitions"]:
|
||||
if d["kind"] != "gte" or not (d["sig"] and d["sig"]["ok"]):
|
||||
continue
|
||||
key = sig_key(d["sig"])
|
||||
t = by_key.get(key)
|
||||
if not t:
|
||||
continue
|
||||
vkey = key + "|" + "|".join(d["sig"]["clob"])
|
||||
if tuple(d["sig"]["clob"]) == tuple(t["clob"]):
|
||||
plan[d["tu"]]["defs"].append(dict(d, action="delete", canonical=t["name"]))
|
||||
if d["name"] != t["name"]:
|
||||
plan[d["tu"]]["renames"][d["name"]] = t["name"]
|
||||
elif vkey in var_by:
|
||||
v = var_by[vkey]
|
||||
plan[d["tu"]]["defs"].append(dict(d, action="rename", canonical=t["name"], variant=v["name"], extra=v["extra"], fewer=v["fewer"]))
|
||||
if d["name"] != v["name"]:
|
||||
plan[d["tu"]]["renames"][d["name"]] = v["name"]
|
||||
for s in inv["direct"]:
|
||||
plan[s["tu"]]["direct"].append(s)
|
||||
files = {tu: p for tu, p in plan.items() if p["defs"] or p["direct"]}
|
||||
if only:
|
||||
files = {tu: p for tu, p in files.items() if any(o == tu or tu.endswith("/" + o) or ("/" + o + "/") in tu for o in only)}
|
||||
return files
|
||||
|
||||
|
||||
def direct_rewrite(s, canon):
|
||||
"""('call', text, name) | ('lever', variant_name, extra) | ('none', why, None) for a direct GTE statement."""
|
||||
sg = s["sig"]
|
||||
if not sg["ok"]:
|
||||
return "none", "unsigned", None
|
||||
key = sig_key(sg)
|
||||
t = canon["canonical"].get(key)
|
||||
if t and sg["nout"] == 0:
|
||||
if tuple(sg["clob"]) == tuple(t["clob"]):
|
||||
return "call", f"{t['name']}({', '.join(sg['ins'])});", t["name"]
|
||||
extra = sorted(set(sg["clob"]) - set(t["clob"]))
|
||||
fewer = sorted(set(t["clob"]) - set(sg["clob"]))
|
||||
return "lever", (f"{t['name']}_m" if extra == ["memory"] and not fewer else f"{t['name']}_v"), extra or fewer
|
||||
# a concatenation of two canonical macros (a load then an op): bytes = b1 + b2, inputs = the first's, clobbers = the union
|
||||
b = sg["bytes"]
|
||||
for k1, t1 in canon["canonical"].items():
|
||||
b1 = k1.split("|")[0]
|
||||
if not b.startswith(b1) or len(b1) == 0 or len(b1) >= len(b):
|
||||
continue
|
||||
k2 = f"{b[len(b1):]}|0|0"
|
||||
t2 = canon["canonical"].get(k2)
|
||||
if not t2 or t1["nout"] or t1["nin"] != sg["nin"]:
|
||||
continue
|
||||
union = sorted(set(t1["clob"]) | set(t2["clob"]))
|
||||
if union == list(sg["clob"]):
|
||||
return "call", f"{t1['name']}({', '.join(sg['ins'])}); {t2['name']}();", f"{t1['name']}+{t2['name']}"
|
||||
extra = sorted(set(sg["clob"]) - set(union))
|
||||
return "lever", f"{t1['name']}+{t2['name']}_m" if extra == ["memory"] else f"{t1['name']}+{t2['name']}_v", extra or sorted(set(union) - set(sg["clob"]))
|
||||
return "none", "no canonical signature", None
|
||||
|
||||
|
||||
def file_edits(tu, raw, p, canon, label):
|
||||
"""[(start, end, repl)] + the record (per definition / use / direct statement) for one file's consolidation."""
|
||||
m = dl.same_len_mask(raw)
|
||||
ls = dl.line_starts(raw)
|
||||
edits, rec = [], dict(deleted=0, renamed_defs=0, use_renames=0, direct_calls=0, direct_levers=0, direct_none=0, marked=0, defs=[], direct=[])
|
||||
# definitions: delete or rename (the #define line's name token); a renamed variant gets a comment line above
|
||||
for d in sorted(p["defs"], key=lambda d: d["l0"]):
|
||||
start, end = ls[d["l0"] - 1], ls[d["l1"]] if d["l1"] < len(ls) else len(raw)
|
||||
if d["action"] == "delete":
|
||||
edits.append((start, end, ""))
|
||||
rec["deleted"] += 1
|
||||
rec["defs"].append(dict(name=d["name"], l0=d["l0"], action="deleted", canonical=d["canonical"]))
|
||||
else:
|
||||
line = raw[start:ls[d["l0"]] - 1]
|
||||
k = line.find(d["name"])
|
||||
if k < 0:
|
||||
raise Refuse(f"{tu}:{d['l0']}: the #define line does not carry `{d['name']}`")
|
||||
note = f"/* {FAKE[3:]} gte variant `{d['variant']}` — {', '.join(d['extra']) or 'fewer clobbers'} beyond Sony's `{d['canonical']}` (a scheduling steer; P36 T5) */\n"
|
||||
edits.append((start, start, note))
|
||||
edits.append((start + k, start + k + len(d["name"]), d["variant"]))
|
||||
rec["renamed_defs"] += 1
|
||||
rec["defs"].append(dict(name=d["name"], l0=d["l0"], action="renamed", variant=d["variant"], canonical=d["canonical"]))
|
||||
# uses: rename tokens (outside the #define lines being edited); mark the uses of a lever variant
|
||||
def_lines = {ln for d in p["defs"] for ln in range(d["l0"], d["l1"] + 1)}
|
||||
lever_names = {d["name"] for d in p["defs"] if d["action"] == "rename"} | {d["variant"] for d in p["defs"] if d["action"] == "rename"}
|
||||
for old, new in p["renames"].items():
|
||||
for mm in re.finditer(r"\b%s\b(?=\s*\()" % re.escape(old), m):
|
||||
ln = m.count("\n", 0, mm.start()) + 1
|
||||
if ln in def_lines:
|
||||
continue
|
||||
edits.append((mm.start(), mm.end(), new))
|
||||
rec["use_renames"] += 1
|
||||
marked_lines = set()
|
||||
for name in lever_names:
|
||||
for mm in re.finditer(r"\b%s\b(?=\s*\()" % re.escape(name), m):
|
||||
ln = m.count("\n", 0, mm.start()) + 1
|
||||
if ln in def_lines or ln in marked_lines:
|
||||
continue
|
||||
le = m.find("\n", mm.start())
|
||||
le = len(raw) if le < 0 else le
|
||||
if FAKE in raw[ls[ln - 1]:le]:
|
||||
continue
|
||||
edits.append((le, le, f" {FAKE} gte via {name if name.endswith(('_m',)) or '_v' in name else p['renames'].get(name, name)} — a clobber Sony's macro lacks (P36 T5 {label})"))
|
||||
marked_lines.add(ln)
|
||||
rec["marked"] += 1
|
||||
# direct statements
|
||||
for s in p["direct"]:
|
||||
pos = ls[s["line"] - 1] + s["col"] - 1
|
||||
e = dl.stmt_end(m, pos)
|
||||
if e < 0 or not dl.ASM_HEAD.match(m, pos):
|
||||
rec["direct"].append(dict(line=s["line"], action="refused", why="statement not found at the census position"))
|
||||
continue
|
||||
how, text, name = direct_rewrite(s, canon)
|
||||
if how == "call":
|
||||
edits.append((pos, e, text))
|
||||
rec["direct_calls"] += 1
|
||||
rec["direct"].append(dict(line=s["line"], action="call", name=name))
|
||||
elif how == "lever":
|
||||
le = m.find("\n", e)
|
||||
le = len(raw) if le < 0 else le
|
||||
if FAKE not in raw[ls[s["line"] - 1]:le] and s["line"] not in marked_lines:
|
||||
edits.append((le, le, f" {FAKE} gte direct — clobbers {name} ({', '.join(text if isinstance(text, list) else [str(text)])}) beyond Sony's (P36 T5 {label})".replace("clobbers " + str(text) + " (", "as " + str(text) + " (")))
|
||||
marked_lines.add(s["line"])
|
||||
rec["marked"] += 1
|
||||
rec["direct_levers"] += 1
|
||||
rec["direct"].append(dict(line=s["line"], action="lever", variant=text, delta=name))
|
||||
else:
|
||||
rec["direct_none"] += 1
|
||||
rec["direct"].append(dict(line=s["line"], action="none", why=text))
|
||||
return edits, rec
|
||||
|
||||
|
||||
def apply_batch(a):
|
||||
clean, dirty = dl.src_clean()
|
||||
if not clean:
|
||||
sys.exit(f"gte_consolidate --apply: src/ is dirty — commit or tools/delever.py --restore first:\n{dirty[:400]}")
|
||||
ok, why = oracle.calibration_current()
|
||||
if not ok:
|
||||
sys.exit(f"gte_consolidate --apply: calibration not current ({why})")
|
||||
if not (REPO / HEADER).exists() or '#include "gte_inline.h"' not in (REPO / COMMON).read_text():
|
||||
sys.exit("gte_consolidate --apply: the header is not in place (--header first, then the fleet run and its commit)")
|
||||
dl.ensure_census(a.jobs)
|
||||
inv = inventory(a.jobs, quiet=True)
|
||||
canon = canonical_table(inv, quiet=True)
|
||||
files = plan_files(inv, canon, a.only)
|
||||
order = sorted(files, key=lambda tu: (-len(files[tu]["defs"]) - len(files[tu]["direct"]), tu))
|
||||
ledger_rows = [json.loads(l) for l in LEDGER.read_text().splitlines() if l.strip()] if LEDGER.exists() else []
|
||||
done = {r["tu"] for r in ledger_rows if r.get("rung") == "gte" and r.get("verdict") in ("CONSOLIDATED", "UNCHANGED")}
|
||||
order = [tu for tu in order if tu not in done or a.rejudge][:a.batch]
|
||||
print(f"gte_consolidate --apply {a.label}: {len(order)} files of {len(files)} with work ({len(done)} done) — "
|
||||
f"{sum(len(files[t]['defs']) for t in order)} definitions, {sum(len(files[t]['direct']) for t in order)} direct statements", flush=True)
|
||||
if not order:
|
||||
print("gte_consolidate --apply: nothing to do")
|
||||
return 0
|
||||
recipes = oracle.load_recipes()["recipes"]
|
||||
by_src = oracle.recipes_by_src(recipes)
|
||||
inc = dl.includers()
|
||||
def recipes_for(tu):
|
||||
return [r for t in inc.get(tu, []) for r in by_src.get(t, [])] if tu.endswith(".h") else by_src.get(tu, [])
|
||||
dl.INFLIGHT.write_text(json.dumps(dict(label=a.label, files={tu: (REPO / tu).read_text(errors="surrogateescape") for tu in order})))
|
||||
log_p = RUN / f"apply_{a.label}.log"
|
||||
log_f = open(log_p, "a")
|
||||
def log(msg):
|
||||
line = f"{time.strftime('%H:%M:%S')} {msg}"
|
||||
with _LOCK:
|
||||
print(line, flush=True)
|
||||
log_f.write(line + "\n")
|
||||
log_f.flush()
|
||||
pool = ThreadPoolExecutor(max_workers=a.jobs)
|
||||
results = []
|
||||
|
||||
def work(tu):
|
||||
path = REPO / tu
|
||||
st = path.stat()
|
||||
raw = path.read_text(errors="surrogateescape")
|
||||
recs = recipes_for(tu)
|
||||
row = dict(ts=time.strftime("%Y-%m-%d %H:%M:%S"), label=a.label, rung="gte", calib=dict(head=oracle.head(), stamp=oracle.config_stamp()),
|
||||
tu=tu, fn=None, addr=None, aliases=None, header=tu.endswith(".h"), sites=[], compiles=0, seconds=0.0)
|
||||
if not recs:
|
||||
row["verdict"] = "NO-RECIPE"
|
||||
return row
|
||||
plan_tu = files[tu]
|
||||
# the variant trial: a definition whose only difference from the canonical one is a clobber is tried AS canonical (deleted, its
|
||||
# uses renamed to the canonical name), one at a time on the untouched text; IDENTICAL -> the clobber was no lever here
|
||||
variants = [d for d in plan_tu["defs"] if d["action"] == "rename"]
|
||||
freed = 0
|
||||
for d in variants:
|
||||
trial = dict(defs=[dict(d, action="delete")], direct=[], renames=({d["name"]: d["canonical"]} if d["name"] != d["canonical"] else {}))
|
||||
try:
|
||||
ed, _ = file_edits(tu, raw, trial, canon, a.label)
|
||||
cand = dl.apply_edits(raw, ed)
|
||||
if cand == raw:
|
||||
continue
|
||||
try:
|
||||
v, dt, err = oracle.judge_all(recs, cand, tag="gtev", write_path=(tu if tu.endswith(".h") else None))
|
||||
finally:
|
||||
dl.restore_file(path, raw, st)
|
||||
row["compiles"] += len(recs)
|
||||
row["seconds"] += dt
|
||||
if v == "IDENTICAL":
|
||||
d["action"], d["trial"] = "delete", "IDENTICAL"
|
||||
if d["name"] != d["canonical"]:
|
||||
plan_tu["renames"][d["name"]] = d["canonical"]
|
||||
plan_tu["renames"].pop(d["name"], None) if d["name"] == d["canonical"] else None
|
||||
freed += 1
|
||||
else:
|
||||
d["trial"] = v
|
||||
except (Refuse, dl.Refuse) as ex:
|
||||
d["trial"] = f"REFUSED {ex}"
|
||||
# a variant that stays a variant keeps its rename to <name>_m
|
||||
for d in variants:
|
||||
if d["action"] == "rename" and d["name"] != d["variant"]:
|
||||
plan_tu["renames"][d["name"]] = d["variant"]
|
||||
try:
|
||||
edits, rec = file_edits(tu, raw, plan_tu, canon, a.label)
|
||||
except Refuse as ex:
|
||||
row.update(verdict="REFUSED", why=str(ex))
|
||||
log(f" {tu}: REFUSED {ex}")
|
||||
return row
|
||||
rec["variants_freed"] = freed
|
||||
rec["variants_kept"] = sum(1 for d in variants if d["action"] == "rename")
|
||||
row["record"] = rec
|
||||
cands = []
|
||||
try:
|
||||
cands.append(("all", dl.apply_edits(raw, edits)))
|
||||
no_direct = [e for e in edits if not any(e[0] == dl.line_starts(raw)[s["line"] - 1] + s["col"] - 1 for s in files[tu]["direct"])]
|
||||
if len(no_direct) != len(edits):
|
||||
cands.append(("defs-only", dl.apply_edits(raw, no_direct)))
|
||||
except dl.Refuse as ex:
|
||||
row.update(verdict="REFUSED", why=f"edits: {ex}")
|
||||
log(f" {tu}: REFUSED edits {ex}")
|
||||
return row
|
||||
verdict = "UNCHANGED"
|
||||
for tag, cand in cands:
|
||||
if cand == raw:
|
||||
continue
|
||||
try:
|
||||
if tu.endswith(".h") and len(recs) > 1:
|
||||
path.write_text(cand, errors="surrogateescape")
|
||||
vs = list(pool.map(lambda r: oracle.judge(r, None, tag="gte"), recs))
|
||||
bad = [v for v in vs if v[0] != "IDENTICAL"]
|
||||
v, dt, err = (bad[0][0], sum(x[1] for x in vs), bad[0][2]) if bad else ("IDENTICAL", sum(x[1] for x in vs), "")
|
||||
else:
|
||||
v, dt, err = oracle.judge_all(recs, cand, tag="gte", write_path=(tu if tu.endswith(".h") else None))
|
||||
finally:
|
||||
dl.restore_file(path, raw, st)
|
||||
row["compiles"] += len(recs)
|
||||
row["seconds"] += dt
|
||||
if v == "IDENTICAL":
|
||||
path.write_text(cand, errors="surrogateescape")
|
||||
verdict = "CONSOLIDATED" if tag == "all" else "CONSOLIDATED-DEFS-ONLY"
|
||||
if tag != "all":
|
||||
rec["direct_calls_refused"] = rec.pop("direct_calls", 0)
|
||||
log(f" {tu}: the direct-statement rewrites DIFFER — kept the definition edits only")
|
||||
break
|
||||
verdict = f"DIFFERS-{tag}" if v == "DIFFERS" else f"{v}-{tag}"
|
||||
log(f" {tu}: {tag} {v} {err[:120]}")
|
||||
row["verdict"] = verdict if verdict.startswith(("CONSOLIDATED", "UNCHANGED")) else "REFUSED"
|
||||
if not verdict.startswith(("CONSOLIDATED", "UNCHANGED")):
|
||||
row["why"] = verdict
|
||||
log(f" {tu}: {row['verdict']} — deleted {rec['deleted']} defs, renamed {rec['renamed_defs']}, use renames {rec['use_renames']}, "
|
||||
f"direct calls {rec.get('direct_calls', 0)}, direct levers {rec['direct_levers']}, unmatched {rec['direct_none']}, marked {rec['marked']}")
|
||||
return row
|
||||
tus = [tu for tu in order if not tu.endswith(".h")]
|
||||
hdrs = [tu for tu in order if tu.endswith(".h")]
|
||||
with ThreadPoolExecutor(max_workers=a.jobs) as ex:
|
||||
results = list(ex.map(work, tus))
|
||||
for h in hdrs:
|
||||
results.append(work(h))
|
||||
pool.shutdown(wait=True)
|
||||
dl.ledger_append(results)
|
||||
agg = collections.Counter()
|
||||
for r in results:
|
||||
agg[r["verdict"]] += 1
|
||||
for k in ("deleted", "renamed_defs", "use_renames", "direct_calls", "direct_levers", "direct_none", "marked", "variants_freed", "variants_kept"):
|
||||
agg[k] += r.get("record", {}).get(k, 0)
|
||||
agg["compiles"] += r["compiles"]
|
||||
line = (f"gte_consolidate: batch {a.label} — {len(order)} files: {agg['CONSOLIDATED']} consolidated, {agg['CONSOLIDATED-DEFS-ONLY']} defs-only, "
|
||||
f"{agg['UNCHANGED']} unchanged, {agg['REFUSED']} refused, {agg['NO-RECIPE']} no-recipe · definitions deleted {agg['deleted']} / renamed as lever variants "
|
||||
f"{agg['renamed_defs']} (variant trials: {agg['variants_freed']} freed, {agg['variants_kept']} kept) · use renames {agg['use_renames']} · direct statements → calls {agg['direct_calls']} / lever {agg['direct_levers']} / "
|
||||
f"unmatched {agg['direct_none']} · markers {agg['marked']} · compiles {agg['compiles']} · final {len(order) - agg['REFUSED'] - agg['NO-RECIPE']}/{len(order)} identical")
|
||||
log(line)
|
||||
(RUN / f"batch_{a.label}.json").write_text(json.dumps(dict(label=a.label, files=order, totals=dict(agg), rows=results), indent=1))
|
||||
return 0 if agg["REFUSED"] == 0 else 1
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
# --sweep: dead asm-bearing macros; a compound macro's inner asm dropped when byte-neutral
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
def sweep(a):
|
||||
clean, dirty = dl.src_clean()
|
||||
if not clean:
|
||||
sys.exit(f"gte_consolidate --sweep: src/ is dirty:\n{dirty[:400]}")
|
||||
ok, why = oracle.calibration_current()
|
||||
if not ok:
|
||||
sys.exit(f"gte_consolidate --sweep: calibration not current ({why})")
|
||||
dl.ensure_census(a.jobs)
|
||||
sites = dl.load_sites()
|
||||
uses_by_file = collections.defaultdict(collections.Counter)
|
||||
for s in sites:
|
||||
if s.get("via"):
|
||||
uses_by_file[s["tu"]][s["via"]] += 1
|
||||
inc = dl.includers()
|
||||
# a macro used by an included shared header counts as used by every includer
|
||||
hdr_uses = {h: uses_by_file.get(h, collections.Counter()) for h in inc}
|
||||
recipes = oracle.load_recipes()["recipes"]
|
||||
by_src = oracle.recipes_by_src(recipes)
|
||||
rows, n_dead, n_inner, n_files = [], 0, 0, 0
|
||||
files = files_under_src()
|
||||
if a.only:
|
||||
files = [p for p in files if any(o in p.as_posix() for o in a.only)]
|
||||
for p in files:
|
||||
rel = p.relative_to(REPO).as_posix()
|
||||
raw = p.read_text(errors="surrogateescape")
|
||||
if "#define" not in raw:
|
||||
continue
|
||||
blocks = [(l0, l1, name, body) for (l0, l1, name, body) in lc.define_blocks(raw) if lc.macro_kind(body) and lc.macro_kind(body) != "gte"]
|
||||
if not blocks:
|
||||
continue
|
||||
used = collections.Counter(uses_by_file.get(rel, {}))
|
||||
for h, hu in hdr_uses.items():
|
||||
if rel in inc.get(h, []):
|
||||
used.update(hu)
|
||||
m = dl.same_len_mask(raw)
|
||||
ls = dl.line_starts(raw)
|
||||
edits, dead, inner = [], [], []
|
||||
for (l0, l1, name, body) in blocks:
|
||||
start, end = ls[l0 - 1], ls[l1] if l1 < len(ls) else len(raw)
|
||||
if used.get(name, 0) == 0 and not re.search(r"\b%s\b" % re.escape(name), m[:start] + m[end:]):
|
||||
edits.append((start, end, ""))
|
||||
dead.append(name)
|
||||
continue
|
||||
# a compound macro: drop the asm statement line(s) inside the definition (the launder/barrier steer), keep the code
|
||||
lines = raw[start:end].split("\n")
|
||||
keep, dropped = [], 0
|
||||
for ln in lines:
|
||||
if lc.ASM_KW.search(ln) and ("__asm__" in ln or "asm" in ln) and ('"' in ln):
|
||||
dropped += 1
|
||||
continue
|
||||
keep.append(ln)
|
||||
if dropped and len(keep) > 1:
|
||||
new = "\n".join(keep)
|
||||
if not new.endswith("\n") and raw[start:end].endswith("\n"):
|
||||
new += "\n"
|
||||
edits.append((start, end, new))
|
||||
inner.append(name)
|
||||
if not edits:
|
||||
continue
|
||||
recs = [r for t in inc.get(rel, []) for r in by_src.get(t, [])] if rel.endswith(".h") else by_src.get(rel, [])
|
||||
if not recs:
|
||||
print(f" {rel}: no recipe — skipped")
|
||||
continue
|
||||
st = p.stat()
|
||||
try:
|
||||
cand = dl.apply_edits(raw, edits)
|
||||
except dl.Refuse as ex:
|
||||
print(f" {rel}: REFUSED {ex}")
|
||||
continue
|
||||
verdict_all = None
|
||||
for tag, cand_t in (("all", cand), ("dead-only", dl.apply_edits(raw, [e for e in edits if e[2] == ""]) if inner and dead else None)):
|
||||
if cand_t is None or cand_t == raw:
|
||||
continue
|
||||
try:
|
||||
v, dt, err = oracle.judge_all(recs, cand_t, tag="sweep", write_path=(rel if rel.endswith(".h") else None))
|
||||
finally:
|
||||
dl.restore_file(p, raw, st)
|
||||
if v == "IDENTICAL":
|
||||
p.write_text(cand_t, errors="surrogateescape")
|
||||
verdict_all = tag
|
||||
break
|
||||
print(f" {rel}: {tag} {v} {err[:100]}")
|
||||
n_files += verdict_all is not None
|
||||
if verdict_all == "all":
|
||||
n_dead += len(dead)
|
||||
n_inner += len(inner)
|
||||
elif verdict_all == "dead-only":
|
||||
n_dead += len(dead)
|
||||
inner_kept = inner
|
||||
inner = []
|
||||
rows.append(dict(ts=time.strftime("%Y-%m-%d %H:%M:%S"), label=a.label, rung="sweep", calib=dict(head=oracle.head(), stamp=oracle.config_stamp()),
|
||||
tu=rel, fn=None, addr=None, aliases=None, header=rel.endswith(".h"), verdict=("SWEPT-" + verdict_all if verdict_all else "REFUSED"),
|
||||
dead=dead, inner_dropped=(inner if verdict_all else []), sites=[], compiles=len(recs)))
|
||||
print(f" {rel}: {rows[-1]['verdict']} — dead {dead} inner-asm dropped {inner if verdict_all else []}")
|
||||
dl.ledger_append(rows)
|
||||
print(f"gte_consolidate --sweep {a.label}: {n_dead} dead asm-bearing macro definitions deleted, {n_inner} compound macros' inner asm dropped, "
|
||||
f"{n_files} files written, {sum(1 for r in rows if r['verdict'] == 'REFUSED')} refused")
|
||||
return 0
|
||||
|
||||
|
||||
def status():
|
||||
rows = [json.loads(l) for l in LEDGER.read_text().splitlines() if l.strip()] if LEDGER.exists() else []
|
||||
g = [r for r in rows if r.get("rung") in ("gte", "sweep")]
|
||||
print(f"gte_consolidate --status: {len(g)} rows — {dict(collections.Counter(r['verdict'] for r in g))}")
|
||||
return 0
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
def selftest():
|
||||
ok = True
|
||||
def fail(msg):
|
||||
nonlocal ok
|
||||
ok = False
|
||||
print("selftest FAIL:", msg)
|
||||
# 1. the signature engine: three spellings of rtps are one signature; a template with an operand binds to $4
|
||||
s1 = signature('"nop;nop;rtps"')
|
||||
s2 = signature('"nop;" "nop;" ".word 0x4a180001"')
|
||||
s3 = signature('"nop;nop;cop2 0x0180001"')
|
||||
if not (s1["ok"] and s1["bytes"] == s2["bytes"] == s3["bytes"]):
|
||||
fail(f"rtps spellings: {s1['bytes'][:40]} {s2['bytes'][:40]} {s3['bytes'][:40]}")
|
||||
s4 = signature('"lwc2 $0, 0( %0 );" "lwc2 $1, 4( %0 )" : : "r"( r0 )')
|
||||
s5 = signature('"lwc2 $0, 0(%0)\\n" "lwc2 $1, 4(%0)\\n" : : "r"(r0) : "memory"')
|
||||
if not (s4["ok"] and s4["bytes"] == s5["bytes"] and s4["nin"] == 1 and s4["clob"] == [] and s5["clob"] == ["memory"]):
|
||||
fail(f"ldv0 spellings/clobbers: {s4} {s5}")
|
||||
if signature('"nop;nop;rtpt"')["bytes"] == s1["bytes"]:
|
||||
fail("rtps and rtpt must differ")
|
||||
# 2. the canonical table on a synthetic inventory: canonical / variant / private name / concatenated direct statement
|
||||
inv = dict(definitions=[
|
||||
dict(tu="a.c", l0=1, l1=2, name="gte_ldv0", params="( r0 )", body='__asm__ volatile ( "lwc2 $0, 0( %0 );" "lwc2 $1, 4( %0 )" : : "r"( r0 ) )', kind="gte", sig=s4),
|
||||
dict(tu="b.c", l0=1, l1=2, name="gte_ldv0", params="(r0)", body='__asm__ __volatile__( "lwc2 $0, 0(%0)\\n" "lwc2 $1, 4(%0)\\n" : : "r"(r0) : "memory")', kind="gte", sig=s5),
|
||||
dict(tu="c.c", l0=1, l1=2, name="LDV0_DA34", params="( r0 )", body='__asm__ volatile ("lwc2 $0, 0( %0 );lwc2 $1, 4( %0 )" : : "r"( r0 ))', kind="gte", sig=s4),
|
||||
dict(tu="a.c", l0=3, l1=3, name="gte_rtps", params="()", body='__asm__ volatile ("nop;nop;rtps")', kind="gte", sig=s1),
|
||||
], direct=[], uses={})
|
||||
canon = canonical_table(inv, quiet=True)
|
||||
t = canon["canonical"].get(sig_key(s4))
|
||||
if not t or t["name"] != "gte_ldv0" or t["clob"] != [] or t["defs"] != 2:
|
||||
fail(f"canonical ldv0: {t}")
|
||||
v = [x for x in canon["variants"].values() if x["of"] == "gte_ldv0"]
|
||||
if len(v) != 1 or v[0]["name"] != "gte_ldv0_m" or v[0]["extra"] != ["memory"]:
|
||||
fail(f"ldv0 memory variant: {v}")
|
||||
# 3. direct statements: a canonical call, a lever, a concatenation
|
||||
d_call = dict(sig=signature('"lwc2 $0, 0(%0);" "lwc2 $1, 4(%0)" : : "r"(p)'))
|
||||
d_lever = dict(sig=signature('"lwc2 $0, 0(%0);" "lwc2 $1, 4(%0)" : : "r"(p) : "memory"'))
|
||||
d_cat = dict(sig=signature('"lwc2 $0, 0(%0);" "lwc2 $1, 4(%0);" "nop;nop;rtps" : : "r"(p)'))
|
||||
if direct_rewrite(d_call, canon) != ("call", "gte_ldv0(p);", "gte_ldv0"):
|
||||
fail(f"direct call: {direct_rewrite(d_call, canon)}")
|
||||
if direct_rewrite(d_lever, canon)[0] != "lever" or direct_rewrite(d_lever, canon)[1] != "gte_ldv0_m":
|
||||
fail(f"direct lever: {direct_rewrite(d_lever, canon)}")
|
||||
r = direct_rewrite(d_cat, canon)
|
||||
if r != ("call", "gte_ldv0(p); gte_rtps();", "gte_ldv0+gte_rtps"):
|
||||
fail(f"direct concatenation: {r}")
|
||||
# 4. the per-file edits on a fixture: the canonical def deleted, the private use renamed, the variant renamed + marked, a direct call
|
||||
fx = ('#define gte_ldv0( r0 ) __asm__ volatile ( "lwc2 $0, 0( %0 );" "lwc2 $1, 4( %0 )" : : "r"( r0 ) )\n'
|
||||
'#define LDV0_DA34( r0 ) __asm__ volatile ("lwc2 $0, 0( %0 );lwc2 $1, 4( %0 )" : : "r"( r0 ))\n'
|
||||
'#define gte_ldv0m( r0 ) __asm__ __volatile__( "lwc2 $0, 0(%0)\\n" "lwc2 $1, 4(%0)\\n" : : "r"(r0) : "memory")\n'
|
||||
'void f(s32 *v) {\n LDV0_DA34(v);\n gte_ldv0m(v);\n __asm__ volatile("lwc2 $0, 0(%0);" "lwc2 $1, 4(%0)" : : "r"(v));\n}\n')
|
||||
defs = []
|
||||
for (l0, l1, name, body) in lc.define_blocks(fx):
|
||||
sg = signature(asm_inner(body))
|
||||
defs.append(dict(tu="fx.c", l0=l0, l1=l1, name=name, params=define_params(fx, l0), body=body, kind="gte", sig=sg))
|
||||
fm, fls = dl.same_len_mask(fx), dl.line_starts(fx)
|
||||
fpos = fls[6] + 4
|
||||
fo = fm.find("(", fpos)
|
||||
finner = fm[fo + 1:lc._paren_span(fm, fo)]
|
||||
inv2 = dict(definitions=defs, direct=[dict(tu="fx.c", line=7, col=5, fn="f", inner=finner, sig=signature(finner))], uses={})
|
||||
canon2 = canonical_table(inv2, quiet=True)
|
||||
plan = plan_files(inv2, canon2)
|
||||
edits, rec = file_edits("fx.c", fx, plan["fx.c"], canon2, "self")
|
||||
out = dl.apply_edits(fx, edits)
|
||||
if "#define gte_ldv0(" in out or "#define LDV0_DA34" in out:
|
||||
fail("canonical/private definitions must be deleted")
|
||||
if "#define gte_ldv0_m( r0 )" not in out or "gte variant `gte_ldv0_m`" not in out:
|
||||
fail(f"the variant definition renamed + noted:\n{out}")
|
||||
if " gte_ldv0(v);\n" not in out:
|
||||
fail(f"the private use renamed to the canonical name:\n{out}")
|
||||
if "gte_ldv0_m(v); // !FAKE: gte via gte_ldv0_m" not in out:
|
||||
fail(f"the lever variant's use marked:\n{out}")
|
||||
if " gte_ldv0(v);\n}" not in out:
|
||||
fail(f"the direct statement rewritten into the canonical call:\n{out}")
|
||||
if rec["deleted"] != 2 or rec["renamed_defs"] != 1 or rec["use_renames"] != 2 or rec["direct_calls"] != 1 or rec["marked"] != 1:
|
||||
fail(f"record: {rec}")
|
||||
# 5. the header renders every canonical macro once, with its parameters
|
||||
h = render_header(canon2)
|
||||
if h.count("#define gte_ldv0(") != 1 or "#define gte_ldv0_m" in h:
|
||||
fail("header: one canonical definition, no variant")
|
||||
print(f"gte_consolidate --selftest: {'OK' if ok else 'FAIL'} — signatures rtps={s1['bytes'][16:24]} ldv0={s4['bytes'][:16]}; table {len(canon2['canonical'])} canonical / {len(canon2['variants'])} variant; fixture edits {len(edits)}")
|
||||
return ok
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description=__doc__.split("\n")[0])
|
||||
ap.add_argument("--inventory", action="store_true")
|
||||
ap.add_argument("--header", action="store_true")
|
||||
ap.add_argument("--apply", action="store_true")
|
||||
ap.add_argument("--sweep", action="store_true")
|
||||
ap.add_argument("--status", action="store_true")
|
||||
ap.add_argument("--selftest", action="store_true")
|
||||
ap.add_argument("--batch", type=int, default=400)
|
||||
ap.add_argument("--label", default=None)
|
||||
ap.add_argument("--only", nargs="*", default=None)
|
||||
ap.add_argument("--rejudge", action="store_true")
|
||||
ap.add_argument("-j", "--jobs", type=int, default=12)
|
||||
a = ap.parse_args()
|
||||
RUN.mkdir(parents=True, exist_ok=True)
|
||||
if a.selftest:
|
||||
sys.exit(0 if selftest() else 1)
|
||||
if a.status:
|
||||
sys.exit(status())
|
||||
if a.inventory:
|
||||
dl.ensure_census(a.jobs)
|
||||
inv = inventory(a.jobs)
|
||||
canonical_table(inv)
|
||||
return
|
||||
if a.header:
|
||||
inv = json.loads(INVENTORY.read_text()) if INVENTORY.exists() else inventory(a.jobs, quiet=True)
|
||||
canon = canonical_table(inv, quiet=True)
|
||||
write_header(canon)
|
||||
return
|
||||
if a.apply:
|
||||
if not a.label:
|
||||
sys.exit("--label is required")
|
||||
sys.exit(apply_batch(a))
|
||||
if a.sweep:
|
||||
if not a.label:
|
||||
sys.exit("--label is required")
|
||||
sys.exit(sweep(a))
|
||||
ap.print_help()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -69,7 +69,50 @@ MIPS_REG_NAMES = {"zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1",
|
||||
GTE_MNEMONICS = {"lwc2", "swc2", "mtc2", "mfc2", "ctc2", "cfc2", "cop2", "rtps", "rtpt", "nclip", "ncds", "nccs", "ncdt", "ncct",
|
||||
"ncs", "nct", "cdp", "cc", "dpcs", "dpct", "dpcl", "intpl", "sqr", "op", "gpf", "gpl", "avsz3", "avsz4", "mvmva"}
|
||||
FAKE_MARK = "!FAKE:"
|
||||
NON_LEVER_KINDS = {"gte", "verbatim-body"} # Sony's coprocessor idiom; a manifest-listed hand-asm routine — censused, never a lever
|
||||
NON_LEVER_KINDS = {"gte", "verbatim-body", "gte-unsigned"} # Sony's coprocessor idiom; a manifest-listed hand-asm routine — censused, never a lever
|
||||
GTE_LEVER_KIND = "gte-lever" # a GTE op whose clobbers exceed its canonical's (a `_m`/`_v` variant macro, or a direct statement): a steer
|
||||
GTE_VARIANT_NAME = re.compile(r"(_m|_v[0-9a-f]{4})$")
|
||||
_gte_tables = None
|
||||
|
||||
|
||||
def _gte_lookup():
|
||||
"""(sig_cache, canonical-by-bytes) from tools/gte_consolidate.py's files, read once; ({}, {}) when T5 has not run."""
|
||||
global _gte_tables
|
||||
if _gte_tables is None:
|
||||
cache, canon = {}, {}
|
||||
try:
|
||||
import json as _json
|
||||
base = REPO / ".run" / "P36" / "gte"
|
||||
if (base / "sig_cache.json").exists():
|
||||
cache = _json.loads((base / "sig_cache.json").read_text())
|
||||
if (base / "canonical.json").exists():
|
||||
for key, tbl in _json.loads((base / "canonical.json").read_text())["canonical"].items():
|
||||
canon[key] = dict(name=tbl["name"], clob=sorted(tbl["clob"]))
|
||||
except Exception: # noqa: BLE001 — a torn file reads as "no table"
|
||||
cache, canon = {}, {}
|
||||
_gte_tables = (cache, canon)
|
||||
return _gte_tables
|
||||
|
||||
|
||||
def classify_direct_gte(inner):
|
||||
"""'gte' (Sony's idiom or no canonical to compare), 'gte-lever' (clobbers beyond the canonical's), 'gte-unsigned' (a text the
|
||||
consolidation tool has not signed yet — run `tools/gte_consolidate.py --inventory`)."""
|
||||
cache, canon = _gte_lookup()
|
||||
if not canon:
|
||||
return "gte"
|
||||
try:
|
||||
import gte_consolidate as gc
|
||||
tmpl, outs, ins, clob = gc.parse_asm(inner)
|
||||
bound = gc.bind_regs(tmpl, len(outs), len(ins))
|
||||
except Exception: # noqa: BLE001
|
||||
return "gte-unsigned"
|
||||
b = cache.get(bound)
|
||||
if not b or b.startswith("ERROR"):
|
||||
return "gte-unsigned"
|
||||
c = canon.get(f"{b}|{len(outs)}|{len(ins)}")
|
||||
if not c:
|
||||
return "gte"
|
||||
return GTE_LEVER_KIND if sorted(clob) != c["clob"] else "gte"
|
||||
# an `asm-body` site (a whole routine as one asm statement inside a C shell) becomes `verbatim-body` only when the manifest lists the
|
||||
# routine as PERMANENT-VERBATIM; an unlisted or DECOMPILE-* one stays a lever and the guard (verbatim_check) names it
|
||||
NORM_SYM = re.compile(r"\b(?:func|D)_80[0-9A-Fa-f]{6}\b")
|
||||
@@ -479,6 +522,8 @@ def walk_file(raw, rel, is_header):
|
||||
continue
|
||||
role(pos, "asm-stmt")
|
||||
d = span_of_line.get(ln)
|
||||
if kind == "gte" and d:
|
||||
kind = classify_direct_gte(masked[o + 1:c])
|
||||
if d and is_whole_body_asm(masked, line_starts[d["line"] - 1], line_starts[d["end"]] - 1, pos, c):
|
||||
kind = "asm-body" # a whole routine as one asm statement inside a C shell (any template: GTE ones too); the manifest decides its fate
|
||||
add_site("B", kind, pos, masked[pos:c + 1], detail=detail, via="")
|
||||
@@ -501,7 +546,10 @@ def walk_file(raw, rel, is_header):
|
||||
continue
|
||||
name = m.group(1)
|
||||
c = _paren_span(masked, m.end() - 1)
|
||||
add_site("B", asm_macro_names[name], m.start(), masked[m.start():c + 1] if c > 0 else name, detail=name, via=name)
|
||||
k = asm_macro_names[name]
|
||||
if k == "gte" and GTE_VARIANT_NAME.search(name):
|
||||
k = GTE_LEVER_KIND
|
||||
add_site("B", k, m.start(), masked[m.start():c + 1] if c > 0 else name, detail=name, via=name)
|
||||
|
||||
# ---- 4. volatile tokens
|
||||
for m in VOLATILE_KW.finditer(masked):
|
||||
@@ -814,6 +862,17 @@ def run_census(jobs, use_cache=True, out_dir=OUT_DIR_DEFAULT, want_sites=False,
|
||||
what="whole routines written in assembly inside a C shell (the §265 lane's in-function form): the manifest's "
|
||||
"PERMANENT-VERBATIM rows are hand asm and not levers; an asm-body the manifest does not list as permanent "
|
||||
"stays a lever (unlisted_routines names them with their disposition)"),
|
||||
gte_levers=dict(sites=sum(1 for s in sites if s["kind"] == GTE_LEVER_KIND),
|
||||
marked=sum(1 for s in sites if s["kind"] == GTE_LEVER_KIND and s["marked"]),
|
||||
unmarked=sum(1 for s in sites if s["kind"] == GTE_LEVER_KIND and not s["marked"]),
|
||||
via_macro=sum(1 for s in sites if s["kind"] == GTE_LEVER_KIND and s.get("via")),
|
||||
direct=sum(1 for s in sites if s["kind"] == GTE_LEVER_KIND and not s.get("via")),
|
||||
unsigned=sum(1 for s in sites if s["kind"] == "gte-unsigned"),
|
||||
what="GTE ops whose clobbers exceed the canonical macro's (a scheduling steer): levers for T7, outside the headline number"),
|
||||
per_tu_asm_macro_definitions=dict(total=sum(1 for m in mdefs_all if m["tu"] != gte_header),
|
||||
by_kind=dict(collections.Counter(m["kind"] for m in mdefs_all if m["tu"] != gte_header)),
|
||||
gte_variants=sum(1 for m in mdefs_all if m["tu"] != gte_header and m["kind"] == "gte" and GTE_VARIANT_NAME.search(m["name"])),
|
||||
what="asm-bearing #define blocks outside the GTE header: 0 at the close (T5 deletes the canonical duplicates, T7 the variants)"),
|
||||
macro_definitions=dict(total=len(mdefs_all), names=len(macro_names), kinds=dict(macro_kinds),
|
||||
names_with_multiple_texts=sum(1 for n, t in macro_texts.items() if len(t) > 1),
|
||||
top=[(n, c, len(macro_texts[n])) for n, c in macro_names.most_common(25)]),
|
||||
@@ -888,6 +947,12 @@ def render(s):
|
||||
L.append(f" THE PHASE'S NUMBER (pins + asm statements, GTE excluded): {ab['sites']:,} sites in {ab['bodies']:,} bodies "
|
||||
f"({ab['distinct_bodies']:,} distinct) · marked !FAKE {ab['marked']:,} · UNMARKED {ab['unmarked']:,}")
|
||||
L.append(f" orphan !FAKE markers (no pin/asm site on the line nor below): {s.get('orphan_markers', {}).get('count', 0)}")
|
||||
g = s.get("gte_levers", {})
|
||||
if g:
|
||||
L.append(f" GTE levers (clobbers beyond the canonical macro's): {g['sites']:,} sites ({g['via_macro']:,} via a variant macro, {g['direct']:,} direct) · "
|
||||
f"marked {g['marked']:,} · UNMARKED {g['unmarked']:,} · unsigned GTE statements {g['unsigned']:,}")
|
||||
pt = s.get("per_tu_asm_macro_definitions", {})
|
||||
L.append(f" per-TU asm macro definitions outside the GTE header: {pt.get('total', 0):,} {pt.get('by_kind', {})} (GTE variants {pt.get('gte_variants', 0):,})")
|
||||
p = s["pins"]
|
||||
L.append(f" pins: {p['sites']:,} · $0 {p['zero']:,} · $sp {p['sp']} · with initializer {p['init']:,} · volatile-qualified "
|
||||
f"{p['volatile_qualified']} · bare-name {p['bare_name']} · spellings {p['spelling']}")
|
||||
@@ -1032,9 +1097,12 @@ def main():
|
||||
ab = [x for x in sites if x["cls"] in "AB" and x["kind"] not in NON_LEVER_KINDS and x["tu"] != a.gte_header]
|
||||
un = [x for x in ab if not x["marked"]]
|
||||
if a.strict:
|
||||
print(f"lever_census --check --strict: pins {s['pins']['sites']}, asm {sum(1 for x in ab if x['cls'] == 'B')}, "
|
||||
f"volatile-needed {s['classes']['C']['sites']}, register-needed {s['classes']['D']['sites']} — {'OK' if not ab else 'FAIL'}")
|
||||
if ab:
|
||||
pt = s.get("per_tu_asm_macro_definitions", {}).get("total", 0)
|
||||
gl = s.get("gte_levers", {}).get("sites", 0)
|
||||
print(f"lever_census --check --strict: pins {s['pins']['sites']}, asm {sum(1 for x in ab if x['cls'] == 'B' and x['kind'] != GTE_LEVER_KIND)}, "
|
||||
f"gte-levers {gl}, per-TU asm macro definitions {pt}, volatile-needed {s['classes']['C']['sites']}, register-needed {s['classes']['D']['sites']} — "
|
||||
f"{'OK' if not ab and not pt else 'FAIL'}")
|
||||
if ab or pt:
|
||||
for x in ab[:10]:
|
||||
print(f" {x['tu']}:{x['line']} {x['fn']} {x['cls']}/{x['kind']} {x['detail']}")
|
||||
rc = 1
|
||||
|
||||
@@ -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:** 299 tool files in scope (submodules, vendored and downloaded code excluded), of which 299 live rows
|
||||
> below; per phase: P1 2 · P2 26 · P3 17 · P4 9 · P5 26 · P6 51 · P7 20 · P8 86 · P9 27 · P10 19 · PROJECT-ONLY 16. Superseded tools appear only as pointers to their successor (30 pointer rows); one-offs are omitted. Table rows in all: 329 (the installer checks its copy against this figure).
|
||||
> **Coverage:** 300 tool files in scope (submodules, vendored and downloaded code excluded), of which 300 live rows
|
||||
> below; per phase: P1 2 · P2 26 · P3 17 · P4 9 · P5 26 · P6 51 · P7 20 · P8 86 · P9 27 · P10 20 · PROJECT-ONLY 16. Superseded tools appear only as pointers to their successor (30 pointer rows); one-offs are omitted. Table rows in all: 330 (the installer checks its copy against this figure).
|
||||
|
||||
## P1 — extraction + manifest
|
||||
|
||||
@@ -327,6 +327,7 @@
|
||||
| `cast_call_sites.py` | add per-site function-pointer casts so a draft can call a differently typed callee | Adds per-site function-pointer casts so a draft can call a differently typed callee | repo src layout |
|
||||
| `ghidra_scripts/ApplySymbols.java` | apply curated names and signatures inside the analysis tool and save | The in-tool half of that mirror: apply curated names and signatures, save on exit | none |
|
||||
| `lever_census.py` | census every compiler-forcing construct in the C and gate the levers-off phase | 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, 0 orphan markers) and --strict (0 pins, 0 asm) gates, --sites for the delever ledger, a fixture self-test | repo paths, the shared-body scanner |
|
||||
| `gte_consolidate.py` | consolidate the GTE coprocessor inline-asm macros under Sony's names in one header and sweep dead lever macros | The Phase-36 T5 tool: every asm-bearing macro definition and direct GTE statement SIGNED by the build's own maspsx→as tail (template bytes with operands bound to fixed registers, operand counts, clobbers); one canonical text per signature named by PsyQ's inline_c.h convention (Sony's file supplies names and clobber lists, never opcode words) written to include/gte_inline.h; per file the canonical duplicates deleted, private names renamed, a variant with extra clobbers tried as canonical and kept as a marked <name>_m lever only when the object differs, direct statements rewritten into canonical calls (a concatenation of two included); every file judged through every recipe; --sweep deletes dead asm-bearing macros and drops a compound macro's inner asm when byte-neutral | Sony's inline_c.h path, the census sites, the oracle |
|
||||
| `macro_to_header.py` | convert a shared-body macro header into per-function plain-C headers included at each site | 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 | the macro form is this project's; a kit-born project shares headers from its first bank |
|
||||
| `asm_verbatim.py` | emit a function's target assembly as an inline-assembly body | Emits the file-scope inline-assembly body form from a disassembly file | repo asm layout |
|
||||
| `build_engine_types.py` | extract inline-defined types and typedefs from a source file into a shared header | Extracts inline-defined named types and typedefs from a source file into a shared header | repo shared-header path |
|
||||
|
||||
@@ -785,6 +785,7 @@ Every script under `tools/` (plus the two report make-targets), grouped by purpo
|
||||
| | `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 0 orphan markers — a `// !FAKE:` line with no pin/asm site on it nor on the line below) and `--check --strict` (0 pins, 0 asm outside the GTE header) gates, the JSON's `head` + stat-based `src_stamp` (delever refuses a census that does not describe the tree) and a per-file walk cache keyed on the walker's own hash (a tool change invalidates it, R35), `--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 AND the campaign tool. 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, refused if it is ever assigned), barrier / keep-alive → deleted, launder → deleted when it launders a value into itself, **an ASSIGNMENT `out = in;` when its output and input differ** (deleting those made cc1 2.7.2 abort — the T2 probe counted them NEEDED), a hand-placed instruction → its C (addu-$zero/move/la/lh/lw/addiu/sll/srl/and/andi/lui/li and the lui+addiu / lui+ori pairs), a macro-carried site → deleted for a pure launder statement macro (`SHB`), its value `((T)(p))` for a launder statement-expression, REFUSED for a compound macro (XFER/DRAW/RTP_SND: the lever is in the `#define`, T5), volatile / register → dropped. Per body: replay (a ledger exemplar with the same normalized text: 1 compile) → rung A strip-all → rung B greedy, through `delever_oracle` (verdicts IDENTICAL / DIFFERS / COMPILE-ERROR / COMPILE-CRASH). **The file is the write unit and its final compile the proof**: every body's accepted edits + the file-scope volatile edits + the `// !FAKE:` markers spliced once, compiled through every recipe of the file (a twin's object; every includer of a header, in parallel) — IDENTICAL or the file-scope edits are dropped, or the file is restored and REFUSED (COMBINATION-FAILED). `--plan` / `--apply --batch N --label L [--headers] [--only …] [-j 12]` (TUs in parallel, exemplar files before their copies; headers serial; the tree must be clean, the calibration current, the census's src stamp the tree's — it reruns the census itself), `--redraw REFUSED NOTHING-USABLE` (draw again the bodies whose latest ledger verdict is one of these — after a tool fix), `--restore` (from `inflight.json`, the only restore — R102; it also drops the in-flight batch's ledger rows into an ignored `ledger.jsonl.killed_<label>`, so a killed batch leaves no trace and its bodies are drawn again), `--status`, `--scrub [--only …]` (remove the census's ORPHAN markers — a `// !FAKE:` whose site is gone — each file judged through every recipe; a removed or rewritten site consumes its own trailing marker, so orphans arise only from older tool versions), `--apply-body TU FN FILE --label L [--rung E]` (T6/T7: a reshaped body judged on the bytes, refused if a class A/B lever remains), `--selftest` (the fixture through the census's own parser: every rewrite, the ladder against a stub oracle with a crashing site, the markers, a replay and a disagreeing replay, the ledger index, the oracle's crash forms), `--probe` (T2). The ledger `.run/P36/delever/ledger.jsonl`: one row per (tu, fn, addr, aliases) per judgement with every site's verdict REMOVED / REWRITTEN / NEEDED(oracle word) / REFUSED(why) / DEFERRED, the rung, the calibration id, the body's nhash BEFORE and AFTER — "done" = the current nhash is some row's after-hash; a body with a row's before-hash replays it. Class C/D survivors are never marked (decision 3); class A/B survivors carry `// !FAKE: <kind> <detail> — <verdict> (P36 <rung> <label>)` at the end of their line. Evidence: `apply_<label>.log`, `batch_<label>.json`. Per-batch setup: the per-file include lists are cached in the ignored `includers_cache.json` on (mtime, size) — 29 s → 1.3 s, proven equal to the uncached map. |
|
||||
| | `tools/gte_consolidate.py` | **(P36 T5)** The GTE consolidation. `--inventory`: every asm-bearing `#define` and every direct GTE statement signed by the build's own tail (`maspsx --aspsx-version=2.56` → `mipsel-linux-gnu-as` with `include/macro.inc` + `gte_macros.inc`, where `rtps`/`mvmva`/`sqr 0` are GAS macros): signature = (bytes with %k bound to $4–$7 / $2–$3, #outputs, #inputs); `nop;nop;rtps`, `.word 0x4a180001` and `cop2 0x0180001` are one signature. The canonical table (`.run/P36/gte/canonical.json`): one text per signature (the majority body — byte-proven in the fleet), Sony's name when the operand counts agree with `inline_c.h` (Sony's converted header is unreliable for opcode words: `gte_rtps` there is `.word 0x0000007f`), Sony's clobber set canonical even when every definition carries the steer (the body is synthesized with Sony's clobbers); a definition with the canonical bytes and OTHER clobbers is a lever variant `<name>_m` / `<name>_v<hash>`. `--header` writes `include/gte_inline.h` (50 macros at T5) and the include in `common.h`. `--apply --batch N --label L`: per file, canonical duplicates deleted, private names renamed to the canonical, each variant TRIED as canonical (the clobber dropped, judged) and kept as a marked `_m` lever only when the object differs, direct statements rewritten into canonical calls (a concatenation of two canonical macros included; a direct statement with extra clobbers is marked as a lever; an unmatched sequence is counted); the file judged through every recipe, the definition edits alone retried when the direct rewrites differ. `--sweep`: dead asm-bearing macros (0 uses in the TU and its included headers) deleted; a compound macro's inner asm dropped when byte-neutral. `tools/delever_cycle.sh … gte` runs the apply batches. The census classes a variant's uses and a direct statement with clobbers beyond the canonical's as `gte-lever` (marked, outside the headline number, 0 at the close) and counts per-TU asm macro definitions. |
|
||||
| | `tools/delever_cycle.sh` | **(P36 T3)** The unattended batch cycle: `[LABEL_PREFIX=…] [TASK=T4] [REDRAW="REFUSED NOTHING-USABLE"] tools/delever_cycle.sh START END [BATCH=300] [tus|headers] [ONLY="alias …"]` — per batch: the tree must be clean (a dotfile under `src/` refuses), the oracle recalibrated when stale (2–3 s; every commit stales it), `delever --apply` (exit code + its `final X/X identical` — an empty verify line is a failure, R32), the CLEAN fleet run as the outer gate (`218 passed, 0 failed of 218`, R22 — it also refreshes `build/`, the oracle's baselines), the census rerun on the new tree, the phase-log entry + the 🛑 headline (R101), one commit of `src/` + the ledger/batch record/logs + the census + the log (R42). Stops on the first red with the batch's files in place (inspect, then `delever --restore`); stops cleanly when nothing is drawable. Size a call so a batch stays under ~10 min. |
|
||||
| | `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. |
|
||||
|
||||
+7
-6
@@ -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:** 299 tool files in scope (submodules, vendored and downloaded code excluded; `find` and `git ls-files` agree) + 38 retired under `tools/sunset/`. Classes: LIVE 240 (a runtime consumer), REFERENCED 29 (a SETUP row only), ORPHAN 30 (neither) — of 299. Portability: PORTABLE 23, ADAPT 259, PROJECT-ONLY 17.
|
||||
**Coverage:** 300 tool files in scope (submodules, vendored and downloaded code excluded; `find` and `git ls-files` agree) + 38 retired under `tools/sunset/`. Classes: LIVE 241 (a runtime consumer), REFERENCED 29 (a SETUP row only), ORPHAN 30 (neither) — of 300. Portability: PORTABLE 23, ADAPT 260, PROJECT-ONLY 17.
|
||||
|
||||
## P1 — extraction + manifest
|
||||
|
||||
@@ -322,22 +322,23 @@ 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, 0 orphan markers) and --strict (0 pins, 0 asm) gates, --sites for the delever ledger, a fixture self-test | delever.py, delever_cycle.sh, 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, 0 orphan markers) and --strict (0 pins, 0 asm) gates, --sites for the delever ledger, a fixture self-test | delever.py, delever_cycle.sh, gte_consolidate.py, progress.py (+1) | repo paths, the shared-body scanner | LIVE |
|
||||
| consolidate the GTE coprocessor inline-asm macros under Sony's names in one header and sweep dead lever macros | `gte_consolidate.py` | The Phase-36 T5 tool: every asm-bearing macro definition and direct GTE statement SIGNED by the build's own maspsx→as tail (template bytes with operands bound to fixed registers, operand counts, clobbers); one canonical text per signature named by PsyQ's inline_c.h convention (Sony's file supplies names and clobber lists, never opcode words) written to include/gte_inline.h; per file the canonical duplicates deleted, private names renamed, a variant with extra clobbers tried as canonical and kept as a marked <name>_m lever only when the object differs, direct statements rewritten into canonical calls (a concatenation of two included); every file judged through every recipe; --sweep deletes dead asm-bearing macros and drops a compound macro's inner asm when byte-neutral | delever_cycle.sh, lever_census.py | Sony's inline_c.h path, the census sites, the oracle | 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 |
|
||||
| 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, delever_cycle.sh | 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 |
|
||||
| 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, delever_cycle.sh, gte_consolidate.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 (+9) | 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 de-lever batch cycle unattended: batch, verify by exit code, clean fleet gate, census, log, commit | `delever_cycle.sh` | The Phase-36 T3/T4 driver: per batch a clean-tree check, the oracle recalibrated when stale, delever --apply read by exit code and its final X/X line, the clean fleet run as the outer gate (218/218), the census rerun, the phase-log entry and the checkpoint headline, one commit per batch; stops on the first red with the files in place for --restore | delever.py | the batch size, the aliases, the fleet count | LIVE |
|
||||
| run the de-lever batch cycle unattended: batch, verify by exit code, clean fleet gate, census, log, commit | `delever_cycle.sh` | The Phase-36 T3/T4 driver: per batch a clean-tree check, the oracle recalibrated when stale, delever --apply read by exit code and its final X/X line, the clean fleet run as the outer gate (218/218), the census rerun, the phase-log entry and the checkpoint headline, one commit per batch; stops on the first red with the files in place for --restore | delever.py, gte_consolidate.py | the batch size, the aliases, the fleet count | 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 and campaign tool: positional rewrites per lever class (pin -> plain declaration, barrier/keep-alive deleted, a launder deleted or turned into the assignment it is, a hand-placed instruction -> its C, the zero-register variable -> 0, a macro-carried site deleted/valued/refused by its macro's shape, volatile/register dropped); per body replay -> rung A strip-all -> rung B greedy through delever_oracle; the file is the write unit and its final compile through every recipe the proof; --plan/--apply batches (TUs parallel, exemplar files first; headers serial, includers parallel), the ledger keyed by tu+fn+addr with the body's nhash before/after, !FAKE markers on class A/B survivors, --redraw for bodies refused by an older tool, --restore from inflight.json, --scrub for orphan markers, --apply-body for T6/T7, --selftest with a stub oracle, --probe (T2) | delever_cycle.sh, lever_census.py | the census site records, the oracle | 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 and campaign tool: positional rewrites per lever class (pin -> plain declaration, barrier/keep-alive deleted, a launder deleted or turned into the assignment it is, a hand-placed instruction -> its C, the zero-register variable -> 0, a macro-carried site deleted/valued/refused by its macro's shape, volatile/register dropped); per body replay -> rung A strip-all -> rung B greedy through delever_oracle; the file is the write unit and its final compile through every recipe the proof; --plan/--apply batches (TUs parallel, exemplar files first; headers serial, includers parallel), the ledger keyed by tu+fn+addr with the body's nhash before/after, !FAKE markers on class A/B survivors, --redraw for bodies refused by an older tool, --restore from inflight.json, --scrub for orphan markers, --apply-body for T6/T7, --selftest with a stub oracle, --probe (T2) | delever_cycle.sh, gte_consolidate.py, lever_census.py | the census site records, the oracle | LIVE |
|
||||
| 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)
|
||||
|
||||
@@ -41,4 +41,5 @@ typedef s64 M2C_UNK64;
|
||||
#define NULL ((void *)0) /* m2c emits NULL for null pointers; byte-neutral (== 0) */
|
||||
#endif
|
||||
|
||||
#include "gte_inline.h" /* Phase 36 T5: the GTE coprocessor macros, one definition each */
|
||||
#endif /* COMMON_H */
|
||||
|
||||
@@ -0,0 +1,450 @@
|
||||
/* include/gte_inline.h — the GTE coprocessor macros under Sony's names, ONE definition per signature (Phase 36 T5).
|
||||
*
|
||||
* Every macro here is the majority spelling the tree already carried in its per-TU definitions (byte-proven in the fleet),
|
||||
* named by PsyQ's inline_c.h convention; the signature of each (the bytes the build's maspsx → as tail makes of the template,
|
||||
* its operand counts, its clobbers) was computed by tools/gte_consolidate.py, which also deleted the per-TU duplicates.
|
||||
* A definition that differs from the one here only by an extra clobber (a scheduling steer) lives on in its TU as <name>_m
|
||||
* and is a marked lever, not a second spelling. Generated: do not edit by hand — regenerate with `tools/gte_consolidate.py --header`.
|
||||
*/
|
||||
#ifndef GTE_INLINE_H
|
||||
#define GTE_INLINE_H
|
||||
|
||||
/* lw $12, 0( %0 );lw $13, 4( %0 );ctc2 $12, $0;ctc2 $13, $1;lw $12, 8( %0 );lw $13, 12( %0 ) … (1016 definitions in 244 files) */
|
||||
#define gte_SetRotMatrix(r0) \
|
||||
__asm__ volatile ( \
|
||||
"lw $12, 0( %0 );" \
|
||||
"lw $13, 4( %0 );" \
|
||||
"ctc2 $12, $0;" \
|
||||
"ctc2 $13, $1;" \
|
||||
"lw $12, 8( %0 );" \
|
||||
"lw $13, 12( %0 );" \
|
||||
"lw $14, 16( %0 );" \
|
||||
"ctc2 $12, $2;" \
|
||||
"ctc2 $13, $3;" \
|
||||
"ctc2 $14, $4" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "$12", "$13", "$14" )
|
||||
|
||||
/* lw $12, 0( %0 );lw $13, 4( %0 );ctc2 $12, $0;ctc2 $13, $1;lw $12, 8( %0 );lw $13, 12( %0 ) … (1 definitions in 1 files; not a PsyQ name) */
|
||||
#define gte_SetRotTransMatrix(r0) \
|
||||
__asm__ volatile ( \
|
||||
"lw $12, 0( %0 );" \
|
||||
"lw $13, 4( %0 );" \
|
||||
"ctc2 $12, $0;" \
|
||||
"ctc2 $13, $1;" \
|
||||
"lw $12, 8( %0 );" \
|
||||
"lw $13, 12( %0 );" \
|
||||
"lw $14, 16( %0 );" \
|
||||
"ctc2 $12, $2;" \
|
||||
"ctc2 $13, $3;" \
|
||||
"ctc2 $14, $4;" \
|
||||
"lw $12, 20( %0 );" \
|
||||
"lw $13, 24( %0 );" \
|
||||
"ctc2 $12, $5;" \
|
||||
"lw $14, 28( %0 );" \
|
||||
"ctc2 $13, $6;" \
|
||||
"ctc2 $14, $7" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "$12", "$13", "$14" )
|
||||
|
||||
/* lw $12, 20( %0 );lw $13, 24( %0 );ctc2 $12, $5;lw $14, 28( %0 );ctc2 $13, $6;ctc2 $14, $7 (1010 definitions in 243 files) */
|
||||
#define gte_SetTransMatrix(r0) \
|
||||
__asm__ volatile ( \
|
||||
"lw $12, 20( %0 );" \
|
||||
"lw $13, 24( %0 );" \
|
||||
"ctc2 $12, $5;" \
|
||||
"lw $14, 28( %0 );" \
|
||||
"ctc2 $13, $6;" \
|
||||
"ctc2 $14, $7" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "$12", "$13", "$14" )
|
||||
|
||||
/* nop;nop;avsz3 (28 definitions in 21 files) */
|
||||
#define gte_avsz3() __asm__ volatile ("nop;nop;avsz3")
|
||||
|
||||
/* nop;nop;avsz4 (57 definitions in 34 files) */
|
||||
#define gte_avsz4() __asm__ volatile ("nop;nop;avsz4")
|
||||
|
||||
/* nop\n\tnop\n\tdpcl (136 definitions in 136 files) */
|
||||
#define gte_dpcl() __asm__ __volatile__("nop\n\tnop\n\tdpcl")
|
||||
|
||||
/* mtc2 $0, $8 (136 definitions in 136 files; not a PsyQ name) */
|
||||
#define gte_ldIR0z() __asm__ __volatile__("mtc2 $0, $8")
|
||||
|
||||
/* lwc2 $28, 0(%0) (136 definitions in 136 files; not a PsyQ name) */
|
||||
#define gte_ldIRGB(p) __asm__ __volatile__("lwc2 $28, 0(%0)" :: "r"(p) : "memory")
|
||||
|
||||
/* lhu $12, 0( %0 );lhu $13, 6( %0 );lhu $14, 12( %0 );mtc2 $12, $9;mtc2 $13, $10;mtc2 $14, $ … (12 definitions in 6 files) */
|
||||
#define gte_ldclmv(r0) \
|
||||
__asm__ volatile ( \
|
||||
"lhu $12, 0( %0 );" \
|
||||
"lhu $13, 6( %0 );" \
|
||||
"lhu $14, 12( %0 );" \
|
||||
"mtc2 $12, $9;" \
|
||||
"mtc2 $13, $10;" \
|
||||
"mtc2 $14, $11" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "$12", "$13", "$14" )
|
||||
|
||||
/* lhu $13, 4( %0 );lhu $12, 0( %0 );sll $13, $13, 16;or $12, $12, $13;mtc2 $12, $0;lwc2 $1, … (145 definitions in 143 files) */
|
||||
#define gte_ldlv0(r0) \
|
||||
__asm__ volatile ( \
|
||||
"lhu $13, 4( %0 );" \
|
||||
"lhu $12, 0( %0 );" \
|
||||
"sll $13, $13, 16;" \
|
||||
"or $12, $12, $13;" \
|
||||
"mtc2 $12, $0;" \
|
||||
"lwc2 $1, 8( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "$12", "$13" )
|
||||
|
||||
/* lb $13, 1( %0 );lb $12, 0( %0 );sll $13, $13, 16;sll $12, $12, 16;srl $12, $12, 16;or $12, … (1 definitions in 1 files; not a PsyQ name) */
|
||||
#define gte_ldlvl_alt(r0) \
|
||||
__asm__ volatile ( \
|
||||
"lb $13, 1( %0 );" \
|
||||
"lb $12, 0( %0 );" \
|
||||
"sll $13, $13, 16;" \
|
||||
"sll $12, $12, 16;" \
|
||||
"srl $12, $12, 16;" \
|
||||
"or $12, $12, $13;" \
|
||||
"lb $13, 2( %0 );" \
|
||||
"mtc2 $12, $0;" \
|
||||
"mtc2 $13, $1" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "$12", "$13" )
|
||||
|
||||
/* lwc2 $6, 0( %0 ) (19 definitions in 2 files) */
|
||||
#define gte_ldrgb(r0) \
|
||||
__asm__ volatile ( \
|
||||
"lwc2 $6, 0( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) )
|
||||
|
||||
/* lwc2 $0, 0( %0 );lwc2 $1, 4( %0 ) (690 definitions in 307 files) */
|
||||
#define gte_ldv0(r0) \
|
||||
__asm__ volatile ( \
|
||||
"lwc2 $0, 0( %0 );" \
|
||||
"lwc2 $1, 4( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) )
|
||||
|
||||
/* lwc2 $0, 0( %0 );lwc2 $1, 4( %0 );lwc2 $2, 0( %1 );lwc2 $3, 4( %1 );lwc2 $4, 0( %2 );lwc2 … (227 definitions in 160 files) */
|
||||
#define gte_ldv3(r0, r1, r2) \
|
||||
__asm__ volatile ( \
|
||||
"lwc2 $0, 0( %0 );" \
|
||||
"lwc2 $1, 4( %0 );" \
|
||||
"lwc2 $2, 0( %1 );" \
|
||||
"lwc2 $3, 4( %1 );" \
|
||||
"lwc2 $4, 0( %2 );" \
|
||||
"lwc2 $5, 4( %2 )" \
|
||||
: \
|
||||
: "r"( r0 ), "r"( r1 ), "r"( r2 ) )
|
||||
|
||||
/* lwc2 $0, 0( %0 );lwc2 $1, 4( %0 );lwc2 $2, 0( %1 );lwc2 $3, 4( %1 );lwc2 $4, 0( %1 );lwc2 … (1 definitions in 1 files; not a PsyQ name) */
|
||||
#define gte_ldv3_alt(r0, r1) \
|
||||
__asm__ volatile ( \
|
||||
"lwc2 $0, 0( %0 );" \
|
||||
"lwc2 $1, 4( %0 );" \
|
||||
"lwc2 $2, 0( %1 );" \
|
||||
"lwc2 $3, 4( %1 );" \
|
||||
"lwc2 $4, 0( %1 );" \
|
||||
"lwc2 $5, 4( %1 )" \
|
||||
: \
|
||||
: "r"( r0 ), "r"( r1 ) )
|
||||
|
||||
/* lwc2 $0, 0( %0 );lwc2 $1, 4( %0 );lwc2 $2, 8( %0 );lwc2 $3, 12( %0 );lwc2 $4, 16( %0 );lwc … (163 definitions in 140 files) */
|
||||
#define gte_ldv3c(r0) \
|
||||
__asm__ volatile ( \
|
||||
"lwc2 $0, 0( %0 );" \
|
||||
"lwc2 $1, 4( %0 );" \
|
||||
"lwc2 $2, 8( %0 );" \
|
||||
"lwc2 $3, 12( %0 );" \
|
||||
"lwc2 $4, 16( %0 );" \
|
||||
"lwc2 $5, 20( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) )
|
||||
|
||||
/* lhu $12, 0(%0)\nlhu $13, 6(%0)\nlhu $14, 12(%0)\nmtc2 $12, $9\nmtc2 $13, $10\nmtc2 $14, $1 … (2 definitions in 2 files; not a PsyQ name) */
|
||||
#define gte_mulcol(r0) \
|
||||
__asm__ __volatile__ ( \
|
||||
"lhu $12, 0(%0)\n" "lhu $13, 6(%0)\n" "lhu $14, 12(%0)\n" \
|
||||
"mtc2 $12, $9\n" "mtc2 $13, $10\n" "mtc2 $14, $11\n" \
|
||||
"nop\n" "nop\n" "mvmva 1, 0, 3, 3, 0\n" \
|
||||
"mfc2 $12, $9\n" "mfc2 $13, $10\n" "mfc2 $14, $11\n" \
|
||||
"sh $12, 0(%0)\n" "sh $13, 6(%0)\n" "sh $14, 12(%0)\n" \
|
||||
: : "r"(r0) : "$12", "$13", "$14", "memory")
|
||||
|
||||
/* lhu $13, 4(%0)\nlhu $12, 0(%0)\nsll $13, $13, 16\nor $12, $12, $13\nmtc2 $12, $0\nlwc2 $1, … (2 definitions in 2 files; not a PsyQ name) */
|
||||
#define gte_multrans(r0) \
|
||||
__asm__ __volatile__ ( \
|
||||
"lhu $13, 4(%0)\n" "lhu $12, 0(%0)\n" \
|
||||
"sll $13, $13, 16\n" "or $12, $12, $13\n" \
|
||||
"mtc2 $12, $0\n" "lwc2 $1, 8(%0)\n" \
|
||||
"nop\n" "nop\n" "mvmva 1, 0, 0, 0, 0\n" \
|
||||
"swc2 $25, 0(%0)\n" "swc2 $26, 4(%0)\n" "swc2 $27, 8(%0)\n" \
|
||||
: : "r"(r0) : "$12", "$13", "memory")
|
||||
|
||||
/* nop;nop;nccs (15 definitions in 2 files) */
|
||||
#define gte_nccs() __asm__ volatile ("nop;nop;nccs")
|
||||
|
||||
/* nop;nop;ncct (10 definitions in 2 files) */
|
||||
#define gte_ncct() __asm__ volatile ("nop;nop;ncct")
|
||||
|
||||
/* nop;nop;nclip (186 definitions in 142 files) */
|
||||
#define gte_nclip() __asm__ volatile ("nop;nop;nclip")
|
||||
|
||||
/* lwc2 $0, 0( %1 );lwc2 $1, 4( %1 );nop;nop;mvmva 1, 0, 0, 3, 0;swc2 $25, 0( %0 );swc2 $26, … (1 definitions in 1 files; not a PsyQ name) */
|
||||
#define gte_rt_alt(r1, r0) \
|
||||
__asm__ volatile ( \
|
||||
"lwc2 $0, 0( %1 );" \
|
||||
"lwc2 $1, 4( %1 );" \
|
||||
"nop;" \
|
||||
"nop;" \
|
||||
"mvmva 1, 0, 0, 3, 0;" \
|
||||
"swc2 $25, 0( %0 );" \
|
||||
"swc2 $26, 4( %0 );" \
|
||||
"swc2 $27, 8( %0 )" \
|
||||
: \
|
||||
: "r"( r1 ), "r"( r0 ) \
|
||||
: "memory" )
|
||||
|
||||
/* nop;nop;mvmva 1, 0, 3, 3, 0 (9 definitions in 6 files) */
|
||||
#define gte_rtir() __asm__ volatile ("nop;nop;mvmva 1, 0, 3, 3, 0")
|
||||
|
||||
/* nop;nop;rtps (681 definitions in 299 files) */
|
||||
#define gte_rtps() __asm__ volatile ("nop;nop;rtps")
|
||||
|
||||
/* nop;nop;rtpt (249 definitions in 163 files) */
|
||||
#define gte_rtpt() __asm__ volatile ("nop;nop;rtpt")
|
||||
|
||||
/* nop;nop;mvmva 1, 0, 0, 3, 0 (0 definitions in 0 files) */
|
||||
#define gte_rtv0() \
|
||||
__asm__ __volatile__( \
|
||||
"nop\n" \
|
||||
"nop\n" \
|
||||
"mvmva 1, 0, 0, 3, 0\n" )
|
||||
|
||||
/* nop;nop;mvmva 1, 0, 0, 0, 0 (11 definitions in 10 files) */
|
||||
#define gte_rtv0tr() __asm__ volatile ("nop;nop;mvmva 1, 0, 0, 0, 0")
|
||||
|
||||
/* swc2 $29, 0(%0) (136 definitions in 136 files; not a PsyQ name) */
|
||||
#define gte_stORGB(p) __asm__ __volatile__("swc2 $29, 0(%0)" :: "r"(p) : "memory")
|
||||
|
||||
/* mfc2 $12, $9;mfc2 $13, $10;mfc2 $14, $11;sh $12, 0( %0 );sh $13, 6( %0 );sh $14, 12( %0 ) (11 definitions in 6 files) */
|
||||
#define gte_stclmv(r0) \
|
||||
__asm__ volatile ( \
|
||||
"mfc2 $12, $9;" \
|
||||
"mfc2 $13, $10;" \
|
||||
"mfc2 $14, $11;" \
|
||||
"sh $12, 0( %0 );" \
|
||||
"sh $13, 6( %0 );" \
|
||||
"sh $14, 12( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "$12", "$13", "$14", "memory" )
|
||||
|
||||
/* cfc2 $12, $31;nop;sw $12, 0( %0 ) (697 definitions in 309 files) */
|
||||
#define gte_stflg(r0) \
|
||||
__asm__ volatile ( \
|
||||
"cfc2 $12, $31;" \
|
||||
"nop;" \
|
||||
"sw $12, 0( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "$12", "memory" )
|
||||
|
||||
/* swc2 $25, 0( %0 );swc2 $26, 4( %0 );swc2 $27, 8( %0 ) (291 definitions in 283 files) */
|
||||
#define gte_stlvnl(r0) \
|
||||
__asm__ volatile ( \
|
||||
"swc2 $25, 0( %0 );" \
|
||||
"swc2 $26, 4( %0 );" \
|
||||
"swc2 $27, 8( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "memory" )
|
||||
|
||||
/* swc2 $24, 0( %0 ) (186 definitions in 142 files) */
|
||||
#define gte_stopz(r0) \
|
||||
__asm__ volatile ( \
|
||||
"swc2 $24, 0( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "memory" )
|
||||
|
||||
/* swc2 $7, 0( %0 ) (51 definitions in 19 files) */
|
||||
#define gte_stotz(r0) \
|
||||
__asm__ volatile ( \
|
||||
"swc2 $7, 0( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "memory" )
|
||||
|
||||
/* swc2 $22, 0( %0 ) (15 definitions in 2 files) */
|
||||
#define gte_strgb(r0) \
|
||||
__asm__ volatile ( \
|
||||
"swc2 $22, 0( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "memory" )
|
||||
|
||||
/* swc2 $20, 0( %0 );swc2 $21, 0( %1 );swc2 $22, 0( %2 ) (4 definitions in 2 files) */
|
||||
#define gte_strgb3(r0, r1, r2) \
|
||||
__asm__ volatile ( \
|
||||
"swc2 $20, 0( %0 );" \
|
||||
"swc2 $21, 0( %1 );" \
|
||||
"swc2 $22, 0( %2 )" \
|
||||
: \
|
||||
: "r"( r0 ), "r"( r1 ), "r"( r2 ) \
|
||||
: "memory" )
|
||||
|
||||
/* swc2 $20, 4( %0 );swc2 $21, 12( %0 );swc2 $22, 20( %0 ) (6 definitions in 2 files) */
|
||||
#define gte_strgb3_g3(r0) \
|
||||
__asm__ volatile ( \
|
||||
"swc2 $20, 4( %0 );" \
|
||||
"swc2 $21, 12( %0 );" \
|
||||
"swc2 $22, 20( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "memory" )
|
||||
|
||||
/* mfc2 $12, $9\nmfc2 $13, $10\nmfc2 $14, $11\nsh $12, 0(%0)\nsh $13, 2(%0)\nsh $14, 4(%0)\n (33 definitions in 21 files) */
|
||||
#define gte_stsv(r0) \
|
||||
__asm__ __volatile__( \
|
||||
"mfc2 $12, $9\n" \
|
||||
"mfc2 $13, $10\n" \
|
||||
"mfc2 $14, $11\n" \
|
||||
"sh $12, 0(%0)\n" \
|
||||
"sh $13, 2(%0)\n" \
|
||||
"sh $14, 4(%0)\n" \
|
||||
: : "r"(r0) : "$12", "$13", "$14", "memory")
|
||||
|
||||
/* swc2 $14, 0( %0 ) (679 definitions in 299 files) */
|
||||
#define gte_stsxy(r0) \
|
||||
__asm__ volatile ( \
|
||||
"swc2 $14, 0( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "memory" )
|
||||
|
||||
/* swc2 $12, 0( %0 ) (6 definitions in 5 files) */
|
||||
#define gte_stsxy0(r0) \
|
||||
__asm__ volatile ( \
|
||||
"swc2 $12, 0( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "memory" )
|
||||
|
||||
/* swc2 $12, 0( %0 );swc2 $13, 0( %1 ) (1 definitions in 1 files) */
|
||||
#define gte_stsxy01(r0, r1) \
|
||||
__asm__ volatile ( \
|
||||
"swc2 $12, 0( %0 );" \
|
||||
"swc2 $13, 0( %1 )" \
|
||||
: \
|
||||
: "r"( r0 ), "r"( r1 ) \
|
||||
: "memory" )
|
||||
|
||||
/* swc2 $13, 0( %0 ) (6 definitions in 5 files) */
|
||||
#define gte_stsxy1(r0) \
|
||||
__asm__ volatile ( \
|
||||
"swc2 $13, 0( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "memory" )
|
||||
|
||||
/* swc2 $12, 0( %0 );swc2 $13, 0( %1 );swc2 $14, 0( %2 ) (219 definitions in 159 files) */
|
||||
#define gte_stsxy3(r0, r1, r2) \
|
||||
__asm__ volatile ( \
|
||||
"swc2 $12, 0( %0 );" \
|
||||
"swc2 $13, 0( %1 );" \
|
||||
"swc2 $14, 0( %2 )" \
|
||||
: \
|
||||
: "r"( r0 ), "r"( r1 ), "r"( r2 ) \
|
||||
: "memory" )
|
||||
|
||||
/* swc2 $12, 8( %0 );swc2 $13, 12( %0 );swc2 $14, 16( %0 ) (250 definitions in 133 files) */
|
||||
#define gte_stsxy3_f3(r0) \
|
||||
__asm__ volatile ( \
|
||||
"swc2 $12, 8( %0 );" \
|
||||
"swc2 $13, 12( %0 );" \
|
||||
"swc2 $14, 16( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "memory" )
|
||||
|
||||
/* swc2 $12, 8( %0 );swc2 $13, 16( %0 );swc2 $14, 24( %0 ) (155 definitions in 124 files) */
|
||||
#define gte_stsxy3_ft3(r0) \
|
||||
__asm__ volatile ( \
|
||||
"swc2 $12, 8( %0 );" \
|
||||
"swc2 $13, 16( %0 );" \
|
||||
"swc2 $14, 24( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "memory" )
|
||||
|
||||
/* swc2 $12, 8( %0 );swc2 $13, 20( %0 );swc2 $14, 32( %0 ) (3 definitions in 2 files) */
|
||||
#define gte_stsxy3_gt4(r0) \
|
||||
__asm__ volatile ( \
|
||||
"swc2 $12, 8( %0 );" \
|
||||
"swc2 $13, 20( %0 );" \
|
||||
"swc2 $14, 32( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "memory" )
|
||||
|
||||
/* swc2 $12, 0( %0 );swc2 $13, 4( %0 );swc2 $14, 8( %0 ) (163 definitions in 141 files) */
|
||||
#define gte_stsxy3c(r0) \
|
||||
__asm__ volatile ( \
|
||||
"swc2 $12, 0( %0 );" \
|
||||
"swc2 $13, 4( %0 );" \
|
||||
"swc2 $14, 8( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "memory" )
|
||||
|
||||
/* swc2 $19, 0( %0 ) (15 definitions in 12 files) */
|
||||
#define gte_stsz(r0) \
|
||||
__asm__ volatile ( \
|
||||
"swc2 $19, 0( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "memory" )
|
||||
|
||||
/* swc2 $17, 0( %0 );swc2 $18, 0( %1 );swc2 $19, 0( %2 ) (166 definitions in 142 files) */
|
||||
#define gte_stsz3(r0, r1, r2) \
|
||||
__asm__ volatile ( \
|
||||
"swc2 $17, 0( %0 );" \
|
||||
"swc2 $18, 0( %1 );" \
|
||||
"swc2 $19, 0( %2 )" \
|
||||
: \
|
||||
: "r"( r0 ), "r"( r1 ), "r"( r2 ) \
|
||||
: "memory" )
|
||||
|
||||
/* swc2 $16, 0(%0);swc2 $17, 0(%1);swc2 $18, 0(%2);swc2 $19, 0(%3) (156 definitions in 140 files) */
|
||||
#define gte_stsz4(r0, r1, r2, r3) \
|
||||
__asm__ volatile ( \
|
||||
"swc2 $16, 0( %0 );" \
|
||||
"swc2 $17, 0( %1 );" \
|
||||
"swc2 $18, 0( %2 );" \
|
||||
"swc2 $19, 0( %3 )" \
|
||||
: \
|
||||
: "r"( r0 ), "r"( r1 ), "r"( r2 ), "r"( r3 ) \
|
||||
: "memory" )
|
||||
|
||||
/* mfc2 $12, $19;nop;sra $12, $12, 2;sw $12, 0( %0 ) (209 definitions in 146 files) */
|
||||
#define gte_stszotz(r0) \
|
||||
__asm__ volatile ( \
|
||||
"mfc2 $12, $19;" \
|
||||
"nop;" \
|
||||
"sra $12, $12, 2;" \
|
||||
"sw $12, 0( %0 )" \
|
||||
: \
|
||||
: "r"( r0 ) \
|
||||
: "$12", "memory" )
|
||||
|
||||
#endif /* GTE_INLINE_H */
|
||||
@@ -412,6 +412,34 @@ accumulate here as the phase produces them.**
|
||||
T1 hashes; the cycle's FINISH mode; the file-scope pseudo-body now holds file-scope asm statements. **T4 ☑.** Rules check (P6) done
|
||||
after T4 (re-read complete).
|
||||
|
||||
- **S98 — T5 opened: the GTE consolidation — design, the signature engine, the header step (gated).** Reconnaissance (R37): 327 GTE macro
|
||||
names (215 `gte_*`, 112 address-suffixed private copies), 9,102 definitions, 1,140 direct GTE statements, 5,751 macro-carried uses;
|
||||
`mipsel-linux-gnu-as` and the maspsx tail both reject `rtps`/`mvmva` — the mnemonics are GAS macros in `include/gte_macros.inc` reached
|
||||
through `include_asm.h`; Sony's converted `inline_c.h` is unreliable for opcode words (`gte_rtps` = `.word 0x0000007f`, which assembles to a
|
||||
bare 0x7f) and serves for names, operand counts and clobber lists only; 3,269 macro uses and 968 direct statements carry a `"memory"`
|
||||
clobber, of which Sony's own STORE macros account for most — the steers are the clobbers on loads and compute ops. **Design (X1):** a
|
||||
SIGNATURE = (the bytes the build's own maspsx→as tail makes of the template with %k bound to $4–$7/$2–$3, #outputs, #inputs) — `nop;nop;rtps`,
|
||||
`.word 0x4a180001` and `cop2 0x0180001` are one; one canonical text per signature (the majority body, byte-proven in the fleet — the header
|
||||
ships nothing new), Sony's name when the operand counts agree, Sony's clobber set canonical EVEN when every definition carries the steer
|
||||
(the body synthesized: `gte_rtv0`'s six definitions all added `"memory"`); a definition with the canonical bytes and other clobbers is a
|
||||
lever variant `<name>_m` / `_v<hash>` — the apply TRIES it as canonical first and keeps it only when the object differs; direct statements
|
||||
rewritten into canonical calls when bytes and clobbers agree (a concatenation of two canonical macros included), marked as levers when
|
||||
their clobbers exceed the canonical's, counted when they match nothing. **Built:** `tools/gte_consolidate.py` (`--inventory`, `--header`,
|
||||
`--apply`, `--sweep`, `--status`, `--selftest`: `OK — signatures rtps=0100184a ldv0=000080c8040081c8; table 1 canonical / 1 variant; fixture
|
||||
edits 8`), the cycle's `MODE=gte`, the census's `gte-lever` class (a variant's uses; a direct statement whose clobbers exceed its canonical's,
|
||||
by a read-only lookup of the tool's signature cache) + `per_tu_asm_macro_definitions` + the strict gate on both (selftest OK), the dictionary
|
||||
+ SETUP rows, kit corpus. **The inventory:** `9540 asm-bearing macro definitions (9102 GTE, 9101 signed), 1140 direct GTE statements (1137
|
||||
signed)` → **`canonical table — 50 signatures (41 with Sony's name), 8402 canonical definitions; 10 lever variants holding 699 definitions;
|
||||
1 unsigned`** (variants: `gte_rtv0tr_m` 307 defs / 293 files, `gte_ldv0_m` 179 / 163, `gte_ldrgb_m` 137, `gte_SetRotMatrix_m` 30,
|
||||
`gte_SetTransMatrix_m` 25, `gte_rtv0_m` 6, `gte_rtir_m` 3, `gte_rt_m` 1; three store macros MISSING Sony's `"memory"`: `gte_stsz4_vda39` 10,
|
||||
`gte_stsxy_vda39` 1, `gte_stclmv_v41be` 1; `gte_ldlvl_v02f2` with extra register clobbers 1; the unsigned one is a two-statement macro in
|
||||
`md_MAIN_019`); direct statements: 713 → canonical calls, 226 lever, 201 matching no canonical signature. **The header step:**
|
||||
`include/gte_inline.h` (50 macros; 9 project names beside Sony's 41: `gte_ldIR0z`, `gte_ldIRGB`, `gte_stORGB`, `gte_SetRotTransMatrix`,
|
||||
`gte_mulcol`, `gte_multrans`, `gte_ldlvl_alt`, `gte_ldv3_alt`, `gte_rt_alt`), included from `include/common.h` → every TU sees it, the local
|
||||
definitions still win as later definitions → **R22 `check-all: 218 passed, 0 failed of 218` (`wall=112.72 s`,
|
||||
`.run/P36/baseline/r22_t5_header.log`)**. Census after the header step (before any apply): GTE levers (clobbers beyond the canonical macro's): 49 sites (0 via a variant macro, 49 direct) · marked 0 · UNMARKED 49 · unsigned GTE statements 3 · per-TU asm macro definitions outside the GTE header: 9,540 {'gte': 9102, 'launder': 428, 'instruction': 9, 'barrier': 1} (GTE variants 0). Next: the apply batches
|
||||
(`tools/delever_cycle.sh 1 4 400 gte`, detached), then `--sweep`, then the T5 verify line.
|
||||
|
||||
## 🛑 SESSION CHECKPOINT — S98 (2026-09-09): T0 ☑ T1 ☑ T1b ☑ T2 ☑ T3 ☑ T4 ☑ — all committed (T4 = 13 batch commits `a605e1dba`…`f087282a3` + the tool commits; this close); NEXT = T5 the GTE consolidation + the dead lever-macro sweep | the number: 33,625 sites in 12,501 bodies (1,728 distinct) · marked 33,625 · UNMARKED 0 · orphans 0 — `lever_census --check` OK
|
||||
|
||||
### 0. How to use this block
|
||||
|
||||
+10
-5
@@ -9,7 +9,7 @@
|
||||
# and src/ + the ledger/batch record/log + the census + the phase log are committed (R42).
|
||||
# Stops on the first red (a non-zero tool exit, a final N/M with N != M, a fleet run that is not 218/218) — the batch's files are then
|
||||
# still in place: inspect, then `tools/delever.py --restore` (never git checkout, R102). Stops cleanly when nothing is drawable.
|
||||
# Usage: [LABEL_PREFIX=t3_] [TASK=T4] [REDRAW="REFUSED NOTHING-USABLE"] tools/delever_cycle.sh START END [BATCH=300] [MODE=tus|headers] [ONLY="alias1 alias2 …"]
|
||||
# Usage: [LABEL_PREFIX=t3_] [TASK=T4] [REDRAW="REFUSED NOTHING-USABLE"] tools/delever_cycle.sh START END [BATCH=300] [MODE=tus|headers|gte] [ONLY="alias1 alias2 …"]
|
||||
# RUN IT DETACHED from the Claude Code harness (its low-memory guard kills a long BACKGROUND task — batch tus3 died mid-apply, S98):
|
||||
# setsid nohup bash -c 'TASK=T4 tools/delever_cycle.sh 3 12 300 tus; echo "cycle exit=$?"' > .run/P36/delever/cycle_<x>.log 2>&1 &
|
||||
# and watch the log (a tiny waiter: `until grep -q 'cycle exit=' <log>; do sleep 60; done`). A killed batch: `tools/delever.py --restore`.
|
||||
@@ -20,7 +20,12 @@ set -o pipefail
|
||||
cd "$(dirname "$0")/.." || exit 2
|
||||
START=$1; END=$2; BATCH=${3:-300}; MODE=${4:-tus}; ONLY=${5:-}
|
||||
[ -n "$START" ] && [ -n "$END" ] || { echo "usage: $0 START END [BATCH] [tus|headers] [ONLY]"; exit 2; }
|
||||
case "$MODE" in tus) HFLAG="";; headers) HFLAG="--headers";; *) echo "MODE must be tus or headers"; exit 2;; esac
|
||||
case "$MODE" in
|
||||
tus) HFLAG=""; TOOL="tools/delever.py --apply"; VPAT='delever: batch .*';;
|
||||
headers) HFLAG="--headers"; TOOL="tools/delever.py --apply"; VPAT='delever: batch .*';;
|
||||
gte) HFLAG=""; TOOL="tools/gte_consolidate.py --apply"; VPAT='gte_consolidate: batch .*';; # T5: the GTE consolidation per file
|
||||
*) echo "MODE must be tus, headers or gte"; exit 2;;
|
||||
esac
|
||||
ONLYFLAG=""; [ -n "$ONLY" ] && ONLYFLAG="--only $ONLY"
|
||||
REDRAWFLAG=""; [ -n "${REDRAW:-}" ] && REDRAWFLAG="--redraw $REDRAW" # e.g. REDRAW="REFUSED NOTHING-USABLE" after a tool fix
|
||||
FLEET=$(ls config/check.*.sha | wc -l)
|
||||
@@ -41,11 +46,11 @@ for k in $(seq "$START" "$END"); do
|
||||
[ -z "$(git status --short --porcelain src | grep '^??')" ] || { echo "cycle: untracked files under src/ (a dotfile probe?) — sweep them first"; exit 3; }
|
||||
$PY tools/delever_oracle.py --status >/dev/null 2>&1 || $PY tools/delever_oracle.py --calibrate ov_SC04_011 ov_SC03_015 ov_SC03_014 main -j 16 > .run/P36/delever/calibrate_${label}.log 2>&1 \
|
||||
|| { echo "cycle: calibration FAILED"; tail -5 .run/P36/delever/calibrate_${label}.log; exit 1; }
|
||||
{ /usr/bin/time -f "$label wall=%e s" $PY tools/delever.py --apply --batch "$BATCH" --label "$label" $HFLAG $ONLYFLAG $REDRAWFLAG -j 12; echo "exit=$?"; } > "$log" 2>&1
|
||||
{ /usr/bin/time -f "$label wall=%e s" $PY $TOOL --batch "$BATCH" --label "$label" $HFLAG $ONLYFLAG $REDRAWFLAG -j 12; echo "exit=$?"; } > "$log" 2>&1
|
||||
fi
|
||||
grep -q '^exit=0$' "$log" || { echo "cycle: batch $label — delever exited non-zero"; tail -4 "$log"; exit 1; }
|
||||
if grep -q 'nothing to do (no drawable file)' "$log"; then echo "cycle: nothing drawable at $label — done"; exit 0; fi
|
||||
vline=$(grep -oE 'delever: batch .*' "$log" | tail -1)
|
||||
if grep -qE 'nothing to do' "$log"; then echo "cycle: nothing drawable at $label — done"; exit 0; fi
|
||||
vline=$(grep -oE "$VPAT" "$log" | tail -1)
|
||||
[ -n "$vline" ] || { echo "cycle: batch $label — no verify line in $log (R32: an empty line is not a pass)"; tail -3 "$log"; exit 1; }
|
||||
final=$(echo "$vline" | grep -oE 'final [0-9]+/[0-9]+' | grep -oE '[0-9]+/[0-9]+')
|
||||
[ -n "$final" ] && [ "${final%/*}" = "${final#*/}" ] || { echo "cycle: batch $label — $vline"; exit 1; }
|
||||
|
||||
@@ -0,0 +1,922 @@
|
||||
#!/usr/bin/env python3
|
||||
"""gte_consolidate.py — the GTE coprocessor asm consolidated under Sony's names, and the dead lever-macro sweep (Phase 36 T5).
|
||||
|
||||
tools/gte_consolidate.py --inventory # every asm-bearing macro definition + every direct GTE statement, SIGNED by the
|
||||
# build's own maspsx→as tail; the canonical table → .run/P36/gte/{inventory,canonical}.json
|
||||
tools/gte_consolidate.py --header # write include/gte_inline.h (one canonical macro per signature) + the include in common.h
|
||||
tools/gte_consolidate.py --apply --batch N --label L [--only …] [-j 12]
|
||||
# per file: canonical-duplicate definitions deleted, private/variant names renamed, direct
|
||||
# statements rewritten into canonical calls; every file judged through every recipe
|
||||
tools/gte_consolidate.py --sweep --label L [--only …] # dead asm-bearing macros deleted; a compound macro's inner asm dropped when byte-neutral
|
||||
tools/gte_consolidate.py --status | --selftest
|
||||
|
||||
A SIGNATURE is what the assembler makes of a template, not how it is spelled: the template with its operands bound to fixed registers
|
||||
(%0.. → $4 $5 $6 $7 for inputs, $2 $3 for outputs) is assembled through the SAME tail as the build (maspsx --aspsx-version=2.56 →
|
||||
mipsel-linux-gnu-as with include/macro.inc + include/gte_macros.inc, where `rtps`, `mvmva`, `sqr 0` are GAS macros), and the signature
|
||||
is (bytes, #outputs, #inputs). `nop;nop;rtps`, `nop;nop;.word 0x4a180001` and `nop;nop;cop2 0x0180001` are one signature. Sony's
|
||||
converted inline_c.h is the source of NAMES and of operand/clobber lists only — its `.word` opcode spellings assemble to nothing
|
||||
useful (`gte_rtps` there is `.word 0x0000007f`).
|
||||
|
||||
THE CANONICAL TABLE: definitions grouped by signature; the canonical CLOBBER set of a signature is Sony's for that name (a store macro
|
||||
legitimately clobbers "memory"), else the majority's; the canonical NAME is the most-used `gte_*` name in the group (Sony's convention),
|
||||
else a name derived from gte_macros.inc, else `gte_seq_<hash>`; the canonical TEXT is the majority definition text (byte-proven in the
|
||||
fleet — the header ships nothing that was not already in the tree). A definition with the canonical signature but EXTRA clobbers (the
|
||||
`"memory"` a drafter added to a load or a compute op to force the schedule) is a LEVER VARIANT: it stays per TU, renamed `<name>_m`, and
|
||||
its uses carry `// !FAKE:` markers for T7. A direct statement is rewritten into `NAME(args)` (or `NAME1(args); NAME2();` for the
|
||||
concatenation of two canonical macros) when bytes and clobbers agree; with extra clobbers it is a lever variant too (marked); a sequence
|
||||
that matches nothing is counted and left. Every file's final text is judged through every recipe of the file (tools/delever_oracle.py);
|
||||
the clean fleet run gates every batch (tools/delever_cycle.sh MODE=gte).
|
||||
"""
|
||||
import argparse
|
||||
import collections
|
||||
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"))
|
||||
import delever as dl # noqa: E402
|
||||
import delever_oracle as oracle # noqa: E402
|
||||
import lever_census as lc # noqa: E402
|
||||
import share_census as sc # noqa: E402
|
||||
|
||||
RUN = REPO / ".run" / "P36" / "gte"
|
||||
SIG_CACHE = RUN / "sig_cache.json"
|
||||
INVENTORY = RUN / "inventory.json"
|
||||
CANON = RUN / "canonical.json"
|
||||
LEDGER = REPO / ".run" / "P36" / "delever" / "ledger.jsonl"
|
||||
HEADER = "include/gte_inline.h"
|
||||
COMMON = "include/common.h"
|
||||
SONY = REPO / "tools/psyq/conv47/psyq-4_7-converted/include/inline_c.h"
|
||||
MASPSX = ".venv/bin/python tools/maspsx/maspsx.py --aspsx-version=2.56 --expand-div"
|
||||
AS = "mipsel-linux-gnu-as -Iinclude -march=r3000 -mtune=r3000 -no-pad-sections -O1 -G0"
|
||||
FAKE = dl.FAKE
|
||||
DEFINE_LINE = re.compile(r"^[ \t]*#[ \t]*define[ \t]+([A-Za-z_]\w*)(\([^)]*\))?[ \t]*(.*)$")
|
||||
_LOCK = dl._LOCK
|
||||
|
||||
|
||||
class Refuse(Exception):
|
||||
pass
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
# the signature engine
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
def parse_asm(inner):
|
||||
"""(template, outs, ins, clobbers) — delever's operand parser plus the clobber section."""
|
||||
tmpl, outs, ins = dl.parse_asm_operands(inner)
|
||||
s = inner.strip()
|
||||
i, n = 0, len(s)
|
||||
while i < n:
|
||||
if s[i] in " \t\r\n":
|
||||
i += 1
|
||||
continue
|
||||
mm = lc.STRLIT.match(s, i)
|
||||
if not mm:
|
||||
break
|
||||
i = mm.end()
|
||||
rest = s[i:].strip()
|
||||
secs = lc._split_sections(rest[1:]) if rest.startswith(":") else []
|
||||
clob = [m.group(1) for m in re.finditer(r'"([^"]*)"', secs[2])] if len(secs) > 2 else []
|
||||
return tmpl, outs, ins, sorted(set(clob))
|
||||
|
||||
|
||||
def bind_regs(tmpl, nout, nin):
|
||||
regs = ["$2", "$3"][:nout] + ["$4", "$5", "$6", "$7"][:nin]
|
||||
def sub(m):
|
||||
k = int(m.group(1))
|
||||
return regs[k] if k < len(regs) else m.group(0)
|
||||
t = re.sub(r"%(\d+)", sub, tmpl)
|
||||
t = re.sub(r"\\n|\\t", "\n", t)
|
||||
return t
|
||||
|
||||
|
||||
_sig_cache = None
|
||||
|
||||
|
||||
def _load_cache():
|
||||
global _sig_cache
|
||||
if _sig_cache is None:
|
||||
_sig_cache = json.loads(SIG_CACHE.read_text()) if SIG_CACHE.exists() else {}
|
||||
return _sig_cache
|
||||
|
||||
|
||||
def _save_cache():
|
||||
RUN.mkdir(parents=True, exist_ok=True)
|
||||
tmp = SIG_CACHE.with_suffix(".tmp")
|
||||
tmp.write_text(json.dumps(_sig_cache, indent=0))
|
||||
os.replace(tmp, SIG_CACHE)
|
||||
|
||||
|
||||
def asm_bytes(bound_template):
|
||||
"""hex of the .text bytes the build's tail makes of the template (cached by text); 'ERROR: …' when it does not assemble."""
|
||||
cache = _load_cache()
|
||||
key = bound_template
|
||||
if key in cache:
|
||||
return cache[key]
|
||||
d = RUN / "probe"
|
||||
d.mkdir(parents=True, exist_ok=True)
|
||||
tag = hashlib.sha1(key.encode()).hexdigest()[:10]
|
||||
s_path, o_path, b_path = d / f"{tag}.s", d / f"{tag}.o", d / f"{tag}.bin"
|
||||
s_path.write_text("\t.include \"include/macro.inc\"\n\t.include \"gte_macros.inc\"\n\t.set\tnoreorder\n\t.set\tnoat\n\t.text\n\t.align\t2\n"
|
||||
"\t.globl\tprobe\n\t.ent\tprobe\nprobe:\n" + bound_template + "\n\t.end\tprobe\n")
|
||||
cmd = f"set -o pipefail; cat {s_path} | {MASPSX} | {AS} -o {o_path} && mipsel-linux-gnu-objcopy -O binary -j .text {o_path} {b_path}"
|
||||
r = subprocess.run(["bash", "-c", cmd], cwd=REPO, capture_output=True, text=True)
|
||||
if r.returncode != 0 or not b_path.exists():
|
||||
res = "ERROR: " + (r.stderr.strip().splitlines() or ["?"])[-1][:120]
|
||||
else:
|
||||
res = b_path.read_bytes().hex()
|
||||
for p in (s_path, o_path, b_path):
|
||||
try:
|
||||
p.unlink()
|
||||
except OSError:
|
||||
pass
|
||||
cache[key] = res
|
||||
return res
|
||||
|
||||
|
||||
def signature(inner):
|
||||
"""dict(bytes, nout, nin, clob, tmpl, ok) for the text between an asm statement's parens."""
|
||||
tmpl, outs, ins, clob = parse_asm(inner)
|
||||
bound = bind_regs(tmpl, len(outs), len(ins))
|
||||
b = asm_bytes(bound)
|
||||
return dict(bytes=b, nout=len(outs), nin=len(ins), clob=clob, tmpl=tmpl, ok=not b.startswith("ERROR"),
|
||||
ins=[e for _, e in ins], outs=[e for _, e in outs])
|
||||
|
||||
|
||||
def set_clobbers(body, clob):
|
||||
"""the macro body with the asm statement's clobber section replaced by `clob` (removed when empty)."""
|
||||
m_asm = lc.ASM_KW.search(body)
|
||||
o = body.find("(", m_asm.end())
|
||||
c = lc._paren_span(body, o)
|
||||
inner = body[o + 1:c]
|
||||
# the sections: template, then up to three ':'-separated parts outside string literals
|
||||
s = inner
|
||||
i, n = 0, len(s)
|
||||
while i < n:
|
||||
if s[i] in " \t\r\n":
|
||||
i += 1
|
||||
continue
|
||||
mm = lc.STRLIT.match(s, i)
|
||||
if not mm:
|
||||
break
|
||||
i = mm.end()
|
||||
head, rest = s[:i], s[i:]
|
||||
secs = lc._split_sections(rest.strip()[1:]) if rest.strip().startswith(":") else []
|
||||
while len(secs) < 3:
|
||||
secs.append("")
|
||||
secs[2] = ", ".join(f'"{x}"' for x in clob) if clob else ""
|
||||
while secs and secs[-1].strip() == "":
|
||||
secs.pop()
|
||||
new_inner = head.rstrip() + ("".join(" : " + x.strip() for x in secs) if secs else "") + " "
|
||||
return body[:o + 1] + new_inner + body[c:]
|
||||
|
||||
|
||||
def sig_key(s):
|
||||
return f"{s['bytes']}|{s['nout']}|{s['nin']}"
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
# the inventory
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
def asm_inner(body_masked):
|
||||
"""the text between the parens of the FIRST asm statement in a macro body (None if none / more than one)."""
|
||||
ms = list(lc.ASM_KW.finditer(body_masked))
|
||||
if len(ms) != 1:
|
||||
return None
|
||||
o = body_masked.find("(", ms[0].end())
|
||||
c = lc._paren_span(body_masked, o) if o >= 0 else -1
|
||||
return body_masked[o + 1:c] if (o >= 0 and c > 0) else None
|
||||
|
||||
|
||||
def define_params(raw, l0):
|
||||
m = DEFINE_LINE.match(raw.split("\n")[l0 - 1])
|
||||
return (m.group(2) or "") if m else ""
|
||||
|
||||
|
||||
def sony_table():
|
||||
"""name -> (nout, nin, clobbers) from Sony's inline_c.h — the parts of it that are plain C text."""
|
||||
out = {}
|
||||
if not SONY.exists():
|
||||
return out
|
||||
raw = SONY.read_text(errors="surrogateescape")
|
||||
for (l0, l1, name, body) in lc.define_blocks(raw):
|
||||
inner = asm_inner(sc.mask_text(body) if "/*" in body else body)
|
||||
if inner is None:
|
||||
continue
|
||||
try:
|
||||
_, outs, ins, clob = parse_asm(inner)
|
||||
except Exception: # noqa: BLE001
|
||||
continue
|
||||
out[name] = (len(outs), len(ins), clob)
|
||||
return out
|
||||
|
||||
|
||||
def inc_names():
|
||||
"""bytes -> name for gte_macros.inc's single-op macros (assembled as `nop;nop;<macro>`), for signatures without a gte_* name."""
|
||||
raw = (REPO / "include/gte_macros.inc").read_text(errors="surrogateescape")
|
||||
out = {}
|
||||
for m in re.finditer(r"^\.macro\s+(\w+)\s*$", raw, re.M):
|
||||
b = asm_bytes(f"nop\nnop\n{m.group(1)}")
|
||||
if not b.startswith("ERROR"):
|
||||
out[f"{b}|0|0"] = m.group(1)
|
||||
return out
|
||||
|
||||
|
||||
def files_under_src():
|
||||
return [p for p in sorted((REPO / "src").rglob("*")) if p.suffix in (".c", ".h") and not p.name.startswith(".")]
|
||||
|
||||
|
||||
def inventory(jobs=12, quiet=False):
|
||||
"""definitions: [{tu, l0, l1, name, params, body, kind, sig}], direct: [{tu, line, col, inner, sig}], uses: Counter(name)."""
|
||||
sites = dl.load_sites()
|
||||
uses = collections.Counter(s["via"] for s in sites if s.get("via") and s["kind"] == "gte")
|
||||
direct_sites = [s for s in sites if s["kind"] == "gte" and not s.get("via")]
|
||||
defs, direct = [], []
|
||||
for p in files_under_src():
|
||||
raw = p.read_text(errors="surrogateescape")
|
||||
if "#define" not in raw and "asm" not in raw:
|
||||
continue
|
||||
rel = p.relative_to(REPO).as_posix()
|
||||
for (l0, l1, name, body) in lc.define_blocks(raw):
|
||||
kind = lc.macro_kind(body)
|
||||
if not kind:
|
||||
continue
|
||||
d = dict(tu=rel, l0=l0, l1=l1, name=name, params=define_params(raw, l0), body=body, kind=kind, sig=None)
|
||||
if kind == "gte":
|
||||
inner = asm_inner(sc.mask_text(body) if ("/*" in body or "//" in body) else body)
|
||||
if inner is not None:
|
||||
try:
|
||||
d["sig"] = signature(inner)
|
||||
except Exception as ex: # noqa: BLE001
|
||||
d["sig"] = dict(bytes=f"ERROR: {ex}"[:100], ok=False, nout=0, nin=0, clob=[], tmpl="", ins=[], outs=[])
|
||||
defs.append(d)
|
||||
by_tu = collections.defaultdict(list)
|
||||
for s in direct_sites:
|
||||
by_tu[s["tu"]].append(s)
|
||||
for tu, ss in by_tu.items():
|
||||
raw = (REPO / tu).read_text(errors="surrogateescape")
|
||||
m = dl.same_len_mask(raw)
|
||||
ls = dl.line_starts(raw)
|
||||
for s in ss:
|
||||
pos = ls[s["line"] - 1] + s["col"] - 1
|
||||
o = m.find("(", pos)
|
||||
c = lc._paren_span(m, o) if o >= 0 else -1
|
||||
if o < 0 or c < 0:
|
||||
continue
|
||||
inner = m[o + 1:c]
|
||||
try:
|
||||
sg = signature(inner)
|
||||
except Exception as ex: # noqa: BLE001
|
||||
sg = dict(bytes=f"ERROR: {ex}"[:100], ok=False, nout=0, nin=0, clob=[], tmpl="", ins=[], outs=[])
|
||||
direct.append(dict(tu=tu, line=s["line"], col=s["col"], fn=s.get("fn"), inner=inner, sig=sg, marked=s.get("marked", False)))
|
||||
_save_cache()
|
||||
inv = dict(generated=time.strftime("%Y-%m-%d %H:%M"), definitions=defs, direct=direct, uses=dict(uses))
|
||||
RUN.mkdir(parents=True, exist_ok=True)
|
||||
INVENTORY.write_text(json.dumps(inv))
|
||||
if not quiet:
|
||||
gte = [d for d in defs if d["kind"] == "gte"]
|
||||
print(f"gte_consolidate --inventory: {len(defs)} asm-bearing macro definitions ({len(gte)} GTE, {sum(1 for d in gte if d['sig'] and d['sig']['ok'])} signed), "
|
||||
f"{len(direct)} direct GTE statements ({sum(1 for d in direct if d['sig']['ok'])} signed), {sum(uses.values())} macro-carried GTE uses")
|
||||
return inv
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
# the canonical table
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
def canonical_table(inv, quiet=False):
|
||||
sony = sony_table()
|
||||
incn = inc_names()
|
||||
groups = collections.defaultdict(list)
|
||||
for d in inv["definitions"]:
|
||||
if d["kind"] == "gte" and d["sig"] and d["sig"]["ok"]:
|
||||
groups[sig_key(d["sig"])].append(d)
|
||||
table, variants, unsigned = {}, {}, [d for d in inv["definitions"] if d["kind"] == "gte" and not (d["sig"] and d["sig"]["ok"])]
|
||||
names_taken = set()
|
||||
for key, ds in sorted(groups.items(), key=lambda kv: -len(kv[1])):
|
||||
names = collections.Counter(d["name"] for d in ds)
|
||||
# candidates: the names with their address / variant suffixes stripped (`gte_stsxy01_80181E04` is Sony's gte_stsxy01;
|
||||
# `LDV0_DA34` is a private copy of gte_ldv0), Sony's spelling preferred, then the most-used gte_* spelling
|
||||
stripped = collections.Counter()
|
||||
for n, cnt in names.items():
|
||||
base = re.sub(r"(_[0-9A-Fa-f]{3,8}|X|_m|_A|_B)$", "", n)
|
||||
base = re.sub(r"_v[0-9a-f]{4}$", "", base)
|
||||
stripped[base] += cnt
|
||||
nout0, nin0 = ds[0]["sig"]["nout"], ds[0]["sig"]["nin"]
|
||||
# a Sony name is taken only when the operand counts agree with Sony's macro (a private compound that borrowed the name is not it)
|
||||
sony_named = [n for n, _ in stripped.most_common() if n in sony and (sony[n][0], sony[n][1]) == (nout0, nin0)]
|
||||
gte_names = [n for n, _ in stripped.most_common() if n.startswith("gte_")]
|
||||
name = (sony_named or gte_names or [None])[0]
|
||||
if name is None:
|
||||
base = incn.get(key)
|
||||
priv = stripped.most_common(1)[0][0]
|
||||
name = f"gte_{base}" if base else f"gte_{re.sub(r'[^A-Za-z0-9]', '', priv).lower()}"
|
||||
if name in sony and not sony_named:
|
||||
name = f"{name}_alt"
|
||||
while name in names_taken:
|
||||
name = f"{name}_alt"
|
||||
names_taken.add(name)
|
||||
clob_sets = collections.Counter(tuple(d["sig"]["clob"]) for d in ds)
|
||||
if name in sony:
|
||||
canon_clob = tuple(sony[name][2])
|
||||
else:
|
||||
canon_clob = clob_sets.most_common(1)[0][0]
|
||||
canon_defs = [d for d in ds if tuple(d["sig"]["clob"]) == canon_clob]
|
||||
synthesized = False
|
||||
if canon_defs:
|
||||
texts = collections.Counter((d["params"], re.sub(r"[ \t]+\n", "\n", d["body"]).strip()) for d in canon_defs)
|
||||
(params, body), _ = texts.most_common(1)[0]
|
||||
else:
|
||||
# nobody spells it with Sony's clobbers (every definition carries the steer): the canonical text is the majority body
|
||||
# with its clobber list set to Sony's — the steer must not become canonical by being universal
|
||||
texts = collections.Counter((d["params"], re.sub(r"[ \t]+\n", "\n", d["body"]).strip()) for d in ds)
|
||||
(params, body), _ = texts.most_common(1)[0]
|
||||
body = set_clobbers(body, list(canon_clob))
|
||||
synthesized = True
|
||||
table[key] = dict(name=name, params=params, body=body, clob=list(canon_clob), defs=len(canon_defs), files=len({d["tu"] for d in canon_defs}),
|
||||
synthesized=synthesized,
|
||||
names=dict(names), sony=(name in sony), nin=ds[0]["sig"]["nin"], nout=ds[0]["sig"]["nout"], tmpl=ds[0]["sig"]["tmpl"])
|
||||
for cs, cnt in clob_sets.items():
|
||||
if cs != canon_clob:
|
||||
extra = sorted(set(cs) - set(canon_clob))
|
||||
fewer = sorted(set(canon_clob) - set(cs))
|
||||
vname = f"{name}_m" if extra == ["memory"] and not fewer else f"{name}_v{hashlib.sha1('|'.join(cs).encode()).hexdigest()[:4]}"
|
||||
variants[f"{key}|{'|'.join(cs)}"] = dict(name=vname, of=name, key=key, clob=list(cs), extra=extra, fewer=fewer, defs=cnt,
|
||||
files=len({d["tu"] for d in ds if tuple(d["sig"]["clob"]) == cs}))
|
||||
out = dict(generated=time.strftime("%Y-%m-%d %H:%M"), canonical=table, variants=variants, unsigned=[dict(tu=d["tu"], name=d["name"], l0=d["l0"]) for d in unsigned])
|
||||
CANON.write_text(json.dumps(out, indent=1))
|
||||
if not quiet:
|
||||
print(f"gte_consolidate: canonical table — {len(table)} signatures ({sum(1 for t in table.values() if t['sony'])} with Sony's name), "
|
||||
f"{sum(t['defs'] for t in table.values())} canonical definitions; {len(variants)} lever variants holding {sum(v['defs'] for v in variants.values())} definitions; "
|
||||
f"{len(unsigned)} unsigned definitions")
|
||||
for v in sorted(variants.values(), key=lambda v: -v["defs"])[:12]:
|
||||
print(f" variant {v['name']:24s} of {v['of']:18s} defs {v['defs']:4d} files {v['files']:4d} extra {v['extra']} fewer {v['fewer']}")
|
||||
for u in unsigned[:8]:
|
||||
print(f" unsigned {u['tu']}:{u['l0']} {u['name']}")
|
||||
return out
|
||||
|
||||
|
||||
def render_header(canon):
|
||||
L = ["/* include/gte_inline.h — the GTE coprocessor macros under Sony's names, ONE definition per signature (Phase 36 T5).",
|
||||
" *",
|
||||
" * Every macro here is the majority spelling the tree already carried in its per-TU definitions (byte-proven in the fleet),",
|
||||
" * named by PsyQ's inline_c.h convention; the signature of each (the bytes the build's maspsx → as tail makes of the template,",
|
||||
" * its operand counts, its clobbers) was computed by tools/gte_consolidate.py, which also deleted the per-TU duplicates.",
|
||||
" * A definition that differs from the one here only by an extra clobber (a scheduling steer) lives on in its TU as <name>_m",
|
||||
" * and is a marked lever, not a second spelling. Generated: do not edit by hand — regenerate with `tools/gte_consolidate.py --header`.",
|
||||
" */", "#ifndef GTE_INLINE_H", "#define GTE_INLINE_H", ""]
|
||||
for key, t in sorted(canon["canonical"].items(), key=lambda kv: kv[1]["name"]):
|
||||
body_lines = t["body"].split("\n")
|
||||
L.append(f"/* {t['tmpl'][:90]}{' …' if len(t['tmpl']) > 90 else ''} ({t['defs']} definitions in {t['files']} files{'' if t['sony'] else '; not a PsyQ name'}) */")
|
||||
if len(body_lines) == 1:
|
||||
L.append(f"#define {t['name']}{t['params']} {body_lines[0]}")
|
||||
else:
|
||||
L.append(f"#define {t['name']}{t['params']} \\")
|
||||
for i, bl in enumerate(body_lines):
|
||||
L.append(f" {bl.strip()}" + (" \\" if i < len(body_lines) - 1 else ""))
|
||||
L.append("")
|
||||
L += ["#endif /* GTE_INLINE_H */", ""]
|
||||
return "\n".join(L)
|
||||
|
||||
|
||||
def write_header(canon):
|
||||
RUN.mkdir(parents=True, exist_ok=True)
|
||||
(REPO / HEADER).write_text(render_header(canon))
|
||||
c = REPO / COMMON
|
||||
t = c.read_text()
|
||||
if '#include "gte_inline.h"' not in t:
|
||||
t = t.replace("#endif /* COMMON_H */", '#include "gte_inline.h" /* Phase 36 T5: the GTE coprocessor macros, one definition each */\n#endif /* COMMON_H */', 1)
|
||||
c.write_text(t)
|
||||
print(f"gte_consolidate --header: {HEADER} written ({len(canon['canonical'])} macros); {COMMON} includes it")
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
# --apply: per file
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
def plan_files(inv, canon, only=None):
|
||||
"""tu -> dict(defs=[definitions to delete/rename], direct=[direct statements], renames={old: new})"""
|
||||
by_key = canon["canonical"]
|
||||
var_by = {v["key"] + "|" + "|".join(v["clob"]): v for v in canon["variants"].values()}
|
||||
plan = collections.defaultdict(lambda: dict(defs=[], direct=[], renames={}))
|
||||
for d in inv["definitions"]:
|
||||
if d["kind"] != "gte" or not (d["sig"] and d["sig"]["ok"]):
|
||||
continue
|
||||
key = sig_key(d["sig"])
|
||||
t = by_key.get(key)
|
||||
if not t:
|
||||
continue
|
||||
vkey = key + "|" + "|".join(d["sig"]["clob"])
|
||||
if tuple(d["sig"]["clob"]) == tuple(t["clob"]):
|
||||
plan[d["tu"]]["defs"].append(dict(d, action="delete", canonical=t["name"]))
|
||||
if d["name"] != t["name"]:
|
||||
plan[d["tu"]]["renames"][d["name"]] = t["name"]
|
||||
elif vkey in var_by:
|
||||
v = var_by[vkey]
|
||||
plan[d["tu"]]["defs"].append(dict(d, action="rename", canonical=t["name"], variant=v["name"], extra=v["extra"], fewer=v["fewer"]))
|
||||
if d["name"] != v["name"]:
|
||||
plan[d["tu"]]["renames"][d["name"]] = v["name"]
|
||||
for s in inv["direct"]:
|
||||
plan[s["tu"]]["direct"].append(s)
|
||||
files = {tu: p for tu, p in plan.items() if p["defs"] or p["direct"]}
|
||||
if only:
|
||||
files = {tu: p for tu, p in files.items() if any(o == tu or tu.endswith("/" + o) or ("/" + o + "/") in tu for o in only)}
|
||||
return files
|
||||
|
||||
|
||||
def direct_rewrite(s, canon):
|
||||
"""('call', text, name) | ('lever', variant_name, extra) | ('none', why, None) for a direct GTE statement."""
|
||||
sg = s["sig"]
|
||||
if not sg["ok"]:
|
||||
return "none", "unsigned", None
|
||||
key = sig_key(sg)
|
||||
t = canon["canonical"].get(key)
|
||||
if t and sg["nout"] == 0:
|
||||
if tuple(sg["clob"]) == tuple(t["clob"]):
|
||||
return "call", f"{t['name']}({', '.join(sg['ins'])});", t["name"]
|
||||
extra = sorted(set(sg["clob"]) - set(t["clob"]))
|
||||
fewer = sorted(set(t["clob"]) - set(sg["clob"]))
|
||||
return "lever", (f"{t['name']}_m" if extra == ["memory"] and not fewer else f"{t['name']}_v"), extra or fewer
|
||||
# a concatenation of two canonical macros (a load then an op): bytes = b1 + b2, inputs = the first's, clobbers = the union
|
||||
b = sg["bytes"]
|
||||
for k1, t1 in canon["canonical"].items():
|
||||
b1 = k1.split("|")[0]
|
||||
if not b.startswith(b1) or len(b1) == 0 or len(b1) >= len(b):
|
||||
continue
|
||||
k2 = f"{b[len(b1):]}|0|0"
|
||||
t2 = canon["canonical"].get(k2)
|
||||
if not t2 or t1["nout"] or t1["nin"] != sg["nin"]:
|
||||
continue
|
||||
union = sorted(set(t1["clob"]) | set(t2["clob"]))
|
||||
if union == list(sg["clob"]):
|
||||
return "call", f"{t1['name']}({', '.join(sg['ins'])}); {t2['name']}();", f"{t1['name']}+{t2['name']}"
|
||||
extra = sorted(set(sg["clob"]) - set(union))
|
||||
return "lever", f"{t1['name']}+{t2['name']}_m" if extra == ["memory"] else f"{t1['name']}+{t2['name']}_v", extra or sorted(set(union) - set(sg["clob"]))
|
||||
return "none", "no canonical signature", None
|
||||
|
||||
|
||||
def file_edits(tu, raw, p, canon, label):
|
||||
"""[(start, end, repl)] + the record (per definition / use / direct statement) for one file's consolidation."""
|
||||
m = dl.same_len_mask(raw)
|
||||
ls = dl.line_starts(raw)
|
||||
edits, rec = [], dict(deleted=0, renamed_defs=0, use_renames=0, direct_calls=0, direct_levers=0, direct_none=0, marked=0, defs=[], direct=[])
|
||||
# definitions: delete or rename (the #define line's name token); a renamed variant gets a comment line above
|
||||
for d in sorted(p["defs"], key=lambda d: d["l0"]):
|
||||
start, end = ls[d["l0"] - 1], ls[d["l1"]] if d["l1"] < len(ls) else len(raw)
|
||||
if d["action"] == "delete":
|
||||
edits.append((start, end, ""))
|
||||
rec["deleted"] += 1
|
||||
rec["defs"].append(dict(name=d["name"], l0=d["l0"], action="deleted", canonical=d["canonical"]))
|
||||
else:
|
||||
line = raw[start:ls[d["l0"]] - 1]
|
||||
k = line.find(d["name"])
|
||||
if k < 0:
|
||||
raise Refuse(f"{tu}:{d['l0']}: the #define line does not carry `{d['name']}`")
|
||||
note = f"/* {FAKE[3:]} gte variant `{d['variant']}` — {', '.join(d['extra']) or 'fewer clobbers'} beyond Sony's `{d['canonical']}` (a scheduling steer; P36 T5) */\n"
|
||||
edits.append((start, start, note))
|
||||
edits.append((start + k, start + k + len(d["name"]), d["variant"]))
|
||||
rec["renamed_defs"] += 1
|
||||
rec["defs"].append(dict(name=d["name"], l0=d["l0"], action="renamed", variant=d["variant"], canonical=d["canonical"]))
|
||||
# uses: rename tokens (outside the #define lines being edited); mark the uses of a lever variant
|
||||
def_lines = {ln for d in p["defs"] for ln in range(d["l0"], d["l1"] + 1)}
|
||||
lever_names = {d["name"] for d in p["defs"] if d["action"] == "rename"} | {d["variant"] for d in p["defs"] if d["action"] == "rename"}
|
||||
for old, new in p["renames"].items():
|
||||
for mm in re.finditer(r"\b%s\b(?=\s*\()" % re.escape(old), m):
|
||||
ln = m.count("\n", 0, mm.start()) + 1
|
||||
if ln in def_lines:
|
||||
continue
|
||||
edits.append((mm.start(), mm.end(), new))
|
||||
rec["use_renames"] += 1
|
||||
marked_lines = set()
|
||||
for name in lever_names:
|
||||
for mm in re.finditer(r"\b%s\b(?=\s*\()" % re.escape(name), m):
|
||||
ln = m.count("\n", 0, mm.start()) + 1
|
||||
if ln in def_lines or ln in marked_lines:
|
||||
continue
|
||||
le = m.find("\n", mm.start())
|
||||
le = len(raw) if le < 0 else le
|
||||
if FAKE in raw[ls[ln - 1]:le]:
|
||||
continue
|
||||
edits.append((le, le, f" {FAKE} gte via {name if name.endswith(('_m',)) or '_v' in name else p['renames'].get(name, name)} — a clobber Sony's macro lacks (P36 T5 {label})"))
|
||||
marked_lines.add(ln)
|
||||
rec["marked"] += 1
|
||||
# direct statements
|
||||
for s in p["direct"]:
|
||||
pos = ls[s["line"] - 1] + s["col"] - 1
|
||||
e = dl.stmt_end(m, pos)
|
||||
if e < 0 or not dl.ASM_HEAD.match(m, pos):
|
||||
rec["direct"].append(dict(line=s["line"], action="refused", why="statement not found at the census position"))
|
||||
continue
|
||||
how, text, name = direct_rewrite(s, canon)
|
||||
if how == "call":
|
||||
edits.append((pos, e, text))
|
||||
rec["direct_calls"] += 1
|
||||
rec["direct"].append(dict(line=s["line"], action="call", name=name))
|
||||
elif how == "lever":
|
||||
le = m.find("\n", e)
|
||||
le = len(raw) if le < 0 else le
|
||||
if FAKE not in raw[ls[s["line"] - 1]:le] and s["line"] not in marked_lines:
|
||||
edits.append((le, le, f" {FAKE} gte direct — clobbers {name} ({', '.join(text if isinstance(text, list) else [str(text)])}) beyond Sony's (P36 T5 {label})".replace("clobbers " + str(text) + " (", "as " + str(text) + " (")))
|
||||
marked_lines.add(s["line"])
|
||||
rec["marked"] += 1
|
||||
rec["direct_levers"] += 1
|
||||
rec["direct"].append(dict(line=s["line"], action="lever", variant=text, delta=name))
|
||||
else:
|
||||
rec["direct_none"] += 1
|
||||
rec["direct"].append(dict(line=s["line"], action="none", why=text))
|
||||
return edits, rec
|
||||
|
||||
|
||||
def apply_batch(a):
|
||||
clean, dirty = dl.src_clean()
|
||||
if not clean:
|
||||
sys.exit(f"gte_consolidate --apply: src/ is dirty — commit or tools/delever.py --restore first:\n{dirty[:400]}")
|
||||
ok, why = oracle.calibration_current()
|
||||
if not ok:
|
||||
sys.exit(f"gte_consolidate --apply: calibration not current ({why})")
|
||||
if not (REPO / HEADER).exists() or '#include "gte_inline.h"' not in (REPO / COMMON).read_text():
|
||||
sys.exit("gte_consolidate --apply: the header is not in place (--header first, then the fleet run and its commit)")
|
||||
dl.ensure_census(a.jobs)
|
||||
inv = inventory(a.jobs, quiet=True)
|
||||
canon = canonical_table(inv, quiet=True)
|
||||
files = plan_files(inv, canon, a.only)
|
||||
order = sorted(files, key=lambda tu: (-len(files[tu]["defs"]) - len(files[tu]["direct"]), tu))
|
||||
ledger_rows = [json.loads(l) for l in LEDGER.read_text().splitlines() if l.strip()] if LEDGER.exists() else []
|
||||
done = {r["tu"] for r in ledger_rows if r.get("rung") == "gte" and r.get("verdict") in ("CONSOLIDATED", "UNCHANGED")}
|
||||
order = [tu for tu in order if tu not in done or a.rejudge][:a.batch]
|
||||
print(f"gte_consolidate --apply {a.label}: {len(order)} files of {len(files)} with work ({len(done)} done) — "
|
||||
f"{sum(len(files[t]['defs']) for t in order)} definitions, {sum(len(files[t]['direct']) for t in order)} direct statements", flush=True)
|
||||
if not order:
|
||||
print("gte_consolidate --apply: nothing to do")
|
||||
return 0
|
||||
recipes = oracle.load_recipes()["recipes"]
|
||||
by_src = oracle.recipes_by_src(recipes)
|
||||
inc = dl.includers()
|
||||
def recipes_for(tu):
|
||||
return [r for t in inc.get(tu, []) for r in by_src.get(t, [])] if tu.endswith(".h") else by_src.get(tu, [])
|
||||
dl.INFLIGHT.write_text(json.dumps(dict(label=a.label, files={tu: (REPO / tu).read_text(errors="surrogateescape") for tu in order})))
|
||||
log_p = RUN / f"apply_{a.label}.log"
|
||||
log_f = open(log_p, "a")
|
||||
def log(msg):
|
||||
line = f"{time.strftime('%H:%M:%S')} {msg}"
|
||||
with _LOCK:
|
||||
print(line, flush=True)
|
||||
log_f.write(line + "\n")
|
||||
log_f.flush()
|
||||
pool = ThreadPoolExecutor(max_workers=a.jobs)
|
||||
results = []
|
||||
|
||||
def work(tu):
|
||||
path = REPO / tu
|
||||
st = path.stat()
|
||||
raw = path.read_text(errors="surrogateescape")
|
||||
recs = recipes_for(tu)
|
||||
row = dict(ts=time.strftime("%Y-%m-%d %H:%M:%S"), label=a.label, rung="gte", calib=dict(head=oracle.head(), stamp=oracle.config_stamp()),
|
||||
tu=tu, fn=None, addr=None, aliases=None, header=tu.endswith(".h"), sites=[], compiles=0, seconds=0.0)
|
||||
if not recs:
|
||||
row["verdict"] = "NO-RECIPE"
|
||||
return row
|
||||
plan_tu = files[tu]
|
||||
# the variant trial: a definition whose only difference from the canonical one is a clobber is tried AS canonical (deleted, its
|
||||
# uses renamed to the canonical name), one at a time on the untouched text; IDENTICAL -> the clobber was no lever here
|
||||
variants = [d for d in plan_tu["defs"] if d["action"] == "rename"]
|
||||
freed = 0
|
||||
for d in variants:
|
||||
trial = dict(defs=[dict(d, action="delete")], direct=[], renames=({d["name"]: d["canonical"]} if d["name"] != d["canonical"] else {}))
|
||||
try:
|
||||
ed, _ = file_edits(tu, raw, trial, canon, a.label)
|
||||
cand = dl.apply_edits(raw, ed)
|
||||
if cand == raw:
|
||||
continue
|
||||
try:
|
||||
v, dt, err = oracle.judge_all(recs, cand, tag="gtev", write_path=(tu if tu.endswith(".h") else None))
|
||||
finally:
|
||||
dl.restore_file(path, raw, st)
|
||||
row["compiles"] += len(recs)
|
||||
row["seconds"] += dt
|
||||
if v == "IDENTICAL":
|
||||
d["action"], d["trial"] = "delete", "IDENTICAL"
|
||||
if d["name"] != d["canonical"]:
|
||||
plan_tu["renames"][d["name"]] = d["canonical"]
|
||||
plan_tu["renames"].pop(d["name"], None) if d["name"] == d["canonical"] else None
|
||||
freed += 1
|
||||
else:
|
||||
d["trial"] = v
|
||||
except (Refuse, dl.Refuse) as ex:
|
||||
d["trial"] = f"REFUSED {ex}"
|
||||
# a variant that stays a variant keeps its rename to <name>_m
|
||||
for d in variants:
|
||||
if d["action"] == "rename" and d["name"] != d["variant"]:
|
||||
plan_tu["renames"][d["name"]] = d["variant"]
|
||||
try:
|
||||
edits, rec = file_edits(tu, raw, plan_tu, canon, a.label)
|
||||
except Refuse as ex:
|
||||
row.update(verdict="REFUSED", why=str(ex))
|
||||
log(f" {tu}: REFUSED {ex}")
|
||||
return row
|
||||
rec["variants_freed"] = freed
|
||||
rec["variants_kept"] = sum(1 for d in variants if d["action"] == "rename")
|
||||
row["record"] = rec
|
||||
cands = []
|
||||
try:
|
||||
cands.append(("all", dl.apply_edits(raw, edits)))
|
||||
no_direct = [e for e in edits if not any(e[0] == dl.line_starts(raw)[s["line"] - 1] + s["col"] - 1 for s in files[tu]["direct"])]
|
||||
if len(no_direct) != len(edits):
|
||||
cands.append(("defs-only", dl.apply_edits(raw, no_direct)))
|
||||
except dl.Refuse as ex:
|
||||
row.update(verdict="REFUSED", why=f"edits: {ex}")
|
||||
log(f" {tu}: REFUSED edits {ex}")
|
||||
return row
|
||||
verdict = "UNCHANGED"
|
||||
for tag, cand in cands:
|
||||
if cand == raw:
|
||||
continue
|
||||
try:
|
||||
if tu.endswith(".h") and len(recs) > 1:
|
||||
path.write_text(cand, errors="surrogateescape")
|
||||
vs = list(pool.map(lambda r: oracle.judge(r, None, tag="gte"), recs))
|
||||
bad = [v for v in vs if v[0] != "IDENTICAL"]
|
||||
v, dt, err = (bad[0][0], sum(x[1] for x in vs), bad[0][2]) if bad else ("IDENTICAL", sum(x[1] for x in vs), "")
|
||||
else:
|
||||
v, dt, err = oracle.judge_all(recs, cand, tag="gte", write_path=(tu if tu.endswith(".h") else None))
|
||||
finally:
|
||||
dl.restore_file(path, raw, st)
|
||||
row["compiles"] += len(recs)
|
||||
row["seconds"] += dt
|
||||
if v == "IDENTICAL":
|
||||
path.write_text(cand, errors="surrogateescape")
|
||||
verdict = "CONSOLIDATED" if tag == "all" else "CONSOLIDATED-DEFS-ONLY"
|
||||
if tag != "all":
|
||||
rec["direct_calls_refused"] = rec.pop("direct_calls", 0)
|
||||
log(f" {tu}: the direct-statement rewrites DIFFER — kept the definition edits only")
|
||||
break
|
||||
verdict = f"DIFFERS-{tag}" if v == "DIFFERS" else f"{v}-{tag}"
|
||||
log(f" {tu}: {tag} {v} {err[:120]}")
|
||||
row["verdict"] = verdict if verdict.startswith(("CONSOLIDATED", "UNCHANGED")) else "REFUSED"
|
||||
if not verdict.startswith(("CONSOLIDATED", "UNCHANGED")):
|
||||
row["why"] = verdict
|
||||
log(f" {tu}: {row['verdict']} — deleted {rec['deleted']} defs, renamed {rec['renamed_defs']}, use renames {rec['use_renames']}, "
|
||||
f"direct calls {rec.get('direct_calls', 0)}, direct levers {rec['direct_levers']}, unmatched {rec['direct_none']}, marked {rec['marked']}")
|
||||
return row
|
||||
tus = [tu for tu in order if not tu.endswith(".h")]
|
||||
hdrs = [tu for tu in order if tu.endswith(".h")]
|
||||
with ThreadPoolExecutor(max_workers=a.jobs) as ex:
|
||||
results = list(ex.map(work, tus))
|
||||
for h in hdrs:
|
||||
results.append(work(h))
|
||||
pool.shutdown(wait=True)
|
||||
dl.ledger_append(results)
|
||||
agg = collections.Counter()
|
||||
for r in results:
|
||||
agg[r["verdict"]] += 1
|
||||
for k in ("deleted", "renamed_defs", "use_renames", "direct_calls", "direct_levers", "direct_none", "marked", "variants_freed", "variants_kept"):
|
||||
agg[k] += r.get("record", {}).get(k, 0)
|
||||
agg["compiles"] += r["compiles"]
|
||||
line = (f"gte_consolidate: batch {a.label} — {len(order)} files: {agg['CONSOLIDATED']} consolidated, {agg['CONSOLIDATED-DEFS-ONLY']} defs-only, "
|
||||
f"{agg['UNCHANGED']} unchanged, {agg['REFUSED']} refused, {agg['NO-RECIPE']} no-recipe · definitions deleted {agg['deleted']} / renamed as lever variants "
|
||||
f"{agg['renamed_defs']} (variant trials: {agg['variants_freed']} freed, {agg['variants_kept']} kept) · use renames {agg['use_renames']} · direct statements → calls {agg['direct_calls']} / lever {agg['direct_levers']} / "
|
||||
f"unmatched {agg['direct_none']} · markers {agg['marked']} · compiles {agg['compiles']} · final {len(order) - agg['REFUSED'] - agg['NO-RECIPE']}/{len(order)} identical")
|
||||
log(line)
|
||||
(RUN / f"batch_{a.label}.json").write_text(json.dumps(dict(label=a.label, files=order, totals=dict(agg), rows=results), indent=1))
|
||||
return 0 if agg["REFUSED"] == 0 else 1
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
# --sweep: dead asm-bearing macros; a compound macro's inner asm dropped when byte-neutral
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
def sweep(a):
|
||||
clean, dirty = dl.src_clean()
|
||||
if not clean:
|
||||
sys.exit(f"gte_consolidate --sweep: src/ is dirty:\n{dirty[:400]}")
|
||||
ok, why = oracle.calibration_current()
|
||||
if not ok:
|
||||
sys.exit(f"gte_consolidate --sweep: calibration not current ({why})")
|
||||
dl.ensure_census(a.jobs)
|
||||
sites = dl.load_sites()
|
||||
uses_by_file = collections.defaultdict(collections.Counter)
|
||||
for s in sites:
|
||||
if s.get("via"):
|
||||
uses_by_file[s["tu"]][s["via"]] += 1
|
||||
inc = dl.includers()
|
||||
# a macro used by an included shared header counts as used by every includer
|
||||
hdr_uses = {h: uses_by_file.get(h, collections.Counter()) for h in inc}
|
||||
recipes = oracle.load_recipes()["recipes"]
|
||||
by_src = oracle.recipes_by_src(recipes)
|
||||
rows, n_dead, n_inner, n_files = [], 0, 0, 0
|
||||
files = files_under_src()
|
||||
if a.only:
|
||||
files = [p for p in files if any(o in p.as_posix() for o in a.only)]
|
||||
for p in files:
|
||||
rel = p.relative_to(REPO).as_posix()
|
||||
raw = p.read_text(errors="surrogateescape")
|
||||
if "#define" not in raw:
|
||||
continue
|
||||
blocks = [(l0, l1, name, body) for (l0, l1, name, body) in lc.define_blocks(raw) if lc.macro_kind(body) and lc.macro_kind(body) != "gte"]
|
||||
if not blocks:
|
||||
continue
|
||||
used = collections.Counter(uses_by_file.get(rel, {}))
|
||||
for h, hu in hdr_uses.items():
|
||||
if rel in inc.get(h, []):
|
||||
used.update(hu)
|
||||
m = dl.same_len_mask(raw)
|
||||
ls = dl.line_starts(raw)
|
||||
edits, dead, inner = [], [], []
|
||||
for (l0, l1, name, body) in blocks:
|
||||
start, end = ls[l0 - 1], ls[l1] if l1 < len(ls) else len(raw)
|
||||
if used.get(name, 0) == 0 and not re.search(r"\b%s\b" % re.escape(name), m[:start] + m[end:]):
|
||||
edits.append((start, end, ""))
|
||||
dead.append(name)
|
||||
continue
|
||||
# a compound macro: drop the asm statement line(s) inside the definition (the launder/barrier steer), keep the code
|
||||
lines = raw[start:end].split("\n")
|
||||
keep, dropped = [], 0
|
||||
for ln in lines:
|
||||
if lc.ASM_KW.search(ln) and ("__asm__" in ln or "asm" in ln) and ('"' in ln):
|
||||
dropped += 1
|
||||
continue
|
||||
keep.append(ln)
|
||||
if dropped and len(keep) > 1:
|
||||
new = "\n".join(keep)
|
||||
if not new.endswith("\n") and raw[start:end].endswith("\n"):
|
||||
new += "\n"
|
||||
edits.append((start, end, new))
|
||||
inner.append(name)
|
||||
if not edits:
|
||||
continue
|
||||
recs = [r for t in inc.get(rel, []) for r in by_src.get(t, [])] if rel.endswith(".h") else by_src.get(rel, [])
|
||||
if not recs:
|
||||
print(f" {rel}: no recipe — skipped")
|
||||
continue
|
||||
st = p.stat()
|
||||
try:
|
||||
cand = dl.apply_edits(raw, edits)
|
||||
except dl.Refuse as ex:
|
||||
print(f" {rel}: REFUSED {ex}")
|
||||
continue
|
||||
verdict_all = None
|
||||
for tag, cand_t in (("all", cand), ("dead-only", dl.apply_edits(raw, [e for e in edits if e[2] == ""]) if inner and dead else None)):
|
||||
if cand_t is None or cand_t == raw:
|
||||
continue
|
||||
try:
|
||||
v, dt, err = oracle.judge_all(recs, cand_t, tag="sweep", write_path=(rel if rel.endswith(".h") else None))
|
||||
finally:
|
||||
dl.restore_file(p, raw, st)
|
||||
if v == "IDENTICAL":
|
||||
p.write_text(cand_t, errors="surrogateescape")
|
||||
verdict_all = tag
|
||||
break
|
||||
print(f" {rel}: {tag} {v} {err[:100]}")
|
||||
n_files += verdict_all is not None
|
||||
if verdict_all == "all":
|
||||
n_dead += len(dead)
|
||||
n_inner += len(inner)
|
||||
elif verdict_all == "dead-only":
|
||||
n_dead += len(dead)
|
||||
inner_kept = inner
|
||||
inner = []
|
||||
rows.append(dict(ts=time.strftime("%Y-%m-%d %H:%M:%S"), label=a.label, rung="sweep", calib=dict(head=oracle.head(), stamp=oracle.config_stamp()),
|
||||
tu=rel, fn=None, addr=None, aliases=None, header=rel.endswith(".h"), verdict=("SWEPT-" + verdict_all if verdict_all else "REFUSED"),
|
||||
dead=dead, inner_dropped=(inner if verdict_all else []), sites=[], compiles=len(recs)))
|
||||
print(f" {rel}: {rows[-1]['verdict']} — dead {dead} inner-asm dropped {inner if verdict_all else []}")
|
||||
dl.ledger_append(rows)
|
||||
print(f"gte_consolidate --sweep {a.label}: {n_dead} dead asm-bearing macro definitions deleted, {n_inner} compound macros' inner asm dropped, "
|
||||
f"{n_files} files written, {sum(1 for r in rows if r['verdict'] == 'REFUSED')} refused")
|
||||
return 0
|
||||
|
||||
|
||||
def status():
|
||||
rows = [json.loads(l) for l in LEDGER.read_text().splitlines() if l.strip()] if LEDGER.exists() else []
|
||||
g = [r for r in rows if r.get("rung") in ("gte", "sweep")]
|
||||
print(f"gte_consolidate --status: {len(g)} rows — {dict(collections.Counter(r['verdict'] for r in g))}")
|
||||
return 0
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
def selftest():
|
||||
ok = True
|
||||
def fail(msg):
|
||||
nonlocal ok
|
||||
ok = False
|
||||
print("selftest FAIL:", msg)
|
||||
# 1. the signature engine: three spellings of rtps are one signature; a template with an operand binds to $4
|
||||
s1 = signature('"nop;nop;rtps"')
|
||||
s2 = signature('"nop;" "nop;" ".word 0x4a180001"')
|
||||
s3 = signature('"nop;nop;cop2 0x0180001"')
|
||||
if not (s1["ok"] and s1["bytes"] == s2["bytes"] == s3["bytes"]):
|
||||
fail(f"rtps spellings: {s1['bytes'][:40]} {s2['bytes'][:40]} {s3['bytes'][:40]}")
|
||||
s4 = signature('"lwc2 $0, 0( %0 );" "lwc2 $1, 4( %0 )" : : "r"( r0 )')
|
||||
s5 = signature('"lwc2 $0, 0(%0)\\n" "lwc2 $1, 4(%0)\\n" : : "r"(r0) : "memory"')
|
||||
if not (s4["ok"] and s4["bytes"] == s5["bytes"] and s4["nin"] == 1 and s4["clob"] == [] and s5["clob"] == ["memory"]):
|
||||
fail(f"ldv0 spellings/clobbers: {s4} {s5}")
|
||||
if signature('"nop;nop;rtpt"')["bytes"] == s1["bytes"]:
|
||||
fail("rtps and rtpt must differ")
|
||||
# 2. the canonical table on a synthetic inventory: canonical / variant / private name / concatenated direct statement
|
||||
inv = dict(definitions=[
|
||||
dict(tu="a.c", l0=1, l1=2, name="gte_ldv0", params="( r0 )", body='__asm__ volatile ( "lwc2 $0, 0( %0 );" "lwc2 $1, 4( %0 )" : : "r"( r0 ) )', kind="gte", sig=s4),
|
||||
dict(tu="b.c", l0=1, l1=2, name="gte_ldv0", params="(r0)", body='__asm__ __volatile__( "lwc2 $0, 0(%0)\\n" "lwc2 $1, 4(%0)\\n" : : "r"(r0) : "memory")', kind="gte", sig=s5),
|
||||
dict(tu="c.c", l0=1, l1=2, name="LDV0_DA34", params="( r0 )", body='__asm__ volatile ("lwc2 $0, 0( %0 );lwc2 $1, 4( %0 )" : : "r"( r0 ))', kind="gte", sig=s4),
|
||||
dict(tu="a.c", l0=3, l1=3, name="gte_rtps", params="()", body='__asm__ volatile ("nop;nop;rtps")', kind="gte", sig=s1),
|
||||
], direct=[], uses={})
|
||||
canon = canonical_table(inv, quiet=True)
|
||||
t = canon["canonical"].get(sig_key(s4))
|
||||
if not t or t["name"] != "gte_ldv0" or t["clob"] != [] or t["defs"] != 2:
|
||||
fail(f"canonical ldv0: {t}")
|
||||
v = [x for x in canon["variants"].values() if x["of"] == "gte_ldv0"]
|
||||
if len(v) != 1 or v[0]["name"] != "gte_ldv0_m" or v[0]["extra"] != ["memory"]:
|
||||
fail(f"ldv0 memory variant: {v}")
|
||||
# 3. direct statements: a canonical call, a lever, a concatenation
|
||||
d_call = dict(sig=signature('"lwc2 $0, 0(%0);" "lwc2 $1, 4(%0)" : : "r"(p)'))
|
||||
d_lever = dict(sig=signature('"lwc2 $0, 0(%0);" "lwc2 $1, 4(%0)" : : "r"(p) : "memory"'))
|
||||
d_cat = dict(sig=signature('"lwc2 $0, 0(%0);" "lwc2 $1, 4(%0);" "nop;nop;rtps" : : "r"(p)'))
|
||||
if direct_rewrite(d_call, canon) != ("call", "gte_ldv0(p);", "gte_ldv0"):
|
||||
fail(f"direct call: {direct_rewrite(d_call, canon)}")
|
||||
if direct_rewrite(d_lever, canon)[0] != "lever" or direct_rewrite(d_lever, canon)[1] != "gte_ldv0_m":
|
||||
fail(f"direct lever: {direct_rewrite(d_lever, canon)}")
|
||||
r = direct_rewrite(d_cat, canon)
|
||||
if r != ("call", "gte_ldv0(p); gte_rtps();", "gte_ldv0+gte_rtps"):
|
||||
fail(f"direct concatenation: {r}")
|
||||
# 4. the per-file edits on a fixture: the canonical def deleted, the private use renamed, the variant renamed + marked, a direct call
|
||||
fx = ('#define gte_ldv0( r0 ) __asm__ volatile ( "lwc2 $0, 0( %0 );" "lwc2 $1, 4( %0 )" : : "r"( r0 ) )\n'
|
||||
'#define LDV0_DA34( r0 ) __asm__ volatile ("lwc2 $0, 0( %0 );lwc2 $1, 4( %0 )" : : "r"( r0 ))\n'
|
||||
'#define gte_ldv0m( r0 ) __asm__ __volatile__( "lwc2 $0, 0(%0)\\n" "lwc2 $1, 4(%0)\\n" : : "r"(r0) : "memory")\n'
|
||||
'void f(s32 *v) {\n LDV0_DA34(v);\n gte_ldv0m(v);\n __asm__ volatile("lwc2 $0, 0(%0);" "lwc2 $1, 4(%0)" : : "r"(v));\n}\n')
|
||||
defs = []
|
||||
for (l0, l1, name, body) in lc.define_blocks(fx):
|
||||
sg = signature(asm_inner(body))
|
||||
defs.append(dict(tu="fx.c", l0=l0, l1=l1, name=name, params=define_params(fx, l0), body=body, kind="gte", sig=sg))
|
||||
fm, fls = dl.same_len_mask(fx), dl.line_starts(fx)
|
||||
fpos = fls[6] + 4
|
||||
fo = fm.find("(", fpos)
|
||||
finner = fm[fo + 1:lc._paren_span(fm, fo)]
|
||||
inv2 = dict(definitions=defs, direct=[dict(tu="fx.c", line=7, col=5, fn="f", inner=finner, sig=signature(finner))], uses={})
|
||||
canon2 = canonical_table(inv2, quiet=True)
|
||||
plan = plan_files(inv2, canon2)
|
||||
edits, rec = file_edits("fx.c", fx, plan["fx.c"], canon2, "self")
|
||||
out = dl.apply_edits(fx, edits)
|
||||
if "#define gte_ldv0(" in out or "#define LDV0_DA34" in out:
|
||||
fail("canonical/private definitions must be deleted")
|
||||
if "#define gte_ldv0_m( r0 )" not in out or "gte variant `gte_ldv0_m`" not in out:
|
||||
fail(f"the variant definition renamed + noted:\n{out}")
|
||||
if " gte_ldv0(v);\n" not in out:
|
||||
fail(f"the private use renamed to the canonical name:\n{out}")
|
||||
if "gte_ldv0_m(v); // !FAKE: gte via gte_ldv0_m" not in out:
|
||||
fail(f"the lever variant's use marked:\n{out}")
|
||||
if " gte_ldv0(v);\n}" not in out:
|
||||
fail(f"the direct statement rewritten into the canonical call:\n{out}")
|
||||
if rec["deleted"] != 2 or rec["renamed_defs"] != 1 or rec["use_renames"] != 2 or rec["direct_calls"] != 1 or rec["marked"] != 1:
|
||||
fail(f"record: {rec}")
|
||||
# 5. the header renders every canonical macro once, with its parameters
|
||||
h = render_header(canon2)
|
||||
if h.count("#define gte_ldv0(") != 1 or "#define gte_ldv0_m" in h:
|
||||
fail("header: one canonical definition, no variant")
|
||||
print(f"gte_consolidate --selftest: {'OK' if ok else 'FAIL'} — signatures rtps={s1['bytes'][16:24]} ldv0={s4['bytes'][:16]}; table {len(canon2['canonical'])} canonical / {len(canon2['variants'])} variant; fixture edits {len(edits)}")
|
||||
return ok
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description=__doc__.split("\n")[0])
|
||||
ap.add_argument("--inventory", action="store_true")
|
||||
ap.add_argument("--header", action="store_true")
|
||||
ap.add_argument("--apply", action="store_true")
|
||||
ap.add_argument("--sweep", action="store_true")
|
||||
ap.add_argument("--status", action="store_true")
|
||||
ap.add_argument("--selftest", action="store_true")
|
||||
ap.add_argument("--batch", type=int, default=400)
|
||||
ap.add_argument("--label", default=None)
|
||||
ap.add_argument("--only", nargs="*", default=None)
|
||||
ap.add_argument("--rejudge", action="store_true")
|
||||
ap.add_argument("-j", "--jobs", type=int, default=12)
|
||||
a = ap.parse_args()
|
||||
RUN.mkdir(parents=True, exist_ok=True)
|
||||
if a.selftest:
|
||||
sys.exit(0 if selftest() else 1)
|
||||
if a.status:
|
||||
sys.exit(status())
|
||||
if a.inventory:
|
||||
dl.ensure_census(a.jobs)
|
||||
inv = inventory(a.jobs)
|
||||
canonical_table(inv)
|
||||
return
|
||||
if a.header:
|
||||
inv = json.loads(INVENTORY.read_text()) if INVENTORY.exists() else inventory(a.jobs, quiet=True)
|
||||
canon = canonical_table(inv, quiet=True)
|
||||
write_header(canon)
|
||||
return
|
||||
if a.apply:
|
||||
if not a.label:
|
||||
sys.exit("--label is required")
|
||||
sys.exit(apply_batch(a))
|
||||
if a.sweep:
|
||||
if not a.label:
|
||||
sys.exit("--label is required")
|
||||
sys.exit(sweep(a))
|
||||
ap.print_help()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+73
-5
@@ -69,7 +69,50 @@ MIPS_REG_NAMES = {"zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1",
|
||||
GTE_MNEMONICS = {"lwc2", "swc2", "mtc2", "mfc2", "ctc2", "cfc2", "cop2", "rtps", "rtpt", "nclip", "ncds", "nccs", "ncdt", "ncct",
|
||||
"ncs", "nct", "cdp", "cc", "dpcs", "dpct", "dpcl", "intpl", "sqr", "op", "gpf", "gpl", "avsz3", "avsz4", "mvmva"}
|
||||
FAKE_MARK = "!FAKE:"
|
||||
NON_LEVER_KINDS = {"gte", "verbatim-body"} # Sony's coprocessor idiom; a manifest-listed hand-asm routine — censused, never a lever
|
||||
NON_LEVER_KINDS = {"gte", "verbatim-body", "gte-unsigned"} # Sony's coprocessor idiom; a manifest-listed hand-asm routine — censused, never a lever
|
||||
GTE_LEVER_KIND = "gte-lever" # a GTE op whose clobbers exceed its canonical's (a `_m`/`_v` variant macro, or a direct statement): a steer
|
||||
GTE_VARIANT_NAME = re.compile(r"(_m|_v[0-9a-f]{4})$")
|
||||
_gte_tables = None
|
||||
|
||||
|
||||
def _gte_lookup():
|
||||
"""(sig_cache, canonical-by-bytes) from tools/gte_consolidate.py's files, read once; ({}, {}) when T5 has not run."""
|
||||
global _gte_tables
|
||||
if _gte_tables is None:
|
||||
cache, canon = {}, {}
|
||||
try:
|
||||
import json as _json
|
||||
base = REPO / ".run" / "P36" / "gte"
|
||||
if (base / "sig_cache.json").exists():
|
||||
cache = _json.loads((base / "sig_cache.json").read_text())
|
||||
if (base / "canonical.json").exists():
|
||||
for key, tbl in _json.loads((base / "canonical.json").read_text())["canonical"].items():
|
||||
canon[key] = dict(name=tbl["name"], clob=sorted(tbl["clob"]))
|
||||
except Exception: # noqa: BLE001 — a torn file reads as "no table"
|
||||
cache, canon = {}, {}
|
||||
_gte_tables = (cache, canon)
|
||||
return _gte_tables
|
||||
|
||||
|
||||
def classify_direct_gte(inner):
|
||||
"""'gte' (Sony's idiom or no canonical to compare), 'gte-lever' (clobbers beyond the canonical's), 'gte-unsigned' (a text the
|
||||
consolidation tool has not signed yet — run `tools/gte_consolidate.py --inventory`)."""
|
||||
cache, canon = _gte_lookup()
|
||||
if not canon:
|
||||
return "gte"
|
||||
try:
|
||||
import gte_consolidate as gc
|
||||
tmpl, outs, ins, clob = gc.parse_asm(inner)
|
||||
bound = gc.bind_regs(tmpl, len(outs), len(ins))
|
||||
except Exception: # noqa: BLE001
|
||||
return "gte-unsigned"
|
||||
b = cache.get(bound)
|
||||
if not b or b.startswith("ERROR"):
|
||||
return "gte-unsigned"
|
||||
c = canon.get(f"{b}|{len(outs)}|{len(ins)}")
|
||||
if not c:
|
||||
return "gte"
|
||||
return GTE_LEVER_KIND if sorted(clob) != c["clob"] else "gte"
|
||||
# an `asm-body` site (a whole routine as one asm statement inside a C shell) becomes `verbatim-body` only when the manifest lists the
|
||||
# routine as PERMANENT-VERBATIM; an unlisted or DECOMPILE-* one stays a lever and the guard (verbatim_check) names it
|
||||
NORM_SYM = re.compile(r"\b(?:func|D)_80[0-9A-Fa-f]{6}\b")
|
||||
@@ -479,6 +522,8 @@ def walk_file(raw, rel, is_header):
|
||||
continue
|
||||
role(pos, "asm-stmt")
|
||||
d = span_of_line.get(ln)
|
||||
if kind == "gte" and d:
|
||||
kind = classify_direct_gte(masked[o + 1:c])
|
||||
if d and is_whole_body_asm(masked, line_starts[d["line"] - 1], line_starts[d["end"]] - 1, pos, c):
|
||||
kind = "asm-body" # a whole routine as one asm statement inside a C shell (any template: GTE ones too); the manifest decides its fate
|
||||
add_site("B", kind, pos, masked[pos:c + 1], detail=detail, via="")
|
||||
@@ -501,7 +546,10 @@ def walk_file(raw, rel, is_header):
|
||||
continue
|
||||
name = m.group(1)
|
||||
c = _paren_span(masked, m.end() - 1)
|
||||
add_site("B", asm_macro_names[name], m.start(), masked[m.start():c + 1] if c > 0 else name, detail=name, via=name)
|
||||
k = asm_macro_names[name]
|
||||
if k == "gte" and GTE_VARIANT_NAME.search(name):
|
||||
k = GTE_LEVER_KIND
|
||||
add_site("B", k, m.start(), masked[m.start():c + 1] if c > 0 else name, detail=name, via=name)
|
||||
|
||||
# ---- 4. volatile tokens
|
||||
for m in VOLATILE_KW.finditer(masked):
|
||||
@@ -814,6 +862,17 @@ def run_census(jobs, use_cache=True, out_dir=OUT_DIR_DEFAULT, want_sites=False,
|
||||
what="whole routines written in assembly inside a C shell (the §265 lane's in-function form): the manifest's "
|
||||
"PERMANENT-VERBATIM rows are hand asm and not levers; an asm-body the manifest does not list as permanent "
|
||||
"stays a lever (unlisted_routines names them with their disposition)"),
|
||||
gte_levers=dict(sites=sum(1 for s in sites if s["kind"] == GTE_LEVER_KIND),
|
||||
marked=sum(1 for s in sites if s["kind"] == GTE_LEVER_KIND and s["marked"]),
|
||||
unmarked=sum(1 for s in sites if s["kind"] == GTE_LEVER_KIND and not s["marked"]),
|
||||
via_macro=sum(1 for s in sites if s["kind"] == GTE_LEVER_KIND and s.get("via")),
|
||||
direct=sum(1 for s in sites if s["kind"] == GTE_LEVER_KIND and not s.get("via")),
|
||||
unsigned=sum(1 for s in sites if s["kind"] == "gte-unsigned"),
|
||||
what="GTE ops whose clobbers exceed the canonical macro's (a scheduling steer): levers for T7, outside the headline number"),
|
||||
per_tu_asm_macro_definitions=dict(total=sum(1 for m in mdefs_all if m["tu"] != gte_header),
|
||||
by_kind=dict(collections.Counter(m["kind"] for m in mdefs_all if m["tu"] != gte_header)),
|
||||
gte_variants=sum(1 for m in mdefs_all if m["tu"] != gte_header and m["kind"] == "gte" and GTE_VARIANT_NAME.search(m["name"])),
|
||||
what="asm-bearing #define blocks outside the GTE header: 0 at the close (T5 deletes the canonical duplicates, T7 the variants)"),
|
||||
macro_definitions=dict(total=len(mdefs_all), names=len(macro_names), kinds=dict(macro_kinds),
|
||||
names_with_multiple_texts=sum(1 for n, t in macro_texts.items() if len(t) > 1),
|
||||
top=[(n, c, len(macro_texts[n])) for n, c in macro_names.most_common(25)]),
|
||||
@@ -888,6 +947,12 @@ def render(s):
|
||||
L.append(f" THE PHASE'S NUMBER (pins + asm statements, GTE excluded): {ab['sites']:,} sites in {ab['bodies']:,} bodies "
|
||||
f"({ab['distinct_bodies']:,} distinct) · marked !FAKE {ab['marked']:,} · UNMARKED {ab['unmarked']:,}")
|
||||
L.append(f" orphan !FAKE markers (no pin/asm site on the line nor below): {s.get('orphan_markers', {}).get('count', 0)}")
|
||||
g = s.get("gte_levers", {})
|
||||
if g:
|
||||
L.append(f" GTE levers (clobbers beyond the canonical macro's): {g['sites']:,} sites ({g['via_macro']:,} via a variant macro, {g['direct']:,} direct) · "
|
||||
f"marked {g['marked']:,} · UNMARKED {g['unmarked']:,} · unsigned GTE statements {g['unsigned']:,}")
|
||||
pt = s.get("per_tu_asm_macro_definitions", {})
|
||||
L.append(f" per-TU asm macro definitions outside the GTE header: {pt.get('total', 0):,} {pt.get('by_kind', {})} (GTE variants {pt.get('gte_variants', 0):,})")
|
||||
p = s["pins"]
|
||||
L.append(f" pins: {p['sites']:,} · $0 {p['zero']:,} · $sp {p['sp']} · with initializer {p['init']:,} · volatile-qualified "
|
||||
f"{p['volatile_qualified']} · bare-name {p['bare_name']} · spellings {p['spelling']}")
|
||||
@@ -1032,9 +1097,12 @@ def main():
|
||||
ab = [x for x in sites if x["cls"] in "AB" and x["kind"] not in NON_LEVER_KINDS and x["tu"] != a.gte_header]
|
||||
un = [x for x in ab if not x["marked"]]
|
||||
if a.strict:
|
||||
print(f"lever_census --check --strict: pins {s['pins']['sites']}, asm {sum(1 for x in ab if x['cls'] == 'B')}, "
|
||||
f"volatile-needed {s['classes']['C']['sites']}, register-needed {s['classes']['D']['sites']} — {'OK' if not ab else 'FAIL'}")
|
||||
if ab:
|
||||
pt = s.get("per_tu_asm_macro_definitions", {}).get("total", 0)
|
||||
gl = s.get("gte_levers", {}).get("sites", 0)
|
||||
print(f"lever_census --check --strict: pins {s['pins']['sites']}, asm {sum(1 for x in ab if x['cls'] == 'B' and x['kind'] != GTE_LEVER_KIND)}, "
|
||||
f"gte-levers {gl}, per-TU asm macro definitions {pt}, volatile-needed {s['classes']['C']['sites']}, register-needed {s['classes']['D']['sites']} — "
|
||||
f"{'OK' if not ab and not pt else 'FAIL'}")
|
||||
if ab or pt:
|
||||
for x in ab[:10]:
|
||||
print(f" {x['tu']}:{x['line']} {x['fn']} {x['cls']}/{x['kind']} {x['detail']}")
|
||||
rc = 1
|
||||
|
||||
Reference in New Issue
Block a user