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 編譯器工具鏈軟體市場(