https://github.com/erichkeane created 
https://github.com/llvm/llvm-project/pull/225929

Itanium emits the complete-object ctor/dtor as an alias to the base object 
version when they are able to be put into COMDAT. However, this is not 
currently implemented in CIR, where we are missing the 'comdat with a name' 
functionality.

This patch limits that to ONLY FuncOp, and only does the named version (instead 
of referencing its own name) for these ctor/dtors. Doing this for the comdat 
attribute and for GlobalOp is left to a future patch.

This showed up in the 'root' project, where the wrong dtor was called as a 
result!

Note: Claude helped me with the diagnosis, and wrote the reproducer.

>From ed598bd7d1b6881a01d1c5aacacd44166b47e99c Mon Sep 17 00:00:00 2001
From: erichkeane <[email protected]>
Date: Wed, 23 Sep 2026 13:19:27 -0700
Subject: [PATCH] [CIR] Implement comdat 'name' for dtors

Itanium emits the complete-object ctor/dtor as an alias to the base
object version when they are able to be put into COMDAT. However, this
is not currently implemented in CIR, where we are missing the 'comdat
with a name' functionality.

This patch limits that to ONLY FuncOp, and only does the named version
(instead of referencing its own name) for these ctor/dtors. Doing this
for the comdat attribute and for GlobalOp is left to a future patch.

This showed up in the 'root' project, where the wrong dtor was called as
a result!

Note: Claude helped me with the diagnosis, and wrote the reproducer.
---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  | 13 ++++++++++++-
 .../clang/CIR/Interfaces/CIROpInterfaces.td   |  2 +-
 clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 18 +++++++++++++++++-
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp       | 18 +++++++++++++++---
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp |  6 ++++--
 clang/test/CIR/CodeGen/dtor-comdat-key.cpp    | 19 +++++++++++++++++++
 6 files changed, 68 insertions(+), 8 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/dtor-comdat-key.cpp

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index f6b524176a655..2b772c34d1cb7 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3439,6 +3439,9 @@ def CIR_GlobalOp : CIR_RegionBranchOpBase<"global", [
                        OptionalAttr<CIR_TLSModelAttr>:$tls_model,
                        
OptionalAttr<CIR_ThreadLocalGlobalWrapperInitAttr>:$tls_refs,
                        OptionalAttr<AnyAttr>:$initial_value,
+                       // FIXME(cir): This eventually needs to become a
+                       // stringattr like FuncOp to allow customization of the
+                       // group name.
                        UnitProp:$comdat,
                        UnitProp:$constant,
                        UnitProp:$dso_local,
@@ -4277,6 +4280,10 @@ def CIR_FuncOp : CIR_Op<"func", [
     exception handling. This is a symbol reference to the personality function
     (e.g., `@__gxx_personality_v0` for C++ exceptions).
 
+    The 'comdat' attribute marks the function as belonging to a COMDAT group.
+    This attribute includes an optional 'group' name, which if not provided,
+    results in the function's symbol name being used.
+
     Example:
 
     ```
@@ -4329,7 +4336,7 @@ def CIR_FuncOp : CIR_Op<"func", [
       "cir::CallingConv::C"
     >:$calling_conv,
     OptionalAttr<StrAttr>:$sym_visibility,
-    UnitAttr:$comdat,
+    OptionalAttr<StrAttr>:$comdat,
     OptionalAttr<I64Attr>:$alignment,
     OptionalAttr<I64Attr>:$preferred_alignment,
     OptionalAttr<DictArrayAttr>:$arg_attrs,
@@ -4376,6 +4383,10 @@ def CIR_FuncOp : CIR_Op<"func", [
        return getFunctionType().getReturnTypes();
     }
 
+    void setComdat(bool) {
+      setComdat(llvm::StringRef(""));
+    }
+
     
//===------------------------------------------------------------------===//
     // SymbolOpInterface Methods
     
//===------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td 
b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td
index 1cd45dc5326a0..7ebce64d3e87b 100644
--- a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td
+++ b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td
@@ -169,7 +169,7 @@ let cppNamespace = "::cir" in {
       InterfaceMethod<"",
       "bool", "hasComdat", (ins), [{}],
       /*defaultImplementation=*/[{
-        return $_op.getComdat();
+        return static_cast<bool>($_op.getComdat());
       }]
       >,
       InterfaceMethod<"",
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp 
b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index 59461a6c6de4a..827f000ae573d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -22,11 +22,14 @@
 
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/GlobalDecl.h"
+#include "clang/AST/Mangle.h"
 #include "clang/AST/TypeBase.h"
 #include "clang/AST/VTableBuilder.h"
 #include "clang/CIR/MissingFeatures.h"
 #include "clang/CodeGenUtils/ItaniumCXXABIUtils.h"
+#include "llvm/ADT/SmallString.h"
 #include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
 
 using namespace clang;
 using namespace clang::CIRGen;
@@ -358,6 +361,7 @@ void CIRGenItaniumCXXABI::emitCXXStructor(GlobalDecl gd) {
   auto *md = cast<CXXMethodDecl>(gd.getDecl());
   StructorCIRGen cirGenType = getCIRGenToUse(cgm, md);
   const auto *cd = dyn_cast<CXXConstructorDecl>(md);
+  const CXXDestructorDecl *dd = cd ? nullptr : cast<CXXDestructorDecl>(md);
 
   if (cd ? gd.getCtorType() == Ctor_Complete
          : gd.getDtorType() == Dtor_Complete) {
@@ -381,7 +385,19 @@ void CIRGenItaniumCXXABI::emitCXXStructor(GlobalDecl gd) {
 
   auto fn = cgm.codegenCXXStructor(gd);
 
-  cgm.maybeSetTrivialComdat(*md, fn);
+  if (cirGenType == StructorCIRGen::COMDAT) {
+    llvm::SmallString<256> comdatKey;
+    llvm::raw_svector_ostream out(comdatKey);
+    ItaniumMangleContext &mangler =
+        cast<ItaniumMangleContext>(cgm.getCXXABI().getMangleContext());
+    if (dd)
+      mangler.mangleCXXDtorComdat(dd, out);
+    else
+      mangler.mangleCXXCtorComdat(cd, out);
+    fn.setComdat(llvm::StringRef(comdatKey));
+  } else {
+    cgm.maybeSetTrivialComdat(*md, fn);
+  }
 }
 
 void CIRGenItaniumCXXABI::addImplicitStructorParams(CIRGenFunction &cgf,
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp 
b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index b994d19ea1477..ce406707f5942 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -2708,8 +2708,17 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, 
OperationState &state) {
   if (parser.parseOptionalKeyword(noProtoNameAttr).succeeded())
     state.addAttribute(noProtoNameAttr, parser.getBuilder().getUnitAttr());
 
-  if (parser.parseOptionalKeyword(comdatNameAttr).succeeded())
-    state.addAttribute(comdatNameAttr, parser.getBuilder().getUnitAttr());
+  if (parser.parseOptionalKeyword(comdatNameAttr).succeeded()) {
+    std::string comdatKey;
+    if (mlir::succeeded(parser.parseOptionalLParen())) {
+      if (parser.parseString(&comdatKey).failed())
+        return failure();
+      if (parser.parseRParen().failed())
+        return failure();
+    }
+    state.addAttribute(comdatNameAttr,
+                       parser.getBuilder().getStringAttr(comdatKey));
+  }
 
   auto parseAlignmentBody = [&](int64_t &value) {
     if (parser.parseLParen().failed() || parser.parseInteger(value).failed() ||
@@ -3043,8 +3052,11 @@ void cir::FuncOp::print(OpAsmPrinter &p) {
   if (getNoProto())
     p << " no_proto";
 
-  if (getComdat())
+  if (std::optional<StringRef> comdatKey = getComdat()) {
     p << " comdat";
+    if (!comdatKey->empty())
+      p << "(\"" << *comdatKey << "\")";
+  }
 
   if (getAlignment())
     p << " alignment(" << *getAlignment() << ')';
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 1b1423862ba7b..bd529f71b38ed 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -3215,10 +3215,12 @@ CIRToLLVMGlobalOpLowering::getComdatAttr(cir::GlobalOp 
&op,
 mlir::SymbolRefAttr
 CIRToLLVMFuncOpLowering::getComdatAttr(cir::FuncOp &op,
                                        mlir::OpBuilder &builder) const {
-  if (!op.getComdat())
+  std::optional<llvm::StringRef> comdat = op.getComdat();
+  if (!comdat)
     return mlir::SymbolRefAttr{};
+  llvm::StringRef comdatKey = comdat->empty() ? op.getSymName() : *comdat;
   return getComdatAttrHelper(op->getParentOfType<mlir::ModuleOp>(), builder,
-                             op.getSymName(), comdatOp);
+                             comdatKey, comdatOp);
 }
 
 mlir::LogicalResult CIRToLLVMSwitchFlatOpLowering::matchAndRewrite(
diff --git a/clang/test/CIR/CodeGen/dtor-comdat-key.cpp 
b/clang/test/CIR/CodeGen/dtor-comdat-key.cpp
new file mode 100644
index 0000000000000..69121adb77669
--- /dev/null
+++ b/clang/test/CIR/CodeGen/dtor-comdat-key.cpp
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 
-mconstructor-aliases -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 
-mconstructor-aliases -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 
-mconstructor-aliases -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM
+
+template <class T> struct K {
+  T v;
+  virtual ~K() { v = T(); }
+};
+template class K<double>;
+
+// CIR: cir.func {{.*}} comdat("_ZN1KIdED5Ev") {{.*}} weak_odr @_ZN1KIdED2Ev(
+// CIR: cir.func weak_odr private @_ZN1KIdED1Ev(!cir.ptr<!rec_K3Cdouble3E>) 
alias(@_ZN1KIdED2Ev)
+
+// LLVM: $_ZN1KIdED5Ev = comdat any
+// LLVM: @_ZN1KIdED1Ev = weak_odr {{.*}}alias void (ptr), ptr @_ZN1KIdED2Ev
+// LLVM: define weak_odr void @_ZN1KIdED2Ev({{.*}}) {{.*}} 
comdat($_ZN1KIdED5Ev)

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to