250 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This contains code dealing with C++ code generation.10//11//===----------------------------------------------------------------------===//12 13#include "CIRGenCXXABI.h"14#include "CIRGenFunction.h"15#include "CIRGenModule.h"16 17#include "clang/AST/GlobalDecl.h"18#include "clang/CIR/MissingFeatures.h"19#include "llvm/Support/SaveAndRestore.h"20 21using namespace clang;22using namespace clang::CIRGen;23 24static void emitDeclInit(CIRGenFunction &cgf, const VarDecl *varDecl,25 cir::GlobalOp globalOp) {26 assert((varDecl->hasGlobalStorage() ||27 (varDecl->hasLocalStorage() &&28 cgf.getContext().getLangOpts().OpenCLCPlusPlus)) &&29 "VarDecl must have global or local (in the case of OpenCL) storage!");30 assert(!varDecl->getType()->isReferenceType() &&31 "Should not call emitDeclInit on a reference!");32 33 CIRGenBuilderTy &builder = cgf.getBuilder();34 35 // Set up the ctor region.36 mlir::OpBuilder::InsertionGuard guard(builder);37 mlir::Block *block = builder.createBlock(&globalOp.getCtorRegion());38 CIRGenFunction::LexicalScope lexScope{cgf, globalOp.getLoc(),39 builder.getInsertionBlock()};40 lexScope.setAsGlobalInit();41 builder.setInsertionPointToStart(block);42 43 Address declAddr(cgf.cgm.getAddrOfGlobalVar(varDecl),44 cgf.cgm.getASTContext().getDeclAlign(varDecl));45 46 QualType type = varDecl->getType();47 LValue lv = cgf.makeAddrLValue(declAddr, type);48 49 const Expr *init = varDecl->getInit();50 switch (CIRGenFunction::getEvaluationKind(type)) {51 case cir::TEK_Scalar:52 assert(!cir::MissingFeatures::objCGC());53 cgf.emitScalarInit(init, cgf.getLoc(varDecl->getLocation()), lv, false);54 break;55 case cir::TEK_Complex:56 cgf.emitComplexExprIntoLValue(init, lv, /*isInit=*/true);57 break;58 case cir::TEK_Aggregate:59 assert(!cir::MissingFeatures::aggValueSlotGC());60 cgf.emitAggExpr(init,61 AggValueSlot::forLValue(lv, AggValueSlot::IsDestructed,62 AggValueSlot::IsNotAliased,63 AggValueSlot::DoesNotOverlap));64 break;65 }66 67 // Finish the ctor region.68 builder.setInsertionPointToEnd(block);69 cir::YieldOp::create(builder, globalOp.getLoc());70}71 72static void emitDeclDestroy(CIRGenFunction &cgf, const VarDecl *vd,73 cir::GlobalOp addr) {74 // Honor __attribute__((no_destroy)) and bail instead of attempting75 // to emit a reference to a possibly nonexistent destructor, which76 // in turn can cause a crash. This will result in a global constructor77 // that isn't balanced out by a destructor call as intended by the78 // attribute. This also checks for -fno-c++-static-destructors and79 // bails even if the attribute is not present.80 QualType::DestructionKind dtorKind = vd->needsDestruction(cgf.getContext());81 82 // FIXME: __attribute__((cleanup)) ?83 84 switch (dtorKind) {85 case QualType::DK_none:86 return;87 88 case QualType::DK_cxx_destructor:89 break;90 91 case QualType::DK_objc_strong_lifetime:92 case QualType::DK_objc_weak_lifetime:93 case QualType::DK_nontrivial_c_struct:94 // We don't care about releasing objects during process teardown.95 assert(!vd->getTLSKind() && "should have rejected this");96 return;97 }98 99 // If not constant storage we'll emit this regardless of NeedsDtor value.100 CIRGenBuilderTy &builder = cgf.getBuilder();101 102 // Prepare the dtor region.103 mlir::OpBuilder::InsertionGuard guard(builder);104 mlir::Block *block = builder.createBlock(&addr.getDtorRegion());105 CIRGenFunction::LexicalScope lexScope{cgf, addr.getLoc(),106 builder.getInsertionBlock()};107 lexScope.setAsGlobalInit();108 builder.setInsertionPointToStart(block);109 110 CIRGenModule &cgm = cgf.cgm;111 QualType type = vd->getType();112 113 // Special-case non-array C++ destructors, if they have the right signature.114 // Under some ABIs, destructors return this instead of void, and cannot be115 // passed directly to __cxa_atexit if the target does not allow this116 // mismatch.117 const CXXRecordDecl *record = type->getAsCXXRecordDecl();118 bool canRegisterDestructor =119 record && (!cgm.getCXXABI().hasThisReturn(120 GlobalDecl(record->getDestructor(), Dtor_Complete)) ||121 cgm.getCXXABI().canCallMismatchedFunctionType());122 123 // If __cxa_atexit is disabled via a flag, a different helper function is124 // generated elsewhere which uses atexit instead, and it takes the destructor125 // directly.126 cir::FuncOp fnOp;127 if (record && (canRegisterDestructor || cgm.getCodeGenOpts().CXAAtExit)) {128 if (vd->getTLSKind())129 cgm.errorNYI(vd->getSourceRange(), "TLS destructor");130 assert(!record->hasTrivialDestructor());131 assert(!cir::MissingFeatures::openCL());132 CXXDestructorDecl *dtor = record->getDestructor();133 // In LLVM OG codegen this is done in registerGlobalDtor, but CIRGen134 // relies on LoweringPrepare for further decoupling, so build the135 // call right here.136 auto gd = GlobalDecl(dtor, Dtor_Complete);137 fnOp = cgm.getAddrAndTypeOfCXXStructor(gd).second;138 builder.createCallOp(cgf.getLoc(vd->getSourceRange()),139 mlir::FlatSymbolRefAttr::get(fnOp.getSymNameAttr()),140 mlir::ValueRange{cgm.getAddrOfGlobalVar(vd)});141 assert(fnOp && "expected cir.func");142 // TODO(cir): This doesn't do anything but check for unhandled conditions.143 // What it is meant to do should really be happening in LoweringPrepare.144 cgm.getCXXABI().registerGlobalDtor(vd, fnOp, nullptr);145 } else {146 // Otherwise, a custom destroyed is needed. Classic codegen creates a helper147 // function here and emits the destroy into the helper function, which is148 // called from __cxa_atexit.149 // In CIR, we just emit the destroy into the dtor region. It will be moved150 // into a separate function during the LoweringPrepare pass.151 // FIXME(cir): We should create a new operation here to explicitly get the152 // address of the global into whose dtor region we are emiiting the destroy.153 // The same applies to code above where it is calling getAddrOfGlobalVar.154 mlir::Value globalVal = builder.createGetGlobal(addr);155 CharUnits alignment = cgf.getContext().getDeclAlign(vd);156 Address globalAddr{globalVal, cgf.convertTypeForMem(type), alignment};157 cgf.emitDestroy(globalAddr, type, cgf.getDestroyer(dtorKind));158 }159 160 builder.setInsertionPointToEnd(block);161 if (block->empty()) {162 block->erase();163 // Don't confuse lexical cleanup.164 builder.clearInsertionPoint();165 } else {166 cir::YieldOp::create(builder, addr.getLoc());167 }168}169 170cir::FuncOp CIRGenModule::codegenCXXStructor(GlobalDecl gd) {171 const CIRGenFunctionInfo &fnInfo =172 getTypes().arrangeCXXStructorDeclaration(gd);173 cir::FuncType funcType = getTypes().getFunctionType(fnInfo);174 cir::FuncOp fn = getAddrOfCXXStructor(gd, &fnInfo, /*FnType=*/nullptr,175 /*DontDefer=*/true, ForDefinition);176 setFunctionLinkage(gd, fn);177 CIRGenFunction cgf{*this, builder};178 curCGF = &cgf;179 {180 mlir::OpBuilder::InsertionGuard guard(builder);181 cgf.generateCode(gd, fn, funcType);182 }183 curCGF = nullptr;184 185 setNonAliasAttributes(gd, fn);186 setCIRFunctionAttributesForDefinition(mlir::cast<FunctionDecl>(gd.getDecl()),187 fn);188 return fn;189}190 191// Global variables requiring non-trivial initialization are handled192// differently in CIR than in classic codegen. Classic codegen emits193// a global init function (__cxx_global_var_init) and inserts194// initialization for each global there. In CIR, we attach a ctor195// region to the global variable and insert the initialization code196// into the ctor region. This will be moved into the197// __cxx_global_var_init function during the LoweringPrepare pass.198void CIRGenModule::emitCXXGlobalVarDeclInit(const VarDecl *varDecl,199 cir::GlobalOp addr,200 bool performInit) {201 QualType ty = varDecl->getType();202 203 // TODO: handle address space204 // The address space of a static local variable (addr) may be different205 // from the address space of the "this" argument of the constructor. In that206 // case, we need an addrspacecast before calling the constructor.207 //208 // struct StructWithCtor {209 // __device__ StructWithCtor() {...}210 // };211 // __device__ void foo() {212 // __shared__ StructWithCtor s;213 // ...214 // }215 //216 // For example, in the above CUDA code, the static local variable s has a217 // "shared" address space qualifier, but the constructor of StructWithCtor218 // expects "this" in the "generic" address space.219 assert(!cir::MissingFeatures::addressSpace());220 221 // Create a CIRGenFunction to emit the initializer. While this isn't a true222 // function, the handling works the same way.223 CIRGenFunction cgf{*this, builder, true};224 llvm::SaveAndRestore<CIRGenFunction *> savedCGF(curCGF, &cgf);225 curCGF->curFn = addr;226 227 CIRGenFunction::SourceLocRAIIObject fnLoc{cgf,228 getLoc(varDecl->getLocation())};229 230 assert(!cir::MissingFeatures::astVarDeclInterface());231 232 if (!ty->isReferenceType()) {233 assert(!cir::MissingFeatures::openMP());234 235 bool needsDtor = varDecl->needsDestruction(getASTContext()) ==236 QualType::DK_cxx_destructor;237 // PerformInit, constant store invariant / destroy handled below.238 if (performInit)239 emitDeclInit(cgf, varDecl, addr);240 241 if (varDecl->getType().isConstantStorage(getASTContext(), true, !needsDtor))242 errorNYI(varDecl->getSourceRange(), "global with constant storage");243 else244 emitDeclDestroy(cgf, varDecl, addr);245 return;246 }247 248 errorNYI(varDecl->getSourceRange(), "global with reference type");249}250