Files
2026-08-20 17:20:09 +08:00

4.9 KiB
Raw Permalink Blame History

阶段 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)