first commit

This commit is contained in:
ylhan
2026-08-20 17:20:09 +08:00
commit 8049b6f58d
14 changed files with 1649 additions and 0 deletions
+174
View File
@@ -0,0 +1,174 @@
# 阶段 2:IREE 架构总览与完整管线 ✅
> 2026-08-14。用 `--mlir-print-ir-after-all` 追踪一个 linalg matmul 从输入到 VM 的完整变换,
> 把 231 个 pass 划分成 7 个阶段,建立 IREE 整体的心智模型。
## 1. 一句话总结
IREE 是一个**「图 → 异步调度 → 设备代码 → 字节码」的完整编译栈**,每一层都用 MLIR dialect 表达:
输入方言(linalg/tosa/stablehlo)→ Flow → Stream → HAL → VM,其中 codegen(可执行体 lowering)
在 Stream 和 HAL 之间发生。
## 2. 完整 pipeline 全景(231 个 pass → 7 个阶段)
| 阶段 | 关键 dialect | 代表性 pass | 职责 | TVM 对应 |
|---|---|---|---|---|
| A 输入 | (输入方言) + util + hal.tensor | `WrapEntryPointsPass`、`InlinerPass` | 统一入口、包 ABI(buffer_view) | 前端导入 |
| B 设备 | hal.device.target | `AssignLegacyTargetDevicesPass`、`MaterializeTargetDevicesPass` | 目标设备分配 | device plan |
| C Flow | **flow** | `FormDispatchRegionsPass`、`OutlineDispatchRegionsPass` | 图级优化、fusion、dispatch 划分 | Relay 图优化 + fusion |
| D Stream | **stream** | `ConvertToStreamPass`、`ScheduleExecutionPass` | 异步调度(timepoint/fence) | (无,IREE 特色) |
| E Codegen | linalg/vector/llvm | `TileAndFuse`、`GenericVectorization`、`Bufferize`、`ConvertToLLVM` | 可执行体 lowering 到设备代码 | TE/TIR + schedule + codegen |
| F HAL | **hal** | `ConvertToHALPass`、`SerializeAllExecutablesPass` | 设备抽象、可执行体序列化 | runtime device 抽象 |
| G VM | **vm** | (尾部 pass + 序列化) | 字节码序列化 | VM |
> 完整 231 个 pass 的去重序列见 [examples/matmul_pipeline_dump.txt](../../examples/matmul_pipeline_dump.txt)
> (32702 行完整 dump,可用 `grep 'IR Dump After'` 提取 pass 序列)。
## 3. 关键 IR 变换(每个 dialect 边界)
### 边界 1:输入 → ABI(WrapEntryPointsPass)
用户写的 `func(tensor)` 被包成对外 `util.func public`,参数变成 `!hal.buffer_view`,
内部用 `hal.tensor.import` / `hal.tensor.export` 在 buffer_view 和 tensor 之间转换:
```mlir
util.func public @matmul(%arg0: !hal.buffer_view, %arg1: !hal.buffer_view) -> !hal.buffer_view {
%0 = hal.tensor.import %arg0 "input0" : !hal.buffer_view -> tensor<2x4xf32>
%1 = hal.tensor.import %arg1 "input1" : !hal.buffer_view -> tensor<4x2xf32>
%2 = util.call @_matmul(%0, %1) : (...) -> tensor<2x2xf32>
%3 = hal.tensor.export %2 "output0" : tensor<2x2xf32> -> !hal.buffer_view
util.return %3 : !hal.buffer_view
}
```
### 边界 2:Flow dispatch 划分(FormDispatchRegionsPass → OutlineDispatchRegionsPass)
`linalg.matmul` 被包进 `flow.dispatch.region`(→ 相当于 TVM 里划分 kernel),
再 outline 成独立的 `flow.executable`,主函数里只剩一个 `flow.dispatch` 调用:
```mlir
// FormDispatchRegionsPass 之后:
%6 = flow.dispatch.region -> (tensor<2x2xf32>) {
%9 = linalg.matmul ins(%1, %3 : ...) outs(%8 : ...) -> tensor<2x2xf32>
flow.return %9 : tensor<2x2xf32>
}
// OutlineDispatchRegionsPass 之后:
flow.executable private @matmul_dispatch_0 {
flow.executable.export public @matmul_dispatch_0_matmul_2x2x4_f32 workgroups() -> (index, index, index) { ... }
builtin.module {
func.func @matmul_dispatch_0_matmul_2x2x4_f32(%arg0: !iree_tensor_ext.dispatch.tensor<...>, ...) {
// 可执行体内部:linalg 计算 + dispatch.tensor.load/store
}
}
}
util.func public @matmul(...) {
%2 = flow.dispatch @matmul_dispatch_0::@matmul_dispatch_0_matmul_2x2x4_f32(%0, %1) : (...) -> tensor<2x2xf32>
}
```
### 边界 3:Flow → Stream(ConvertToStreamPass)
可执行体变 `stream.executable`,绑定变 `!stream.binding`;主函数变成异步资源流:
```mlir
// 可执行体函数签名:!stream.binding(抽象设备缓冲区)
func.func @matmul_dispatch_0_matmul_2x2x4_f32(%arg0: !stream.binding, %arg1: !stream.binding, %arg2: !stream.binding)
// 主函数:tensor → stream.resource + 设备亲和性标注
%1 = stream.tensor.import on(#hal.device.affinity<@__device_0>) %arg0 : !hal.buffer_view -> tensor<2x4xf32> in !stream.resource<external>{%0}
%7 = stream.tensor.dispatch on(#hal.device.affinity<@__device_0>) @matmul_dispatch_0::@matmul_dispatch_0_matmul_2x2x4_f32(%2, %5) : (...) -> tensor<2x2xf32> in !stream.resource<*>{%6}
```
### 边界 4:Stream 异步调度(ScheduleExecutionPass)
dispatch 被包进 `stream.async.execute`,返回 `!stream.timepoint`,再 `await`:
```mlir
%results, %result_timepoint = stream.async.execute on(...) with(%0 as %arg2, %1 as %arg3) -> !stream.resource<...> {
%4 = stream.async.dispatch @matmul_dispatch_0::@matmul_dispatch_0_matmul_2x2x4_f32(%arg2[...], %arg3[...]) : ...
stream.yield %4 : !stream.resource<...>
} => !stream.timepoint
%2 = stream.timepoint.await %result_timepoint => %results : !stream.resource<...>
```
> `timepoint` 是 IREE 异步模型的核心:类似 future/fence,表示「某批异步工作何时完成」,
> 用来表达数据依赖和跨设备同步。这是 TVM 没有的层。
### 边界 5:Codegen(可执行体 lowering,ConvertToLLVMPass)
可执行体里的 linalg matmul 被 tiling → vectorization → bufferize → LLVM:
```mlir
// 最终:显式循环 + fmuladd + 向量 load
llvm.func @matmul_dispatch_0_matmul_2x2x4_f32(%arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) -> i32 {
// 从 iree_hal_executable_dispatch_state_v0_t 取 bindings
// ^bb1/^bb2/^bb3 显式循环,llvm.intr.fmuladd 做 MAC,vector<1xf32> 向量化
}
```
> 注意:codegen 在 Stream 和 HAL 之间发生(pass 130~199),因为可执行体要先编译成设备代码,
> 才能在 HAL 层被序列化。关键 pass:`LLVMCPUSelectLoweringStrategyPass`(选 tiling/向量化策略)、
> `TileAndDistributeToWorkgroups`、`LLVMCPUTileAndFuse`、`GenericVectorization`、
> `IREEComprehensiveBufferize`、`ConvertToLLVM`。
### 边界 6:Stream → HAL(ConvertToHALPass)
可执行体包成 `hal.executable.variant`,带 pipeline layout(缓冲区绑定)和 ordinal:
```mlir
hal.executable private @matmul_dispatch_0 {
hal.executable.variant public @embedded_elf_x86_64 target(#executable_target_embedded_elf_x86_64) {
hal.executable.export public @matmul_dispatch_0_matmul_2x2x4_f32 ordinal(0) layout(#pipeline_layout)
attributes {workgroup_size = [1 : index, 1 : index, 1 : index]}
builtin.module { llvm.func @matmul_dispatch_0_matmul_2x2x4_f32(...) { ... } }
}
}
// #pipeline_layout 定义了 3 个 storage_buffer 绑定(2 ReadOnly + 1 输出)
```
### 边界 7:最终 VM(字节码模块)
序列化后的 VM 模块:声明类型、依赖 `hal` 模块、import 一堆 `hal.*` 函数:
```
Required Types: i32, i64, !hal.allocator, !hal.buffer, !hal.buffer_view,
!hal.command_buffer, !hal.device, !hal.executable, !hal.fence, !vm.buffer
Imported Functions:
[ 7] hal.command_buffer.dispatch(...) -> ()
[11] hal.device.queue.execute.indirect(...) -> ()
[16] hal.fence.await(i32, i64, tuple<!vm.ref<?>>...) -> (i32)
...
```
运行时 `iree-runtime` 加载这个 VM 模块、解析 hal 模块 import、用 HAL driver 执行。
## 4. TVM 0.6 对照总结
| IREE 层 | 作用 | TVM 0.6 对应 | 差异点 |
|---|---|---|---|
| Flow | 图级优化 + dispatch 划分 | Relay(fusion、算子划分) | 划分出的 dispatch = 一个 kernel |
| Stream | 异步调度 | 无 | timepoint/fence 显式建模异步 |
| Codegen | 可执行体 lowering | TE/TIR + schedule + codegen | MLIR dialect lowering,非 schedule 原语 |
| HAL | 设备抽象 | runtime device + codegen 的 device 层 | 更抽象,统一 CPU/GPU/Vulkan |
| VM | 字节码 | graph runtime / 早期 VM | 紧凑字节码 + 模块依赖 |
## 5. 复现命令
```bash
# 完整 dump(含每个 pass 的 IR)
iree-compile --iree-hal-target-backends=llvm-cpu --mlir-print-ir-after-all \
examples/matmul.mlir -o /tmp/matmul.vmfb 2> dump.txt
# 只看 pass 序列
grep -oE 'IR Dump After .*//' dump.txt | sed 's/IR Dump After //; s/ (.*//; s/ \/\/$//' \
| awk '!seen[$0]++' | nl
# 只看某个 pass 之后的 IR
iree-compile --iree-hal-target-backends=llvm-cpu \
--mlir-print-ir-after=ConvertToStreamPass --mlir-print-ir-module-scope \
examples/matmul.mlir -o /tmp/matmul.vmfb 2>&1 | head -80
# 看最终 VM 模块
iree-dump-module /tmp/matmul.vmfb
```
## 6. 下一步
- 阶段 1(MLIR 基础)可选补:现在读懂了 dialect 转换,回头看 Operation/Value/Pass/PatternRewriting
会很快,可先快速过一遍再深入各层。
- 阶段 3(前端)、4(Flow+Stream)、5(HAL)、6(codegen)、7(运行时)逐层深入,
每一层都对着 `src/iree/compiler/src/iree/compiler/Dialect/<X>/` 的源码读。