[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