Skip to content

Commit

Permalink
[SCEV] Prove condition invariance via context
Browse files Browse the repository at this point in the history
Contextual knowledge may be used to prove invariance of some conditions.
For example, in this case:
```
  ; %len >= 0
  guard(%iv = {start,+,1}<nuw> <s %len)
  guard(%iv = {start,+,1}<nuw> <u %len)
```
the 2nd check always fails if `start` is negative and always passes otherwise.

It looks like there are more opportunities of this kind that are still to be
implemented in the future.

Differential Revision: https://reviews.llvm.org/D129753
Reviewed By: apilipenko
  • Loading branch information
xortator committed Aug 12, 2022
1 parent 90736ba commit a3d1fb3
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 14 deletions.
3 changes: 2 additions & 1 deletion llvm/include/llvm/Analysis/ScalarEvolution.h
Expand Up @@ -1091,7 +1091,8 @@ class ScalarEvolution {
/// invariants, available at L's entry. Otherwise, return None.
Optional<LoopInvariantPredicate>
getLoopInvariantPredicate(ICmpInst::Predicate Pred, const SCEV *LHS,
const SCEV *RHS, const Loop *L);
const SCEV *RHS, const Loop *L,
const Instruction *CtxI = nullptr);

/// If the result of the predicate LHS `Pred` RHS is loop invariant with
/// respect to L at given Context during at least first MaxIter iterations,
Expand Down
41 changes: 37 additions & 4 deletions llvm/lib/Analysis/ScalarEvolution.cpp
Expand Up @@ -10756,8 +10756,8 @@ ScalarEvolution::getMonotonicPredicateTypeImpl(const SCEVAddRecExpr *LHS,
Optional<ScalarEvolution::LoopInvariantPredicate>
ScalarEvolution::getLoopInvariantPredicate(ICmpInst::Predicate Pred,
const SCEV *LHS, const SCEV *RHS,
const Loop *L) {

const Loop *L,
const Instruction *CtxI) {
// If there is a loop-invariant, force it into the RHS, otherwise bail out.
if (!isLoopInvariant(RHS, L)) {
if (!isLoopInvariant(LHS, L))
Expand Down Expand Up @@ -10794,10 +10794,43 @@ ScalarEvolution::getLoopInvariantPredicate(ICmpInst::Predicate Pred,
bool Increasing = *MonotonicType == ScalarEvolution::MonotonicallyIncreasing;
auto P = Increasing ? Pred : ICmpInst::getInversePredicate(Pred);

if (!isLoopBackedgeGuardedByCond(L, P, LHS, RHS))
if (isLoopBackedgeGuardedByCond(L, P, LHS, RHS))
return ScalarEvolution::LoopInvariantPredicate(Pred, ArLHS->getStart(),
RHS);

if (!CtxI)
return None;
// Try to prove via context.
// TODO: Support other cases.
switch (Pred) {
default:
break;
case ICmpInst::ICMP_ULE:
case ICmpInst::ICMP_ULT: {
assert(ArLHS->hasNoUnsignedWrap() && "Is a requirement of monotonicity!");
// Given preconditions
// (1) ArLHS does not cross 0 (due to NoUnsignedWrap)
// (2) ArLHS <s RHS
// (3) RHS >=s 0
// we can replace the loop variant ArLHS <u RHS condition with loop
// invariant Start(ArLHS) <u RHS.
//
// Because of (1) there are two options:
// - ArLHS is always negative. It means that ArLHS <u RHS is always false;
// - ArLHS is always non-negative. Because of (3) RHS is also non-negative.
// It means that ArLHS <s RHS <=> ArLHS <u RHS.
// Because of (2) ArLHS <u RHS is trivially true.
// All together it means that ArLHS <u RHS <=> Start(ArLHS) >=s 0.
// We can strengthen this to Start(ArLHS) <u RHS.
auto SignFlippedPred = ICmpInst::getFlippedSignednessPredicate(Pred);
if (isKnownNonNegative(RHS) &&
isKnownPredicateAt(SignFlippedPred, ArLHS, RHS, CtxI))
return ScalarEvolution::LoopInvariantPredicate(Pred, ArLHS->getStart(),
RHS);
}
}

return ScalarEvolution::LoopInvariantPredicate(Pred, ArLHS->getStart(), RHS);
return None;
}

Optional<ScalarEvolution::LoopInvariantPredicate>
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/Transforms/Utils/SimplifyIndVar.cpp
Expand Up @@ -213,7 +213,8 @@ bool SimplifyIndvar::makeIVComparisonInvariant(ICmpInst *ICmp,
auto *PN = dyn_cast<PHINode>(IVOperand);
if (!PN)
return false;
auto LIP = SE->getLoopInvariantPredicate(Pred, S, X, L);

auto LIP = SE->getLoopInvariantPredicate(Pred, S, X, L, ICmp);
if (!LIP)
return false;
ICmpInst::Predicate InvariantPredicate = LIP->Pred;
Expand Down
10 changes: 6 additions & 4 deletions llvm/test/Transforms/IndVarSimplify/cycled_phis.ll
Expand Up @@ -320,6 +320,8 @@ done:

; Slightly more complex version of previous one (cycled phis).
; TODO: remove unsigned comparison by proving non-negativity of iv.start.
; TODO: When we check against IV_START, for some reason we then cannot infer nuw for IV.next.
; It was possible while checking against IV. Missing inference logic somewhere.
define i32 @start.from.sibling.iv.wide.cycled.phis(i32* %len.ptr, i32* %sibling.len.ptr) {
; CHECK-LABEL: @start.from.sibling.iv.wide.cycled.phis(
; CHECK-NEXT: entry:
Expand Down Expand Up @@ -349,10 +351,10 @@ define i32 @start.from.sibling.iv.wide.cycled.phis(i32* %len.ptr, i32* %sibling.
; CHECK-NEXT: [[SIGNED_CMP:%.*]] = icmp slt i32 [[IV]], [[LEN]]
; CHECK-NEXT: br i1 [[SIGNED_CMP]], label [[SIGNED_PASSED:%.*]], label [[FAILED_SIGNED:%.*]]
; CHECK: signed.passed:
; CHECK-NEXT: [[UNSIGNED_CMP:%.*]] = icmp ult i32 [[IV]], [[LEN]]
; CHECK-NEXT: [[UNSIGNED_CMP:%.*]] = icmp ult i32 [[IV_START]], [[LEN]]
; CHECK-NEXT: br i1 [[UNSIGNED_CMP]], label [[BACKEDGE]], label [[FAILED_UNSIGNED:%.*]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1
; CHECK-NEXT: [[IV_NEXT]] = add nsw i32 [[IV]], 1
; CHECK-NEXT: [[COND:%.*]] = call i1 @cond()
; CHECK-NEXT: br i1 [[COND]], label [[LOOP]], label [[OUTER_LOOP_BACKEDGE]]
; CHECK: outer.loop.backedge:
Expand Down Expand Up @@ -467,10 +469,10 @@ define i32 @start.from.sibling.iv.wide.cycled.phis.complex.phis(i32* %len.ptr, i
; CHECK-NEXT: [[SIGNED_CMP:%.*]] = icmp slt i32 [[IV]], [[LEN]]
; CHECK-NEXT: br i1 [[SIGNED_CMP]], label [[SIGNED_PASSED:%.*]], label [[FAILED_SIGNED:%.*]]
; CHECK: signed.passed:
; CHECK-NEXT: [[UNSIGNED_CMP:%.*]] = icmp ult i32 [[IV]], [[LEN]]
; CHECK-NEXT: [[UNSIGNED_CMP:%.*]] = icmp ult i32 [[IV_START]], [[LEN]]
; CHECK-NEXT: br i1 [[UNSIGNED_CMP]], label [[BACKEDGE]], label [[FAILED_UNSIGNED:%.*]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1
; CHECK-NEXT: [[IV_NEXT]] = add nsw i32 [[IV]], 1
; CHECK-NEXT: [[COND:%.*]] = call i1 @cond()
; CHECK-NEXT: br i1 [[COND]], label [[LOOP]], label [[OUTER_LOOP_SELECTION:%.*]]
; CHECK: outer.loop.selection:
Expand Down
8 changes: 4 additions & 4 deletions llvm/test/Transforms/IndVarSimplify/outer_phi.ll
Expand Up @@ -412,7 +412,7 @@ define i32 @test_05(i32 %a, i32* %bp) {
; CHECK-NEXT: [[SIGNED_COND:%.*]] = icmp slt i32 [[IV]], [[B]]
; CHECK-NEXT: br i1 [[SIGNED_COND]], label [[INNER_1:%.*]], label [[SIDE_EXIT:%.*]]
; CHECK: inner.1:
; CHECK-NEXT: [[UNSIGNED_COND:%.*]] = icmp ult i32 [[IV]], [[B]]
; CHECK-NEXT: [[UNSIGNED_COND:%.*]] = icmp ult i32 [[OUTER_IV]], [[B]]
; CHECK-NEXT: br i1 [[UNSIGNED_COND]], label [[INNER_BACKEDGE]], label [[SIDE_EXIT]]
; CHECK: inner.backedge:
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1
Expand Down Expand Up @@ -974,7 +974,7 @@ define i32 @test_06(i32 %a, i32* %bp) {
; CHECK-NEXT: [[SIGNED_COND:%.*]] = icmp slt i32 [[IV]], [[B]]
; CHECK-NEXT: br i1 [[SIGNED_COND]], label [[INNER_1:%.*]], label [[SIDE_EXIT:%.*]]
; CHECK: inner.1:
; CHECK-NEXT: [[UNSIGNED_COND:%.*]] = icmp ult i32 [[IV]], [[B]]
; CHECK-NEXT: [[UNSIGNED_COND:%.*]] = icmp ult i32 [[OUTER_IV]], [[B]]
; CHECK-NEXT: br i1 [[UNSIGNED_COND]], label [[INNER_BACKEDGE]], label [[SIDE_EXIT]]
; CHECK: inner.backedge:
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1
Expand Down Expand Up @@ -1045,7 +1045,7 @@ define i32 @test_07(i32 %a, i32* %bp) {
; CHECK-NEXT: [[SIGNED_COND:%.*]] = icmp slt i32 [[IV]], [[B]]
; CHECK-NEXT: br i1 [[SIGNED_COND]], label [[INNER_1:%.*]], label [[SIDE_EXIT:%.*]]
; CHECK: inner.1:
; CHECK-NEXT: [[UNSIGNED_COND:%.*]] = icmp ult i32 [[IV]], [[B]]
; CHECK-NEXT: [[UNSIGNED_COND:%.*]] = icmp ult i32 [[OUTER_IV]], [[B]]
; CHECK-NEXT: br i1 [[UNSIGNED_COND]], label [[INNER_BACKEDGE]], label [[SIDE_EXIT]]
; CHECK: inner.backedge:
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1
Expand Down Expand Up @@ -1128,7 +1128,7 @@ define i32 @test_08(i32 %a, i32* %bp) {
; CHECK-NEXT: [[SIGNED_COND:%.*]] = icmp slt i32 [[IV]], [[B]]
; CHECK-NEXT: br i1 [[SIGNED_COND]], label [[INNER_1:%.*]], label [[SIDE_EXIT:%.*]]
; CHECK: inner.1:
; CHECK-NEXT: [[UNSIGNED_COND:%.*]] = icmp ult i32 [[IV]], [[B]]
; CHECK-NEXT: [[UNSIGNED_COND:%.*]] = icmp ult i32 [[OUTER_IV]], [[B]]
; CHECK-NEXT: br i1 [[UNSIGNED_COND]], label [[INNER_BACKEDGE]], label [[SIDE_EXIT]]
; CHECK: inner.backedge:
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1
Expand Down

0 comments on commit a3d1fb3

Please sign in to comment.