在LLVM IR中,phi节点是SSA(静态单赋值)形式的关键组成部分,用于表示程序中的控制流。据我所知,LLVM 中的
mem2reg
优化过程用于通过引入 phi 节点将内存访问转换为 SSA 形式。
但是当我尝试编译简单的
app.c
程序时:
#include <stdio.h>
int main(){
int x = 1;
if(x > 2){
x++;
}else{
x--;
}
printf("%d", x);
}
通过运行以下命令:
clang -S -emit-llvm -O -Xclang -disable-llvm-passes app.c
opt -S -mem2reg -o opt.ll app.ll
我得到了下一个结果。
app.ll
:
define i32 @main() #0 {
%1 = alloca i32, align 4
%2 = alloca i32, align 4
store i32 0, ptr %1, align 4
call void @llvm.lifetime.start.p0(i64 4, ptr %2) #3
store i32 1, ptr %2, align 4, !tbaa !5
%3 = load i32, ptr %2, align 4, !tbaa !5
%4 = icmp sgt i32 %3, 2
br i1 %4, label %5, label %8
5: ; preds = %0
%6 = load i32, ptr %2, align 4, !tbaa !5
%7 = add nsw i32 %6, 1
store i32 %7, ptr %2, align 4, !tbaa !5
br label %11
8: ; preds = %0
%9 = load i32, ptr %2, align 4, !tbaa !5
%10 = add nsw i32 %9, -1
store i32 %10, ptr %2, align 4, !tbaa !5
br label %11
11: ; preds = %8, %5
%12 = load i32, ptr %2, align 4, !tbaa !5
%13 = call i32 (ptr, ...) @printf(ptr noundef @.str, i32 noundef %12)
call void @llvm.lifetime.end.p0(i64 4, ptr %2) #3
%14 = load i32, ptr %1, align 4
ret i32 %14
}
如您所见,这里没有 phi 节点,我不确定我们是否可以将其称为 SSA 形式。 应用
mem2reg
优化后 (opt.ll
):
define i32 @main() #0 {
%1 = icmp sgt i32 1, 2
br i1 %1, label %2, label %4
2: ; preds = %0
%3 = add nsw i32 1, 1
br label %6
4: ; preds = %0
%5 = add nsw i32 1, -1
br label %6
6: ; preds = %4, %2
%.0 = phi i32 [ %3, %2 ], [ %5, %4 ]
%7 = call i32 (ptr, ...) @printf(ptr noundef @.str, i32 noundef %.0)
ret i32 0
}
它看起来像 SSA 表格。
在应用
mem2reg
优化过程之前,phi 节点是否已经存在于 LLVM IR 中?或者它们只是由 mem2reg
传递引入,作为将内存访问转换为 SSA 形式的过程的一部分?
如果您对这个主题有任何见解或澄清,我将不胜感激。谢谢!
是的。 Phi 节点是在 CodeGen 中生成的,作为 AST 到 IR 转换的一部分。这将处理内在函数、OpenMP 等语言特定的复杂性考虑到了 LLVM 与语言无关的mem2reg。
您可以在 lib/CodeGen/CGBuiltin.cpp 以及 clang/test/CodeGen/ms-intrinsics.c.
中查看相关示例。