[llvm] a3d1fb3 - [SCEV] Prove condition invariance via context
Max Kazantsev via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 12 00:23:59 PDT 2022
Author: Max Kazantsev
Date: 2022-08-12T14:23:35+07:00
New Revision: a3d1fb3b59b473e4f262a05f187de6474b7721e7
URL: https://github.com/llvm/llvm-project/commit/a3d1fb3b59b473e4f262a05f187de6474b7721e7
DIFF: https://github.com/llvm/llvm-project/commit/a3d1fb3b59b473e4f262a05f187de6474b7721e7.diff
LOG: [SCEV] Prove condition invariance via context
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
Added:
Modified:
llvm/include/llvm/Analysis/ScalarEvolution.h
llvm/lib/Analysis/ScalarEvolution.cpp
llvm/lib/Transforms/Utils/SimplifyIndVar.cpp
llvm/test/Transforms/IndVarSimplify/cycled_phis.ll
llvm/test/Transforms/IndVarSimplify/outer_phi.ll
Removed:
################################################################################
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 @@ 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,
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 5bd110942597..c09437afae4b 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -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,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>
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<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;
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
More information about the llvm-commits
mailing list