blob: 274d11b8c7629eaa41656cab86466e264d9d5a21 [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// 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 C++ code generation.
//
//===----------------------------------------------------------------------===//
#include "CIRGenCXXABI.h"
#include "CIRGenFunction.h"
#include "CIRGenModule.h"
#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,
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;
}
// If not constant storage we'll emit this regardless of NeedsDtor value.
CIRGenBuilderTy &builder = cgf.getBuilder();
// Prepare the dtor region.
mlir::OpBuilder::InsertionGuard guard(builder);
mlir::Block *block = builder.createBlock(&addr.getDtorRegion());
CIRGenFunction::LexicalScope lexScope{cgf, addr.getLoc(),
builder.getInsertionBlock()};
lexScope.setAsGlobalInit();
builder.setInsertionPointToStart(block);
CIRGenModule &cgm = cgf.cgm;
QualType type = vd->getType();
// Special-case non-array C++ destructors, if they have the right signature.
// Under some ABIs, destructors return this instead of void, and cannot be
// passed directly to __cxa_atexit if the target does not allow this
// mismatch.
const CXXRecordDecl *record = type->getAsCXXRecordDecl();
bool canRegisterDestructor =
record && (!cgm.getCXXABI().hasThisReturn(
GlobalDecl(record->getDestructor(), Dtor_Complete)) ||
cgm.getCXXABI().canCallMismatchedFunctionType());
// If __cxa_atexit is disabled via a flag, a different helper function is
// generated elsewhere which uses atexit instead, and it takes the destructor
// directly.
cir::FuncOp fnOp;
if (record && (canRegisterDestructor || cgm.getCodeGenOpts().CXAAtExit)) {
if (vd->getTLSKind())
cgm.errorNYI(vd->getSourceRange(), "TLS destructor");
assert(!record->hasTrivialDestructor());
assert(!cir::MissingFeatures::openCL());
CXXDestructorDecl *dtor = record->getDestructor();
// In LLVM OG codegen this is done in registerGlobalDtor, but CIRGen
// relies on LoweringPrepare for further decoupling, so build the
// call right here.
auto gd = GlobalDecl(dtor, Dtor_Complete);
fnOp = cgm.getAddrAndTypeOfCXXStructor(gd).second;
cgf.getBuilder().createCallOp(
cgf.getLoc(vd->getSourceRange()),
mlir::FlatSymbolRefAttr::get(fnOp.getSymNameAttr()),
mlir::ValueRange{cgm.getAddrOfGlobalVar(vd)});
} else {
cgm.errorNYI(vd->getSourceRange(), "array destructor");
}
assert(fnOp && "expected cir.func");
cgm.getCXXABI().registerGlobalDtor(vd, fnOp, nullptr);
builder.setInsertionPointToEnd(block);
if (block->empty()) {
block->erase();
// Don't confuse lexical cleanup.
builder.clearInsertionPoint();
} else {
builder.create<cir::YieldOp>(addr.getLoc());
}
}
cir::FuncOp CIRGenModule::codegenCXXStructor(GlobalDecl gd) {
const CIRGenFunctionInfo &fnInfo =
getTypes().arrangeCXXStructorDeclaration(gd);
cir::FuncType funcType = getTypes().getFunctionType(fnInfo);
cir::FuncOp fn = getAddrOfCXXStructor(gd, &fnInfo, /*FnType=*/nullptr,
/*DontDefer=*/true, ForDefinition);
setFunctionLinkage(gd, fn);
CIRGenFunction cgf{*this, builder};
curCGF = &cgf;
{
mlir::OpBuilder::InsertionGuard guard(builder);
cgf.generateCode(gd, fn, funcType);
}
curCGF = nullptr;
setNonAliasAttributes(gd, fn);
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");
}