究竟什么是PHI指令以及如何在LLVM中使用它

时间:2012-07-14 16:42:29

标签: llvm

LLVM有phi指令,解释非常奇怪:

  

'phi'指令用于在表示函数的SSA图中实现φ节点。

通常用于实现分支。如果我理解正确,则需要进行依赖性分析,在某些情况下,它可以帮助避免不必要的加载。然而,它仍然很难理解它究竟做了什么。

Kaleidoscope exampleif案件的解释相当不错。但是,如何实现&&||等逻辑操作并不清楚。如果我在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节点,以及如何用它来实现||

2 个答案:

答案 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.endlor.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节点(伪代码):

  1. 将4分配给x1
  2. if(!something)分支到4
  3. 通过添加2
  4. 从x1计算x2
  5. 从x1和x2
  6. 分配x3 phi
  7. 使用x3打印打印
  8. 但你可以不用phi节点(在伪代码中):

    1. 在名为x
    2. 的堆栈上分配局部变量
    3. 加载到temp x1值4
    4. 将x1存储到x
    5. if(!something)分支到8
    6. 将x加载到temp x2
    7. 将x2与4添加到temp x3
    8. 将x3存储到x
    9. 将x加载到temp x4
    10. 使用x4打印打印
    11. 通过使用llvm运行优化传递,第二个代码将优化为第一个代码。