Published on

VM Dialect 详解

Authors
  • avatar
    Name
    alan
    Twitter

Dialect 名称: vm C++ 命名空间: ::mlir::iree_compiler::IREE::VM 主要用途: 抽象虚拟机操作,可以序列化为字节码或降低为静态表示


目录

  1. 概述
  2. 文件结构
  3. 核心概念
  4. 类型系统
  5. Opcode 表
  6. 核心 Op 列表
  7. 关键 Interface
  8. 在编译 Pipeline 中的位置

1. 概述

VM (Virtual Machine) Dialect 是 IREE 编译器的虚拟机抽象层。虚拟机操作设计为可以序列化为字节码表示(在运行时解释),或进一步降低为静态表示(如 LLVM IR、C 等)。

设计哲学

VM Dialect 的类型和操作主要用于编码资源所有权规则和控制流,可以由目标运行时以多种不同方式表示。

设计目标:

  1. 可伸缩运行时 - 支持从解释器到完全原生的多种执行模式
  2. 最小化假设 - 保持实现简单
  3. 资源所有权 - 编码资源所有权规则
  4. 多目标支持 - 可降低到多种表示形式

数学限制

由于假设所有实际数学计算都发生在 dispatch regions 内,VM Dialect 仅提供:

  • 标量操作 - 用于偏移和形状计算
  • 简单流控制 - 固定范围循环

这种设计允许 VM 极其轻量和高效。

引用计数模型

除了整数值之外,唯一的其他存储类型是变体引用,建模抽象的 iree_vm_ref_t。这使得:

  • 其他方言可以依赖自动引用计数
  • 避免需要更冗长的手动引用计数逻辑(考虑到 VM 的类似协程特性,这可能很难或不可能管理)
  • 降低目标可以根据需要插入引用计数

类型设计原则

VM Dialect 中的类型对应于存储类型而不是值类型,类型的解释编码在操作上。


2. 文件结构

文件内容
VMBase.tdDialect 定义、Opcode 表、声明式编码框架、基础类
VMOpcodesCore.td核心 Opcode 定义
VMOpcodesF32.tdF32 浮点数 Opcode 定义
VMOpcodesF64.tdF64 浮点数 Opcode 定义
VMOps.td所有 VM Operation 定义
VMTypes.tdVM Type 定义
VMAttrs.tdVM Attribute 定义
VMInterfaces.tdVM Interface 定义

3. 核心概念

3.1 声明式编码框架

VM ops 使用声明式编码框架进行序列化,由 iree-tblgen gen-vm-op-encoding-defs 生成器处理。

编码例程签名:

LogicalResult encode(SymbolTable &syms, VMFuncEncoder &e);
  • syms - 父模块中的所有符号
  • e - 字节码编码器,操作应该被编码到其中

3.2 Op 编码示例

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();
}

3.3 字节码格式

VM 字节码是一种紧凑的二进制格式,包含:

  • 模块元数据
  • 函数定义
  • 常量/只读数据(rodata)
  • 全局变量
  • 导入/导出表
  • 字节码指令流

4. 类型系统

4.1 基本类型

类型名MLIR 语法说明
VM_i1!vm.i11 位布尔值
VM_i8!vm.i88 位整数
VM_i16!vm.i1616 位整数
VM_i32!vm.i3232 位整数
VM_i64!vm.i6464 位整数
VM_f32!vm.f3232 位浮点数
VM_f64!vm.f6464 位浮点数

4.2 引用类型

类型名MLIR 语法说明
VM_ref!vm.ref通用引用类型(带类型擦除)
VM_ref<>!vm.ref<?>带类型的引用
VM_buffer!vm.buffer缓冲区引用
VM_list!vm.list列表引用

4.3 特殊类型

类型名MLIR 语法说明
VM_rodata!vm.rodata只读数据
VM_ordinal!vm.ordinal序号(符号引用)

5. Opcode 表

5.1 核心 Opcode(VMOpcodesCore.td)

类别Opcode 范围说明
控制流0x00 - 0x1F分支、返回、调用
整数运算0x20 - 0x5Fi32、i64 的算术和位操作
比较0x60 - 0x7F整数和引用比较
常量0x80 - 0x9F常量加载
全局变量0xA0 - 0xBF全局变量加载/存储
引用0xC0 - 0xDF引用计数、检查、转换
列表0xE0 - 0xFF列表操作
缓冲区0x100 - 0x11F缓冲区操作
调试/追踪0x120 - 0x13F调试和追踪操作

5.2 F32 Opcode(VMOpcodesF32.td)

类别Opcode 范围说明
F32 运算0x200 - 0x21F32 位浮点数算术
F32 比较0x220 - 0x22F32 位浮点数比较
F32 转换0x230 - 0x23F32 位浮点数与整数转换

5.3 F64 Opcode(VMOpcodesF64.td)

类别Opcode 范围说明
F64 运算0x240 - 0x25F64 位浮点数算术
F64 比较0x260 - 0x26F64 位浮点数比较
F64 转换0x270 - 0x27F64 位浮点数与整数转换

6. 核心 Op 列表

6.1 控制流操作

Op 名称说明
vm.return从函数返回
vm.branch无条件分支
vm.cond_br条件分支
vm.switchswitch 语句
vm.call函数调用
vm.call.variadic可变参数函数调用
vm.import导入外部函数
vm.export导出函数
vm.func函数定义

6.2 整数运算(i32 / i64)

操作i32i64说明
加法vm.add.i32vm.add.i64整数加法
减法vm.sub.i32vm.sub.i64整数减法
乘法vm.mul.i32vm.mul.i64整数乘法
除法vm.div.s.i32vm.div.s.i64有符号整数除法
取模vm.rem.s.i32vm.rem.s.i64有符号整数取模
按位与vm.and.i32vm.and.i64按位与
按位或vm.or.i32vm.or.i64按位或
按位异或vm.xor.i32vm.xor.i64按位异或
左移vm.shl.i32vm.shl.i64左移
右移vm.shr.s.i32vm.shr.s.i64算术右移
右移(逻辑)vm.shr.u.i32vm.shr.u.i64逻辑右移
取反vm.neg.i32vm.neg.i64取反
按位取反vm.not.i32vm.not.i64按位取反

6.3 比较操作

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引用不等

6.4 常量加载

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加载空引用

6.5 全局变量操作

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全局变量声明

6.6 引用操作

Op 名称说明
vm.ref.check检查引用类型
vm.ref.retain增加引用计数
vm.ref.release减少引用计数
vm.ref.assign赋值引用

6.7 列表操作

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调整列表大小

6.8 缓冲区操作

| Op 名称 | 说明 | |---|---|---| | vm.buffer.length | 获取缓冲区长度 | | vm.buffer.subspan | 获取缓冲区子范围 | | vm.buffer.copy | 复制缓冲区 | | vm.buffer.fill | 填充缓冲区 |

6.9 浮点数操作(F32 / F64)

操作F32F64说明
加法vm.add.f32vm.add.f64浮点数加法
减法vm.sub.f32vm.sub.f64浮点数减法
乘法vm.mul.f32vm.mul.f64浮点数乘法
除法vm.div.f32vm.div.f64浮点数除法
取模vm.rem.f32vm.rem.f64浮点数取模
取反vm.neg.f32vm.neg.f64浮点数取反
绝对值vm.abs.f32vm.abs.f64浮点数绝对值
平方根vm.sqrt.f32vm.sqrt.f64浮点数平方根
最小值vm.min.f32vm.min.f64浮点数最小值
最大值vm.max.f32vm.max.f64浮点数最大值
最近整数vm.round.f32vm.round.f64最近整数
向上取整vm.ceil.f32vm.ceil.f64向上取整
向下取整vm.floor.f32vm.floor.f64向下取整
截断取整vm.trunc.f32vm.trunc.f64截断取整

6.10 浮点数比较

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

6.11 浮点数转换

Op 名称说明
vm.cast.f32.s.i32i32 转 f32(有符号)
vm.cast.f32.s.i64i64 转 f32(有符号)
vm.cast.s.i32.f32f32 转 i32(有符号截断)
vm.cast.s.i64.f32f32 转 i64(有符号截断)
vm.cast.f32.f64f64 转 f32
vm.cast.f64.f32f32 转 f64

6.12 调试/追踪操作

Op 名称说明
vm.trace追踪点
vm.print打印值
vm.break断点

7. 关键 Interface

7.1 VMSerializableOpInterface

字节码 VM ops 使用的接口,提供一致的序列化和反序列化信息。

方法:

  • encode(SymbolTable &syms, VMFuncEncoder &e) - 将操作编码为字节码
  • verify() - 验证操作的合法性

8. 在编译 Pipeline 中的位置

VM Dialect 位于编译 Pipeline 的最后阶段,是最终的 IR 表示:

Input → ... → HAL → **VM** → 字节码 / LLVM IR / C

VM Pipeline 阶段

顺序Pass 名称说明
1ConversionPass从标准/HAL 等 dialect 转到 VM dialect
2GlobalInitializationPass全局初始化
3OrdinalAllocationPass分配序号
4AnnotateFunctionsPass注解函数
5DeduplicateRodataPass去重只读数据
6HoistInlinedRodataPass提升内联只读数据
7ResolveRodataLoadsPass解析只读数据加载
8ReifyRodataTablesPass具体化只读数据表
9DropEmptyModuleInitializersPass删除空模块初始化器
10DropOptimizationBarriersPass删除优化屏障
11DropUnusedCallsPass删除未使用的调用
12ConvertToYieldableCallsPass转换为可 yield 调用
13SinkDefiningOpsPass下沉定义操作
14MaterializeRefDiscardsPass具体化引用丢弃

输出目标

VM Dialect 可以输出到多种目标:

目标输出格式用途
VM 字节码.vmfb 二进制文件运行时解释执行
LLVM IRLLVM IR → 原生机器码AOT 编译,原生执行
C 代码C 源文件嵌入式或自定义构建
SPIR-VSPIR-V 字节码在 GPU 上运行 VM

总结

VM Dialect 是 IREE 编译器的最终代码生成层:

  • ✅ 抽象虚拟机操作,支持多种降低目标
  • ✅ 声明式字节码编码框架
  • ✅ 自动引用计数系统
  • ✅ 仅限标量操作(所有实计算在 dispatch regions)
  • ✅ 可序列化为字节码或降低为 LLVM IR/C 等
  • ✅ 设计支持在持久化 Vulkan kernel 内运行整个 VM

VM Dialect 的关键设计决策:

  1. 存储类型 vs 值类型 - 类型对应存储而非值
  2. 最小数学支持 - 仅提供偏移/形状计算所需的标量操作
  3. 自动引用计数 - 内置 ref 类型简化资源管理
  4. 多目标设计 - 一次编写,多处运行