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.
Files changed (54) hide show
  1. codecin/__init__.py +27 -0
  2. codecin/aot.py +229 -0
  3. codecin/assembler.py +548 -0
  4. codecin/cache.py +71 -0
  5. codecin/cin.py +2558 -0
  6. codecin/cli.py +282 -0
  7. codecin/config.py +58 -0
  8. codecin/console.py +183 -0
  9. codecin/cpu.py +1654 -0
  10. codecin/crom.py +222 -0
  11. codecin/debugger.py +659 -0
  12. codecin/disasm.py +86 -0
  13. codecin/errors.py +58 -0
  14. codecin/isa.py +457 -0
  15. codecin/jit.py +270 -0
  16. codecin/libcodecin_native.so +0 -0
  17. codecin/logger.py +145 -0
  18. codecin/memory.py +348 -0
  19. codecin/native/aot/aot.go +70 -0
  20. codecin/native/aot/build.go +219 -0
  21. codecin/native/aot/stub_main.go.txt +22 -0
  22. codecin/native/build.ps1 +33 -0
  23. codecin/native/build.sh +48 -0
  24. codecin/native/cmd/codecin/main.go +287 -0
  25. codecin/native/compiler/codegen.go +1938 -0
  26. codecin/native/compiler/compile_test.go +164 -0
  27. codecin/native/compiler/parser.go +1071 -0
  28. codecin/native/compiler/syscalls.go +86 -0
  29. codecin/native/compiler/tokenizer.go +336 -0
  30. codecin/native/compiler/types.go +213 -0
  31. codecin/native/engine/audio.go +152 -0
  32. codecin/native/engine/audio_other.go +46 -0
  33. codecin/native/engine/audio_windows.go +43 -0
  34. codecin/native/engine/canvas.go +271 -0
  35. codecin/native/engine/crom.go +86 -0
  36. codecin/native/engine/encode.go +93 -0
  37. codecin/native/engine/engine_test.go +198 -0
  38. codecin/native/engine/isa_gen.go +445 -0
  39. codecin/native/engine/system.go +190 -0
  40. codecin/native/engine/termux.go +113 -0
  41. codecin/native/engine/version_gen.go +7 -0
  42. codecin/native/engine/vm.go +1157 -0
  43. codecin/native/go.mod +3 -0
  44. codecin/native/ir/ir.go +48 -0
  45. codecin/native/main.go +225 -0
  46. codecin/native.py +336 -0
  47. codecin/registers.py +130 -0
  48. codecin/stats.py +235 -0
  49. codecin-5.4.2.dist-info/METADATA +985 -0
  50. codecin-5.4.2.dist-info/RECORD +54 -0
  51. codecin-5.4.2.dist-info/WHEEL +5 -0
  52. codecin-5.4.2.dist-info/entry_points.txt +2 -0
  53. codecin-5.4.2.dist-info/licenses/LICENSE +7 -0
  54. codecin-5.4.2.dist-info/top_level.txt +1 -0
codecin/jit.py ADDED
@@ -0,0 +1,270 @@
1
+ """Python 级 JIT: 将无分支基本块动态编译为 Python 函数。
2
+
3
+ 原生 Go 库不可用且显式 --jit 时使用。编译块仅包含直线指令
4
+ (数据传输/算术/逻辑/加载存储/栈), 遇到控制流指令即结束块。
5
+ """
6
+
7
+ from typing import Any, Callable, Dict, List, Optional, Tuple
8
+
9
+ from .isa import Constants
10
+
11
+ MASK64 = 0xFFFFFFFFFFFFFFFF
12
+
13
+ # 可安全编译进直线块的指令
14
+ _JIT_OPS = {
15
+ 'MOV', 'ADD', 'SUB', 'MUL', 'DIV', 'AND', 'OR', 'XOR', 'SHL', 'SHR',
16
+ 'INC', 'DEC', 'ADDI', 'XORI', 'ORI', 'ANDI', 'LSL', 'LSR',
17
+ 'LDR', 'STR', 'LOAD', 'STORE', 'LB', 'LH', 'LW', 'LD', 'SB', 'SH', 'SW', 'SD',
18
+ 'PUSH', 'POP', 'NOP', 'MVN',
19
+ }
20
+
21
+ _BRANCH_OPS = Constants.BRANCH_OPS | {'HALT', 'SYS', 'IN', 'OUT'}
22
+
23
+
24
+ class JITCompiler:
25
+ def __init__(self, logger=None):
26
+ self.logger = logger
27
+ self.blocks: Dict[int, Callable] = {}
28
+ self.block_ranges: Dict[int, Tuple[int, int]] = {}
29
+ self.compilation_count = 0
30
+ self.total_calls = 0
31
+ self.cache_hits = 0
32
+
33
+ def _dbg(self, msg: str) -> None:
34
+ if self.logger is not None:
35
+ self.logger.debug(msg)
36
+
37
+ # ---------------- 代码生成 ----------------
38
+
39
+ def _reg_read(self, n: int) -> str:
40
+ if n == 31:
41
+ return '0'
42
+ if n == Constants.SP_REG:
43
+ return 'cpu.sp'
44
+ return f'R[{n}]'
45
+
46
+ def _reg_write(self, n: int, expr: str) -> str:
47
+ if n == 31:
48
+ return ''
49
+ if n == Constants.SP_REG:
50
+ return f'cpu.sp = ({expr}) & MASK'
51
+ return f'R[{n}] = ({expr}) & MASK'
52
+
53
+ def _val(self, op: Tuple) -> Optional[str]:
54
+ kind = op[0]
55
+ if kind == 'reg':
56
+ return self._reg_read(op[1])
57
+ if kind == 'imm':
58
+ return str(op[1] & MASK64)
59
+ if kind == 'mem':
60
+ return None # 内存作为值操作不编译
61
+ return None
62
+
63
+ def _mem_addr(self, op: Tuple) -> Optional[str]:
64
+ if op[0] != 'mem':
65
+ return None
66
+ base, off = op[1], op[2]
67
+ if base >= 0:
68
+ return f'({self._reg_read(base)} + {off})'
69
+ return str(off)
70
+
71
+ def _gen(self, opcode: str, args: List[Tuple]) -> Optional[List[str]]:
72
+ try:
73
+ if opcode == 'MOV':
74
+ val = self._val(args[1])
75
+ return [self._reg_write(args[0][1], val)] if val is not None else None
76
+ if opcode in ('ADD', 'SUB', 'MUL', 'AND', 'OR', 'XOR'):
77
+ sym = {'ADD': '+', 'SUB': '-', 'MUL': '*', 'AND': '&',
78
+ 'OR': '|', 'XOR': '^'}[opcode]
79
+ val = self._val(args[1])
80
+ rd = args[0][1]
81
+ return [self._reg_write(rd, f'{self._reg_read(rd)} {sym} {val}')] if val else None
82
+ if opcode in ('SHL', 'SHR'):
83
+ sym = '<<' if opcode == 'SHL' else '>>'
84
+ val = self._val(args[1])
85
+ rd = args[0][1]
86
+ if val is None:
87
+ return None
88
+ if opcode == 'SHR':
89
+ return [f'_v = ({self._reg_read(rd)} & MASK) >> (({val}) & 63)',
90
+ self._reg_write(rd, '_v')]
91
+ return [self._reg_write(rd, f'{self._reg_read(rd)} {sym} (({val}) & 63)')]
92
+ if opcode == 'DIV':
93
+ rd = args[0][1]
94
+ val = self._val(args[1])
95
+ if val is None:
96
+ return None
97
+ # 有符号除法, 向零截断 (与解释器一致)
98
+ return [f'_d = {val}',
99
+ 'if _d == 0: raise ExecutionError("Division by zero")',
100
+ '_n = ' + self._reg_read(rd),
101
+ '_sn = _n if _n < (1 << 63) else _n - (1 << 64)',
102
+ '_sd = _d if _d < (1 << 63) else _d - (1 << 64)',
103
+ '_q = abs(_sn) // abs(_sd)',
104
+ '_q = _q if (_sn < 0) == (_sd < 0) else -_q',
105
+ self._reg_write(rd, '_q')]
106
+ if opcode == 'INC':
107
+ rd = args[0][1]
108
+ return [self._reg_write(rd, f'{self._reg_read(rd)} + 1')]
109
+ if opcode == 'DEC':
110
+ rd = args[0][1]
111
+ return [self._reg_write(rd, f'{self._reg_read(rd)} - 1')]
112
+ if opcode == 'MVN':
113
+ val = self._val(args[1])
114
+ return [self._reg_write(args[0][1], f'~({val})')] if val else None
115
+ if opcode in ('ADDI',):
116
+ rd, rs = args[0][1], args[1][1]
117
+ val = self._val(args[2])
118
+ return [self._reg_write(rd, f'{self._reg_read(rs)} + {val}')] if val else None
119
+ if opcode in ('XORI', 'ORI', 'ANDI'):
120
+ sym = {'XORI': '^', 'ORI': '|', 'ANDI': '&'}[opcode]
121
+ rd, rs = args[0][1], args[1][1]
122
+ val = self._val(args[2])
123
+ return [self._reg_write(rd, f'{self._reg_read(rs)} {sym} {val}')] if val else None
124
+ if opcode in ('LSL', 'LSR'):
125
+ rd, rn = args[0][1], args[1][1]
126
+ val = self._val(args[2])
127
+ if val is None:
128
+ return None
129
+ if opcode == 'LSL':
130
+ return [self._reg_write(rd, f'{self._reg_read(rn)} << (({val}) & 63)')]
131
+ return [f'_v = ({self._reg_read(rn)} & MASK) >> (({val}) & 63)',
132
+ self._reg_write(rd, '_v')]
133
+ if opcode in ('LDR', 'LOAD', 'LW'):
134
+ addr = self._mem_addr(args[1]) if args[1][0] == 'mem' else self._val(args[1])
135
+ rd = args[0][1]
136
+ if addr is None:
137
+ return None
138
+ return [self._reg_write(rd, f'mem.read_dword({addr})')]
139
+ if opcode in ('STR', 'STORE', 'SW'):
140
+ addr = self._mem_addr(args[1]) if args[1][0] == 'mem' else self._val(args[1])
141
+ rs = args[0][1]
142
+ if addr is None:
143
+ return None
144
+ return [f'mem.write_dword({addr}, {self._reg_read(rs)} & 0xFFFFFFFF)']
145
+ if opcode == 'LD':
146
+ addr = self._mem_addr(args[1]) if args[1][0] == 'mem' else self._val(args[1])
147
+ return [self._reg_write(args[0][1], f'mem.read_qword({addr})')] if addr else None
148
+ if opcode == 'SD':
149
+ addr = self._mem_addr(args[1]) if args[1][0] == 'mem' else self._val(args[1])
150
+ return [f'mem.write_qword({addr}, {self._reg_read(args[0][1])})'] if addr else None
151
+ if opcode in ('LB', 'LH'):
152
+ width = 1 if opcode == 'LB' else 2
153
+ fn = 'read_byte' if width == 1 else 'read_word'
154
+ addr = self._mem_addr(args[1]) if args[1][0] == 'mem' else self._val(args[1])
155
+ if addr is None:
156
+ return None
157
+ bits = width * 8
158
+ return [f'_v = mem.{fn}({addr})',
159
+ f'if _v & {1 << (bits - 1)}: _v -= {1 << bits}',
160
+ self._reg_write(args[0][1], '_v')]
161
+ if opcode in ('SB', 'SH'):
162
+ mask = '0xFF' if opcode == 'SB' else '0xFFFF'
163
+ fn = 'write_byte' if opcode == 'SB' else 'write_word'
164
+ addr = self._mem_addr(args[1]) if args[1][0] == 'mem' else self._val(args[1])
165
+ if addr is None:
166
+ return None
167
+ return [f'mem.{fn}({addr}, {self._reg_read(args[0][1])} & {mask})']
168
+ if opcode == 'PUSH':
169
+ val = self._val(args[0])
170
+ if val is None:
171
+ return None
172
+ return ['cpu.sp = (cpu.sp - 8) & MASK',
173
+ f'mem.write_qword(cpu.sp, ({val}) & MASK)']
174
+ if opcode == 'POP':
175
+ rd = args[0][1]
176
+ return ['_v = mem.read_qword(cpu.sp)',
177
+ 'cpu.sp = (cpu.sp + 8) & MASK',
178
+ self._reg_write(rd, '_v')]
179
+ if opcode == 'NOP':
180
+ return ['pass']
181
+ except Exception:
182
+ return None
183
+ return None
184
+
185
+ def compile_block(self, cpu) -> Optional[int]:
186
+ """编译 cpu.pc 处开始的基本块, 返回块结束 PC。"""
187
+ start = cpu.pc
188
+ if start in self.blocks:
189
+ self.cache_hits += 1
190
+ if self.logger is not None and self.logger.is_debug:
191
+ self._dbg(f"JIT cache hit: block @0x{start:x} "
192
+ f"(hits={self.cache_hits})")
193
+ return self.block_ranges[start][1]
194
+
195
+ end = start
196
+ body: List[str] = []
197
+ for i in range(start, min(start + 32, len(cpu.instructions))):
198
+ opcode, args = cpu.instructions[i]
199
+ if opcode in _BRANCH_OPS or opcode not in _JIT_OPS:
200
+ break
201
+ lines = self._gen(opcode, args)
202
+ if lines is None:
203
+ break
204
+ body.extend(f' {ln}' for ln in lines if ln)
205
+ end = i + 1
206
+
207
+ if end <= start:
208
+ return None
209
+
210
+ source = (
211
+ "def block(cpu, mem, R):\n"
212
+ " MASK = 0xFFFFFFFFFFFFFFFF\n"
213
+ + ("\n".join(body) if body else " pass") + "\n"
214
+ " return None\n"
215
+ )
216
+ try:
217
+ from .errors import ExecutionError
218
+ namespace: Dict[str, Any] = {'ExecutionError': ExecutionError}
219
+ exec(compile(source, '<jit-block>', 'exec'), namespace)
220
+ func = namespace['block']
221
+ except Exception as e:
222
+ cpu.logger.debug(f"JIT block compile failed at {start}: {e}")
223
+ return None
224
+
225
+ self.blocks[start] = func
226
+ self.block_ranges[start] = (start, end)
227
+ self.compilation_count += 1
228
+ self._dbg(f"JIT compiled block 0x{start:x}-0x{end:x} "
229
+ f"({end - start} instrs, total {self.compilation_count})")
230
+ if self.logger is not None and self.logger.is_debug:
231
+ self._dbg('JIT block source:\n' + source.rstrip())
232
+ return end
233
+
234
+ def try_step(self, cpu) -> Optional[bool]:
235
+ """尝试以 JIT 块执行; 不适合 JIT 时返回 None。"""
236
+ self.total_calls += 1
237
+ start = cpu.pc
238
+ if start < 0 or start >= len(cpu.instructions):
239
+ return False
240
+ opcode, _ = cpu.instructions[start]
241
+ if opcode in _BRANCH_OPS or opcode not in _JIT_OPS:
242
+ return None
243
+
244
+ end = self.compile_block(cpu)
245
+ if end is None:
246
+ return None
247
+
248
+ func = self.blocks.get(start)
249
+ if func is None:
250
+ return None
251
+ try:
252
+ func(cpu, cpu.memory, cpu.regs._regs)
253
+ except Exception as e:
254
+ cpu.logger.debug(f"JIT execution failed: {e}")
255
+ self.blocks.pop(start, None)
256
+ return None
257
+
258
+ for i in range(start, end):
259
+ cpu.stats.record_instruction(cpu.instructions[i][0])
260
+ cpu.pc = end
261
+ return True
262
+
263
+ def get_stats(self) -> Dict[str, Any]:
264
+ return {
265
+ 'blocks_compiled': self.compilation_count,
266
+ 'cached_blocks': len(self.blocks),
267
+ 'total_calls': self.total_calls,
268
+ 'cache_hits': self.cache_hits,
269
+ 'hit_rate': self.cache_hits / self.total_calls if self.total_calls else 0.0,
270
+ }
Binary file
codecin/logger.py ADDED
@@ -0,0 +1,145 @@
1
+ import io
2
+ import logging
3
+ import os
4
+ from typing import Any, Dict, Optional
5
+
6
+ from rich.logging import RichHandler
7
+ from rich.table import Table as RichTable
8
+
9
+ from .console import Console
10
+
11
+ _FORMAT = '%(message)s'
12
+
13
+
14
+ def _render_plain(renderable) -> str:
15
+ """将 rich 渲染对象渲染为纯文本 (用于日志消息)。"""
16
+ buf = io.StringIO()
17
+ from rich.console import Console as RC
18
+ rc = RC(file=buf, highlight=False, emoji=False, markup=False, width=120)
19
+ rc.print(renderable)
20
+ return buf.getvalue().rstrip('\n')
21
+
22
+
23
+ class Logger:
24
+
25
+ LEVELS = {'DEBUG': 0, 'INFO': 1, 'WARNING': 2, 'ERROR': 3, 'CRITICAL': 4}
26
+
27
+ def __init__(self, console: Optional[Console] = None, level: str = 'INFO'):
28
+ self.console = console or Console()
29
+ self.level = self._parse_level(level)
30
+
31
+ self._log = logging.getLogger('codecin')
32
+ self._log.propagate = False
33
+ self._log.setLevel(logging.DEBUG) # handler 负责过滤
34
+ # 本类独占 'codecin' logger, 避免重复 handler
35
+ for h in list(self._log.handlers):
36
+ self._log.removeHandler(h)
37
+
38
+ self._handler = RichHandler(
39
+ console=self.console.rich,
40
+ show_time=True,
41
+ show_level=True,
42
+ show_path=False,
43
+ rich_tracebacks=True,
44
+ tracebacks_show_locals=False,
45
+ markup=False,
46
+ log_time_format='%H:%M:%S',
47
+ )
48
+ self._handler.setFormatter(logging.Formatter(_FORMAT))
49
+ self._handler.setLevel(self._to_logging_level(self.level))
50
+ self._log.addHandler(self._handler)
51
+
52
+ self._file_handler: Optional[logging.FileHandler] = None
53
+ self.log_file: Optional[str] = None
54
+
55
+ # ---------------- 级别控制 ----------------
56
+
57
+ @staticmethod
58
+ def _to_logging_level(codecin_level: int) -> int:
59
+ # codecin: DEBUG=0..CRITICAL=4 -> logging: 10..50
60
+ return (codecin_level + 1) * 10
61
+
62
+ def _parse_level(self, level: str) -> int:
63
+ return self.LEVELS.get(str(level).upper(), 1)
64
+
65
+ def set_level(self, level: str) -> None:
66
+ self.level = self._parse_level(level)
67
+ self._handler.setLevel(self._to_logging_level(self.level))
68
+
69
+ @property
70
+ def is_debug(self) -> bool:
71
+ return self.level <= self.LEVELS['DEBUG']
72
+
73
+ def set_log_file(self, filename: str) -> None:
74
+ self.log_file = filename
75
+ if self._file_handler is not None:
76
+ self._log.removeHandler(self._file_handler)
77
+ self._file_handler.close()
78
+ os.makedirs(os.path.dirname(os.path.abspath(filename)) or '.',
79
+ exist_ok=True)
80
+ fh = logging.FileHandler(filename, mode='w', encoding='utf-8')
81
+ fh.setFormatter(logging.Formatter(
82
+ '[%(asctime)s] [%(levelname)s] %(message)s', datefmt='%H:%M:%S'))
83
+ fh.setLevel(logging.DEBUG) # 文件记录全量
84
+ self._log.addHandler(fh)
85
+ self._file_handler = fh
86
+
87
+ # ---------------- 标准级别 ----------------
88
+
89
+ def debug(self, msg: str) -> None:
90
+ self._log.debug(msg)
91
+
92
+ def info(self, msg: str) -> None:
93
+ self._log.info(msg)
94
+
95
+ def warning(self, msg: str) -> None:
96
+ self._log.warning(msg)
97
+
98
+ def error(self, msg: str, exc_info: Any = None) -> None:
99
+ self._log.error(msg, exc_info=exc_info)
100
+
101
+ def critical(self, msg: str, exc_info: Any = None) -> None:
102
+ self._log.critical(msg, exc_info=exc_info)
103
+
104
+ def exception(self, msg: str) -> None:
105
+ """rich 彩色堆栈回溯输出。"""
106
+ self._log.error(msg, exc_info=True)
107
+
108
+ # ---------------- 超详细调试辅助 ----------------
109
+
110
+ def trace(self, msg: str) -> None:
111
+ """逐指令级追踪 (仅 DEBUG)。"""
112
+ self._log.debug(msg)
113
+
114
+ def dump(self, title: str, fields: Dict[str, Any]) -> None:
115
+ """以表格输出调试快照 (仅 DEBUG)。"""
116
+ if not self.is_debug:
117
+ return
118
+ t = RichTable(title=title, show_header=False, border_style='dim',
119
+ padding=(0, 1), highlight=False)
120
+ t.add_column(style='bold cyan', no_wrap=True)
121
+ t.add_column()
122
+ for k, v in fields.items():
123
+ t.add_row(str(k), str(v))
124
+ self._log.debug(_render_plain(t))
125
+
126
+ def hexdump(self, title: str, addr: int, data: bytes,
127
+ width: int = 16) -> None:
128
+ """内存十六进制转储 (仅 DEBUG)。"""
129
+ if not self.is_debug:
130
+ return
131
+ lines = []
132
+ for off in range(0, min(len(data), 256), width):
133
+ chunk = data[off:off + width]
134
+ hexs = ' '.join(f'{b:02x}' for b in chunk)
135
+ asc = ''.join(chr(b) if 32 <= b < 127 else '.' for b in chunk)
136
+ lines.append(f' {addr + off:06x} {hexs:<{width * 3}} {asc}')
137
+ if len(data) > 256:
138
+ lines.append(f' ... ({len(data)} bytes total)')
139
+ self._log.debug(f'{title}\n' + '\n'.join(lines))
140
+
141
+ def close(self) -> None:
142
+ if self._file_handler is not None:
143
+ self._log.removeHandler(self._file_handler)
144
+ self._file_handler.close()
145
+ self._file_handler = None