/// 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,
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))
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 the border of positive and negative parts of
+ // range because of:
+ // - Positive step; (TODO: lift this limitation)
+ // - nuw - does not cross zero boundary;
+ // - nsw - does not cross SINT_MAX boundary;
+ // (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 (ArLHS->hasNoSignedWrap() && ArLHS->isAffine() &&
+ isKnownPositive(ArLHS->getStepRecurrence(*this)) &&
+ 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>
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;
; 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:
; 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:
; 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:
; 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
; 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
; 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
; 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