clang 22.0.0git
CIRGenCXX.cpp
Go to the documentation of this file.
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-exception
6//
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
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
45
46 QualType type = varDecl->getType();
47 LValue lv = cgf.makeAddrLValue(declAddr, type);
48
49 const Expr *init = varDecl->getInit();
51 case cir::TEK_Scalar:
53 cgf.emitScalarInit(init, cgf.getLoc(varDecl->getLocation()), lv, false);
54 break;
56 cgf.emitComplexExprIntoLValue(init, lv, /*isInit=*/true);
57 break;
60 cgf.emitAggExpr(init,
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 attempting
75 // to emit a reference to a possibly nonexistent destructor, which
76 // in turn can cause a crash. This will result in a global constructor
77 // that isn't balanced out by a destructor call as intended by the
78 // attribute. This also checks for -fno-c++-static-destructors and
79 // bails even if the attribute is not present.
81
82 // FIXME: __attribute__((cleanup)) ?
83
84 switch (dtorKind) {
86 return;
87
89 break;
90
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 be
115 // passed directly to __cxa_atexit if the target does not allow this
116 // mismatch.
117 const CXXRecordDecl *record = type->getAsCXXRecordDecl();
118 bool canRegisterDestructor =
119 record && (!cgm.getCXXABI().hasThisReturn(
122
123 // If __cxa_atexit is disabled via a flag, a different helper function is
124 // generated elsewhere which uses atexit instead, and it takes the destructor
125 // 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());
132 CXXDestructorDecl *dtor = record->getDestructor();
133 // In LLVM OG codegen this is done in registerGlobalDtor, but CIRGen
134 // relies on LoweringPrepare for further decoupling, so build the
135 // 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 helper
147 // function here and emits the destroy into the helper function, which is
148 // called from __cxa_atexit.
149 // In CIR, we just emit the destroy into the dtor region. It will be moved
150 // into a separate function during the LoweringPrepare pass.
151 // FIXME(cir): We should create a new operation here to explicitly get the
152 // 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
171 const CIRGenFunctionInfo &fnInfo =
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 handled
192// differently in CIR than in classic codegen. Classic codegen emits
193// a global init function (__cxx_global_var_init) and inserts
194// initialization for each global there. In CIR, we attach a ctor
195// region to the global variable and insert the initialization code
196// into the ctor region. This will be moved into the
197// __cxx_global_var_init function during the LoweringPrepare pass.
199 cir::GlobalOp addr,
200 bool performInit) {
201 QualType ty = varDecl->getType();
202
203 // TODO: handle address space
204 // The address space of a static local variable (addr) may be different
205 // from the address space of the "this" argument of the constructor. In that
206 // 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 a
217 // "shared" address space qualifier, but the constructor of StructWithCtor
218 // expects "this" in the "generic" address space.
220
221 // Create a CIRGenFunction to emit the initializer. While this isn't a true
222 // 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
228 getLoc(varDecl->getLocation())};
229
231
232 if (!ty->isReferenceType()) {
234
235 bool needsDtor = varDecl->needsDestruction(getASTContext()) ==
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 else
244 emitDeclDestroy(cgf, varDecl, addr);
245 return;
246 }
247
248 errorNYI(varDecl->getSourceRange(), "global with reference type");
249}
static void emitDeclDestroy(CIRGenFunction &cgf, const VarDecl *vd, cir::GlobalOp addr)
Definition CIRGenCXX.cpp:72
static void emitDeclInit(CIRGenFunction &cgf, const VarDecl *varDecl, cir::GlobalOp globalOp)
Definition CIRGenCXX.cpp:24
mlir::Value createGetGlobal(mlir::Location loc, cir::GlobalOp global)
cir::CallOp createCallOp(mlir::Location loc, mlir::SymbolRefAttr callee, mlir::Type returnType, mlir::ValueRange operands, llvm::ArrayRef< mlir::NamedAttribute > attrs={})
const LangOptions & getLangOpts() const
Definition ASTContext.h:926
CharUnits getDeclAlign(const Decl *D, bool ForAlignof=false) const
Return a conservative estimate of the alignment of the specified decl D.
static AggValueSlot forLValue(const LValue &LV, IsDestructed_t isDestructed, IsAliased_t isAliased, Overlap_t mayOverlap, IsZeroed_t isZeroed=IsNotZeroed)
virtual void registerGlobalDtor(const VarDecl *vd, cir::FuncOp dtor, mlir::Value addr)=0
Emit code to force the execution of a destructor during global teardown.
virtual bool canCallMismatchedFunctionType() const
Returns true if the target allows calling a function through a pointer with a different signature tha...
static cir::TypeEvaluationKind getEvaluationKind(clang::QualType type)
Return the cir::TypeEvaluationKind of QualType type.
cir::FuncOp generateCode(clang::GlobalDecl gd, cir::FuncOp fn, cir::FuncType funcType)
mlir::Location getLoc(clang::SourceLocation srcLoc)
Helpers to convert Clang's SourceLocation to a MLIR Location.
mlir::Type convertTypeForMem(QualType t)
void emitScalarInit(const clang::Expr *init, mlir::Location loc, LValue lvalue, bool capturedByInit=false)
CIRGenBuilderTy & getBuilder()
void emitDestroy(Address addr, QualType type, Destroyer *destroyer)
Immediately perform the destruction of the given object.
Destroyer * getDestroyer(clang::QualType::DestructionKind kind)
void emitComplexExprIntoLValue(const Expr *e, LValue dest, bool isInit)
LValue makeAddrLValue(Address addr, QualType ty, AlignmentSource source=AlignmentSource::Type)
clang::ASTContext & getContext() const
void emitAggExpr(const clang::Expr *e, AggValueSlot slot)
This class organizes the cross-function state that is used while generating CIR code.
void emitCXXGlobalVarDeclInit(const VarDecl *varDecl, cir::GlobalOp addr, bool performInit)
Emit the function that initializes the specified global.
DiagnosticBuilder errorNYI(SourceLocation, llvm::StringRef)
Helpers to emit "not yet implemented" error diagnostics.
clang::ASTContext & getASTContext() const
cir::FuncOp getAddrOfCXXStructor(clang::GlobalDecl gd, const CIRGenFunctionInfo *fnInfo=nullptr, cir::FuncType fnType=nullptr, bool dontDefer=false, ForDefinition_t isForDefinition=NotForDefinition)
std::pair< cir::FuncType, cir::FuncOp > getAddrAndTypeOfCXXStructor(clang::GlobalDecl gd, const CIRGenFunctionInfo *fnInfo=nullptr, cir::FuncType fnType=nullptr, bool dontDefer=false, ForDefinition_t isForDefinition=NotForDefinition)
mlir::Value getAddrOfGlobalVar(const VarDecl *d, mlir::Type ty={}, ForDefinition_t isForDefinition=NotForDefinition)
Return the mlir::Value for the address of the given global variable.
const clang::CodeGenOptions & getCodeGenOpts() const
cir::FuncOp codegenCXXStructor(clang::GlobalDecl gd)
mlir::Location getLoc(clang::SourceLocation cLoc)
Helpers to convert the presumed location of Clang's SourceLocation to an MLIR Location.
CIRGenCXXABI & getCXXABI() const
void setCIRFunctionAttributesForDefinition(const clang::FunctionDecl *fd, cir::FuncOp f)
Set extra attributes (inline, etc.) for a function.
void setFunctionLinkage(GlobalDecl gd, cir::FuncOp f)
const CIRGenFunctionInfo & arrangeCXXStructorDeclaration(clang::GlobalDecl gd)
cir::FuncType getFunctionType(const CIRGenFunctionInfo &info)
Get the CIR function type for.
Represents a C++ destructor within a class.
Definition DeclCXX.h:2869
Represents a C++ struct/union/class.
Definition DeclCXX.h:258
bool hasTrivialDestructor() const
Determine whether this class has a trivial destructor (C++ [class.dtor]p3)
Definition DeclCXX.h:1366
CXXDestructorDecl * getDestructor() const
Returns the destructor decl for this class.
Definition DeclCXX.cpp:2121
CharUnits - This is an opaque type for sizes expressed in character units.
Definition CharUnits.h:38
This represents one expression.
Definition Expr.h:112
GlobalDecl - represents a global declaration.
Definition GlobalDecl.h:57
const Decl * getDecl() const
Definition GlobalDecl.h:106
A (possibly-)qualified type.
Definition TypeBase.h:937
bool isReferenceType() const
Definition TypeBase.h:8539
QualType getType() const
Definition Decl.h:723
Represents a variable declaration or definition.
Definition Decl.h:926
TLSKind getTLSKind() const
Definition Decl.cpp:2173
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition Decl.cpp:2195
QualType::DestructionKind needsDestruction(const ASTContext &Ctx) const
Would the destruction of this variable have any effect, and if so, what kind?
Definition Decl.cpp:2856
const internal::VariadicDynCastAllOfMatcher< Decl, VarDecl > varDecl
Matches variable declarations.
const internal::VariadicAllOfMatcher< Type > type
Matches Types in the clang AST.
The JSON file list parser is used to communicate input to InstallAPI.
@ Dtor_Complete
Complete object dtor.
Definition ABI.h:36
static bool addressSpace()
static bool aggValueSlotGC()
static bool astVarDeclInterface()
Represents a scope, including function bodies, compound statements, and the substatements of if/while...