[llvm] [SCEV] Add function to compute minium of countable exits. (PR #93498)
Florian Hahn via llvm-commits
llvm-commits at lists.llvm.org
Mon May 27 21:35:13 PDT 2024
https://github.com/fhahn updated https://github.com/llvm/llvm-project/pull/93498
>From 7945b8ed4e3f6246ff92899c0cf232a4b93fba57 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Mon, 27 May 2024 21:01:53 -0700
Subject: [PATCH 1/3] [SCEV] Compute symbolic max backedge taken count in BTI
directly.
---
llvm/include/llvm/Analysis/ScalarEvolution.h | 5 --
llvm/lib/Analysis/ScalarEvolution.cpp | 49 +++++++++-----------
2 files changed, 23 insertions(+), 31 deletions(-)
diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index 5828cc156cc78..1d016b28347d2 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -1761,11 +1761,6 @@ class ScalarEvolution {
ExitLimit computeExitLimit(const Loop *L, BasicBlock *ExitingBlock,
bool AllowPredicates = false);
- /// Return a symbolic upper bound for the backedge taken count of the loop.
- /// This is more general than getConstantMaxBackedgeTakenCount as it returns
- /// an arbitrary expression as opposed to only constants.
- const SCEV *computeSymbolicMaxBackedgeTakenCount(const Loop *L);
-
// Helper functions for computeExitLimitFromCond to avoid exponential time
// complexity.
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 8d971e6a78e42..2c4e08d05021e 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -8647,8 +8647,29 @@ ScalarEvolution::BackedgeTakenInfo::getConstantMax(ScalarEvolution *SE) const {
const SCEV *
ScalarEvolution::BackedgeTakenInfo::getSymbolicMax(const Loop *L,
ScalarEvolution *SE) {
- if (!SymbolicMax)
- SymbolicMax = SE->computeSymbolicMaxBackedgeTakenCount(L);
+ if (!SymbolicMax) {
+
+ // Form an expression for the maximum exit count possible for this loop. We
+ // merge the max and exact information to approximate a version of
+ // getConstantMaxBackedgeTakenCount which isn't restricted to just
+ // constants.
+ SmallVector<const SCEV *, 4> ExitCounts;
+
+ for (const auto &ENT : ExitNotTaken) {
+ const SCEV *ExitCount = ENT.SymbolicMaxNotTaken;
+ if (!isa<SCEVCouldNotCompute>(ExitCount)) {
+ assert(SE->DT.dominates(ENT.ExitingBlock, L->getLoopLatch()) &&
+ "We should only have known counts for exiting blocks that "
+ "dominate latch!");
+ ExitCounts.push_back(ExitCount);
+ }
+ }
+ if (ExitCounts.empty())
+ SymbolicMax = SE->getCouldNotCompute();
+ else
+ SymbolicMax =
+ SE->getUMinFromMismatchedTypes(ExitCounts, /*Sequential*/ true);
+ }
return SymbolicMax;
}
@@ -14964,30 +14985,6 @@ bool ScalarEvolution::matchURem(const SCEV *Expr, const SCEV *&LHS,
return false;
}
-const SCEV *
-ScalarEvolution::computeSymbolicMaxBackedgeTakenCount(const Loop *L) {
- SmallVector<BasicBlock*, 16> ExitingBlocks;
- L->getExitingBlocks(ExitingBlocks);
-
- // Form an expression for the maximum exit count possible for this loop. We
- // merge the max and exact information to approximate a version of
- // getConstantMaxBackedgeTakenCount which isn't restricted to just constants.
- SmallVector<const SCEV*, 4> ExitCounts;
- for (BasicBlock *ExitingBB : ExitingBlocks) {
- const SCEV *ExitCount =
- getExitCount(L, ExitingBB, ScalarEvolution::SymbolicMaximum);
- if (!isa<SCEVCouldNotCompute>(ExitCount)) {
- assert(DT.dominates(ExitingBB, L->getLoopLatch()) &&
- "We should only have known counts for exiting blocks that "
- "dominate latch!");
- ExitCounts.push_back(ExitCount);
- }
- }
- if (ExitCounts.empty())
- return getCouldNotCompute();
- return getUMinFromMismatchedTypes(ExitCounts, /*Sequential*/ true);
-}
-
/// A rewriter to replace SCEV expressions in Map with the corresponding entry
/// in the map. It skips AddRecExpr because we cannot guarantee that the
/// replacement is loop invariant in the loop of the AddRec.
>From 6afc39a555cb915440f521f38a701190b3bb9345 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Mon, 27 May 2024 21:29:32 -0700
Subject: [PATCH 2/3] [SCEV] Add tests for symbolic max BTC requiring
predicates.
---
...cated-symbolic-max-backedge-taken-count.ll | 113 ++++++++++++++++++
1 file changed, 113 insertions(+)
create mode 100644 llvm/test/Analysis/ScalarEvolution/predicated-symbolic-max-backedge-taken-count.ll
diff --git a/llvm/test/Analysis/ScalarEvolution/predicated-symbolic-max-backedge-taken-count.ll b/llvm/test/Analysis/ScalarEvolution/predicated-symbolic-max-backedge-taken-count.ll
new file mode 100644
index 0000000000000..506b3d7e63842
--- /dev/null
+++ b/llvm/test/Analysis/ScalarEvolution/predicated-symbolic-max-backedge-taken-count.ll
@@ -0,0 +1,113 @@
+; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes='print<scalar-evolution>' %s -disable-output 2>&1 | FileCheck %s
+
+target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
+
+; %i and %i + 1 can overflow.
+define void @test1(i64 %x, ptr %a, ptr %b) {
+; CHECK-LABEL: 'test1'
+; CHECK-NEXT: Classifying expressions for: @test1
+; CHECK-NEXT: %conv11 = phi i64 [ 0, %entry ], [ %conv, %latch ]
+; CHECK-NEXT: --> (zext i32 {0,+,1}<%header> to i64) U: [0,4294967296) S: [0,4294967296) Exits: <<Unknown>> LoopDispositions: { %header: Computable }
+; CHECK-NEXT: %i.010 = phi i32 [ 0, %entry ], [ %add, %latch ]
+; CHECK-NEXT: --> {0,+,1}<%header> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Computable }
+; CHECK-NEXT: %add = add i32 %i.010, 1
+; CHECK-NEXT: --> {1,+,1}<%header> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Computable }
+; CHECK-NEXT: %idxprom = zext i32 %add to i64
+; CHECK-NEXT: --> (zext i32 {1,+,1}<%header> to i64) U: [0,4294967296) S: [0,4294967296) Exits: <<Unknown>> LoopDispositions: { %header: Computable }
+; CHECK-NEXT: %arrayidx = getelementptr inbounds i32, ptr %a, i64 %idxprom
+; CHECK-NEXT: --> ((4 * (zext i32 {1,+,1}<%header> to i64))<nuw><nsw> + %a)<nuw> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Computable }
+; CHECK-NEXT: %ld = load i32, ptr %arrayidx, align 4
+; CHECK-NEXT: --> %ld U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Variant }
+; CHECK-NEXT: %add2 = add nsw i32 %ld, 1
+; CHECK-NEXT: --> (1 + %ld) U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Variant }
+; CHECK-NEXT: %arrayidx4 = getelementptr inbounds i32, ptr %b, i64 %conv11
+; CHECK-NEXT: --> ((4 * (zext i32 {0,+,1}<%header> to i64))<nuw><nsw> + %b) U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Computable }
+; CHECK-NEXT: %conv = zext i32 %add to i64
+; CHECK-NEXT: --> (zext i32 {1,+,1}<%header> to i64) U: [0,4294967296) S: [0,4294967296) Exits: <<Unknown>> LoopDispositions: { %header: Computable }
+; CHECK-NEXT: Determining loop execution counts for: @test1
+; CHECK-NEXT: Loop %header: <multiple exits> Unpredictable backedge-taken count.
+; CHECK-NEXT: exit count for header: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: exit count for latch: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: Loop %header: Unpredictable constant max backedge-taken count.
+; CHECK-NEXT: Loop %header: Unpredictable symbolic max backedge-taken count.
+; CHECK-NEXT: symbolic max exit count for header: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: symbolic max exit count for latch: ***COULDNOTCOMPUTE***
+;
+entry:
+ br label %header
+
+header:
+ %conv11 = phi i64 [ 0, %entry ], [ %conv, %latch ]
+ %i.010 = phi i32 [ 0, %entry ], [ %add, %latch ]
+ %add = add i32 %i.010, 1
+ %idxprom = zext i32 %add to i64
+ %arrayidx = getelementptr inbounds i32, ptr %a, i64 %idxprom
+ %ld = load i32, ptr %arrayidx, align 4
+ %uncountable.c = icmp eq i32 %ld, 10
+ br i1 %uncountable.c, label %exit, label %latch
+
+latch:
+ %add2 = add nsw i32 %ld, 1
+ %arrayidx4 = getelementptr inbounds i32, ptr %b, i64 %conv11
+ store i32 %add2, ptr %arrayidx4, align 4
+ %conv = zext i32 %add to i64
+ %cmp = icmp ult i64 %conv, %x
+ br i1 %cmp, label %header, label %exit
+
+exit:
+ ret void
+}
+
+; %i can overflow.
+;
+; We need to check that i doesn't wrap, but we don't need a run-time alias
+; check. We also need an extra no-wrap check to get the backedge taken count.
+define void @test2(i64 %x, ptr %a) {
+; CHECK-LABEL: 'test2'
+; CHECK-NEXT: Classifying expressions for: @test2
+; CHECK-NEXT: %conv11 = phi i64 [ 0, %entry ], [ %conv, %latch ]
+; CHECK-NEXT: --> (zext i32 {0,+,1}<%header> to i64) U: [0,4294967296) S: [0,4294967296) Exits: <<Unknown>> LoopDispositions: { %header: Computable }
+; CHECK-NEXT: %i.010 = phi i32 [ 0, %entry ], [ %inc, %latch ]
+; CHECK-NEXT: --> {0,+,1}<%header> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Computable }
+; CHECK-NEXT: %arrayidx = getelementptr inbounds i32, ptr %a, i64 %conv11
+; CHECK-NEXT: --> ((4 * (zext i32 {0,+,1}<%header> to i64))<nuw><nsw> + %a)<nuw> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Computable }
+; CHECK-NEXT: %ld = load i32, ptr %arrayidx, align 4
+; CHECK-NEXT: --> %ld U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Variant }
+; CHECK-NEXT: %add = add nsw i32 %ld, 1
+; CHECK-NEXT: --> (1 + %ld) U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Variant }
+; CHECK-NEXT: %inc = add i32 %i.010, 1
+; CHECK-NEXT: --> {1,+,1}<%header> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %header: Computable }
+; CHECK-NEXT: %conv = zext i32 %inc to i64
+; CHECK-NEXT: --> (zext i32 {1,+,1}<%header> to i64) U: [0,4294967296) S: [0,4294967296) Exits: <<Unknown>> LoopDispositions: { %header: Computable }
+; CHECK-NEXT: Determining loop execution counts for: @test2
+; CHECK-NEXT: Loop %header: <multiple exits> Unpredictable backedge-taken count.
+; CHECK-NEXT: exit count for header: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: exit count for latch: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: Loop %header: Unpredictable constant max backedge-taken count.
+; CHECK-NEXT: Loop %header: Unpredictable symbolic max backedge-taken count.
+; CHECK-NEXT: symbolic max exit count for header: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: symbolic max exit count for latch: ***COULDNOTCOMPUTE***
+;
+entry:
+ br label %header
+
+header:
+ %conv11 = phi i64 [ 0, %entry ], [ %conv, %latch ]
+ %i.010 = phi i32 [ 0, %entry ], [ %inc, %latch ]
+ %arrayidx = getelementptr inbounds i32, ptr %a, i64 %conv11
+ %ld = load i32, ptr %arrayidx, align 4
+ %uncountable.c = icmp eq i32 %ld, 10
+ br i1 %uncountable.c, label %exit, label %latch
+
+latch:
+ %add = add nsw i32 %ld, 1
+ store i32 %add, ptr %arrayidx, align 4
+ %inc = add i32 %i.010, 1
+ %conv = zext i32 %inc to i64
+ %cmp = icmp ult i64 %conv, %x
+ br i1 %cmp, label %header, label %exit
+
+exit:
+ ret void
+}
>From afe475dd14e84831a66d1345fec8b57921ec424a Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Mon, 27 May 2024 21:17:21 -0700
Subject: [PATCH 3/3] [SCEV] Add predicated version of
getSymbolicMaxBackedgeTakenCount.
---
llvm/include/llvm/Analysis/ScalarEvolution.h | 15 +++++-
llvm/lib/Analysis/ScalarEvolution.cpp | 49 +++++++++++++++++--
...cated-symbolic-max-backedge-taken-count.ll | 6 +++
3 files changed, 63 insertions(+), 7 deletions(-)
diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index 1d016b28347d2..d9e10dd3150a3 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -912,6 +912,9 @@ class ScalarEvolution {
return getBackedgeTakenCount(L, SymbolicMaximum);
}
+ const SCEV *getPredicatedSymbolicMaxBackedgeTakenCount(
+ const Loop *L, SmallVector<const SCEVPredicate *, 4> &Predicates);
+
/// Return true if the backedge taken count is either the value returned by
/// getConstantMaxBackedgeTakenCount or zero.
bool isBackedgeTakenCountMaxOrZero(const Loop *L);
@@ -1549,7 +1552,9 @@ class ScalarEvolution {
ScalarEvolution *SE) const;
/// Get the symbolic max backedge taken count for the loop.
- const SCEV *getSymbolicMax(const Loop *L, ScalarEvolution *SE);
+ const SCEV *
+ getSymbolicMax(const Loop *L, ScalarEvolution *SE,
+ SmallVector<const SCEVPredicate *, 4> *Predicates = nullptr);
/// Get the symbolic max backedge taken count for the particular loop exit.
const SCEV *getSymbolicMax(const BasicBlock *ExitingBlock,
@@ -1746,7 +1751,7 @@ class ScalarEvolution {
/// Similar to getBackedgeTakenInfo, but will add predicates as required
/// with the purpose of returning complete information.
- const BackedgeTakenInfo &getPredicatedBackedgeTakenInfo(const Loop *L);
+ BackedgeTakenInfo &getPredicatedBackedgeTakenInfo(const Loop *L);
/// Compute the number of times the specified loop will iterate.
/// If AllowPredicates is set, we will create new SCEV predicates as
@@ -2311,6 +2316,9 @@ class PredicatedScalarEvolution {
/// Get the (predicated) backedge count for the analyzed loop.
const SCEV *getBackedgeTakenCount();
+ /// Get the (predicated) symbolic max backedge count for the analyzed loop.
+ const SCEV *getSymbolicMaxBackedgeTakenCount();
+
/// Adds a new predicate.
void addPredicate(const SCEVPredicate &Pred);
@@ -2379,6 +2387,9 @@ class PredicatedScalarEvolution {
/// The backedge taken count.
const SCEV *BackedgeCount = nullptr;
+
+ /// The symbolic backedge taken count.
+ const SCEV *SymbolicMaxBackedgeCount = nullptr;
};
template <> struct DenseMapInfo<ScalarEvolution::FoldID> {
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 2c4e08d05021e..e46d7183a2a35 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -8295,6 +8295,11 @@ const SCEV *ScalarEvolution::getBackedgeTakenCount(const Loop *L,
llvm_unreachable("Invalid ExitCountKind!");
}
+const SCEV *ScalarEvolution::getPredicatedSymbolicMaxBackedgeTakenCount(
+ const Loop *L, SmallVector<const SCEVPredicate *, 4> &Preds) {
+ return getPredicatedBackedgeTakenInfo(L).getSymbolicMax(L, this, &Preds);
+}
+
bool ScalarEvolution::isBackedgeTakenCountMaxOrZero(const Loop *L) {
return getBackedgeTakenInfo(L).isConstantMaxOrZero(this);
}
@@ -8311,7 +8316,7 @@ static void PushLoopPHIs(const Loop *L,
Worklist.push_back(&PN);
}
-const ScalarEvolution::BackedgeTakenInfo &
+ScalarEvolution::BackedgeTakenInfo &
ScalarEvolution::getPredicatedBackedgeTakenInfo(const Loop *L) {
auto &BTI = getBackedgeTakenInfo(L);
if (BTI.hasFullInfo())
@@ -8644,11 +8649,10 @@ ScalarEvolution::BackedgeTakenInfo::getConstantMax(ScalarEvolution *SE) const {
return getConstantMax();
}
-const SCEV *
-ScalarEvolution::BackedgeTakenInfo::getSymbolicMax(const Loop *L,
- ScalarEvolution *SE) {
+const SCEV *ScalarEvolution::BackedgeTakenInfo::getSymbolicMax(
+ const Loop *L, ScalarEvolution *SE,
+ SmallVector<const SCEVPredicate *, 4> *Predicates) {
if (!SymbolicMax) {
-
// Form an expression for the maximum exit count possible for this loop. We
// merge the max and exact information to approximate a version of
// getConstantMaxBackedgeTakenCount which isn't restricted to just
@@ -8662,6 +8666,12 @@ ScalarEvolution::BackedgeTakenInfo::getSymbolicMax(const Loop *L,
"We should only have known counts for exiting blocks that "
"dominate latch!");
ExitCounts.push_back(ExitCount);
+ if (Predicates)
+ for (const auto *P : ENT.Predicates)
+ Predicates->push_back(P);
+
+ assert((Predicates || ENT.hasAlwaysTruePredicate()) &&
+ "Predicate should be always true!");
}
}
if (ExitCounts.empty())
@@ -13610,6 +13620,24 @@ static void PrintLoopInfo(raw_ostream &OS, ScalarEvolution *SE,
P->print(OS, 4);
}
+ Preds.clear();
+ auto *PredSymbolicMax =
+ SE->getPredicatedSymbolicMaxBackedgeTakenCount(L, Preds);
+ if (SymbolicBTC != PredSymbolicMax) {
+ OS << "Loop ";
+ L->getHeader()->printAsOperand(OS, /*PrintType=*/false);
+ OS << ": ";
+ if (!isa<SCEVCouldNotCompute>(PredSymbolicMax)) {
+ OS << "Predicated symbolic max backedge-taken count is ";
+ PrintSCEVWithTypeHint(OS, PredSymbolicMax);
+ } else
+ OS << "Unpredictable predicated symbolic max backedge-taken count.";
+ OS << "\n";
+ OS << " Predicates:\n";
+ for (const auto *P : Preds)
+ P->print(OS, 4);
+ }
+
if (SE->hasLoopInvariantBackedgeTakenCount(L)) {
OS << "Loop ";
L->getHeader()->printAsOperand(OS, /*PrintType=*/false);
@@ -14823,6 +14851,17 @@ const SCEV *PredicatedScalarEvolution::getBackedgeTakenCount() {
return BackedgeCount;
}
+const SCEV *PredicatedScalarEvolution::getSymbolicMaxBackedgeTakenCount() {
+ if (!SymbolicMaxBackedgeCount) {
+ SmallVector<const SCEVPredicate *, 4> Preds;
+ SymbolicMaxBackedgeCount =
+ SE.getPredicatedSymbolicMaxBackedgeTakenCount(&L, Preds);
+ for (const auto *P : Preds)
+ addPredicate(*P);
+ }
+ return SymbolicMaxBackedgeCount;
+}
+
void PredicatedScalarEvolution::addPredicate(const SCEVPredicate &Pred) {
if (Preds->implies(&Pred))
return;
diff --git a/llvm/test/Analysis/ScalarEvolution/predicated-symbolic-max-backedge-taken-count.ll b/llvm/test/Analysis/ScalarEvolution/predicated-symbolic-max-backedge-taken-count.ll
index 506b3d7e63842..ddbcd76e9fa86 100644
--- a/llvm/test/Analysis/ScalarEvolution/predicated-symbolic-max-backedge-taken-count.ll
+++ b/llvm/test/Analysis/ScalarEvolution/predicated-symbolic-max-backedge-taken-count.ll
@@ -33,6 +33,9 @@ define void @test1(i64 %x, ptr %a, ptr %b) {
; CHECK-NEXT: Loop %header: Unpredictable symbolic max backedge-taken count.
; CHECK-NEXT: symbolic max exit count for header: ***COULDNOTCOMPUTE***
; CHECK-NEXT: symbolic max exit count for latch: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: Loop %header: Predicated symbolic max backedge-taken count is (-1 + (1 umax %x))
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: {1,+,1}<%header> Added Flags: <nusw>
;
entry:
br label %header
@@ -88,6 +91,9 @@ define void @test2(i64 %x, ptr %a) {
; CHECK-NEXT: Loop %header: Unpredictable symbolic max backedge-taken count.
; CHECK-NEXT: symbolic max exit count for header: ***COULDNOTCOMPUTE***
; CHECK-NEXT: symbolic max exit count for latch: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: Loop %header: Predicated symbolic max backedge-taken count is (-1 + (1 umax %x))
+; CHECK-NEXT: Predicates:
+; CHECK-NEXT: {1,+,1}<%header> Added Flags: <nusw>
;
entry:
br label %header
More information about the llvm-commits
mailing list