zexus 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +0 -0
- package/README.md +2513 -0
- package/bin/zexus +2 -0
- package/bin/zpics +2 -0
- package/bin/zpm +2 -0
- package/bin/zx +2 -0
- package/bin/zx-deploy +2 -0
- package/bin/zx-dev +2 -0
- package/bin/zx-run +2 -0
- package/package.json +66 -0
- package/scripts/README.md +24 -0
- package/scripts/postinstall.js +44 -0
- package/shared_config.json +24 -0
- package/src/README.md +1525 -0
- package/src/tests/run_zexus_tests.py +117 -0
- package/src/tests/test_all_phases.zx +346 -0
- package/src/tests/test_blockchain_features.zx +306 -0
- package/src/tests/test_complexity_features.zx +321 -0
- package/src/tests/test_core_integration.py +185 -0
- package/src/tests/test_phase10_ecosystem.zx +177 -0
- package/src/tests/test_phase1_modifiers.zx +87 -0
- package/src/tests/test_phase2_plugins.zx +80 -0
- package/src/tests/test_phase3_security.zx +97 -0
- package/src/tests/test_phase4_vfs.zx +116 -0
- package/src/tests/test_phase5_types.zx +117 -0
- package/src/tests/test_phase6_metaprogramming.zx +125 -0
- package/src/tests/test_phase7_optimization.zx +132 -0
- package/src/tests/test_phase9_advanced_types.zx +157 -0
- package/src/tests/test_security_features.py +419 -0
- package/src/tests/test_security_features.zx +276 -0
- package/src/tests/test_simple_zx.zx +1 -0
- package/src/tests/test_verification_simple.zx +69 -0
- package/src/zexus/__init__.py +28 -0
- package/src/zexus/__main__.py +5 -0
- package/src/zexus/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/advanced_types.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/builtin_modules.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/capability_system.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/complexity_system.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/concurrency_system.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/config.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/dependency_injection.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/ecosystem.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/environment.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/error_reporter.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/hybrid_orchestrator.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/lexer.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/metaprogramming.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/module_cache.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/object.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/optimization.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/plugin_system.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/policy_engine.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/security.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/stdlib_integration.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/strategy_recovery.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/syntax_validator.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/type_system.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/virtual_filesystem.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/zexus_ast.cpython-312.pyc +0 -0
- package/src/zexus/__pycache__/zexus_token.cpython-312.pyc +0 -0
- package/src/zexus/advanced_types.py +401 -0
- package/src/zexus/blockchain/__init__.py +40 -0
- package/src/zexus/blockchain/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/blockchain/__pycache__/crypto.cpython-312.pyc +0 -0
- package/src/zexus/blockchain/__pycache__/ledger.cpython-312.pyc +0 -0
- package/src/zexus/blockchain/__pycache__/transaction.cpython-312.pyc +0 -0
- package/src/zexus/blockchain/crypto.py +463 -0
- package/src/zexus/blockchain/ledger.py +255 -0
- package/src/zexus/blockchain/transaction.py +267 -0
- package/src/zexus/builtin_modules.py +284 -0
- package/src/zexus/builtin_plugins.py +317 -0
- package/src/zexus/capability_system.py +372 -0
- package/src/zexus/cli/__init__.py +2 -0
- package/src/zexus/cli/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/cli/__pycache__/main.cpython-312.pyc +0 -0
- package/src/zexus/cli/main.py +707 -0
- package/src/zexus/cli/zpm.py +203 -0
- package/src/zexus/compare_interpreter_compiler.py +146 -0
- package/src/zexus/compiler/__init__.py +169 -0
- package/src/zexus/compiler/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/compiler/__pycache__/lexer.cpython-312.pyc +0 -0
- package/src/zexus/compiler/__pycache__/parser.cpython-312.pyc +0 -0
- package/src/zexus/compiler/__pycache__/zexus_ast.cpython-312.pyc +0 -0
- package/src/zexus/compiler/bytecode.py +266 -0
- package/src/zexus/compiler/compat_runtime.py +277 -0
- package/src/zexus/compiler/lexer.py +257 -0
- package/src/zexus/compiler/parser.py +779 -0
- package/src/zexus/compiler/semantic.py +118 -0
- package/src/zexus/compiler/zexus_ast.py +454 -0
- package/src/zexus/complexity_system.py +575 -0
- package/src/zexus/concurrency_system.py +493 -0
- package/src/zexus/config.py +201 -0
- package/src/zexus/crypto_bridge.py +19 -0
- package/src/zexus/dependency_injection.py +423 -0
- package/src/zexus/ecosystem.py +434 -0
- package/src/zexus/environment.py +101 -0
- package/src/zexus/environment_manager.py +119 -0
- package/src/zexus/error_reporter.py +314 -0
- package/src/zexus/evaluator/__init__.py +12 -0
- package/src/zexus/evaluator/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/bytecode_compiler.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/core.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/expressions.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/functions.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/integration.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/statements.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/__pycache__/utils.cpython-312.pyc +0 -0
- package/src/zexus/evaluator/bytecode_compiler.py +700 -0
- package/src/zexus/evaluator/core.py +891 -0
- package/src/zexus/evaluator/expressions.py +827 -0
- package/src/zexus/evaluator/functions.py +3989 -0
- package/src/zexus/evaluator/integration.py +396 -0
- package/src/zexus/evaluator/statements.py +4303 -0
- package/src/zexus/evaluator/utils.py +126 -0
- package/src/zexus/evaluator_original.py +2041 -0
- package/src/zexus/external_bridge.py +16 -0
- package/src/zexus/find_affected_imports.sh +155 -0
- package/src/zexus/hybrid_orchestrator.py +152 -0
- package/src/zexus/input_validation.py +259 -0
- package/src/zexus/lexer.py +571 -0
- package/src/zexus/logging.py +89 -0
- package/src/zexus/lsp/__init__.py +9 -0
- package/src/zexus/lsp/completion_provider.py +207 -0
- package/src/zexus/lsp/definition_provider.py +22 -0
- package/src/zexus/lsp/hover_provider.py +71 -0
- package/src/zexus/lsp/server.py +269 -0
- package/src/zexus/lsp/symbol_provider.py +31 -0
- package/src/zexus/metaprogramming.py +321 -0
- package/src/zexus/module_cache.py +89 -0
- package/src/zexus/module_manager.py +107 -0
- package/src/zexus/object.py +973 -0
- package/src/zexus/optimization.py +424 -0
- package/src/zexus/parser/__init__.py +31 -0
- package/src/zexus/parser/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/parser/__pycache__/parser.cpython-312.pyc +0 -0
- package/src/zexus/parser/__pycache__/strategy_context.cpython-312.pyc +0 -0
- package/src/zexus/parser/__pycache__/strategy_structural.cpython-312.pyc +0 -0
- package/src/zexus/parser/integration.py +86 -0
- package/src/zexus/parser/parser.py +3977 -0
- package/src/zexus/parser/strategy_context.py +7254 -0
- package/src/zexus/parser/strategy_structural.py +1033 -0
- package/src/zexus/persistence.py +391 -0
- package/src/zexus/plugin_system.py +290 -0
- package/src/zexus/policy_engine.py +365 -0
- package/src/zexus/profiler/__init__.py +5 -0
- package/src/zexus/profiler/profiler.py +233 -0
- package/src/zexus/purity_system.py +398 -0
- package/src/zexus/runtime/__init__.py +20 -0
- package/src/zexus/runtime/async_runtime.py +324 -0
- package/src/zexus/search_old_imports.sh +65 -0
- package/src/zexus/security.py +1407 -0
- package/src/zexus/stack_trace.py +233 -0
- package/src/zexus/stdlib/__init__.py +27 -0
- package/src/zexus/stdlib/blockchain.py +341 -0
- package/src/zexus/stdlib/compression.py +167 -0
- package/src/zexus/stdlib/crypto.py +124 -0
- package/src/zexus/stdlib/datetime.py +163 -0
- package/src/zexus/stdlib/db_mongo.py +199 -0
- package/src/zexus/stdlib/db_mysql.py +162 -0
- package/src/zexus/stdlib/db_postgres.py +163 -0
- package/src/zexus/stdlib/db_sqlite.py +133 -0
- package/src/zexus/stdlib/encoding.py +230 -0
- package/src/zexus/stdlib/fs.py +195 -0
- package/src/zexus/stdlib/http.py +219 -0
- package/src/zexus/stdlib/http_server.py +248 -0
- package/src/zexus/stdlib/json_module.py +61 -0
- package/src/zexus/stdlib/math.py +360 -0
- package/src/zexus/stdlib/os_module.py +265 -0
- package/src/zexus/stdlib/regex.py +148 -0
- package/src/zexus/stdlib/sockets.py +253 -0
- package/src/zexus/stdlib/test_framework.zx +208 -0
- package/src/zexus/stdlib/test_runner.zx +119 -0
- package/src/zexus/stdlib_integration.py +341 -0
- package/src/zexus/strategy_recovery.py +256 -0
- package/src/zexus/syntax_validator.py +356 -0
- package/src/zexus/testing/zpics.py +407 -0
- package/src/zexus/testing/zpics_runtime.py +369 -0
- package/src/zexus/type_system.py +374 -0
- package/src/zexus/validation_system.py +569 -0
- package/src/zexus/virtual_filesystem.py +355 -0
- package/src/zexus/vm/__init__.py +8 -0
- package/src/zexus/vm/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/async_optimizer.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/bytecode.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/cache.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/jit.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/memory_manager.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/memory_pool.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/optimizer.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/parallel_vm.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/peephole_optimizer.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/profiler.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/register_allocator.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/register_vm.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/ssa_converter.cpython-312.pyc +0 -0
- package/src/zexus/vm/__pycache__/vm.cpython-312.pyc +0 -0
- package/src/zexus/vm/async_optimizer.py +420 -0
- package/src/zexus/vm/bytecode.py +428 -0
- package/src/zexus/vm/bytecode_converter.py +297 -0
- package/src/zexus/vm/cache.py +532 -0
- package/src/zexus/vm/jit.py +720 -0
- package/src/zexus/vm/memory_manager.py +520 -0
- package/src/zexus/vm/memory_pool.py +511 -0
- package/src/zexus/vm/optimizer.py +478 -0
- package/src/zexus/vm/parallel_vm.py +899 -0
- package/src/zexus/vm/peephole_optimizer.py +452 -0
- package/src/zexus/vm/profiler.py +527 -0
- package/src/zexus/vm/register_allocator.py +462 -0
- package/src/zexus/vm/register_vm.py +520 -0
- package/src/zexus/vm/ssa_converter.py +757 -0
- package/src/zexus/vm/vm.py +1392 -0
- package/src/zexus/zexus_ast.py +1782 -0
- package/src/zexus/zexus_token.py +253 -0
- package/src/zexus/zpm/__init__.py +15 -0
- package/src/zexus/zpm/installer.py +116 -0
- package/src/zexus/zpm/package_manager.py +208 -0
- package/src/zexus/zpm/publisher.py +98 -0
- package/src/zexus/zpm/registry.py +110 -0
- package/src/zexus.egg-info/PKG-INFO +2235 -0
- package/src/zexus.egg-info/SOURCES.txt +876 -0
- package/src/zexus.egg-info/dependency_links.txt +1 -0
- package/src/zexus.egg-info/entry_points.txt +3 -0
- package/src/zexus.egg-info/not-zip-safe +1 -0
- package/src/zexus.egg-info/requires.txt +14 -0
- package/src/zexus.egg-info/top_level.txt +2 -0
- package/zexus.json +14 -0
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Register Allocator for Zexus Register VM
|
|
3
|
+
|
|
4
|
+
Implements graph coloring register allocation with:
|
|
5
|
+
- Live range analysis
|
|
6
|
+
- Interference graph construction
|
|
7
|
+
- Graph coloring with spilling
|
|
8
|
+
- Register coalescing
|
|
9
|
+
- Optimized register assignment
|
|
10
|
+
|
|
11
|
+
Phase 8.5 of VM Optimization Project
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
from typing import Dict, List, Set, Tuple, Optional, Any
|
|
16
|
+
from collections import defaultdict
|
|
17
|
+
from enum import Enum
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class RegisterType(Enum):
|
|
21
|
+
"""Register types"""
|
|
22
|
+
GENERAL = 0 # General purpose registers
|
|
23
|
+
TEMP = 1 # Temporary registers
|
|
24
|
+
ARGUMENT = 2 # Function argument registers
|
|
25
|
+
RETURN = 3 # Return value registers
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class LiveRange:
|
|
30
|
+
"""Live range for a variable"""
|
|
31
|
+
variable: str
|
|
32
|
+
start: int
|
|
33
|
+
end: int
|
|
34
|
+
uses: List[int] = field(default_factory=list)
|
|
35
|
+
defs: List[int] = field(default_factory=list)
|
|
36
|
+
|
|
37
|
+
def overlaps(self, other: 'LiveRange') -> bool:
|
|
38
|
+
"""Check if this live range overlaps with another"""
|
|
39
|
+
return not (self.end < other.start or other.end < self.start)
|
|
40
|
+
|
|
41
|
+
def __hash__(self):
|
|
42
|
+
return hash((self.variable, self.start, self.end))
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class InterferenceGraph:
|
|
47
|
+
"""Graph representing register interference"""
|
|
48
|
+
nodes: Set[str] = field(default_factory=set)
|
|
49
|
+
edges: Dict[str, Set[str]] = field(default_factory=lambda: defaultdict(set))
|
|
50
|
+
|
|
51
|
+
def add_node(self, var: str):
|
|
52
|
+
"""Add a node (variable) to the graph"""
|
|
53
|
+
self.nodes.add(var)
|
|
54
|
+
if var not in self.edges:
|
|
55
|
+
self.edges[var] = set()
|
|
56
|
+
|
|
57
|
+
def add_edge(self, var1: str, var2: str):
|
|
58
|
+
"""Add an interference edge between two variables"""
|
|
59
|
+
self.add_node(var1)
|
|
60
|
+
self.add_node(var2)
|
|
61
|
+
self.edges[var1].add(var2)
|
|
62
|
+
self.edges[var2].add(var1)
|
|
63
|
+
|
|
64
|
+
def degree(self, var: str) -> int:
|
|
65
|
+
"""Get the degree (number of neighbors) of a variable"""
|
|
66
|
+
return len(self.edges.get(var, set()))
|
|
67
|
+
|
|
68
|
+
def neighbors(self, var: str) -> Set[str]:
|
|
69
|
+
"""Get neighbors of a variable"""
|
|
70
|
+
return self.edges.get(var, set())
|
|
71
|
+
|
|
72
|
+
def remove_node(self, var: str):
|
|
73
|
+
"""Remove a node from the graph"""
|
|
74
|
+
if var in self.nodes:
|
|
75
|
+
self.nodes.remove(var)
|
|
76
|
+
|
|
77
|
+
# Remove edges
|
|
78
|
+
neighbors = self.edges.get(var, set())
|
|
79
|
+
for neighbor in neighbors:
|
|
80
|
+
self.edges[neighbor].discard(var)
|
|
81
|
+
|
|
82
|
+
if var in self.edges:
|
|
83
|
+
del self.edges[var]
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@dataclass
|
|
87
|
+
class AllocationResult:
|
|
88
|
+
"""Result of register allocation"""
|
|
89
|
+
allocation: Dict[str, int] # Variable -> Register mapping
|
|
90
|
+
spilled: Set[str] # Variables that had to be spilled to memory
|
|
91
|
+
num_registers_used: int
|
|
92
|
+
coalesced_moves: int = 0
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class RegisterAllocator:
|
|
96
|
+
"""
|
|
97
|
+
Graph coloring register allocator
|
|
98
|
+
|
|
99
|
+
Uses the classic graph coloring algorithm with improvements:
|
|
100
|
+
- Chaitin-Briggs coloring with optimistic spilling
|
|
101
|
+
- Coalescing of move-related variables
|
|
102
|
+
- Biased coloring for better cache locality
|
|
103
|
+
- Spill cost minimization
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
def __init__(self, num_registers: int = 16, num_temp_registers: int = 8):
|
|
107
|
+
"""
|
|
108
|
+
Initialize register allocator
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
num_registers: Number of general-purpose registers available
|
|
112
|
+
num_temp_registers: Number of temporary registers
|
|
113
|
+
"""
|
|
114
|
+
self.num_registers = num_registers
|
|
115
|
+
self.num_temp_registers = num_temp_registers
|
|
116
|
+
self.total_registers = num_registers + num_temp_registers
|
|
117
|
+
|
|
118
|
+
# Reserved registers
|
|
119
|
+
self.reserved = {0, 1} # R0 (zero), R1 (stack pointer)
|
|
120
|
+
self.available_registers = set(range(2, self.num_registers))
|
|
121
|
+
self.temp_registers = set(range(self.num_registers, self.total_registers))
|
|
122
|
+
|
|
123
|
+
# Statistics
|
|
124
|
+
self.stats = {
|
|
125
|
+
'allocations': 0,
|
|
126
|
+
'spills': 0,
|
|
127
|
+
'coalesced_moves': 0,
|
|
128
|
+
'colorings': 0,
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
def allocate(
|
|
132
|
+
self,
|
|
133
|
+
instructions: List[Tuple],
|
|
134
|
+
live_ranges: Dict[str, LiveRange]
|
|
135
|
+
) -> AllocationResult:
|
|
136
|
+
"""
|
|
137
|
+
Allocate registers for variables
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
instructions: List of instructions
|
|
141
|
+
live_ranges: Live ranges for all variables
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
AllocationResult with allocation and spilled variables
|
|
145
|
+
"""
|
|
146
|
+
self.stats['allocations'] += 1
|
|
147
|
+
|
|
148
|
+
# Build interference graph
|
|
149
|
+
interference_graph = self._build_interference_graph(live_ranges)
|
|
150
|
+
|
|
151
|
+
# Find move-related variables for coalescing
|
|
152
|
+
move_pairs = self._find_move_pairs(instructions)
|
|
153
|
+
|
|
154
|
+
# Coalesce moves when possible
|
|
155
|
+
coalesced = self._coalesce_moves(interference_graph, move_pairs)
|
|
156
|
+
|
|
157
|
+
# Color the graph
|
|
158
|
+
allocation, spilled = self._color_graph(
|
|
159
|
+
interference_graph,
|
|
160
|
+
self.available_registers
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
return AllocationResult(
|
|
164
|
+
allocation=allocation,
|
|
165
|
+
spilled=spilled,
|
|
166
|
+
num_registers_used=len(set(allocation.values())),
|
|
167
|
+
coalesced_moves=coalesced
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
def _build_interference_graph(
|
|
171
|
+
self,
|
|
172
|
+
live_ranges: Dict[str, LiveRange]
|
|
173
|
+
) -> InterferenceGraph:
|
|
174
|
+
"""
|
|
175
|
+
Build interference graph from live ranges
|
|
176
|
+
|
|
177
|
+
Two variables interfere if their live ranges overlap
|
|
178
|
+
"""
|
|
179
|
+
graph = InterferenceGraph()
|
|
180
|
+
|
|
181
|
+
# Add all variables as nodes
|
|
182
|
+
for var in live_ranges:
|
|
183
|
+
graph.add_node(var)
|
|
184
|
+
|
|
185
|
+
# Add edges for interfering variables
|
|
186
|
+
variables = list(live_ranges.keys())
|
|
187
|
+
for i, var1 in enumerate(variables):
|
|
188
|
+
for var2 in variables[i + 1:]:
|
|
189
|
+
if live_ranges[var1].overlaps(live_ranges[var2]):
|
|
190
|
+
graph.add_edge(var1, var2)
|
|
191
|
+
|
|
192
|
+
return graph
|
|
193
|
+
|
|
194
|
+
def _find_move_pairs(self, instructions: List[Tuple]) -> List[Tuple[str, str]]:
|
|
195
|
+
"""
|
|
196
|
+
Find pairs of variables related by move instructions
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
instructions: List of instructions (tuples or Instruction objects)
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
List of (source, dest) variable pairs
|
|
203
|
+
"""
|
|
204
|
+
move_pairs = []
|
|
205
|
+
|
|
206
|
+
# Normalize instructions
|
|
207
|
+
normalized = []
|
|
208
|
+
for instr in instructions:
|
|
209
|
+
if instr is None:
|
|
210
|
+
normalized.append(None)
|
|
211
|
+
elif hasattr(instr, 'opcode') and hasattr(instr, 'arg'):
|
|
212
|
+
normalized.append((instr.opcode, instr.arg))
|
|
213
|
+
else:
|
|
214
|
+
normalized.append(instr)
|
|
215
|
+
|
|
216
|
+
for instr in normalized:
|
|
217
|
+
if not instr or len(instr) < 2:
|
|
218
|
+
continue
|
|
219
|
+
|
|
220
|
+
opcode = instr[0]
|
|
221
|
+
|
|
222
|
+
# Detect move instructions (LOAD_FAST followed by STORE_FAST is a move)
|
|
223
|
+
if opcode == 'LOAD_FAST' and len(instr) >= 3:
|
|
224
|
+
source = instr[1]
|
|
225
|
+
dest = instr[2] if len(instr) > 2 else None
|
|
226
|
+
if source and dest and isinstance(source, str) and isinstance(dest, str):
|
|
227
|
+
move_pairs.append((source, dest))
|
|
228
|
+
|
|
229
|
+
# MOVE opcode
|
|
230
|
+
elif opcode == 'MOVE' and len(instr) >= 3:
|
|
231
|
+
source = instr[1]
|
|
232
|
+
dest = instr[2]
|
|
233
|
+
if isinstance(source, str) and isinstance(dest, str):
|
|
234
|
+
move_pairs.append((source, dest))
|
|
235
|
+
|
|
236
|
+
return move_pairs
|
|
237
|
+
|
|
238
|
+
def _coalesce_moves(
|
|
239
|
+
self,
|
|
240
|
+
graph: InterferenceGraph,
|
|
241
|
+
move_pairs: List[Tuple[str, str]]
|
|
242
|
+
) -> int:
|
|
243
|
+
"""
|
|
244
|
+
Coalesce move-related variables when possible
|
|
245
|
+
|
|
246
|
+
Two variables can be coalesced if:
|
|
247
|
+
1. They are related by a move
|
|
248
|
+
2. They don't interfere
|
|
249
|
+
3. Coalescing won't make the graph uncolorable
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
Number of moves coalesced
|
|
253
|
+
"""
|
|
254
|
+
coalesced_count = 0
|
|
255
|
+
|
|
256
|
+
for source, dest in move_pairs:
|
|
257
|
+
# Check if both variables exist in graph
|
|
258
|
+
if source not in graph.nodes or dest not in graph.nodes:
|
|
259
|
+
continue
|
|
260
|
+
|
|
261
|
+
# Check if they interfere
|
|
262
|
+
if dest in graph.neighbors(source):
|
|
263
|
+
continue
|
|
264
|
+
|
|
265
|
+
# Check Briggs' conservative criterion:
|
|
266
|
+
# Coalescing is safe if the merged node has < K neighbors with degree >= K
|
|
267
|
+
combined_neighbors = graph.neighbors(source) | graph.neighbors(dest)
|
|
268
|
+
high_degree_neighbors = sum(
|
|
269
|
+
1 for n in combined_neighbors
|
|
270
|
+
if graph.degree(n) >= self.num_registers
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
if high_degree_neighbors < self.num_registers:
|
|
274
|
+
# Safe to coalesce
|
|
275
|
+
self._merge_nodes(graph, source, dest)
|
|
276
|
+
coalesced_count += 1
|
|
277
|
+
|
|
278
|
+
self.stats['coalesced_moves'] += coalesced_count
|
|
279
|
+
return coalesced_count
|
|
280
|
+
|
|
281
|
+
def _merge_nodes(self, graph: InterferenceGraph, var1: str, var2: str):
|
|
282
|
+
"""
|
|
283
|
+
Merge two nodes in the interference graph
|
|
284
|
+
|
|
285
|
+
Combines var1 and var2 into a single node (var1)
|
|
286
|
+
"""
|
|
287
|
+
# Add all edges from var2 to var1
|
|
288
|
+
for neighbor in graph.neighbors(var2):
|
|
289
|
+
if neighbor != var1:
|
|
290
|
+
graph.add_edge(var1, neighbor)
|
|
291
|
+
|
|
292
|
+
# Remove var2
|
|
293
|
+
graph.remove_node(var2)
|
|
294
|
+
|
|
295
|
+
def _color_graph(
|
|
296
|
+
self,
|
|
297
|
+
graph: InterferenceGraph,
|
|
298
|
+
available_colors: Set[int]
|
|
299
|
+
) -> Tuple[Dict[str, int], Set[str]]:
|
|
300
|
+
"""
|
|
301
|
+
Color the interference graph using graph coloring algorithm
|
|
302
|
+
|
|
303
|
+
Uses Chaitin-Briggs algorithm with optimistic spilling.
|
|
304
|
+
|
|
305
|
+
Returns:
|
|
306
|
+
(allocation dict, set of spilled variables)
|
|
307
|
+
"""
|
|
308
|
+
self.stats['colorings'] += 1
|
|
309
|
+
|
|
310
|
+
allocation = {}
|
|
311
|
+
spilled = set()
|
|
312
|
+
stack = []
|
|
313
|
+
remaining_graph = InterferenceGraph()
|
|
314
|
+
remaining_graph.nodes = graph.nodes.copy()
|
|
315
|
+
remaining_graph.edges = {k: v.copy() for k, v in graph.edges.items()}
|
|
316
|
+
|
|
317
|
+
# Simplification phase: remove nodes with degree < K
|
|
318
|
+
while remaining_graph.nodes:
|
|
319
|
+
# Find node with degree < K
|
|
320
|
+
low_degree_node = None
|
|
321
|
+
for node in remaining_graph.nodes:
|
|
322
|
+
if remaining_graph.degree(node) < len(available_colors):
|
|
323
|
+
low_degree_node = node
|
|
324
|
+
break
|
|
325
|
+
|
|
326
|
+
if low_degree_node:
|
|
327
|
+
# Remove and push to stack
|
|
328
|
+
stack.append((low_degree_node, remaining_graph.neighbors(low_degree_node).copy()))
|
|
329
|
+
remaining_graph.remove_node(low_degree_node)
|
|
330
|
+
else:
|
|
331
|
+
# Need to spill - choose node with highest degree
|
|
332
|
+
if not remaining_graph.nodes:
|
|
333
|
+
break
|
|
334
|
+
|
|
335
|
+
spill_node = max(remaining_graph.nodes, key=lambda n: remaining_graph.degree(n))
|
|
336
|
+
stack.append((spill_node, remaining_graph.neighbors(spill_node).copy()))
|
|
337
|
+
remaining_graph.remove_node(spill_node)
|
|
338
|
+
spilled.add(spill_node)
|
|
339
|
+
self.stats['spills'] += 1
|
|
340
|
+
|
|
341
|
+
# Coloring phase: assign colors from stack
|
|
342
|
+
while stack:
|
|
343
|
+
node, neighbors = stack.pop()
|
|
344
|
+
|
|
345
|
+
# Get colors used by neighbors
|
|
346
|
+
used_colors = {
|
|
347
|
+
allocation[n] for n in neighbors
|
|
348
|
+
if n in allocation
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
# Find available color
|
|
352
|
+
available = available_colors - used_colors
|
|
353
|
+
|
|
354
|
+
if available:
|
|
355
|
+
# Assign first available color
|
|
356
|
+
allocation[node] = min(available)
|
|
357
|
+
elif node not in spilled:
|
|
358
|
+
# Optimistic coloring failed - must spill
|
|
359
|
+
spilled.add(node)
|
|
360
|
+
self.stats['spills'] += 1
|
|
361
|
+
|
|
362
|
+
return allocation, spilled
|
|
363
|
+
|
|
364
|
+
def get_stats(self) -> Dict[str, Any]:
|
|
365
|
+
"""Get allocation statistics"""
|
|
366
|
+
return self.stats.copy()
|
|
367
|
+
|
|
368
|
+
def reset_stats(self):
|
|
369
|
+
"""Reset statistics"""
|
|
370
|
+
self.stats = {
|
|
371
|
+
'allocations': 0,
|
|
372
|
+
'spills': 0,
|
|
373
|
+
'coalesced_moves': 0,
|
|
374
|
+
'colorings': 0,
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def compute_live_ranges(instructions: List[Tuple]) -> Dict[str, LiveRange]:
|
|
379
|
+
"""
|
|
380
|
+
Compute live ranges for all variables in instructions
|
|
381
|
+
|
|
382
|
+
Args:
|
|
383
|
+
instructions: List of instructions (tuples or Instruction objects)
|
|
384
|
+
|
|
385
|
+
Returns:
|
|
386
|
+
Dictionary mapping variable names to their live ranges
|
|
387
|
+
"""
|
|
388
|
+
live_ranges = {}
|
|
389
|
+
|
|
390
|
+
# Normalize instructions
|
|
391
|
+
normalized = []
|
|
392
|
+
for instr in instructions:
|
|
393
|
+
if instr is None:
|
|
394
|
+
normalized.append(None)
|
|
395
|
+
elif hasattr(instr, 'opcode') and hasattr(instr, 'arg'):
|
|
396
|
+
# Instruction object from peephole optimizer
|
|
397
|
+
normalized.append((instr.opcode, instr.arg))
|
|
398
|
+
else:
|
|
399
|
+
# Already a tuple
|
|
400
|
+
normalized.append(instr)
|
|
401
|
+
|
|
402
|
+
# First pass: find all def and use positions
|
|
403
|
+
for i, instr in enumerate(normalized):
|
|
404
|
+
if not instr or len(instr) < 2:
|
|
405
|
+
continue
|
|
406
|
+
|
|
407
|
+
opcode = instr[0]
|
|
408
|
+
|
|
409
|
+
# Extract variable uses and defs
|
|
410
|
+
defs, uses = _extract_vars(instr)
|
|
411
|
+
|
|
412
|
+
# Update live ranges
|
|
413
|
+
for var in defs:
|
|
414
|
+
if var not in live_ranges:
|
|
415
|
+
live_ranges[var] = LiveRange(variable=var, start=i, end=i)
|
|
416
|
+
live_ranges[var].defs.append(i)
|
|
417
|
+
live_ranges[var].end = max(live_ranges[var].end, i)
|
|
418
|
+
|
|
419
|
+
for var in uses:
|
|
420
|
+
if var not in live_ranges:
|
|
421
|
+
live_ranges[var] = LiveRange(variable=var, start=i, end=i)
|
|
422
|
+
live_ranges[var].uses.append(i)
|
|
423
|
+
live_ranges[var].start = min(live_ranges[var].start, i)
|
|
424
|
+
live_ranges[var].end = max(live_ranges[var].end, i)
|
|
425
|
+
|
|
426
|
+
return live_ranges
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def _extract_vars(instr: Tuple) -> Tuple[List[str], List[str]]:
|
|
430
|
+
"""
|
|
431
|
+
Extract variables defined and used in an instruction
|
|
432
|
+
|
|
433
|
+
Args:
|
|
434
|
+
instr: Instruction tuple
|
|
435
|
+
|
|
436
|
+
Returns:
|
|
437
|
+
(defs, uses) - lists of variable names
|
|
438
|
+
"""
|
|
439
|
+
opcode = instr[0] if instr else None
|
|
440
|
+
defs = []
|
|
441
|
+
uses = []
|
|
442
|
+
|
|
443
|
+
# Simple heuristic: first arg is often dest, rest are sources
|
|
444
|
+
if opcode in ('LOAD_FAST', 'STORE_FAST'):
|
|
445
|
+
if len(instr) > 1 and isinstance(instr[1], str):
|
|
446
|
+
if opcode == 'STORE_FAST':
|
|
447
|
+
defs.append(instr[1])
|
|
448
|
+
else:
|
|
449
|
+
uses.append(instr[1])
|
|
450
|
+
|
|
451
|
+
elif opcode in ('BINARY_ADD', 'BINARY_SUB', 'BINARY_MUL', 'BINARY_DIV'):
|
|
452
|
+
# Typically: dest = src1 op src2
|
|
453
|
+
if len(instr) >= 4:
|
|
454
|
+
dest, src1, src2 = instr[1], instr[2], instr[3]
|
|
455
|
+
if isinstance(dest, str):
|
|
456
|
+
defs.append(dest)
|
|
457
|
+
if isinstance(src1, str):
|
|
458
|
+
uses.append(src1)
|
|
459
|
+
if isinstance(src2, str):
|
|
460
|
+
uses.append(src2)
|
|
461
|
+
|
|
462
|
+
return defs, uses
|