什么c++代码可以在LLVM中生成phi节点
What c++ code can generate phi node in LLVM
理论上,当无法静态确定变量赋值时,就会出现 phi 节点。但是下面的代码并没有像预期的那样生成 phi 节点:
clang++ -c -emit-llvm -fno-discard-value-names -S -o test.ll test.cpp
struct someClass
{
int a;
char* b;
bool c;
someClass *next;
};
someClass* m(bool r, bool y, someClass* c){
bool l = y || r ;
if(l)
return c;
else
return nullptr;
}
someClass* phiFunc(int a, int b, someClass *c) {
if (a > b) {
c = m(false, true, c);
} else {
c = m(true, false, c);
}
someClass* ret = m(true, true, c); // <<--- phi node expected here.
return ret;
}
将 c
更改为 int
或添加 -O0
也没有任何区别。
通过查看下面生成的 IR,我们可以看到 c.addr
分别存储在 if.then
和 if.else
分支中的寄存器 call
和 call1
中。
但是加上--print-memoryssa
,我觉得问题出在为什么内存phi出现编译器不生成phi节点
opt-10 --print-memoryssa -S test.ll
; Function Attrs: noinline nounwind optnone uwtable
define dso_local %struct.someClass* @_Z7phiFunciiP9someClass(i32 %a, i32 %b, %struct.someClass* %c) #0 {
entry:
%a.addr = alloca i32, align 4
%b.addr = alloca i32, align 4
%c.addr = alloca %struct.someClass*, align 8
%ret = alloca %struct.someClass*, align 8
; 1 = MemoryDef(liveOnEntry)
store i32 %a, i32* %a.addr, align 4
; 2 = MemoryDef(1)
store i32 %b, i32* %b.addr, align 4
; 3 = MemoryDef(2)
store %struct.someClass* %c, %struct.someClass** %c.addr, align 8
; MemoryUse(1) MustAlias
%0 = load i32, i32* %a.addr, align 4
; MemoryUse(2) MustAlias
%1 = load i32, i32* %b.addr, align 4
%cmp = icmp sgt i32 %0, %1
br i1 %cmp, label %if.then, label %if.else
if.then: ; preds = %entry
; MemoryUse(3) MustAlias
%2 = load %struct.someClass*, %struct.someClass** %c.addr, align 8
; 4 = MemoryDef(3)
%call = call %struct.someClass* @_Z1mbbP9someClass(i1 zeroext false, i1 zeroext true, %struct.someClass* %2)
; 5 = MemoryDef(4)
store %struct.someClass* %call, %struct.someClass** %c.addr, align 8
br label %if.end
if.else: ; preds = %entry
; MemoryUse(3) MustAlias
%3 = load %struct.someClass*, %struct.someClass** %c.addr, align 8
; 6 = MemoryDef(3)
%call1 = call %struct.someClass* @_Z1mbbP9someClass(i1 zeroext true, i1 zeroext false, %struct.someClass* %3)
; 7 = MemoryDef(6)
store %struct.someClass* %call1, %struct.someClass** %c.addr, align 8
br label %if.end
if.end: ; preds = %if.else, %if.then
; 10 = MemoryPhi({if.then,5},{if.else,7})
; MemoryUse(10) MayAlias
%4 = load %struct.someClass*, %struct.someClass** %c.addr, align 8
; 8 = MemoryDef(10)
%call2 = call %struct.someClass* @_Z1mbbP9someClass(i1 zeroext true, i1 zeroext true, %struct.someClass* %4)
; 9 = MemoryDef(8)
store %struct.someClass* %call2, %struct.someClass** %ret, align 8
; MemoryUse(9) MustAlias
%5 = load %struct.someClass*, %struct.someClass** %ret, align 8
ret %struct.someClass* %5
}
使用mem2reg
优化也无济于事:
➜ ~ opt -mem2reg -S test.ll -o test-opt.ll
➜ ~ diff test.ll test-opt.ll
1c1
< ; ModuleID = 'test.cpp'
---
> ; ModuleID = 'test.ll'
如何让编译器按预期生成 phi 节点?
谢谢!
请参阅函数声明上方评论中的 optnone
?
; Function Attrs: noinline nounwind optnone uwtable
clang默认的优化级别是-O0
,表示不优化,clang标记所有函数禁止进一步优化。将 -Xclang -disable-O0-optnone
传递给 clang 以允许 LLVM 的 mem2reg
工作。
$ clang++ -Xclang -disable-O0-optnone -c -emit-llvm -fno-discard-value-names -S -o - 72123225.cc | opt -mem2reg -S -o - | llvm-extract --func=_Z7phiFunciiP9someClass | llvm-dis
; ModuleID = '<stdin>'
source_filename = "72123225.cc"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"
%struct.someClass = type { i32, i8*, i8, %struct.someClass* }
; Function Attrs: noinline nounwind uwtable
declare dso_local %struct.someClass* @_Z1mbbP9someClass(i1 zeroext, i1 zeroext, %struct.someClass*) #0
; Function Attrs: noinline nounwind uwtable
define dso_local %struct.someClass* @_Z7phiFunciiP9someClass(i32 %a, i32 %b, %struct.someClass* %c) #0 {
entry:
%cmp = icmp sgt i32 %a, %b
br i1 %cmp, label %if.then, label %if.else
if.then: ; preds = %entry
%call = call %struct.someClass* @_Z1mbbP9someClass(i1 zeroext false, i1 zeroext true, %struct.someClass* %c)
br label %if.end
if.else: ; preds = %entry
%call1 = call %struct.someClass* @_Z1mbbP9someClass(i1 zeroext true, i1 zeroext false, %struct.someClass* %c)
br label %if.end
if.end: ; preds = %if.else, %if.then
%c.addr.0 = phi %struct.someClass* [ %call, %if.then ], [ %call1, %if.else ]
%call2 = call %struct.someClass* @_Z1mbbP9someClass(i1 zeroext true, i1 zeroext true, %struct.someClass* %c.addr.0)
ret %struct.someClass* %call2
}
attributes #0 = { noinline nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
!llvm.module.flags = !{!0}
!llvm.ident = !{!1}
!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"Debian clang version 11.1.0-6+b2"}
理论上,当无法静态确定变量赋值时,就会出现 phi 节点。但是下面的代码并没有像预期的那样生成 phi 节点:
clang++ -c -emit-llvm -fno-discard-value-names -S -o test.ll test.cpp
struct someClass
{
int a;
char* b;
bool c;
someClass *next;
};
someClass* m(bool r, bool y, someClass* c){
bool l = y || r ;
if(l)
return c;
else
return nullptr;
}
someClass* phiFunc(int a, int b, someClass *c) {
if (a > b) {
c = m(false, true, c);
} else {
c = m(true, false, c);
}
someClass* ret = m(true, true, c); // <<--- phi node expected here.
return ret;
}
将 c
更改为 int
或添加 -O0
也没有任何区别。
通过查看下面生成的 IR,我们可以看到 c.addr
分别存储在 if.then
和 if.else
分支中的寄存器 call
和 call1
中。
但是加上--print-memoryssa
,我觉得问题出在为什么内存phi出现编译器不生成phi节点
opt-10 --print-memoryssa -S test.ll
; Function Attrs: noinline nounwind optnone uwtable
define dso_local %struct.someClass* @_Z7phiFunciiP9someClass(i32 %a, i32 %b, %struct.someClass* %c) #0 {
entry:
%a.addr = alloca i32, align 4
%b.addr = alloca i32, align 4
%c.addr = alloca %struct.someClass*, align 8
%ret = alloca %struct.someClass*, align 8
; 1 = MemoryDef(liveOnEntry)
store i32 %a, i32* %a.addr, align 4
; 2 = MemoryDef(1)
store i32 %b, i32* %b.addr, align 4
; 3 = MemoryDef(2)
store %struct.someClass* %c, %struct.someClass** %c.addr, align 8
; MemoryUse(1) MustAlias
%0 = load i32, i32* %a.addr, align 4
; MemoryUse(2) MustAlias
%1 = load i32, i32* %b.addr, align 4
%cmp = icmp sgt i32 %0, %1
br i1 %cmp, label %if.then, label %if.else
if.then: ; preds = %entry
; MemoryUse(3) MustAlias
%2 = load %struct.someClass*, %struct.someClass** %c.addr, align 8
; 4 = MemoryDef(3)
%call = call %struct.someClass* @_Z1mbbP9someClass(i1 zeroext false, i1 zeroext true, %struct.someClass* %2)
; 5 = MemoryDef(4)
store %struct.someClass* %call, %struct.someClass** %c.addr, align 8
br label %if.end
if.else: ; preds = %entry
; MemoryUse(3) MustAlias
%3 = load %struct.someClass*, %struct.someClass** %c.addr, align 8
; 6 = MemoryDef(3)
%call1 = call %struct.someClass* @_Z1mbbP9someClass(i1 zeroext true, i1 zeroext false, %struct.someClass* %3)
; 7 = MemoryDef(6)
store %struct.someClass* %call1, %struct.someClass** %c.addr, align 8
br label %if.end
if.end: ; preds = %if.else, %if.then
; 10 = MemoryPhi({if.then,5},{if.else,7})
; MemoryUse(10) MayAlias
%4 = load %struct.someClass*, %struct.someClass** %c.addr, align 8
; 8 = MemoryDef(10)
%call2 = call %struct.someClass* @_Z1mbbP9someClass(i1 zeroext true, i1 zeroext true, %struct.someClass* %4)
; 9 = MemoryDef(8)
store %struct.someClass* %call2, %struct.someClass** %ret, align 8
; MemoryUse(9) MustAlias
%5 = load %struct.someClass*, %struct.someClass** %ret, align 8
ret %struct.someClass* %5
}
使用mem2reg
优化也无济于事:
➜ ~ opt -mem2reg -S test.ll -o test-opt.ll
➜ ~ diff test.ll test-opt.ll
1c1
< ; ModuleID = 'test.cpp'
---
> ; ModuleID = 'test.ll'
如何让编译器按预期生成 phi 节点?
谢谢!
请参阅函数声明上方评论中的 optnone
?
; Function Attrs: noinline nounwind optnone uwtable
clang默认的优化级别是-O0
,表示不优化,clang标记所有函数禁止进一步优化。将 -Xclang -disable-O0-optnone
传递给 clang 以允许 LLVM 的 mem2reg
工作。
$ clang++ -Xclang -disable-O0-optnone -c -emit-llvm -fno-discard-value-names -S -o - 72123225.cc | opt -mem2reg -S -o - | llvm-extract --func=_Z7phiFunciiP9someClass | llvm-dis
; ModuleID = '<stdin>'
source_filename = "72123225.cc"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"
%struct.someClass = type { i32, i8*, i8, %struct.someClass* }
; Function Attrs: noinline nounwind uwtable
declare dso_local %struct.someClass* @_Z1mbbP9someClass(i1 zeroext, i1 zeroext, %struct.someClass*) #0
; Function Attrs: noinline nounwind uwtable
define dso_local %struct.someClass* @_Z7phiFunciiP9someClass(i32 %a, i32 %b, %struct.someClass* %c) #0 {
entry:
%cmp = icmp sgt i32 %a, %b
br i1 %cmp, label %if.then, label %if.else
if.then: ; preds = %entry
%call = call %struct.someClass* @_Z1mbbP9someClass(i1 zeroext false, i1 zeroext true, %struct.someClass* %c)
br label %if.end
if.else: ; preds = %entry
%call1 = call %struct.someClass* @_Z1mbbP9someClass(i1 zeroext true, i1 zeroext false, %struct.someClass* %c)
br label %if.end
if.end: ; preds = %if.else, %if.then
%c.addr.0 = phi %struct.someClass* [ %call, %if.then ], [ %call1, %if.else ]
%call2 = call %struct.someClass* @_Z1mbbP9someClass(i1 zeroext true, i1 zeroext true, %struct.someClass* %c.addr.0)
ret %struct.someClass* %call2
}
attributes #0 = { noinline nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
!llvm.module.flags = !{!0}
!llvm.ident = !{!1}
!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"Debian clang version 11.1.0-6+b2"}