[llvm] Fix comdat removal (PR #199463)

via llvm-commits llvm-commits at lists.llvm.org
Sun May 24 16:53:38 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Aleksey (aleksisch)

<details>
<summary>Changes</summary>

Fixes #<!-- -->199462

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


4 Files Affected:

- (modified) llvm/lib/Transforms/IPO/ConstantMerge.cpp (+8-3) 
- (modified) llvm/lib/Transforms/IPO/GlobalOpt.cpp (+16-4) 
- (added) llvm/test/Transforms/ConstantMerge/comdat-key-associative.ll (+22) 
- (added) llvm/test/Transforms/GlobalOpt/comdat-key-associative.ll (+23) 


``````````diff
diff --git a/llvm/lib/Transforms/IPO/ConstantMerge.cpp b/llvm/lib/Transforms/IPO/ConstantMerge.cpp
index 06e684dbe5a93..6be3e05f8a3d3 100644
--- a/llvm/lib/Transforms/IPO/ConstantMerge.cpp
+++ b/llvm/lib/Transforms/IPO/ConstantMerge.cpp
@@ -160,9 +160,14 @@ static bool mergeConstants(Module &M) {
       // If this GV is dead, remove it.
       GV.removeDeadConstantUsers();
       if (GV.use_empty() && GV.hasLocalLinkage()) {
-        GV.eraseFromParent();
-        ++ChangesMade;
-        continue;
+        // Don't remove a comdat key; it would strand the group's associative
+        // members. Comdat-aware DCE handles dead keys.
+        const Comdat *C = GV.getComdat();
+        if (!C || C->getName() != GV.getName()) {
+          GV.eraseFromParent();
+          ++ChangesMade;
+          continue;
+        }
       }
 
       if (isUnmergeableGlobal(&GV, UsedGlobals))
diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index 75d0f27169822..4fe98700e0ca0 100644
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -1338,8 +1338,11 @@ deleteIfDead(GlobalValue &GV,
   if (!GV.isDiscardableIfUnused() && !GV.isDeclaration())
     return false;
 
+  // Don't delete the comdat key while the group must be kept: it would strand
+  // the group's associative members. (A local non-key member is removable.)
   if (const Comdat *C = GV.getComdat())
-    if (!GV.hasLocalLinkage() && NotDiscardableComdats.count(C))
+    if (NotDiscardableComdats.count(C) &&
+        (!GV.hasLocalLinkage() || GV.getName() == C->getName()))
       return false;
 
   bool Dead;
@@ -1648,7 +1651,8 @@ static bool
 processGlobal(GlobalValue &GV,
               function_ref<TargetTransformInfo &(Function &)> GetTTI,
               function_ref<TargetLibraryInfo &(Function &)> GetTLI,
-              function_ref<DominatorTree &(Function &)> LookupDomTree) {
+              function_ref<DominatorTree &(Function &)> LookupDomTree,
+              const SmallPtrSetImpl<const Comdat *> &NotDiscardableComdats) {
   if (GV.getName().starts_with("llvm."))
     return false;
 
@@ -1679,6 +1683,12 @@ processGlobal(GlobalValue &GV,
   if (GVar->isConstant() || !GVar->hasInitializer())
     return Changed;
 
+  // Don't let processInternalGlobal delete/replace a comdat key while the group
+  // must be kept; that would strand the group's associative members.
+  if (const Comdat *C = GVar->getComdat())
+    if (NotDiscardableComdats.count(C) && GVar->getName() == C->getName())
+      return Changed;
+
   return processInternalGlobal(GVar, GS, GetTTI, GetTLI, LookupDomTree) ||
          Changed;
 }
@@ -1978,7 +1988,8 @@ OptimizeFunctions(Module &M,
       }
     }
 
-    Changed |= processGlobal(F, GetTTI, GetTLI, LookupDomTree);
+    Changed |=
+        processGlobal(F, GetTTI, GetTLI, LookupDomTree, NotDiscardableComdats);
 
     if (!F.hasLocalLinkage())
       continue;
@@ -2077,7 +2088,8 @@ OptimizeGlobalVars(Module &M,
       continue;
     }
 
-    Changed |= processGlobal(GV, GetTTI, GetTLI, LookupDomTree);
+    Changed |= processGlobal(GV, GetTTI, GetTLI, LookupDomTree,
+                             NotDiscardableComdats);
   }
   return Changed;
 }
diff --git a/llvm/test/Transforms/ConstantMerge/comdat-key-associative.ll b/llvm/test/Transforms/ConstantMerge/comdat-key-associative.ll
new file mode 100644
index 0000000000000..5f157798eef98
--- /dev/null
+++ b/llvm/test/Transforms/ConstantMerge/comdat-key-associative.ll
@@ -0,0 +1,22 @@
+; RUN: opt -passes=constmerge -S < %s | FileCheck %s
+
+; @key is the key of a comdat group whose associative member @assoc (pinned by
+; llvm.used) survives. Even though @key is locally unused, constmerge's
+; dead-global cleanup must keep it: deleting the key would leave @assoc
+; referencing a comdat whose key symbol no longer exists, which is illegal for
+; COFF and aborts codegen in getComdatGVForCOFF.
+
+target triple = "x86_64-pc-windows-msvc"
+
+$key = comdat any
+$initfn = comdat any
+
+; CHECK: @key = internal thread_local global i32 0, comdat
+ at key = internal thread_local global i32 0, comdat, align 8
+; CHECK: @assoc = internal constant ptr @initfn{{.*}}comdat($key)
+ at assoc = internal constant ptr @initfn, section ".CRT$XDU", comdat($key)
+ at llvm.used = appending global [1 x ptr] [ptr @assoc], section "llvm.metadata"
+
+define internal void @initfn() comdat {
+  ret void
+}
diff --git a/llvm/test/Transforms/GlobalOpt/comdat-key-associative.ll b/llvm/test/Transforms/GlobalOpt/comdat-key-associative.ll
new file mode 100644
index 0000000000000..046868db2a669
--- /dev/null
+++ b/llvm/test/Transforms/GlobalOpt/comdat-key-associative.ll
@@ -0,0 +1,23 @@
+; RUN: opt -passes=globalopt -S < %s | FileCheck %s
+
+; @key is the key of a comdat group whose associative member @assoc (pinned by
+; llvm.used) survives. Even though @key is locally unused, globalopt must keep
+; it: deleting the key would leave @assoc referencing a comdat whose key symbol
+; no longer exists, which is illegal for COFF and aborts codegen in
+; getComdatGVForCOFF.
+
+target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc"
+
+$key = comdat any
+$initfn = comdat any
+
+; CHECK: @key = internal thread_local {{.*}}global i32 0, comdat
+ at key = internal thread_local global i32 0, comdat, align 8
+; CHECK: @assoc = internal constant ptr @initfn{{.*}}comdat($key)
+ at assoc = internal constant ptr @initfn, section ".CRT$XDU", comdat($key)
+ at llvm.used = appending global [1 x ptr] [ptr @assoc], section "llvm.metadata"
+
+define internal void @initfn() comdat {
+  ret void
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/199463


More information about the llvm-commits mailing list