背景
当前 lua-net 是纯解释器实现。调研 LuaJIT 架构后,确认追踪式 JIT 不适合 .NET 平台(需要直接发射机器码,无法利用 .NET GC 和安全模型)。
更合适的方案是方法级 JIT:将 Lua 字节码翻译为 CIL,通过 DynamicMethod + ILGenerator 发射,让 RyuJIT 完成原生码编译。
与 LuaJIT 的关键差异
| 维度 |
LuaJIT |
lua-net JIT 方案 |
| 编译单元 |
线性追踪(跨函数热路径) |
方法/函数 |
| 目标码 |
x86/ARM 机器码 |
CIL → RyuJIT → 原生码 |
| 寄存器分配 |
自研反向线性扫描 |
RyuJIT 自动处理 |
| 类型特化 |
追踪录制时隐式获取 |
需要显式类型反馈收集 |
| 退优化 |
快照 + 侧出口 |
类型守卫 + 回退到解释器 |
.NET 免费提供:寄存器分配、指令选择、机器码内存管理、多架构支持。
需要新增的模块
src/Lua.JIT/
├── Profiling/
│ ├── HotCountProfiler.cs 热点计数(函数调用 + 循环回边)
│ └── TypeProfiler.cs 运行时类型反馈收集
├── IR/
│ ├── IrOpcode.cs IR 操作码
│ ├── IrInstruction.cs SSA 指令节点
│ ├── IrBuilder.cs 字节码 → IR(方法级 CFG,非追踪)
│ └── IrFunction.cs 基本块 + 控制流图
├── Optimization/
│ ├── ConstantFolder.cs 常量折叠与代数简化
│ ├── DeadCodeEliminator.cs 死代码消除
│ ├── TypeSpecializer.cs 根据类型反馈插入类型守卫
│ ├── NarrowingPass.cs float → int 窄化
│ └── InlineExpander.cs 小函数内联
├── Emit/
│ ├── CilEmitter.cs IR → CIL(DynamicMethod + ILGenerator)
│ ├── GuardEmitter.cs 类型守卫 + 退优化分支
│ └── TableAccessEmitter.cs 表访问快速路径
└── Runtime/
├── Deoptimizer.cs 守卫失败时恢复解释器状态
├── JitHelpers.cs JIT 代码调用的运行时辅助方法
└── CompiledFunction.cs 编译后的委托包装
分阶段计划
| 阶段 |
内容 |
预期收益 |
| Phase 0 |
模板 JIT:每个字节码对应一段 CIL 模板,无 IR 层 |
2–5x 加速,数周可完成 |
| Phase 1 |
热点计数 + 类型反馈收集(埋点到解释器) |
为后续优化提供数据 |
| Phase 2 |
IR 构建器(字节码 → SSA IR + 基本块) |
验证 IR 正确性 |
| Phase 3 |
CIL 发射器(类型特化算术 + 简单控制流) |
数值密集型代码大幅加速 |
| Phase 4 |
类型守卫 + 退优化(守卫失败安全回退解释器) |
多态函数安全处理 |
| Phase 5 |
常量折叠 + DCE + float→int 窄化 |
进一步优化 |
| Phase 6 |
表访问优化 + 内联缓存 + 函数内联 |
接近 LuaJIT 性能水平 |
前置条件
JIT 工作应在路线图 Step 1–10(解释器 + 标准库 + coroutine)基本完成后再启动,原因:
- JIT 编译的语义正确性依赖解释器作为参考实现
- 退优化需要回退到解释器,解释器必须功能完备
- 类型反馈收集需要解释器执行路径稳定
- coroutine 的 yield/resume 会影响 JIT 调用栈设计
参考资料
背景
当前 lua-net 是纯解释器实现。调研 LuaJIT 架构后,确认追踪式 JIT 不适合 .NET 平台(需要直接发射机器码,无法利用 .NET GC 和安全模型)。
更合适的方案是方法级 JIT:将 Lua 字节码翻译为 CIL,通过
DynamicMethod+ILGenerator发射,让 RyuJIT 完成原生码编译。与 LuaJIT 的关键差异
.NET 免费提供:寄存器分配、指令选择、机器码内存管理、多架构支持。
需要新增的模块
分阶段计划
前置条件
JIT 工作应在路线图 Step 1–10(解释器 + 标准库 + coroutine)基本完成后再启动,原因:
参考资料