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) {
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(&globalOp.getCtorRegion());
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) {
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(&addr.getDtorRegion());
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 if (vd->getTLSKind())
159 cgm.errorNYI(vd->getSourceRange(), "TLS destructor");
160 assert(!record->hasTrivialDestructor());
162 CXXDestructorDecl *dtor = record->getDestructor();
163 // In LLVM OG codegen this is done in registerGlobalDtor, but CIRGen
164 // relies on LoweringPrepare for further decoupling, so build the
165 // call right here.
166 auto gd = GlobalDecl(dtor, Dtor_Complete);
167 fnOp = cgm.getAddrAndTypeOfCXXStructor(gd).second;
168 builder.createCallOp(cgf.getLoc(vd->getSourceRange()),
169 mlir::FlatSymbolRefAttr::get(fnOp.getSymNameAttr()),
170 mlir::ValueRange{cgm.getAddrOfGlobalVar(vd)});
171 assert(fnOp && "expected cir.func");
172 // TODO(cir): This doesn't do anything but check for unhandled conditions.
173 // What it is meant to do should really be happening in LoweringPrepare.
174 cgm.getCXXABI().registerGlobalDtor(vd, fnOp, nullptr);
175 } else {
176 // Otherwise, a custom destroyed is needed. Classic codegen creates a helper
177 // function here and emits the destroy into the helper function, which is
178 // called from __cxa_atexit.
179 // In CIR, we just emit the destroy into the dtor region. It will be moved
180 // into a separate function during the LoweringPrepare pass.
181 // FIXME(cir): We should create a new operation here to explicitly get the
182 // address of the global into whose dtor region we are emiiting the destroy.
183 // The same applies to code above where it is calling getAddrOfGlobalVar.
184 mlir::Value globalVal = builder.createGetGlobal(addr);
185 CharUnits alignment = cgf.getContext().getDeclAlign(vd);
186 Address globalAddr{globalVal, cgf.convertTypeForMem(type), alignment};
187 cgf.emitDestroy(globalAddr, type, cgf.getDestroyer(dtorKind));
188 }
189
190 builder.setInsertionPointToEnd(block);
191 if (block->empty()) {
192 block->erase();
193 // Don't confuse lexical cleanup.
194 builder.clearInsertionPoint();
195 } else {
196 cir::YieldOp::create(builder, addr.getLoc());
197 }
198}
199
201 const CIRGenFunctionInfo &fnInfo =
203 cir::FuncType funcType = getTypes().getFunctionType(fnInfo);
204 cir::FuncOp fn = getAddrOfCXXStructor(gd, &fnInfo, /*FnType=*/nullptr,
205 /*DontDefer=*/true, ForDefinition);
206 setFunctionLinkage(gd, fn);
207 CIRGenFunction cgf{*this, builder};
208 curCGF = &cgf;
209 {
210 mlir::OpBuilder::InsertionGuard guard(builder);
211 cgf.generateCode(gd, fn, funcType);
212 }
213 curCGF = nullptr;
214
215 setNonAliasAttributes(gd, fn);
216 setCIRFunctionAttributesForDefinition(mlir::cast<FunctionDecl>(gd.getDecl()),
217 fn);
218 return fn;
219}
220
221// Global variables requiring non-trivial initialization are handled
222// differently in CIR than in classic codegen. Classic codegen emits
223// a global init function (__cxx_global_var_init) and inserts
224// initialization for each global there. In CIR, we attach a ctor
225// region to the global variable and insert the initialization code
226// into the ctor region. This will be moved into the
227// __cxx_global_var_init function during the LoweringPrepare pass.
229 cir::GlobalOp addr,
230 bool performInit) {
231 QualType ty = varDecl->getType();
232
233 // TODO: handle address space
234 // The address space of a static local variable (addr) may be different
235 // from the address space of the "this" argument of the constructor. In that
236 // case, we need an addrspacecast before calling the constructor.
237 //
238 // struct StructWithCtor {
239 // __device__ StructWithCtor() {...}
240 // };
241 // __device__ void foo() {
242 // __shared__ StructWithCtor s;
243 // ...
244 // }
245 //
246 // For example, in the above CUDA code, the static local variable s has a
247 // "shared" address space qualifier, but the constructor of StructWithCtor
248 // expects "this" in the "generic" address space.
250
251 // Create a CIRGenFunction to emit the initializer. While this isn't a true
252 // function, the handling works the same way.
253 CIRGenFunction cgf{*this, builder, true};
254 llvm::SaveAndRestore<CIRGenFunction *> savedCGF(curCGF, &cgf);
255 curCGF->curFn = addr;
256
258 getLoc(varDecl->getLocation())};
259
260 addr.setAstAttr(cir::ASTVarDeclAttr::get(&getMLIRContext(), varDecl));
261
262 if (!ty->isReferenceType()) {
264
265 bool needsDtor = varDecl->needsDestruction(getASTContext()) ==
267 bool isConstantStorage =
268 varDecl->getType().isConstantStorage(getASTContext(), true, !needsDtor);
269 // PerformInit, constant store invariant / destroy handled below.
270 if (performInit) {
271 emitDeclInit(cgf, varDecl, addr);
272 // For constant storage, emit invariant.start in the ctor region after
273 // initialization but before the yield.
274 if (isConstantStorage) {
275 CIRGenBuilderTy &builder = cgf.getBuilder();
276 mlir::OpBuilder::InsertionGuard guard(builder);
277 // Set insertion point to end of ctor region (before yield)
278 if (!addr.getCtorRegion().empty()) {
279 mlir::Block *block = &addr.getCtorRegion().back();
280 // Find the yield op and insert before it
281 mlir::Operation *yieldOp = block->getTerminator();
282 if (yieldOp) {
283 builder.setInsertionPoint(yieldOp);
285 }
286 }
287 }
288 } else if (isConstantStorage) {
290 }
291
292 if (!isConstantStorage)
293 emitDeclDestroy(cgf, varDecl, addr);
294 return;
295 }
296
297 mlir::OpBuilder::InsertionGuard guard(builder);
298 auto *block = builder.createBlock(&addr.getCtorRegion());
299 CIRGenFunction::LexicalScope scope{*curCGF, addr.getLoc(),
300 builder.getInsertionBlock()};
301 scope.setAsGlobalInit();
302 builder.setInsertionPointToStart(block);
303 mlir::Value getGlobal = builder.createGetGlobal(addr);
304
305 Address declAddr(getGlobal, getASTContext().getDeclAlign(varDecl));
306 assert(performInit && "cannot have a constant initializer which needs "
307 "destruction for reference");
308 RValue rv = cgf.emitReferenceBindingToExpr(varDecl->getInit());
309 {
310 mlir::OpBuilder::InsertionGuard guard(builder);
311 mlir::Operation *rvalDefOp = rv.getValue().getDefiningOp();
312 if (rvalDefOp && rvalDefOp->getBlock()) {
313 mlir::Block *rvalSrcBlock = rvalDefOp->getBlock();
314
315 if (!rvalSrcBlock->empty() && isa<cir::YieldOp>(rvalSrcBlock->back())) {
316 mlir::Operation &front = rvalSrcBlock->front();
317 getGlobal.getDefiningOp()->moveBefore(&front);
318 builder.setInsertionPoint(cast<cir::YieldOp>(rvalSrcBlock->back()));
319 }
320 }
321 cgf.emitStoreOfScalar(rv.getValue(), declAddr, /*isVolatile=*/false, ty,
323 }
324
325 builder.setInsertionPointToEnd(block);
326 cir::YieldOp::create(builder, addr->getLoc());
327}
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)
static void emitDeclInit(CIRGenFunction &cgf, const VarDecl *varDecl, cir::GlobalOp globalOp)
Definition CIRGenCXX.cpp:54
mlir::Value createGetGlobal(mlir::Location loc, cir::GlobalOp global, bool threadLocal=false)
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:951
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.
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.
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)
const CIRGenFunctionInfo & arrangeCXXStructorDeclaration(clang::GlobalDecl gd)
cir::FuncType getFunctionType(const CIRGenFunctionInfo &info)
Get the CIR function type for.
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:2876
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:8649
QualType getType() const
Definition Decl.h:723
Represents a variable declaration or definition.
Definition Decl.h:926
TLSKind getTLSKind() const
Definition Decl.cpp:2180
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition Decl.cpp:2202
QualType::DestructionKind needsDestruction(const ASTContext &Ctx) const
Would the destruction of this variable have any effect, and if so, what kind?
Definition Decl.cpp:2863
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...