[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