Skip to content

Commit

Permalink
[ScalarEvolution] Handle <= and >= in non infinite loops
Browse files Browse the repository at this point in the history
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
  • Loading branch information
wsmoses committed Jan 28, 2022
1 parent 354ec4a commit 99d2582
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 13 deletions.
6 changes: 4 additions & 2 deletions llvm/include/llvm/Analysis/ScalarEvolution.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
30 changes: 19 additions & 11 deletions llvm/lib/Analysis/ScalarEvolution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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;
Expand Down Expand Up @@ -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'.
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
}
Expand Down
175 changes: 175 additions & 0 deletions llvm/test/Analysis/ScalarEvolution/finite-trip-count.ll
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit 99d2582

Please sign in to comment.