[llvm] [llvm] Remove incorrect IRMover assert (PR #221758)
Sebastian Neubauer via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 22 03:19:42 PDT 2026
https://github.com/Flakebi updated https://github.com/llvm/llvm-project/pull/221758
>From 6ae72ebde95aac7cbbf83eb896b2752a52f5e2a2 Mon Sep 17 00:00:00 2001
From: Sebastian Neubauer <Sebastian.Neubauer at amd.com>
Date: Mon, 7 Sep 2026 16:55:43 +0200
Subject: [PATCH 1/4] [llvm] Remove incorrect IRMover assert
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The IRMover had an assertion that failed on modules with correct code.
The assert fired in an out-of-tree frontend.
Remove the assertion to fix that.
The assertion has been there for a long time (10 years if I read
git blame correctly) as the failing case cannot be hit when loading
completely separated modules (i.e. this is not reproducible with
`llvm-link`). It is however reproducible when generating modules from
code. One peculiarity is that a named struct type needs to be used in
both, the source and target module that are linked.
As types are stored in the LLVMContext, not in the Module, I believe
this is still correct IR and the IRLinker/Mover should be able to handle
that correctly.
Concretely, the assert fired when filling `MappedTypes`.
The dst Module contains `%SharedTy`,
the src Module contains `%SharedTy` (exact same type) and `%Ty2`
(structurally equivalent to `%SharedTy`).
When the linker encounters `%Ty2` in src, it searches for an equivalent
type in dst, finding `%SharedTy` and recording the `%Ty` → `%SharedTy`
mapping in `MappedTypes`.
When the linker afterwards encounters `%SharedTy`, it finds a mapping
that has `%SharedTy` as a target and asserts.
As in this case, `%SharedTy` is used in both, source and destination
modules, finding `%SharedTy` as a target is not a sign of corrupted
state and we can just continue linking.
---
llvm/lib/Linker/IRMover.cpp | 9 ------
llvm/unittests/Linker/LinkModulesTest.cpp | 34 +++++++++++++++++++++++
2 files changed, 34 insertions(+), 9 deletions(-)
diff --git a/llvm/lib/Linker/IRMover.cpp b/llvm/lib/Linker/IRMover.cpp
index 3b72b412d0b2e0..148afa012f9068 100644
--- a/llvm/lib/Linker/IRMover.cpp
+++ b/llvm/lib/Linker/IRMover.cpp
@@ -163,15 +163,6 @@ Type *TypeMapTy::get(Type *Ty) {
// These are types that LLVM itself will unique.
bool IsUniqued = !isa<StructType>(Ty) || cast<StructType>(Ty)->isLiteral();
- if (!IsUniqued) {
-#ifndef NDEBUG
- for (auto &Pair : MappedTypes) {
- assert(!(Pair.first != Ty && Pair.second == Ty) &&
- "mapping to a source type");
- }
-#endif
- }
-
// If this is not a recursive type, then just map all of the elements and
// then rebuild the type from inside out.
SmallVector<Type *, 4> ElementTypes;
diff --git a/llvm/unittests/Linker/LinkModulesTest.cpp b/llvm/unittests/Linker/LinkModulesTest.cpp
index 9d996e3eba0efb..939ac3997bb842 100644
--- a/llvm/unittests/Linker/LinkModulesTest.cpp
+++ b/llvm/unittests/Linker/LinkModulesTest.cpp
@@ -203,6 +203,40 @@ TEST_F(LinkModuleTest, TypeMerge) {
M1->getNamedGlobal("t2")->getType());
}
+TEST_F(LinkModuleTest, TypeMergeSameType) {
+ IRBuilder<> Builder(EntryBB);
+
+ // Create a type in the context, shared between modules
+ Type *SharedTy = StructType::create(Ctx, {Builder.getInt32Ty()}, "SharedTy");
+
+ // Use it in the first module
+ Value *Load = Builder.CreateLoad(SharedTy, GV, "load");
+ Builder.CreateRet(Load);
+
+ std::unique_ptr<Module> NewMod = std::make_unique<Module>("MySecondModule", Ctx);
+ Ctx.setDiagnosticHandlerCallBack(expectNoDiags);
+
+ // Create another type, only for the second module.
+ // When linking, this one will be merged with SharedTy from the first module.
+ Type *Ty2 = StructType::create(Ctx, {Builder.getInt32Ty()}, "Ty2");
+ // define ptr @func2(%Ty2 %0, %SharedTy %1)
+ FunctionType *FTy =
+ FunctionType::get(Builder.getInt32Ty(), {Ty2, SharedTy},
+ false /*=isVarArg*/);
+ Function *F2 = Function::Create(FTy, Function::ExternalLinkage, "func2", NewMod.get());
+ BasicBlock *EntryBB2 = BasicBlock::Create(Ctx, "entry", F2);
+ Builder.SetInsertPoint(EntryBB2);
+ Builder.CreateRet(Builder.getInt32(2));
+
+ // Linking the modules previously asserted when mapping types.
+ Linker::linkModules(*M, std::move(NewMod));
+
+ // Make sure the linked module looks somewhat like expected, having func2 with
+ // merged argument types.
+ EXPECT_EQ(M->getFunction("func2")->getFunctionType()->getParamType(0), SharedTy);
+ EXPECT_EQ(M->getFunction("func2")->getFunctionType()->getParamType(1), SharedTy);
+}
+
TEST_F(LinkModuleTest, NewCAPISuccess) {
std::unique_ptr<Module> DestM(getExternal(Ctx, "foo"));
std::unique_ptr<Module> SourceM(getExternal(Ctx, "bar"));
>From 95bd3e1bfd7e2c992b5591407a6b095ed64121b1 Mon Sep 17 00:00:00 2001
From: Sebastian Neubauer <Sebastian.Neubauer at amd.com>
Date: Mon, 7 Sep 2026 17:18:17 +0200
Subject: [PATCH 2/4] Fix formatting
---
llvm/unittests/Linker/LinkModulesTest.cpp | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/llvm/unittests/Linker/LinkModulesTest.cpp b/llvm/unittests/Linker/LinkModulesTest.cpp
index 939ac3997bb842..5f12cfdaceee0a 100644
--- a/llvm/unittests/Linker/LinkModulesTest.cpp
+++ b/llvm/unittests/Linker/LinkModulesTest.cpp
@@ -213,17 +213,18 @@ TEST_F(LinkModuleTest, TypeMergeSameType) {
Value *Load = Builder.CreateLoad(SharedTy, GV, "load");
Builder.CreateRet(Load);
- std::unique_ptr<Module> NewMod = std::make_unique<Module>("MySecondModule", Ctx);
+ std::unique_ptr<Module> NewMod =
+ std::make_unique<Module>("MySecondModule", Ctx);
Ctx.setDiagnosticHandlerCallBack(expectNoDiags);
// Create another type, only for the second module.
// When linking, this one will be merged with SharedTy from the first module.
Type *Ty2 = StructType::create(Ctx, {Builder.getInt32Ty()}, "Ty2");
// define ptr @func2(%Ty2 %0, %SharedTy %1)
- FunctionType *FTy =
- FunctionType::get(Builder.getInt32Ty(), {Ty2, SharedTy},
- false /*=isVarArg*/);
- Function *F2 = Function::Create(FTy, Function::ExternalLinkage, "func2", NewMod.get());
+ FunctionType *FTy = FunctionType::get(Builder.getInt32Ty(), {Ty2, SharedTy},
+ false /*=isVarArg*/);
+ Function *F2 =
+ Function::Create(FTy, Function::ExternalLinkage, "func2", NewMod.get());
BasicBlock *EntryBB2 = BasicBlock::Create(Ctx, "entry", F2);
Builder.SetInsertPoint(EntryBB2);
Builder.CreateRet(Builder.getInt32(2));
@@ -233,8 +234,10 @@ TEST_F(LinkModuleTest, TypeMergeSameType) {
// Make sure the linked module looks somewhat like expected, having func2 with
// merged argument types.
- EXPECT_EQ(M->getFunction("func2")->getFunctionType()->getParamType(0), SharedTy);
- EXPECT_EQ(M->getFunction("func2")->getFunctionType()->getParamType(1), SharedTy);
+ EXPECT_EQ(M->getFunction("func2")->getFunctionType()->getParamType(0),
+ SharedTy);
+ EXPECT_EQ(M->getFunction("func2")->getFunctionType()->getParamType(1),
+ SharedTy);
}
TEST_F(LinkModuleTest, NewCAPISuccess) {
>From 66467fa33a96c83f27968a5dc86778803e1b0256 Mon Sep 17 00:00:00 2001
From: Sebastian Neubauer <Sebastian.Neubauer at amd.com>
Date: Thu, 17 Sep 2026 19:55:51 +0200
Subject: [PATCH 3/4] IRMover: Do not clear name of source type
Preserves the type name if it is also used in the destination module.
---
llvm/lib/Linker/IRMover.cpp | 1 -
llvm/unittests/Linker/LinkModulesTest.cpp | 1 +
2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Linker/IRMover.cpp b/llvm/lib/Linker/IRMover.cpp
index 148afa012f9068..3f413bfc04b2b4 100644
--- a/llvm/lib/Linker/IRMover.cpp
+++ b/llvm/lib/Linker/IRMover.cpp
@@ -218,7 +218,6 @@ Type *TypeMapTy::get(Type *Ty) {
if (StructType *OldT =
DstStructTypesSet.findNonOpaque(ElementTypes, IsPacked)) {
- STy->setName("");
return *Entry = OldT;
}
diff --git a/llvm/unittests/Linker/LinkModulesTest.cpp b/llvm/unittests/Linker/LinkModulesTest.cpp
index 5f12cfdaceee0a..8290838086f4ca 100644
--- a/llvm/unittests/Linker/LinkModulesTest.cpp
+++ b/llvm/unittests/Linker/LinkModulesTest.cpp
@@ -238,6 +238,7 @@ TEST_F(LinkModuleTest, TypeMergeSameType) {
SharedTy);
EXPECT_EQ(M->getFunction("func2")->getFunctionType()->getParamType(1),
SharedTy);
+ EXPECT_EQ(SharedTy->getStructName(), "SharedTy");
}
TEST_F(LinkModuleTest, NewCAPISuccess) {
>From 6a13116b231fdeed034d46e0a2cd2fe7505a11b7 Mon Sep 17 00:00:00 2001
From: Sebastian Neubauer <Sebastian.Neubauer at amd.com>
Date: Tue, 22 Sep 2026 12:18:46 +0200
Subject: [PATCH 4/4] Add lto test that failed assert
---
llvm/test/ThinLTO/X86/Inputs/link-type-src.ll | 20 ++++++++++++
llvm/test/ThinLTO/X86/link-type.ll | 32 +++++++++++++++++++
2 files changed, 52 insertions(+)
create mode 100644 llvm/test/ThinLTO/X86/Inputs/link-type-src.ll
create mode 100644 llvm/test/ThinLTO/X86/link-type.ll
diff --git a/llvm/test/ThinLTO/X86/Inputs/link-type-src.ll b/llvm/test/ThinLTO/X86/Inputs/link-type-src.ll
new file mode 100644
index 00000000000000..97a60cef9a117a
--- /dev/null
+++ b/llvm/test/ThinLTO/X86/Inputs/link-type-src.ll
@@ -0,0 +1,20 @@
+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"
+
+%T2 = type { double, double, i32, i32 }
+
+define void @src_f(ptr byval(%T2) %p) {
+ ret void, !dbg !3
+}
+
+!llvm.module.flags = !{!0}
+!llvm.dbg.cu = !{!1}
+
+!0 = !{i32 2, !"Debug Info Version", i32 3}
+!1 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !2, emissionKind: FullDebug)
+!2 = !DIFile(filename: "src.cpp", directory: "")
+!3 = !DILocation(line: 1, scope: !4)
+!4 = distinct !DISubprogram(name: "g", scope: !2, file: !2, type: !5, spFlags: DISPFlagDefinition, unit: !1)
+!5 = !DISubroutineType(types: !6)
+!6 = !{!7}
+!7 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "Shared", identifier: "_ZTS6Shared")
diff --git a/llvm/test/ThinLTO/X86/link-type.ll b/llvm/test/ThinLTO/X86/link-type.ll
new file mode 100644
index 00000000000000..9d414e3febfc40
--- /dev/null
+++ b/llvm/test/ThinLTO/X86/link-type.ll
@@ -0,0 +1,32 @@
+; Check that linking with the same type in source and target modules works without asserting.
+
+; RUN: opt -module-summary -o %t.bc %s
+; RUN: opt -module-summary -o %t-src.bc %p/Inputs/link-type-src.ll
+; RUN: llvm-lto2 run -o %t.out %t.bc %t-src.bc \
+; RUN: -r %t.bc,dst_f,px -r %t.bc,src_f, -r %t-src.bc,src_f,px
+
+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"
+
+%T1 = type { double, double, i32, i32 }
+
+define void @dst_f() {
+ call void @src_f(ptr byval(%T1) null), !dbg !3
+ ret void
+}
+
+declare void @src_f(ptr)
+
+!llvm.module.flags = !{!0}
+!llvm.dbg.cu = !{!1}
+
+!0 = !{i32 2, !"Debug Info Version", i32 3}
+!1 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !2, emissionKind: FullDebug)
+!2 = !DIFile(filename: "dst.cpp", directory: "")
+!3 = !DILocation(line: 1, scope: !4)
+!4 = distinct !DISubprogram(name: "f", scope: !2, file: !2, type: !5, spFlags: DISPFlagDefinition, unit: !1)
+!5 = !DISubroutineType(types: !6)
+!6 = !{!7}
+!7 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "Shared", templateParams: !8, identifier: "_ZTS6Shared")
+!8 = !{!9}
+!9 = !DITemplateValueParameter(value: %T1 zeroinitializer)
More information about the llvm-commits
mailing list