[clang] [CIR] Add support for global destructors (PR #162532)

via cfe-commits cfe-commits at lists.llvm.org
Wed Oct 8 11:45:28 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Andy Kaylor (andykaylor)

<details>
<summary>Changes</summary>

This adds support for generating destructor calls for global variables that have a destructor. This doesn't handle functions that are marked as global destructors with "__attribute__((destructor))". That will be handled later.

---

Patch is 23.84 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/162532.diff


8 Files Affected:

- (modified) clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h (+6) 
- (modified) clang/include/clang/CIR/MissingFeatures.h (+1-2) 
- (modified) clang/lib/CIR/CodeGen/CIRGenCXX.cpp (+58-1) 
- (modified) clang/lib/CIR/CodeGen/CIRGenCXXABI.h (+18) 
- (modified) clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp (+23) 
- (modified) clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp (+96-7) 
- (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+6-1) 
- (modified) clang/test/CIR/CodeGen/global-init.cpp (+90-18) 


``````````diff
diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index 89b519e96a93e..11ad61fdb8065 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -374,6 +374,12 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
                         resOperands, attrs);
   }
 
+  cir::CallOp createCallOp(mlir::Location loc, mlir::SymbolRefAttr callee,
+                           mlir::ValueRange operands = mlir::ValueRange(),
+                           llvm::ArrayRef<mlir::NamedAttribute> attrs = {}) {
+    return createCallOp(loc, callee, cir::VoidType(), operands, attrs);
+  }
+
   cir::CallOp createTryCallOp(
       mlir::Location loc, mlir::SymbolRefAttr callee = mlir::SymbolRefAttr(),
       mlir::Type returnType = cir::VoidType(),
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 3b7b130ebc973..fcce668947f53 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -38,9 +38,8 @@ struct MissingFeatures {
   static bool opGlobalPartition() { return false; }
   static bool opGlobalUsedOrCompilerUsed() { return false; }
   static bool opGlobalAnnotations() { return false; }
-  static bool opGlobalDtorLowering() { return false; }
   static bool opGlobalCtorPriority() { return false; }
-  static bool opGlobalCtorList() { return false; }
+  static bool opGlobalDtorList() { return false; }
   static bool setDSOLocal() { return false; }
   static bool setComdat() { return false; }
 
diff --git a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
index d5b35c25c83ba..274d11b8c7629 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
@@ -10,6 +10,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "CIRGenCXXABI.h"
 #include "CIRGenFunction.h"
 #include "CIRGenModule.h"
 
@@ -95,7 +96,63 @@ static void emitDeclDestroy(CIRGenFunction &cgf, const VarDecl *vd,
     return;
   }
 
-  cgf.cgm.errorNYI(vd->getSourceRange(), "global with destructor");
+  // If not constant storage we'll emit this regardless of NeedsDtor value.
+  CIRGenBuilderTy &builder = cgf.getBuilder();
+
+  // Prepare the dtor region.
+  mlir::OpBuilder::InsertionGuard guard(builder);
+  mlir::Block *block = builder.createBlock(&addr.getDtorRegion());
+  CIRGenFunction::LexicalScope lexScope{cgf, addr.getLoc(),
+                                        builder.getInsertionBlock()};
+  lexScope.setAsGlobalInit();
+  builder.setInsertionPointToStart(block);
+
+  CIRGenModule &cgm = cgf.cgm;
+  QualType type = vd->getType();
+
+  // Special-case non-array C++ destructors, if they have the right signature.
+  // Under some ABIs, destructors return this instead of void, and cannot be
+  // passed directly to __cxa_atexit if the target does not allow this
+  // mismatch.
+  const CXXRecordDecl *record = type->getAsCXXRecordDecl();
+  bool canRegisterDestructor =
+      record && (!cgm.getCXXABI().hasThisReturn(
+                     GlobalDecl(record->getDestructor(), Dtor_Complete)) ||
+                 cgm.getCXXABI().canCallMismatchedFunctionType());
+
+  // If __cxa_atexit is disabled via a flag, a different helper function is
+  // generated elsewhere which uses atexit instead, and it takes the destructor
+  // directly.
+  cir::FuncOp fnOp;
+  if (record && (canRegisterDestructor || cgm.getCodeGenOpts().CXAAtExit)) {
+    if (vd->getTLSKind())
+      cgm.errorNYI(vd->getSourceRange(), "TLS destructor");
+    assert(!record->hasTrivialDestructor());
+    assert(!cir::MissingFeatures::openCL());
+    CXXDestructorDecl *dtor = record->getDestructor();
+    // In LLVM OG codegen this is done in registerGlobalDtor, but CIRGen
+    // relies on LoweringPrepare for further decoupling, so build the
+    // call right here.
+    auto gd = GlobalDecl(dtor, Dtor_Complete);
+    fnOp = cgm.getAddrAndTypeOfCXXStructor(gd).second;
+    cgf.getBuilder().createCallOp(
+        cgf.getLoc(vd->getSourceRange()),
+        mlir::FlatSymbolRefAttr::get(fnOp.getSymNameAttr()),
+        mlir::ValueRange{cgm.getAddrOfGlobalVar(vd)});
+  } else {
+    cgm.errorNYI(vd->getSourceRange(), "array destructor");
+  }
+  assert(fnOp && "expected cir.func");
+  cgm.getCXXABI().registerGlobalDtor(vd, fnOp, nullptr);
+
+  builder.setInsertionPointToEnd(block);
+  if (block->empty()) {
+    block->erase();
+    // Don't confuse lexical cleanup.
+    builder.clearInsertionPoint();
+  } else {
+    builder.create<cir::YieldOp>(addr.getLoc());
+  }
 }
 
 cir::FuncOp CIRGenModule::codegenCXXStructor(GlobalDecl gd) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
index 2465a68b068bf..26da7ce94e0ef 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
+++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
@@ -160,6 +160,14 @@ class CIRGenCXXABI {
                                   bool forVirtualBase, bool delegating,
                                   Address thisAddr, QualType thisTy) = 0;
 
+  /// Emit code to force the execution of a destructor during global
+  /// teardown.  The default implementation of this uses atexit.
+  ///
+  /// \param dtor - a function taking a single pointer argument
+  /// \param addr - a pointer to pass to the destructor function.
+  virtual void registerGlobalDtor(const VarDecl *vd, cir::FuncOp dtor,
+                                  mlir::Value addr) = 0;
+
   /// Checks if ABI requires extra virtual offset for vtable field.
   virtual bool
   isVirtualOffsetNeededForVTableField(CIRGenFunction &cgf,
@@ -233,6 +241,16 @@ class CIRGenCXXABI {
     return false;
   }
 
+  /// Returns true if the target allows calling a function through a pointer
+  /// with a different signature than the actual function (or equivalently,
+  /// bitcasting a function or function pointer to a different function type).
+  /// In principle in the most general case this could depend on the target, the
+  /// calling convention, and the actual types of the arguments and return
+  /// value. Here it just means whether the signature mismatch could *ever* be
+  /// allowed; in other words, does the target do strict checking of signatures
+  /// for all calls.
+  virtual bool canCallMismatchedFunctionType() const { return true; }
+
   /// Gets the mangle context.
   clang::MangleContext &getMangleContext() { return *mangleContext; }
 
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index 04181740ccf6e..5622a68f608a0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -68,6 +68,8 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
                           CXXDtorType type, bool forVirtualBase,
                           bool delegating, Address thisAddr,
                           QualType thisTy) override;
+  void registerGlobalDtor(const VarDecl *vd, cir::FuncOp dtor,
+                          mlir::Value addr) override;
 
   void emitRethrow(CIRGenFunction &cgf, bool isNoReturn) override;
   void emitThrow(CIRGenFunction &cgf, const CXXThrowExpr *e) override;
@@ -1507,6 +1509,27 @@ void CIRGenItaniumCXXABI::emitDestructorCall(
                             vttTy, nullptr);
 }
 
+void CIRGenItaniumCXXABI::registerGlobalDtor(const VarDecl *vd,
+                                             cir::FuncOp dtor,
+                                             mlir::Value addr) {
+  if (vd->isNoDestroy(cgm.getASTContext()))
+    return;
+
+  if (vd->getTLSKind()) {
+    cgm.errorNYI(vd->getSourceRange(), "registerGlobalDtor: TLS");
+    return;
+  }
+
+  // HLSL doesn't support atexit.
+  if (cgm.getLangOpts().HLSL) {
+    cgm.errorNYI(vd->getSourceRange(), "registerGlobalDtor: HLSL");
+    return;
+  }
+
+  // The default behavior is to use atexit. This is handled in lowering
+  // prepare. Nothing to be done for CIR here.
+}
+
 // The idea here is creating a separate block for the throw with an
 // `UnreachableOp` as the terminator. So, we branch from the current block
 // to the throw block and create a block for the remaining operations.
diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index bc917d0604668..706e54f064aa6 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -41,6 +41,16 @@ static SmallString<128> getTransformedFileName(mlir::ModuleOp mlirModule) {
   return fileName;
 }
 
+/// Return the FuncOp called by `callOp`.
+static cir::FuncOp getCalledFunction(cir::CallOp callOp) {
+  mlir::SymbolRefAttr sym = llvm::dyn_cast_if_present<mlir::SymbolRefAttr>(
+      callOp.getCallableForCallee());
+  if (!sym)
+    return nullptr;
+  return dyn_cast_or_null<cir::FuncOp>(
+      mlir::SymbolTable::lookupNearestSymbolFrom(callOp, sym));
+}
+
 namespace {
 struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
   LoweringPreparePass() = default;
@@ -69,6 +79,12 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
       cir::FuncType type,
       cir::GlobalLinkageKind linkage = cir::GlobalLinkageKind::ExternalLinkage);
 
+  cir::GlobalOp buildRuntimeVariable(
+      mlir::OpBuilder &builder, llvm::StringRef name, mlir::Location loc,
+      mlir::Type type,
+      cir::GlobalLinkageKind linkage = cir::GlobalLinkageKind::ExternalLinkage,
+      cir::VisibilityKind visibility = cir::VisibilityKind::Default);
+
   ///
   /// AST related
   /// -----------
@@ -90,6 +106,25 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
 
 } // namespace
 
+cir::GlobalOp LoweringPreparePass::buildRuntimeVariable(
+    mlir::OpBuilder &builder, llvm::StringRef name, mlir::Location loc,
+    mlir::Type type, cir::GlobalLinkageKind linkage,
+    cir::VisibilityKind visibility) {
+  cir::GlobalOp g = dyn_cast_or_null<cir::GlobalOp>(
+      mlir::SymbolTable::lookupNearestSymbolFrom(
+          mlirModule, mlir::StringAttr::get(mlirModule->getContext(), name)));
+  if (!g) {
+    g = cir::GlobalOp::create(builder, loc, name, type);
+    g.setLinkageAttr(
+        cir::GlobalLinkageKindAttr::get(builder.getContext(), linkage));
+    mlir::SymbolTable::setSymbolVisibility(
+        g, mlir::SymbolTable::Visibility::Private);
+    g.setGlobalVisibilityAttr(
+        cir::VisibilityAttr::get(builder.getContext(), visibility));
+  }
+  return g;
+}
+
 cir::FuncOp LoweringPreparePass::buildRuntimeFunction(
     mlir::OpBuilder &builder, llvm::StringRef name, mlir::Location loc,
     cir::FuncType type, cir::GlobalLinkageKind linkage) {
@@ -640,7 +675,8 @@ LoweringPreparePass::buildCXXGlobalVarDeclInitFunc(cir::GlobalOp op) {
   // Create a variable initialization function.
   CIRBaseBuilderTy builder(getContext());
   builder.setInsertionPointAfter(op);
-  auto fnType = cir::FuncType::get({}, builder.getVoidTy());
+  cir::VoidType voidTy = builder.getVoidTy();
+  auto fnType = cir::FuncType::get({}, voidTy);
   FuncOp f = buildRuntimeFunction(builder, fnName, op.getLoc(), fnType,
                                   cir::GlobalLinkageKind::InternalLinkage);
 
@@ -655,8 +691,57 @@ LoweringPreparePass::buildCXXGlobalVarDeclInitFunc(cir::GlobalOp op) {
   // Register the destructor call with __cxa_atexit
   mlir::Region &dtorRegion = op.getDtorRegion();
   if (!dtorRegion.empty()) {
-    assert(!cir::MissingFeatures::opGlobalDtorLowering());
-    llvm_unreachable("dtor region lowering is NYI");
+    assert(!cir::MissingFeatures::astVarDeclInterface());
+    assert(!cir::MissingFeatures::opGlobalThreadLocal());
+    // Create a variable that binds the atexit to this shared object.
+    builder.setInsertionPointToStart(&mlirModule.getBodyRegion().front());
+    cir::GlobalOp handle = buildRuntimeVariable(
+        builder, "__dso_handle", op.getLoc(), builder.getI8Type(),
+        cir::GlobalLinkageKind::ExternalLinkage, cir::VisibilityKind::Hidden);
+
+    // Look for the destructor call in dtorBlock
+    mlir::Block &dtorBlock = dtorRegion.front();
+    cir::CallOp dtorCall;
+    for (auto op : reverse(dtorBlock.getOps<cir::CallOp>())) {
+      dtorCall = op;
+      break;
+    }
+    assert(dtorCall && "Expected a dtor call");
+    cir::FuncOp dtorFunc = getCalledFunction(dtorCall);
+    assert(dtorFunc && "Expected a dtor call");
+
+    // Create a runtime helper function:
+    //    extern "C" int __cxa_atexit(void (*f)(void *), void *p, void *d);
+    auto voidPtrTy = cir::PointerType::get(voidTy);
+    auto voidFnTy = cir::FuncType::get({voidPtrTy}, voidTy);
+    auto voidFnPtrTy = cir::PointerType::get(voidFnTy);
+    auto handlePtrTy = cir::PointerType::get(handle.getSymType());
+    auto fnAtExitType =
+        cir::FuncType::get({voidFnPtrTy, voidPtrTy, handlePtrTy}, voidTy);
+    const char *nameAtExit = "__cxa_atexit";
+    cir::FuncOp fnAtExit =
+        buildRuntimeFunction(builder, nameAtExit, op.getLoc(), fnAtExitType);
+
+    // Replace the dtor call with a call to __cxa_atexit(&dtor, &var,
+    // &__dso_handle)
+    builder.setInsertionPointAfter(dtorCall);
+    mlir::Value args[3];
+    auto dtorPtrTy = cir::PointerType::get(dtorFunc.getFunctionType());
+    // dtorPtrTy
+    args[0] = cir::GetGlobalOp::create(builder, dtorCall.getLoc(), dtorPtrTy,
+                                       dtorFunc.getSymName());
+    args[0] = cir::CastOp::create(builder, dtorCall.getLoc(), voidFnPtrTy,
+                                  cir::CastKind::bitcast, args[0]);
+    args[1] =
+        cir::CastOp::create(builder, dtorCall.getLoc(), voidPtrTy,
+                            cir::CastKind::bitcast, dtorCall.getArgOperand(0));
+    args[2] = cir::GetGlobalOp::create(builder, handle.getLoc(), handlePtrTy,
+                                       handle.getSymName());
+    builder.createCallOp(dtorCall.getLoc(), fnAtExit, args);
+    dtorCall->erase();
+    entryBB->getOperations().splice(entryBB->end(), dtorBlock.getOperations(),
+                                    dtorBlock.begin(),
+                                    std::prev(dtorBlock.end()));
   }
 
   // Replace cir.yield with cir.return
@@ -666,11 +751,12 @@ LoweringPreparePass::buildCXXGlobalVarDeclInitFunc(cir::GlobalOp op) {
     mlir::Block &block = op.getCtorRegion().front();
     yieldOp = &block.getOperations().back();
   } else {
-    assert(!cir::MissingFeatures::opGlobalDtorLowering());
-    llvm_unreachable("dtor region lowering is NYI");
+    assert(!dtorRegion.empty());
+    mlir::Block &block = dtorRegion.front();
+    yieldOp = &block.getOperations().back();
   }
 
-  assert(isa<YieldOp>(*yieldOp));
+  assert(isa<cir::YieldOp>(*yieldOp));
   cir::ReturnOp::create(builder, yieldOp->getLoc());
   return f;
 }
@@ -715,7 +801,10 @@ void LoweringPreparePass::buildGlobalCtorDtorList() {
                         mlir::ArrayAttr::get(&getContext(), globalCtors));
   }
 
-  assert(!cir::MissingFeatures::opGlobalDtorLowering());
+  // We will eventual need to populate a global_dtor list, but that's not
+  // needed for globals with destructors. It will only be needed for functions
+  // that are marked as global destructors with an attribute.
+  assert(!cir::MissingFeatures::opGlobalDtorList());
 }
 
 void LoweringPreparePass::buildCXXGlobalInitFunc() {
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index a80a2959deb8d..a1ecfc7a70909 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -1771,9 +1771,13 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite(
   }
 
   // Rewrite op.
-  rewriter.replaceOpWithNewOp<mlir::LLVM::GlobalOp>(
+  auto newOp = rewriter.replaceOpWithNewOp<mlir::LLVM::GlobalOp>(
       op, llvmType, isConst, linkage, symbol, init.value_or(mlir::Attribute()),
       alignment, addrSpace, isDsoLocal, isThreadLocal, comdatAttr, attributes);
+  newOp.setVisibility_Attr(mlir::LLVM::VisibilityAttr::get(
+      getContext(), lowerCIRVisibilityToLLVMVisibility(
+                        op.getGlobalVisibilityAttr().getValue())));
+
   return mlir::success();
 }
 
@@ -2594,6 +2598,7 @@ void ConvertCIRToLLVMPass::runOnOperation() {
                       return std::make_pair(ctorAttr.getName(),
                                             ctorAttr.getPriority());
                     });
+  assert(!cir::MissingFeatures::opGlobalDtorList());
 }
 
 mlir::LogicalResult CIRToLLVMBrOpLowering::matchAndRewrite(
diff --git a/clang/test/CIR/CodeGen/global-init.cpp b/clang/test/CIR/CodeGen/global-init.cpp
index 2afb5a5af01a0..0aab69536241a 100644
--- a/clang/test/CIR/CodeGen/global-init.cpp
+++ b/clang/test/CIR/CodeGen/global-init.cpp
@@ -6,6 +6,23 @@
 // RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
 // RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
 
+// Declarations that appear before global-specific definitions
+
+// CIR: module @{{.*}} attributes {
+// CIR-SAME: cir.global_ctors = [#cir.global_ctor<"_GLOBAL__sub_I_[[FILENAME:.*]]", 65535>]
+
+// LLVM: @__dso_handle = external hidden global i8
+// LLVM: @needsCtor = global %struct.NeedsCtor zeroinitializer, align 1
+// LLVM: @needsDtor = global %struct.NeedsDtor zeroinitializer, align 1
+// LLVM: @needsCtorDtor = global %struct.NeedsCtorDtor zeroinitializer, align 1
+// LLVM: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @_GLOBAL__sub_I_[[FILENAME:.*]], ptr null }]
+
+// OGCG: @needsCtor = global %struct.NeedsCtor zeroinitializer, align 1
+// OGCG: @needsDtor = global %struct.NeedsDtor zeroinitializer, align 1
+// OGCG: @__dso_handle = external hidden global i8
+// OGCG: @needsCtorDtor = global %struct.NeedsCtorDtor zeroinitializer, align 1
+// OGCG: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @_GLOBAL__sub_I_[[FILENAME:.*]], ptr null }]
+
 struct NeedsCtor {
   NeedsCtor();
 };
@@ -16,34 +33,89 @@ NeedsCtor needsCtor;
 // CIR-BEFORE-LPP:   %[[THIS:.*]] = cir.get_global @needsCtor : !cir.ptr<!rec_NeedsCtor>
 // CIR-BEFORE-LPP:   cir.call @_ZN9NeedsCtorC1Ev(%[[THIS]]) : (!cir.ptr<!rec_NeedsCtor>) -> ()
 
-// CIR: module @{{.*}} attributes {
-// CIR-SAME: cir.global_ctors = [#cir.global_ctor<"_GLOBAL__sub_I_[[FILENAME:.*]]", 65535>]
-
 // CIR: cir.global external @needsCtor = #cir.zero : !rec_NeedsCtor
 // CIR: cir.func internal private @__cxx_global_var_init() {
 // CIR:   %0 = cir.get_global @needsCtor : !cir.ptr<!rec_NeedsCtor>
 // CIR:   cir.call @_ZN9NeedsCtorC1Ev(%0) : (!cir.ptr<!rec_NeedsCtor>) -> ()
 
-// CIR: cir.func private @_GLOBAL__sub_I_[[FILENAME:.*]]() {
-// CIR:   cir.call @__cxx_global_var_init() : () -> ()
-// CIR:   cir.return
-// CIR: }
-
-// LLVM: @needsCtor = global %struct.NeedsCtor zeroinitializer, align 1
-// LLVM: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @_GLOBAL__sub_I_[[FILENAME:.*]], ptr null }]
-// LLVM: declare void @_ZN9NeedsCtorC1Ev(ptr)
-
 // LLVM: define internal void @__cxx_global_var_init()
 // LLVM:   call void @_ZN9NeedsCtorC1Ev(ptr @needsCtor)
 
-// LLVM: define void @_GLOBAL__sub_I_[[FILENAME]]()
-// LLVM:   call void @__cxx_global_var_init()
-
-// OGCG: @needsCtor = global %struct.NeedsCtor zeroinitializer, align 1
-// OGCG: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @_GLOBAL__sub_I_[[FILENAME:.*]], ptr null }]
-
 // OGCG: define internal void @__cxx_global_var_init() {{.*}} section ".text.startup" {
 // OGCG:   call void @_ZN9NeedsCtorC1Ev(ptr noundef nonnull align 1 dereferenceable(1) @needsCtor)
 
+
+struct NeedsDtor {
+  ~NeedsDtor();
+};
+
+NeedsDtor needsDtor;
+
+// CIR-BEFORE-LPP: cir.global external @needsDtor = #cir.zero : !rec_NeedsDtor dtor {
+// CIR-BEFORE-LPP:   %[[THIS:.*]] = cir.get_global @needsDtor : !cir.ptr<!rec_NeedsDtor>
+// CIR-BEFORE-LPP:   cir.call @_ZN9NeedsDtorD1Ev(%[[THIS]]) : (!cir.ptr<!rec_NeedsDtor>) -> ()
+
+// CIR: cir.global external @needsDtor = #cir.zero : !rec_NeedsDtor
+// CIR: cir.func internal private @__cxx_global_var_init.1() {
+// CIR:   %[[OBJ:.*]] = c...
[truncated]

``````````

</details>


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


More information about the cfe-commits mailing list