0

Theoretically, a phi node occurs when a variable assignment can not be determined statically. But the following code does not generate the phi node as expected:

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;
}

Changing c to int or adding -O0 does not make any difference either. By looking at the IR generated below, we can see c.addr is stored with register call and call1 respectively in if.then and if.else branches. But adding --print-memoryssa, I think the problem is why the compiler does not generate phi node even though memory phi occurs.

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
}

Using mem2reg optimization does not help either:

➜  ~ opt -mem2reg -S test.ll -o test-opt.ll
➜  ~ diff test.ll test-opt.ll
1c1
< ; ModuleID = 'test.cpp'
---
> ; ModuleID = 'test.ll'

How can I make the compiler generate phi node as expected?

Thanks!

Tim He
  • 350
  • 2
  • 13
  • Here's a very simple example that produces a PHI node: `void use(int); int f1(); int f2(); void test(int i) { int x; if (i) { x = f1(); } else { x = f2(); } use(x); }` – Nick Lewycky May 05 '22 at 07:30
  • 1
    Does this answer your question? [LLVM opt mem2reg has no effect](https://stackoverflow.com/questions/46513801/llvm-opt-mem2reg-has-no-effect) – arnt May 05 '22 at 12:25

1 Answers1

3

See optnone in the comment above your function declaration?

; Function Attrs: noinline nounwind optnone uwtable

The default optimization level for clang is -O0 which means not to optimize, and clang marks all the functions to forbid further optimization. Pass -Xclang -disable-O0-optnone to clang to allow LLVM's mem2reg to work.

$ 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"}
Nick Lewycky
  • 1,182
  • 6
  • 14
  • 1
    Actually, this is a duplicate of https://stackoverflow.com/questions/46513801/llvm-opt-mem2reg-has-no-effect but I don't have the rep to mark it a dupe. – Nick Lewycky May 05 '22 at 07:53
  • 1
    I voted to close as duplicate, and upvoted your answer too. You're not far from the threshold, if you keep posting such good answers you should pass it soon. – arnt May 05 '22 at 12:28