核心概念
在 MLIR 中,Operation 可以包含 Region,Region 包含 Block,Block 包含 Operation ------ 这是一个递归的层次结构。
Operation
└── Region (0..N 个)
└── Block (1..N 个)
└── Operation (0..N 个) ← 递归回去
逐行拆解代码
mlir
func.func @foo(%arg0: f32) -> f32 { // ← func.func 有一个 Region
%0 = arith.addf %arg0, %arg0 : f32 // ← Region 里的 Block
func.return %0 : f32
}
1. func.func @foo(...) ------ 一个 Operation
func.func 本身是一个 Operation 。它的特殊之处在于:它带有一个 Region(函数体) 。所有"能包含代码块"的 op 都是这样,比如 scf.for、affine.for、func.func。
2. { ... } ------ 这个花括号就是一个 Region
花括号里的所有内容构成了 func.func 的 唯一一个 Region。
- Region 是 Operation 的"子程序容器"。
- 一个 Operation 可以有 0 个、1 个或多个 Region(比如
scf.if有 then/else 两个 Region)。 func.func有且仅有 1 个 Region。
3. 里面的两条指令 ------ 它们属于同一个 Block
mlir
%0 = arith.addf %arg0, %arg0 : f32
func.return %0 : f32
这两条 Operation 顺序排列,共同构成 Region 里的一个 Block。
- Block 是一段顺序执行的指令序列(类似基本块 basic block)。
- Block 没有显式的大括号语法,只要没有遇到
^bbN:这样的标签,连续的指令就属于同一个 Block。 - 这个 Block 的结尾是
func.return(终结符 terminator),符合"每个 Block 必须以 terminator 结尾"的约束。
用图表示
func.func @foo ← Operation
├── Region ← 花括号 { }
│ └── Block ← 隐式的、唯一的一个块
│ ├── arith.addf ← Operation
│ └── func.return ← Operation (terminator)
└── (结果类型 f32 等属性)
对比:多个 Block 的例子
如果函数里有控制流,Region 内就会出现多个 Block(用 ^bb0: 标签分隔):
mlir
func.func @cond_br(%c: i1) -> i32 {
cf.cond_br %c, ^bb1, ^bb2 // Block 0 结束
^bb1: // ← 新 Block 开始
%0 = arith.constant 1 : i32
func.return %0 : i32
^bb2: // ← 另一个新 Block
%1 = arith.constant 2 : i32
func.return %1 : i32
}
这里 一个 Region 内有 3 个 Block (Block 0、^bb1、^bb2)。
用 C++/Python API 视角看
cpp
// C++ 伪代码
Operation *func = ...; // func.func op
Region &body = func->getRegion(0); // 拿到它的 Region
Block &entry = body.front(); // Region 里第一个 Block
for (Operation &op : entry) { // 遍历 Block 里的 op
// arith.addf, func.return ...
}
对应关系:
| 概念 | 含义 | 代码中的体现 |
|---|---|---|
| Operation | 一个 IR 节点 | func.func, arith.addf, func.return |
| Region | op 的子代码容器 | { } 里的整体 |
| Block | Region 内的顺序指令段 | 无标签时默认的一个块 |
一句话总结
func.func是一个 Operation,它的函数体是它的一个 Region,Region 内默认由一个 Block 组成,arith.addf和func.return这两条 Operation 就住在这个 Block 里。 没有控制流时一个 Region 通常只有一个 Block;有分支时才会有多个 Block。