[llvm] r268997 - [SCEV] Use guards to prove predicates

Sanjoy Das via llvm-commits llvm-commits at lists.llvm.org
Mon May 9 17:31:50 PDT 2016


Author: sanjoy
Date: Mon May  9 19:31:49 2016
New Revision: 268997

URL: http://llvm.org/viewvc/llvm-project?rev=268997&view=rev
Log:
[SCEV] Use guards to prove predicates

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.

Added:
    llvm/trunk/test/Analysis/ScalarEvolution/guards.ll
Modified:
    llvm/trunk/include/llvm/Analysis/ScalarEvolution.h
    llvm/trunk/lib/Analysis/ScalarEvolution.cpp

Modified: llvm/trunk/include/llvm/Analysis/ScalarEvolution.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Analysis/ScalarEvolution.h?rev=268997&r1=268996&r2=268997&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Analysis/ScalarEvolution.h (original)
+++ llvm/trunk/include/llvm/Analysis/ScalarEvolution.h Mon May  9 19:31:49 2016
@@ -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.

Modified: llvm/trunk/lib/Analysis/ScalarEvolution.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/ScalarEvolution.cpp?rev=268997&r1=268996&r2=268997&view=diff
==============================================================================
--- llvm/trunk/lib/Analysis/ScalarEvolution.cpp (original)
+++ llvm/trunk/lib/Analysis/ScalarEvolution.cpp Mon May  9 19:31:49 2016
@@ -7805,6 +7805,23 @@ bool ScalarEvolution::isKnownPredicateVi
          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::isLoopBackedgeGuardedBy
   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::isLoopEntryGuardedByCon
        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(Functio
       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)),

Added: llvm/trunk/test/Analysis/ScalarEvolution/guards.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Analysis/ScalarEvolution/guards.ll?rev=268997&view=auto
==============================================================================
--- llvm/trunk/test/Analysis/ScalarEvolution/guards.ll (added)
+++ llvm/trunk/test/Analysis/ScalarEvolution/guards.ll Mon May  9 19:31:49 2016
@@ -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
+}




More information about the llvm-commits mailing list