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
24/// Emit code to cause the variable at the given address to be considered as
25/// constant from this point onwards.
26static void emitDeclInvariant(CIRGenFunction &cgf, const VarDecl *d) {
27 mlir::Value addr = cgf.cgm.getAddrOfGlobalVar(d);
29 addr, cgf.getLoc(d->getSourceRange()));
30}
31
33 mlir::Location loc) {
34 // Do not emit the intrinsic if we're not optimizing.
35 if (!cgm.getCodeGenOpts().OptimizationLevel)
36 return;
37
38 CIRGenBuilderTy &builder = getBuilder();
39
40 // Create the size constant as i64
41 uint64_t width = size.getQuantity();
42 mlir::Value sizeValue = builder.getConstInt(loc, builder.getSInt64Ty(),
43 static_cast<int64_t>(width));
44
45 // Create the intrinsic call. The llvm.invariant.start intrinsic returns a
46 // token, but we don't need to capture it. The address space will be
47 // automatically handled when the intrinsic is lowered to LLVM IR.
48 cir::LLVMIntrinsicCallOp::create(
49 builder, loc, builder.getStringAttr("invariant.start"), addr.getType(),
50 mlir::ValueRange{sizeValue, addr});
51}
52
53static void emitDeclInit(CIRGenFunction &cgf, const VarDecl *varDecl,
54 cir::GlobalOp globalOp) {
55 assert((varDecl->hasGlobalStorage() ||
56 (varDecl->hasLocalStorage() &&
57 cgf.getContext().getLangOpts().OpenCLCPlusPlus)) &&
58 "VarDecl must have global or local (in the case of OpenCL) storage!");
59 assert(!varDecl->getType()->isReferenceType() &&
60 "Should not call emitDeclInit on a reference!");
61
62 CIRGenBuilderTy &builder = cgf.getBuilder();
63
64 // Set up the ctor region.
65 mlir::OpBuilder::InsertionGuard guard(builder);
66 mlir::Block *block = builder.createBlock(&globalOp.getCtorRegion());
67 CIRGenFunction::LexicalScope lexScope{cgf, globalOp.getLoc(),
68 builder.getInsertionBlock()};
69 lexScope.setAsGlobalInit();
70 builder.setInsertionPointToStart(block);
71
74
75 QualType type = varDecl->getType();
76 LValue lv = cgf.makeAddrLValue(declAddr, type);
77
78 const Expr *init = varDecl->getInit();
80 case cir::TEK_Scalar:
82 cgf.emitScalarInit(init, cgf.getLoc(varDecl->getLocation()), lv, false);
83 break;
85 cgf.emitComplexExprIntoLValue(init, lv, /*isInit=*/true);
86 break;
89 cgf.emitAggExpr(init,
93 break;
94 }
95
96 // Finish the ctor region.
97 builder.setInsertionPointToEnd(block);
98 cir::YieldOp::create(builder, globalOp.getLoc());
99}
100
101static void emitDeclDestroy(CIRGenFunction &cgf, const VarDecl *vd,
102 cir::GlobalOp addr) {
103 // Honor __attribute__((no_destroy)) and bail instead of attempting
104 // to emit a reference to a possibly nonexistent destructor, which
105 // in turn can cause a crash. This will result in a global constructor
106 // that isn't balanced out by a destructor call as intended by the
107 // attribute. This also checks for -fno-c++-static-destructors and
108 // bails even if the attribute is not present.
110
111 // FIXME: __attribute__((cleanup)) ?
112
113 switch (dtorKind) {
115 return;
116
118 break;
119
123 // We don't care about releasing objects during process teardown.
124 assert(!vd->getTLSKind() && "should have rejected this");
125 return;
126 }
127
128 // If not constant storage we'll emit this regardless of NeedsDtor value.
129 CIRGenBuilderTy &builder = cgf.getBuilder();
130
131 // Prepare the dtor region.
132 mlir::OpBuilder::InsertionGuard guard(builder);
133 mlir::Block *block = builder.createBlock(&addr.getDtorRegion());
134 CIRGenFunction::LexicalScope lexScope{cgf, addr.getLoc(),
135 builder.getInsertionBlock()};
136 lexScope.setAsGlobalInit();
137 builder.setInsertionPointToStart(block);
138
139 CIRGenModule &cgm = cgf.cgm;
140 QualType type = vd->getType();
141
142 // Special-case non-array C++ destructors, if they have the right signature.
143 // Under some ABIs, destructors return this instead of void, and cannot be
144 // passed directly to __cxa_atexit if the target does not allow this
145 // mismatch.
146 const CXXRecordDecl *record = type->getAsCXXRecordDecl();
147 bool canRegisterDestructor =
148 record && (!cgm.getCXXABI().hasThisReturn(
151
152 // If __cxa_atexit is disabled via a flag, a different helper function is
153 // generated elsewhere which uses atexit instead, and it takes the destructor
154 // directly.
155 cir::FuncOp fnOp;
156 if (record && (canRegisterDestructor || cgm.getCodeGenOpts().CXAAtExit)) {
157 if (vd->getTLSKind())
158 cgm.errorNYI(vd->getSourceRange(), "TLS destructor");
159 assert(!record->hasTrivialDestructor());
161 CXXDestructorDecl *dtor = record->getDestructor();
162 // In LLVM OG codegen this is done in registerGlobalDtor, but CIRGen
163 // relies on LoweringPrepare for further decoupling, so build the
164 // call right here.
165 auto gd = GlobalDecl(dtor, Dtor_Complete);
166 fnOp = cgm.getAddrAndTypeOfCXXStructor(gd).second;
167 builder.createCallOp(cgf.getLoc(vd->getSourceRange()),
168 mlir::FlatSymbolRefAttr::get(fnOp.getSymNameAttr()),
169 mlir::ValueRange{cgm.getAddrOfGlobalVar(vd)});
170 assert(fnOp && "expected cir.func");
171 // TODO(cir): This doesn't do anything but check for unhandled conditions.
172 // What it is meant to do should really be happening in LoweringPrepare.
173 cgm.getCXXABI().registerGlobalDtor(vd, fnOp, nullptr);
174 } else {
175 // Otherwise, a custom destroyed is needed. Classic codegen creates a helper
176 // function here and emits the destroy into the helper function, which is
177 // called from __cxa_atexit.
178 // In CIR, we just emit the destroy into the dtor region. It will be moved
179 // into a separate function during the LoweringPrepare pass.
180 // FIXME(cir): We should create a new operation here to explicitly get the
181 // address of the global into whose dtor region we are emiiting the destroy.
182 // The same applies to code above where it is calling getAddrOfGlobalVar.
183 mlir::Value globalVal = builder.createGetGlobal(addr);
184 CharUnits alignment = cgf.getContext().getDeclAlign(vd);
185 Address globalAddr{globalVal, cgf.convertTypeForMem(type), alignment};
186 cgf.emitDestroy(globalAddr, type, cgf.getDestroyer(dtorKind));
187 }
188
189 builder.setInsertionPointToEnd(block);
190 if (block->empty()) {
191 block->erase();
192 // Don't confuse lexical cleanup.
193 builder.clearInsertionPoint();
194 } else {
195 cir::YieldOp::create(builder, addr.getLoc());
196 }
197}
198
200 const CIRGenFunctionInfo &fnInfo =
202 cir::FuncType funcType = getTypes().getFunctionType(fnInfo);
203 cir::FuncOp fn = getAddrOfCXXStructor(gd, &fnInfo, /*FnType=*/nullptr,
204 /*DontDefer=*/true, ForDefinition);
205 setFunctionLinkage(gd, fn);
206 CIRGenFunction cgf{*this, builder};
207 curCGF = &cgf;
208 {
209 mlir::OpBuilder::InsertionGuard guard(builder);
210 cgf.generateCode(gd, fn, funcType);
211 }
212 curCGF = nullptr;
213
214 setNonAliasAttributes(gd, fn);
215 setCIRFunctionAttributesForDefinition(mlir::cast<FunctionDecl>(gd.getDecl()),
216 fn);
217 return fn;
218}
219
220// Global variables requiring non-trivial initialization are handled
221// differently in CIR than in classic codegen. Classic codegen emits
222// a global init function (__cxx_global_var_init) and inserts
223// initialization for each global there. In CIR, we attach a ctor
224// region to the global variable and insert the initialization code
225// into the ctor region. This will be moved into the
226// __cxx_global_var_init function during the LoweringPrepare pass.
228 cir::GlobalOp addr,
229 bool performInit) {
230 QualType ty = varDecl->getType();
231
232 // TODO: handle address space
233 // The address space of a static local variable (addr) may be different
234 // from the address space of the "this" argument of the constructor. In that
235 // case, we need an addrspacecast before calling the constructor.
236 //
237 // struct StructWithCtor {
238 // __device__ StructWithCtor() {...}
239 // };
240 // __device__ void foo() {
241 // __shared__ StructWithCtor s;
242 // ...
243 // }
244 //
245 // For example, in the above CUDA code, the static local variable s has a
246 // "shared" address space qualifier, but the constructor of StructWithCtor
247 // expects "this" in the "generic" address space.
249
250 // Create a CIRGenFunction to emit the initializer. While this isn't a true
251 // function, the handling works the same way.
252 CIRGenFunction cgf{*this, builder, true};
253 llvm::SaveAndRestore<CIRGenFunction *> savedCGF(curCGF, &cgf);
254 curCGF->curFn = addr;
255
257 getLoc(varDecl->getLocation())};
258
260
261 if (!ty->isReferenceType()) {
263
264 bool needsDtor = varDecl->needsDestruction(getASTContext()) ==
266 bool isConstantStorage =
267 varDecl->getType().isConstantStorage(getASTContext(), true, !needsDtor);
268 // PerformInit, constant store invariant / destroy handled below.
269 if (performInit) {
270 emitDeclInit(cgf, varDecl, addr);
271 // For constant storage, emit invariant.start in the ctor region after
272 // initialization but before the yield.
273 if (isConstantStorage) {
274 CIRGenBuilderTy &builder = cgf.getBuilder();
275 mlir::OpBuilder::InsertionGuard guard(builder);
276 // Set insertion point to end of ctor region (before yield)
277 if (!addr.getCtorRegion().empty()) {
278 mlir::Block *block = &addr.getCtorRegion().back();
279 // Find the yield op and insert before it
280 mlir::Operation *yieldOp = block->getTerminator();
281 if (yieldOp) {
282 builder.setInsertionPoint(yieldOp);
284 }
285 }
286 }
287 } else if (isConstantStorage) {
289 }
290
291 if (!isConstantStorage)
292 emitDeclDestroy(cgf, varDecl, addr);
293 return;
294 }
295
296 errorNYI(varDecl->getSourceRange(), "global with reference type");
297}
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:26
static void emitDeclDestroy(CIRGenFunction &cgf, const VarDecl *vd, cir::GlobalOp addr)
static void emitDeclInit(CIRGenFunction &cgf, const VarDecl *varDecl, cir::GlobalOp globalOp)
Definition CIRGenCXX.cpp:53
cir::CallOp createCallOp(mlir::Location loc, mlir::SymbolRefAttr callee, mlir::Type returnType, mlir::ValueRange operands, llvm::ArrayRef< mlir::NamedAttribute > attrs={})
mlir::Value createGetGlobal(mlir::Location loc, cir::GlobalOp global, bool threadLocal=false)
const LangOptions & getLangOpts() const
Definition ASTContext.h:944
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:32
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
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:8554
QualType getType() const
Definition Decl.h:723
Represents a variable declaration or definition.
Definition Decl.h:926
TLSKind getTLSKind() const
Definition Decl.cpp:2175
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition Decl.cpp:2197
QualType::DestructionKind needsDestruction(const ASTContext &Ctx) const
Would the destruction of this variable have any effect, and if so, what kind?
Definition Decl.cpp:2858
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...