[llvm] [ThinLTO] Drop !inline_history metadata when importing functions (PR #192564)

Arthur Eubanks via llvm-commits llvm-commits at lists.llvm.org
Fri Apr 17 11:00:15 PDT 2026


https://github.com/aeubanks updated https://github.com/llvm/llvm-project/pull/192564

>From acc084bb7aae5d693f2bc56368c22a5581ac14f9 Mon Sep 17 00:00:00 2001
From: Arthur Eubanks <aeubanks at google.com>
Date: Thu, 16 Apr 2026 22:43:17 +0000
Subject: [PATCH 1/3] [ThinLTO] Drop !inline_history metadata when importing
 functions

In #190876 we now have functions in ValueAsMetadata (!inline_history
metadata). This has caused undefined symbol linker errors in some
ThinLTO builds. The following is what's going on:

@f in module A is getting imported from module A to module B, and it has
a call with !inline_history pointing to @g in module A, so a declaration
for @g is also imported into module B. But @g gets internalized in
module A, causing the undefined symbol error at link time due to
memprof's ICP in module B creating a call to @g since we can ICP a call
to any declaration.

To avoid pulling in a function declaration that may be wrong, simply
drop !inline_history metadata when importing functions. They aren't
necessary for correctness, they only prevent inlining explosion in some
recursive edge cases. Worst case is we do another round of inlining
through mutually recursive functions and then stop again due to newly
added !inline_history metadata, which should be fine; the inlining
explosion typically happens because we keep inlining through mutually
recursive functions.
---
 .../include/llvm/Transforms/Utils/ValueMapper.h |  6 ++++++
 llvm/lib/Linker/IRMover.cpp                     |  5 ++++-
 llvm/lib/Transforms/Utils/ValueMapper.cpp       |  6 ++++++
 .../FunctionImport/Inputs/inline-history.ll     | 11 +++++++++++
 .../Transforms/FunctionImport/inline-history.ll | 17 +++++++++++++++++
 5 files changed, 44 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/Transforms/FunctionImport/Inputs/inline-history.ll
 create mode 100644 llvm/test/Transforms/FunctionImport/inline-history.ll

diff --git a/llvm/include/llvm/Transforms/Utils/ValueMapper.h b/llvm/include/llvm/Transforms/Utils/ValueMapper.h
index 28c4ae840b29f..f93ad822ce718 100644
--- a/llvm/include/llvm/Transforms/Utils/ValueMapper.h
+++ b/llvm/include/llvm/Transforms/Utils/ValueMapper.h
@@ -113,6 +113,12 @@ enum RemapFlags {
   /// don't use this flag. It's used when remapping is known to be un-necessary
   /// to save some compile-time.
   RF_DoNotRemapAtoms = 16,
+
+  /// Ignore metadata that references globals. In ThinLTO this is used because
+  /// metadata references to functions are not tracked and may cause referenced
+  /// functions to be incorrectly imported. Currently all metadata references
+  /// to functions are droppable.
+  RF_IgnoreMetadataReferencesToGlobals = 32,
 };
 
 inline RemapFlags operator|(RemapFlags LHS, RemapFlags RHS) {
diff --git a/llvm/lib/Linker/IRMover.cpp b/llvm/lib/Linker/IRMover.cpp
index 03c807ead9dfd..5eb632b6f0b18 100644
--- a/llvm/lib/Linker/IRMover.cpp
+++ b/llvm/lib/Linker/IRMover.cpp
@@ -446,7 +446,10 @@ class IRLinker {
         AddLazyFor(std::move(AddLazyFor)), TypeMap(Set),
         GValMaterializer(*this), LValMaterializer(*this), SharedMDs(SharedMDs),
         IsPerformingImport(IsPerformingImport),
-        Mapper(ValueMap, RF_ReuseAndMutateDistinctMDs | RF_IgnoreMissingLocals,
+        Mapper(ValueMap, RF_ReuseAndMutateDistinctMDs | RF_IgnoreMissingLocals |
+                             (IsPerformingImport
+                                  ? RF_IgnoreMetadataReferencesToGlobals
+                                  : RF_None),
                &TypeMap, &GValMaterializer),
         IndirectSymbolMCID(Mapper.registerAlternateMappingContext(
             IndirectSymbolValueMap, &LValMaterializer)) {
diff --git a/llvm/lib/Transforms/Utils/ValueMapper.cpp b/llvm/lib/Transforms/Utils/ValueMapper.cpp
index 6e36006890df4..1513639ff63c3 100644
--- a/llvm/lib/Transforms/Utils/ValueMapper.cpp
+++ b/llvm/lib/Transforms/Utils/ValueMapper.cpp
@@ -993,6 +993,12 @@ void Mapper::remapInstruction(Instruction *I) {
   if (auto *CB = dyn_cast<CallBase>(I)) {
     if (CB->getMetadata(LLVMContext::MD_callee_type) && !CB->isIndirectCall())
       CB->setMetadata(LLVMContext::MD_callee_type, nullptr);
+    // inline_history metadata can contain references to other functions that
+    // we don't want to map (e.g. ThinLTO doesn't track metadata references to
+    // functions).
+    if ((Flags & RF_IgnoreMetadataReferencesToGlobals) &&
+        CB->getMetadata(LLVMContext::MD_inline_history))
+      CB->setMetadata(LLVMContext::MD_inline_history, nullptr);
   }
 
   // Remap phi nodes' incoming blocks.
diff --git a/llvm/test/Transforms/FunctionImport/Inputs/inline-history.ll b/llvm/test/Transforms/FunctionImport/Inputs/inline-history.ll
new file mode 100644
index 0000000000000..a9c9d3687cd19
--- /dev/null
+++ b/llvm/test/Transforms/FunctionImport/Inputs/inline-history.ll
@@ -0,0 +1,11 @@
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+define void @imported_func() {
+  call void @another_func(), !inline_history !0
+  ret void
+}
+
+declare void @another_func()
+
+!0 = !{ptr @another_func}
diff --git a/llvm/test/Transforms/FunctionImport/inline-history.ll b/llvm/test/Transforms/FunctionImport/inline-history.ll
new file mode 100644
index 0000000000000..430607e1eb745
--- /dev/null
+++ b/llvm/test/Transforms/FunctionImport/inline-history.ll
@@ -0,0 +1,17 @@
+; RUN: opt -module-summary %s -o %t.bc
+; RUN: opt -module-summary %p/Inputs/inline-history.ll -o %t2.bc
+; RUN: llvm-lto -thinlto -o %t3 %t.bc %t2.bc
+; RUN: opt -passes=function-import -summary-file %t3.thinlto.bc %t.bc -S | FileCheck %s --implicit-check-not=inline_history
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+define void @caller() {
+  call void @imported_func()
+  ret void
+}
+
+declare void @imported_func()
+
+; CHECK: define available_externally void @imported_func()
+; CHECK-NEXT: call void @another_func()

>From cb74dff315fe6487db62d916f53b04e8498b2add Mon Sep 17 00:00:00 2001
From: Arthur Eubanks <aeubanks at google.com>
Date: Thu, 16 Apr 2026 23:24:29 +0000
Subject: [PATCH 2/3] format

---
 llvm/lib/Linker/IRMover.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Linker/IRMover.cpp b/llvm/lib/Linker/IRMover.cpp
index 5eb632b6f0b18..0b67c19298ffa 100644
--- a/llvm/lib/Linker/IRMover.cpp
+++ b/llvm/lib/Linker/IRMover.cpp
@@ -446,10 +446,10 @@ class IRLinker {
         AddLazyFor(std::move(AddLazyFor)), TypeMap(Set),
         GValMaterializer(*this), LValMaterializer(*this), SharedMDs(SharedMDs),
         IsPerformingImport(IsPerformingImport),
-        Mapper(ValueMap, RF_ReuseAndMutateDistinctMDs | RF_IgnoreMissingLocals |
-                             (IsPerformingImport
-                                  ? RF_IgnoreMetadataReferencesToGlobals
-                                  : RF_None),
+        Mapper(ValueMap,
+               RF_ReuseAndMutateDistinctMDs | RF_IgnoreMissingLocals |
+                   (IsPerformingImport ? RF_IgnoreMetadataReferencesToGlobals
+                                       : RF_None),
                &TypeMap, &GValMaterializer),
         IndirectSymbolMCID(Mapper.registerAlternateMappingContext(
             IndirectSymbolValueMap, &LValMaterializer)) {

>From 5c7b552121d8cb9002f533ba03796d27021b28c2 Mon Sep 17 00:00:00 2001
From: Arthur Eubanks <aeubanks at google.com>
Date: Fri, 17 Apr 2026 17:59:58 +0000
Subject: [PATCH 3/3] address comments

---
 llvm/include/llvm/Transforms/Utils/ValueMapper.h      | 8 +++-----
 llvm/lib/Linker/IRMover.cpp                           | 3 +--
 llvm/lib/Transforms/Utils/ValueMapper.cpp             | 8 ++++----
 llvm/test/Transforms/FunctionImport/inline-history.ll | 4 ++++
 4 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/llvm/include/llvm/Transforms/Utils/ValueMapper.h b/llvm/include/llvm/Transforms/Utils/ValueMapper.h
index f93ad822ce718..55c8862a6c32b 100644
--- a/llvm/include/llvm/Transforms/Utils/ValueMapper.h
+++ b/llvm/include/llvm/Transforms/Utils/ValueMapper.h
@@ -114,11 +114,9 @@ enum RemapFlags {
   /// to save some compile-time.
   RF_DoNotRemapAtoms = 16,
 
-  /// Ignore metadata that references globals. In ThinLTO this is used because
-  /// metadata references to functions are not tracked and may cause referenced
-  /// functions to be incorrectly imported. Currently all metadata references
-  /// to functions are droppable.
-  RF_IgnoreMetadataReferencesToGlobals = 32,
+  /// Indicate that we are importing functions, specifically in the context of
+  /// ThinLTO. There is some ad-hoc behavior required in this mode.
+  RF_Importing = 32,
 };
 
 inline RemapFlags operator|(RemapFlags LHS, RemapFlags RHS) {
diff --git a/llvm/lib/Linker/IRMover.cpp b/llvm/lib/Linker/IRMover.cpp
index 0b67c19298ffa..c88bdbb124205 100644
--- a/llvm/lib/Linker/IRMover.cpp
+++ b/llvm/lib/Linker/IRMover.cpp
@@ -448,8 +448,7 @@ class IRLinker {
         IsPerformingImport(IsPerformingImport),
         Mapper(ValueMap,
                RF_ReuseAndMutateDistinctMDs | RF_IgnoreMissingLocals |
-                   (IsPerformingImport ? RF_IgnoreMetadataReferencesToGlobals
-                                       : RF_None),
+                   (IsPerformingImport ? RF_Importing : RF_None),
                &TypeMap, &GValMaterializer),
         IndirectSymbolMCID(Mapper.registerAlternateMappingContext(
             IndirectSymbolValueMap, &LValMaterializer)) {
diff --git a/llvm/lib/Transforms/Utils/ValueMapper.cpp b/llvm/lib/Transforms/Utils/ValueMapper.cpp
index 1513639ff63c3..73ba1e6a128b0 100644
--- a/llvm/lib/Transforms/Utils/ValueMapper.cpp
+++ b/llvm/lib/Transforms/Utils/ValueMapper.cpp
@@ -993,10 +993,10 @@ void Mapper::remapInstruction(Instruction *I) {
   if (auto *CB = dyn_cast<CallBase>(I)) {
     if (CB->getMetadata(LLVMContext::MD_callee_type) && !CB->isIndirectCall())
       CB->setMetadata(LLVMContext::MD_callee_type, nullptr);
-    // inline_history metadata can contain references to other functions that
-    // we don't want to map (e.g. ThinLTO doesn't track metadata references to
-    // functions).
-    if ((Flags & RF_IgnoreMetadataReferencesToGlobals) &&
+    // !inline_history metadata can contain references to other functions that
+    // we don't want to map because ThinLTO doesn't track metadata references
+    // to functions.
+    if ((Flags & RF_Importing) &&
         CB->getMetadata(LLVMContext::MD_inline_history))
       CB->setMetadata(LLVMContext::MD_inline_history, nullptr);
   }
diff --git a/llvm/test/Transforms/FunctionImport/inline-history.ll b/llvm/test/Transforms/FunctionImport/inline-history.ll
index 430607e1eb745..5d24bb0dc9d51 100644
--- a/llvm/test/Transforms/FunctionImport/inline-history.ll
+++ b/llvm/test/Transforms/FunctionImport/inline-history.ll
@@ -1,3 +1,7 @@
+; Test to ensure that we don't import !inline_history metadata on calls since
+; doing so may end up importing other function declarations in a way that isn't
+; tracked by ThinLTO, breaking IR semantics.
+
 ; RUN: opt -module-summary %s -o %t.bc
 ; RUN: opt -module-summary %p/Inputs/inline-history.ll -o %t2.bc
 ; RUN: llvm-lto -thinlto -o %t3 %t.bc %t2.bc



More information about the llvm-commits mailing list