MLIR(Multi-Level Intermediate Representation)
3 秒看懂
MLIR 是编译器基础设施中的”通用中间语言框架”——它不只是一张 IR,而是一套让不同抽象层级、不同领域的编译方言(Dialect)在同一张图里共存、逐步降级(Progressive Lowering)到硬件指令的机制。 它是 LLVM 项目的一部分,正在成为 AI 编译器栈的核心枢纽。
3 分钟产业解释
问题:AI 编译器的”巴别塔”
在 MLIR 出现之前,AI 编译器领域面临严重的中间表示(Intermediate Representation, IR)碎片化问题:
- 框架层有自己的图表示(TensorFlow GraphDef、PyTorch 的 FX Graph、ONNX)
- 优化层需要高层语义(张量形状、广播规则、融合策略)
- 代码生成层需要低层语义(循环嵌套、内存布局、向量化指令)
- 每个硬件后端(NVIDIA GPU、Google TPU、各路 AI ASIC)都有自己的一套 IR 或 DSL
结果是:每个框架 × 每个硬件 ≈ 一个独立的编译器栈,生态维护成本呈乘法爆炸。
MLIR 的解法:Dialect 框架
MLIR 的核心洞察是:不要试图定义一张”万能 IR”,而是提供一套让所有人都能定义自己 IR 层级的框架。
- 框架开发者定义 高层 Dialect(如
tosa、stablehlo、torch) - 优化开发者定义 中层 Dialect(如
linalg、tensor、memref) - 硬件后端定义 低层 Dialect(如
gpu、spirv、llvm、amdgpu) - 通过 Progressive Lowering(逐步降级)把高层图一路变换到目标硬件指令
这让”框架到硬件”的编译路径变成了可组合、可复用的 Dialect 变换链条,而非每个 pair 写一个完整编译器。
产业位置
MLIR 已成为 AI 编译器生态的事实标准基础设施:
| 采用方 | 角色 |
|---|---|
| TensorFlow/XLA | 将 TF 图转为 StableHLO(MLIR Dialect),再经由 XLA 编译器降级到目标代码 |
| PyTorch 2.0+ | torch.compile 默认使用 TorchInductor 后端生成 Triton/C++/CUDA 代码;社区项目 Torch-MLIR 可将 FX 图编译至 MLIR(非默认路径) |
| JAX/XLA | 通过 StableHLO Dialect 接入 MLIR |
| ONNX-MLIR | 将 ONNX 模型直接编译为 MLIR |
| 各路 AI 芯片公司 | 大量厂商自定义 Dialect 接入 MLIR 生态 |
通俗类比:如果说 LLVM IR 是 CPU 编译领域的”通用汇编”,那么 MLIR 尺是试图成为 AI + 异构计算领域的”通用降级框架”——而且它把 LLVM IR 作为其最低层 Dialect 之一纳入其中。
15 分钟专家深入
1. 设计哲学:为什么 LLVM IR 不够用
LLVM IR 是一张单一抽象层级的 IR——它接近 SSA 形式的机器指令,非常擅长标量/向量级优化。但 AI 编译的挑战在于:
| 需求 | LLVM IR 的局限 |
|---|---|
| 张量级语义(形状推导、广播、数据布局变换) | LLVM IR 不感知”张量”,只有扁平的指针/数组,无法表达张量的形状、布局等语义信息 |
| 结构化控制流(for 循环嵌套、并行模式) | LLVM IR 只有 br/phi,丢失了循环结构,优化需重建 |
| 多级抽象 | 一张 IR 只能覆盖一个层级,无法”逐步降级” |
| 领域可扩展性 | LLVM IR 是固定的指令集,新领域只能用 intrinsic hack |
| 多硬件目标 | LLVM 主要面向 CPU/GPU,DSA 的特殊语义难以表达 |
MLIR 的回答是:让 IR 本身成为可扩展的框架——通过 Dialect 机制,每个领域可以注册自己的操作(Operation)、类型(Type)、属性(Attribute),并且不同 Dialect 可以在同一张 IR 图中共存。
2. 核心数据模型
MLIR 的 IR 结构是嵌套的:
Module(模块)
└── Operation(操作) ← MLIR 的基本计算单元
├── Dialect + OpName ← 归属哪个方言、叫什么名
├── Operands(操作数) ← 来自其他 Op 的 SSA 值
├── Results(结果) ← 产生新的 SSA 值
├── Attributes(属性) ← 编译期常量:shape、dtype、fused_op 等
└── Regions(区域) ← 嵌套的子图(可选)
└── Block(基本块)
└── 更多 Operation...
关键点:
- Operation 是泛化的:没有固定的指令集,不同 Dialect 定义不同的 Op
- SSA 值贯穿全图:所有 Op 的结果都是 SSA 值,数据依赖关系天然清晰
- Region/Block 的嵌套使得结构化控制流(scf.for、scf.if)可以被精确表示,优化 pass 可以直接操作循环结构而无需重建
3. Dialect 体系
Dialect 是 MLIR 的核心扩展机制。一个 Dialect 定义了一组 Operations、Types 和 Attributes。典型的分层关系(从高到低,非唯一路径):
┌─────────────────────────────────────────────────────────┐
│ 高层框架 Dialect │
│ stablehlo / torch / tosa / onnx / linalg-on-tensors │
│ (张量级语义, 形状信息完整) │
└────────────────────────┬────────────────────────────────┘
│ ← Pass: tensor → buffer 化、算子融合
┌────────────────────────▼────────────────────────────────┐
│ 中层 Dialect │
│ linalg / tensor / memref / scf / affine / arith │
│ (结构化控制流 + 显式内存管理) │
└────────────────────────┬────────────────────────────────┘
│ ← Pass: 向量化、并行化、循环变换
┌────────────────────────▼────────────────────────────────┐
│ 低层 Dialect │
│ vector / gpu / nvgpu / amdgpu / spirv / llvm │
│ (接近目标硬件) │
└────────────────────────┬────────────────────────────────┘
│ ← 最终代码生成
┌────────────────────────▼────────────────────────────────┐
│ LLVM IR → 机器码 (CPU/GPU) │
│ 或 SPIR-V → 驱动 (Vulkan) │
└─────────────────────────────────────────────────────────┘
Progressive Lowering 的核心是:每一步只降一个抽象层级,每一步的变换是局部且可验证的。
4. 关键 Dialect 详解
linalg(Linear Algebra Dialect)
- 表达结构化的线性代数计算:matmul、conv、generic(通用张量运算)
- 关键特性:Linalg Op 的计算体(Region)和迭代空间(Indexing Map)是分离的,便于 Tiling(分块)、Fusion(融合)、Vectorization(向量化)
- 是 MLIR 中从”张量世界”到”循环世界”的关键桥梁
affine(Affine Dialect)
- 表达仿射循环嵌套和访存
affine.for、affine.if、affine.load、affine.store- 支持多面体(Polyhedral)分析:依赖分析、循环变换、分块——这些在 AI 编译器中是算子融合和调度优化的核心数学工具
scf(Structured Control Flow)
- 比 affine 更通用的结构化控制流:
scf.for、scf.while、scf.if、scf.forall(并行循环) - 不要求索引表达式是仿射的,适用于更广泛的场景
tensor / memref
tensor:不可变的多维数组(Value Semantics),在高层使用memref:带形状和布局信息的内存引用(Reference Semantics),在低层使用- 从 tensor 到 memref 的 Bufferization 是 MLIR 编译流程中的关键步骤
vector(Vector Dialect)
- 表达向量化操作,最终可映射到 SIMD/SIMT 指令
- 与
llvmdialect 对接,生成 LLVM IR 向量指令
gpu / 硬件特定 Dialect
gpu:通用 GPU 概念(kernel launch、thread/block index、barrier)nvgpu:NVIDIA 特定操作(WGMMA、TMA、async copy 等,随 NVIDIA 架构演进不断扩展 [来源:MLIR 上游代码])amdgpu:AMD GPU 特定操作spirv:直接生成 SPIR-V 中间表示
5. Pass Infrastructure
MLIR 的优化通过 Pass(变换)实现,每个 Pass 通常在特定 Dialect 间做变换:
- Pattern Rewriting:基于图模式匹配的重写规则(
OpRewritePattern),是 MLIR 中最常用的变换机制 - Conversion Pattern:跨 Dialect 的类型转换和操作降级(
TypeConverter+ConversionPattern) - Analysis:Preserved analyses 用于跨 pass 传递信息(如 Liveness、Dominance)
典型的编译流程(以一个简单的 matmul 为例):
stablehlo.dot_general // 高层语义: "做矩阵乘法, 左边 shape [M,K], 右边 [K,N]"
│
▼ (stablehlo → linalg)
linalg.matmul // 结构化线性代数: 三层循环 + 乘累加
│
▼ (linalg → linalg, tiling + fusion)
linalg.matmul (tiled) // 分块: 外层循环 + 内层小 matmul, 便于适配寄存器/SRAM 容量
│
▼ (linalg → vector + scf)
scf.for { vector.contract } // 向量化: 内层变为 SIMD 指令
│
▼ (scf + vector → gpu)
gpu.launch { ... } // 映射到 GPU 线程层级
│
▼ (gpu → nvgpu → llvm)
LLVM IR / PTX // 最终代码
6. TableGen 与 Dialect 定义
MLIR 使用 ODS(Operation Definition Specification) + TableGen 来声明式地定义 Dialect 中的 Operation:
def MatmulOp : Op<Linalg_Dialect, "matmul",
[Pure, AllTypesMatch<["lhs", "rhs", "result"]>]> {
let arguments = (ins Tensor:$lhs, Tensor:$rhs);
let results = (outs Tensor:$result);
// ... verifier, printer, parser 等自动生成
}
这使得新 Dialect 的定义极为高效:开发者只需声明操作的签名、语义约束和属性,框架自动生成 C++ 类、Python 绑定、验证器、序列化/反序列化、文档等基础设施。
7. 与传统编译器 IR 的对比
| 特性 | LLVM IR | GCC GIMPLE | MLIR |
|---|---|---|---|
| 抽象层级 | 低(接近汇编) | 中低 | 任意层级(可扩展) |
| 指令集 | 固定 | 固定 | Dialect 可扩展 |
| 结构化控制流 | 否(CFG) | 否(CFG) | 是(scf/affine) |
| 类型系统 | 固定(标量/指针/向量) | 固定 | 可扩展 |
| 嵌套 IR | 不支持 | 不支持 | Region 嵌套 |
| 主要领域 | CPU 通用编译 | CPU 通用编译 | AI + 异构计算 |
技术原理(最深)
核心机制一:Progressive Lowering
Progressive Lowering 是 MLIR 最核心的设计范式。其要点:
- 每个 Pass 只负责降一个抽象层级:例如从
stablehlo到linalg,或从linalg到scf + vector - 不同层级可以共存:一张 IR 图中可以同时包含
linalg.matmul(高层)和affine.for(中层),只要它们通过 Region 边界隔离 - 变换链可组合、可插入:新硬件后端只需在合适的位置插入自己的 Dialect 和变换 Pass,无需重写整个编译器
// MLIR 中一张混合层级的 IR 示例(伪代码)
func.func @forward(%arg0: tensor<4x8xf32>, %arg1: tensor<8x16xf32>) -> tensor<4x16xf32> {
%0 = stablehlo.dot_general %arg0, %arg1 // ← 高层 Dialect 还没降
{ lhs_contracting_dims = [1], rhs_contracting_dims = [0] }
%1 = stablehlo.tanh %0 // ← 激活函数
return %1 : tensor<4x16xf32>
}
// 降级后 (stablehlo → linalg → scf):
func.func @forward(%arg0: memref<4x8xf32>, %arg1: memref<8x16xf32>, %out: memref<4x16xf32>) {
affine.for %i = 0 to 4 {
affine.for %j = 0 to 16 {
%acc = affine.for %k = 0 to 8 iter_args(%cst = %c0f) -> f32 {
%a = affine.load %arg0[%i, %k]
%b = affine.load %arg1[%k, %j]
%prod = arith.mulf %a, %b
%sum = arith.addf %cst, %prod
affine.yield %sum
}
%tanh = math.tanh %acc
affine.store %tanh, %out[%i, %j]
}
}
return
}
核心机制二:Dialect Conversion Framework
跨 Dialect 降级使用 Dialect Conversion 框架,其内部机制:
输入: 混合 Dialect 的 IR Graph (legal + illegal ops)
│
▼
┌─────── Analysis ───────┐
│ 标记哪些 Op 是 legal 的 │
│ 哪些需要被转换 │
└───────────┬────────────┘
│
▼
┌─── Partial Lowering ───┐
│ 一次只处理可转换的 Op │
│ 未就绪的 Op 保持不变 │ ← 关键: 支持"部分降级"
└───────────┬────────────┘
│
▼
┌── Type Materialization ──┐
│ 在 legal/illegal 区域边界 │
│ 插入类型转换操作 │
└───────────┬──────────────┘
│
▼
全部 legal → 完成
这个框架保证了:
- 原子性:如果一个 Pattern 转换失败,可以回滚
- 逐步收敛:多次迭代直到没有 illegal op
- TypeConverter 处理不同类型间的桥接(如
tensor<f32>↔memref<f32>)
核心机制三:Structured Ops 与 Tiling/Fusion
这是 MLIR 在 AI 编译中最强大的能力之一。以 linalg.matmul 为例:
linalg.matmul {
indexing_maps = [
affine_map<(m, n, k) -> (m, k)>, // A 的索引
affine_map<(m, n, k) -> (k, n)>, // B 的索引
affine_map<(m, n, k) -> (m, n)> // C 的索引
],
iterator_types = ["parallel", "parallel", "reduction"],
ins(%A, %B : tensor<128x256xf32>, tensor<256x64xf32>)
outs(%C : tensor<128x64xf32>)
^bb0(%a: f32, %b: f32, %c: f32):
%prod = arith.mulf %a, %b
%sum = arith.addf %c, %prod
linalg.yield %sum
}
- Indexing Map 精确定义了每次迭代中操作数的访存模式
- iterator_types 标记哪些维度是
parallel(可并行),哪些是reduction(需要规约) - 基于这些信息,框架可以自动推导:
- Tiling 后子块的 shape
- 是否可以与相邻 Op 融合(共享数据是否在同一次遍历中产生和消耗)
- 向量化后内层循环的向量宽度
这正是 AI 编译器做自动算子融合的数学基础——多面体模型在 MLIR 中以可编程的方式嵌入。
核心机制四:Bufferization
从不可变的 tensor 语义到可变的 memref(内存分配 + 显式读写)的转换,称为 Bufferization:
- One-Shot Bufferize:MLIR 中的主要 Bufferization 分析,通过分析 use-def 链和 alias 关系,在整个函数级别做 tensor → memref 的转换
- 核心挑战:何时插入
memref.copy(确保 tensor 语义的不可变性被正确维护),何时可以原地写入(避免冗余拷贝) - Bufferization 的质量直接影响最终生成代码的内存效率
核心机制五:Transform Dialect(新范式)
MLIR 近年引入的 Transform Dialect 是一种新的编排变换的方式:
%matmul = transform.structured.match ops{["linalg.matmul"]} in %module
%tiled_l2, %loops:2 = transform.structured.tile %matmul [32, 32, 8]
%tiled_l1, %inner_loops:2 = transform.structured.tile %loops#1 [4, 4, 4]
transform.structured.vectorize %tiled_l1
- 与传统的 Pattern-based Pass 不同,Transform Dialect 让调度策略本身成为 IR
- 这使得搜索式编译器(如 AutoTVM / MetaSchedule 的思路)可以直接在 MLIR 中表达搜索空间
技术演进史
| 时间 | 事件 |
|---|---|
| ~2018 | Google 内部 Chris Lattner 团队启动 MLIR 项目(Lattner 此前创建了 LLVM 和 Swift) |
| 2019.04 | MLIR 代码开源到 GitHub(作为 LLVM monorepo 的子项目) |
| 2019.10 | MLIR 正式被 LLVM Foundation 接纳为 LLVM 孵化器项目 |
| 2020 | TensorFlow 团队开始将 XLA 的 HLO 改为 MLIR Dialect 表达 |
| 2021 | linalg dialect 成熟,成为结构化计算的核心表达;TOSA Dialect 被 Arm 提出并用于移动端 AI |
| 2022 | PyTorch 2.0 发布,torchdynamo + Torch-MLIR 将 MLIR 引入 PyTorch 生态;StableHLO 由 Google 提出,作为 JAX/TF 的高层 Dialect |
| 2023 | MLIR 在 LLVM 17/18 中持续成熟;Transform Dialect 推进;各 AI 芯片公司的 Dialect 大量涌现 |
| 2024+ | MLIR 已成为 AI 编译器的事实标准 IR 基础设施,IREE(Google 的端到端编译器)、Triton(OpenAI)、Mojo 等均基于 MLIR 构建 |
技术路线对比
AI 编译器 IR 方案对比
| 维度 | MLIR (LLVM) | TVM (Relay/tir) | Halide | XLA (HLO) | Triton IR |
|---|---|---|---|---|---|
| 核心思想 | 多层级可扩展 Dialect 框架 | 两层 IR(Relay 高层 + tir 低层)+ 自动调度 | 计算与调度分离 | 单一层级的张量 IR | 以程序块为单位的 GPU kernel IR |
| 可扩展性 | 极高(Dialect 机制) | 中(可定义新 Relay Op,但框架固定) | 低(面向图像处理) | 低(Google 自用,逐步开放) | 中(面向 GPU kernel) |
| 结构化控制流 | 原生支持 (scf/affine) | tir 中有循环,但抽象层级较固定 | 有(compute/schedule 分离) | 不完整 | 有(programmable scheduling) |
| 自动调度 | Transform Dialect(进行中)+ 外接搜索 | AutoTVM / MetaSchedule 成熟 | Halide autoscheduler | 自动(但较少外部可控) | Triton 语言级调度 |
| 社区生态 | LLVM 社区 + Google + 众多厂商 | Apache 社区(维护状态需关注) | 学术为主 | Google 主导 | OpenAI 主导 |
| 硬件覆盖 | 最广(CPU/GPU/TPU/ASIC/NPU) | 较广 | 主要 CPU/GPU | Google TPU + GPU | 主要 NVIDIA GPU |
| 成熟度 | 生产级(2024) | 生产级 | 生产级(图像领域) | 生产级(Google 内部) | 生产级(GPU kernel) |
注:Triton 自 2024 年起也在底层使用 MLIR(Triton dialect 基于 MLIR 构建),因此 MLIR 与 Triton 并非完全对立,而是层次互补。
上下游
上游(谁向 MLIR 输入)
AI 框架的计算图
├── TensorFlow Graph → StableHLO (MLIR Dialect)
├── PyTorch Model → Torch-MLIR (MLIR Dialect)
├── JAX Function → StableHLO (MLIR Dialect)
├── ONNX Model → ONNX Dialect (MLIR)
└── 用户自定义 DSL → 自定义 Dialect (MLIR)
下游(MLIR 向谁输出)
MLIR 最终降级目标
├── LLVM IR → x86/ARM/RISC-V 机器码 (CPU)
├── PTX / NVVM (NVIDIA GPU)
├── ROCm / AMDGPU Dialect (AMD GPU)
├── SPIR-V (Vulkan / OpenCL)
├── 自定义 ISA / 微码 (AI ASIC / NPU)
└── IREE Runtime (Google 的端到端部署方案)
工具链位置
[模型训练] → [模型导出 (ONNX/PT2)] → [前端接入 MLIR]
→ [高层 Dialect: stablehlo/torch]
→ [中层优化: tiling/fusion/vectorization/bufferization]
→ [低层 Dialect: gpu/nvgpu/llvm]
→ [代码生成: PTX/LLVM IR/自定义 ISA]
→ [运行时: IREE / CUDA Runtime / 自研 Runtime]
关键指标
| 指标 | 说明 | 量级/参考 |
|---|---|---|
| Dialect 数量 | MLIR 上游 + 孵化中的 Dialect | 上游官方约 30+ 个 Dialect(arith, func, scf, affine, linalg, tensor, memref, vector, gpu, nvgpu, amdgpu, spirv, llvm, tosa, stablehlo…),第三方难以精确统计 [来源:MLIR 上游代码仓库] |
| 采用的框架 | 主流 AI 框架中使用 MLIR 作为编译后端的 | TensorFlow, PyTorch, JAX, ONNX 均已接入 |
| 代码规模 | LLVM monorepo 中 mlir/ 目录 | 数十万行 C++ 代码 [估算:按 GitHub 仓库规模] |
| 社区活跃度 | LLVM 论坛中 MLIR 相关讨论 | LLVM Discourse 上 MLIR 是最活跃的板块之一 |
| 编译延迟 | MLIR 本身的 Pass 编译开销 | 取决于模型大小和 Pass 数量,业界有优化在进行中([未充分披露精确数据]) |
供需与市场数据
需求侧
- AI 编译器工程师是当前 AI 基础设施最稀缺的岗位之一。掌握 MLIR(特别是 Dialect 开发、Pass 编写、算子融合策略)的人才在全球范围内供不应求。
- 每个推出自研 AI 芯片的公司都需要为自己的硬件写编译器栈,而 MLIR 已成为事实标准入口——不接入 MLIR 生态 = 无法被主流框架支持。
- 云端训练和推理都需要编译器优化来提升有效算力利用率(MFU/MFU),MLIR 是实现这一目标的核心工具链。
供给侧
- MLIR 的核心开发由 Google、Intel、NVIDIA、AMD、Arm、Qualcomm、SiFive 等公司在 LLVM 社区中协作推进 [来源:LLVM 社区贡献者列表]。
- 中国厂商(华为、寒武纪、壁仞、燧原、摩尔线程等)均有基于 MLIR 的编译器团队,但具体 Dialect 设计多为自用,部分贡献回上游。
市场规模(间接估算)
MLIR 本身是开源基础设施(BSD/Apache 许可),不直接产生收入。但其支撑的 AI 编译器市场是整个 AI 芯片 + 云服务生态的使能层:
- AI 芯片市场规模:[根据多家行业报告估算] 2024 年全球 AI 加速器市场约 800-1000 亿美元,MLIR 的编译器栈是每颗芯片的必要软件配套。
- AI 编译器工具链软件市场(