[SCEV] Prove condition invariance via context, try 2

Initial implementation had too weak requirements to positive/negative
range crossings. Not crossing zero with nuw is not enough for two reasons:

- If ArLHS has negative step, it may turn from positive to negative
  without crossing 0 boundary from left to right (and crossing right to
  left doesn't count for unsigned);
- If ArLHS crosses SINT_MAX boundary, it still turns from positive to
  negative;

In fact we require that ArLHS always stays non-negative or negative,
which an be enforced by the following set of preconditions:

- both nuw and nsw;
- positive step (looks liftable);

Because of positive step, boundary crossing is only possible from left
part to the right part. And because of no-wrap flags, it is guaranteed
to never happen.
This commit is contained in:
Max Kazantsev 2022-08-22 13:09:20 +07:00
parent 5541e6bc05
commit e587199a50
5 changed files with 58 additions and 15 deletions

View File

@ -1091,7 +1091,8 @@ public:
/// 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,

View File

@ -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))
@ -10794,10 +10794,49 @@ ScalarEvolution::getLoopInvariantPredicate(ICmpInst::Predicate Pred,
bool Increasing = *MonotonicType == ScalarEvolution::MonotonicallyIncreasing;
auto P = Increasing ? Pred : ICmpInst::getInversePredicate(Pred);
if (!isLoopBackedgeGuardedByCond(L, P, LHS, RHS))
return None;
if (isLoopBackedgeGuardedByCond(L, P, LHS, RHS))
return ScalarEvolution::LoopInvariantPredicate(Pred, ArLHS->getStart(),
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 None;
}
Optional<ScalarEvolution::LoopInvariantPredicate>

View File

@ -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;

View File

@ -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:
@ -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:
@ -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:

View File

@ -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
@ -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
@ -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
@ -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