Dialect 名称: vm C++ 命名空间: ::mlir::iree_compiler::IREE::VM 主要用途: 抽象虚拟机操作,可以序列化为字节码或降低为静态表示
- 概述
- 文件结构
- 核心概念
- 类型系统
- Opcode 表
- 核心 Op 列表
- 关键 Interface
- 在编译 Pipeline 中的位置
VM (Virtual Machine) Dialect 是 IREE 编译器的虚拟机抽象层。虚拟机操作设计为可以序列化为字节码表示(在运行时解释),或进一步降低为静态表示(如 LLVM IR、C 等)。
VM Dialect 的类型和操作主要用于编码资源所有权规则和控制流,可以由目标运行时以多种不同方式表示。
设计目标:
- 可伸缩运行时 - 支持从解释器到完全原生的多种执行模式
- 最小化假设 - 保持实现简单
- 资源所有权 - 编码资源所有权规则
- 多目标支持 - 可降低到多种表示形式
由于假设所有实际数学计算都发生在 dispatch regions 内,VM Dialect 仅提供:
- 标量操作 - 用于偏移和形状计算
- 简单流控制 - 固定范围循环
这种设计允许 VM 极其轻量和高效。
除了整数值之外,唯一的其他存储类型是变体引用,建模抽象的 iree_vm_ref_t。这使得:
- 其他方言可以依赖自动引用计数
- 避免需要更冗长的手动引用计数逻辑(考虑到 VM 的类似协程特性,这可能很难或不可能管理)
- 降低目标可以根据需要插入引用计数
VM Dialect 中的类型对应于存储类型而不是值类型,类型的解释编码在操作上。
| 文件 | 内容 |
|---|
VMBase.td | Dialect 定义、Opcode 表、声明式编码框架、基础类 |
VMOpcodesCore.td | 核心 Opcode 定义 |
VMOpcodesF32.td | F32 浮点数 Opcode 定义 |
VMOpcodesF64.td | F64 浮点数 Opcode 定义 |
VMOps.td | 所有 VM Operation 定义 |
VMTypes.td | VM Type 定义 |
VMAttrs.td | VM Attribute 定义 |
VMInterfaces.td | VM Interface 定义 |
VM ops 使用声明式编码框架进行序列化,由 iree-tblgen gen-vm-op-encoding-defs 生成器处理。
编码例程签名:
LogicalResult encode(SymbolTable &syms, VMFuncEncoder &e);
syms - 父模块中的所有符号e - 字节码编码器,操作应该被编码到其中
let encoding = [
VM_EncOpcode<VM_OPC_Return>,
VM_EncVariadicOperands<"operands">
];
生成的编码函数(伪代码):
LogicalResult encode(SymbolTable &syms, VMFuncEncoder &e) {
if (failed(e.encodeI8(234)) ||
failed(e.encodeOperands(operands()))) {
return failure();
}
return success();
}
VM 字节码是一种紧凑的二进制格式,包含:
- 模块元数据
- 函数定义
- 常量/只读数据(rodata)
- 全局变量
- 导入/导出表
- 字节码指令流
| 类型名 | MLIR 语法 | 说明 |
|---|
VM_i1 | !vm.i1 | 1 位布尔值 |
VM_i8 | !vm.i8 | 8 位整数 |
VM_i16 | !vm.i16 | 16 位整数 |
VM_i32 | !vm.i32 | 32 位整数 |
VM_i64 | !vm.i64 | 64 位整数 |
VM_f32 | !vm.f32 | 32 位浮点数 |
VM_f64 | !vm.f64 | 64 位浮点数 |
| 类型名 | MLIR 语法 | 说明 |
|---|
VM_ref | !vm.ref | 通用引用类型(带类型擦除) |
VM_ref<> | !vm.ref<?> | 带类型的引用 |
VM_buffer | !vm.buffer | 缓冲区引用 |
VM_list | !vm.list | 列表引用 |
| 类型名 | MLIR 语法 | 说明 |
|---|
VM_rodata | !vm.rodata | 只读数据 |
VM_ordinal | !vm.ordinal | 序号(符号引用) |
| 类别 | Opcode 范围 | 说明 |
|---|
| 控制流 | 0x00 - 0x1F | 分支、返回、调用 |
| 整数运算 | 0x20 - 0x5F | i32、i64 的算术和位操作 |
| 比较 | 0x60 - 0x7F | 整数和引用比较 |
| 常量 | 0x80 - 0x9F | 常量加载 |
| 全局变量 | 0xA0 - 0xBF | 全局变量加载/存储 |
| 引用 | 0xC0 - 0xDF | 引用计数、检查、转换 |
| 列表 | 0xE0 - 0xFF | 列表操作 |
| 缓冲区 | 0x100 - 0x11F | 缓冲区操作 |
| 调试/追踪 | 0x120 - 0x13F | 调试和追踪操作 |
| 类别 | Opcode 范围 | 说明 |
|---|
| F32 运算 | 0x200 - 0x21F | 32 位浮点数算术 |
| F32 比较 | 0x220 - 0x22F | 32 位浮点数比较 |
| F32 转换 | 0x230 - 0x23F | 32 位浮点数与整数转换 |
| 类别 | Opcode 范围 | 说明 |
|---|
| F64 运算 | 0x240 - 0x25F | 64 位浮点数算术 |
| F64 比较 | 0x260 - 0x26F | 64 位浮点数比较 |
| F64 转换 | 0x270 - 0x27F | 64 位浮点数与整数转换 |
| Op 名称 | 说明 |
|---|
vm.return | 从函数返回 |
vm.branch | 无条件分支 |
vm.cond_br | 条件分支 |
vm.switch | switch 语句 |
vm.call | 函数调用 |
vm.call.variadic | 可变参数函数调用 |
vm.import | 导入外部函数 |
vm.export | 导出函数 |
vm.func | 函数定义 |
| 操作 | i32 | i64 | 说明 |
|---|
| 加法 | vm.add.i32 | vm.add.i64 | 整数加法 |
| 减法 | vm.sub.i32 | vm.sub.i64 | 整数减法 |
| 乘法 | vm.mul.i32 | vm.mul.i64 | 整数乘法 |
| 除法 | vm.div.s.i32 | vm.div.s.i64 | 有符号整数除法 |
| 取模 | vm.rem.s.i32 | vm.rem.s.i64 | 有符号整数取模 |
| 按位与 | vm.and.i32 | vm.and.i64 | 按位与 |
| 按位或 | vm.or.i32 | vm.or.i64 | 按位或 |
| 按位异或 | vm.xor.i32 | vm.xor.i64 | 按位异或 |
| 左移 | vm.shl.i32 | vm.shl.i64 | 左移 |
| 右移 | vm.shr.s.i32 | vm.shr.s.i64 | 算术右移 |
| 右移(逻辑) | vm.shr.u.i32 | vm.shr.u.i64 | 逻辑右移 |
| 取反 | vm.neg.i32 | vm.neg.i64 | 取反 |
| 按位取反 | vm.not.i32 | vm.not.i64 | 按位取反 |
| Op 名称 | 说明 |
|---|
vm.cmp.eq.i32 / vm.cmp.eq.i64 | 等于 |
vm.cmp.ne.i32 / vm.cmp.ne.i64 | 不等于 |
vm.cmp.lt.s.i32 / vm.cmp.lt.s.i64 | 小于(有符号) |
vm.cmp.le.i32 / vm.cmp.le.i64 | 小于等于(有符号) |
vm.cmp.gt.s.i32 / vm.cmp.gt.s.i64 | 大于(有符号) |
vm.cmp.ge.i32 / vm.cmp.ge.i64 | 大于等于(有符号) |
vm.cmp.eq.ref | 引用相等 |
vm.cmp.ne.ref | 引用不等 |
| Op 名称 | 说明 |
|---|
vm.const.i32 | 加载 i32 常量 |
vm.const.i64 | 加载 i64 常量 |
vm.const.f32 | 加载 f32 常量 |
vm.const.f64 | 加载 f64 常量 |
vm.const.i32.zero | 加载 i32 零 |
vm.const.ref.null | 加载空引用 |
| Op 名称 | 说明 |
|---|
vm.global.i32.load | 加载 i32 全局变量 |
vm.global.i32.store | 存储 i32 全局变量 |
vm.global.i64.load | 加载 i64 全局变量 |
vm.global.i64.store | 存储 i64 全局变量 |
vm.global.ref.load | 加载引用全局变量 |
vm.global.ref.store | 存储引用全局变量 |
vm.global | 全局变量声明 |
| Op 名称 | 说明 |
|---|
vm.ref.check | 检查引用类型 |
vm.ref.retain | 增加引用计数 |
vm.ref.release | 减少引用计数 |
vm.ref.assign | 赋值引用 |
| Op 名称 | 说明 |
|---|
vm.list.get.i32 | 获取列表元素(i32) |
vm.list.get.i64 | 获取列表元素(i64) |
vm.list.get.ref | 获取列表元素(引用) |
vm.list.set.i32 | 设置列表元素(i32) |
vm.list.set.i64 | 设置列表元素(i64) |
vm.list.set.ref | 设置列表元素(引用) |
vm.list.size | 获取列表大小 |
vm.list.resize | 调整列表大小 |
| Op 名称 | 说明 | |---|---|---| | vm.buffer.length | 获取缓冲区长度 | | vm.buffer.subspan | 获取缓冲区子范围 | | vm.buffer.copy | 复制缓冲区 | | vm.buffer.fill | 填充缓冲区 |
| 操作 | F32 | F64 | 说明 |
|---|
| 加法 | vm.add.f32 | vm.add.f64 | 浮点数加法 |
| 减法 | vm.sub.f32 | vm.sub.f64 | 浮点数减法 |
| 乘法 | vm.mul.f32 | vm.mul.f64 | 浮点数乘法 |
| 除法 | vm.div.f32 | vm.div.f64 | 浮点数除法 |
| 取模 | vm.rem.f32 | vm.rem.f64 | 浮点数取模 |
| 取反 | vm.neg.f32 | vm.neg.f64 | 浮点数取反 |
| 绝对值 | vm.abs.f32 | vm.abs.f64 | 浮点数绝对值 |
| 平方根 | vm.sqrt.f32 | vm.sqrt.f64 | 浮点数平方根 |
| 最小值 | vm.min.f32 | vm.min.f64 | 浮点数最小值 |
| 最大值 | vm.max.f32 | vm.max.f64 | 浮点数最大值 |
| 最近整数 | vm.round.f32 | vm.round.f64 | 最近整数 |
| 向上取整 | vm.ceil.f32 | vm.ceil.f64 | 向上取整 |
| 向下取整 | vm.floor.f32 | vm.floor.f64 | 向下取整 |
| 截断取整 | vm.trunc.f32 | vm.trunc.f64 | 截断取整 |
| Op 名称 | 说明 |
|---|
vm.cmp.eq.f32 / vm.cmp.eq.f64 | 等于 |
vm.cmp.ne.f32 / vm.cmp.ne.f64 | 不等于 |
vm.cmp.lt.f32 / vm.cmp.lt.f64 | 小于 |
vm.cmp.le.f32 / vm.cmp.le.f64 | 小于等于 |
vm.cmp.gt.f32 / vm.cmp.gt.f64 | 大于 |
vm.cmp.ge.f32 / vm.cmp.ge.f64 | 大于等于 |
vm.cmp.nan.f32 / vm.cmp.nan.f64 | 是 NaN |
| Op 名称 | 说明 |
|---|
vm.cast.f32.s.i32 | i32 转 f32(有符号) |
vm.cast.f32.s.i64 | i64 转 f32(有符号) |
vm.cast.s.i32.f32 | f32 转 i32(有符号截断) |
vm.cast.s.i64.f32 | f32 转 i64(有符号截断) |
vm.cast.f32.f64 | f64 转 f32 |
vm.cast.f64.f32 | f32 转 f64 |
| Op 名称 | 说明 |
|---|
vm.trace | 追踪点 |
vm.print | 打印值 |
vm.break | 断点 |
字节码 VM ops 使用的接口,提供一致的序列化和反序列化信息。
方法:
encode(SymbolTable &syms, VMFuncEncoder &e) - 将操作编码为字节码verify() - 验证操作的合法性
VM Dialect 位于编译 Pipeline 的最后阶段,是最终的 IR 表示:
Input → ... → HAL → **VM** → 字节码 / LLVM IR / C
| 顺序 | Pass 名称 | 说明 |
|---|
| 1 | ConversionPass | 从标准/HAL 等 dialect 转到 VM dialect |
| 2 | GlobalInitializationPass | 全局初始化 |
| 3 | OrdinalAllocationPass | 分配序号 |
| 4 | AnnotateFunctionsPass | 注解函数 |
| 5 | DeduplicateRodataPass | 去重只读数据 |
| 6 | HoistInlinedRodataPass | 提升内联只读数据 |
| 7 | ResolveRodataLoadsPass | 解析只读数据加载 |
| 8 | ReifyRodataTablesPass | 具体化只读数据表 |
| 9 | DropEmptyModuleInitializersPass | 删除空模块初始化器 |
| 10 | DropOptimizationBarriersPass | 删除优化屏障 |
| 11 | DropUnusedCallsPass | 删除未使用的调用 |
| 12 | ConvertToYieldableCallsPass | 转换为可 yield 调用 |
| 13 | SinkDefiningOpsPass | 下沉定义操作 |
| 14 | MaterializeRefDiscardsPass | 具体化引用丢弃 |
VM Dialect 可以输出到多种目标:
| 目标 | 输出格式 | 用途 |
|---|
| VM 字节码 | .vmfb 二进制文件 | 运行时解释执行 |
| LLVM IR | LLVM IR → 原生机器码 | AOT 编译,原生执行 |
| C 代码 | C 源文件 | 嵌入式或自定义构建 |
| SPIR-V | SPIR-V 字节码 | 在 GPU 上运行 VM |
VM Dialect 是 IREE 编译器的最终代码生成层:
- ✅ 抽象虚拟机操作,支持多种降低目标
- ✅ 声明式字节码编码框架
- ✅ 自动引用计数系统
- ✅ 仅限标量操作(所有实计算在 dispatch regions)
- ✅ 可序列化为字节码或降低为 LLVM IR/C 等
- ✅ 设计支持在持久化 Vulkan kernel 内运行整个 VM
VM Dialect 的关键设计决策:
- 存储类型 vs 值类型 - 类型对应存储而非值
- 最小数学支持 - 仅提供偏移/形状计算所需的标量操作
- 自动引用计数 - 内置 ref 类型简化资源管理
- 多目标设计 - 一次编写,多处运行