源码路径
llvm\include\llvm\IR\Instruction.h
llvm\include\llvm\IR\Instruction.def
llvm\include\llvm\IR\Instructions.h
llvm\include\llvm\IR\InstrTypes.h
llvm PHI Node
LLVM IR采用SSA(Static Single Assignment)的形式,这种形式的核心要求有2点:
- 每个变量只被赋值1次。
- 每个变量在使用前必须先定义。
如下源代码:
int foo(int x) {
x = x - 3;
x = x + 2;
return x;
}
生成的SSA如下所示,可以看到同一个变量x在IR中变成了2个变量%sub和%add,分别对应两次赋值:
define dso_local i32 @foo(i32 %x) #0 {
entry:
%sub = sub nsw i32 %x, 3
%add = add nsw i32 %sub, 2
ret i32 %add
}
SSA这种形式,在程序中存在分支的情况下,带来了1个问题:不确定该使用哪个变量。比如下列源代码:
int bar(int a) {
int x;
if (a) {
x = a + 3;
} else {
x = a + 5;
}
int y = x + 3;
return y;
}
生成的伪SSA:
define dso_local i32 @bar(i32 %a) #0 {
entry:
%tobool = icmp ne i32 %a, 0
br i1 %tobool, label %if.then, label %if.else
if.then: ; preds = %entry
%add = add nsw i32 %a, 3 ; x1 = a + 3
br label %if.end
if.else: ; preds = %entry
%add1 = add nsw i32 %a, 5 ; x2 = a + 5
br label %if.end
if.end: ; preds = %if.else, %if.then
%x.0 = ? ; x3 = x1 or x2?
}
为了解决这个问题,llvm在IR中引入了PHI Node,表示这种关系,可以看到实际生产的IR中,用%x.0 = phi i32 [ %add, %if.then ], [ %add1, %if.else ]表示了不同分支下的x的取值,其中的phi既PHI Node Instruction。
define dso_local i32 @bar(i32 %a) #0 {
entry:
%tobool = icmp ne i32 %a, 0
br i1 %tobool, label %if.then, label %if.else
if.then: ; preds = %entry
%add = add nsw i32 %a, 3
br label %if.end
if.else: ; preds = %entry
%add1 = add nsw i32 %a, 5
br label %if.end
if.end: ; preds = %if.else, %if.then
%x.0 = phi i32 [ %add, %if.then ], [ %add1, %if.else ]
%add2 = add nsw i32 %x.0, 3
ret i32 %add2
}
PHI Node Instruction
phi指令用于实现SSA中的PHI Node。
语法
<result> = phi [fast-math-flags] <ty> [ <val0>, <label0>], ...
参数说明:
ty:valx的类型。
语义
在运行时,phi指令通过当前BasicBlock的前继BasicBlock的label来匹配labelx,result等于对应的valx。
示例
Loop: ; Infinite loop that counts from 0 on up...
%indvar = phi i32 [ 0, %LoopHeader ], [ %nextindvar, %Loop ]
%nextindvar = add i32 %indvar, 1
br label %Loop