[llvm] 64b7dfe - [PassManager] Store analysis results in contiguous storage (#222003)

via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 11 13:00:39 PDT 2026


Author: Clemens Wasser
Date: 2026-09-11T22:00:34+02:00
New Revision: 64b7dfef40e59b8596a272a431aa3a4abcef33c1

URL: https://github.com/llvm/llvm-project/commit/64b7dfef40e59b8596a272a431aa3a4abcef33c1
DIFF: https://github.com/llvm/llvm-project/commit/64b7dfef40e59b8596a272a431aa3a4abcef33c1.diff

LOG: [PassManager] Store analysis results in contiguous storage (#222003)

Store per-IR analysis results in a `SmallVector` instead of `std::list`
to avoid a heap allocation per cached analysis. The lookup map stores
the raw result pointer, which stays stable behind the unique_ptr.

geomean -0.12% for optimizing builds, clang build -0.34%

https://llvm-compile-time-tracker.com/compare.php?from=e6734a41bb41d91cb880fe7abce46d8146182518&to=4fc43802817cf8daddbebadb0b7fbfaa6286e7fe&stat=instructions:u

Added: 
    

Modified: 
    llvm/include/llvm/IR/PassManager.h
    llvm/include/llvm/IR/PassManagerImpl.h
    llvm/lib/Transforms/Scalar/InferAlignment.cpp
    llvm/tools/llubi/lib/Interpreter.cpp
    polly/include/polly/ScopInfo.h

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/IR/PassManager.h b/llvm/include/llvm/IR/PassManager.h
index da2eec0756c5b..c8e2276ac22ee 100644
--- a/llvm/include/llvm/IR/PassManager.h
+++ b/llvm/include/llvm/IR/PassManager.h
@@ -39,6 +39,7 @@
 
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/TinyPtrVector.h"
@@ -48,7 +49,6 @@
 #include "llvm/Support/TypeName.h"
 #include <cassert>
 #include <cstring>
-#include <list>
 #include <memory>
 #include <tuple>
 #include <type_traits>
@@ -282,22 +282,22 @@ template <typename IRUnitT, typename... ExtraArgTs> class AnalysisManager {
 
   /// List of analysis pass IDs and associated concept pointers.
   ///
-  /// Requires iterators to be valid across appending new entries and arbitrary
-  /// erases. Provides the analysis ID to enable finding iterators to a given
-  /// entry in maps below, and provides the storage for the actual result
-  /// concept.
+  /// Requires result pointers to stay valid across appending new entries and
+  /// arbitrary erases (results are heap allocated behind unique_ptrs).
+  /// Provides the analysis ID to enable finding results for a given entry in
+  /// the map below, and provides the storage for the actual result concept.
   using AnalysisResultListT =
-      std::list<std::pair<AnalysisKey *, typename ResultConceptT::unique_ptr>>;
+      SmallVector<std::pair<AnalysisKey *, typename ResultConceptT::unique_ptr>,
+                  8>;
 
   /// Map type from IRUnitT pointer to our custom list type.
   using AnalysisResultListMapT = DenseMap<IRUnitT *, AnalysisResultListT>;
 
-  /// Map type from a pair of analysis ID and IRUnitT pointer to an
-  /// iterator into a particular result list (which is where the actual analysis
-  /// result is stored).
+  /// Map type from a pair of analysis ID and IRUnitT pointer to the result in
+  /// a particular result list (which is where the actual analysis result is
+  /// stored).
   using AnalysisResultMapT =
-      DenseMap<std::pair<AnalysisKey *, IRUnitT *>,
-               typename AnalysisResultListT::iterator>;
+      DenseMap<std::pair<AnalysisKey *, IRUnitT *>, ResultConceptT *>;
 
 public:
   /// API to communicate dependencies between analyses during invalidation.
@@ -364,7 +364,7 @@ template <typename IRUnitT, typename... ExtraArgTs> class AnalysisManager {
              "manager's cache is always an error, likely due to a stale result "
              "handle!");
 
-      auto &Result = static_cast<ResultT &>(*RI->second->second);
+      auto &Result = static_cast<ResultT &>(*RI->second);
 
       // Insert into the map whether the result should be invalidated and return
       // that. Note that we cannot reuse IMapI and must do a fresh insert here,
@@ -515,7 +515,10 @@ template <typename IRUnitT, typename... ExtraArgTs> class AnalysisManager {
   /// sure you want to *only* clear this analysis without asking if it is
   /// invalid.
   template <typename AnalysisT> void clearAnalysis(IRUnitT &IR) {
-    AnalysisResultListT &ResultsList = AnalysisResultLists[&IR];
+    auto ResultsListI = AnalysisResultLists.find(&IR);
+    assert(ResultsListI != AnalysisResultLists.end() &&
+           "Analysis must be available");
+    AnalysisResultListT &ResultsList = ResultsListI->second;
     AnalysisKey *ID = AnalysisT::ID();
 
     auto I =
@@ -550,7 +553,7 @@ template <typename IRUnitT, typename... ExtraArgTs> class AnalysisManager {
   ResultConceptT *getCachedResultImpl(AnalysisKey *ID, IRUnitT &IR) const {
     typename AnalysisResultMapT::const_iterator RI =
         AnalysisResults.find({ID, &IR});
-    return RI == AnalysisResults.end() ? nullptr : &*RI->second->second;
+    return RI == AnalysisResults.end() ? nullptr : RI->second;
   }
 
   /// Map type from analysis pass ID to pass concept pointer.

diff  --git a/llvm/include/llvm/IR/PassManagerImpl.h b/llvm/include/llvm/IR/PassManagerImpl.h
index 03b523149ce50..af1269b27460e 100644
--- a/llvm/include/llvm/IR/PassManagerImpl.h
+++ b/llvm/include/llvm/IR/PassManagerImpl.h
@@ -119,7 +119,7 @@ AnalysisManager<IRUnitT, ExtraArgTs...>::clear(IRUnitT &IR,
   auto ResultsListI = AnalysisResultLists.find(&IR);
   if (ResultsListI == AnalysisResultLists.end())
     return;
-  // Delete the map entries that point into the results list.
+  // Delete the map entries that point to the results in the list.
   for (auto &IDAndResult : ResultsListI->second)
     AnalysisResults.erase({IDAndResult.first, &IR});
 
@@ -131,7 +131,8 @@ template <typename IRUnitT, typename... ExtraArgTs>
 inline typename AnalysisManager<IRUnitT, ExtraArgTs...>::ResultConceptT &
 AnalysisManager<IRUnitT, ExtraArgTs...>::getResultImpl(
     AnalysisKey *ID, IRUnitT &IR, ExtraArgTs... ExtraArgs) {
-  auto [RI, Inserted] = AnalysisResults.try_emplace(std::make_pair(ID, &IR));
+  auto [RI, Inserted] =
+      AnalysisResults.try_emplace(std::make_pair(ID, &IR), nullptr);
 
   // If we don't have a cached result for this function, look up the pass and
   // run it to produce a result, which we then add to the cache.
@@ -144,9 +145,12 @@ AnalysisManager<IRUnitT, ExtraArgTs...>::getResultImpl(
       PI.runBeforeAnalysis(P, IR);
     }
 
+    // Run the analysis first: running it can recursively cache another
+    // result and rehash the map.
+    auto Result = P.run(IR, *this, std::forward<ExtraArgTs>(ExtraArgs)...);
+    ResultConceptT *ResultPtr = Result.get();
     AnalysisResultListT &ResultList = AnalysisResultLists[&IR];
-    ResultList.emplace_back(
-        ID, P.run(IR, *this, std::forward<ExtraArgTs>(ExtraArgs)...));
+    ResultList.emplace_back(ID, std::move(Result));
 
     PI.runAfterAnalysis(P, IR);
 
@@ -155,10 +159,10 @@ AnalysisManager<IRUnitT, ExtraArgTs...>::getResultImpl(
     RI = AnalysisResults.find({ID, &IR});
     assert(RI != AnalysisResults.end() && "we just inserted it!");
 
-    RI->second = std::prev(ResultList.end());
+    RI->second = ResultPtr;
   }
 
-  return *RI->second->second;
+  return *RI->second;
 }
 
 template <typename IRUnitT, typename... ExtraArgTs>
@@ -172,7 +176,10 @@ inline void AnalysisManager<IRUnitT, ExtraArgTs...>::invalidate(
   // IsResultInvalidated.
   SmallDenseMap<AnalysisKey *, bool, 8> IsResultInvalidated;
   Invalidator Inv(IsResultInvalidated, AnalysisResults);
-  AnalysisResultListT &ResultsList = AnalysisResultLists[&IR];
+  auto ResultsListI = AnalysisResultLists.find(&IR);
+  if (ResultsListI == AnalysisResultLists.end())
+    return;
+  AnalysisResultListT &ResultsList = ResultsListI->second;
   for (auto &AnalysisResultPair : ResultsList) {
     // This is basically the same thing as Invalidator::invalidate, but we
     // can't call it here because we're operating on the type-erased result.
@@ -200,19 +207,22 @@ inline void AnalysisManager<IRUnitT, ExtraArgTs...>::invalidate(
 
   // Now erase the results that were marked above as invalidated.
   if (!IsResultInvalidated.empty()) {
-    for (auto I = ResultsList.begin(), E = ResultsList.end(); I != E;) {
-      AnalysisKey *ID = I->first;
+    size_t WriteIdx = 0;
+    for (size_t ReadIdx = 0, E = ResultsList.size(); ReadIdx < E; ++ReadIdx) {
+      AnalysisKey *ID = ResultsList[ReadIdx].first;
       if (!IsResultInvalidated.lookup(ID)) {
-        ++I;
+        if (WriteIdx != ReadIdx)
+          ResultsList[WriteIdx] = std::move(ResultsList[ReadIdx]);
+        ++WriteIdx;
         continue;
       }
 
       if (auto *PI = getCachedResult<PassInstrumentationAnalysis>(IR))
         PI->runAnalysisInvalidated(this->lookUpPass(ID), IR);
 
-      I = ResultsList.erase(I);
       AnalysisResults.erase({ID, &IR});
     }
+    ResultsList.resize(WriteIdx);
   }
 
   if (ResultsList.empty())

diff  --git a/llvm/lib/Transforms/Scalar/InferAlignment.cpp b/llvm/lib/Transforms/Scalar/InferAlignment.cpp
index 57a1119c2bfa9..c734eafd6e75d 100644
--- a/llvm/lib/Transforms/Scalar/InferAlignment.cpp
+++ b/llvm/lib/Transforms/Scalar/InferAlignment.cpp
@@ -24,6 +24,7 @@
 #include "llvm/Support/KnownBits.h"
 #include "llvm/Transforms/Scalar.h"
 #include "llvm/Transforms/Utils/Local.h"
+#include <list>
 
 using namespace llvm;
 using namespace llvm::PatternMatch;

diff  --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index 77b7fae4c4ab3..ede61f858ca2a 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -28,6 +28,7 @@
 #include <cassert>
 #include <cstring>
 #include <limits>
+#include <list>
 
 namespace llvm::ubi {
 

diff  --git a/polly/include/polly/ScopInfo.h b/polly/include/polly/ScopInfo.h
index 8c1bb5a3633fd..086904f6b99dd 100644
--- a/polly/include/polly/ScopInfo.h
+++ b/polly/include/polly/ScopInfo.h
@@ -31,6 +31,7 @@
 #include <cassert>
 #include <cstddef>
 #include <forward_list>
+#include <list>
 #include <optional>
 
 namespace polly {


        


More information about the llvm-commits mailing list