clang 23.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
20#include "llvm/Support/SaveAndRestore.h"
21
22using namespace clang;
23using namespace clang::CIRGen;
24
25/// Emit code to cause the variable at the given address to be considered as
26/// constant from this point onwards.
27static void emitDeclInvariant(CIRGenFunction &cgf, const VarDecl *d) {
28 mlir::Value addr = cgf.cgm.getAddrOfGlobalVar(d);
30 addr, cgf.getLoc(d->getSourceRange()));
31}
32
34 mlir::Location loc) {
35 // Do not emit the intrinsic if we're not optimizing.
36 if (!cgm.getCodeGenOpts().OptimizationLevel)
37 return;
38
39 CIRGenBuilderTy &builder = getBuilder();
40
41 // Create the size constant as i64
42 uint64_t width = size.getQuantity();
43 mlir::Value sizeValue = builder.getConstInt(loc, builder.getSInt64Ty(),
44 static_cast<int64_t>(width));
45
46 // Create the intrinsic call. The llvm.invariant.start intrinsic returns a
47 // token, but we don't need to capture it. The address space will be
48 // automatically handled when the intrinsic is lowered to LLVM IR.
49 cir::LLVMIntrinsicCallOp::create(
50 builder, loc, builder.getStringAttr("invariant.start"), addr.getType(),
51 mlir::ValueRange{sizeValue, addr});
52}
53
54static void emitDeclInit(CIRGenFunction &cgf, const VarDecl *varDecl,
55 cir::GlobalOp globalOp, mlir::Region &ctorRegion) {
56 assert((varDecl->hasGlobalStorage() ||
57 (varDecl->hasLocalStorage() &&
58 cgf.getContext().getLangOpts().OpenCLCPlusPlus)) &&
59 "VarDecl must have global or local (in the case of OpenCL) storage!");
60 assert(!varDecl->getType()->isReferenceType() &&
61 "Should not call emitDeclInit on a reference!");
62
63 CIRGenBuilderTy &builder = cgf.getBuilder();
64
65 // Set up the ctor region.
66 mlir::OpBuilder::InsertionGuard guard(builder);
67 mlir::Block *block = builder.createBlock(&ctorRegion);
68 CIRGenFunction::LexicalScope lexScope{cgf, globalOp.getLoc(),
69 builder.getInsertionBlock()};
70 lexScope.setAsGlobalInit();
71 builder.setInsertionPointToStart(block);
72
75
76 QualType type = varDecl->getType();
77 LValue lv = cgf.makeAddrLValue(declAddr, type);
78
79 const Expr *init = varDecl->getInit();
81 case cir::TEK_Scalar:
83 cgf.emitScalarInit(init, cgf.getLoc(varDecl->getLocation()), lv, false);
84 break;
86 cgf.emitComplexExprIntoLValue(init, lv, /*isInit=*/true);
87 break;
90 cgf.emitAggExpr(init,
94 break;
95 }
96
97 // Finish the ctor region.
98 builder.setInsertionPointToEnd(block);
99 cir::YieldOp::create(builder, globalOp.getLoc());
100}
101
102static void emitDeclDestroy(CIRGenFunction &cgf, const VarDecl *vd,
103 cir::GlobalOp addr, mlir::Region &dtorRegion) {
104 // Honor __attribute__((no_destroy)) and bail instead of attempting
105 // to emit a reference to a possibly nonexistent destructor, which
106 // in turn can cause a crash. This will result in a global constructor
107 // that isn't balanced out by a destructor call as intended by the
108 // attribute. This also checks for -fno-c++-static-destructors and
109 // bails even if the attribute is not present.
111
112 // FIXME: __attribute__((cleanup)) ?
113
114 switch (dtorKind) {
116 return;
117
119 break;
120
124 // We don't care about releasing objects during process teardown.
125 assert(!vd->getTLSKind() && "should have rejected this");
126 return;
127 }
128
129 // If not constant storage we'll emit this regardless of NeedsDtor value.
130 CIRGenBuilderTy &builder = cgf.getBuilder();
131
132 // Prepare the dtor region.
133 mlir::OpBuilder::InsertionGuard guard(builder);
134 mlir::Block *block = builder.createBlock(&dtorRegion);
135 CIRGenFunction::LexicalScope lexScope{cgf, addr.getLoc(),
136 builder.getInsertionBlock()};
137 lexScope.setAsGlobalInit();
138 builder.setInsertionPointToStart(block);
139
140 CIRGenModule &cgm = cgf.cgm;
141 QualType type = vd->getType();
142
143 // Special-case non-array C++ destructors, if they have the right signature.
144 // Under some ABIs, destructors return this instead of void, and cannot be
145 // passed directly to __cxa_atexit if the target does not allow this
146 // mismatch.
147 const CXXRecordDecl *record = type->getAsCXXRecordDecl();
148 bool canRegisterDestructor =
149 record && (!cgm.getCXXABI().hasThisReturn(
152
153 // If __cxa_atexit is disabled via a flag, a different helper function is
154 // generated elsewhere which uses atexit instead, and it takes the destructor
155 // directly.
156 cir::FuncOp fnOp;
157 if (record && (canRegisterDestructor || cgm.getCodeGenOpts().CXAAtExit)) {
158 assert(!record->hasTrivialDestructor());
160 CXXDestructorDecl *dtor = record->getDestructor();
161 // In LLVM OG codegen this is done in registerGlobalDtor, but CIRGen
162 // relies on LoweringPrepare for further decoupling, so build the
163 // call right here.
164 auto gd = GlobalDecl(dtor, Dtor_Complete);
165 fnOp = cgm.getAddrAndTypeOfCXXStructor(gd).second;
166 // When a global has a constant initializer that fixes the active member
167 // of a union (e.g. an SSO short variant), CIR creates the global with
168 // the initializer's narrowed record type, so `getAddrOfGlobalVar` returns
169 // a pointer to the narrowed type rather than the variable's declared
170 // type. Mirror the cast pattern from `emitGlobalVarDeclLValue` so the
171 // destructor receives a `this` pointer typed as the declared class.
172 mlir::Value thisAddr = cgm.getAddrOfGlobalVar(vd);
173 mlir::Type realVarTy = cgm.getTypes().convertTypeForMem(type);
174 cir::PointerType realPtrTy = cir::PointerType::get(
175 realVarTy,
176 mlir::cast<cir::PointerType>(thisAddr.getType()).getAddrSpace());
177 if (realPtrTy != thisAddr.getType())
178 thisAddr = builder.createBitcast(thisAddr.getLoc(), thisAddr, realPtrTy);
179 builder.createCallOp(cgf.getLoc(vd->getSourceRange()),
180 mlir::FlatSymbolRefAttr::get(fnOp.getSymNameAttr()),
181 mlir::ValueRange{thisAddr});
182 assert(fnOp && "expected cir.func");
183 // TODO(cir): This doesn't do anything but check for unhandled conditions.
184 // What it is meant to do should really be happening in LoweringPrepare.
185 cgm.getCXXABI().registerGlobalDtor(vd, fnOp, nullptr);
186 } else {
187 // Otherwise, a custom destroyed is needed. Classic codegen creates a helper
188 // function here and emits the destroy into the helper function, which is
189 // called from __cxa_atexit.
190 // In CIR, we just emit the destroy into the dtor region. It will be moved
191 // into a separate function during the LoweringPrepare pass.
192 // FIXME(cir): We should create a new operation here to explicitly get the
193 // address of the global into whose dtor region we are emiiting the destroy.
194 // The same applies to code above where it is calling getAddrOfGlobalVar.
195 mlir::Value globalVal = builder.createGetGlobal(addr);
196 globalVal.getDefiningOp<cir::GetGlobalOp>().setStaticLocal(
197 addr.getStaticLocalGuard().has_value());
198 CharUnits alignment = cgf.getContext().getDeclAlign(vd);
199 Address globalAddr{globalVal, cgf.convertTypeForMem(type), alignment};
200 cgf.emitDestroy(globalAddr, type, cgf.getDestroyer(dtorKind));
201 }
202
203 builder.setInsertionPointToEnd(block);
204 if (block->empty()) {
205 block->erase();
206 // Don't confuse lexical cleanup.
207 builder.clearInsertionPoint();
208 } else {
209 cir::YieldOp::create(builder, addr.getLoc());
210 }
211}
212
214 const CIRGenFunctionInfo &fnInfo =
216 cir::FuncType funcType = getTypes().getFunctionType(fnInfo);
217 cir::FuncOp fn = getAddrOfCXXStructor(gd, &fnInfo, /*FnType=*/nullptr,
218 /*DontDefer=*/true, ForDefinition);
219 setFunctionLinkage(gd, fn);
220 CIRGenFunction cgf{*this, builder};
221 curCGF = &cgf;
222 {
223 mlir::OpBuilder::InsertionGuard guard(builder);
224 cgf.generateCode(gd, fn, funcType);
225 }
226 curCGF = nullptr;
227
228 setNonAliasAttributes(gd, fn);
229 setCIRFunctionAttributesForDefinition(mlir::cast<FunctionDecl>(gd.getDecl()),
230 fn);
231 return fn;
232}
233
234// Global variables requiring non-trivial initialization are handled
235// differently in CIR than in classic codegen. Classic codegen emits
236// a global init function (__cxx_global_var_init) and inserts
237// initialization for each global there. In CIR, we attach a ctor
238// region to the global variable and insert the initialization code
239// into the ctor region. This will be moved into the
240// __cxx_global_var_init function during the LoweringPrepare pass.
242 cir::GlobalOp addr,
243 bool performInit,
244 mlir::Region &ctorRegion,
245 mlir::Region &dtorRegion) {
246 QualType ty = varDecl->getType();
247 assert(curCGF && "Special var init only available inside of a function");
248 CIRGenFunction &cgf = *curCGF;
249
250 // TODO: handle address space
251 // The address space of a static local variable (addr) may be different
252 // from the address space of the "this" argument of the constructor. In that
253 // case, we need an addrspacecast before calling the constructor.
254 //
255 // struct StructWithCtor {
256 // __device__ StructWithCtor() {...}
257 // };
258 // __device__ void foo() {
259 // __shared__ StructWithCtor s;
260 // ...
261 // }
262 //
263 // For example, in the above CUDA code, the static local variable s has a
264 // "shared" address space qualifier, but the constructor of StructWithCtor
265 // expects "this" in the "generic" address space.
267
268 addr.setAstAttr(cir::ASTVarDeclAttr::get(&getMLIRContext(), varDecl));
269
270 if (!ty->isReferenceType()) {
272
273 bool needsDtor = varDecl->needsDestruction(getASTContext()) ==
275 bool isConstantStorage =
276 varDecl->getType().isConstantStorage(getASTContext(), true, !needsDtor);
277 // PerformInit, constant store invariant / destroy handled below.
278 if (performInit) {
279 emitDeclInit(cgf, varDecl, addr, ctorRegion);
280 // For constant storage, emit invariant.start in the ctor region after
281 // initialization but before the yield.
282 if (isConstantStorage) {
283 CIRGenBuilderTy &builder = cgf.getBuilder();
284 mlir::OpBuilder::InsertionGuard guard(builder);
285 // Set insertion point to end of ctor region (before yield)
286 if (!ctorRegion.empty()) {
287 mlir::Block *block = &ctorRegion.back();
288 // Find the yield op and insert before it
289 mlir::Operation *yieldOp = block->getTerminator();
290 if (yieldOp) {
291 builder.setInsertionPoint(yieldOp);
293 }
294 }
295 }
296 } else if (isConstantStorage) {
298 }
299
300 if (!isConstantStorage)
301 emitDeclDestroy(cgf, varDecl, addr, dtorRegion);
302 return;
303 }
304
305 mlir::OpBuilder::InsertionGuard guard(builder);
306 auto *block = builder.createBlock(&ctorRegion);
307 CIRGenFunction::LexicalScope scope{*curCGF, addr.getLoc(),
308 builder.getInsertionBlock()};
309 scope.setAsGlobalInit();
310 builder.setInsertionPointToStart(block);
311 mlir::Value getGlobal = builder.createGetGlobal(addr, varDecl->getTLSKind());
312 // If we're initializing a static local with a guard variable, set the flag
313 // that indicates that.
314 getGlobal.getDefiningOp<cir::GetGlobalOp>().setStaticLocal(
315 addr.getStaticLocalGuard().has_value());
316
317 Address declAddr(getGlobal, getASTContext().getDeclAlign(varDecl));
318 assert(performInit && "cannot have a constant initializer which needs "
319 "destruction for reference");
320 RValue rv = cgf.emitReferenceBindingToExpr(varDecl->getInit());
321 {
322 mlir::OpBuilder::InsertionGuard guard(builder);
323 mlir::Operation *rvalDefOp = rv.getValue().getDefiningOp();
324 if (rvalDefOp && rvalDefOp->getBlock()) {
325 mlir::Block *rvalSrcBlock = rvalDefOp->getBlock();
326
327 if (!rvalSrcBlock->empty() && isa<cir::YieldOp>(rvalSrcBlock->back())) {
328 mlir::Operation &front = rvalSrcBlock->front();
329 getGlobal.getDefiningOp()->moveBefore(&front);
330 builder.setInsertionPoint(cast<cir::YieldOp>(rvalSrcBlock->back()));
331 }
332 }
333 cgf.emitStoreOfScalar(rv.getValue(), declAddr, /*isVolatile=*/false, ty,
335 }
336
337 builder.setInsertionPointToEnd(block);
338 cir::YieldOp::create(builder, addr->getLoc());
339}
340
342 cir::GlobalOp addr,
343 bool performInit) {
344 assert(!varDecl->isStaticLocal());
345
346 // Create a CIRGenFunction to emit the initializer. While this isn't a true
347 // function, the handling works the same way.
348 CIRGenFunction cgf{*this, builder, true};
349 llvm::SaveAndRestore<CIRGenFunction *> savedCGF(curCGF, &cgf);
350 curCGF->curFn = addr;
351
353 getLoc(varDecl->getLocation())};
354
355 emitCXXSpecialVarDeclInit(varDecl, addr, performInit, addr.getCtorRegion(),
356 addr.getDtorRegion());
357}
358
360 cir::GlobalOp addr,
361 bool performInit) {
362 assert(varDecl->isStaticLocal());
363
364 auto initOp =
365 cir::LocalInitOp::create(builder, addr->getLoc(), addr.getSymNameAttr(),
366 varDecl->getTLSKind() != VarDecl::TLS_None);
367
368 emitCXXSpecialVarDeclInit(varDecl, addr, performInit, initOp.getCtorRegion(),
369 initOp.getDtorRegion());
370}
static void emitDeclInvariant(CIRGenFunction &cgf, const VarDecl *d)
Emit code to cause the variable at the given address to be considered as constant from this point onw...
Definition CIRGenCXX.cpp:27
static void emitDeclDestroy(CIRGenFunction &cgf, const VarDecl *vd, cir::GlobalOp addr, mlir::Region &dtorRegion)
static void emitDeclInit(CIRGenFunction &cgf, const VarDecl *varDecl, cir::GlobalOp globalOp, mlir::Region &ctorRegion)
Definition CIRGenCXX.cpp:54
cir::GetGlobalOp createGetGlobal(mlir::Location loc, cir::GlobalOp global, bool threadLocal=false)
mlir::Value createBitcast(mlir::Value src, mlir::Type newTy)
cir::CallOp createCallOp(mlir::Location loc, mlir::SymbolRefAttr callee, mlir::Type returnType, mlir::ValueRange operands, llvm::ArrayRef< mlir::NamedAttribute > attrs={}, llvm::ArrayRef< mlir::NamedAttrList > argAttrs={}, llvm::ArrayRef< mlir::NamedAttribute > resAttrs={})
const LangOptions & getLangOpts() const
Definition ASTContext.h:959
CharUnits getDeclAlign(const Decl *D, bool ForAlignof=false) const
Return a conservative estimate of the alignment of the specified decl D.
CharUnits getTypeSizeInChars(QualType T) const
Return the size of the specified (complete) type T, in characters.
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)
void emitInvariantStart(CharUnits size, mlir::Value addr, mlir::Location loc)
Definition CIRGenCXX.cpp:33
mlir::Location getLoc(clang::SourceLocation srcLoc)
Helpers to convert Clang's SourceLocation to a MLIR Location.
RValue emitReferenceBindingToExpr(const Expr *e)
Emits a reference binding to the passed in expression.
mlir::Type convertTypeForMem(QualType t)
void emitStoreOfScalar(mlir::Value value, Address addr, bool isVolatile, clang::QualType ty, LValueBaseInfo baseInfo, bool isInit=false, bool isNontemporal=false)
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.
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)
void emitCXXStaticLocalVarDeclInit(const VarDecl *varDecl, cir::GlobalOp addr, bool performInit)
Emit the function that initializes the specified static-local variable.
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.
mlir::MLIRContext & getMLIRContext()
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)
void emitCXXSpecialVarDeclInit(const VarDecl *varDecl, cir::GlobalOp addr, bool performInit, mlir::Region &ctorRegion, mlir::Region &dtorRegion)
Helper function for the below two that will create the constructor/destructor in specified regions,...
const CIRGenFunctionInfo & arrangeCXXStructorDeclaration(clang::GlobalDecl gd)
cir::FuncType getFunctionType(const CIRGenFunctionInfo &info)
Get the CIR function type for.
mlir::Type convertTypeForMem(clang::QualType, bool forBitField=false)
Convert type T into an mlir::Type.
This trivial value class is used to represent the result of an expression that is evaluated.
Definition CIRGenValue.h:33
mlir::Value getValue() const
Return the value of this scalar value.
Definition CIRGenValue.h:57
Represents a C++ destructor within a class.
Definition DeclCXX.h:2882
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:1372
CXXDestructorDecl * getDestructor() const
Returns the destructor decl for this class.
Definition DeclCXX.cpp:2131
CharUnits - This is an opaque type for sizes expressed in character units.
Definition CharUnits.h:38
QuantityType getQuantity() const
getQuantity - Get the raw integer representation of this quantity.
Definition CharUnits.h:185
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:8706
QualType getType() const
Definition Decl.h:723
Represents a variable declaration or definition.
Definition Decl.h:924
TLSKind getTLSKind() const
Definition Decl.cpp:2152
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition Decl.cpp:2174
QualType::DestructionKind needsDestruction(const ASTContext &Ctx) const
Would the destruction of this variable have any effect, and if so, what kind?
Definition Decl.cpp:2835
@ TLS_None
Not a TLS variable.
Definition Decl.h:944
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.
bool isa(CodeGen::Address addr)
Definition Address.h:330
@ Dtor_Complete
Complete object dtor.
Definition ABI.h:36
U cast(CodeGen::Address addr)
Definition Address.h:327
static bool addressSpace()
static bool aggValueSlotGC()
Represents a scope, including function bodies, compound statements, and the substatements of if/while...