Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down
139 changes: 139 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,89 @@

#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,
cir::GlobalOp globalOp) {
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!");

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(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);
break;
case cir::TEK_Complex:
cgf.cgm.errorNYI(varDecl->getSourceRange(), "complex global initializer");
break;
case cir::TEK_Aggregate:
assert(!cir::MissingFeatures::aggValueSlotGC());
cgf.emitAggExpr(init,
AggValueSlot::forLValue(lv, AggValueSlot::IsDestructed,
AggValueSlot::IsNotAliased,
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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.

AggValueSlot::DoesNotOverlap));
break;
}

// Finish the ctor region.
builder.setInsertionPointToEnd(block);
cir::YieldOp::create(builder, globalOp.getLoc());
}

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
// 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);
Expand All @@ -38,3 +117,63 @@ 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 (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.
//
// 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)
emitDeclInit(cgf, varDecl, addr);

if (varDecl->getType().isConstantStorage(getASTContext(), true, !needsDtor))
errorNYI(varDecl->getSourceRange(), "global with constant storage");
else
emitDeclDestroy(cgf, varDecl, addr);
return;
}

errorNYI(varDecl->getSourceRange(), "global with reference type");
}
28 changes: 28 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp
Original file line number Diff line number Diff line change
@@ -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);
}
4 changes: 3 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {};
}

Expand Down
10 changes: 7 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()));
}
Expand Down Expand Up @@ -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,
Expand Down
12 changes: 9 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand Down
19 changes: 14 additions & 5 deletions clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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);

Expand Down Expand Up @@ -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");
}
Expand All @@ -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());
}
}

Expand Down Expand Up @@ -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);
Expand All @@ -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,
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CIR/CodeGen/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ add_clang_library(clangCIR
CIRGenCXXABI.cpp
CIRGenBuiltin.cpp
CIRGenDecl.cpp
CIRGenDeclCXX.cpp
CIRGenDeclOpenACC.cpp
CIRGenException.cpp
CIRGenExpr.cpp
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
17 changes: 17 additions & 0 deletions clang/test/CIR/CodeGen/global-init.cpp
Original file line number Diff line number Diff line change
@@ -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 from 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: }