我正在尝试了解 llvm 优化。我通过编写 C 代码并转换为 LLVM IR 并读取生成的代码来实现此目的。现在据我了解,phi 节点需要返回块,以便它知道要使用哪些寄存器。所以下面的函数是生成 phi 的一个很好的例子。
int fib(int a)
{
if(a < 3)
return 1;
else
return (fib(a-1) + fib(a-2));
}
Clang 10 生成了以下 IR 代码:
define dso_local i32 @fib(i32 %0) #0 {
%2 = alloca i32, align 4
%3 = alloca i32, align 4
store i32 %0, i32* %3, align 4
%4 = load i32, i32* %3, align 4
%5 = icmp slt i32 %4, 3
br i1 %5, label %6, label %7
6: ; preds = %1
store i32 1, i32* %2, align 4
br label %15
7: ; preds = %1
%8 = load i32, i32* %3, align 4
%9 = sub nsw i32 %8, 1
%10 = call i32 @fib(i32 %9)
%11 = load i32, i32* %3, align 4
%12 = sub nsw i32 %11, 2
%13 = call i32 @fib(i32 %12)
%14 = add nsw i32 %10, %13
store i32 %14, i32* %2, align 4
br label %15
15: ; preds = %7, %6
%16 = load i32, i32* %2, align 4
ret i32 %16
}
但是,C 块显式返回值,因为 C 是命令式语言。在 Rust/ocaml 等语言中,我们知道最后一个表达式也是返回值,但在 C 中,这样的概念甚至不存在或不需要。我玩过其他几个例子,但我无法让 clang 产生 phi 指令。
这让我想知道。要么因为 C 语言的特性,Clang 不利用 C 语言的 phi 指令,要么我就是那个无法让编译器生成它的人。
我想知道是否有人给我一个关于如何为 C 语言生成 phi 指令和相关优化的想法或示例。
void use(int);
int f1();
int f2();
void test(int i) {
int x;
if (i) x = f1(); else x = f2();
use(x);
}
运行
clang -cc1 -disable-O0-optnone foo.c -emit-llvm -O0
,生成 LLVM IR 格式的带有 O0 优化代码的 foo.ll
。请注意,-disable0O0-optnone
很重要,否则 llvm opt
工具将拒绝进一步优化代码,这意味着您将永远不会看到 phi
节点。
您将看到
O0
IR 为:
define void @test(i32 noundef %i) #0 {
entry:
%i.addr = alloca i32, align 4
%x = alloca i32, align 4
store i32 %i, ptr %i.addr, align 4
%0 = load i32, ptr %i.addr, align 4
%tobool = icmp ne i32 %0, 0
br i1 %tobool, label %if.then, label %if.else
if.then: ; preds = %entry
%call = call i32 @f1()
store i32 %call, ptr %x, align 4
br label %if.end
if.else: ; preds = %entry
%call1 = call i32 @f2()
store i32 %call1, ptr %x, align 4
br label %if.end
if.end: ; preds = %if.else, %if.then
%1 = load i32, ptr %x, align 4
call void @use(i32 noundef %1)
ret void
}
然后,以
O1
级别运行它:
$ clang -cc1 foo.c -emit-llvm -O1
输出IR为:
; Function Attrs: nounwind
define void @test(i32 noundef %i) local_unnamed_addr #0 {
entry:
%tobool.not = icmp eq i32 %i, 0
br i1 %tobool.not, label %if.else, label %if.then
if.then: ; preds = %entry
%call = tail call i32 @f1() #2
br label %if.end
if.else: ; preds = %entry
%call1 = tail call i32 @f2() #2
br label %if.end
if.end: ; preds = %if.else, %if.then
%x.0 = phi i32 [ %call, %if.then ], [ %call1, %if.else ]
tail call void @use(i32 noundef %x.0) #2
ret void
}
我写了一篇关于它的博客,其中介绍了如何使用llvm
opt
通过O0
传递优化带有phi
节点的生成mem2reg
代码。希望它可以给你带来很多帮助:
https://yuchenx.pages.dev/blog/llvm/llvm-control-flow-and-phi-node/