📅 创建时间:2026-06-03 🏷️ 标签:#MLIR #Dialect #方言 #Linalg #SCF #Affine #IREE #ODS #重用力
📚 前置知识:[[02-ir-fundamentals]](IR 基础) 📚 相关知识:[[03-graph-representation]](计算图表示)[[25-mlir-llvm]](MLIR-LLVM 集成)
MLIR 架构、方言与渐进式降级 / MLIR Architecture, Dialects, and Progressive Lowering
┌──────────────────────────────────────────────────────────────────────────────┐
│ 🎯 场景:为什么 IREE 能用同一个 IR 编译 PyTorch 和 TensorFlow 模型 │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ 你在研究 IREE(一个基于 MLIR 的推理引擎),发现它能同时: │
│ │
│ ✅ 把 TensorFlow 模型 lower 到 Linalg Dialect │
│ ✅ 把 PyTorch 模型 lower 到 Linalg Dialect │
│ ✅ 最终 lower 到 LLVM Dialect,运行在 x86、ARM、RISC-V 上 │
│ │
│ 但 TensorFlow 和 PyTorch 的算子完全不同, │
│ 为什么能用同一个中间表示?Dialect 到底是什么? │
│ │
└──────────────────────────────────────────────────────────────────────────────┘第1节:MLIR 之前的问题——每个框架维护自己的编译器
1.1 深度学习编译器的碎片化
在 MLIR 出现之前(大约 2019 年之前),每个深度学习框架都维护自己的编译器栈:
┌─────────────────────────────────────────────────────────────────────────────┐
│ 深度学习编译器碎片化时代 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ TensorFlow PyTorch MXNet │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ TF/XLA │ │ Torch │ │ NNVM │ │
│ │ │ │ Script │ │ /TVM │ │
│ └────┬───┘ └────┬───┘ └────┬───┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │TF/Lite │ │Mobile │ │ TVM │ │
│ │Compiler│ │Support │ │Runtime │ │
│ └────────┘ └────────┘ └────────┘ │
│ │
│ 问题:每个框架都要独立实现: │
│ ❌ 算子 lowering │
│ ❌ 优化通道(pass) │
│ ❌ 目标代码生成 │
│ ❌ 硬件支持 │
│ │
└─────────────────────────────────────────────────────────────────────────────┘1.2 每个框架的问题
| 框架 | 编译器 | 问题 |
|---|---|---|
| TensorFlow | XLA | 维护两套编译器(TF/XLA 和 TFLite),代码重复 |
| TensorFlow Lite | TFLite | 只有有限的操作支持,自定义算子困难 |
| PyTorch | Caffe2 → ONNX | 转换路径不统一,精度丢失 |
| MXNet | NNVM → TVM | 影响力小,生态有限 |
| ONNX | 各框架实现 | opset 版本混乱,行为不一致 |
"""
具体问题的例子:实现一个 Conv + ReLU 融合
在 TensorFlow/XLA 中:
需要在 XLA 的 HLO 级别实现融合
代码位置:tensorflow/compiler/jit/
在 TensorFlow Lite 中:
需要在 TFLite 的 operator 级别实现
代码位置:tensorflow/lite/kernels/
在 PyTorch 中:
需要在 ATen/核心库级别实现
代码位置:aten/src/ATen/native/
同一个优化逻辑,需要在 3 个不同的地方实现!
而且实现语言也不同(XLA 用 C++, TFLite 用 C++, ATen 用 C++)
但核心逻辑是类似的。
"""第2节:MLIR 的核心洞察——方言作为可扩展的类型化 IR
2.1 MLIR 的设计哲学
MLIR(Multi-Level Intermediate Representation)由 Chris Lattner 在 2019 年提出,核心洞察是:
方言(Dialect)作为可扩展的类型化 IR,允许不同抽象级别的表示共存于同一个系统中。
"""
MLIR vs 传统 IR 的对比
传统 IR(如 LLVM IR):
只有一种表示方式
从高级语言到机器码需要一次"大跳跃"
所有信息都被 lowered 到同一种形式
MLIR:
多种方言共存于同一系统
每个方言代表一个抽象级别
可以逐步 lower(多级lowering)
信息可以在高级方言中保留更久
"""2.2 方言层级架构
┌─────────────────────────────────────────────────────────────────────────────┐
│ MLIR Dialect 层级 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 高层方言(接近算法) │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Linalg Dialect:线性代数操作(matmul, conv, pooling) │ │
│ │ "表示:通用批量矩阵乘法、卷积等计算" │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ SCF Dialect (Structured Control Flow):结构化控制流(if, for, while) │ │
│ │ "表示:循环嵌套、条件分支" │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 中层方言(接近硬件) │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Affine Dialect:仿射循环变换(loop tiling, fusion, unrolling) │ │
│ │ "表示:多面体分析,可优化的循环结构" │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ MemRef Dialect:内存引用(load, store, alloc, dealloc) │ │
│ │ "表示:内存访问模式" │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 低层方言(接近硬件/指令) │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Vector Dialect:向量操作(vector.load, vector.store, vector.contract) │ │
│ │ "表示:SIMD 指令" │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ LLVM Dialect:LLVM 类型化 SSA │ │
│ │ "表示:LLVM IR 的 MLIR 表示" │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ SPIR-V Dialect:GPU 着色器 │ │
│ │ "表示:Vulkan/OpenCL 着色器代码" │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘2.3 为什么方言是可扩展的
MLIR 允许用户定义自己的方言:
"""
定义一个新方言的动机
场景:硬件公司有自己的特殊加速器
问题:
- 加速器有自己的指令集
- 现有方言无法直接表示
- 但又希望利用 MLIR 的优化基础设施
解决方案:
定义一个 CustomHW Dialect
然后写 Lower 规则到 Linalg 或 LLVM
这样可以:
1. 利用 MLIR 的通用优化(死代码消除、公共子表达式等)
2. 在 CustomHW Dialect 级别做硬件特定的优化
3. 通过 Lower 到已知方言来实现代码生成
"""第3节:主要方言详解
3.1 Linalg Dialect(线性代数)
Linalg 是 MLIR 中最常用的高层方言,专门用于表示批量线性代数操作。
"""
Linalg Dialect 特点:
1. 面向批量操作:支持 batch matmul, grouped conv 等
2. 迭代空间显式:循环边界在操作参数中
3. 融合友好:容易识别可融合的操作
4. 硬件无关:可以 lower 到 CPU/GPU/NPU
示例:Linalg Matmul
linalg.matmul ins(%A, %B : memref<?x?xf32>, memref<?x?xf32>)
outs(%C : memref<?x?xf32>)
"""Linalg 操作列表:
| 操作 | 含义 | 数学表示 |
|---|---|---|
linalg.matmul | 矩阵乘法 | C[i][j] += A[i][k] * B[k][j] |
linalg.batch_matmul | 批量矩阵乘法 | BMM[b][i][j] += A[b][i][k] * B[b][k][j] |
linalg.conv_2d | 2D 卷积 | 标准 CNN 卷积 |
linalg.generic | 通用索引映射 | 任意逐元素操作 |
linalg.pooling_max | 最大池化 | window max |
linalg.pooling_sum | 求和池化 | window sum |
3.2 SCF Dialect(结构化控制流)
SCF(Structured Control Flow) 表示高级控制流结构。
"""
SCF Dialect 特点:
1. 结构化:if, for, while 都有良好的嵌套结构
2. SSA 形式:使用 SSA 值而非内存
3. 可转换:可以 lower 到 CFG(控制流图)或 Affine
示例:SCF If
scf.if %cond {
// then 分支
%0 = ...
} else {
// else 分支
%1 = ...
}
示例:SCF For 循环
scf.for %i = %c0 to %c100 step %c1 {
%val = memref.load %A[%i] : memref<100xf32>
...
}
"""3.3 Affine Dialect(仿射循环优化)
Affine 用于表示可以分析优化的循环结构,基于多面体模型(Polyhedral Model)。
"""
Affine Dialect 特点:
1. 循环边界是仿射表达式:i < 1024, i < N*M
2. 索引是仿射表达式:A[i, j] where i, j from loops
3. 支持复杂变换:
- loop tiling
- loop interchange
- loop fusion
- loop unrolling
示例:Affine For
affine.for %i = 0 to 100 {
affine.for %j = 0 to 100 {
%val = affine.load %A[%i, %j] : memref<100x100xf32>
...
}
}
"""3.4 Vector Dialect(向量操作)
Vector 表示 SIMD 和向量操作,是从高层到底层硬件的桥梁。
"""
Vector Dialect 特点:
1. 面向硬件向量宽度:vscale * 16 等
2. 支持向量 Load/Store
3. 支持向量点积、矩阵乘法等操作
示例:向量 Load
vector.load %A[%i, %j], %memref : memref<?x?xf32>, vector<8xf32>
示例:向量点积
vector.contract {
indexing_maps = [
affine_map<(i, j, k) -> (i, k)>,
affine_map<(i, j, k) -> (k, j)>,
affine_map<(i, j, k) -> (i, j)>
],
iterator_types = ["parallel", "parallel", "reduction"]
} %A, %B, %C : vector<4x8xf32>, vector<8x16xf32>, vector<4x16xf32>
"""3.5 LLVM Dialect(LLVM 集成)
LLVM 提供和 LLVM IR 的双向转换。
"""
LLVM Dialect 特点:
1. 类型映射:
- MLIR i32 → LLVM i32
- MLIR memref → LLVM {ptr, offset, shape}
- MLIR func → LLVM function
2. 支持 Intrinsics 调用
3. 可以 lower 到任何 LLVM 支持的目标
示例:LLVM Add
%result = llvm.add %a, %b : i64
"""3.6 方言对比表
| 方言 | 抽象级别 | 主要用途 | 优化机会 |
|---|---|---|---|
| Linalg | 高层 | 批量线性代数 | 算子融合、tiling |
| SCF | 中高层 | 结构化控制流 | 循环重排 |
| Affine | 中层 | 仿射循环 | 多面体优化 |
| MemRef | 中层 | 内存引用 | 内存布局、分配优化 |
| Vector | 中低层 | SIMD 操作 | 向量化、寄存器分配 |
| LLVM | 低层 | LLVM IR | 指令选择、寄存器分配 |
| SPIR-V | 低层 | GPU 着色器 | SPIR-V 优化 |
第4节:ODS(Operation Definition Specification)
4.1 ODS 简介
ODS 是用 TableGen 定义 MLIR 算子的规范。它提供了:
- 简洁的算子定义语法
- 自动生成 C++ 代码
- 自动生成 ODS 解析器和构建器
- 自动生成文档
"""
ODS 文件的后缀是 .td(TableGen 定义)
核心概念:
1. Operation:定义一个操作(类似于算子)
2. Operand:操作的输入
3. Result:操作的输出
4. Attribute:操作的属性(常量参数)
5. Region:操作包含的嵌套结构(用于 if/for 等)
"""4.2 ODS 定义示例:通用矩阵乘法
// ============================================================
// Linalg Matmul 的 ODS 定义(简化版)
// 完整定义在 llvm-project/mlir/include/mlir/Dialect/Linalg/IR/LinalgOps.td
// ============================================================
def Linalg_MatmulOp : Linalg_Op<"matmul", [
// 类型约束:输入必须是 MemRef
DeclareOpInterfaceMethods<MemoryEffectOpInterface>,
// 符号属性
AttrSizedOperandSegments
]> {
// 算子名称
let summary = "Generic matrix multiplication operation";
// 描述
let description = [{
Computes matrix multiplication: C = A * B
where A, B, C are 2D memrefs.
}];
// 输入操作数
// A: M x K 矩阵
// B: K x N 矩阵
arguments = (ins
Arg<MemRef, "left-hand side input">:$A, // MemRef<<?x?xf32>
Arg<MemRef, "right-hand side input">:$B, // MemRef<<?x?xf32>
Arg<MemRef, "result memref">:$C // MemRef<<?x?xf32>
);
// 输出结果
results = (outs);
// regions: matmul 内部没有 regions(简单操作)
regions = (region SizedRegion<0>:$body);
// 额外参数
let arguments = (ins
// 迭代空间维度(可选)
DefaultValuedAttr<I64Attr, "1">:$num_parallel_loops,
// 索引映射(可选)
StrAttr:$indexing_maps
);
// 自动生成方法
let hasCanonicalizer = 1;
let hasFolder = 1;
}
// ============================================================
// 实际的 Linalg 简化表示(MLIR Syntax)
// ============================================================
// 上面定义的算子在 MLIR 文件中这样使用:
// func @matmul(%A: memref<?x?xf32>, %B: memref<?x?xf32>, %C: memref<?x?xf32>) {
// linalg.matmul ins(%A, %B: memref<?x?xf32>, memref<?x?xf32>)
// outs(%C: memref<?x?xf32>)
// return
// }4.3 ODS 定义的流程
"""
ODS 定义 → C++ 代码生成的流程
1. 编写 .td 文件(TableGen 定义)
│
▼
2. TableGen 处理 .td 文件
│
▼
3. 生成 C++ 头文件(*.h.inc)
│ - Op 类声明
│ - Builder 方法声明
│ - 访问器声明
│
▼
4. 生成 C++ 实现(*.inc)
│ - Op 构造函数实现
│ - 解析器实现
│ - 打印器实现
│
▼
5. 在 C++ 中包含生成的头文件
│
▼
6. 编译进 MLIR
具体命令:
$ tablegen file.td -gen-op-decls -o file.h.inc
$ tablegen file.td -gen-op-defs -o file.inc
"""第5节:Dialect Conversion——方言之间的转换
5.1 Conversion Framework
MLIR 提供了 Conversion Framework 来处理方言之间的转换。
"""
Dialect Conversion 的核心概念:
1. Pattern-based Rewriting:
- 用 pattern 描述如何把一个 operation 转换成其他 operations
- 类似于 LLVM 的 pattern matching
2. Type Conversion:
- 当 lower 时,类型可能需要转换
- Conversion Framework 会自动处理
3. Legality:
- 可以标记某些 operations 是非法的(illegal)
- 必须被 lower 到其他 operations
- 框架会自动检查是否所有 illegal operations 都被转换
"""5.2 Conversion 示例:Linalg → Affine
"""
Linalg 到 Affine 的转换示例
原始 Linalg IR:
#accesses = [
affine_map<(i, j, k) -> (i, k)>, // A[i, k]
affine_map<(i, j, k) -> (k, j)>, // B[k, j]
affine_map<(i, j, k) -> (i, j)> // C[i, j]
]
#attr = affine_map<(i, j, k) -> (i, j, k)>
linalg.generic {
indexing_maps = #accesses,
iterator_types = ["parallel", "parallel", "reduction"]
} ins(%A, %B: memref<?x?xf32>, memref<?x?xf32>)
outs(%C: memref<?x?xf32>) {
^bb0(%a: f32, %b: f32, %c: f32):
%0 = arith.mulf %a, %b : f32
%1 = arith.addf %c, %0 : f32
linalg.yield %1 : f32
}
Lower 后的 Affine IR:
affine.for %i = 0 to %M {
affine.for %j = 0 to %N {
%result = arith.constant 0.0 : f32
affine.for %k = 0 to %K {
%a = affine.load %A[%i, %k] : memref<?x?xf32>
%b = affine.load %B[%k, %j] : memref<?x?xf32>
%prod = arith.mulf %a, %b : f32
%result = arith.addf %result, %prod : f32
}
affine.store %result, %C[%i, %j] : memref<?x?xf32>
}
}
"""第6节:多级 Lowering 实例
6.1 端到端 Lowering 示例
┌─────────────────────────────────────────────────────────────────────────────┐
│ 端到端 Lowering:Linalg → LLVM │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Level 1: Linalg (高层抽象) │
│ ═══════════════════════════════════════════════════════════════ │
│ linalg.matmul ins(%A, %B: memref<128x64xf32>, memref<64x32xf32>) │
│ outs(%C: memref<128x32xf32>) │
│ │
│ ↓ Lowering using `linalg-to-affine` pass │
│ │
│ Level 2: Affine (循环层级) │
│ ═══════════════════════════════════════════════════════════════ │
│ affine.for %i = 0 to 128 { │
│ affine.for %j = 0 to 32 { │
│ %0 = arith.constant 0.0 : f32 │
│ affine.for %k = 0 to 64 { │
│ %a = affine.load %A[%i, %k] │
│ %b = affine.load %B[%k, %j] │
│ %0 = arith.addf %0, (%a * %b) │
│ } │
│ affine.store %0, %C[%i, %j] │
│ } │
│ } │
│ │
│ ↓ Lowering using `affine-to-scf` pass │
│ │
│ Level 3: SCF (显式循环) │
│ ═══════════════════════════════════════════════════════════════ │
│ scf.for %i = 0 to 128 { │
│ scf.for %j = 0 to 32 { │
│ ... // 类似 Affine,但没有仿射约束 │
│ } │
│ } │
│ │
│ ↓ Lowering using `scf-to-cf` pass │
│ │
│ Level 4: CF + MemRef (控制流 + 内存) │
│ ═══════════════════════════════════════════════════════════════ │
│ llvm.mlir.enter %i.0, ^bb1 │
│ llvm.br ^bb2 │
│ llvm.load %A_ptr │
│ ... │
│ │
│ ↓ Lowering using `func-to-llvm` + `memref-to-llvm` passes │
│ │
│ Level 5: LLVM Dialect │
│ ═══════════════════════════════════════════════════════════════ │
│ llvm.call @matmul_f32(%A_ptr, %B_ptr, %C_ptr, %M, %N, %K) │
│ │
│ ↓ Lowering using `llvm-to-llvm` + `llvm-to-asm` │
│ │
│ Level 6: 机器码 │
│ ═══════════════════════════════════════════════════════════════ │
│ vmovups %xmm0, (%rax) # 加载数据 │
│ vfmadd231ps %xmm0, (%rbx) # fused multiply-add │
│ ... │
│ │
└─────────────────────────────────────────────────────────────────────────────┘6.2 每个层级的优化机会
| 层级 | 可用优化 | 示例 |
|---|---|---|
| Linalg | 算子融合、tiling、padding | Conv+ReLU 融合成单kernel |
| Affine | 循环变换、交换、分块 | 将 j 循环提到 i 循环内 |
| SCF | 循环展开、预取 | 减少分支开销 |
| MemRef | 内存布局、分配优化 | 内存池化 |
| Vector | 向量化、混合宽度 | 利用 SIMD |
| LLVM | 指令选择、寄存器分配 | 更好的寄存器分配 |
第7节:MLIR 在实际项目中的应用
7.1 IREE——端到端推理引擎
IREE(Intermediate Representation Execution Environment) 是 Google 基于 MLIR 的端到端推理引擎。
"""
IREE 的架构:
┌─────────────────────────────────────────────────────────────────────────────┐
│ IREE 架构 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 输入模型 │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │TensorFlow│ │ PyTorch │ │ ONNX │ │ TFLite │ │
│ │Models │ │Models │ │ Models │ │ Models │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ IREE Input Dialect │ │
│ │ (统一的、面向模型的中间表示) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Linalg Dialect │ │
│ │ (批量线性代数操作) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Vector + SCF Dialects │ │
│ │ (向量化 + 结构化控制流) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ LLVM Dialect │ │
│ │ (LLVM 类型的 SSA) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Runtime │ │
│ │ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │ │
│ │ │ x86 │ │ ARM │ │RISC-V │ │ Vulkan │ │ │
│ │ │ VMApp │ │ VMApp │ │ VMApp │ │ SPIR-V │ │ │
│ │ └────────┘ └────────┘ └────────┘ └────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
"""7.2 IREE 的编译流程
"""
IREE 编译 PyTorch 模型的完整流程:
1. PyTorch → IREE Input
┌─────────────────────────────────────────────────────────────────────┐
│ import iree.compiler.torch as iree_torch │
│ │
│ # PyTorch 模型 │
│ model = torchvision.models.resnet18(pretrained=True) │
│ model.eval() │
│ │
│ # 编译到 IREE │
│ binary = iree_torch.compile_file( │
│ "resnet18.pt", │
│ output_format="vm-bytecode-fb", │
│ import_args=["--torch-backend-type=vmfb"] │
│ ) │
└─────────────────────────────────────────────────────────────────────┘
2. IREE Input → Flow Dialect
- 简化模型结构
- 提取函数签名
- 处理动态 shape
3. Flow Dialect → Stream Dialect
- 资源流化
- 内存规划
4. Stream Dialect → HAL Dialect
- 设备分配
- 命令缓冲
5. HAL Dialect → Executable Dialect
- 具体化操作
- 绑定内存
"""
print("IREE 的核心优势:")
print()
print("1. 统一表示:")
print(" PyTorch、TensorFlow、ONNX 模型都可以编译到同一个中间表示")
print()
print("2. 多目标支持:")
print(" - VMFB(虚拟机字节码):通用部署")
print(" - CUDA/ROCm:GPU 支持")
print(" - Vulkan SPIR-V:移动端 GPU")
print(" - LLVM:CPU 后端(x86, ARM, RISC-V)")
print()
print("3. 分层优化:")
print(" - 高层优化(Linalg):算子融合、tiling")
print(" - 中层优化(Affine/Vector):循环优化、向量化")
print(" - 低层优化(LLVM):指令选择、寄存器分配")7.3 其他使用 MLIR 的项目
| 项目 | 用途 | MLIR 层级 |
|---|---|---|
| TensorFlow/XLA | Google 深度学习编译器 | HLO → LMHLO → LLVM |
| PyTorch (Torch-MLIR) | PyTorch 到 MLIR | Torch IR → Linalg |
| MLIR-AIE | AMD AI Engine | Python → Linalg → Vector |
| TOSA | 标准算子接口 | Model → TOSA → Linalg |
| SPIR-V Tools | GPU 着色器 | Linalg → SPIR-V |
| Flang | Fortran 编译器 | Fortran → HLFIR → MLIR |
第8节:为什么 MLIR 重要
8.1 MLIR 解决了什么问题
"""
MLIR 之前的编译器架构(以 TensorFlow 为例):
TensorFlow → XLA HLO ──┬──→ CPU (LLVM)
│
└──→ GPU (CUDA/NVPTX)
TensorFlow → TFLite ───→ FlatBuffer
问题:
1. XLA 和 TFLite 是完全独立的代码库
2. 优化不能共享
3. 新硬件支持需要在两个地方实现
MLIR 之后的架构:
TensorFlow → TF Dialect → XLA HLO ─┬──→ LLVM → CPU
│ │
│ └──→ NVPTX → CUDA
│
└──→ TFLite Dialect → Linalg ──→ LLVM → CPU
│
└──→ SPIR-V → Vulkan
优势:
1. 共享优化基础设施
2. 可以在不同层级做优化
3. 新硬件支持只需要 lower 到 LLVM
"""8.2 MLIR 的设计原则
| 原则 | 说明 | 示例 |
|---|---|---|
| 可扩展 | 允许定义新的方言 | CustomHW Dialect |
| 分层 | 多种抽象层级共存 | Linalg → Affine → LLVM |
| SSA + 类型 | 所有值都有类型 | i32, memref<f32> |
| Region | 操作可以嵌套 | scf.if, scf.for |
| Pattern Rewriting | 基于规则的转换 | linalg-fusion pass |
| Pass Infrastructure | 标准化优化管道 | -convert-linalg-to-affine |
升华
┌──────────────────────────────────────────────────────────────────────────────┐
│ 📚 MLIR 架构:核心原则 │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. **方言是 MLIR 的核心抽象**:每种方言代表一个计算抽象级别 │
│ │
│ 2. **多级 Lowering 是 MLIR 的核心思想**:逐步从高级表示 lower 到硬件 │
│ │
│ 3. **可扩展性是 MLIR 的灵魂**:可以定义新方言来解决特殊问题 │
│ │
│ 4. **共享优化基础设施**:不同框架/硬件可以用相同的优化 pass │
│ │
└──────────────────────────────────────────────────────────────────────────────┘"AI 可查 vs 必须理解"清单
必须理解(不理解就等于不会):
- 🔴 方言层级的概念:Linalg(高层)、Affine(中层)、LLVM(低层)分别代表什么
- 🔴 多级 Lowering 的思想:为什么需要逐步 lower,而不是一步到位
- 🔴 ODS 的基本用途:TableGen 定义算子、自动生成代码的概念
- 🔴 MLIR 的核心问题:解决了深度学习编译器碎片化的什么问题
- 🔴 IREE 的架构:输入模型如何 lower 到不同硬件
AI 可查(知道去哪查就行):
- ✅ 具体方言的语法:MLIR Syntax 细节,用到时查文档
- ✅ ODS 定义的完整语法:TableGen 语法很复杂,完整参考在 LLVM 文档
- ✅ 特定 pass 的选项:
--linalg-fusion有哪些参数 - ✅ 特定硬件的后端细节:不同硬件有不同限制
- ✅ MLIR C++ API:操作 MLIR 库的细节
学习状态:🟡 开始学习