[LoopUnswitch] Unswitch on conditions feeding into guards

Summary:
This is a straightforward extension of what LoopUnswitch does to
branches to guards.  That is, we unswitch

```
for (;;) {
  ...
  guard(loop_invariant_cond);
  ...
}
```

into

```
if (loop_invariant_cond) {
  for (;;) {
    ...
    // There is no need to emit guard(true)
    ...
  }
} else {
  for (;;) {
    ...
    guard(false);
    // SimplifyCFG will clean this up by adding an
    // unreachable after the guard(false)
    ...
  }
}
```

Reviewers: majnemer

Subscribers: mcrosier, llvm-commits, mzolotukhin

Differential Revision: http://reviews.llvm.org/D21725

llvm-svn: 273801
This commit is contained in:
Sanjoy Das 2016-06-26 05:10:45 +00:00
parent 7dda0edb5f
commit a37bb4a65d
2 changed files with 130 additions and 7 deletions

View File

@ -65,6 +65,7 @@ using namespace llvm;
STATISTIC(NumBranches, "Number of branches unswitched");
STATISTIC(NumSwitches, "Number of switches unswitched");
STATISTIC(NumGuards, "Number of guards unswitched");
STATISTIC(NumSelects , "Number of selects unswitched");
STATISTIC(NumTrivial , "Number of unswitches that are trivial");
STATISTIC(NumSimplify, "Number of simplifications of unswitched code");
@ -514,22 +515,34 @@ bool LoopUnswitch::processCurrentLoop() {
return true;
}
// Do not unswitch loops containing convergent operations, as we might be
// making them control dependent on the unswitch value when they were not
// before.
// FIXME: This could be refined to only bail if the convergent operation is
// not already control-dependent on the unswitch value.
// Run through the instructions in the loop, keeping track of three things:
//
// - That we do not unswitch loops containing convergent operations, as we
// might be making them control dependent on the unswitch value when they
// were not before.
// FIXME: This could be refined to only bail if the convergent operation is
// not already control-dependent on the unswitch value.
//
// - That basic blocks in the loop contain invokes whose predecessor edges we
// cannot split.
//
// - The set of guard intrinsics encountered (these are non terminator
// instructions that are also profitable to be unswitched).
SmallVector<IntrinsicInst *, 4> Guards;
for (const auto BB : currentLoop->blocks()) {
for (auto &I : *BB) {
auto CS = CallSite(&I);
if (!CS) continue;
if (CS.hasFnAttr(Attribute::Convergent))
return false;
// Return false if any loop blocks contain invokes whose predecessor edges
// we cannot split.
if (auto *II = dyn_cast<InvokeInst>(&I))
if (!II->getUnwindDest()->canSplitPredecessors())
return false;
if (auto *II = dyn_cast<IntrinsicInst>(&I))
if (II->getIntrinsicID() == Intrinsic::experimental_guard)
Guards.push_back(II);
}
}
@ -549,6 +562,19 @@ bool LoopUnswitch::processCurrentLoop() {
return false;
}
for (IntrinsicInst *Guard : Guards) {
Value *LoopCond =
FindLIVLoopCondition(Guard->getOperand(0), currentLoop, Changed);
if (LoopCond &&
UnswitchIfProfitable(LoopCond, ConstantInt::getTrue(Context))) {
// NB! Unswitching (if successful) could have erased some of the
// instructions in Guards leaving dangling pointers there. This is fine
// because we're returning now, and won't look at Guards again.
++NumGuards;
return true;
}
}
// Loop over all of the basic blocks in the loop. If we find an interior
// block that is branching on a loop-invariant condition, we can unswitch this
// loop.

View File

@ -0,0 +1,97 @@
; RUN: opt -S -loop-unswitch < %s | FileCheck %s
declare void @llvm.experimental.guard(i1, ...)
define void @f_0(i32 %n, i32* %ptr, i1 %c) {
; CHECK-LABEL: @f_0(
; CHECK: loop.us:
; CHECK-NOT: guard
; CHECK: loop:
; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 false) [ "deopt"() ]
entry:
br label %loop
loop:
%iv = phi i32 [ 0, %entry ], [ %iv.inc, %loop ]
%iv.inc = add i32 %iv, 1
call void(i1, ...) @llvm.experimental.guard(i1 %c) [ "deopt"() ]
store volatile i32 0, i32* %ptr
%becond = icmp ult i32 %iv.inc, %n
br i1 %becond, label %leave, label %loop
leave:
ret void
}
define void @f_1(i32 %n, i32* %ptr, i1 %c_0, i1 %c_1) {
; CHECK-LABEL: @f_1(
; CHECK: loop.us.us:
; CHECK-NOT: guard
; CHECK: loop.us:
; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 false) [ "deopt"(i32 2) ]
; CHECK-NOT: guard
; CHECK: loop.us1:
; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 false) [ "deopt"(i32 1) ]
; CHECK-NOT: guard
; CHECK: loop:
; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 false) [ "deopt"(i32 1) ]
; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 false) [ "deopt"(i32 2) ]
entry:
br label %loop
loop:
%iv = phi i32 [ 0, %entry ], [ %iv.inc, %loop ]
%iv.inc = add i32 %iv, 1
call void(i1, ...) @llvm.experimental.guard(i1 %c_0) [ "deopt"(i32 1) ]
store volatile i32 0, i32* %ptr
call void(i1, ...) @llvm.experimental.guard(i1 %c_1) [ "deopt"(i32 2) ]
%becond = icmp ult i32 %iv.inc, %n
br i1 %becond, label %leave, label %loop
leave:
ret void
}
; Basic negative test
define void @f_3(i32 %n, i32* %ptr, i1* %c_ptr) {
; CHECK-LABEL: @f_3(
; CHECK-NOT: loop.us:
entry:
br label %loop
loop:
%iv = phi i32 [ 0, %entry ], [ %iv.inc, %loop ]
%iv.inc = add i32 %iv, 1
%c = load volatile i1, i1* %c_ptr
call void(i1, ...) @llvm.experimental.guard(i1 %c) [ "deopt"() ]
store volatile i32 0, i32* %ptr
%becond = icmp ult i32 %iv.inc, %n
br i1 %becond, label %leave, label %loop
leave:
ret void
}
define void @f_4(i32 %n, i32* %ptr, i1 %c) {
; CHECK-LABEL: @f_4(
;
; Demonstrate that unswitching on one guard can cause another guard to
; be erased (this has implications on what guards we can keep raw
; pointers to).
entry:
br label %loop
loop:
%iv = phi i32 [ 0, %entry ], [ %iv.inc, %loop ]
%iv.inc = add i32 %iv, 1
call void(i1, ...) @llvm.experimental.guard(i1 %c) [ "deopt"(i32 1) ]
store volatile i32 0, i32* %ptr
%neg = xor i1 %c, 1
call void(i1, ...) @llvm.experimental.guard(i1 %neg) [ "deopt"(i32 2) ]
%becond = icmp ult i32 %iv.inc, %n
br i1 %becond, label %leave, label %loop
leave:
ret void
}