From e587199a505bf517935026e878fc811a38532e4a Mon Sep 17 00:00:00 2001 From: Max Kazantsev Date: Mon, 22 Aug 2022 13:09:20 +0700 Subject: [PATCH] [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. --- llvm/include/llvm/Analysis/ScalarEvolution.h | 3 +- llvm/lib/Analysis/ScalarEvolution.cpp | 49 +++++++++++++++++-- llvm/lib/Transforms/Utils/SimplifyIndVar.cpp | 3 +- .../Transforms/IndVarSimplify/cycled_phis.ll | 10 ++-- .../Transforms/IndVarSimplify/outer_phi.ll | 8 +-- 5 files changed, 58 insertions(+), 15 deletions(-) diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h index 0de9ba7a4b8f..c258837d030c 100644 --- a/llvm/include/llvm/Analysis/ScalarEvolution.h +++ b/llvm/include/llvm/Analysis/ScalarEvolution.h @@ -1091,7 +1091,8 @@ public: /// invariants, available at L's entry. Otherwise, return None. Optional 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, diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp index b6d8281f42c7..6a180f1675cd 100644 --- a/llvm/lib/Analysis/ScalarEvolution.cpp +++ b/llvm/lib/Analysis/ScalarEvolution.cpp @@ -10756,8 +10756,8 @@ ScalarEvolution::getMonotonicPredicateTypeImpl(const SCEVAddRecExpr *LHS, Optional 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 0 + // we can replace the loop variant ArLHS ArLHS Start(ArLHS) >=s 0. + // We can strengthen this to Start(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 diff --git a/llvm/lib/Transforms/Utils/SimplifyIndVar.cpp b/llvm/lib/Transforms/Utils/SimplifyIndVar.cpp index 0a856eec3e12..08c48b02e591 100644 --- a/llvm/lib/Transforms/Utils/SimplifyIndVar.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyIndVar.cpp @@ -213,7 +213,8 @@ bool SimplifyIndvar::makeIVComparisonInvariant(ICmpInst *ICmp, auto *PN = dyn_cast(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; diff --git a/llvm/test/Transforms/IndVarSimplify/cycled_phis.ll b/llvm/test/Transforms/IndVarSimplify/cycled_phis.ll index 419844d05919..809ec68d483f 100644 --- a/llvm/test/Transforms/IndVarSimplify/cycled_phis.ll +++ b/llvm/test/Transforms/IndVarSimplify/cycled_phis.ll @@ -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: diff --git a/llvm/test/Transforms/IndVarSimplify/outer_phi.ll b/llvm/test/Transforms/IndVarSimplify/outer_phi.ll index 8da1648f64e5..19ea0a839056 100644 --- a/llvm/test/Transforms/IndVarSimplify/outer_phi.ll +++ b/llvm/test/Transforms/IndVarSimplify/outer_phi.ll @@ -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