diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h --- a/llvm/include/llvm/Analysis/ScalarEvolution.h +++ b/llvm/include/llvm/Analysis/ScalarEvolution.h @@ -1111,9 +1111,11 @@ /// Simplify LHS and RHS in a comparison with predicate Pred. Return true /// iff any changes were made. If the operands are provably equal or /// unequal, LHS and RHS are set to the same value and Pred is set to either - /// ICMP_EQ or ICMP_NE. + /// ICMP_EQ or ICMP_NE. ControllingFiniteLoop is set if this comparison + /// controls the exit of a loop known to have a finite number of iterations. bool SimplifyICmpOperands(ICmpInst::Predicate &Pred, const SCEV *&LHS, - const SCEV *&RHS, unsigned Depth = 0); + const SCEV *&RHS, unsigned Depth = 0, + bool ControlingFiniteLoop = false); /// Return the "disposition" of the given SCEV with respect to the given /// loop. diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp --- a/llvm/lib/Analysis/ScalarEvolution.cpp +++ b/llvm/lib/Analysis/ScalarEvolution.cpp @@ -8467,7 +8467,10 @@ } // Simplify the operands before analyzing them. - (void)SimplifyICmpOperands(Pred, LHS, RHS); + (void)SimplifyICmpOperands(Pred, LHS, RHS, /*Depth=*/0, + /*ControllingFiniteLoop=*/ControlsExit && + loopHasNoAbnormalExits(L) && + loopIsFiniteByAssumption(L)); // If we have a comparison of a chrec against a constant, try to use value // ranges to answer this query. @@ -9940,7 +9943,8 @@ bool ScalarEvolution::SimplifyICmpOperands(ICmpInst::Predicate &Pred, const SCEV *&LHS, const SCEV *&RHS, - unsigned Depth) { + unsigned Depth, + bool ControllingFiniteLoop) { bool Changed = false; // Simplifies ICMP to trivial true or false by turning it into '0 == 0' or // '0 != 0'. @@ -10069,10 +10073,15 @@ } // If possible, canonicalize GE/LE comparisons to GT/LT comparisons, by - // adding or subtracting 1 from one of the operands. + // adding or subtracting 1 from one of the operands. This can be done for + // one of two reasons: + // 1) The range of the RHS does not include the (signed/unsigned) boundaries + // 2) The loop is finite, with this comparison controling the exit. Since the + // loop is finite, the bound cannot include the corresponding boundary + // (otherwise it would loop forever). switch (Pred) { case ICmpInst::ICMP_SLE: - if (!getSignedRangeMax(RHS).isMaxSignedValue()) { + if (ControllingFiniteLoop || !getSignedRangeMax(RHS).isMaxSignedValue()) { RHS = getAddExpr(getConstant(RHS->getType(), 1, true), RHS, SCEV::FlagNSW); Pred = ICmpInst::ICMP_SLT; @@ -10085,7 +10094,7 @@ } break; case ICmpInst::ICMP_SGE: - if (!getSignedRangeMin(RHS).isMinSignedValue()) { + if (ControllingFiniteLoop || !getSignedRangeMin(RHS).isMinSignedValue()) { RHS = getAddExpr(getConstant(RHS->getType(), (uint64_t)-1, true), RHS, SCEV::FlagNSW); Pred = ICmpInst::ICMP_SGT; @@ -10098,7 +10107,7 @@ } break; case ICmpInst::ICMP_ULE: - if (!getUnsignedRangeMax(RHS).isMaxValue()) { + if (ControllingFiniteLoop || !getUnsignedRangeMax(RHS).isMaxValue()) { RHS = getAddExpr(getConstant(RHS->getType(), 1, true), RHS, SCEV::FlagNUW); Pred = ICmpInst::ICMP_ULT; @@ -10110,7 +10119,7 @@ } break; case ICmpInst::ICMP_UGE: - if (!getUnsignedRangeMin(RHS).isMinValue()) { + if (ControllingFiniteLoop || !getUnsignedRangeMin(RHS).isMinValue()) { RHS = getAddExpr(getConstant(RHS->getType(), (uint64_t)-1, true), RHS); Pred = ICmpInst::ICMP_UGT; Changed = true; @@ -10130,7 +10139,8 @@ // Recursively simplify until we either hit a recursion limit or nothing // changes. if (Changed) - return SimplifyICmpOperands(Pred, LHS, RHS, Depth+1); + return SimplifyICmpOperands(Pred, LHS, RHS, Depth + 1, + ControllingFiniteLoop); return Changed; } diff --git a/llvm/test/Analysis/ScalarEvolution/finite-trip-count.ll b/llvm/test/Analysis/ScalarEvolution/finite-trip-count.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Analysis/ScalarEvolution/finite-trip-count.ll @@ -0,0 +1,98 @@ +; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py +; RUN: opt < %s -disable-output "-passes=print" -scalar-evolution-max-iterations=0 -scalar-evolution-classify-expressions=0 2>&1 | FileCheck %s + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +declare void @non_exit_use(i32 %i) #0 + +define void @SLE(i32 %len) #1 { +; CHECK-LABEL: 'SLE' +; CHECK-NEXT: Determining loop execution counts for: @SLE +; CHECK-NEXT: Loop %for.body: backedge-taken count is (0 smax (1 + %len)) +; CHECK-NEXT: Loop %for.body: max backedge-taken count is 2147483647 +; CHECK-NEXT: Loop %for.body: Predicated backedge-taken count is (0 smax (1 + %len)) +; CHECK-NEXT: Predicates: +; +entry: + br label %for.body + +for.body: + %iv = phi i32 [ %inc, %for.body ], [ 0, %entry ] + call void @non_exit_use(i32 %iv) + %inc = add i32 %iv, 1 + %cmp = icmp sle i32 %iv, %len + br i1 %cmp, label %for.body, label %for.end + +for.end: + ret void +} + +define void @ULE(i32 %len) #1 { +; CHECK-LABEL: 'ULE' +; CHECK-NEXT: Determining loop execution counts for: @ULE +; CHECK-NEXT: Loop %for.body: backedge-taken count is (1 + %len) +; CHECK-NEXT: Loop %for.body: max backedge-taken count is -1 +; CHECK-NEXT: Loop %for.body: Predicated backedge-taken count is (1 + %len) +; CHECK-NEXT: Predicates: +; +entry: + br label %for.body + +for.body: + %iv = phi i32 [ %inc, %for.body ], [ 0, %entry ] + call void @non_exit_use(i32 %iv) + %inc = add i32 %iv, 1 + %cmp = icmp ule i32 %iv, %len + br i1 %cmp, label %for.body, label %for.end + +for.end: + ret void +} + +define void @SGE(i32 %len) #1 { +; CHECK-LABEL: 'SGE' +; CHECK-NEXT: Determining loop execution counts for: @SGE +; CHECK-NEXT: Loop %for.body: backedge-taken count is ((-1 * (0 smin %len)) + %len) +; CHECK-NEXT: Loop %for.body: max backedge-taken count is 2147483647 +; CHECK-NEXT: Loop %for.body: Predicated backedge-taken count is ((-1 * (0 smin %len)) + %len) +; CHECK-NEXT: Predicates: +; +entry: + br label %for.body + +for.body: + %iv = phi i32 [ %inc, %for.body ], [ %len, %entry ] + call void @non_exit_use(i32 %iv) + %inc = add i32 %iv, -1 + %cmp = icmp sge i32 %iv, 1 + br i1 %cmp, label %for.body, label %for.end + +for.end: + ret void +} + +define void @UGE(i32 %len) #1 { +; CHECK-LABEL: 'UGE' +; CHECK-NEXT: Determining loop execution counts for: @UGE +; CHECK-NEXT: Loop %for.body: backedge-taken count is %len +; CHECK-NEXT: Loop %for.body: max backedge-taken count is -1 +; CHECK-NEXT: Loop %for.body: Predicated backedge-taken count is %len +; CHECK-NEXT: Predicates: +; +entry: + br label %for.body + +for.body: + %iv = phi i32 [ %inc, %for.body ], [ %len, %entry ] + call void @non_exit_use(i32 %iv) + %inc = add i32 %iv, -1 + %cmp = icmp uge i32 %iv, 1 + br i1 %cmp, label %for.body, label %for.end + +for.end: + ret void +} + +attributes #0 = { nounwind willreturn } +attributes #1 = { willreturn }