[llvm] [ThinLTO] Add HasLocal flag to GlobalValueSummaryInfo (PR #164647)
    Teresa Johnson via llvm-commits 
    llvm-commits at lists.llvm.org
       
    Wed Oct 22 10:40:29 PDT 2025
    
    
  
https://github.com/teresajohnson updated https://github.com/llvm/llvm-project/pull/164647
>From 87a00e4f3dc91f1142251a920b7368f68ead7426 Mon Sep 17 00:00:00 2001
From: Teresa Johnson <tejohnson at google.com>
Date: Tue, 21 Oct 2025 16:11:42 -0700
Subject: [PATCH 1/2] [ThinLTO] Add HasLocal flag to GlobalValueSummaryInfo
Add a flag to the GlobalValueSummaryInfo indicating whether the
associated SummaryList (all summaries with the same GUID) contains any
summaries with local linkage. This flag is set when building the index,
so it is associated with the original linkage type before
internalization and promotion. Consumers should check the
withInternalizeAndPromote() flag on the index before using it.
In most cases we expect a 1-1 mapping between a GUID and a summary with
local linkage, because for locals the GUID is computed from the hash of
"modulepath:name". However, there can be multiple locals with the same
GUID if translation units are not compiled with enough path. And in rare
but theoretically possible cases, there can be hash collisions on the
underlying MD5 computation. So to be safe when looking for local
summaries, analyses currently look through all summaries in the list.
These lists can be extremely long in the case of large binaries with
template function defs in widely used headers (i.e. linkonce_odr).
A follow on change will use this flag to reduce ThinLTO analysis time in
WPD by 5-6% for a large target (details in PR164046 which will be
reworked to use this flag).
Note that in the past we have tried to keep bits related to the GUID in
the ValueInfo (which has a pointer to the associated
GlobalValueSummaryInfo), via its PointerIntPair. However, we are out of
bits there. This change does add a byte to every GlobalValueSummaryInfo
instance, which I measured as a little under 0.90% overhead in a large
target. However, it enables adding 7 bits of other per-GUID flags in the
future without adding more overhead. Note that it was lower overhead to
add this to the GlobalValueSummaryInfo than the ValueInfo, which tends
to be copied into other maps.
---
 llvm/include/llvm/IR/ModuleSummaryIndex.h | 44 ++++++++++++++++++++---
 llvm/lib/LTO/LTO.cpp                      |  4 +++
 2 files changed, 44 insertions(+), 4 deletions(-)
diff --git a/llvm/include/llvm/IR/ModuleSummaryIndex.h b/llvm/include/llvm/IR/ModuleSummaryIndex.h
index 98df06ae43ad0..fb1f5c3bc0440 100644
--- a/llvm/include/llvm/IR/ModuleSummaryIndex.h
+++ b/llvm/include/llvm/IR/ModuleSummaryIndex.h
@@ -172,9 +172,11 @@ struct alignas(8) GlobalValueSummaryInfo {
 
   /// Add a summary corresponding to a global value definition in a module with
   /// the corresponding GUID.
-  void addSummary(std::unique_ptr<GlobalValueSummary> Summary) {
-    return SummaryList.push_back(std::move(Summary));
-  }
+  inline void addSummary(std::unique_ptr<GlobalValueSummary> Summary);
+
+  /// Verify that the HasLocal flag is consistent with the SummaryList. Should
+  /// only be called prior to index-based internalization and promotion.
+  inline void verifyLocal() const;
 
 private:
   /// List of global value summary structures for a particular value held
@@ -183,6 +185,22 @@ struct alignas(8) GlobalValueSummaryInfo {
   /// compiling without sufficient distinguishing path, or (theoretically) hash
   /// collisions. Each summary is from a different module.
   GlobalValueSummaryList SummaryList;
+
+  /// True if the SummaryList contains at least one summary with local linkage.
+  /// In most cases there should be only one, unless translation units with
+  /// same-named locals were compiled without distinguishing path. And generally
+  /// there should not be a mix of local and non-local summaries, because the
+  /// GUID for a local is computed with the path prepended and a ':' delimiter.
+  /// In extremely rare cases there could be a GUID hash collision. Having the
+  /// flag saves having to walk through all summaries to prove the existence or
+  /// not of any locals.
+  /// NOTE: this flag is set when the index is built. It does not reflect
+  /// index-based internalization and promotion decisions. Generally most
+  /// index-based analysis occurs before then, but any users should assert that
+  /// the withInternalizeAndPromote() flag is not set on the index.
+  /// TODO: Replace checks in various ThinLTO analyses that loop through all
+  /// summaries to handle the local case with a check of the flag.
+  bool HasLocal : 1;
 };
 
 /// Map from global value GUID to corresponding summary structures. Use a
@@ -219,6 +237,8 @@ struct ValueInfo {
     return getRef()->second.getSummaryList();
   }
 
+  void verifyLocal() const { getRef()->second.verifyLocal(); }
+
   // Even if the index is built with GVs available, we may not have one for
   // summary entries synthesized for profiled indirect call targets.
   bool hasName() const { return !haveGVs() || getValue(); }
@@ -649,7 +669,23 @@ class GlobalValueSummary {
   friend class ModuleSummaryIndex;
 };
 
-GlobalValueSummaryInfo::GlobalValueSummaryInfo(bool HaveGVs) : U(HaveGVs) {}
+GlobalValueSummaryInfo::GlobalValueSummaryInfo(bool HaveGVs)
+    : U(HaveGVs), HasLocal(false) {}
+
+void GlobalValueSummaryInfo::addSummary(
+    std::unique_ptr<GlobalValueSummary> Summary) {
+  if (GlobalValue::isLocalLinkage(Summary->linkage()))
+    HasLocal = true;
+  return SummaryList.push_back(std::move(Summary));
+}
+
+void GlobalValueSummaryInfo::verifyLocal() const {
+  assert(HasLocal ==
+         llvm::any_of(SummaryList,
+                      [](const std::unique_ptr<GlobalValueSummary> &Summary) {
+                        return GlobalValue::isLocalLinkage(Summary->linkage());
+                      }));
+}
 
 /// Alias summary information.
 class AliasSummary : public GlobalValueSummary {
diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index 72ae064e46a0c..86780e19f22b8 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -477,6 +477,10 @@ static void thinLTOInternalizeAndPromoteGUID(
                        return !GlobalValue::isLocalLinkage(Summary->linkage());
                      });
 
+  // Before performing index-based internalization and promotion for this GUID,
+  // the local flag should be consistent with the summary list linkage types.
+  VI.verifyLocal();
+
   for (auto &S : VI.getSummaryList()) {
     // First see if we need to promote an internal value because it is not
     // exported.
>From 44714dac5c02a0593f87245882acb9fa9668a306 Mon Sep 17 00:00:00 2001
From: Teresa Johnson <tejohnson at google.com>
Date: Wed, 22 Oct 2025 10:40:04 -0700
Subject: [PATCH 2/2] Update delimiter
---
 llvm/include/llvm/IR/ModuleSummaryIndex.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/include/llvm/IR/ModuleSummaryIndex.h b/llvm/include/llvm/IR/ModuleSummaryIndex.h
index fb1f5c3bc0440..cdfee727387a5 100644
--- a/llvm/include/llvm/IR/ModuleSummaryIndex.h
+++ b/llvm/include/llvm/IR/ModuleSummaryIndex.h
@@ -190,7 +190,7 @@ struct alignas(8) GlobalValueSummaryInfo {
   /// In most cases there should be only one, unless translation units with
   /// same-named locals were compiled without distinguishing path. And generally
   /// there should not be a mix of local and non-local summaries, because the
-  /// GUID for a local is computed with the path prepended and a ':' delimiter.
+  /// GUID for a local is computed with the path prepended and a ';' delimiter.
   /// In extremely rare cases there could be a GUID hash collision. Having the
   /// flag saves having to walk through all summaries to prove the existence or
   /// not of any locals.
    
    
More information about the llvm-commits
mailing list