首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >PHI指令到底做了什么以及如何在LLVM中使用它

PHI指令到底做了什么以及如何在LLVM中使用它
EN

Stack Overflow用户
提问于 2012-07-15 00:42:29
回答 3查看 36.8K关注 0票数 98

LLVM有phi指令,但解释很奇怪:

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

通常,它用于实现分支。如果我理解正确的话,它需要使依赖分析成为可能,在某些情况下,它可以帮助避免不必要的加载。然而,仍然很难理解它到底是做什么的。

对于if案例,万花筒example很好地解释了这一点。然而,如何实现像&&||这样的逻辑操作还不是很清楚。如果我在online llvm编译器中输入以下内容:

代码语言:javascript
运行
复制
void main1(bool r, bool y) {
    bool l = y || r;
}

最后几行完全把我搞糊涂了:

代码语言:javascript
运行
复制
; <label>:10                                      ; preds = %7, %0
%11 = phi i1 [ true, %0 ], [ %9, %7 ]
%12 = zext i1 %11 to i8

看起来phi节点生成了一个可以使用的结果。我的印象是,phi节点只定义了来自哪些路径的值。

有人能解释一下什么是Phi节点,以及如何用它实现||吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-07-15 01:42:00

phi节点是一条指令,用于根据当前块的前身选择值(查看here可以看到完整的层次结构-它也用作一个值,这是它继承的类之一)。

由于LLVM代码的SSA (静态单一赋值)样式的结构,需要使用Phi节点-例如,下面的C++函数

代码语言:javascript
运行
复制
void m(bool r, bool y){
    bool l = y || r ;
}

翻译成以下IR:(通过clang -c -emit-llvm file.c -o out.bc创建,然后通过llvm-dis查看)

代码语言:javascript
运行
复制
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++代码不同,在LLVM中,变量bool l可以是0或1,它必须定义为一次。因此,我们检查%tobool是否为真,然后跳转到lor.endlor.rhs

lor.end中,我们最终得到了||运算符的值。如果我们是从入口块到达的,那么它就是真的。否则,它等于%tobool2的值--这正是我们从下面的IR行得到的:

代码语言:javascript
运行
复制
%2 = phi i1 [ true, %entry ], [ %tobool2, %lor.rhs ]
票数 87
EN

Stack Overflow用户

发布于 2012-07-15 05:40:48

你根本不需要使用phi。只需创建一堆临时变量。LLVM优化过程将负责优化临时变量,并将自动使用phi节点。

例如,如果您想这样做:

代码语言:javascript
运行
复制
x = 4;
if (something) x = x + 2;
print(x);

为此,您可以使用phi node (在伪代码中):

  1. assign 4 to x1
  2. if (!something)分支到4
  3. calculate x2 from x1 by adding 2
  4. assign x3 phi from x1
  5. x2
  6. 调用print with x3

但是您可以不使用phi node (在伪代码中):

  1. 在堆栈上分配名为x
  2. load into temp x1 value 4
  3. 将x1存储到x
  4. if (!something)分支到8

H122load x to temp x2

  1. add x2 with 4 to temp x3
  2. 将x3存储到x
  3. load x to temp x4

H130调用使用X4打印

通过使用llvm运行优化过程,第二个代码将被优化为第一个代码。

票数 35
EN

Stack Overflow用户

发布于 2022-01-29 02:17:46

现有的答案是好的。但是,我想让它更简单、更简短。

代码语言:javascript
运行
复制
block3:
    %result = phi i32 [%a, %block1], [%b, %block2]

这意味着如果前一个块为block1,则选择value a。如果上一个块为block2,请选择value b

为什么我们要这样写呢?这是为了防止在两个不同的块中分配result,例如if块和else块。因为,我们不想违反SSA原则。SSA帮助编译器应用各种优化,并且它是中间代码的事实标准。有关更多信息,请参阅this reference

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11485531

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档