[llvm] [SCEV] Use SCEVUse for ValuesAtScope (NFC). (PR #207062)
Florian Hahn via llvm-commits
llvm-commits at lists.llvm.org
Sun Aug 16 13:30:55 PDT 2026
https://github.com/fhahn updated https://github.com/llvm/llvm-project/pull/207062
>From 980fba1c2f6cf2aaeb35e31884c8fc2879da66ff Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Sun, 16 Aug 2026 13:58:15 +0100
Subject: [PATCH] [SCEV] Use SCEVUse for ValuesAtScope (NFC).
Update ValuesAtScope to track SCEVUse. This also requires tracking a
mapping of canonical SCEVs to corresponding SCEVUse, for invalidation.
Currently NFC, prepares for follow-up.
---
llvm/include/llvm/Analysis/ScalarEvolution.h | 32 ++++++--
llvm/lib/Analysis/ScalarEvolution.cpp | 84 +++++++++++++-------
2 files changed, 82 insertions(+), 34 deletions(-)
diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index 0d7f9ae298e2a..596c7af3fa2b4 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -141,6 +141,9 @@ struct SCEVUseT : private PointerIntPair<SCEVPtrT, 2> {
/// operands.
bool isCanonical() const { return getCanonical() == getOpaqueValue(); }
+ /// Returns true if this use itself carries use-specific no-wrap flags.
+ bool hasUseFlags() const { return getOpaqueValue() != getPointer(); }
+
/// Return the canonical SCEV for this SCEVUse.
const SCEV *getCanonical() const;
@@ -720,6 +723,13 @@ class ScalarEvolution {
LLVM_ABI void registerUser(const SCEV *User, ArrayRef<const SCEV *> Ops);
LLVM_ABI void registerUser(const SCEV *User, ArrayRef<SCEVUse> Ops);
+ /// Attach use-specific no-wrap \p Flags to \p S and record the use.
+ SCEVUse getUseWithFlags(const SCEV *S, SCEVNoWrapFlags Flags) {
+ SCEVUse U(S, Flags);
+ registerFlaggedUse(U);
+ return U;
+ }
+
/// Return true if the SCEV expression contains an undef value.
LLVM_ABI bool containsUndefs(const SCEV *S) const;
@@ -958,10 +968,10 @@ class ScalarEvolution {
///
/// In the case that a relevant loop exit value cannot be computed, the
/// original value V is returned.
- LLVM_ABI const SCEV *getSCEVAtScope(const SCEV *S, const Loop *L);
+ LLVM_ABI SCEVUse getSCEVAtScope(SCEVUse S, const Loop *L);
/// This is a convenience function which does getSCEVAtScope(getSCEV(V), L).
- LLVM_ABI const SCEV *getSCEVAtScope(Value *V, const Loop *L);
+ LLVM_ABI SCEVUse getSCEVAtScope(Value *V, const Loop *L);
/// Test whether entry to the loop is protected by a conditional between LHS
/// and RHS. This is used to help avoid max expressions in loop trip
@@ -1905,14 +1915,17 @@ class ScalarEvolution {
/// This map contains entries for all the expressions that we attempt to
/// compute getSCEVAtScope information for, which can be expensive in
/// extreme cases.
- DenseMap<const SCEV *, SmallVector<std::pair<const Loop *, const SCEV *>, 2>>
+ DenseMap<SCEVUse, SmallVector<std::pair<const Loop *, SCEVUse>, 2>>
ValuesAtScopes;
/// Reverse map for invalidation purposes: Stores of which SCEV and which
/// loop this is the value-at-scope of.
- DenseMap<const SCEV *, SmallVector<std::pair<const Loop *, const SCEV *>, 2>>
+ DenseMap<SCEVUse, SmallVector<std::pair<const Loop *, SCEVUse>, 2>>
ValuesAtScopesUsers;
+ /// Map canonical SCEV to SCEVUse variants created for it.
+ DenseMap<const SCEV *, SmallSetVector<SCEVUse, 2>> FlaggedUses;
+
/// Memoized computeLoopDisposition results.
DenseMap<const SCEV *,
SmallVector<PointerIntPair<const Loop *, 2, LoopDisposition>, 2>>
@@ -2064,7 +2077,7 @@ class ScalarEvolution {
/// Implementation code for getSCEVAtScope; called at most once for each
/// SCEV+Loop pair.
- const SCEV *computeSCEVAtScope(const SCEV *S, const Loop *L);
+ SCEVUse computeSCEVAtScope(SCEVUse S, const Loop *L);
/// Return the BackedgeTakenInfo for the given loop, lazily computing new
/// values if the loop hasn't been analyzed yet. The returned result is
@@ -2372,7 +2385,14 @@ class ScalarEvolution {
void forgetMemoizedResults(ArrayRef<SCEVUse> SCEVs);
/// Helper for forgetMemoizedResults.
- void forgetMemoizedResultsImpl(const SCEV *S);
+ void forgetMemoizedResultsImpl(SCEVUse S);
+
+ /// If \p U carries use-specific flags, record it as a flagged use of the
+ /// underlying canonical SCEV.
+ void registerFlaggedUse(SCEVUse U) {
+ if (U.hasUseFlags())
+ FlaggedUses[U.getPointer()].insert(U);
+ }
/// Iterate over instructions in \p Worklist and their users. Erase entries
/// from ValueExprMap and collect SCEV expressions in \p ToForget
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 27a1a20bcdf79..1d726085e1bfa 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -8720,6 +8720,7 @@ void ScalarEvolution::forgetAllLoops() {
ValueExprMap.clear();
ValuesAtScopes.clear();
ValuesAtScopesUsers.clear();
+ FlaggedUses.clear();
LoopDispositions.clear();
BlockDispositions.clear();
UnsignedRanges.clear();
@@ -10127,9 +10128,8 @@ const SCEV *ScalarEvolution::computeExitCountExhaustively(const Loop *L,
return getCouldNotCompute();
}
-const SCEV *ScalarEvolution::getSCEVAtScope(const SCEV *V, const Loop *L) {
- SmallVector<std::pair<const Loop *, const SCEV *>, 2> &Values =
- ValuesAtScopes[V];
+SCEVUse ScalarEvolution::getSCEVAtScope(SCEVUse V, const Loop *L) {
+ auto &Values = ValuesAtScopes[V];
// Check to see if we've folded this expression at this loop before.
for (auto &LS : Values)
if (LS.first == L)
@@ -10138,7 +10138,7 @@ const SCEV *ScalarEvolution::getSCEVAtScope(const SCEV *V, const Loop *L) {
Values.emplace_back(L, nullptr);
// Otherwise compute it.
- const SCEV *C = computeSCEVAtScope(V, L);
+ SCEVUse C = computeSCEVAtScope(V, L);
for (auto &LS : reverse(ValuesAtScopes[V]))
if (LS.first == L) {
LS.second = C;
@@ -10248,7 +10248,7 @@ const SCEV *ScalarEvolution::getWithOperands(const SCEV *S,
llvm_unreachable("Unknown SCEV kind!");
}
-const SCEV *ScalarEvolution::computeSCEVAtScope(const SCEV *V, const Loop *L) {
+SCEVUse ScalarEvolution::computeSCEVAtScope(SCEVUse V, const Loop *L) {
switch (V->getSCEVType()) {
case scConstant:
case scVScale:
@@ -10261,7 +10261,7 @@ const SCEV *ScalarEvolution::computeSCEVAtScope(const SCEV *V, const Loop *L) {
// Avoid performing the look-up in the common case where the specified
// expression has no loop-variant portions.
for (unsigned i = 0, e = AddRec->getNumOperands(); i != e; ++i) {
- const SCEV *OpAtScope = getSCEVAtScope(AddRec->getOperand(i), L);
+ SCEVUse OpAtScope = getSCEVAtScope(AddRec->getOperand(i), L);
if (OpAtScope == AddRec->getOperand(i))
continue;
@@ -10316,8 +10316,8 @@ const SCEV *ScalarEvolution::computeSCEVAtScope(const SCEV *V, const Loop *L) {
// Avoid performing the look-up in the common case where the specified
// expression has no loop-variant portions.
for (unsigned i = 0, e = Ops.size(); i != e; ++i) {
- const SCEV *OpAtScope = getSCEVAtScope(Ops[i].getPointer(), L);
- if (OpAtScope != Ops[i].getPointer()) {
+ SCEVUse OpAtScope = getSCEVAtScope(Ops[i], L);
+ if (OpAtScope != Ops[i]) {
// Okay, at least one of these operands is loop variant but might be
// foldable. Build a new instance of the folded commutative expression.
SmallVector<SCEVUse, 8> NewOps;
@@ -10326,7 +10326,7 @@ const SCEV *ScalarEvolution::computeSCEVAtScope(const SCEV *V, const Loop *L) {
NewOps.push_back(OpAtScope);
for (++i; i != e; ++i) {
- OpAtScope = getSCEVAtScope(Ops[i].getPointer(), L);
+ OpAtScope = getSCEVAtScope(Ops[i], L);
NewOps.push_back(OpAtScope);
}
@@ -10447,7 +10447,7 @@ const SCEV *ScalarEvolution::computeSCEVAtScope(const SCEV *V, const Loop *L) {
llvm_unreachable("Unknown SCEV type!");
}
-const SCEV *ScalarEvolution::getSCEVAtScope(Value *V, const Loop *L) {
+SCEVUse ScalarEvolution::getSCEVAtScope(Value *V, const Loop *L) {
return getSCEVAtScope(getSCEV(V), L);
}
@@ -14121,6 +14121,7 @@ ScalarEvolution::ScalarEvolution(ScalarEvolution &&Arg)
std::move(Arg.ConstantEvolutionLoopExitValue)),
ValuesAtScopes(std::move(Arg.ValuesAtScopes)),
ValuesAtScopesUsers(std::move(Arg.ValuesAtScopesUsers)),
+ FlaggedUses(std::move(Arg.FlaggedUses)),
LoopDispositions(std::move(Arg.LoopDispositions)),
LoopPropertiesCache(std::move(Arg.LoopPropertiesCache)),
BlockDispositions(std::move(Arg.BlockDispositions)),
@@ -14691,7 +14692,7 @@ void ScalarEvolution::forgetMemoizedResults(ArrayRef<SCEVUse> SCEVs) {
[&](const auto &Entry) { return ToForget.count(Entry.first.first); });
}
-void ScalarEvolution::forgetMemoizedResultsImpl(const SCEV *S) {
+void ScalarEvolution::forgetMemoizedResultsImpl(SCEVUse S) {
LoopDispositions.erase(S);
BlockDispositions.erase(S);
UnsignedRanges.erase(S);
@@ -14714,20 +14715,33 @@ void ScalarEvolution::forgetMemoizedResultsImpl(const SCEV *S) {
ExprValueMap.erase(ExprIt);
}
- auto ScopeIt = ValuesAtScopes.find(S);
- if (ScopeIt != ValuesAtScopes.end()) {
- for (const auto &Pair : ScopeIt->second)
- if (!isa_and_nonnull<SCEVConstant>(Pair.second))
- llvm::erase(ValuesAtScopesUsers[Pair.second],
- std::make_pair(Pair.first, S));
- ValuesAtScopes.erase(ScopeIt);
- }
+ auto EraseAtScopeKey = [&](SCEVUse Key) {
+ auto ScopeIt = ValuesAtScopes.find(Key);
+ if (ScopeIt != ValuesAtScopes.end()) {
+ for (const auto &Pair : ScopeIt->second)
+ if (!isa_and_nonnull<SCEVConstant>(Pair.second))
+ llvm::erase(ValuesAtScopesUsers[Pair.second],
+ std::make_pair(Pair.first, Key));
+ ValuesAtScopes.erase(ScopeIt);
+ }
- auto ScopeUserIt = ValuesAtScopesUsers.find(S);
- if (ScopeUserIt != ValuesAtScopesUsers.end()) {
- for (const auto &Pair : ScopeUserIt->second)
- llvm::erase(ValuesAtScopes[Pair.second], std::make_pair(Pair.first, S));
- ValuesAtScopesUsers.erase(ScopeUserIt);
+ auto ScopeUserIt = ValuesAtScopesUsers.find(Key);
+ if (ScopeUserIt != ValuesAtScopesUsers.end()) {
+ for (const auto &Pair : ScopeUserIt->second)
+ llvm::erase(ValuesAtScopes[Pair.second],
+ std::make_pair(Pair.first, Key));
+ ValuesAtScopesUsers.erase(ScopeUserIt);
+ }
+ };
+ EraseAtScopeKey(S);
+ // Also erase any use-flagged variants of S.
+ if (!FlaggedUses.empty()) {
+ auto FlaggedIt = FlaggedUses.find(S);
+ if (FlaggedIt != FlaggedUses.end()) {
+ for (SCEVUse Variant : FlaggedIt->second)
+ EraseAtScopeKey(Variant);
+ FlaggedUses.erase(FlaggedIt);
+ }
}
auto BEUsersIt = BECountUsers.find(S);
@@ -14968,10 +14982,10 @@ void ScalarEvolution::verify() const {
// Verify integrity of ValuesAtScopes users.
for (const auto &ValueAndVec : ValuesAtScopes) {
- const SCEV *Value = ValueAndVec.first;
+ SCEVUse Value = ValueAndVec.first;
for (const auto &LoopAndValueAtScope : ValueAndVec.second) {
const Loop *L = LoopAndValueAtScope.first;
- const SCEV *ValueAtScope = LoopAndValueAtScope.second;
+ SCEVUse ValueAtScope = LoopAndValueAtScope.second;
if (!isa<SCEVConstant>(ValueAtScope)) {
auto It = ValuesAtScopesUsers.find(ValueAtScope);
if (It != ValuesAtScopesUsers.end() &&
@@ -14985,10 +14999,10 @@ void ScalarEvolution::verify() const {
}
for (const auto &ValueAtScopeAndVec : ValuesAtScopesUsers) {
- const SCEV *ValueAtScope = ValueAtScopeAndVec.first;
+ SCEVUse ValueAtScope = ValueAtScopeAndVec.first;
for (const auto &LoopAndValue : ValueAtScopeAndVec.second) {
const Loop *L = LoopAndValue.first;
- const SCEV *Value = LoopAndValue.second;
+ SCEVUse Value = LoopAndValue.second;
assert(!isa<SCEVConstant>(Value));
auto It = ValuesAtScopes.find(Value);
if (It != ValuesAtScopes.end() &&
@@ -15000,6 +15014,20 @@ void ScalarEvolution::verify() const {
}
}
+ auto VerifyFlaggedUseTracked = [&](SCEVUse Key) {
+ if (!Key.hasUseFlags())
+ return;
+ auto It = FlaggedUses.find(Key.getPointer());
+ if (It != FlaggedUses.end() && It->second.count(Key))
+ return;
+ dbgs() << "Flagged at-scope key: " << *Key << " missing in FlaggedUses\n";
+ std::abort();
+ };
+ for (const auto &ValueAndVec : ValuesAtScopes)
+ VerifyFlaggedUseTracked(ValueAndVec.first);
+ for (const auto &ValueAtScopeAndVec : ValuesAtScopesUsers)
+ VerifyFlaggedUseTracked(ValueAtScopeAndVec.first);
+
// Verify integrity of BECountUsers.
auto VerifyBECountUsers = [&](bool Predicated) {
auto &BECounts =
More information about the llvm-commits
mailing list