如何在 clang 中为 llvm ir 生成 phi 指令?

问题描述 投票:0回答:1

我正在尝试了解 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 指令和相关优化的想法或示例。

c assembly optimization clang llvm
1个回答
0
投票
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/

© www.soinside.com 2019 - 2024. All rights reserved.