-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[CIR] Upstream support for generating global ctor regions #161298
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
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.
@llvm/pr-subscribers-clang Author: Andy Kaylor (andykaylor) ChangesThis 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. Full diff: https://github.com/llvm/llvm-project/pull/161298.diff 11 Files Affected:
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: }
|
@llvm/pr-subscribers-clangir Author: Andy Kaylor (andykaylor) ChangesThis 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. Full diff: https://github.com/llvm/llvm-project/pull/161298.diff 11 Files Affected:
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: }
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, Just one nit
clang/lib/CIR/CodeGen/CIRGenCXX.cpp
Outdated
getASTContext().getDeclAlign(varDecl)); | ||
emitDeclInit(cgf, varDecl, declAddr); | ||
builder.setInsertionPointToEnd(block); | ||
builder.create<cir::YieldOp>(addr->getLoc()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
builder.create<cir::YieldOp>(addr->getLoc()); | |
cir::YieldOp::create(builder, addr->getLoc()); |
clang/lib/CIR/CodeGen/CIRGenCXX.cpp
Outdated
// Don't confuse lexical cleanup. | ||
builder.clearInsertionPoint(); | ||
} else { | ||
builder.create<cir::YieldOp>(addr->getLoc()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
builder.create<cir::YieldOp>(addr->getLoc()); | |
cir::YieldOp::create(builder, addr->getLoc()); |
assert(!cir::MissingFeatures::aggValueSlotGC()); | ||
cgf.emitAggExpr(init, | ||
AggValueSlot::forLValue(lv, AggValueSlot::IsDestructed, | ||
AggValueSlot::IsNotAliased, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OGCG takes here AggValueSlot::DoesNotNeedGCBarrier
as well.
Any reason it is missing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We haven't upstreamed any of the support for GC yet. That's what the aggValueSlotGC
assertion above is tracking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah sorry, I overlooked the assertion.
clang/lib/CIR/CodeGen/CIRGenCXX.cpp
Outdated
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)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason this is in performInit
? OGCG declares this before if (!ty->isReferenceType())
and it is used in other branches too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As you noted below, we need this to be inside the correct region because getAddrOfGlobalVar
emits an operation.
clang/lib/CIR/CodeGen/CIRGenCXX.cpp
Outdated
} else { | ||
// If not constant storage we'll emit this regardless of NeedsDtor value. | ||
mlir::OpBuilder::InsertionGuard guard(builder); | ||
auto *block = builder.createBlock(&addr.getDtorRegion()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I see now that it uses different region for insertion.
Maybe I would suggest to set insertion inside emitDeclDestroy
and emitDeclInit
as it semantically needs to always set the correct insertion point for the global?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think that makes sense. Near as I can tell, if we don't emit any dtor
code we'll be creating an entry block and then erasing it, and I guess our canonicalization pass erases the region. It would be better to just not create it in the first place, and as you say, having the insertion point set where it's used will be better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm % modulo nits
assert(!cir::MissingFeatures::aggValueSlotGC()); | ||
cgf.emitAggExpr(init, | ||
AggValueSlot::forLValue(lv, AggValueSlot::IsDestructed, | ||
AggValueSlot::IsNotAliased, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah sorry, I overlooked the assertion.
// 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Note: The CIR generated fro this test isn't ready for lowering to LLVM yet. | |
// Note: The CIR generated from this test isn't ready for lowering to LLVM yet. |
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/163/builds/27366 Here is the relevant piece of the build log for the reference
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/95/builds/18521 Here is the relevant piece of the build log for the reference
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/168/builds/16431 Here is the relevant piece of the build log for the reference
|
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.
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.