LLVM 中的循环: Loop
文章目录
之前写 canonicalization 对循环的概念直接略过了,没做展开,循环在日常开发中使用还是挺广泛的,这里简单介绍 LLVM 中循环的概念。
1、基本概念
在 LLVM 当中,会使用 LoopInfo
(用于持有循环信息的核心分析) 在 CFG(control-flow graph,控制流图,节点是 Basic Block 基本块) 来检测出循环。
|
|
关于 Loop,有几个术语需要解释一下:
- Header Block,循环的入口(entry),支配循环当中所有的节点,也是回边所指向的节点。
- Entering Block,不是循环的一部分,但有边可以进入循环的节点。
- Pre-Header Block,虽然不是循环的一部分,但它代表 header block 的唯一前继节点(即只有唯一一个 Entering Block,且它唯一的边是指向 Header)。用于方便进行循环转换(比如把重复计算放到循环外,没有这个术语概念的话,就得把重复的指令写到循环的每一个前继节点)。
- Back Edge,从某个节点回到 header block 的控制流边,循环起码会有一条,可以有多条。
- Latch Block,这是 back edge 的出边节点。
- Exiting Block,具有 exit edge 的节点,在循环内指向循环外的出边节点。
- Exit Edge,由循环内控制流指向跳到循环外的边。
- Exit Block,跳出到循环外的节点,不是循环的一部分,一个循环可以有多个 exit block。
组成一个循环的核心,要有 header block、back edge、latch block。
一个最小型的循环如下图,即只有一个基本块节点(可以是死循环,这时候就不会是 exiting block):
1.1、示例
|
|
使用 opt -enable-new-pm=0 -analyze -loops loop.ll
可以输出循环相关信息:
|
|
可以用 opt -enable-new-pm=0 -dot-cfg
和 dot -Tpng -o main.png main.dot
生成 CFG 图对照着看:
2、canonical loop
一个 Loop 实例如果有较好的基础属性可以简便地进行循环转换的话,则称之为 canonical loop。而 LLVM 当中有两种 canonical loop 的格式,分别是 simplified 格式和 rotated 格式(关于 canonical 的概念可以翻翻我之前写的文章)。
一开始是大部分循环的 pass 都需要依赖 simplified 格式,不过也分出了派系,一些 pass 则需要 rotated 格式来进行分析,rotated 格式是可转换的,官网对于 rotated 格式还标着双引号为 “More Canonical” Loops。
2.1、simplified
simplified 格式有以下属性:
- 一个 pre-header block。
- 只有一条 back edge (因此也只有一个 latch block)。
- exit blocks 的前继节点都来源于循环当中,也就是说,header block 支配所有的 exit blocks。
可以通过对原始的循环执行 LoopSimplfyPass (-loop-simplify)方法得到 simplified 格式的循环,但首先应该使用 Loop::isLoopSimplifyForm
检查是否满足要求。
只有一条 back edge,我们可以很容易地分析出哪些变量是用于循环迭代的(这种变量称之为 induction variable,比如 for(int i=0; i<9; ++i)
里面的变量 i,可以知道循环迭代的次数)。而因为每个 exit blocks 都被循环内的节点所支配,可以很容易地把一些不影响其他控制流路径的指令,放到循环的外面上头。
2.2、rotated
使用 LoopRotationPass 可以进行转换,使用 Loop::isRotatedForm
判断是否能进行转换。rotated 格式基本上是把一个任意的循环转换成 do-while 的结构。
|
|
除了多出 loop guard 之外,还有 header、latch、exiting 块节点结合在一起,这样可以保证这个节点里的所有指令拥有一样的执行次数(execution count),对于进行循环向量化(loop vectorization)很有帮助。
为什么这里会多出 loop guard?官网有举例说明:
|
|
把不变部分移出循环体通常不是 LoopRotate 做的,而是由其他 pass 像 Loop-Invariant Code Motion (-licm) 来完成。
3、Loop Pass
除了 Module 和 Function 可以编写 Pass 之外,Loop 也是支持的,但它的 run 方法签名不太一样。
|
|
对于嵌套的循环(nested loop),通常会描述成树结构,称之为 loop tree(循环树)。
|
|
参考
- LLVM Loop Terminology (and Canonical Forms)
- 《LLVM Techniques, Tips, and Best Practices》
- Bottom-Tested Canonical Loops in LLVM
文章作者 calssion
上次更新 2022-07-02