diff --git a/llvm/include/llvm/Analysis/LoopInfo.h b/llvm/include/llvm/Analysis/LoopInfo.h --- a/llvm/include/llvm/Analysis/LoopInfo.h +++ b/llvm/include/llvm/Analysis/LoopInfo.h @@ -1332,6 +1332,10 @@ /// the containing function attribute too. bool hasMustProgress(const Loop *L); +/// Return true if this loop can be assumed to run for a finite number of +/// iterations. +bool isFinite(const Loop *L); + /// Return true if this loop can be assumed to make progress. (i.e. can't /// be infinite without side effects without also being undefined) bool isMustProgress(const Loop *L); 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/LoopInfo.cpp b/llvm/lib/Analysis/LoopInfo.cpp --- a/llvm/lib/Analysis/LoopInfo.cpp +++ b/llvm/lib/Analysis/LoopInfo.cpp @@ -1107,6 +1107,10 @@ return getOptionalIntLoopAttribute(TheLoop, Name).getValueOr(Default); } +bool llvm::isFinite(const Loop *L) { + return L->getHeader()->getParent()->willReturn(); +} + static const char *LLVMLoopMustProgress = "llvm.loop.mustprogress"; bool llvm::hasMustProgress(const Loop *L) { 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 @@ -7017,7 +7017,7 @@ // A mustprogress loop without side effects must be finite. // TODO: The check used here is very conservative. It's only *specific* // side effects which are well defined in infinite loops. - return isMustProgress(L) && loopHasNoSideEffects(L); + return isFinite(L) || (isMustProgress(L) && loopHasNoSideEffects(L)); } const SCEV *ScalarEvolution::createSCEV(Value *V) { @@ -8472,7 +8472,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. @@ -9945,7 +9948,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'. @@ -10074,10 +10078,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; @@ -10090,7 +10099,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; @@ -10103,7 +10112,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; @@ -10115,7 +10124,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; @@ -10135,7 +10144,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 }