Phi节点是一种指令,用于根据当前块的前驱选择一个值(查看这里以查看完整的继承层次结构 - 它也被用作值之一,它继承自其中的类)。
由于LLVM代码采用SSA(静态单赋值)风格的结构,因此Phi节点是必需的。例如,以下C++函数:
void m(bool r, bool y){
bool l = y || r ;
}
被翻译成下面的 IR(通过 clang -c -emit-llvm file.c -o out.bc
创建,并通过 llvm-dis
查看)
define void @_Z1mbb(i1 zeroext %r, i1 zeroext %y) nounwind {
entry:
%r.addr = alloca i8, align 1
%y.addr = alloca i8, align 1
%l = alloca i8, align 1
%frombool = zext i1 %r to i8
store i8 %frombool, i8* %r.addr, align 1
%frombool1 = zext i1 %y to i8
store i8 %frombool1, i8* %y.addr, align 1
%0 = load i8* %y.addr, align 1
%tobool = trunc i8 %0 to i1
br i1 %tobool, label %lor.end, label %lor.rhs
lor.rhs: ; preds = %entry
%1 = load i8* %r.addr, align 1
%tobool2 = trunc i8 %1 to i1
br label %lor.end
lor.end: ; preds = %lor.rhs, %entry
%2 = phi i1 [ true, %entry ], [ %tobool2, %lor.rhs ]
%frombool3 = zext i1 %2 to i8
store i8 %frombool3, i8* %l, align 1
ret void
}
那么这里会发生什么呢?
与 C++ 代码不同,变量 bool l
在 LLVM IR 中必须被定义一次,它只能是 0 或 1。所以我们检查 %tobool
是否为 true,然后跳转到 lor.end
或 lor.rhs
。
在 lor.end
中,我们最终得到了 || 运算符的值。如果我们从入口块进入,则其值为 true。否则,它等于 %tobool2
的值——这正是我们从下面的 IR 行中得到的:
%2 = phi i1 [ true, %entry ], [ %tobool2, %lor.rhs ]
phi
节点是编译器将IR转换为“静态单赋值”形式的问题的解决方案。为了更好地理解这个解决方案,我建议先更好地理解这个问题。因此,我会向您推荐一个链接:“为什么需要phi
节点”。 - Vraj Pandya