LLVM有phi指令,解释非常奇怪:
'phi'指令用于在表示函数的SSA图中实现φ节点。
通常用于实现分支。如果我理解正确,则需要进行依赖性分析,在某些情况下,它可以帮助避免不必要的加载。然而,它仍然很难理解它究竟做了什么。
Kaleidoscope example对if
案件的解释相当不错。但是,如何实现&&
和||
等逻辑操作并不清楚。如果我在online llvm编译器中输入以下内容:
void main1(bool r, bool y) {
bool l = y || r;
}
最后几行完全让我困惑:
; <label>:10 ; preds = %7, %0
%11 = phi i1 [ true, %0 ], [ %9, %7 ]
%12 = zext i1 %11 to i8
看起来phi节点产生可以使用的结果。而且我的印象是,phi节点只是定义了哪些路径值。
有人可以解释一下什么是Phi节点,以及如何用它来实现||
?
答案 0 :(得分:69)
phi节点是一个指令,用于根据当前块的前一个选择一个值(Look here以查看完整的层次结构 - 它也被用作一个值,它是它的一个类继承自。)
由于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
可以是0或1,在LLVM IR中,必须定义一次。因此,我们检查%tobool
是否为真,然后跳转到lor.end
或lor.rhs
。
在lor.end
中,我们终于得到了||的值运营商。如果我们从入境区到达 - 那就是真的。否则,它等于%tobool2
的值 - 这正是我们从以下IR线得到的:
%2 = phi i1 [ true, %entry ], [ %tobool2, %lor.rhs ]
答案 1 :(得分:26)
您根本不需要使用phi。只需创建一堆临时变量。 LLVM优化传递将负责优化临时变量,并将自动使用phi节点。
例如,如果你想这样做:
x = 4;
if (something) x = x + 2;
print(x);
您可以使用phi节点(伪代码):
但你可以不用phi节点(在伪代码中):
通过使用llvm运行优化传递,第二个代码将优化为第一个代码。