[llvm] change GlobalValueSummaryMapTy from std::map to llvm::MapVector (PR #157839)
Zhaoxuan Jiang via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 1 00:15:58 PDT 2026
https://github.com/nocchijiang updated https://github.com/llvm/llvm-project/pull/157839
>From df20970861d3c4f732acfa44a2fcb9dc9872fcf0 Mon Sep 17 00:00:00 2001
From: Zhaoxuan Jiang <jiangzhaoxuan94 at gmail.com>
Date: Thu, 28 May 2026 15:23:09 +0800
Subject: [PATCH] [ThinLTO] Change GlobalValueSummaryMapTy from std::map to
DenseMap+deque
Replace std::map<GUID, GlobalValueSummaryInfo> with a custom container
using DenseMap for O(1) lookup and std::deque for storage. Sort by GUID
at serialization points to preserve deterministic output order.
RFC: https://discourse.llvm.org/t/rfc-change-globalvaluesummarymapty-from-std-map-to-llvm-densemap-for-thin-linking-performance/88191
---
llvm/include/llvm/IR/ModuleSummaryIndex.h | 58 ++++++++++++++++---
llvm/include/llvm/IR/ModuleSummaryIndexYAML.h | 11 +++-
llvm/lib/Bitcode/Writer/BitcodeWriter.cpp | 28 +++++++--
llvm/lib/IR/AsmWriter.cpp | 31 +++++++---
.../IPO/MemProfContextDisambiguation.cpp | 22 +++++--
5 files changed, 123 insertions(+), 27 deletions(-)
diff --git a/llvm/include/llvm/IR/ModuleSummaryIndex.h b/llvm/include/llvm/IR/ModuleSummaryIndex.h
index ed39eb99a9196..e029ca6985cfa 100644
--- a/llvm/include/llvm/IR/ModuleSummaryIndex.h
+++ b/llvm/include/llvm/IR/ModuleSummaryIndex.h
@@ -39,6 +39,7 @@
#include <cassert>
#include <cstddef>
#include <cstdint>
+#include <deque>
#include <map>
#include <memory>
#include <optional>
@@ -180,13 +181,54 @@ struct alignas(8) GlobalValueSummaryInfo {
};
/// Map from global value GUID to corresponding summary structures. Use a
-/// std::map rather than a DenseMap so that pointers to the map's value_type
-/// (which are used by ValueInfo) are not invalidated by insertion. Also it will
-/// likely incur less overhead, as the value type is not very small and the size
-/// of the map is unknown, resulting in inefficiencies due to repeated
-/// insertions and resizing.
-using GlobalValueSummaryMapTy =
- std::map<GlobalValue::GUID, GlobalValueSummaryInfo>;
+/// DenseMap for O(1) lookup and a std::deque for storage. std::deque
+/// guarantees that pointers to elements are not invalidated by push_back,
+/// which is required because ValueInfo stores a raw pointer to elements of
+/// this container.
+class GlobalValueSummaryMap {
+public:
+ using key_type = GlobalValue::GUID;
+ using mapped_type = GlobalValueSummaryInfo;
+ using value_type = std::pair<key_type, mapped_type>;
+ using iterator = std::deque<value_type>::iterator;
+ using const_iterator = std::deque<value_type>::const_iterator;
+ using size_type = std::deque<value_type>::size_type;
+
+private:
+ DenseMap<key_type, unsigned> Map;
+ std::deque<value_type> Storage;
+
+public:
+ template <typename... Ts>
+ std::pair<iterator, bool> try_emplace(key_type Key, Ts &&...Args) {
+ auto Res = Map.try_emplace(Key, Storage.size());
+ if (Res.second) {
+ Storage.emplace_back(std::piecewise_construct, std::forward_as_tuple(Key),
+ std::forward_as_tuple(std::forward<Ts>(Args)...));
+ return {std::prev(Storage.end()), true};
+ }
+ return {Storage.begin() + Res.first->second, false};
+ }
+
+ iterator find(key_type Key) {
+ auto It = Map.find(Key);
+ return It == Map.end() ? Storage.end() : Storage.begin() + It->second;
+ }
+
+ const_iterator find(key_type Key) const {
+ auto It = Map.find(Key);
+ return It == Map.end() ? Storage.end() : Storage.begin() + It->second;
+ }
+
+ iterator begin() { return Storage.begin(); }
+ const_iterator begin() const { return Storage.begin(); }
+ iterator end() { return Storage.end(); }
+ const_iterator end() const { return Storage.end(); }
+ size_type size() const { return Storage.size(); }
+ bool empty() const { return Storage.empty(); }
+};
+
+using GlobalValueSummaryMapTy = GlobalValueSummaryMap;
/// Struct that holds a reference to a particular GUID in a global value
/// summary.
@@ -1564,7 +1606,7 @@ class ModuleSummaryIndex {
GlobalValueSummaryMapTy::value_type *
getOrInsertValuePtr(GlobalValue::GUID GUID) {
- return &*GlobalValueMap.emplace(GUID, GlobalValueSummaryInfo(HaveGVs))
+ return &*GlobalValueMap.try_emplace(GUID, GlobalValueSummaryInfo(HaveGVs))
.first;
}
diff --git a/llvm/include/llvm/IR/ModuleSummaryIndexYAML.h b/llvm/include/llvm/IR/ModuleSummaryIndexYAML.h
index d374e2eeb24a6..82e2223c863d1 100644
--- a/llvm/include/llvm/IR/ModuleSummaryIndexYAML.h
+++ b/llvm/include/llvm/IR/ModuleSummaryIndexYAML.h
@@ -261,7 +261,16 @@ template <> struct CustomMappingTraits<GlobalValueSummaryMapTy> {
}
}
static void output(IO &io, GlobalValueSummaryMapTy &V) {
- for (auto &P : V) {
+ // Sort by GUID for deterministic output.
+ SmallVector<const GlobalValueSummaryMapTy::value_type *, 0> Sorted;
+ Sorted.reserve(V.size());
+ for (const auto &E : V)
+ Sorted.push_back(&E);
+ llvm::sort(Sorted, [](const auto *A, const auto *B) {
+ return A->first < B->first;
+ });
+ for (const auto *PP : Sorted) {
+ const auto &P = *PP;
std::vector<GlobalValueSummaryYaml> GVSums;
for (auto &Sum : P.second.getSummaryList()) {
if (auto *FSum = dyn_cast<FunctionSummary>(Sum.get())) {
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index 21cb0d2a28e36..88309777b69de 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -230,10 +230,18 @@ class ModuleBitcodeWriterBase : public BitcodeWriterBase {
GlobalValueId = VE.getValues().size();
if (!Index)
return;
- for (const auto &GUIDSummaryLists : *Index)
+ // Sort by GUID for deterministic value ID assignment.
+ SmallVector<const GlobalValueSummaryMapTy::value_type *, 0> Sorted;
+ Sorted.reserve(Index->size());
+ for (const auto &E : *Index)
+ Sorted.push_back(&E);
+ llvm::sort(Sorted, [](const auto *A, const auto *B) {
+ return A->first < B->first;
+ });
+ for (const auto *GUIDSummaryLists : Sorted)
// Examine all summaries for this GUID.
- for (auto &Summary : GUIDSummaryLists.second.getSummaryList())
- if (auto FS = dyn_cast<FunctionSummary>(Summary.get())) {
+ for (auto &Summary : GUIDSummaryLists->second.getSummaryList())
+ if (auto *FS = dyn_cast<FunctionSummary>(Summary.get())) {
// For each call in the function summary, see if the call
// is to a GUID (which means it is for an indirect call,
// otherwise we would have a Value for it). If so, synthesize
@@ -584,9 +592,17 @@ class IndexBitcodeWriter : public BitcodeWriterBase {
Callback({AS->getAliaseeGUID(), &AS->getAliasee()}, true);
}
} else {
- for (auto &Summaries : Index)
- for (auto &Summary : Summaries.second.getSummaryList())
- Callback({Summaries.first, Summary.get()}, false);
+ // Sort by GUID for deterministic output.
+ SmallVector<const GlobalValueSummaryMapTy::value_type *, 0> Sorted;
+ Sorted.reserve(Index.size());
+ for (const auto &E : Index)
+ Sorted.push_back(&E);
+ llvm::sort(Sorted, [](const auto *A, const auto *B) {
+ return A->first < B->first;
+ });
+ for (const auto *Entry : Sorted)
+ for (auto &Summary : Entry->second.getSummaryList())
+ Callback({Entry->first, Summary.get()}, false);
}
}
diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp
index 057255caaea3b..1179c76d76026 100644
--- a/llvm/lib/IR/AsmWriter.cpp
+++ b/llvm/lib/IR/AsmWriter.cpp
@@ -1202,8 +1202,15 @@ int SlotTracker::processIndex() {
// Start numbering the GUIDs after the module ids.
GUIDNext = ModulePathNext;
- for (auto &GlobalList : *TheIndex)
- CreateGUIDSlot(GlobalList.first);
+ // Sort by GUID for deterministic slot assignment.
+ SmallVector<const GlobalValueSummaryMapTy::value_type *, 0> SortedGVS;
+ SortedGVS.reserve(TheIndex->size());
+ for (const auto &E : *TheIndex)
+ SortedGVS.push_back(&E);
+ llvm::sort(SortedGVS,
+ [](const auto *A, const auto *B) { return A->first < B->first; });
+ for (const auto *Entry : SortedGVS)
+ CreateGUIDSlot(Entry->first);
// Start numbering the TypeIdCompatibleVtables after the GUIDs.
TypeIdCompatibleVtableNext = GUIDNext;
@@ -3229,16 +3236,24 @@ void AssemblyWriter::printModuleSummaryIndex() {
// FIXME: Change AliasSummary to hold a ValueInfo instead of summary pointer
// for aliasee (then update BitcodeWriter.cpp and remove get/setAliaseeGUID).
- for (auto &GlobalList : *TheIndex) {
- auto GUID = GlobalList.first;
- for (auto &Summary : GlobalList.second.getSummaryList())
+ // Sort by GUID for deterministic output matching slot assignment order.
+ SmallVector<const GlobalValueSummaryMapTy::value_type *, 0> SortedGVS;
+ SortedGVS.reserve(TheIndex->size());
+ for (const auto &E : *TheIndex)
+ SortedGVS.push_back(&E);
+ llvm::sort(SortedGVS,
+ [](const auto *A, const auto *B) { return A->first < B->first; });
+
+ for (const auto *Entry : SortedGVS) {
+ auto GUID = Entry->first;
+ for (auto &Summary : Entry->second.getSummaryList())
SummaryToGUIDMap[Summary.get()] = GUID;
}
// Print the global value summary entries.
- for (auto &GlobalList : *TheIndex) {
- auto GUID = GlobalList.first;
- auto VI = TheIndex->getValueInfo(GlobalList);
+ for (const auto *Entry : SortedGVS) {
+ auto GUID = Entry->first;
+ auto VI = TheIndex->getValueInfo(*Entry);
printSummaryInfo(Machine.getGUIDSlot(GUID), VI);
}
diff --git a/llvm/lib/Transforms/IPO/MemProfContextDisambiguation.cpp b/llvm/lib/Transforms/IPO/MemProfContextDisambiguation.cpp
index 3cc35877d2add..ad02a4512fe3b 100644
--- a/llvm/lib/Transforms/IPO/MemProfContextDisambiguation.cpp
+++ b/llvm/lib/Transforms/IPO/MemProfContextDisambiguation.cpp
@@ -2479,8 +2479,15 @@ ModuleCallsiteContextGraph::ModuleCallsiteContextGraph(
DenseSet<GlobalValue::GUID>
IndexCallsiteContextGraph::findAliaseeGUIDsPrevailingInDifferentModule() {
DenseSet<GlobalValue::GUID> AliaseeGUIDs;
- for (auto &I : Index) {
- auto VI = Index.getValueInfo(I);
+ // Sort by GUID for deterministic graph construction order.
+ SmallVector<const GlobalValueSummaryMapTy::value_type *, 0> SortedEntries;
+ SortedEntries.reserve(Index.size());
+ for (const auto &E : Index)
+ SortedEntries.push_back(&E);
+ llvm::sort(SortedEntries,
+ [](const auto *A, const auto *B) { return A->first < B->first; });
+ for (const auto *I : SortedEntries) {
+ auto VI = Index.getValueInfo(*I);
for (auto &S : VI.getSummaryList()) {
// We only care about aliases to functions.
auto *AS = dyn_cast<AliasSummary>(S.get());
@@ -2525,8 +2532,15 @@ IndexCallsiteContextGraph::IndexCallsiteContextGraph(
// by MemProfTopNImportant. Must be a std::map (not DenseMap) because keys
// must be sorted.
std::map<uint64_t, uint32_t> TotalSizeToContextIdTopNCold;
- for (auto &I : Index) {
- auto VI = Index.getValueInfo(I);
+ // Sort by GUID for deterministic graph construction order.
+ SmallVector<const GlobalValueSummaryMapTy::value_type *, 0> SortedEntries;
+ SortedEntries.reserve(Index.size());
+ for (const auto &E : Index)
+ SortedEntries.push_back(&E);
+ llvm::sort(SortedEntries,
+ [](const auto *A, const auto *B) { return A->first < B->first; });
+ for (const auto *I : SortedEntries) {
+ auto VI = Index.getValueInfo(*I);
if (GUIDsToSkip.contains(VI.getGUID()))
continue;
for (auto &S : VI.getSummaryList()) {
More information about the llvm-commits
mailing list