前段时间写了一个栈式虚拟机(Stack-based VM),整理了一份设计文档。本文是对这份文档的解读和总结。
项目代码:https://github.com/xyanrch1024/VM
为什么选栈式架构
虚拟机架构主要有两种:
| 特性 | 栈式 | 寄存器式 |
|---|---|---|
| 指令长度 | 短(1-3 字节) | 较长 |
| 编译器后端 | 极简 | 需要寄存器分配 |
| 解释执行 | 直观,Bug 少 | 指令数少约 30% |
| JIT 适配 | 较难 | 天然适合 |
这个项目选择栈式,原因很简单:纯解释执行场景下,栈式实现简单、代码生成方便、不易出错。
总体架构
VM 实例包含以下核心组件:
- 操作数栈 —
vector<Value>,同时存放局部变量和临时计算值 - 调用栈 —
vector<CallFrame>,管理函数调用 - 字符串表 — intern 池,字符串比较退化为指针比较
- 函数表 — 所有已编译函数的集合
- PC / FP — 程序计数器和帧指针
值系统
每个 Value 占 8 字节,用 1 字节类型标签 + 7 字节值:
┌──────┬──────────────────────────────┐
│ type │ value │
│ 1B │ 7B │
├──────┼──────────────────────────────┤
│ NIL │ padding │
│ BOOL │ bool │
│ INT │ int64_t │
│FLOAT │ double │
│STRING│ void* (→ string table) │
└──────┴──────────────────────────────┘
类型提升规则:INT + FLOAT → FLOAT,字符串 + 走拼接。
指令集(40 条)
指令编码格式:1 字节 opcode + 变长操作数。
分为几大类:
| 类别 | 数量 | 示例 |
|---|---|---|
| 常量压栈 | 5 | OP_CONSTANT, OP_NIL |
| 栈操作 | 5 | OP_DUP, OP_SWAP, OP_ROT |
| 局部变量 | 10 | OP_LOAD, OP_STORE_0~3 |
| 算术运算 | 7 | OP_ADD, OP_SUB, OP_MUL |
| 比较运算 | 6 | OP_EQ, OP_LT, OP_GE |
| 位运算 | 6 | OP_BIT_AND, OP_SHL |
| 逻辑运算 | 1 | OP_NOT |
| 控制流 | 4 | OP_JMP, OP_JZ, OP_LOOP |
| 函数调用 | 2 | OP_CALL, OP_RET |
| 其他 | 4 | OP_PRINT, OP_HALT |
执行模型
操作数栈分为两个区域:
┌───────────────────┐
│ 局部变量区域 │ [fp, fp+numLocals)
│ slot 0,1,...,N │ LOAD/STORE 访问
├───────────────────┤
│ 表达式栈 │ [fp+numLocals, sp]
│ 临时计算值 │ PUSH/POP 操作
└───────────────────┘
函数调用时:预分配局部变量 → 压入 CallFrame → 执行 → RET 弹出清栈。
内存管理:字符串驻留
所有字符串在加载时放入 intern 表。每次遇到字符串常量,先查 hash map:
已存在?→ 返回已有指针(O(1) 比较)
不存在?→ 创建新副本,加入表,返回稳定指针
编译示例
1 + 2 × 3 的字节码:
OP_CONSTANT 0 ; push 1
OP_CONSTANT 1 ; push 2
OP_CONSTANT 2 ; push 3
OP_MUL ; 2 × 3 = 6
OP_ADD ; 1 + 6 = 7
OP_PRINTLN ; print 7
OP_HALT
递归阶乘 factorial(5):
与多数语言编译器的中间表示类似,函数体内通过 LOAD/STORE 访问参数和局部变量,CALL/RET 管理调用栈。
测试覆盖
13 个测试覆盖了:算术、局部变量、条件分支、循环、递归、浮点、字符串、比较、位运算、栈操作、双递归调用(fibonacci)。
优化方向(待做)
- Top-of-stack caching — 栈顶值缓存到寄存器
- Computed goto — switch 替换为跳转表
- 内联缓存 — 频繁调用函数做内联
- JIT 编译 — 热点字节码编译为本地机器码
结语
这份设计文档完整描述了一个可工作的栈式虚拟机。代码量不大(C++17,8 个文件),但涵盖了 VM 的核心设计范式。对编译器或语言实现感兴趣的同学可以参考。
完整源码:https://github.com/xyanrch1024/VM