4.9 KiB
4.9 KiB
阶段 7:运行时(VM + iree-runtime)✅
2026-08-15。对应规划阶段 7。分两部分: 编译侧
src/iree/compiler/src/iree/compiler/Dialect/VM/(生成字节码) 运行时src/iree/runtime/src/iree/(C 实现)。
1. 整体图景
编译的最后一步是把 HAL 模块序列化成 VM 字节码(.vmfb),运行时加载并解释执行:
HAL 模块 → VM 转换(HALToVM)→ vm.module(字节码 IR)→ 序列化 → .vmfb 文件
↓
iree-runtime 加载 → VM 解释器 → 调 hal.* import → HAL driver 执行
2. VM = 紧凑字节码虚拟机(编译侧)
VM dialect 定义了一套字节码级的 IR,核心 op(IR/VMOps.td):
| op | 作用 |
|---|---|
vm.module |
顶层字节码模块 |
vm.func |
VM 函数 |
vm.import / vm.export |
导入外部函数(如 hal.command_buffer.dispatch)/ 导出 |
vm.call / vm.call.variadic / vm.call.yieldable |
调用(含可让出调用,协作式调度) |
vm.branch / vm.cond_branch / vm.branch_table |
字节码级控制流 |
vm.global.load/store.ref |
全局引用变量 |
vm.const.ref.rodata/zero |
常量 |
vm.list.* / vm.buffer.* |
列表 / buffer 操作 |
vm.discard.refs |
引用计数回收(显式 GC) |
字节码格式
用 FlatBuffers 定义(runtime/src/iree/schemas/bytecode_module_def.fbs),
是紧凑、可直接 mmap 加载的二进制格式。阶段 2 用 iree-dump-module 看到的就是反汇编:
Required Types: i32, i64, !hal.allocator, !hal.buffer, !hal.buffer_view,
!hal.command_buffer, !hal.device, !hal.executable, !hal.fence, !vm.buffer
Module Dependencies: hal, version >= 6
Imported Functions:
[ 7] hal.command_buffer.dispatch(...) -> ()
[11] hal.device.queue.execute.indirect(...) -> ()
[16] hal.fence.await(i32, i64, tuple<!vm.ref<?>>...) -> (i32)
3. iree-runtime(C 运行时)
runtime/src/iree/ 目录:
| 目录 | 作用 |
|---|---|
vm/ + vm/bytecode/ |
VM 解释器(字节码解码 + 分发执行) |
hal/ + hal/drivers/ |
HAL 运行时 + 各设备驱动 |
task/ |
多线程任务调度系统(local-task driver 的基础) |
base/ |
基础工具(status、allocator、logging) |
builtins/ / modules/ |
VM 内置函数 / 内置模块(如 hal 模块) |
schemas/ |
FlatBuffers schema(字节码 + 各后端可执行体格式) |
runtime/ |
顶层 API(iree_runtime_instance 等) |
io/ |
参数/文件 IO |
HAL drivers(真正的设备后端)
hal/drivers/ 下每个目录是一个设备实现:
| driver | 设备 |
|---|---|
local_task |
CPU 多线程(基于 task 系统)—— 默认 |
local_sync |
CPU 同步(调试用) |
cuda / hip / amdgpu |
NVIDIA / AMD GPU |
vulkan |
Vulkan |
metal |
Apple Metal |
webgpu |
WebGPU |
null |
空设备(测试) |
当前环境只装了 base 包,运行时只有 local-task/local-sync。CUDA driver 需要额外构建。
4. 与 TVM 0.6 对照
| IREE | TVM 0.6 |
|---|---|
| VM 字节码(.vmfb) | graph runtime / relay VM 的序列化 |
| vm.import(hal.*) | 生成的 kernel 调用 |
| HAL driver(local-task/cuda/vulkan) | DeviceAPI(CPU/CUDA/OpenCL/Metal) |
| task 系统 | CPU 线程池 runtime |
| 引用计数 GC | 无(TVM 用 DLTensor 引用计数) |
核心差异:IREE 的 VM 是完整字节码虚拟机(带控制流、全局变量、yieldable 调用), 而 TVM 的 graph runtime 只是「按拓扑顺序执行节点」。IREE 的 VM 更接近一个真正的 VM。
5. 源码阅读路径
# 编译侧(字节码生成)
src/iree/compiler/src/iree/compiler/Dialect/VM/
├── IR/VMOps.td # VM op 定义
├── Conversion/HALToVM/ # HAL → VM 转换
└── Target/ # 序列化到字节码
# 运行时(C)
src/iree/runtime/src/iree/
├── vm/ # VM 解释器
├── hal/drivers/local_task/ # CPU 多线程 driver(默认)
├── hal/drivers/cuda/ # CUDA driver
├── task/ # 任务调度
└── schemas/bytecode_module_def.fbs # 字节码格式
6. 全流程回顾(7 个阶段串起来)
输入(StableHLO/TOSA) → [前端] → linalg → [Flow] dispatch 划分 → [Stream] 异步调度
→ [HAL] 设备抽象 + 触发 codegen → [Codegen] linalg→LLVM/SPIR-V → [VM] 字节码 → [runtime] 执行
你现在已经把 IREE 从输入到运行时完整走了一遍。每个阶段都有对应笔记和源码路径。
下一步(阶段 8 实践,可选)
- 写一个自定义 pass(如自定义 fusion 规则),需要源码构建
- 配 CUDA 后端,看 GPU 的 codegen(LLVMGPU → NVVM)
- 深入某一层读源码(建议 Stream 或 Codegen)