From: Sanjoy Das Date: Tue, 10 May 2016 00:31:49 +0000 (+0000) Subject: [SCEV] Use guards to prove predicates X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=cbda428c365eb501f11292870bee26d514a0e002;p=llvm [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. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@268997 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/llvm/Analysis/ScalarEvolution.h b/include/llvm/Analysis/ScalarEvolution.h index bb30c90abf9..e49f7d4f269 100644 --- a/include/llvm/Analysis/ScalarEvolution.h +++ b/include/llvm/Analysis/ScalarEvolution.h @@ -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. diff --git a/lib/Analysis/ScalarEvolution.cpp b/lib/Analysis/ScalarEvolution.cpp index d1a330d0948..be3902c99f4 100644 --- a/lib/Analysis/ScalarEvolution.cpp +++ b/lib/Analysis/ScalarEvolution.cpp @@ -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( + 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(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 index 00000000000..52ad4dc73d4 --- /dev/null +++ b/test/Analysis/ScalarEvolution/guards.ll @@ -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 +}