[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