[PATCH] D109760: [GVN] Simple GVN hoist

Momchil Velikov via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 15 12:52:59 PDT 2021


chill marked 3 inline comments as done.
chill added inline comments.


================
Comment at: llvm/lib/Transforms/Scalar/GVN.cpp:2842
+  return I.isVolatile() || I.isAtomic() ||
+         !isGuaranteedToTransferExecutionToSuccessor(&I);
+}
----------------
junparser wrote:
> can you give an example that when I can not transfer execution, how do subsequent instructions in BB to be hoisted?  IMHO, collectHoistCandidates should stop here.
For example:

```
define dso_local i32 @f2(i32 %c, i32 %a, i32 %b) {
entry:
  %tobool = icmp ne i32 %c, 0
  br i1 %tobool, label %if.then, label %if.else

if.then:
  call void @h()
  %0 = sdiv i32 %a, %b
  br label %if.end

if.else:
  call void @h()
  %1 = sdiv i32 %a, %b
  br label %if.end

if.end:
  %2  = phi i32 [%0, %if.then], [%1, %if.else]
  ret i32 %2
}

declare dso_local void @h() readnone
```

`%0` and `%1` will get the same value number, but should not be hoisted, unless ` call void @h()` is also hoisted.
We would indeed stop the iteration in `collectHoistCandidates`, but hoist the call itself. Then on the next invocation of
`performHoist` we will have

```
define dso_local i32 @f2(i32 %c, i32 %a, i32 %b) {
entry:
  %tobool = icmp ne i32 %c, 0
  call void @h()
  br i1 %tobool, label %if.then, label %if.else

if.then:
  %0 = sdiv i32 %a, %b
  br label %if.end

if.else:
  %1 = sdiv i32 %a, %b
  br label %if.end

if.end:
  %2  = phi i32 [%0, %if.then], [%1, %if.else]
  ret i32 %2
}

declare dso_local void @h() readnone
```

and **then **we'll move the `sdiv` too.



================
Comment at: llvm/lib/Transforms/Scalar/GVN.cpp:2860
+    // appearing as operands in hoisted instructions).
+    if (isa<GetElementPtrInst>(&I))
+      continue;
----------------
junparser wrote:
> Is there any reason not to handle GEPs?
That's to avoid hoisting GEPs, but leaving their users behind. I was following the comment from `GVNHoist.cpp`


> // Hoisting may affect the performance in some cases. To mitigate that, hoisting
> // is disabled in the following cases.
> // 1. Scalars across calls.
> // 2. geps when corresponding load/store cannot be hoisted.

I sort of assume someone measured and found that useful :D


================
Comment at: llvm/lib/Transforms/Scalar/GVN.cpp:3034
+        // Hoisting these creates opportunities for more hoisting.
+        if (P.first->mayReadOrWriteMemory() || isHoistBarrier(*P.first))
+          RunAgain = true;
----------------
junparser wrote:
> ditto
So, an instruction that reads or writes memory could be a local dependency. If you hoist such an instruction, it may turn the local dependency into a non-local one, thus allowing more hoisting.

I guess there are alternative approaches (see discussion on the ML) when we explicitly have a dependency instruction, e.g. a non-volatile load
followed by a non-volatile store, aliasing. Store can be hoisted only if the load is hoisted too.

Not clear what to do when we don't have an explicit dependency, e.g. 

```
define dso_local i32 @f0(i32 %c, i32* %p, i32* %q) {
entry:
  %tobool = icmp ne i32 %c, 0
  br i1 %tobool, label %if.then, label %if.else

if.then:
  store volatile i32 0, i32* %p
  store volatile i32 0, i32* %q
  br label %if.end

if.else:
  store volatile i32 0, i32* %p
  store volatile i32 0, i32* %q
  br label %if.end

if.end:
  ret i32 0
}
```

(Current patch does not handle this case as well, I guess because of MemDep caching).


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D109760/new/

https://reviews.llvm.org/D109760



More information about the llvm-commits mailing list