[llvm] 99d2582 - [ScalarEvolution] Handle <= and >= in non infinite loops

William S. Moses via llvm-commits llvm-commits at lists.llvm.org
Fri Jan 28 14:41:12 PST 2022


Author: William S. Moses
Date: 2022-01-28T17:41:08-05:00
New Revision: 99d2582164c4bc8f10c3e3c20dd3690243ad49ba

URL: https://github.com/llvm/llvm-project/commit/99d2582164c4bc8f10c3e3c20dd3690243ad49ba
DIFF: https://github.com/llvm/llvm-project/commit/99d2582164c4bc8f10c3e3c20dd3690243ad49ba.diff

LOG: [ScalarEvolution] Handle <= and >= in non infinite loops

Extend scalar evolution to handle >= and <= if a loop is known to be finite and the induction variable guards the condition. Specifically, with these assumptions lhs <= rhs is equivalent to lhs < rhs + 1 and lhs >= rhs to lhs > rhs -1.

In the case of lhs <= rhs, this is true since the only case these are not equivalent
is when rhs == unsigned/signed intmax, which would have resulted in an infinite loop.

In the case of lhs >= rhs, this is true since the only case these are not equivalent
is when rhs == unsigned/signed intmin, which would again have resulted in an infinite loop.

Reviewed By: lebedev.ri

Differential Revision: https://reviews.llvm.org/D118090

Added: 
    llvm/test/Analysis/ScalarEvolution/finite-trip-count.ll

Modified: 
    llvm/include/llvm/Analysis/ScalarEvolution.h
    llvm/lib/Analysis/ScalarEvolution.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index 1e6dac44cf2b7..b16aa70177190 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -1111,9 +1111,11 @@ class ScalarEvolution {
   /// 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 ControllingFiniteLoop = 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
index 56eb7c6b767d7..46833a8a681ba 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -8466,8 +8466,11 @@ ScalarEvolution::computeExitLimitFromICmp(const Loop *L,
     Pred = ICmpInst::getSwappedPredicate(Pred);
   }
 
+  bool ControllingFiniteLoop =
+      ControlsExit && loopHasNoAbnormalExits(L) && loopIsFiniteByAssumption(L);
   // Simplify the operands before analyzing them.
-  (void)SimplifyICmpOperands(Pred, LHS, RHS);
+  (void)SimplifyICmpOperands(Pred, LHS, RHS, /*Depth=*/0,
+                             ControllingFiniteLoop);
 
   // If we have a comparison of a chrec against a constant, try to use value
   // ranges to answer this query.
@@ -8487,9 +8490,7 @@ ScalarEvolution::computeExitLimitFromICmp(const Loop *L,
   // the same values on self-wrap of the IV, then we can infer that IV
   // doesn't self wrap because if it did, we'd have an infinite (undefined)
   // loop.
-  if (ControlsExit && isLoopInvariant(RHS, L) && loopHasNoAbnormalExits(L) &&
-      loopIsFiniteByAssumption(L)) {
-
+  if (ControllingFiniteLoop && isLoopInvariant(RHS, L)) {
     // TODO: We can peel off any functions which are invertible *in L*.  Loop
     // invariant terms are effectively constants for our purposes here.
     auto *InnerLHS = LHS;
@@ -9940,7 +9941,8 @@ static bool HasSameValue(const SCEV *A, const SCEV *B) {
 
 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 +10071,15 @@ bool ScalarEvolution::SimplifyICmpOperands(ICmpInst::Predicate &Pred,
   }
 
   // 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 controlling 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 +10092,7 @@ bool ScalarEvolution::SimplifyICmpOperands(ICmpInst::Predicate &Pred,
     }
     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 +10105,7 @@ bool ScalarEvolution::SimplifyICmpOperands(ICmpInst::Predicate &Pred,
     }
     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 +10117,7 @@ bool ScalarEvolution::SimplifyICmpOperands(ICmpInst::Predicate &Pred,
     }
     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 +10137,8 @@ bool ScalarEvolution::SimplifyICmpOperands(ICmpInst::Predicate &Pred,
   // 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
index 0000000000000..1c6cf2212d2ca
--- /dev/null
+++ b/llvm/test/Analysis/ScalarEvolution/finite-trip-count.ll
@@ -0,0 +1,175 @@
+; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py
+; RUN: opt < %s -disable-output "-passes=print<scalar-evolution>" -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) willreturn {
+; CHECK-LABEL: 'SLE'
+; CHECK-NEXT:  Determining loop execution counts for: @SLE
+; CHECK-NEXT:  Loop %for.body: backedge-taken count is (0 smax (1 + %len)<nsw>)
+; 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)<nsw>)
+; 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) nounwind willreturn
+  %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 @SLE_infinite(i32 %len) {
+; CHECK-LABEL: 'SLE_infinite'
+; CHECK-NEXT:  Determining loop execution counts for: @SLE_infinite
+; CHECK-NEXT:  Loop %for.body: Unpredictable backedge-taken count.
+; CHECK-NEXT:  Loop %for.body: Unpredictable max backedge-taken count.
+;
+entry:
+  br label %for.body
+
+for.body:
+  %iv = phi i32 [ %inc, %for.body ], [ 0, %entry ]
+  call void @non_exit_use(i32 %iv) nounwind willreturn
+  %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) willreturn {
+; CHECK-LABEL: 'ULE'
+; CHECK-NEXT:  Determining loop execution counts for: @ULE
+; CHECK-NEXT:  Loop %for.body: backedge-taken count is (1 + %len)<nuw>
+; CHECK-NEXT:  Loop %for.body: max backedge-taken count is -1
+; CHECK-NEXT:  Loop %for.body: Predicated backedge-taken count is (1 + %len)<nuw>
+; 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) nounwind willreturn
+  %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 @ULE_infinite(i32 %len) {
+; CHECK-LABEL: 'ULE_infinite'
+; CHECK-NEXT:  Determining loop execution counts for: @ULE_infinite
+; CHECK-NEXT:  Loop %for.body: Unpredictable backedge-taken count.
+; CHECK-NEXT:  Loop %for.body: Unpredictable max backedge-taken count.
+;
+entry:
+  br label %for.body
+
+for.body:
+  %iv = phi i32 [ %inc, %for.body ], [ 0, %entry ]
+  call void @non_exit_use(i32 %iv) nounwind willreturn
+  %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 %end) willreturn {
+; CHECK-LABEL: 'SGE'
+; CHECK-NEXT:  Determining loop execution counts for: @SGE
+; CHECK-NEXT: Loop %for.body: backedge-taken count is (100 + (-1 * (100 smin (-1 + %end)<nsw>)))
+; CHECK-NEXT: Loop %for.body: max backedge-taken count is -2147483548
+; CHECK-NEXT: Loop %for.body: Predicated backedge-taken count is (100 + (-1 * (100 smin (-1 + %end)<nsw>)))
+; CHECK-NEXT:   Predicates:
+;
+entry:
+  br label %for.body
+
+for.body:
+  %iv = phi i32 [ %inc, %for.body ], [ 100, %entry ]
+  call void @non_exit_use(i32 %iv) nounwind willreturn
+  %inc = add i32 %iv, -1
+  %cmp = icmp sge i32 %iv, %end
+  br i1 %cmp, label %for.body, label %for.end
+
+for.end:
+  ret void
+}
+
+define void @SGE_infinite(i32 %end) {
+; CHECK-LABEL: 'SGE_infinite'
+; CHECK-NEXT:  Determining loop execution counts for: @SGE_infinite
+; CHECK-NEXT:  Loop %for.body: Unpredictable backedge-taken count.
+; CHECK-NEXT:  Loop %for.body: Unpredictable max backedge-taken count.
+;
+entry:
+  br label %for.body
+
+for.body:
+  %iv = phi i32 [ %inc, %for.body ], [ 100, %entry ]
+  call void @non_exit_use(i32 %iv) nounwind willreturn
+  %inc = add i32 %iv, -1
+  %cmp = icmp sge i32 %iv, %end
+  br i1 %cmp, label %for.body, label %for.end
+
+for.end:
+  ret void
+}
+
+define void @UGE(i32 %end) willreturn {
+; CHECK-LABEL: 'UGE'
+; CHECK-NEXT:  Determining loop execution counts for: @UGE
+; CHECK-NEXT: Loop %for.body: backedge-taken count is (100 + (-1 * (100 umin (-1 + %end)))<nsw>)<nsw>
+; CHECK-NEXT: Loop %for.body: max backedge-taken count is 100
+; CHECK-NEXT: Loop %for.body: Predicated backedge-taken count is (100 + (-1 * (100 umin (-1 + %end)))<nsw>)<nsw>
+; CHECK-NEXT:   Predicates:
+;
+entry:
+  br label %for.body
+
+for.body:
+  %iv = phi i32 [ %inc, %for.body ], [ 100, %entry ]
+  call void @non_exit_use(i32 %iv) nounwind willreturn
+  %inc = add i32 %iv, -1
+  %cmp = icmp uge i32 %iv, %end
+  br i1 %cmp, label %for.body, label %for.end
+
+for.end:
+  ret void
+}
+
+define void @UGE_infinite(i32 %end) {
+; CHECK-LABEL: 'UGE_infinite'
+; CHECK-NEXT:  Determining loop execution counts for: @UGE_infinite
+; CHECK-NEXT:  Loop %for.body: Unpredictable backedge-taken count.
+; CHECK-NEXT:  Loop %for.body: Unpredictable max backedge-taken count.
+;
+entry:
+  br label %for.body
+
+for.body:
+  %iv = phi i32 [ %inc, %for.body ], [ 100, %entry ]
+  call void @non_exit_use(i32 %iv) nounwind willreturn
+  %inc = add i32 %iv, -1
+  %cmp = icmp uge i32 %iv, %end
+  br i1 %cmp, label %for.body, label %for.end
+
+for.end:
+  ret void
+}


        


More information about the llvm-commits mailing list