MLIR 层次化编译规范
一、核心设计思想
MLIR(Multi-Level Intermediate Representation)的核心方法论是分层渐进式降阶(Progressive Lowering):将复杂的 AI 编译问题拆解为多个抽象层次,每层只关注单一维度的语义表达与优化,通过明确的边界和约束实现可维护、可扩展的编译器架构。
Google IREE(Intermediate Representation Execution Environment)作为 MLIR 生态中最具代表性的 AI 编译器工程落地,完整践行了这一设计哲学。
二、六层层次规范
总体流向
Plain
用户模型 → 高层语义层 → 算子语义层 → 调度语义层 → 内存语义层 → 硬件语义层 → 代码生成 → 可执行文件
ONNX → 图级 IR → 张量级 IR → 调度级 IR → 内存级 IR → 硬件级 IR
第 1 层:高层语义层(Top Dialect / 图级)
核心职责:忠实表达前端模型的计算图语义,保留完整的算子拓扑与控制流结构,不涉及底层实现细节。
代表 Dialect:StableHLO / MHLO / TOSA / ONNX
关键 Invariants(不变约束):
-
所有操作均以
tensor类型为操作数,无显式内存分配 -
算子语义与前端框架定义严格对齐,数值等价性可验证
-
保留完整的计算图结构(节点、边、控制依赖)
-
形状信息完整可推导(静态或动态 shape)
IREE 对应阶段:InputConversion 管线,负责将各前端输入统一转换为标准高层 IR,执行全局图优化(算子融合、常量折叠、形状推导、转置传播等)。
第 2 层:算子语义层(Linalg / 张量级)
核心职责:将高层算子解构为可组合的计算原语,以张量级别的循环嵌套语义表达计算逻辑,为调度优化提供统一的操作界面。
代表 Dialect:Linalg / LinalgExt / Tensor
关键 Invariants:
-
所有计算以
linalg.generic或命名算子(linalg.matmul、linalg.conv_2d等)表达,具备明确的迭代域与索引映射 -
操作数仍为
tensor类型(Value Semantics),无副作用 -
循环嵌套结构规范,可进行 tiling、fusion、 interchange 等变换
-
算子计算语义与硬件无关,纯算法表达
IREE 对应实践:IREE 约 99% 的计算调度基于 Linalg on Tensors 开展,仅少量特殊算子使用 LinalgExt 扩展。这一层是调度优化的核心舞台。
第 3 层:调度语义层(调度级)
核心职责:为计算算子附加并行划分、分块策略、执行顺序等调度信息,完成计算任务到硬件执行单元的映射规划。
代表 Dialect:IREE Flow / Stream / SCF + 调度属性
关键 Invariants:
-
计算被划分为独立的 dispatch region(调度区域),区域间无数据依赖冲突
-
每个 dispatch 内部的并行度、分块尺寸、向粒度明确
-
调度决策以属性(attribute)或嵌套结构形式附加,不破坏计算语义
-
跨 dispatch 的数据流与依赖关系可追踪
IREE 对应实践:
-
Flow Dialect:建模执行数据流与分区,创建 dispatch region,进行算子融合与任务划分
-
Stream Dialect:建模执行分区与调度,管理并发执行、资源分配与命令队列编排
-
调度决策不依赖具体硬件指令集,属于架构无关的宏观调度
第 4 层:内存语义层(Memref / 内存级)
核心职责:完成张量值语义到缓冲区内存语义的转换(Bufferization),显化内存分配、布局、别名与生命周期。
代表 Dialect:MemRef / Bufferization / Affine
关键 Invariants:
-
所有操作数从
tensor转为memref,具备明确的内存布局与地址空间 -
内存分配与释放成对出现,无内存泄漏
-
缓冲区别名关系清晰,读写依赖可静态分析
-
内存布局(stride、padding、memory space)明确可查
IREE 对应实践:在 dispatch region 内部执行 One-Shot Bufferization,将 Linalg on tensors 降阶为 Linalg on memrefs,随后展开为循环嵌套(SCF),并进行向量化与内存访问优化。
第 5 层:硬件语义层(硬件级)
核心职责:将通用计算表达映射为目标硬件的指令集模型或执行模型,引入硬件特有操作与约束。
代表 Dialect:
-
CPU:LLVM / X86Vector / ArmSME
-
GPU:SPIR-V / NVVM / ROCDL
-
专用加速器:自定义 Hardware Dialect
-
抽象层:IREE HAL Dialect
关键 Invariants:
-
操作集合限定在目标硬件支持的范围内
-
向量宽度、寄存器组织、内存层级与硬件规格匹配
-
硬件特有约束(如对齐、bank 冲突、指令延迟)可被 verifier 检查
-
可直接翻译为目标汇编 / 字节码
IREE 对应实践:
-
HAL Dialect:硬件抽象层,统一描述可执行文件、命令缓冲区、事件、内存分配等运行时概念
-
各后端独立降阶路径:LLVM(CPU)、SPIR-V(Vulkan)、NVVM(CUDA)、VMVX(可移植向量解释器)
第 6 层:代码生成层(CodeGen)
核心职责:将硬件语义 IR 翻译为目标可执行代码,经过汇编 / 链接生成最终二进制。
输出产物:
-
源文件:
.c/.ll/.spvasm/ PTX 汇编 -
目标文件:
.o/.cubin/.spv -
可执行文件:ELF / 扁平缓冲区模块(.vmfb)
IREE 对应实践:
-
设备端 kernel:经 LLVM/SPIR-V 等编译为机器码
-
主机端调度逻辑:编译为 VM 字节码,由 IREE 运行时解释执行
-
最终打包为
.vmfb(VM FlatBuffer)统一部署格式
三、六大设计原则
1. 每层职责单一
每一层只聚焦一个抽象维度的问题,不跨层承担职责:
-
高层不关心内存布局,底层不关心算子业务语义
-
调度层只做任务划分,不修改计算逻辑
-
内存层只管理缓冲区,不引入新的计算语义
IREE 体现 :Dialect 目录严格按职责划分(InputConversion/、Codegen/、Dialect/Flow/、Dialect/HAL/、Dialect/VM/),每个 dialect 仅维护自身语义范畴内的操作与类型。
2. 向下单向流动
信息只能从高层向底层逐层降阶,不允许逆向依赖:
-
低层可以引用高层的分析结果,但高层 pass 不得依赖低层 dialect
-
每层转换是单调的:信息只增不减,语义只细化不丢失
-
避免跨层 "抄近道",保证架构的可演进性
3. 每层有明确的 Invariants 与 Verifier
每一层都定义了该层必须始终成立的约束条件,由 MLIR 的 Operation Verifier 自动检查:
-
类型约束:操作数 / 结果的类型、形状、秩
-
结构约束:区域嵌套规则、后继块数量
-
语义约束:属性取值范围、内存空间合法性
-
Pass 执行前后 invariants 保持成立,可用于回归验证
这一机制使得每层都是 "自验证" 的,错误在最早出现的层级被捕获,而不是传导到下游引发难以调试的问题。
4. 转换 Pass 边界清晰
Pass 是层与层之间的转换单元,遵循以下规范:
-
命名规范 :
ConvertXToY模式明确标识源与目标 dialect -
输入输出明确:每个 pass 声明输入 dialect 集合与输出 dialect 集合
-
可组合性:pass 可按 pipeline 编排,支持插入自定义 pass
-
幂等性与可重入:canonicalization 模式下重复执行不产生副作用
IREE 体现 :编译管线被拆分为 InputConversionPipeline、FlowTransformPassPipeline、StreamTransformPassPipeline、HALTransformPassPipeline、VMTransformPassPipeline 等独立阶段,边界清晰,可单独调试与替换。
5. 调试友好
分层架构天然支持调试能力:
-
每层 IR 均可独立 dump,便于定位问题出在哪一次降阶
-
MLIR 提供
-print-ir-after-all、-print-ir-before-all等调试标志 -
每层 invariants 出错即停,错误信息精确到操作级别
-
IREE 提供
--mlir-print-ir-after-failure等工具,失败时自动留存现场 IR
6. 文档化、规范化、模板化、流程化
文档化 :每个 dialect 有官方 Rationale 与操作语义文档,每个 pass 有功能说明与示例
规范化 :命名、目录结构、ODS(Operation Definition Specification)定义统一规范
模板化 :Pass 编写、Dialect 定义、转换模式均有固定模板与脚手架
Agent 化 :结构化的 IR 与 pass 管线便于自动化工具(如搜索调度、自动降阶)进行程序化操作
流程化:编译管线可配置、可复用,支持以 pipeline 配置文件驱动编译流程
IREE 体现:所有内部 dialect 均有自动生成的参考文档;Codegen 采用可插拔的编译策略配置;调度管线支持通过编译标志灵活组合。
四、IREE 完整编译管线映射
| 阶段 | 核心 Dialect | 主要工作 | 对应管线 |
|---|---|---|---|
| 输入导入 | StableHLO / TOSA / Torch | 前端模型转 MLIR,图优化 | InputConversion |
| 全局优化 | StableHLO + Tensor | 常量折叠、形状推导、算子融合 | global-optimization |
| 流划分 | Flow | 划分 dispatch region,数据流建模 | flow |
| 流调度 | Stream | 并发调度、资源分配、命令编排 | stream |
| 算子调度 | Linalg + SCF | Tiling、Fusion、Vectorize | dispatch-creation / codegen |
| 内存降阶 | MemRef + Bufferization | 缓冲区分配、布局转换 | executable-sources |
| 硬件映射 | HAL + 后端 dialect | 硬件抽象、目标指令集降阶 | executable-targets |
| 代码生成 | LLVM / SPIR-V / VMVX | 汇编、链接、生成二进制 | 各后端 codegen |
| 宿主编排 | VM | 主机控制流字节码生成 | vm |
五、总结
这套层次化规范从工程上解决了 AI 编译器的三大核心难题:
-
复杂度可控:将 "模型→硬件" 的巨大语义鸿沟拆解为六次小跨越,每层复杂度在人力可掌握范围内
-
可扩展性强:新增前端只需对接高层 dialect,新增硬件只需扩展底层 codegen,中间层复用
-
质量可保障:每层 invariants + verifier 构成纵深防御体系,配合逐层 dump 调试能力,显著降低排错成本
IREE 作为该方法论的标杆实践,证明了基于 MLIR 分层降阶的架构能够在多前端、多后端的复杂场景下,同时保证编译效率、运行性能与工程可维护性。