Skip to content

Python 嵌入 API ​

Code CIN 除了命令行工具, 也可以直接当作 Python 库嵌入到自己的程序里: 构造一个 Config, 交给 CPU, 调用 run() 一次性执行。v5.9.0 起默认路径是 native-only: 整份字节码一次调用交给 Go 原生引擎 (codecin_run_v2), 不再有逐条解释 / JIT / 单步调试路径。v7.22.10 另有 --native 调用入口 (cincc) 直出机器码。 整条工具链 (汇编器、CIN 编译器、Go 原生 VM 桥接、.sdgcc/CROM 读写、AOT、 cincc、反汇编、统计) 都是公开可调用的。

本页所有签名均以当前源码为准: 包导出见 codecin/__init__.py, 配置字段见 codecin/config.py, 运行时见 codecin/cpu.py。

顶层导出 ​

from codecin import ... 可以拿到下面这些名字 (即 codecin.__all__, 见 codecin/__init__.py):

名称类型说明
CPUclassCPU 核心: 加载程序、一次性原生执行、状态查看
Configdataclass运行配置, 全部字段见下表
Opcodeenum.Enum112 条指令的枚举, 值即 UCBC 编码
Constantsclass常量集合: 寄存器数/OPCODE_NAMES/ARG_COUNTS/OPCODE_NAME_TO_ENUM/PL_KEYWORDS 等
Syscallenum.IntEnumSYS 功能号 0–157 (含 FFI 140–144 与网络 145–157)
CPUSimulatorErrorexception所有 Code CIN 错误的基类
AssemblerErrorexception汇编阶段 (.pl / .asm) 错误
CompilerErrorexceptionCIN 编译阶段错误
ExecutionErrorexception指令执行阶段错误
MemoryAccessErrorexception内存越界 / 保护违例
__version__str版本号单一真源 ("x.y.z")
python
import codecin
from codecin import CPU, Config, Opcode, Constants, Syscall

print(codecin.__version__)            # 打印版本号
print(len(list(Opcode)))              # 112
print(Constants.OPCODE_NAMES[Opcode.SYS])   # 'SYS'
print(Syscall.ALLOCFRAME)             # 137

其余子系统需要按模块导入 (它们都是包的公开模块, 只是不在顶层短名单里):

python
from codecin import (native, aot, crom, disasm, stats, memory, registers,
                     version)
from codecin.cin import CINCompiler
from codecin.assembler import Assembler

Config 全字段 ​

Config 是一个 @dataclass (codecin/config.py), 所有字段都有默认值, 因此 Config() 就是"命令行什么都不传"的行为。CPU 不会替你调用 Config.validate() —— 需要夹取取值范围时自己调一次。

字段类型默认值含义
mem_sizeint1 << 30逻辑内存字节数 (默认 1 GiB, 稀疏分页按需提交); validate() 夹到 [256, 1 TiB]
auto_save_cromboolFalserun() 结束后自动保存同名 .crom 镜像
max_instructionsint100_000_000指令数上限, 超出按失败处理; < 1 抬到 1
sandbox_modeboolFalse沙箱模式: 只放行 ALLOCFRAME / TIMEUS / TIMENS 等核心系统调用
log_levelstr'INFO'DEBUG / INFO / WARNING / ERROR
log_fileOptional[str]None日志文件路径 (非空则同时写入文件)
strict_modeboolFalse汇编器严格模式
output_fileOptional[str]None.bin / .crom 输出路径
optimizeint0优化级别 0–3 (夹取)
compress_cromboolTrue.crom 是否 zlib 压缩
compile_to_binboolFalse编译为 .bin 后继续执行
compile_onlyboolFalse只编译不执行
seedOptional[int]NoneSYS RAND 确定性种子 (None / 0 = 随机)
bounds_checkboolFalseCIN 定长数组越界检查 (编译期注入, 与原生引擎兼容)
program_argsList[str][]传给 CIN 程序的参数 (arg_count() / arg(i) 的数据源)

Config.validate() 会把越界值夹回合法区间: mem_size < 256 → 256、 mem_size > 1 TiB → 1 TiB、max_instructions < 1 → 1、optimize 夹到 0..3。

python
from codecin import CPU, Config

cfg = Config(
    max_instructions=5_000_000,
    seed=1234,                 # 确定性 rand()
    log_level='ERROR',
    program_args=['--input', 'data.txt'],
)
cfg.validate()
cpu = CPU(cfg, 'program.cin')
cpu.run()
print('failed =', cpu.execution_failed)

嵌入式场景的注意事项

建议 log_level='ERROR', 避免 rich 表格污染你自己的 stdout。需要捕获程序输出时, 把 cpu._capture_output 置 True, 结果会收进 cpu.output_buffer (见下)。 默认 1 GiB 内存是稀疏分页的, 不必为嵌入式场景刻意调小 mem_size。

CPU 构造与方法 ​

python
CPU(config: Config,
    filename: Optional[str] = None,
    crom_file: Optional[str] = None,
    from_bin: bool = False,
    console: Optional[Console] = None)
参数说明
config运行配置, 必填
filename程序路径。按扩展名分派: .cin → CIN 编译器, .bin / from_bin=True → UCBC 字节码, .pl / .asm → 汇编器
crom_file汇编路径下要预加载的 .crom 内存镜像; 传 None 时会尝试同名 .crom
from_bin强制按 .bin 解释 filename (扩展名不是 .bin 时也有用)
console自定义 codecin.console.Console; 默认新建一个

构造时如果 filename 非空, 会立即调用 load_program() 完成加载。也可以在 filename=None 构造空 CPU, 然后手工灌入指令 (测试辅助 tests/helpers.py: new_cpu() 就是这么做的)。

方法 ​

v5.9.0 起执行是一次性的: run() 把整份字节码交给 Go 原生引擎, 结束后把 寄存器 / 标志位 / 指针 / 脏内存段同步回 Python 侧对象。不再提供逐条 step() / execute()、断点与交互调试会话。

方法说明
load_program(filename, crom_file=None, from_bin=False)重新加载程序; 文件不存在抛 CPUSimulatorError
run()装载好的程序一次性交给原生引擎执行; 内部捕获异常并置 execution_failed
display_state(title='CPU State', ...)渲染寄存器/内存/栈状态面板

实例状态 (嵌入式最常读的几个) ​

属性说明
pc / sp / heap_ptr / heap_base程序计数器 / 栈指针 / 堆指针 / 堆基址
regsRegisterFile, 读 cpu.regs.read(n), 全量 cpu.regs.get_all()
vec_regsVectorRegisterFile, read_vector(n) / get_all()
pstate{'N': bool, 'Z': bool, 'C': bool, 'V': bool}
memoryFastMemory 实例 (4 KiB 稀疏分页, resident_bytes 给出常驻字节数)
statsStatistics 实例, 见下文
instructions / labels / data_labels / entry_pc已加载的程序与符号表
input_buffer / _input_pos程序读入内容的缓冲 (read() 内建的数据源)
output_buffer / _capture_output输出重定向缓冲 (见下)
native_engine / native_used原生引擎实例与本次执行是否真的走了 Go 原生 VM
execution_failed执行期是否发生过错误 (CLI 用它决定退出码)
python
from codecin import CPU, Config

cpu = CPU(Config(log_level='ERROR'), 'examples/control_flow.cin')
cpu._capture_output = True          # 把 OUT / SYS 打印收进 output_buffer
cpu.run()
text = ''.join(cpu.output_buffer)
print(text)
print('pc=0x%x sp=0x%x failed=%s native=%s'
      % (cpu.pc, cpu.sp, cpu.execution_failed, cpu.native_used))

run() 不抛异常

CPU.run() 内部 try/except 掉所有 Exception, 写日志并置 execution_failed = True。判断成功要看 cpu.execution_failed, 而不是等异常。 真的需要异常请直接调用底层模块 (见下文 codecin.native) —— 原生引擎返回 错误状态时, 桥接层会抛 ExecutionError。

原生库缺失时没有回退

v5.9.0 起 native-only: 找不到/加载不了 Go 原生库时, run() 直接报错并给出 重建提示 (NATIVE_HINT), 不会回退到 Python 解释器。构建方法见 Go 原生运行时。

异常层次 ​

Exception
└── CPUSimulatorError                 # 基类: message + detail
    ├── AssemblerError                # 附加 line / line_num / filename
    ├── CompilerError                 # 附加 line_num / filename
    ├── ExecutionError                # 除零、未实现指令、栈溢出、SYS 未知号
    └── MemoryAccessError             # 越界 / 保护违例
异常触发条件
CPUSimulatorError文件不存在、.crom 头非法、版本或校验和不符、字节码 magic 错误
AssemblerError汇编语法错误、未定义标签/符号、操作数个数不符; 消息形如 file:line: Assembler error: <原文> -- <详情>
CompilerErrorCIN 词法/语法/语义错误 (如 Unknown function、参数过少、非恒定 case)
ExecutionErrorDivision by zero、Stack overflow (collides with heap)、Heap exhausted: …、instruction limit reached (N steps)、原生引擎回传的运行期错误
MemoryAccessError地址越界、对只读页写入、保护位不允许该访问
python
from codecin import CPU, Config
from codecin.errors import (
    CPUSimulatorError, ExecutionError, MemoryAccessError,
)

try:
    cpu = CPU(Config(log_level='ERROR'), 'prog.cin')
    cpu.memory.set_protection(0x1000, 'r')   # 制造只读页
    cpu.memory.write_qword(0x1000, 1)
except MemoryAccessError as e:
    print('访问违例:', e)
except CPUSimulatorError as e:
    print('其它 Code CIN 错误:', e.message, e.detail)

版本与环境: codecin.version ​

codecin/version.py 把版本号从"一个字符串"升级为可编程、可自检的设施。 唯一真源始终是 codecin/__init__.py 的 __version__,本模块只做解析、比较与探测。 version 是包的公开子模块,但不在 codecin.__all__ 里(顶层短名单只导出 CPU / Config 等),用 from codecin import version 或 import codecin.version 导入。

接口说明
current_version()返回 __version__ 字符串(每次动态读取)
version_info()当前版本的 (major, minor, patch);非 x.y.z 抛 VersionError
version_tuple(text)解析任意版本串为整数三元组(纯函数,允许 v 前缀与 - / + 后缀)
try_version_tuple(text)同上但失败返回 None(不抛异常)
is_version_string(text)是否是合法版本串(与 version_tuple 接受的形式一致)
compare(a, b)只比较 x.y.z 三段数值,返回 -1 / 0 / 1;任一入参非法抛 VersionError
build_info()运行环境字典,永不抛异常
build_info_json(info=None)渲染成可 json.loads 的 JSON 字符串
format_build_info(info=None)渲染成可读的多行纯文本(无 ANSI, 便于重定向/贴报告)
VersionErrorversion_tuple() / version_info() 解析失败时抛出(ValueError 子类)

build_info() 的字段:

字段类型说明
versionstr当前版本
version_infolist[int] | None解析结果(解析不了就是 None)
python / python_implementationstr解释器版本 / 实现名
platformstr"<sys.platform>/<machine>" 形式(本机为 win32/AMD64)
system / machinestrplatform.system() / platform.machine()(如 Windows / AMD64)
nativebool原生库是否可用(v5.9.0 起不可用即无法执行程序)
native_versionstr | None原生库自报版本串原文
native_pathstr | None实际加载的库文件路径
native_version_matchesbool | None自报串是否包含包版本;无法判定为 None
package_pathstr包目录
executablestr当前解释器可执行文件
python
from codecin import version

info = version.build_info()          # 原生库缺失/加载失败也会正常返回
print(info['native'], info['native_version_matches'])

# 判断"原生库是否需要重建"
if info['native_version_matches'] is False:
    print('原生库版本与包不一致, 建议重新构建')

发布流程

python script/bump_version.py x.y.z 会一次性改真源 → 重新生成 Go 侧 engine/version_gen.go → 在 CHANGELOG.md 插入新版本小节骨架,先校验后落盘、任一步 失败按字节备份回滚(自检失败时 Go 生成物可能已更新,脚本会提示);加 --dry-run 可先预演, --date / --root 可指定小节日期与目标仓库副本。仓库里有测试强制 CHANGELOG.md 必须出现 当前版本号,所以"提了版本但忘写更新日志"会在 CI 上失败。

Go 原生库: codecin.native ​

codecin/native.py 是 ctypes 桥接层。库文件按 codecin/native/ → 包目录 的顺序查找, 先试带平台/架构后缀的 Release 资产名 (libcodecin_native-linux-x64.so / libcodecin_native-win-amd64.dll), 再试通用名 (libcodecin_native.so / .dll / .dylib); 也支持环境变量 CODECIN_NATIVE_LIB 强制指定。

接口说明
get_engine(logger=None) -> Optional[NativeEngine]查找并加载原生库; 失败返回 None (结果被缓存, 只尝试一次)
NativeEngine.version()库自报版本字符串, 如 codecin-native <版本> (Go)
NativeEngine.run_v2(bytecode, segments, entry, sp, heap_base, mem_size, ...)ABI v2 整程序执行, 返回结果字典 (见下)
NativeEngine.crom_pack(mem, compress) / crom_unpack(data)CROM 打包/解包
encode_program(instructions, entry=0, labels=None) -> bytes指令元组列表 → UCBC 字节码 (labels 把 ('label', name) 操作数解析为立即数)
decode_program(data) -> (instructions, entry)UCBC 字节码 → 指令元组列表

run_v2 详解 ​

python
run_v2(bytecode: bytes,
       segments,                     # List[Tuple[int, bytes]]: 初始内存段
       entry: int, sp: int, heap_base: int, mem_size: int,
       input_data: bytes = b'',      # 程序 stdin
       max_steps: int = 0,           # 0 = 不限
       args: Optional[List[str]] = None,   # arg_count()/arg(i) 数据源
       seed: Optional[int] = None,   # SYS RAND 种子
       sandbox: bool = False,        # 沙箱标志
       ) -> Optional[Dict[str, Any]]

返回字典 (底层通信失败返回 None):

键说明
status0 OK / 1 Done (两者均为正常结束) / 2 未实现指令 / 3 运行期错误
flags{'N', 'Z', 'C', 'V'}
pc / sp / heap_ptr结束时的指针
steps已执行指令数
regs33 个 u64 (x0–x31 + sp/fp 等)
vec_regs32 × 4 个 f64
segmentsList[(addr, bytes)] 脏内存段 (引擎按 64 KiB 页合并)
output程序全部 stdout (str)
errorstatus 为 2 / 3 时的错误文本, 否则 None

CPU.run() 拿到结果后由 _apply_native_state() 同步: 寄存器/向量/标志位/指针写回, 脏段 write_block 进稀疏内存, steps 一次性批量记入统计。

python
from codecin import native

engine = native.get_engine()
if engine is None:
    raise SystemExit('原生库未加载: 请先构建 (codecin/native/build.ps1 / build.sh)')
print('native version:', engine.version())

# 手工编码并执行一个小程序
prog = [
    ('MOV', [('reg', 0), ('imm', 21)]),
    ('ADD', [('reg', 0), ('imm', 21)]),
    ('HALT', []),
]
bc = native.encode_program(prog, entry=0)
mem_size = 1 << 30
res = engine.run_v2(bytecode=bc, segments=[], entry=0,
                    sp=(mem_size - 8) & ~0x7, heap_base=mem_size // 2,
                    mem_size=mem_size, max_steps=10_000)
print(res['status'], res['regs'][0])   # 正常结束, x0 = 42

内存布局公式

sp 初值 = (mem_size - 8) & ~0x7 (栈向下增长), heap_base = mem_size // 2 (堆向上增长)。与 寄存器与内存模型 的区域表一致。

AOT: codecin.aot ​

codecin/aot.py 把 CIN/ASM/PL/SDGCC/BIN/CO 程序编译成独立可执行文件。默认 auto 后端优先 C 后端 (codecin/cbackend.py + 系统 GCC/Clang/zig cc), C 不支持程序指令时回退 Go native 或 vm。C 后端默认动态链接系统 C 库 (c_static=True 时静态链接); Go 后端强制 CGO_ENABLED=0 纯静态。 native 代码生成位于 codecin/native_codegen.py。 Go 后端产物里 cgo 实现的 FFI 调用会得到运行期错误; C 后端按其 SYS 白名单执行 (详见 AOT 独立可执行文件)。

接口签名要点
build_program(program_file, out=None, target=None, keep_temp=False, mem_size=None, backend='auto', max_instructions=0, optimize=0, use_cache=True, debug=False, c_static=False, c_strip=False, c_keep_c=False, logger=None) -> str由源文件/.sdgcc/.bin/.co 一键构建, 返回产物绝对路径; max_instructions=0 表示不限制
build(bytecode, seg_file, out, target=None, keep_temp=False, debug=False, logger=None) -> strVM 底层入口, 直接吃字节码与段文件
build_native(go_source, out, target=None, keep_temp=False, debug=False, logger=None) -> strGo native 底层入口, 编译 generate_go_source() 的产物
build_from_ir(instructions, labels, data_labels, segments, entry_pc, mem_size, sp, heap_base, out, target=None, backend='auto', max_instructions=0, optimize=0, program_name='linked', keep_temp=False, debug=False, use_cache=False, cache_key=None, c_static=False, c_strip=False, c_keep_c=False, logger=None) -> str从链接后的 IR 构建 (对象/归档链接使用), 同样支持 C 后端开关
build_cache_key(...) / link_cache_key(...)AOT 源构建/对象链接缓存键
pack_seg_file(mem_size, sp, heap_base, segs) -> bytes打包初始内存段 + 布局参数为 program.segs 段文件
program_dependencies(program_file) -> List[str]依赖闭包 (含自身, 去重保序)
host_target() / parse_target() / supported_targets()目标平台工具
sweep_stale_build_dirs(...)清理过期临时构建目录 (默认 > 6 小时)
AotError构建失败 (非 CPUSimulatorError 子类)
python
from codecin import aot

print(aot.host_target())                  # 例如 'windows/amd64'
print(aot.supported_targets())            # ['windows/amd64', 'linux/arm64', ...]

exe = aot.build_program('examples/control_flow.cin',
                        out='demo', target='linux/amd64')
print('产物:', exe)                        # 绝对路径; Windows 目标自动补 .exe

build_program 会先做依赖完整性检查 (缺失或循环 → AotError), 再把全部依赖在编译期 展开嵌入产物, 所以产物运行时不读取任何 .cin。交叉编译需要本机 Go 工具链。 详见 AOT 独立可执行文件。

对象与链接: codecin.object ​

接口说明
compile_asm_object(path) / compile_cin_object(path)生成可重定位 .co 对象
read_object(path) / write_object(obj, path)对象序列化
link_objects(objects, gc_sections=False)合并对象、解析符号/重定位、可选 GC
link_inputs(paths, gc_sections=False) / link_files(...)链接对象、源文件与 .colib 归档
create_archive / write_archive / read_archive.colib 静态库归档
gc_instructions(instructions, labels, entry_pc)保守链接期可达性 GC
python
from codecin import aot
from codecin.object import compile_cin_object, link_objects

linked = link_objects([
    compile_cin_object('main.cin'),
    compile_cin_object('lib.cin'),
], gc_sections=True)
exe = aot.build_from_ir(
    linked['instructions'], linked['labels'], linked['data_labels'],
    linked['segments'], linked['entry_pc'], linked['mem_size'],
    linked['sp'], linked['heap_base'], 'app', optimize=2)

CROM 与 .bin: codecin.crom ​

接口说明
save_crom(memory, path, compress=True, logger=None)把 FastMemory 写成 CROM v4 段式镜像 (只存已分配页; 有原生库时走 Go 打包, 否则 zlib)
load_crom(memory, path, logger=None)读回镜像, 自动识别 CROM v4 (段式) 与 v3 (旧全量, 只读兼容)
save_bin(cpu, path, logger=None)把 CPU 的指令 + 内存镜像写成 BIN v3 容器
load_bin(cpu, path)读回 .bin (v3, 亦可读 v2), 重建 instructions / pc / sp; v2 要求 cpu.memory 大小与文件头 mem_size 一致, 否则抛 CPUSimulatorError 并提示重新编译或匹配旧布局
python
from codecin import CPU, Config, crom

cfg = Config(log_level='ERROR')
cpu = CPU(cfg, 'examples/control_flow.cin')
cpu.run()

crom.save_crom(cpu.memory, 'snapshot.crom', compress=True)
crom.save_bin(cpu, 'snapshot.bin')

# 读回 CROM 到一个新 CPU
cpu2 = CPU(Config(log_level='ERROR'))
crom.load_crom(cpu2.memory, 'snapshot.crom')

# 读回 .bin (直接当作程序加载也可以: CPU(cfg, 'snapshot.bin'))
cpu3 = CPU(Config(log_level='ERROR'))
crom.load_bin(cpu3, 'snapshot.bin')
print(len(cpu3.instructions), hex(cpu3.pc))

格式细节 (段式头布局、flags、CRC32) 见 二进制格式。

反汇编: codecin.disasm ​

disassemble_file() 接受两种输入: CPUSA 容器 (.bin, --compile-only 的产物) 或 裸 UCBC 段, 返回按行拆分的文本清单 (List[str])。

python
from codecin import crom, disasm
from codecin import CPU, Config

cpu = CPU(Config(), 'examples/control_flow.cin')
crom.save_bin(cpu, 'prog.bin')

for line in disasm.disassemble_file('prog.bin')[:12]:
    print(line)

同模块还导出 disassemble_bytes(data) (直接吃 bytes) 与容器解析辅助 _extract_from_cpusa(data) / _extract_from_bin_v3(data) / _extract_from_bin_v2(data) (返回 (bytecode, mem_size, entry, sp) 或 None)。命令行等价物是 codecin prog.bin --disasm, 见 命令行参考。

统计: codecin.stats ​

CPU.stats 是一个 Statistics 实例。注意原生 VM 路径不按指令逐个记账 (整程序执行完毕一次性批量写入), 所以 opcode_count 是空的, 只有总数可靠。

成员说明
instruction_count已执行指令总数
opcode_countdefaultdict(int), 各助记符执行次数 (原生路径为空)
execution_timestart() / stop() 之间的墙钟秒数
memory_reads / memory_writes内存读写次数
get_hot_instructions(top_n=10)最热的指令列表 (原生路径只有 '?' 一项)
inst_profilerInstructionProfiler: 指令周期表, get_total_cycles()
performance_countersPerformanceCounters: get_ipc() / get_stats() 给出 IPC / 分支准确率等
display_summary(console=None, cache_stats=None, native_used=True)渲染统计表 (cache_stats 为兼容保留的透传参数)
python
from codecin import CPU, Config

cpu = CPU(Config(log_level='ERROR'))
cpu.run()
print('instructions =', cpu.stats.instruction_count)
print('exec time    = %.4fs' % cpu.stats.execution_time)
print('perf         =', cpu.stats.performance_counters.get_stats())
cpu.stats.display_summary(cpu.console, native_used=cpu.native_used)

相关页面 ​

文档由 VitePress 构建