]> granicus.if.org Git - llvm/commitdiff
[SCEV] Use guards to prove predicates
authorSanjoy Das <sanjoy@playingwithpointers.com>
Tue, 10 May 2016 00:31:49 +0000 (00:31 +0000)
committerSanjoy Das <sanjoy@playingwithpointers.com>
Tue, 10 May 2016 00:31:49 +0000 (00:31 +0000)
We can use calls to @llvm.experimental.guard to prove predicates,
relying on the fact that in all locations domianted by a call to
@llvm.experimental.guard the predicate it is guarding is known to be
true.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@268997 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/Analysis/ScalarEvolution.h
lib/Analysis/ScalarEvolution.cpp
test/Analysis/ScalarEvolution/guards.ll [new file with mode: 0644]

index bb30c90abf90ffa0793a8a3929fd4f1d0880f286..e49f7d4f2698d808d5c001e3f73758eb3f9f972a 100644 (file)
@@ -464,6 +464,12 @@ namespace llvm {
     ///
     Function &F;
 
+    /// Does the module have any calls to the llvm.experimental.guard intrinsic
+    /// at all?  If this is false, we avoid doing work that will only help if
+    /// thare are guards present in the IR.
+    ///
+    bool HasGuards;
+
     /// The target library information for the target we are targeting.
     ///
     TargetLibraryInfo &TLI;
@@ -1007,6 +1013,11 @@ namespace llvm {
                                         const SCEV *FoundLHS,
                                         const SCEV *FoundRHS);
 
+    /// Return true if the condition denoted by \p LHS \p Pred \p RHS is implied
+    /// by a call to \c @llvm.experimental.guard in \p BB.
+    bool isImpliedViaGuard(BasicBlock *BB, ICmpInst::Predicate Pred,
+                           const SCEV *LHS, const SCEV *RHS);
+
     /// Test whether the condition described by Pred, LHS, and RHS is true
     /// whenever the condition described by Pred, FoundLHS, and FoundRHS is
     /// true.
index d1a330d094876f1e5516b1bbe0e08d28e514e45f..be3902c99f4563e9550b6891520a4e78c30a5c58 100644 (file)
@@ -7805,6 +7805,23 @@ bool ScalarEvolution::isKnownPredicateViaSplitting(ICmpInst::Predicate Pred,
          isKnownPredicate(CmpInst::ICMP_SLT, LHS, RHS);
 }
 
+bool ScalarEvolution::isImpliedViaGuard(BasicBlock *BB,
+                                        ICmpInst::Predicate Pred,
+                                        const SCEV *LHS, const SCEV *RHS) {
+  // No need to even try if we know the module has no guards.
+  if (!HasGuards)
+    return false;
+
+  return any_of(*BB, [&](Instruction &I) {
+    using namespace llvm::PatternMatch;
+
+    Value *Condition;
+    return match(&I, m_Intrinsic<Intrinsic::experimental_guard>(
+                         m_Value(Condition))) &&
+           isImpliedCond(Pred, LHS, RHS, Condition, false);
+  });
+}
+
 /// isLoopBackedgeGuardedByCond - Test whether the backedge of the loop is
 /// protected by a conditional between LHS and RHS.  This is used to
 /// to eliminate casts.
@@ -7872,12 +7889,18 @@ ScalarEvolution::isLoopBackedgeGuardedByCond(const Loop *L,
   if (!DT.isReachableFromEntry(L->getHeader()))
     return false;
 
+  if (isImpliedViaGuard(Latch, Pred, LHS, RHS))
+    return true;
+
   for (DomTreeNode *DTN = DT[Latch], *HeaderDTN = DT[L->getHeader()];
        DTN != HeaderDTN; DTN = DTN->getIDom()) {
 
     assert(DTN && "should reach the loop header before reaching the root!");
 
     BasicBlock *BB = DTN->getBlock();
+    if (isImpliedViaGuard(BB, Pred, LHS, RHS))
+      return true;
+
     BasicBlock *PBB = BB->getSinglePredecessor();
     if (!PBB)
       continue;
@@ -7930,6 +7953,9 @@ ScalarEvolution::isLoopEntryGuardedByCond(const Loop *L,
        Pair.first;
        Pair = getPredecessorWithUniqueSuccessorForBB(Pair.first)) {
 
+    if (isImpliedViaGuard(Pair.first, Pred, LHS, RHS))
+      return true;
+
     BranchInst *LoopEntryPredicate =
       dyn_cast<BranchInst>(Pair.first->getTerminator());
     if (!LoopEntryPredicate ||
@@ -9462,11 +9488,26 @@ ScalarEvolution::ScalarEvolution(Function &F, TargetLibraryInfo &TLI,
       CouldNotCompute(new SCEVCouldNotCompute()),
       WalkingBEDominatingConds(false), ProvingSplitPredicate(false),
       ValuesAtScopes(64), LoopDispositions(64), BlockDispositions(64),
-      FirstUnknown(nullptr) {}
+      FirstUnknown(nullptr) {
+
+  // To use guards for proving predicates, we need to scan every instruction in
+  // relevant basic blocks, and not just terminators.  Doing this is a waste of
+  // time if the IR does not actually contain any calls to
+  // @llvm.experimental.guard, so do a quick check and remember this beforehand.
+  //
+  // This pessimizes the case where a pass that preserves ScalarEvolution wants
+  // to _add_ guards to the module when there weren't any before, and wants
+  // ScalarEvolution to optimize based on those guards.  For now we prefer to be
+  // efficient in lieu of being smart in that rather obscure case.
+
+  auto *GuardDecl = F.getParent()->getFunction(
+      Intrinsic::getName(Intrinsic::experimental_guard));
+  HasGuards = GuardDecl && !GuardDecl->use_empty();
+}
 
 ScalarEvolution::ScalarEvolution(ScalarEvolution &&Arg)
-    : F(Arg.F), TLI(Arg.TLI), AC(Arg.AC), DT(Arg.DT), LI(Arg.LI),
-      CouldNotCompute(std::move(Arg.CouldNotCompute)),
+    : F(Arg.F), HasGuards(Arg.HasGuards), TLI(Arg.TLI), AC(Arg.AC), DT(Arg.DT),
+      LI(Arg.LI), CouldNotCompute(std::move(Arg.CouldNotCompute)),
       ValueExprMap(std::move(Arg.ValueExprMap)),
       WalkingBEDominatingConds(false), ProvingSplitPredicate(false),
       BackedgeTakenCounts(std::move(Arg.BackedgeTakenCounts)),
diff --git a/test/Analysis/ScalarEvolution/guards.ll b/test/Analysis/ScalarEvolution/guards.ll
new file mode 100644 (file)
index 0000000..52ad4dc
--- /dev/null
@@ -0,0 +1,141 @@
+; RUN: opt -S -indvars < %s | FileCheck %s
+
+; Check that SCEV is able to recognize and use guards to prove
+; conditions gaurding loop entries and backedges.  This isn't intended
+; to be a comprehensive test of SCEV's simplification capabilities,
+; tests directly testing e.g. if SCEV can elide a sext should go
+; elsewhere.
+
+target datalayout = "n8:16:32:64"
+
+declare void @llvm.experimental.guard(i1, ...)
+
+define void @test_1(i1* %cond_buf, i32* %len_buf) {
+; CHECK-LABEL: @test_1(
+entry:
+  %len = load i32, i32* %len_buf, !range !{i32 1, i32 2147483648}
+  br label %loop
+
+loop:
+; CHECK: loop:
+; CHECK:  call void (i1, ...) @llvm.experimental.guard(i1 true) [ "deopt"() ]
+; CHECK:  %iv.inc.cmp = icmp slt i32 %iv.inc, %len
+; CHECK:  call void (i1, ...) @llvm.experimental.guard(i1 %iv.inc.cmp) [ "deopt"() ]
+; CHECK: leave:
+
+  %iv = phi i32 [ 0, %entry ], [ %iv.inc, %loop ]
+  %iv.inc = add i32 %iv, 1
+
+  %iv.cmp = icmp slt i32 %iv, %len
+  call void(i1, ...) @llvm.experimental.guard(i1 %iv.cmp) [ "deopt"() ]
+
+  %iv.inc.cmp = icmp slt i32 %iv.inc, %len
+  call void(i1, ...) @llvm.experimental.guard(i1 %iv.inc.cmp) [ "deopt"() ]
+
+  %becond = load volatile i1, i1* %cond_buf
+  br i1 %becond, label %loop, label %leave
+
+leave:
+  ret void
+}
+
+define void @test_2(i32 %n, i32* %len_buf) {
+; CHECK-LABEL: @test_2(
+; CHECK:  [[LEN_SEXT:%[^ ]+]] = sext i32 %len to i64
+; CHECK:  br label %loop
+
+entry:
+  %len = load i32, i32* %len_buf, !range !{i32 0, i32 2147483648}
+  br label %loop
+
+loop:
+; CHECK: loop:
+; CHECK:  %indvars.iv = phi i64 [ %indvars.iv.next, %loop ], [ 0, %entry ]
+; CHECK:  %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1
+; CHECK:  %iv.inc.cmp = icmp slt i64 %indvars.iv.next, [[LEN_SEXT]]
+; CHECK:  call void (i1, ...) @llvm.experimental.guard(i1 %iv.inc.cmp) [ "deopt"() ]
+; CHECK: leave:
+
+  %iv = phi i32 [ 0, %entry ], [ %iv.inc, %loop ]
+  %iv.inc = add i32 %iv, 1
+
+  %iv.sext = sext i32 %iv to i64
+
+  %iv.inc.cmp = icmp slt i32 %iv.inc, %len
+  call void(i1, ...) @llvm.experimental.guard(i1 %iv.inc.cmp) [ "deopt"() ]
+
+  %becond = icmp ne i32 %iv, %n
+  br i1 %becond, label %loop, label %leave
+
+leave:
+  ret void
+}
+
+define void @test_3(i1* %cond_buf, i32* %len_buf) {
+; CHECK-LABEL: @test_3(
+
+entry:
+  %len = load i32, i32* %len_buf
+  %entry.cond = icmp sgt i32 %len, 0
+  call void(i1, ...) @llvm.experimental.guard(i1 %entry.cond) [ "deopt"() ]
+  br label %loop
+
+loop:
+; CHECK: loop:
+; CHECK:  call void (i1, ...) @llvm.experimental.guard(i1 true) [ "deopt"() ]
+; CHECK:  %iv.inc.cmp = icmp slt i32 %iv.inc, %len
+; CHECK:  call void (i1, ...) @llvm.experimental.guard(i1 %iv.inc.cmp) [ "deopt"() ]
+; CHECK: leave:
+  %iv = phi i32 [ 0, %entry ], [ %iv.inc, %loop ]
+  %iv.inc = add i32 %iv, 1
+
+  %iv.cmp = icmp slt i32 %iv, %len
+  call void(i1, ...) @llvm.experimental.guard(i1 %iv.cmp) [ "deopt"() ]
+
+  %iv.inc.cmp = icmp slt i32 %iv.inc, %len
+  call void(i1, ...) @llvm.experimental.guard(i1 %iv.inc.cmp) [ "deopt"() ]
+
+  %becond = load volatile i1, i1* %cond_buf
+  br i1 %becond, label %loop, label %leave
+
+leave:
+  ret void
+}
+
+define void @test_4(i1* %cond_buf, i32* %len_buf) {
+; CHECK-LABEL: @test_4(
+
+entry:
+  %len = load i32, i32* %len_buf
+  %entry.cond = icmp sgt i32 %len, 0
+  call void(i1, ...) @llvm.experimental.guard(i1 %entry.cond) [ "deopt"() ]
+  br label %loop
+
+loop:
+  %iv = phi i32 [ 0, %entry ], [ %iv.inc, %be ]
+  %iv.inc = add i32 %iv, 1
+
+  %cond = load volatile i1, i1* %cond_buf
+  br i1 %cond, label %left, label %be
+
+left:
+  ; Does not dominate the backedge, so cannot be used in the inductive proof
+  %iv.inc.cmp = icmp slt i32 %iv.inc, %len
+  call void(i1, ...) @llvm.experimental.guard(i1 %iv.inc.cmp) [ "deopt"() ]
+  br label %be
+
+be:
+; CHECK: be:
+; CHECK-NEXT:  %iv.cmp = icmp slt i32 %iv, %len
+; CHECK-NEXT:  call void (i1, ...) @llvm.experimental.guard(i1 %iv.cmp) [ "deopt"() ]
+; CHECK: leave:
+
+  %iv.cmp = icmp slt i32 %iv, %len
+  call void(i1, ...) @llvm.experimental.guard(i1 %iv.cmp) [ "deopt"() ]
+
+  %becond = load volatile i1, i1* %cond_buf
+  br i1 %becond, label %loop, label %leave
+
+leave:
+  ret void
+}