GVN-PRE简介
参考 https://cs.wheaton.edu/~tvandrun/writings/cc04.pdf
只考虑表达式,基于SSA
GVN是将Global value numbering,而PRE是Partial redundancy elimination。一个是值编号,一个是部分冗余消除。
GVN的思路简单,按照domtree进行深度优先遍历,记录每个block中表达式的编号,如果重复就删除重复的。
对如下ir
1 | define i32 @foo(i32 %a, i32 %cond){ |
按照domtree进行遍历,bb0,bb1,bb2,bb3,有以下结果。
%a: v0
%cond: v1
%add: v2
%c: v3
%a2: v2 (same as %add)
%a3: v4
%phia: v5
所以说%a2是冗余的值,可以删除掉。
PRE更加复杂一些。
在这个例子2里面,%sa和%a2重复,但bb1不dom bb3. 单纯GVN没法处理。
1 | define i32 @bar(i32 %a, i32 %cond){ |
PRE的算法有些复杂,这里先不介绍了。
GVN-PRE
结合二者的优点,GVN简单,PRE能力强。
针对上面的例子2,如果用逆向数据流分析,将%sa从bb3放到bb0底部,那么也就能消除%a2了。
不过首先需要确定可用集合。就是在每个block最后,有哪些表达式可用。
还是按照GVN的思路,根据domtree遍历。
所以数据流公式就是
1 | AVAIL_IN[b] = AVAIL_OUT[dom(b)] |
然后因为需要逆向数据流分析(回想下liveness),所以
1 | ANTIC_OUT[b] = |
所以算法就是
- 计算avail和antic集合
- 处理hoist
for b in blocks{
if preds(b) < 2
continue
for e in ANTIC_IN[b]:
if e is avaiable in at least one predcessor, then insert e into predecessors where not avaiable.
}
(还有sink和hoist相反,原理类似) - 删除冗余表达式