@@ -202,6 +202,99 @@ mlir::Location CIRGenModule::getLoc(SourceRange cRange) {
202
202
return mlir::FusedLoc::get ({begin, end}, metadata, builder.getContext ());
203
203
}
204
204
205
+ mlir::Operation *
206
+ CIRGenModule::getAddrOfGlobal (GlobalDecl gd, ForDefinition_t isForDefinition) {
207
+ const Decl *d = gd.getDecl ();
208
+
209
+ if (isa<CXXConstructorDecl>(d) || isa<CXXDestructorDecl>(d)) {
210
+ errorNYI (d->getSourceRange (),
211
+ " getAddrOfGlobal: C++ constructor/destructor" );
212
+ return nullptr ;
213
+ }
214
+
215
+ if (isa<CXXMethodDecl>(d)) {
216
+ errorNYI (d->getSourceRange (), " getAddrOfGlobal: C++ method decl" );
217
+ return nullptr ;
218
+ }
219
+
220
+ if (isa<FunctionDecl>(d)) {
221
+ errorNYI (d->getSourceRange (), " getAddrOfGlobal: function decl" );
222
+ return nullptr ;
223
+ }
224
+
225
+ return getAddrOfGlobalVar (cast<VarDecl>(d), /* ty=*/ nullptr , isForDefinition)
226
+ .getDefiningOp ();
227
+ }
228
+
229
+ void CIRGenModule::emitGlobalDecl (const clang::GlobalDecl &d) {
230
+ // We call getAddrOfGlobal with isForDefinition set to ForDefinition in
231
+ // order to get a Value with exactly the type we need, not something that
232
+ // might have been created for another decl with the same mangled name but
233
+ // different type.
234
+ mlir::Operation *op = getAddrOfGlobal (d, ForDefinition);
235
+
236
+ // In case of different address spaces, we may still get a cast, even with
237
+ // IsForDefinition equal to ForDefinition. Query mangled names table to get
238
+ // GlobalValue.
239
+ if (!op)
240
+ op = getGlobalValue (getMangledName (d));
241
+
242
+ assert (op && " expected a valid global op" );
243
+
244
+ // Check to see if we've already emitted this. This is necessary for a
245
+ // couple of reasons: first, decls can end up in deferred-decls queue
246
+ // multiple times, and second, decls can end up with definitions in unusual
247
+ // ways (e.g. by an extern inline function acquiring a strong function
248
+ // redefinition). Just ignore those cases.
249
+ // TODO: Not sure what to map this to for MLIR
250
+ mlir::Operation *globalValueOp = op;
251
+ if (auto gv = dyn_cast<cir::GetGlobalOp>(op))
252
+ globalValueOp =
253
+ mlir::SymbolTable::lookupSymbolIn (getModule (), gv.getNameAttr ());
254
+
255
+ if (auto cirGlobalValue =
256
+ dyn_cast<cir::CIRGlobalValueInterface>(globalValueOp))
257
+ if (!cirGlobalValue.isDeclaration ())
258
+ return ;
259
+
260
+ // If this is OpenMP, check if it is legal to emit this global normally.
261
+ assert (!cir::MissingFeatures::openMP ());
262
+
263
+ // Otherwise, emit the definition and move on to the next one.
264
+ emitGlobalDefinition (d, op);
265
+ }
266
+
267
+ void CIRGenModule::emitDeferred () {
268
+ // Emit code for any potentially referenced deferred decls. Since a previously
269
+ // unused static decl may become used during the generation of code for a
270
+ // static function, iterate until no changes are made.
271
+
272
+ assert (!cir::MissingFeatures::openMP ());
273
+ assert (!cir::MissingFeatures::deferredVtables ());
274
+ assert (!cir::MissingFeatures::cudaSupport ());
275
+
276
+ // Stop if we're out of both deferred vtables and deferred declarations.
277
+ if (deferredDeclsToEmit.empty ())
278
+ return ;
279
+
280
+ // Grab the list of decls to emit. If emitGlobalDefinition schedules more
281
+ // work, it will not interfere with this.
282
+ std::vector<GlobalDecl> curDeclsToEmit;
283
+ curDeclsToEmit.swap (deferredDeclsToEmit);
284
+
285
+ for (const GlobalDecl &d : curDeclsToEmit) {
286
+ emitGlobalDecl (d);
287
+
288
+ // If we found out that we need to emit more decls, do that recursively.
289
+ // This has the advantage that the decls are emitted in a DFS and related
290
+ // ones are close together, which is convenient for testing.
291
+ if (!deferredDeclsToEmit.empty ()) {
292
+ emitDeferred ();
293
+ assert (deferredDeclsToEmit.empty ());
294
+ }
295
+ }
296
+ }
297
+
205
298
void CIRGenModule::emitGlobal (clang::GlobalDecl gd) {
206
299
if (const auto *cd = dyn_cast<clang::OpenACCConstructDecl>(gd.getDecl ())) {
207
300
emitGlobalOpenACCDecl (cd);
@@ -240,8 +333,33 @@ void CIRGenModule::emitGlobal(clang::GlobalDecl gd) {
240
333
}
241
334
}
242
335
243
- // TODO(CIR): Defer emitting some global definitions until later
244
- emitGlobalDefinition (gd);
336
+ // Defer code generation to first use when possible, e.g. if this is an inline
337
+ // function. If the global must always be emitted, do it eagerly if possible
338
+ // to benefit from cache locality. Deferring code generation is necessary to
339
+ // avoid adding initializers to external declarations.
340
+ if (mustBeEmitted (global) && mayBeEmittedEagerly (global)) {
341
+ // Emit the definition if it can't be deferred.
342
+ emitGlobalDefinition (gd);
343
+ return ;
344
+ }
345
+
346
+ // If we're deferring emission of a C++ variable with an initializer, remember
347
+ // the order in which it appeared on the file.
348
+ assert (!cir::MissingFeatures::deferredCXXGlobalInit ());
349
+
350
+ llvm::StringRef mangledName = getMangledName (gd);
351
+ if (getGlobalValue (mangledName) != nullptr ) {
352
+ // The value has already been used and should therefore be emitted.
353
+ addDeferredDeclToEmit (gd);
354
+ } else if (mustBeEmitted (global)) {
355
+ // The value must be emitted, but cannot be emitted eagerly.
356
+ assert (!mayBeEmittedEagerly (global));
357
+ addDeferredDeclToEmit (gd);
358
+ } else {
359
+ // Otherwise, remember that we saw a deferred decl with this name. The first
360
+ // use of the mangled name will cause it to move into deferredDeclsToEmit.
361
+ deferredDecls[mangledName] = gd;
362
+ }
245
363
}
246
364
247
365
void CIRGenModule::emitGlobalFunctionDefinition (clang::GlobalDecl gd,
@@ -402,6 +520,17 @@ CIRGenModule::getOrCreateCIRGlobal(StringRef mangledName, mlir::Type ty,
402
520
CIRGenModule::createGlobalOp (*this , loc, mangledName, ty,
403
521
/* insertPoint=*/ entry.getOperation ());
404
522
523
+ // This is the first use or definition of a mangled name. If there is a
524
+ // deferred decl with this name, remember that we need to emit it at the end
525
+ // of the file.
526
+ auto ddi = deferredDecls.find (mangledName);
527
+ if (ddi != deferredDecls.end ()) {
528
+ // Move the potentially referenced deferred decl to the DeferredDeclsToEmit
529
+ // list, and remove it from DeferredDecls (since we don't need it anymore).
530
+ addDeferredDeclToEmit (ddi->second );
531
+ deferredDecls.erase (ddi);
532
+ }
533
+
405
534
// Handle things which are present even on external declarations.
406
535
if (d) {
407
536
if (langOpts.OpenMP && !langOpts.OpenMPSimd )
@@ -1121,12 +1250,88 @@ void CIRGenModule::emitTentativeDefinition(const VarDecl *d) {
1121
1250
if (gv && !mlir::cast<cir::GlobalOp>(gv).isDeclaration ())
1122
1251
return ;
1123
1252
1124
- assert (!cir::MissingFeatures::deferredDecls ());
1253
+ // If we have not seen a reference to this variable yet, place it into the
1254
+ // deferred declarations table to be emitted if needed later.
1255
+ if (!mustBeEmitted (d) && !gv) {
1256
+ deferredDecls[mangledName] = d;
1257
+ return ;
1258
+ }
1125
1259
1126
1260
// The tentative definition is the only definition.
1127
1261
emitGlobalVarDefinition (d);
1128
1262
}
1129
1263
1264
+ bool CIRGenModule::mustBeEmitted (const ValueDecl *global) {
1265
+ // Never defer when EmitAllDecls is specified.
1266
+ if (langOpts.EmitAllDecls )
1267
+ return true ;
1268
+
1269
+ const auto *vd = dyn_cast<VarDecl>(global);
1270
+ if (vd &&
1271
+ ((codeGenOpts.KeepPersistentStorageVariables &&
1272
+ (vd->getStorageDuration () == SD_Static ||
1273
+ vd->getStorageDuration () == SD_Thread)) ||
1274
+ (codeGenOpts.KeepStaticConsts && vd->getStorageDuration () == SD_Static &&
1275
+ vd->getType ().isConstQualified ())))
1276
+ return true ;
1277
+
1278
+ // TODO(cir): We do want to defer function decls, but it's not implemented.
1279
+ assert (!cir::MissingFeatures::deferredFuncDecls ());
1280
+ if (isa<FunctionDecl>(global))
1281
+ return true ;
1282
+
1283
+ return getASTContext ().DeclMustBeEmitted (global);
1284
+ }
1285
+
1286
+ bool CIRGenModule::mayBeEmittedEagerly (const ValueDecl *global) {
1287
+ // In OpenMP 5.0 variables and function may be marked as
1288
+ // device_type(host/nohost) and we should not emit them eagerly unless we sure
1289
+ // that they must be emitted on the host/device. To be sure we need to have
1290
+ // seen a declare target with an explicit mentioning of the function, we know
1291
+ // we have if the level of the declare target attribute is -1. Note that we
1292
+ // check somewhere else if we should emit this at all.
1293
+ if (langOpts.OpenMP >= 50 && !langOpts.OpenMPSimd ) {
1294
+ std::optional<OMPDeclareTargetDeclAttr *> activeAttr =
1295
+ OMPDeclareTargetDeclAttr::getActiveAttr (global);
1296
+ if (!activeAttr || (*activeAttr)->getLevel () != (unsigned )-1 )
1297
+ return false ;
1298
+ }
1299
+
1300
+ const auto *fd = dyn_cast<FunctionDecl>(global);
1301
+ if (fd) {
1302
+ // Implicit template instantiations may change linkage if they are later
1303
+ // explicitly instantiated, so they should not be emitted eagerly.
1304
+ if (fd->getTemplateSpecializationKind () == TSK_ImplicitInstantiation)
1305
+ return false ;
1306
+ // Defer until all versions have been semantically checked.
1307
+ if (fd->hasAttr <TargetVersionAttr>() && !fd->isMultiVersion ())
1308
+ return false ;
1309
+ if (langOpts.SYCLIsDevice ) {
1310
+ errorNYI (fd->getSourceRange (), " mayBeEmittedEagerly: SYCL" );
1311
+ return false ;
1312
+ }
1313
+ }
1314
+ const auto *vd = dyn_cast<VarDecl>(global);
1315
+ if (vd)
1316
+ if (astContext.getInlineVariableDefinitionKind (vd) ==
1317
+ ASTContext::InlineVariableDefinitionKind::WeakUnknown)
1318
+ // A definition of an inline constexpr static data member may change
1319
+ // linkage later if it's redeclared outside the class.
1320
+ return false ;
1321
+
1322
+ // If OpenMP is enabled and threadprivates must be generated like TLS, delay
1323
+ // codegen for global variables, because they may be marked as threadprivate.
1324
+ if (langOpts.OpenMP && langOpts.OpenMPUseTLS &&
1325
+ astContext.getTargetInfo ().isTLSSupported () && isa<VarDecl>(global) &&
1326
+ !global->getType ().isConstantStorage (astContext, false , false ) &&
1327
+ !OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration (global))
1328
+ return false ;
1329
+
1330
+ assert ((fd || vd) &&
1331
+ " Only FunctionDecl and VarDecl should hit this path so far." );
1332
+ return true ;
1333
+ }
1334
+
1130
1335
static bool shouldAssumeDSOLocal (const CIRGenModule &cgm,
1131
1336
cir::CIRGlobalValueInterface gv) {
1132
1337
if (gv.hasLocalLinkage ())
@@ -1394,6 +1599,13 @@ CIRGenModule::getGlobalVisibilityAttrFromDecl(const Decl *decl) {
1394
1599
return cirVisibility;
1395
1600
}
1396
1601
1602
+ void CIRGenModule::release () {
1603
+ emitDeferred ();
1604
+
1605
+ // There's a lot of code that is not implemented yet.
1606
+ assert (!cir::MissingFeatures::cgmRelease ());
1607
+ }
1608
+
1397
1609
mlir::Type CIRGenModule::convertType (QualType type) {
1398
1610
return genTypes.convertType (type);
1399
1611
}
0 commit comments