codecin 5.4.2__py3-none-any.whl
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.
- codecin/__init__.py +27 -0
- codecin/aot.py +229 -0
- codecin/assembler.py +548 -0
- codecin/cache.py +71 -0
- codecin/cin.py +2558 -0
- codecin/cli.py +282 -0
- codecin/config.py +58 -0
- codecin/console.py +183 -0
- codecin/cpu.py +1654 -0
- codecin/crom.py +222 -0
- codecin/debugger.py +659 -0
- codecin/disasm.py +86 -0
- codecin/errors.py +58 -0
- codecin/isa.py +457 -0
- codecin/jit.py +270 -0
- codecin/libcodecin_native.so +0 -0
- codecin/logger.py +145 -0
- codecin/memory.py +348 -0
- codecin/native/aot/aot.go +70 -0
- codecin/native/aot/build.go +219 -0
- codecin/native/aot/stub_main.go.txt +22 -0
- codecin/native/build.ps1 +33 -0
- codecin/native/build.sh +48 -0
- codecin/native/cmd/codecin/main.go +287 -0
- codecin/native/compiler/codegen.go +1938 -0
- codecin/native/compiler/compile_test.go +164 -0
- codecin/native/compiler/parser.go +1071 -0
- codecin/native/compiler/syscalls.go +86 -0
- codecin/native/compiler/tokenizer.go +336 -0
- codecin/native/compiler/types.go +213 -0
- codecin/native/engine/audio.go +152 -0
- codecin/native/engine/audio_other.go +46 -0
- codecin/native/engine/audio_windows.go +43 -0
- codecin/native/engine/canvas.go +271 -0
- codecin/native/engine/crom.go +86 -0
- codecin/native/engine/encode.go +93 -0
- codecin/native/engine/engine_test.go +198 -0
- codecin/native/engine/isa_gen.go +445 -0
- codecin/native/engine/system.go +190 -0
- codecin/native/engine/termux.go +113 -0
- codecin/native/engine/version_gen.go +7 -0
- codecin/native/engine/vm.go +1157 -0
- codecin/native/go.mod +3 -0
- codecin/native/ir/ir.go +48 -0
- codecin/native/main.go +225 -0
- codecin/native.py +336 -0
- codecin/registers.py +130 -0
- codecin/stats.py +235 -0
- codecin-5.4.2.dist-info/METADATA +985 -0
- codecin-5.4.2.dist-info/RECORD +54 -0
- codecin-5.4.2.dist-info/WHEEL +5 -0
- codecin-5.4.2.dist-info/entry_points.txt +2 -0
- codecin-5.4.2.dist-info/licenses/LICENSE +7 -0
- codecin-5.4.2.dist-info/top_level.txt +1 -0
codecin/registers.py
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
from typing import Any, Dict, List, Optional
|
|
2
|
+
|
|
3
|
+
from .console import Console, Table
|
|
4
|
+
from .errors import ExecutionError
|
|
5
|
+
from .isa import Constants
|
|
6
|
+
|
|
7
|
+
MASK64 = 0xFFFFFFFFFFFFFFFF
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class RegisterFile:
|
|
11
|
+
def __init__(self, console: Optional[Console] = None,
|
|
12
|
+
num_regs: int = Constants.NUM_REGISTERS):
|
|
13
|
+
self.console = console or Console()
|
|
14
|
+
self.num_regs = num_regs
|
|
15
|
+
self._regs = [0] * num_regs
|
|
16
|
+
|
|
17
|
+
def read(self, idx: int) -> int:
|
|
18
|
+
if idx == 31:
|
|
19
|
+
return 0
|
|
20
|
+
if not 0 <= idx < self.num_regs:
|
|
21
|
+
raise ExecutionError(f"Invalid register index: X{idx}")
|
|
22
|
+
return self._regs[idx]
|
|
23
|
+
|
|
24
|
+
def write(self, idx: int, value: int, pc: int = 0) -> None:
|
|
25
|
+
if idx == 31:
|
|
26
|
+
return
|
|
27
|
+
if not 0 <= idx < self.num_regs:
|
|
28
|
+
raise ExecutionError(f"Invalid register index: X{idx}")
|
|
29
|
+
self._regs[idx] = value & MASK64
|
|
30
|
+
|
|
31
|
+
def get_all(self) -> List[int]:
|
|
32
|
+
return self._regs.copy()
|
|
33
|
+
|
|
34
|
+
def set_all(self, values: List[int]) -> None:
|
|
35
|
+
for i, v in enumerate(values[:self.num_regs]):
|
|
36
|
+
self._regs[i] = v & MASK64
|
|
37
|
+
|
|
38
|
+
def reset(self) -> None:
|
|
39
|
+
self._regs = [0] * self.num_regs
|
|
40
|
+
|
|
41
|
+
def display_registers(self, title: str = "Registers",
|
|
42
|
+
extra_info: Optional[Dict[str, Any]] = None,
|
|
43
|
+
console: Optional[Console] = None) -> None:
|
|
44
|
+
if console is None:
|
|
45
|
+
console = self.console
|
|
46
|
+
table = Table(title=title)
|
|
47
|
+
table.add_column("Register")
|
|
48
|
+
table.add_column("Value (Dec)")
|
|
49
|
+
table.add_column("Value (Hex)")
|
|
50
|
+
for i in range(16):
|
|
51
|
+
val = self._regs[i]
|
|
52
|
+
table.add_row(f"X{i}", str(val), f"0x{val:x}")
|
|
53
|
+
table.add_row("", "", "")
|
|
54
|
+
for i in range(16, 31):
|
|
55
|
+
val = self._regs[i]
|
|
56
|
+
table.add_row(f"X{i}", str(val), f"0x{val:x}")
|
|
57
|
+
table.add_row("XZR", "0", "0x0")
|
|
58
|
+
if extra_info:
|
|
59
|
+
for key, value in extra_info.items():
|
|
60
|
+
table.add_row(key, str(value),
|
|
61
|
+
f"0x{value:x}" if isinstance(value, int) else "")
|
|
62
|
+
console.print(str(table))
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class VectorRegisterFile:
|
|
66
|
+
|
|
67
|
+
def __init__(self, num_regs: int = Constants.NUM_VECTOR_REGISTERS,
|
|
68
|
+
lanes: int = Constants.VECTOR_LANES):
|
|
69
|
+
self.num_regs = num_regs
|
|
70
|
+
self.lanes = lanes
|
|
71
|
+
self._regs = [[0.0] * lanes for _ in range(num_regs)]
|
|
72
|
+
|
|
73
|
+
def read_vector(self, idx: int) -> List[float]:
|
|
74
|
+
if idx == 31:
|
|
75
|
+
return [0.0] * self.lanes
|
|
76
|
+
if not 0 <= idx < self.num_regs:
|
|
77
|
+
raise ExecutionError(f"Invalid vector register index: V{idx}")
|
|
78
|
+
return self._regs[idx].copy()
|
|
79
|
+
|
|
80
|
+
def write_vector(self, idx: int, values: List[float]) -> None:
|
|
81
|
+
if idx == 31:
|
|
82
|
+
return
|
|
83
|
+
if not 0 <= idx < self.num_regs:
|
|
84
|
+
raise ExecutionError(f"Invalid vector register index: V{idx}")
|
|
85
|
+
if len(values) != self.lanes:
|
|
86
|
+
raise ExecutionError(f"Need {self.lanes} values, got {len(values)}")
|
|
87
|
+
self._regs[idx] = list(values)
|
|
88
|
+
|
|
89
|
+
def read_scalar(self, idx: int, lane: int = 0) -> float:
|
|
90
|
+
if idx == 31:
|
|
91
|
+
return 0.0
|
|
92
|
+
if not 0 <= idx < self.num_regs:
|
|
93
|
+
raise ExecutionError(f"Invalid vector register index: V{idx}")
|
|
94
|
+
if not 0 <= lane < self.lanes:
|
|
95
|
+
raise ExecutionError(f"Invalid lane index: {lane}")
|
|
96
|
+
return self._regs[idx][lane]
|
|
97
|
+
|
|
98
|
+
def write_scalar(self, idx: int, value: float, lane: int = 0) -> None:
|
|
99
|
+
if idx == 31:
|
|
100
|
+
return
|
|
101
|
+
if not 0 <= idx < self.num_regs:
|
|
102
|
+
raise ExecutionError(f"Invalid vector register index: V{idx}")
|
|
103
|
+
if not 0 <= lane < self.lanes:
|
|
104
|
+
raise ExecutionError(f"Invalid lane index: {lane}")
|
|
105
|
+
self._regs[idx][lane] = float(value)
|
|
106
|
+
|
|
107
|
+
def get_all(self) -> List[List[float]]:
|
|
108
|
+
return [v.copy() for v in self._regs]
|
|
109
|
+
|
|
110
|
+
def set_all(self, values: List[List[float]]) -> None:
|
|
111
|
+
for i, v in enumerate(values[:self.num_regs]):
|
|
112
|
+
self._regs[i] = [float(x) for x in v[:self.lanes]]
|
|
113
|
+
|
|
114
|
+
def reset(self) -> None:
|
|
115
|
+
self._regs = [[0.0] * self.lanes for _ in range(self.num_regs)]
|
|
116
|
+
|
|
117
|
+
def display_vector_registers(self, title: str = "Vector Registers",
|
|
118
|
+
console: Optional[Console] = None) -> None:
|
|
119
|
+
if console is None:
|
|
120
|
+
return
|
|
121
|
+
table = Table(title=title)
|
|
122
|
+
table.add_column("Register")
|
|
123
|
+
for lane in range(self.lanes):
|
|
124
|
+
table.add_column(f"Lane {lane}")
|
|
125
|
+
for i in range(self.num_regs):
|
|
126
|
+
vec = self._regs[i]
|
|
127
|
+
if any(v != 0.0 for v in vec):
|
|
128
|
+
table.add_row(f"V{i}", *[f"{v:.4g}" for v in vec])
|
|
129
|
+
if table.rows:
|
|
130
|
+
console.print(str(table))
|
codecin/stats.py
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from collections import defaultdict
|
|
3
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
4
|
+
|
|
5
|
+
from .console import Console, Table
|
|
6
|
+
from .isa import Constants
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BranchPredictor:
|
|
10
|
+
|
|
11
|
+
def __init__(self):
|
|
12
|
+
self.saturating_counters: Dict[int, int] = {}
|
|
13
|
+
self.correct_predictions = 0
|
|
14
|
+
self.total_predictions = 0
|
|
15
|
+
|
|
16
|
+
def predict(self, pc: int) -> bool:
|
|
17
|
+
return self.saturating_counters.get(pc, 2) >= 2
|
|
18
|
+
|
|
19
|
+
def update(self, pc: int, taken: bool) -> None:
|
|
20
|
+
counter = self.saturating_counters.get(pc, 2)
|
|
21
|
+
counter = min(3, counter + 1) if taken else max(0, counter - 1)
|
|
22
|
+
self.saturating_counters[pc] = counter
|
|
23
|
+
|
|
24
|
+
def record_prediction(self, predicted: bool, actual: bool) -> None:
|
|
25
|
+
self.total_predictions += 1
|
|
26
|
+
if predicted == actual:
|
|
27
|
+
self.correct_predictions += 1
|
|
28
|
+
|
|
29
|
+
def get_accuracy(self) -> float:
|
|
30
|
+
if self.total_predictions == 0:
|
|
31
|
+
return 1.0
|
|
32
|
+
return self.correct_predictions / self.total_predictions
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class PerformanceCounters:
|
|
36
|
+
def __init__(self):
|
|
37
|
+
self.counters = {
|
|
38
|
+
'cycles': 0,
|
|
39
|
+
'instructions': 0,
|
|
40
|
+
'branches': 0,
|
|
41
|
+
'branch_mispredictions': 0,
|
|
42
|
+
'cache_hits': 0,
|
|
43
|
+
'cache_misses': 0,
|
|
44
|
+
'memory_reads': 0,
|
|
45
|
+
'memory_writes': 0,
|
|
46
|
+
'stalls': 0,
|
|
47
|
+
'flops': 0,
|
|
48
|
+
}
|
|
49
|
+
self.branch_predictor = BranchPredictor()
|
|
50
|
+
|
|
51
|
+
def record_instruction(self, opcode: str) -> None:
|
|
52
|
+
self.counters['instructions'] += 1
|
|
53
|
+
if opcode in Constants.BRANCH_OPS:
|
|
54
|
+
self.counters['branches'] += 1
|
|
55
|
+
if opcode in Constants.FP_OPS:
|
|
56
|
+
self.counters['flops'] += 1
|
|
57
|
+
|
|
58
|
+
def record_branch_result(self, pc: int, taken: bool, predicted: bool) -> None:
|
|
59
|
+
self.branch_predictor.record_prediction(predicted, taken)
|
|
60
|
+
if taken != predicted:
|
|
61
|
+
self.counters['branch_mispredictions'] += 1
|
|
62
|
+
self.branch_predictor.update(pc, taken)
|
|
63
|
+
|
|
64
|
+
def record_cache(self, hit: bool) -> None:
|
|
65
|
+
if hit:
|
|
66
|
+
self.counters['cache_hits'] += 1
|
|
67
|
+
else:
|
|
68
|
+
self.counters['cache_misses'] += 1
|
|
69
|
+
|
|
70
|
+
def record_memory_read(self) -> None:
|
|
71
|
+
self.counters['memory_reads'] += 1
|
|
72
|
+
|
|
73
|
+
def record_memory_write(self) -> None:
|
|
74
|
+
self.counters['memory_writes'] += 1
|
|
75
|
+
|
|
76
|
+
def add_cycles(self, n: int) -> None:
|
|
77
|
+
self.counters['cycles'] += n
|
|
78
|
+
|
|
79
|
+
def get_ipc(self) -> float:
|
|
80
|
+
if self.counters['cycles'] == 0:
|
|
81
|
+
return 0
|
|
82
|
+
return self.counters['instructions'] / self.counters['cycles']
|
|
83
|
+
|
|
84
|
+
def get_stats(self) -> Dict[str, Any]:
|
|
85
|
+
stats = self.counters.copy()
|
|
86
|
+
stats['ipc'] = self.get_ipc()
|
|
87
|
+
stats['branch_accuracy'] = self.branch_predictor.get_accuracy()
|
|
88
|
+
total_cache = stats['cache_hits'] + stats['cache_misses']
|
|
89
|
+
stats['cache_hit_rate'] = stats['cache_hits'] / total_cache if total_cache > 0 else 0
|
|
90
|
+
return stats
|
|
91
|
+
|
|
92
|
+
def display(self, console: Console) -> None:
|
|
93
|
+
stats = self.get_stats()
|
|
94
|
+
table = Table(title="Performance Counters")
|
|
95
|
+
table.add_column("Metric")
|
|
96
|
+
table.add_column("Value")
|
|
97
|
+
for key, value in stats.items():
|
|
98
|
+
if key in ('branch_accuracy', 'cache_hit_rate'):
|
|
99
|
+
table.add_row(key.replace('_', ' ').title(), f"{value * 100:.1f}%")
|
|
100
|
+
elif key == 'ipc':
|
|
101
|
+
table.add_row("Instructions Per Cycle", f"{value:.2f}")
|
|
102
|
+
else:
|
|
103
|
+
table.add_row(key.replace('_', ' ').title(), str(value))
|
|
104
|
+
console.print(str(table))
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class InstructionProfiler:
|
|
108
|
+
|
|
109
|
+
def __init__(self):
|
|
110
|
+
self.cycles: Dict[str, int] = defaultdict(int)
|
|
111
|
+
self.latency: Dict[str, int] = {
|
|
112
|
+
'ADD': 1, 'SUB': 1, 'MUL': 3, 'DIV': 10,
|
|
113
|
+
'LOAD': 4, 'STORE': 4, 'FADD': 3, 'FMUL': 5,
|
|
114
|
+
'FDIV': 10, 'VADD': 2, 'VMUL': 4, 'VDIV': 8,
|
|
115
|
+
'LSL': 1, 'LSR': 1, 'AND': 1, 'OR': 1, 'XOR': 1,
|
|
116
|
+
'LD': 4, 'SD': 4, 'LDR': 4, 'STR': 4,
|
|
117
|
+
'LB': 4, 'LH': 4, 'LW': 4, 'SB': 4, 'SH': 4, 'SW': 4,
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
def record(self, opcode: str) -> None:
|
|
121
|
+
self.cycles[opcode] += self.latency.get(opcode, 1)
|
|
122
|
+
|
|
123
|
+
def get_total_cycles(self) -> int:
|
|
124
|
+
return sum(self.cycles.values())
|
|
125
|
+
|
|
126
|
+
def display_report(self, console: Optional[Console] = None) -> None:
|
|
127
|
+
if console is None:
|
|
128
|
+
return
|
|
129
|
+
total = self.get_total_cycles()
|
|
130
|
+
table = Table(title="Instruction Cycle Profile")
|
|
131
|
+
table.add_column("Instruction")
|
|
132
|
+
table.add_column("Cycles")
|
|
133
|
+
table.add_column("Percentage")
|
|
134
|
+
for op, cycles in sorted(self.cycles.items(), key=lambda x: x[1], reverse=True)[:20]:
|
|
135
|
+
pct = (cycles / total * 100) if total > 0 else 0
|
|
136
|
+
table.add_row(op, str(cycles), f"{pct:.1f}%")
|
|
137
|
+
console.print(str(table))
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class Statistics:
|
|
141
|
+
def __init__(self):
|
|
142
|
+
self.reset()
|
|
143
|
+
|
|
144
|
+
def reset(self):
|
|
145
|
+
self.instruction_count = 0
|
|
146
|
+
self.opcode_count: Dict[str, int] = defaultdict(int)
|
|
147
|
+
self.execution_time = 0.0
|
|
148
|
+
self.start_time: Optional[float] = None
|
|
149
|
+
self.memory_reads = 0
|
|
150
|
+
self.memory_writes = 0
|
|
151
|
+
self.inst_profiler = InstructionProfiler()
|
|
152
|
+
self.hot_instructions: Dict[str, int] = defaultdict(int)
|
|
153
|
+
self.performance_counters = PerformanceCounters()
|
|
154
|
+
|
|
155
|
+
def start(self):
|
|
156
|
+
self.start_time = time.time()
|
|
157
|
+
|
|
158
|
+
def stop(self):
|
|
159
|
+
if self.start_time:
|
|
160
|
+
self.execution_time = time.time() - self.start_time
|
|
161
|
+
|
|
162
|
+
def record_instruction(self, opcode: str):
|
|
163
|
+
self.instruction_count += 1
|
|
164
|
+
self.opcode_count[opcode] += 1
|
|
165
|
+
self.inst_profiler.record(opcode)
|
|
166
|
+
self.hot_instructions[opcode] += 1
|
|
167
|
+
self.performance_counters.record_instruction(opcode)
|
|
168
|
+
|
|
169
|
+
def record_memory_read(self):
|
|
170
|
+
self.memory_reads += 1
|
|
171
|
+
self.performance_counters.record_memory_read()
|
|
172
|
+
|
|
173
|
+
def record_memory_write(self):
|
|
174
|
+
self.memory_writes += 1
|
|
175
|
+
self.performance_counters.record_memory_write()
|
|
176
|
+
|
|
177
|
+
def record_cache(self, hit: bool):
|
|
178
|
+
self.performance_counters.record_cache(hit)
|
|
179
|
+
|
|
180
|
+
def record_branch(self, pc: int, taken: bool, predicted: bool):
|
|
181
|
+
self.performance_counters.record_branch_result(pc, taken, predicted)
|
|
182
|
+
|
|
183
|
+
def get_hot_instructions(self, top_n: int = 10) -> List[Tuple[str, int]]:
|
|
184
|
+
return sorted(self.hot_instructions.items(),
|
|
185
|
+
key=lambda x: x[1], reverse=True)[:top_n]
|
|
186
|
+
|
|
187
|
+
def display_summary(self, console: Optional[Console] = None,
|
|
188
|
+
cache_stats: Optional[Dict] = None,
|
|
189
|
+
jit_stats: Optional[Dict] = None,
|
|
190
|
+
native_used: bool = False) -> None:
|
|
191
|
+
if console is None:
|
|
192
|
+
return
|
|
193
|
+
|
|
194
|
+
table = Table(title="Execution Statistics")
|
|
195
|
+
table.add_column("Metric")
|
|
196
|
+
table.add_column("Value")
|
|
197
|
+
table.add_row("Engine", "Go native" if native_used else "Python interpreter")
|
|
198
|
+
table.add_row("Total Instructions", str(self.instruction_count))
|
|
199
|
+
table.add_row("Total Cycles", str(self.inst_profiler.get_total_cycles()))
|
|
200
|
+
if self.instruction_count > 0:
|
|
201
|
+
table.add_row("CPI (Cycles/Inst)",
|
|
202
|
+
f"{self.inst_profiler.get_total_cycles() / self.instruction_count:.2f}")
|
|
203
|
+
table.add_row("Execution Time", f"{self.execution_time:.4f}s")
|
|
204
|
+
if self.execution_time > 0:
|
|
205
|
+
table.add_row("Instructions/sec",
|
|
206
|
+
f"{self.instruction_count / self.execution_time:,.0f}")
|
|
207
|
+
table.add_row("Memory Reads", str(self.memory_reads))
|
|
208
|
+
table.add_row("Memory Writes", str(self.memory_writes))
|
|
209
|
+
|
|
210
|
+
perf_stats = self.performance_counters.get_stats()
|
|
211
|
+
table.add_row("Branch Accuracy", f"{perf_stats['branch_accuracy'] * 100:.1f}%")
|
|
212
|
+
|
|
213
|
+
if cache_stats:
|
|
214
|
+
table.add_row("Cache Hit Rate", f"{cache_stats.get('hit_rate', 0) * 100:.1f}%")
|
|
215
|
+
|
|
216
|
+
if jit_stats:
|
|
217
|
+
table.add_row("JIT Hit Rate", f"{jit_stats.get('hit_rate', 0) * 100:.1f}%")
|
|
218
|
+
table.add_row("JIT Blocks Compiled", str(jit_stats.get('blocks_compiled', 0)))
|
|
219
|
+
|
|
220
|
+
console.print(str(table))
|
|
221
|
+
|
|
222
|
+
if self.opcode_count:
|
|
223
|
+
op_table = Table(title="Instruction Usage")
|
|
224
|
+
op_table.add_column("Instruction")
|
|
225
|
+
op_table.add_column("Count")
|
|
226
|
+
op_table.add_column("Percentage")
|
|
227
|
+
total = sum(self.opcode_count.values())
|
|
228
|
+
for op, count in sorted(self.opcode_count.items(),
|
|
229
|
+
key=lambda x: x[1], reverse=True)[:20]:
|
|
230
|
+
pct = (count / total * 100) if total > 0 else 0
|
|
231
|
+
op_table.add_row(op, str(count), f"{pct:.1f}%")
|
|
232
|
+
console.print(str(op_table))
|
|
233
|
+
|
|
234
|
+
self.inst_profiler.display_report(console)
|
|
235
|
+
self.performance_counters.display(console)
|