SSA Form and PHI Nodes
本文主要简单介绍 LLVM 中的 PHINode
。
最近接触到追踪一个变量在程序中的使用。当对 LLVM IR 中 instruction 进行分析的时候,遇到类似如下的代码:
%19 = phi i1 [ false, %entry ], [ true, %land.rhs ]
该指令为 PHINode
,为 llvm 中的一个指令类,继承于 llvm::Instruction
,具体继承关系和 llvm 源码见:PHINode 类
(一)、SSA 简介
在正式介绍 PHI Nodes 之前首先需要了解 SSA
,因为所有的 LLVM IR 指令都是使用 SSA
形式表示。
(1)、SSA
SSA (static single assignment form)
:静态一次性赋值;意思是说每个变量(虚拟寄存器)都只能被赋值一次;这样做的好处是可以方便优化代码。
举个例子:
y := 1
y := 2
x := y
显然,在以上代码中我们能够一眼看出第一行代码是多余的,因为在第三行使用的 $y$ 的值是来自第二行对 $y$ 的赋值,而不是来自第一行对 $y$ 的赋值。而对于程序来说并不能直观的得出第一行是多余的,需要做数据流分析才能判断第一行是多余的。$SSA$ 恰好能够解决该问题,以上代码用 $SSA$ 形式来表示的话:
y1 := 1
y2 := 2
x1 := y2
很明显,用以上表示方法程序不需要做数据流分析就能够知道第三行使用的 $y$ 是来自第二行的定义。当然,使用 $SSA$ 还能做很多别的优化,我也没有细看,这里就不再赘述了,有兴趣的话可以参考:维基百科上的介绍
(2)、生成 SSA 形式的指令
我们可以通过探究 $SSA$ 指令的生成知道为什么要引入 PHI Nodes
。
把一个普通程序转化为 $SSA$ 形式,最简单的做法就是把每一次的赋值都赋值给一个新的变量(或者说是同一个变量的不同版本),同时在使用变量的时候,使用能够到达该程序点对应的”版本”。 举个例子,在下图的控制流图中,左边图一是原程序流图,右图为按照上述方法生成的 SSA 形式的程序流图。
图一:原程序流图 | 图二:SSA 形式流图(中间版本) |
---|---|
![]() |
![]() |
我们可以发现在右图最底下的基本块中,对于 $y$ 的使用可能来自 $y_1$ 也可能来自 $y_2$,取决于程序是从哪条路径到达该程序点的。为了解决该问题,引入了一条新的语句:$\phi$,该语句会生成一个新的 $y$ 的定义 $y_3$,$y_3$ 的值会根据控制流是从哪条路径到达而选择不同的值 $y_1$ 或 $y_2$:
图三:SSA 形式流图 |
---|
![]() |
上述的 $\phi$ 函数就是我们所要介绍的 PHI Node
。
$ y_3 \leftarrow \phi (y_1, y_2) $
PHI node 根据控制流是从哪一个 block ( $y_1\leftarrow x_2 * 2$ 或 $y_2\leftarrow x_2 -3$) 到达,来决定使用 $y_1$ 或 $y_2$ 赋值给 $y_3$。
(二)、PHI Nodes 简介
在上一节中我们已经介绍了为什么要引入 PHI Nodes
;本节主要通过一个例子来讲解 PHI Nodes。
回顾一下 PHI Nodes:
- PHI Nodes 会记录是从哪个 control-flow 过来的,从而使用相应的值(类似多路复用器)。
- 并没有实际实现,只是编译器会保证相应的 virtual registers 映射到了同一个 physical register。
举个例子,以下左图为原程序,右图为相应的 control-flow。
当引入 SSA 形式:
对于一个 PHI node 可以表示为:
%result = phi i32 [value1, BB label1], [value2, BB label2]
当从 label1 所对应的 basic block(BB) 路径到达 PHI node, 则 result 的值为 value1; 同理,当从 label2 所对应的 basic block路径到达 PHI node, 则 result 的值为 value2。
(三)、参考链接
http://www.llvmpy.org/llvmpy-doc/dev/doc/llvm_concepts.html#ssa-form-and-phi-nodes
https://en.wikipedia.org/wiki/Static_single_assignment_form
https://cs.uni-paderborn.de/fileadmin/informatik/fg/hit/teaching/SS2017/HWSW-Codesign/02-Compiler-LLVM.pdf
https://llvm.org/doxygen/classllvm_1_1PHINode.html