Skip to content

Conversation

@nga888
Copy link
Collaborator

@nga888 nga888 commented Nov 4, 2025

Backport the following commits to release/21.x:

0341fb6: [ThinLTO] Avoid creating map entries on lookup (NFCI) (#164873)
564c3de: [ThinLTO][NFC] Improve performance of addThinLTO (#166067)

teresajohnson and others added 2 commits November 4, 2025 16:39
We could inadvertently create new entries in the PrevailingModuleForGUID
map during lookup, which was always using operator[]. In most cases we
will have one for external symbols, but not in cases where the
prevailing copy is in a native object. Or if this happened to be looked
up for a local.

Make the map private and create and use accessors.

(cherry picked from commit 0341fb6)
Avoid the construction of `GUID` when not required. This improves the
performance of a LLD `--thinlto-index-only` link of `clang` by ~4-5% on
both Windows and Linux.

(cherry picked from commit 564c3de)
@nga888 nga888 added this to the LLVM 21.x Release milestone Nov 4, 2025
@nga888 nga888 requested a review from teresajohnson November 4, 2025 17:52
@github-project-automation github-project-automation bot moved this to Needs Triage in LLVM Release Status Nov 4, 2025
@llvmbot llvmbot added the LTO Link time optimization (regular/full LTO or ThinLTO) label Nov 4, 2025
@llvmbot
Copy link
Member

llvmbot commented Nov 4, 2025

@llvm/pr-subscribers-lto

Author: Andrew Ng (nga888)

Changes

Backport the following commits to release/21.x:

0341fb6: [ThinLTO] Avoid creating map entries on lookup (NFCI) (#164873)
564c3de: [ThinLTO][NFC] Improve performance of addThinLTO (#166067)


Full diff: https://github.com/llvm/llvm-project/pull/166409.diff

2 Files Affected:

  • (modified) llvm/include/llvm/LTO/LTO.h (+13)
  • (modified) llvm/lib/LTO/LTO.cpp (+19-23)
diff --git a/llvm/include/llvm/LTO/LTO.h b/llvm/include/llvm/LTO/LTO.h
index d8e632b5a49d5..d5e7d2ede7e9b 100644
--- a/llvm/include/llvm/LTO/LTO.h
+++ b/llvm/include/llvm/LTO/LTO.h
@@ -464,6 +464,19 @@ class LTO {
     ModuleMapType ModuleMap;
     // The bitcode modules to compile, if specified by the LTO Config.
     std::optional<ModuleMapType> ModulesToCompile;
+
+    void setPrevailingModuleForGUID(GlobalValue::GUID GUID, StringRef Module) {
+      PrevailingModuleForGUID[GUID] = Module;
+    }
+    bool isPrevailingModuleForGUID(GlobalValue::GUID GUID,
+                                   StringRef Module) const {
+      auto It = PrevailingModuleForGUID.find(GUID);
+      return It != PrevailingModuleForGUID.end() && It->second == Module;
+    }
+
+  private:
+    // Make this private so all accesses must go through above accessor methods
+    // to avoid inadvertently creating new entries on lookups.
     DenseMap<GlobalValue::GUID, StringRef> PrevailingModuleForGUID;
   } ThinLTO;
 
diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index 73e79c08a56ca..3c6951d8ec5fe 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -1035,63 +1035,59 @@ Error LTO::linkRegularLTO(RegularLTOState::AddedModule Mod,
 Error LTO::addThinLTO(BitcodeModule BM, ArrayRef<InputFile::Symbol> Syms,
                       const SymbolResolution *&ResI,
                       const SymbolResolution *ResE) {
+  const auto BMID = BM.getModuleIdentifier();
   const SymbolResolution *ResITmp = ResI;
   for (const InputFile::Symbol &Sym : Syms) {
     assert(ResITmp != ResE);
     SymbolResolution Res = *ResITmp++;
 
-    if (!Sym.getIRName().empty()) {
+    if (!Sym.getIRName().empty() && Res.Prevailing) {
       auto GUID = GlobalValue::getGUIDAssumingExternalLinkage(
           GlobalValue::getGlobalIdentifier(Sym.getIRName(),
                                            GlobalValue::ExternalLinkage, ""));
-      if (Res.Prevailing)
-        ThinLTO.PrevailingModuleForGUID[GUID] = BM.getModuleIdentifier();
+      ThinLTO.setPrevailingModuleForGUID(GUID, BMID);
     }
   }
 
-  if (Error Err =
-          BM.readSummary(ThinLTO.CombinedIndex, BM.getModuleIdentifier(),
-                         [&](GlobalValue::GUID GUID) {
-                           return ThinLTO.PrevailingModuleForGUID[GUID] ==
-                                  BM.getModuleIdentifier();
-                         }))
+  if (Error Err = BM.readSummary(
+          ThinLTO.CombinedIndex, BMID, [&](GlobalValue::GUID GUID) {
+            return ThinLTO.isPrevailingModuleForGUID(GUID, BMID);
+          }))
     return Err;
-  LLVM_DEBUG(dbgs() << "Module " << BM.getModuleIdentifier() << "\n");
+  LLVM_DEBUG(dbgs() << "Module " << BMID << "\n");
 
   for (const InputFile::Symbol &Sym : Syms) {
     assert(ResI != ResE);
     SymbolResolution Res = *ResI++;
 
-    if (!Sym.getIRName().empty()) {
+    if (!Sym.getIRName().empty() &&
+        (Res.Prevailing || Res.FinalDefinitionInLinkageUnit)) {
       auto GUID = GlobalValue::getGUIDAssumingExternalLinkage(
           GlobalValue::getGlobalIdentifier(Sym.getIRName(),
                                            GlobalValue::ExternalLinkage, ""));
       if (Res.Prevailing) {
-        assert(ThinLTO.PrevailingModuleForGUID[GUID] ==
-               BM.getModuleIdentifier());
+        assert(ThinLTO.isPrevailingModuleForGUID(GUID, BMID));
 
         // For linker redefined symbols (via --wrap or --defsym) we want to
         // switch the linkage to `weak` to prevent IPOs from happening.
         // Find the summary in the module for this very GV and record the new
         // linkage so that we can switch it when we import the GV.
         if (Res.LinkerRedefined)
-          if (auto S = ThinLTO.CombinedIndex.findSummaryInModule(
-                  GUID, BM.getModuleIdentifier()))
+          if (auto S = ThinLTO.CombinedIndex.findSummaryInModule(GUID, BMID))
             S->setLinkage(GlobalValue::WeakAnyLinkage);
       }
 
       // If the linker resolved the symbol to a local definition then mark it
       // as local in the summary for the module we are adding.
       if (Res.FinalDefinitionInLinkageUnit) {
-        if (auto S = ThinLTO.CombinedIndex.findSummaryInModule(
-                GUID, BM.getModuleIdentifier())) {
+        if (auto S = ThinLTO.CombinedIndex.findSummaryInModule(GUID, BMID)) {
           S->setDSOLocal(true);
         }
       }
     }
   }
 
-  if (!ThinLTO.ModuleMap.insert({BM.getModuleIdentifier(), BM}).second)
+  if (!ThinLTO.ModuleMap.insert({BMID, BM}).second)
     return make_error<StringError>(
         "Expected at most one ThinLTO module per bitcode file",
         inconvertibleErrorCode());
@@ -1102,10 +1098,10 @@ Error LTO::addThinLTO(BitcodeModule BM, ArrayRef<InputFile::Symbol> Syms,
     // This is a fuzzy name matching where only modules with name containing the
     // specified switch values are going to be compiled.
     for (const std::string &Name : Conf.ThinLTOModulesToCompile) {
-      if (BM.getModuleIdentifier().contains(Name)) {
-        ThinLTO.ModulesToCompile->insert({BM.getModuleIdentifier(), BM});
-        LLVM_DEBUG(dbgs() << "[ThinLTO] Selecting " << BM.getModuleIdentifier()
-                          << " to compile\n");
+      if (BMID.contains(Name)) {
+        ThinLTO.ModulesToCompile->insert({BMID, BM});
+        LLVM_DEBUG(dbgs() << "[ThinLTO] Selecting " << BMID << " to compile\n");
+        break;
       }
     }
   }
@@ -1974,7 +1970,7 @@ Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache,
                                LocalWPDTargetsMap);
 
   auto isPrevailing = [&](GlobalValue::GUID GUID, const GlobalValueSummary *S) {
-    return ThinLTO.PrevailingModuleForGUID[GUID] == S->modulePath();
+    return ThinLTO.isPrevailingModuleForGUID(GUID, S->modulePath());
   };
   if (EnableMemProfContextDisambiguation) {
     MemProfContextDisambiguation ContextDisambiguation;

Copy link
Contributor

@teresajohnson teresajohnson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm thanks

@dyung
Copy link
Collaborator

dyung commented Nov 5, 2025

Hi @nga888, at this point in the release cycle we are only accepting fixes for regressions or major bug fixes. This only seems to be a performance improvement in a very specific scenario, so it does not seem to meet the bar, plus I am unsure how risky taking these two changes might be for the release branch. Can you provide some detail on why you feel we should incorporate these changes into the release branch at this time?

@nga888
Copy link
Collaborator Author

nga888 commented Nov 5, 2025

Hi @nga888, at this point in the release cycle we are only accepting fixes for regressions or major bug fixes. This only seems to be a performance improvement in a very specific scenario, so it does not seem to meet the bar, plus I am unsure how risky taking these two changes might be for the release branch. Can you provide some detail on why you feel we should incorporate these changes into the release branch at this time?

Yes, I forgot that these "final" point releases are only for regressions or bug fixes. Both these changes are NFC but you're right they don't really meet the criteria. Will have to wait for the improvement in the next release. Thanks!

@nga888 nga888 closed this Nov 5, 2025
@nga888 nga888 deleted the backport-pr164873-pr166067-21.x branch November 5, 2025 11:17
@dyung dyung moved this from Needs Triage to Won't Merge in LLVM Release Status Nov 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

LTO Link time optimization (regular/full LTO or ThinLTO)

Projects

Status: Won't Merge

Development

Successfully merging this pull request may close these issues.

4 participants