[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