From 23381e1b4083d70cff406319560ff2ddee9429b3 Mon Sep 17 00:00:00 2001 From: Max Kazantsev Date: Wed, 7 Feb 2018 07:56:26 +0000 Subject: [PATCH] [SCEV] Make isLoopEntryGuardedByCond a bit smarter Sometimes `isLoopEntryGuardedByCond` cannot prove predicate `a > b` directly. But it is a common situation when `a >= b` is known from ranges and `a != b` is known from a dominating condition. Thia patch teaches SCEV to sum these facts together and prove strict comparison via non-strict one. Differential Revision: https://reviews.llvm.org/D42835 llvm-svn: 324453 --- include/llvm/IR/InstrTypes.h | 13 +++ lib/Analysis/ScalarEvolution.cpp | 62 +++++++++++-- lib/IR/Instructions.cpp | 14 +++ test/Transforms/IRCE/conjunctive-checks.ll | 6 +- test/Transforms/IRCE/decrementing-loop.ll | 86 ++++++++++++++++++- .../IRCE/single-access-no-preloop.ll | 6 +- .../loop-invariant-conditions.ll | 30 +++++++ 7 files changed, 205 insertions(+), 12 deletions(-) diff --git a/include/llvm/IR/InstrTypes.h b/include/llvm/IR/InstrTypes.h index 871f702f95f..9e938a462cf 100644 --- a/include/llvm/IR/InstrTypes.h +++ b/include/llvm/IR/InstrTypes.h @@ -973,6 +973,19 @@ public: /// @brief Return the predicate as if the operands were swapped. static Predicate getSwappedPredicate(Predicate pred); + /// For example, SGT -> SGE, SLT -> SLE, ULT -> ULE, UGT -> UGE. + /// @brief Returns the non-strict version of strict comparisons. + Predicate getNonStrictPredicate() const { + return getNonStrictPredicate(getPredicate()); + } + + /// This is a static version that you can use without an instruction + /// available. + /// @returns the non-strict version of comparison provided in \p pred. + /// If \p pred is not a strict comparison predicate, returns \p pred. + /// @brief Returns the non-strict version of strict comparisons. + static Predicate getNonStrictPredicate(Predicate pred); + /// @brief Provide more efficient getOperand methods. DECLARE_TRANSPARENT_OPERAND_ACCESSORS(Value); diff --git a/lib/Analysis/ScalarEvolution.cpp b/lib/Analysis/ScalarEvolution.cpp index f44bb8d447e..8f5e0bc51df 100644 --- a/lib/Analysis/ScalarEvolution.cpp +++ b/lib/Analysis/ScalarEvolution.cpp @@ -9073,6 +9073,59 @@ ScalarEvolution::isLoopEntryGuardedByCond(const Loop *L, if (isKnownPredicateViaConstantRanges(Pred, LHS, RHS)) return true; + // If we cannot prove strict comparison (e.g. a > b), maybe we can prove + // the facts (a >= b && a != b) separately. A typical situation is when the + // non-strict comparison is known from ranges and non-equality is known from + // dominating predicates. If we are proving strict comparison, we always try + // to prove non-equality and non-strict comparison separately. + auto NonStrictPredicate = ICmpInst::getNonStrictPredicate(Pred); + const bool ProvingStrictComparison = (Pred != NonStrictPredicate); + bool ProvedNonStrictComparison = false; + bool ProvedNonEquality = false; + + if (ProvingStrictComparison) { + ProvedNonStrictComparison = + isKnownPredicateViaConstantRanges(NonStrictPredicate, LHS, RHS); + ProvedNonEquality = + isKnownPredicateViaConstantRanges(ICmpInst::ICMP_NE, LHS, RHS); + assert((!ProvedNonStrictComparison || !ProvedNonEquality) && + "Why we were unable to prove (Pred, LHS, RHS) via constant ranges?"); + } + + // Try to prove (Pred, LHS, RHS) using isImpliedViaGuard. + auto ProveViaGuard = [&](BasicBlock *Block) { + if (isImpliedViaGuard(Block, Pred, LHS, RHS)) + return true; + if (ProvingStrictComparison) { + if (!ProvedNonStrictComparison) + ProvedNonStrictComparison = + isImpliedViaGuard(Block, NonStrictPredicate, LHS, RHS); + if (!ProvedNonEquality) + ProvedNonEquality = + isImpliedViaGuard(Block, ICmpInst::ICMP_NE, LHS, RHS); + if (ProvedNonStrictComparison && ProvedNonEquality) + return true; + } + return false; + }; + + // Try to prove (Pred, LHS, RHS) using isImpliedCond. + auto ProveViaCond = [&](Value *Condition, bool Inverse) { + if (isImpliedCond(Pred, LHS, RHS, Condition, Inverse)) + return true; + if (ProvingStrictComparison) { + if (!ProvedNonStrictComparison) + ProvedNonStrictComparison = + isImpliedCond(NonStrictPredicate, LHS, RHS, Condition, Inverse); + if (!ProvedNonEquality) + ProvedNonEquality = + isImpliedCond(ICmpInst::ICMP_NE, LHS, RHS, Condition, Inverse); + if (ProvedNonStrictComparison && ProvedNonEquality) + return true; + } + return false; + }; + // Starting at the loop predecessor, climb up the predecessor chain, as long // as there are predecessors that can be found that have unique successors // leading to the original header. @@ -9081,7 +9134,7 @@ ScalarEvolution::isLoopEntryGuardedByCond(const Loop *L, Pair.first; Pair = getPredecessorWithUniqueSuccessorForBB(Pair.first)) { - if (isImpliedViaGuard(Pair.first, Pred, LHS, RHS)) + if (ProveViaGuard(Pair.first)) return true; BranchInst *LoopEntryPredicate = @@ -9090,9 +9143,8 @@ ScalarEvolution::isLoopEntryGuardedByCond(const Loop *L, LoopEntryPredicate->isUnconditional()) continue; - if (isImpliedCond(Pred, LHS, RHS, - LoopEntryPredicate->getCondition(), - LoopEntryPredicate->getSuccessor(0) != Pair.second)) + if (ProveViaCond(LoopEntryPredicate->getCondition(), + LoopEntryPredicate->getSuccessor(0) != Pair.second)) return true; } @@ -9104,7 +9156,7 @@ ScalarEvolution::isLoopEntryGuardedByCond(const Loop *L, if (!DT.dominates(CI, L->getHeader())) continue; - if (isImpliedCond(Pred, LHS, RHS, CI->getArgOperand(0), false)) + if (ProveViaCond(CI->getArgOperand(0), false)) return true; } diff --git a/lib/IR/Instructions.cpp b/lib/IR/Instructions.cpp index 490fcbce743..7fa4b06c566 100644 --- a/lib/IR/Instructions.cpp +++ b/lib/IR/Instructions.cpp @@ -3467,6 +3467,20 @@ CmpInst::Predicate CmpInst::getSwappedPredicate(Predicate pred) { } } +CmpInst::Predicate CmpInst::getNonStrictPredicate(Predicate pred) { + switch (pred) { + case ICMP_SGT: return ICMP_SGE; + case ICMP_SLT: return ICMP_SLE; + case ICMP_UGT: return ICMP_UGE; + case ICMP_ULT: return ICMP_ULE; + case FCMP_OGT: return FCMP_OGE; + case FCMP_OLT: return FCMP_OLE; + case FCMP_UGT: return FCMP_UGE; + case FCMP_ULT: return FCMP_ULE; + default: return pred; + } +} + CmpInst::Predicate CmpInst::getSignedPredicate(Predicate pred) { assert(CmpInst::isUnsigned(pred) && "Call only with signed predicates!"); diff --git a/test/Transforms/IRCE/conjunctive-checks.ll b/test/Transforms/IRCE/conjunctive-checks.ll index f6a909e432c..b1a7063523d 100644 --- a/test/Transforms/IRCE/conjunctive-checks.ll +++ b/test/Transforms/IRCE/conjunctive-checks.ll @@ -4,10 +4,10 @@ define void @f_0(i32 *%arr, i32 *%a_len_ptr, i32 %n, i1* %cond_buf) { ; CHECK-LABEL: @f_0( ; CHECK: loop.preheader: -; CHECK: [[not_n:[^ ]+]] = sub i32 -1, %n ; CHECK: [[not_safe_range_end:[^ ]+]] = sub i32 3, %len -; CHECK: [[not_exit_main_loop_at_hiclamp_cmp:[^ ]+]] = icmp sgt i32 [[not_n]], [[not_safe_range_end]] -; CHECK: [[not_exit_main_loop_at_hiclamp:[^ ]+]] = select i1 [[not_exit_main_loop_at_hiclamp_cmp]], i32 [[not_n]], i32 [[not_safe_range_end]] +; CHECK: [[not_n:[^ ]+]] = sub i32 -1, %n +; CHECK: [[not_exit_main_loop_at_hiclamp_cmp:[^ ]+]] = icmp sgt i32 [[not_safe_range_end]], [[not_n]] +; CHECK: [[not_exit_main_loop_at_hiclamp:[^ ]+]] = select i1 [[not_exit_main_loop_at_hiclamp_cmp]], i32 [[not_safe_range_end]], i32 [[not_n]] ; CHECK: [[exit_main_loop_at_hiclamp:[^ ]+]] = sub i32 -1, [[not_exit_main_loop_at_hiclamp]] ; CHECK: [[exit_main_loop_at_loclamp_cmp:[^ ]+]] = icmp sgt i32 [[exit_main_loop_at_hiclamp]], 0 ; CHECK: [[exit_main_loop_at_loclamp:[^ ]+]] = select i1 [[exit_main_loop_at_loclamp_cmp]], i32 [[exit_main_loop_at_hiclamp]], i32 0 diff --git a/test/Transforms/IRCE/decrementing-loop.ll b/test/Transforms/IRCE/decrementing-loop.ll index fac873b4a24..395e73c5566 100644 --- a/test/Transforms/IRCE/decrementing-loop.ll +++ b/test/Transforms/IRCE/decrementing-loop.ll @@ -1,4 +1,8 @@ -; RUN: opt -verify-loop-info -irce -S < %s | FileCheck %s +; RUN: opt -verify-loop-info -irce -debug-only=irce -S < %s 2>&1 | FileCheck %s + +; CHECK-LABEL: irce: in function decrementing_loop: constrained Loop at depth 1 +; CHECK-LABEL: irce: in function test_01: constrained Loop at depth 1 +; CHECK-LABEL: irce: in function test_02: constrained Loop at depth 1 define void @decrementing_loop(i32 *%arr, i32 *%a_len_ptr, i32 %n) { entry: @@ -38,5 +42,85 @@ define void @decrementing_loop(i32 *%arr, i32 *%a_len_ptr, i32 %n) { ; CHECK: %exit.preloop.at = add i32 [[not_exit_preloop_at]], -1 } +; Make sure that we can eliminate the range check when the loop looks like: +; for (i = len.a - 1; i >= 0; --i) +; b[i] = a[i]; +define void @test_01(i32* %a, i32* %b, i32* %a_len_ptr, i32* %b_len_ptr) { + +; CHECK-LABEL: test_01 +; CHECK: mainloop: +; CHECK-NEXT: br label %loop +; CHECK: loop: +; CHECK: %rc = and i1 true, true +; CHECK: loop.preloop: + + entry: + %len.a = load i32, i32* %a_len_ptr, !range !0 + %len.b = load i32, i32* %b_len_ptr, !range !0 + %first.itr.check = icmp ne i32 %len.a, 0 + br i1 %first.itr.check, label %loop, label %exit + + loop: + %idx = phi i32 [ %len.a, %entry ] , [ %idx.next, %in.bounds ] + %idx.next = sub i32 %idx, 1 + %rca = icmp ult i32 %idx.next, %len.a + %rcb = icmp ult i32 %idx.next, %len.b + %rc = and i1 %rca, %rcb + br i1 %rc, label %in.bounds, label %out.of.bounds, !prof !1 + + in.bounds: + %el.a = getelementptr i32, i32* %a, i32 %idx.next + %el.b = getelementptr i32, i32* %b, i32 %idx.next + %v = load i32, i32* %el.a + store i32 %v, i32* %el.b + %loop.cond = icmp slt i32 %idx, 2 + br i1 %loop.cond, label %exit, label %loop + + out.of.bounds: + ret void + + exit: + ret void +} + +; Same as test_01, but the latch condition is unsigned +define void @test_02(i32* %a, i32* %b, i32* %a_len_ptr, i32* %b_len_ptr) { + +; CHECK-LABEL: test_02 +; CHECK: mainloop: +; CHECK-NEXT: br label %loop +; CHECK: loop: +; CHECK: %rc = and i1 true, true +; CHECK: loop.preloop: + + entry: + %len.a = load i32, i32* %a_len_ptr, !range !0 + %len.b = load i32, i32* %b_len_ptr, !range !0 + %first.itr.check = icmp ne i32 %len.a, 0 + br i1 %first.itr.check, label %loop, label %exit + + loop: + %idx = phi i32 [ %len.a, %entry ] , [ %idx.next, %in.bounds ] + %idx.next = sub i32 %idx, 1 + %rca = icmp ult i32 %idx.next, %len.a + %rcb = icmp ult i32 %idx.next, %len.b + %rc = and i1 %rca, %rcb + br i1 %rc, label %in.bounds, label %out.of.bounds, !prof !1 + + in.bounds: + %el.a = getelementptr i32, i32* %a, i32 %idx.next + %el.b = getelementptr i32, i32* %b, i32 %idx.next + %v = load i32, i32* %el.a + store i32 %v, i32* %el.b + %loop.cond = icmp ult i32 %idx, 2 + br i1 %loop.cond, label %exit, label %loop + + out.of.bounds: + ret void + + exit: + ret void +} + !0 = !{i32 0, i32 2147483647} !1 = !{!"branch_weights", i32 64, i32 4} diff --git a/test/Transforms/IRCE/single-access-no-preloop.ll b/test/Transforms/IRCE/single-access-no-preloop.ll index 53f430d0ba3..c9da4e28950 100644 --- a/test/Transforms/IRCE/single-access-no-preloop.ll +++ b/test/Transforms/IRCE/single-access-no-preloop.ll @@ -85,10 +85,10 @@ define void @single_access_no_preloop_with_offset(i32 *%arr, i32 *%a_len_ptr, i3 ; CHECK-LABEL: @single_access_no_preloop_with_offset( ; CHECK: loop.preheader: -; CHECK: [[not_n:[^ ]+]] = sub i32 -1, %n ; CHECK: [[not_safe_range_end:[^ ]+]] = sub i32 3, %len -; CHECK: [[not_exit_main_loop_at_hiclamp_cmp:[^ ]+]] = icmp sgt i32 [[not_n]], [[not_safe_range_end]] -; CHECK: [[not_exit_main_loop_at_hiclamp:[^ ]+]] = select i1 [[not_exit_main_loop_at_hiclamp_cmp]], i32 [[not_n]], i32 [[not_safe_range_end]] +; CHECK: [[not_n:[^ ]+]] = sub i32 -1, %n +; CHECK: [[not_exit_main_loop_at_hiclamp_cmp:[^ ]+]] = icmp sgt i32 [[not_safe_range_end]], [[not_n]] +; CHECK: [[not_exit_main_loop_at_hiclamp:[^ ]+]] = select i1 [[not_exit_main_loop_at_hiclamp_cmp]], i32 [[not_safe_range_end]], i32 [[not_n]] ; CHECK: [[exit_main_loop_at_hiclamp:[^ ]+]] = sub i32 -1, [[not_exit_main_loop_at_hiclamp]] ; CHECK: [[exit_main_loop_at_loclamp_cmp:[^ ]+]] = icmp sgt i32 [[exit_main_loop_at_hiclamp]], 0 ; CHECK: [[exit_main_loop_at_loclamp:[^ ]+]] = select i1 [[exit_main_loop_at_loclamp_cmp]], i32 [[exit_main_loop_at_hiclamp]], i32 0 diff --git a/test/Transforms/IndVarSimplify/loop-invariant-conditions.ll b/test/Transforms/IndVarSimplify/loop-invariant-conditions.ll index 70cf714ba9f..30249828107 100644 --- a/test/Transforms/IndVarSimplify/loop-invariant-conditions.ll +++ b/test/Transforms/IndVarSimplify/loop-invariant-conditions.ll @@ -325,6 +325,36 @@ exit: ret void } +; check that we can figure out that iv.next > 1 from the facts that iv >= 0 and +; iv.start != 0. +define void @test11(i64* %inc_ptr) { +; CHECK-LABEL: @test11 +entry: + %inc = load i64, i64* %inc_ptr, !range !0 + %ne.cond = icmp ne i64 %inc, 0 + br i1 %ne.cond, label %loop, label %exit + +loop: + %iv = phi i64 [ %inc, %entry ], [ %iv.next, %backedge ] + %iv.next = add i64 %iv, 1 + %brcond = icmp sgt i64 %iv.next, 1 + ; CHECK: br i1 true, label %if.true, label %if.false + br i1 %brcond, label %if.true, label %if.false + +if.true: + br label %backedge + +if.false: + br label %backedge + +backedge: + %loopcond = icmp slt i64 %iv, 200 + br i1 %loopcond, label %loop, label %exit + +exit: + ret void +} + !1 = !{i64 -1, i64 100}