[clang] [CIR] Upstream support for generating global ctor regions (PR #161298)

Andy Kaylor via cfe-commits cfe-commits at lists.llvm.org
Tue Sep 30 11:41:21 PDT 2025


https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/161298

>From 75f621dfedf61632bd4be3d1d00c0bc42f3dd1a0 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Thu, 25 Sep 2025 13:21:30 -0700
Subject: [PATCH 1/2] [CIR] Upstream support for generating global ctor regions

This adds support for handling global variables with non-trivial
constructors. The constructor call is emitted in CIR as a 'ctor' region
associated with the global definition. This form of global definition
cannot be lowered to LLVM IR yet.

A later change will add support in LoweringPrepare to move the ctor code
into a __cxx_global_var_init() function and add that function to the list
of global global ctors, but for now we must stop at the initial CIR
generation.
---
 clang/include/clang/CIR/MissingFeatures.h     |   1 -
 clang/lib/CIR/CodeGen/CIRGenCXX.cpp           | 152 ++++++++++++++++++
 clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp       |  28 ++++
 clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp  |   4 +-
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp      |  10 +-
 clang/lib/CIR/CodeGen/CIRGenFunction.h        |  12 +-
 clang/lib/CIR/CodeGen/CIRGenModule.cpp        |  19 ++-
 clang/lib/CIR/CodeGen/CIRGenModule.h          |   7 +
 clang/lib/CIR/CodeGen/CMakeLists.txt          |   1 +
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp |   5 +
 clang/test/CIR/CodeGen/global-init.cpp        |  17 ++
 11 files changed, 243 insertions(+), 13 deletions(-)
 create mode 100644 clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp
 create mode 100644 clang/test/CIR/CodeGen/global-init.cpp

diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 7e59989dc09f1..3e25101de40c6 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -246,7 +246,6 @@ struct MissingFeatures {
   static bool metaDataNode() { return false; }
   static bool moduleNameHash() { return false; }
   static bool msabi() { return false; }
-  static bool needsGlobalCtorDtor() { return false; }
   static bool nrvo() { return false; }
   static bool objCBlocks() { return false; }
   static bool objCGC() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
index da507d6f28335..4ade81a68990f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
@@ -15,10 +15,72 @@
 
 #include "clang/AST/GlobalDecl.h"
 #include "clang/CIR/MissingFeatures.h"
+#include "llvm/Support/SaveAndRestore.h"
 
 using namespace clang;
 using namespace clang::CIRGen;
 
+static void emitDeclInit(CIRGenFunction &cgf, const VarDecl *varDecl,
+                         Address declPtr) {
+  assert((varDecl->hasGlobalStorage() ||
+          (varDecl->hasLocalStorage() &&
+           cgf.getContext().getLangOpts().OpenCLCPlusPlus)) &&
+         "VarDecl must have global or local (in the case of OpenCL) storage!");
+  assert(!varDecl->getType()->isReferenceType() &&
+         "Should not call emitDeclInit on a reference!");
+
+  QualType type = varDecl->getType();
+  LValue lv = cgf.makeAddrLValue(declPtr, type);
+
+  const Expr *init = varDecl->getInit();
+  switch (CIRGenFunction::getEvaluationKind(type)) {
+  case cir::TEK_Scalar:
+    assert(!cir::MissingFeatures::objCGC());
+    cgf.emitScalarInit(init, cgf.getLoc(varDecl->getLocation()), lv, false);
+    return;
+  case cir::TEK_Complex:
+    cgf.cgm.errorNYI(varDecl->getSourceRange(), "complex global initializer");
+    return;
+  case cir::TEK_Aggregate:
+    assert(!cir::MissingFeatures::aggValueSlotGC());
+    cgf.emitAggExpr(init,
+                    AggValueSlot::forLValue(lv, AggValueSlot::IsDestructed,
+                                            AggValueSlot::IsNotAliased,
+                                            AggValueSlot::DoesNotOverlap));
+    return;
+  }
+  llvm_unreachable("bad evaluation kind");
+}
+
+static void emitDeclDestroy(CIRGenFunction &cgf, const VarDecl *vd) {
+  // Honor __attribute__((no_destroy)) and bail instead of attempting
+  // to emit a reference to a possibly nonexistent destructor, which
+  // in turn can cause a crash. This will result in a global constructor
+  // that isn't balanced out by a destructor call as intended by the
+  // attribute. This also checks for -fno-c++-static-destructors and
+  // bails even if the attribute is not present.
+  QualType::DestructionKind dtorKind = vd->needsDestruction(cgf.getContext());
+
+  // FIXME:  __attribute__((cleanup)) ?
+
+  switch (dtorKind) {
+  case QualType::DK_none:
+    return;
+
+  case QualType::DK_cxx_destructor:
+    break;
+
+  case QualType::DK_objc_strong_lifetime:
+  case QualType::DK_objc_weak_lifetime:
+  case QualType::DK_nontrivial_c_struct:
+    // We don't care about releasing objects during process teardown.
+    assert(!vd->getTLSKind() && "should have rejected this");
+    return;
+  }
+
+  cgf.cgm.errorNYI(vd->getSourceRange(), "global with destructor");
+}
+
 cir::FuncOp CIRGenModule::codegenCXXStructor(GlobalDecl gd) {
   const CIRGenFunctionInfo &fnInfo =
       getTypes().arrangeCXXStructorDeclaration(gd);
@@ -38,3 +100,93 @@ cir::FuncOp CIRGenModule::codegenCXXStructor(GlobalDecl gd) {
   assert(!cir::MissingFeatures::opFuncAttributesForDefinition());
   return fn;
 }
+
+// Global variables requiring non-trivial initialization are handled
+// differently in CIR than in classic codegen. Classic codegen emits
+// a global init function (__cxx_global_var_init) and inserts
+// initialization for each global there. In CIR, we attach a ctor
+// region to the global variable and insert the initialization code
+// into the ctor region. This will be moved into the
+// __cxx_global_var_init function during the LoweringPrepare pass.
+void CIRGenModule::emitCXXGlobalVarDeclInit(const VarDecl *varDecl,
+                                            cir::GlobalOp addr,
+                                            bool performInit) {
+  QualType ty = varDecl->getType();
+
+  // TODO: handle address space
+  // The address space of a static local variable (DeclPtr) may be different
+  // from the address space of the "this" argument of the constructor. In that
+  // case, we need an addrspacecast before calling the constructor.
+  //
+  // struct StructWithCtor {
+  //   __device__ StructWithCtor() {...}
+  // };
+  // __device__ void foo() {
+  //   __shared__ StructWithCtor s;
+  //   ...
+  // }
+  //
+  // For example, in the above CUDA code, the static local variable s has a
+  // "shared" address space qualifier, but the constructor of StructWithCtor
+  // expects "this" in the "generic" address space.
+  assert(!cir::MissingFeatures::addressSpace());
+
+  // Create a CIRGenFunction to emit the initializer. While this isn't a true
+  // function, the handling works the same way.
+  CIRGenFunction cgf{*this, builder, true};
+  llvm::SaveAndRestore<CIRGenFunction *> savedCGF(curCGF, &cgf);
+  curCGF->curFn = addr;
+
+  CIRGenFunction::SourceLocRAIIObject fnLoc{cgf,
+                                            getLoc(varDecl->getLocation())};
+
+  assert(!cir::MissingFeatures::astVarDeclInterface());
+
+  if (!ty->isReferenceType()) {
+    assert(!cir::MissingFeatures::openMP());
+
+    bool needsDtor = varDecl->needsDestruction(getASTContext()) ==
+                     QualType::DK_cxx_destructor;
+    // PerformInit, constant store invariant / destroy handled below.
+    if (performInit) {
+      mlir::OpBuilder::InsertionGuard guard(builder);
+      auto *block = builder.createBlock(&addr.getCtorRegion());
+      CIRGenFunction::LexicalScope lexScope{*curCGF, addr.getLoc(),
+                                            builder.getInsertionBlock()};
+      lexScope.setAsGlobalInit();
+
+      builder.setInsertionPointToStart(block);
+      Address declAddr(getAddrOfGlobalVar(varDecl),
+                       getASTContext().getDeclAlign(varDecl));
+      emitDeclInit(cgf, varDecl, declAddr);
+      builder.setInsertionPointToEnd(block);
+      builder.create<cir::YieldOp>(addr->getLoc());
+    }
+
+    if (varDecl->getType().isConstantStorage(getASTContext(), true,
+                                             !needsDtor)) {
+      errorNYI(varDecl->getSourceRange(), "global with constant storage");
+    } else {
+      // If not constant storage we'll emit this regardless of NeedsDtor value.
+      mlir::OpBuilder::InsertionGuard guard(builder);
+      auto *block = builder.createBlock(&addr.getDtorRegion());
+      CIRGenFunction::LexicalScope lexScope{*curCGF, addr.getLoc(),
+                                            builder.getInsertionBlock()};
+      lexScope.setAsGlobalInit();
+
+      builder.setInsertionPointToStart(block);
+      emitDeclDestroy(cgf, varDecl);
+      builder.setInsertionPointToEnd(block);
+      if (block->empty()) {
+        block->erase();
+        // Don't confuse lexical cleanup.
+        builder.clearInsertionPoint();
+      } else {
+        builder.create<cir::YieldOp>(addr->getLoc());
+      }
+    }
+    return;
+  }
+
+  errorNYI(varDecl->getSourceRange(), "global with reference type");
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp
new file mode 100644
index 0000000000000..d1efed80aaf0e
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This contains code dealing with code generation of C++ declarations
+//
+//===----------------------------------------------------------------------===//
+
+#include "CIRGenModule.h"
+#include "clang/AST/Attr.h"
+#include "clang/Basic/LangOptions.h"
+
+using namespace clang;
+using namespace clang::CIRGen;
+
+void CIRGenModule::emitCXXGlobalVarDeclInitFunc(const VarDecl *vd,
+                                                cir::GlobalOp addr,
+                                                bool performInit) {
+  assert(!cir::MissingFeatures::cudaSupport());
+
+  assert(!cir::MissingFeatures::deferredCXXGlobalInit());
+
+  emitCXXGlobalVarDeclInit(vd, addr, performInit);
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
index 178b276f19d41..e20a4fc3c63aa 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
@@ -775,7 +775,9 @@ class ConstExprEmitter
   }
 
   mlir::Attribute VisitCXXConstructExpr(CXXConstructExpr *e, QualType ty) {
-    cgm.errorNYI(e->getBeginLoc(), "ConstExprEmitter::VisitCXXConstructExpr");
+    if (!e->getConstructor()->isTrivial())
+      return nullptr;
+    cgm.errorNYI(e->getBeginLoc(), "trivial constructor const handling");
     return {};
   }
 
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 0abb21a670719..e68ce99dbdc74 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -342,10 +342,12 @@ void CIRGenFunction::LexicalScope::cleanup() {
 cir::ReturnOp CIRGenFunction::LexicalScope::emitReturn(mlir::Location loc) {
   CIRGenBuilderTy &builder = cgf.getBuilder();
 
-  if (!cgf.curFn.getFunctionType().hasVoidReturn()) {
+  auto fn = dyn_cast<cir::FuncOp>(cgf.curFn);
+  assert(fn && "emitReturn from non-function");
+  if (!fn.getFunctionType().hasVoidReturn()) {
     // Load the value from `__retval` and return it via the `cir.return` op.
     auto value = builder.create<cir::LoadOp>(
-        loc, cgf.curFn.getFunctionType().getReturnType(), *cgf.fnRetAlloca);
+        loc, fn.getFunctionType().getReturnType(), *cgf.fnRetAlloca);
     return builder.create<cir::ReturnOp>(loc,
                                          llvm::ArrayRef(value.getResult()));
   }
@@ -459,7 +461,9 @@ void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType,
     const auto *md = cast<CXXMethodDecl>(d);
     if (md->getParent()->isLambda() && md->getOverloadedOperator() == OO_Call) {
       // We're in a lambda.
-      curFn.setLambda(true);
+      auto fn = dyn_cast<cir::FuncOp>(curFn);
+      assert(fn && "lambda in non-function region");
+      fn.setLambda(true);
 
       // Figure out the captures.
       md->getParent()->getCaptureFields(lambdaCaptureFields,
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index ef07db3d48ffc..c0ed8b4006ec5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -98,8 +98,10 @@ class CIRGenFunction : public CIRGenTypeCache {
   /// This is the inner-most code context, which includes blocks.
   const clang::Decl *curCodeDecl = nullptr;
 
-  /// The function for which code is currently being generated.
-  cir::FuncOp curFn;
+  /// The current function or global initializer that is generated code for.
+  /// This is usually a cir::FuncOp, but it can also be a cir::GlobalOp for
+  /// global initializers.
+  mlir::Operation *curFn = nullptr;
 
   using DeclMapTy = llvm::DenseMap<const clang::Decl *, Address>;
   /// This keeps track of the CIR allocas or globals for local C
@@ -116,7 +118,11 @@ class CIRGenFunction : public CIRGenTypeCache {
   CIRGenModule &getCIRGenModule() { return cgm; }
   const CIRGenModule &getCIRGenModule() const { return cgm; }
 
-  mlir::Block *getCurFunctionEntryBlock() { return &curFn.getRegion().front(); }
+  mlir::Block *getCurFunctionEntryBlock() {
+    // We currently assume this isn't called for a global initializer.
+    auto fn = mlir::cast<cir::FuncOp>(curFn);
+    return &fn.getRegion().front();
+  }
 
   /// Sanitizers enabled for this function.
   clang::SanitizerSet sanOpts;
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index c977ff9f06de6..5dc4335aeb6ad 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -730,7 +730,6 @@ void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
   // since this is the job for its original source.
   bool isDefinitionAvailableExternally =
       astContext.GetGVALinkageForVariable(vd) == GVA_AvailableExternally;
-  assert(!cir::MissingFeatures::needsGlobalCtorDtor());
 
   // It is useless to emit the definition for an available_externally variable
   // which can't be marked as const.
@@ -743,6 +742,10 @@ void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
     return;
 
   mlir::Attribute init;
+  bool needsGlobalCtor = false;
+  bool needsGlobalDtor =
+      !isDefinitionAvailableExternally &&
+      vd->needsDestruction(astContext) == QualType::DK_cxx_destructor;
   const VarDecl *initDecl;
   const Expr *initExpr = vd->getAnyInitializer(initDecl);
 
@@ -777,8 +780,8 @@ void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
         if (initDecl->hasFlexibleArrayInit(astContext))
           errorNYI(vd->getSourceRange(), "flexible array initializer");
         init = builder.getZeroInitAttr(convertType(qt));
-        if (astContext.GetGVALinkageForVariable(vd) != GVA_AvailableExternally)
-          errorNYI(vd->getSourceRange(), "global constructor");
+        if (!isDefinitionAvailableExternally)
+          needsGlobalCtor = true;
       } else {
         errorNYI(vd->getSourceRange(), "static initializer");
       }
@@ -787,8 +790,7 @@ void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
       // We don't need an initializer, so remove the entry for the delayed
       // initializer position (just in case this entry was delayed) if we
       // also don't need to register a destructor.
-      if (vd->needsDestruction(astContext) == QualType::DK_cxx_destructor)
-        errorNYI(vd->getSourceRange(), "delayed destructor");
+      assert(!cir::MissingFeatures::deferredCXXGlobalInit());
     }
   }
 
@@ -827,6 +829,9 @@ void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
   if (emitter)
     emitter->finalize(gv);
 
+  assert(!cir::MissingFeatures::opGlobalConstant());
+  assert(!cir::MissingFeatures::opGlobalSection());
+
   // Set CIR's linkage type as appropriate.
   cir::GlobalLinkageKind linkage =
       getCIRLinkageVarDefinition(vd, /*IsConstant=*/false);
@@ -844,6 +849,10 @@ void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
   assert(!cir::MissingFeatures::opGlobalThreadLocal());
 
   maybeSetTrivialComdat(*vd, gv);
+
+  // Emit the initializer function if necessary.
+  if (needsGlobalCtor || needsGlobalDtor)
+    emitCXXGlobalVarDeclInitFunc(vd, gv, needsGlobalCtor);
 }
 
 void CIRGenModule::emitGlobalDefinition(clang::GlobalDecl gd,
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 073e8d96b773b..7630daabfb3a4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -408,6 +408,13 @@ class CIRGenModule : public CIRGenTypeCache {
   void emitGlobalVarDefinition(const clang::VarDecl *vd,
                                bool isTentative = false);
 
+  /// Emit the function that initializes the specified global
+  void emitCXXGlobalVarDeclInit(const VarDecl *varDecl, cir::GlobalOp addr,
+                                bool performInit);
+
+  void emitCXXGlobalVarDeclInitFunc(const VarDecl *vd, cir::GlobalOp addr,
+                                    bool performInit);
+
   void emitGlobalOpenACCDecl(const clang::OpenACCConstructDecl *cd);
 
   // C++ related functions.
diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt
index c1f27ec8ba858..3ebf460f7d34c 100644
--- a/clang/lib/CIR/CodeGen/CMakeLists.txt
+++ b/clang/lib/CIR/CodeGen/CMakeLists.txt
@@ -18,6 +18,7 @@ add_clang_library(clangCIR
   CIRGenCXXABI.cpp
   CIRGenBuiltin.cpp
   CIRGenDecl.cpp
+  CIRGenDeclCXX.cpp
   CIRGenDeclOpenACC.cpp
   CIRGenException.cpp
   CIRGenExpr.cpp
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 876948d53010b..1edec057e6307 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -1710,6 +1710,11 @@ CIRToLLVMGlobalOpLowering::matchAndRewriteRegionInitializedGlobal(
 mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite(
     cir::GlobalOp op, OpAdaptor adaptor,
     mlir::ConversionPatternRewriter &rewriter) const {
+  // If this global requires non-trivial initialization or destruction,
+  // that needs to be moved to runtime handlers during LoweringPrepare.
+  if (!op.getCtorRegion().empty() || !op.getDtorRegion().empty())
+    return op.emitError() << "GlobalOp ctor and dtor regions should be removed "
+                             "in LoweringPrepare";
 
   std::optional<mlir::Attribute> init = op.getInitialValue();
 
diff --git a/clang/test/CIR/CodeGen/global-init.cpp b/clang/test/CIR/CodeGen/global-init.cpp
new file mode 100644
index 0000000000000..98d0320a9e4f5
--- /dev/null
+++ b/clang/test/CIR/CodeGen/global-init.cpp
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
+
+// Note: The CIR generated fro this test isn't ready for lowering to LLVM yet.
+//       That will require changes to LoweringPrepare.
+
+struct NeedsCtor {
+  NeedsCtor();
+};
+
+NeedsCtor needsCtor;
+
+// CIR: cir.func private @_ZN9NeedsCtorC1Ev(!cir.ptr<!rec_NeedsCtor>)
+// CIR: cir.global external @needsCtor = ctor : !rec_NeedsCtor {
+// CIR:   %[[THIS:.*]] = cir.get_global @needsCtor : !cir.ptr<!rec_NeedsCtor>
+// CIR:   cir.call @_ZN9NeedsCtorC1Ev(%[[THIS]]) : (!cir.ptr<!rec_NeedsCtor>) -> ()
+// CIR: }

>From fb77e5a81e823e10eb61cfaa535256505a60a345 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Tue, 30 Sep 2025 11:40:05 -0700
Subject: [PATCH 2/2] Move region and insert point handling to point of use

---
 clang/lib/CIR/CodeGen/CIRGenCXX.cpp | 75 ++++++++++++-----------------
 1 file changed, 31 insertions(+), 44 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
index 4ade81a68990f..d5b35c25c83ba 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
@@ -21,7 +21,7 @@ using namespace clang;
 using namespace clang::CIRGen;
 
 static void emitDeclInit(CIRGenFunction &cgf, const VarDecl *varDecl,
-                         Address declPtr) {
+                         cir::GlobalOp globalOp) {
   assert((varDecl->hasGlobalStorage() ||
           (varDecl->hasLocalStorage() &&
            cgf.getContext().getLangOpts().OpenCLCPlusPlus)) &&
@@ -29,30 +29,47 @@ static void emitDeclInit(CIRGenFunction &cgf, const VarDecl *varDecl,
   assert(!varDecl->getType()->isReferenceType() &&
          "Should not call emitDeclInit on a reference!");
 
+  CIRGenBuilderTy &builder = cgf.getBuilder();
+
+  // Set up the ctor region.
+  mlir::OpBuilder::InsertionGuard guard(builder);
+  mlir::Block *block = builder.createBlock(&globalOp.getCtorRegion());
+  CIRGenFunction::LexicalScope lexScope{cgf, globalOp.getLoc(),
+                                        builder.getInsertionBlock()};
+  lexScope.setAsGlobalInit();
+  builder.setInsertionPointToStart(block);
+
+  Address declAddr(cgf.cgm.getAddrOfGlobalVar(varDecl),
+                   cgf.cgm.getASTContext().getDeclAlign(varDecl));
+
   QualType type = varDecl->getType();
-  LValue lv = cgf.makeAddrLValue(declPtr, type);
+  LValue lv = cgf.makeAddrLValue(declAddr, type);
 
   const Expr *init = varDecl->getInit();
   switch (CIRGenFunction::getEvaluationKind(type)) {
   case cir::TEK_Scalar:
     assert(!cir::MissingFeatures::objCGC());
     cgf.emitScalarInit(init, cgf.getLoc(varDecl->getLocation()), lv, false);
-    return;
+    break;
   case cir::TEK_Complex:
     cgf.cgm.errorNYI(varDecl->getSourceRange(), "complex global initializer");
-    return;
+    break;
   case cir::TEK_Aggregate:
     assert(!cir::MissingFeatures::aggValueSlotGC());
     cgf.emitAggExpr(init,
                     AggValueSlot::forLValue(lv, AggValueSlot::IsDestructed,
                                             AggValueSlot::IsNotAliased,
                                             AggValueSlot::DoesNotOverlap));
-    return;
+    break;
   }
-  llvm_unreachable("bad evaluation kind");
+
+  // Finish the ctor region.
+  builder.setInsertionPointToEnd(block);
+  cir::YieldOp::create(builder, globalOp.getLoc());
 }
 
-static void emitDeclDestroy(CIRGenFunction &cgf, const VarDecl *vd) {
+static void emitDeclDestroy(CIRGenFunction &cgf, const VarDecl *vd,
+                            cir::GlobalOp addr) {
   // Honor __attribute__((no_destroy)) and bail instead of attempting
   // to emit a reference to a possibly nonexistent destructor, which
   // in turn can cause a crash. This will result in a global constructor
@@ -114,7 +131,7 @@ void CIRGenModule::emitCXXGlobalVarDeclInit(const VarDecl *varDecl,
   QualType ty = varDecl->getType();
 
   // TODO: handle address space
-  // The address space of a static local variable (DeclPtr) may be different
+  // The address space of a static local variable (addr) may be different
   // from the address space of the "this" argument of the constructor. In that
   // case, we need an addrspacecast before calling the constructor.
   //
@@ -148,43 +165,13 @@ void CIRGenModule::emitCXXGlobalVarDeclInit(const VarDecl *varDecl,
     bool needsDtor = varDecl->needsDestruction(getASTContext()) ==
                      QualType::DK_cxx_destructor;
     // PerformInit, constant store invariant / destroy handled below.
-    if (performInit) {
-      mlir::OpBuilder::InsertionGuard guard(builder);
-      auto *block = builder.createBlock(&addr.getCtorRegion());
-      CIRGenFunction::LexicalScope lexScope{*curCGF, addr.getLoc(),
-                                            builder.getInsertionBlock()};
-      lexScope.setAsGlobalInit();
-
-      builder.setInsertionPointToStart(block);
-      Address declAddr(getAddrOfGlobalVar(varDecl),
-                       getASTContext().getDeclAlign(varDecl));
-      emitDeclInit(cgf, varDecl, declAddr);
-      builder.setInsertionPointToEnd(block);
-      builder.create<cir::YieldOp>(addr->getLoc());
-    }
-
-    if (varDecl->getType().isConstantStorage(getASTContext(), true,
-                                             !needsDtor)) {
+    if (performInit)
+      emitDeclInit(cgf, varDecl, addr);
+
+    if (varDecl->getType().isConstantStorage(getASTContext(), true, !needsDtor))
       errorNYI(varDecl->getSourceRange(), "global with constant storage");
-    } else {
-      // If not constant storage we'll emit this regardless of NeedsDtor value.
-      mlir::OpBuilder::InsertionGuard guard(builder);
-      auto *block = builder.createBlock(&addr.getDtorRegion());
-      CIRGenFunction::LexicalScope lexScope{*curCGF, addr.getLoc(),
-                                            builder.getInsertionBlock()};
-      lexScope.setAsGlobalInit();
-
-      builder.setInsertionPointToStart(block);
-      emitDeclDestroy(cgf, varDecl);
-      builder.setInsertionPointToEnd(block);
-      if (block->empty()) {
-        block->erase();
-        // Don't confuse lexical cleanup.
-        builder.clearInsertionPoint();
-      } else {
-        builder.create<cir::YieldOp>(addr->getLoc());
-      }
-    }
+    else
+      emitDeclDestroy(cgf, varDecl, addr);
     return;
   }
 



More information about the cfe-commits mailing list