[llvm] [llvm] don't merge constants with COMDAT (PR #192477)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Apr 19 13:10:37 PDT 2026
https://github.com/8051Enthusiast updated https://github.com/llvm/llvm-project/pull/192477
>From e47be65bbede730b4e4dfc4e662bc7203253affb Mon Sep 17 00:00:00 2001
From: 8051enthusiast <8051enthusiast at protonmail.com>
Date: Thu, 16 Apr 2026 17:20:23 +0200
Subject: [PATCH] [llvm] don't merge constants with differing COMDAT
After merging #190995 (now reverted) the CI failed because
ConstantMerge currently merges constants with differing COMDATs.
This can result in a function referencing globals with a different
COMDAT than before the merge, producing a linking error when that
global gets discarded.
---
llvm/lib/Transforms/IPO/ConstantMerge.cpp | 27 +++++++++++++++++++
.../Transforms/ConstantMerge/dont-merge.ll | 22 +++++++++++++++
.../Transforms/ConstantMerge/merge-comdat.ll | 18 +++++++++++++
3 files changed, 67 insertions(+)
create mode 100644 llvm/test/Transforms/ConstantMerge/merge-comdat.ll
diff --git a/llvm/lib/Transforms/IPO/ConstantMerge.cpp b/llvm/lib/Transforms/IPO/ConstantMerge.cpp
index 282bcabc34534..636d8de535456 100644
--- a/llvm/lib/Transforms/IPO/ConstantMerge.cpp
+++ b/llvm/lib/Transforms/IPO/ConstantMerge.cpp
@@ -92,14 +92,41 @@ isUnmergeableGlobal(GlobalVariable *GV,
}
enum class CanMerge { No, Yes };
+
+static CanMerge isMergeableComdat(GlobalVariable *Old, GlobalVariable *New) {
+ auto OldComdat = Old->getComdat();
+ auto NewComdat = New->getComdat();
+
+ if (OldComdat == NewComdat)
+ return CanMerge::Yes;
+
+ // If one of the globals doesn't have comdat, we can merge the globals into
+ // a global without comdat. The references to the global with comdat
+ // effectively get moved to the one without comdat, and the global with comdat
+ // has local linkage so it is allowed to be removed after having been merged
+ if ((!OldComdat && New->hasLocalLinkage()) ||
+ (!NewComdat && Old->hasLocalLinkage()))
+ return CanMerge::Yes;
+
+ return CanMerge::No;
+}
+
static CanMerge makeMergeable(GlobalVariable *Old, GlobalVariable *New) {
if (!Old->hasGlobalUnnamedAddr() && !New->hasGlobalUnnamedAddr())
return CanMerge::No;
if (Old->hasMetadataOtherThanDebugLoc())
return CanMerge::No;
+ // Merging constants with different comdats means one group cannot in general
+ // be dropped independently without the other group now having an invalid
+ // reference to the dropped constant
+ if (isMergeableComdat(Old, New) == CanMerge::No)
+ return CanMerge::No;
assert(!New->hasMetadataOtherThanDebugLoc());
if (!Old->hasGlobalUnnamedAddr())
New->setUnnamedAddr(GlobalValue::UnnamedAddr::None);
+ if (!Old->hasComdat())
+ New->setComdat(nullptr);
+
return CanMerge::Yes;
}
diff --git a/llvm/test/Transforms/ConstantMerge/dont-merge.ll b/llvm/test/Transforms/ConstantMerge/dont-merge.ll
index 0d90d82809693..4f2f734cd1e77 100644
--- a/llvm/test/Transforms/ConstantMerge/dont-merge.ll
+++ b/llvm/test/Transforms/ConstantMerge/dont-merge.ll
@@ -92,3 +92,25 @@ define void @test5(ptr %P1, ptr %P2) {
store ptr @T5ua, ptr %P2
ret void
}
+
+$test6a = comdat any
+$test6b = comdat any
+
+; If @T6a were to get merged into @T6b and the $test6b comdat group gets dropped,
+; @test6a would have a (now invalid) reference to the dropped @T6b constant
+
+ at T6a = private unnamed_addr constant i32 666, comdat($test6a)
+ at T6b = private unnamed_addr constant i32 666, comdat($test6b)
+
+; CHECK: @T6a
+; CHECK: @T6b
+
+define void @test6a(ptr %P) comdat {
+ store ptr @T6a, ptr %P
+ ret void
+}
+
+define void @test6b(ptr %P) comdat {
+ store ptr @T6b, ptr %P
+ ret void
+}
diff --git a/llvm/test/Transforms/ConstantMerge/merge-comdat.ll b/llvm/test/Transforms/ConstantMerge/merge-comdat.ll
new file mode 100644
index 0000000000000..93a767cd4abd3
--- /dev/null
+++ b/llvm/test/Transforms/ConstantMerge/merge-comdat.ll
@@ -0,0 +1,18 @@
+; RUN: opt -passes=constmerge -S < %s | FileCheck %s
+
+; @ComdatGlobal can be merged into @RegularGlobal which does not have
+; a comdat group and should not get dropped by the linker
+
+$test = comdat any
+
+ at ComdatGlobal = private unnamed_addr constant i32 111, comdat($test)
+ at RegularGlobal = constant i32 111
+
+; CHECK NOT: @ComdatGlobal
+; CHECK: @RegularGlobal
+
+define void @test(ptr %P) comdat {
+ store ptr @ComdatGlobal, ptr %P
+ store ptr @RegularGlobal, ptr %P
+ ret void
+}
More information about the llvm-commits
mailing list