@@ -208,8 +208,25 @@ void CIRGenFunction::emitVarDecl(const VarDecl &d) {
208
208
if (d.hasExternalStorage ())
209
209
return ;
210
210
211
- if (d.getStorageDuration () != SD_Automatic)
212
- cgm.errorNYI (d.getSourceRange (), " emitVarDecl automatic storage duration" );
211
+ if (d.getStorageDuration () != SD_Automatic) {
212
+ // Static sampler variables translated to function calls.
213
+ if (d.getType ()->isSamplerT ()) {
214
+ // Nothing needs to be done here, but let's flag it as an error until we
215
+ // have a test. It requires OpenCL support.
216
+ cgm.errorNYI (d.getSourceRange (), " emitVarDecl static sampler type" );
217
+ return ;
218
+ }
219
+
220
+ cir::GlobalLinkageKind linkage =
221
+ cgm.getCIRLinkageVarDefinition (&d, /* IsConstant=*/ false );
222
+
223
+ // FIXME: We need to force the emission/use of a guard variable for
224
+ // some variables even if we can constant-evaluate them because
225
+ // we can't guarantee every translation unit will constant-evaluate them.
226
+
227
+ return emitStaticVarDecl (d, linkage);
228
+ }
229
+
213
230
if (d.getType ().getAddressSpace () == LangAS::opencl_local)
214
231
cgm.errorNYI (d.getSourceRange (), " emitVarDecl openCL address space" );
215
232
@@ -219,6 +236,233 @@ void CIRGenFunction::emitVarDecl(const VarDecl &d) {
219
236
return emitAutoVarDecl (d);
220
237
}
221
238
239
+ static std::string getStaticDeclName (CIRGenModule &cgm, const VarDecl &d) {
240
+ if (cgm.getLangOpts ().CPlusPlus )
241
+ return cgm.getMangledName (&d).str ();
242
+
243
+ // If this isn't C++, we don't need a mangled name, just a pretty one.
244
+ assert (!d.isExternallyVisible () && " name shouldn't matter" );
245
+ std::string contextName;
246
+ const DeclContext *dc = d.getDeclContext ();
247
+ if (auto *cd = dyn_cast<CapturedDecl>(dc))
248
+ dc = cast<DeclContext>(cd->getNonClosureContext ());
249
+ if (const auto *fd = dyn_cast<FunctionDecl>(dc))
250
+ contextName = std::string (cgm.getMangledName (fd));
251
+ else if (isa<BlockDecl>(dc))
252
+ cgm.errorNYI (d.getSourceRange (), " block decl context for static var" );
253
+ else if (isa<ObjCMethodDecl>(dc))
254
+ cgm.errorNYI (d.getSourceRange (), " ObjC decl context for static var" );
255
+ else
256
+ cgm.errorNYI (d.getSourceRange (), " Unknown context for static var decl" );
257
+
258
+ contextName += " ." + d.getNameAsString ();
259
+ return contextName;
260
+ }
261
+
262
+ // TODO(cir): LLVM uses a Constant base class. Maybe CIR could leverage an
263
+ // interface for all constants?
264
+ cir::GlobalOp
265
+ CIRGenModule::getOrCreateStaticVarDecl (const VarDecl &d,
266
+ cir::GlobalLinkageKind linkage) {
267
+ // In general, we don't always emit static var decls once before we reference
268
+ // them. It is possible to reference them before emitting the function that
269
+ // contains them, and it is possible to emit the containing function multiple
270
+ // times.
271
+ if (cir::GlobalOp existingGV = getStaticLocalDeclAddress (&d))
272
+ return existingGV;
273
+
274
+ QualType ty = d.getType ();
275
+ assert (ty->isConstantSizeType () && " VLAs can't be static" );
276
+
277
+ // Use the label if the variable is renamed with the asm-label extension.
278
+ if (d.hasAttr <AsmLabelAttr>())
279
+ errorNYI (d.getSourceRange (), " getOrCreateStaticVarDecl: asm label" );
280
+
281
+ std::string name = getStaticDeclName (*this , d);
282
+
283
+ mlir::Type lty = getTypes ().convertTypeForMem (ty);
284
+ assert (!cir::MissingFeatures::addressSpace ());
285
+
286
+ if (d.hasAttr <LoaderUninitializedAttr>() || d.hasAttr <CUDASharedAttr>())
287
+ errorNYI (d.getSourceRange (),
288
+ " getOrCreateStaticVarDecl: LoaderUninitializedAttr" );
289
+ assert (!cir::MissingFeatures::addressSpace ());
290
+
291
+ mlir::Attribute init = builder.getZeroInitAttr (convertType (ty));
292
+
293
+ cir::GlobalOp gv = builder.createVersionedGlobal (
294
+ getModule (), getLoc (d.getLocation ()), name, lty, linkage);
295
+ // TODO(cir): infer visibility from linkage in global op builder.
296
+ gv.setVisibility (getMLIRVisibilityFromCIRLinkage (linkage));
297
+ gv.setInitialValueAttr (init);
298
+ gv.setAlignment (getASTContext ().getDeclAlign (&d).getAsAlign ().value ());
299
+
300
+ if (supportsCOMDAT () && gv.isWeakForLinker ())
301
+ gv.setComdat (true );
302
+
303
+ assert (!cir::MissingFeatures::opGlobalThreadLocal ());
304
+
305
+ setGVProperties (gv, &d);
306
+
307
+ // OG checks if the expected address space, denoted by the type, is the
308
+ // same as the actual address space indicated by attributes. If they aren't
309
+ // the same, an addrspacecast is emitted when this variable is accessed.
310
+ // In CIR however, cir.get_global already carries that information in
311
+ // !cir.ptr type - if this global is in OpenCL local address space, then its
312
+ // type would be !cir.ptr<..., addrspace(offload_local)>. Therefore we don't
313
+ // need an explicit address space cast in CIR: they will get emitted when
314
+ // lowering to LLVM IR.
315
+
316
+ // Ensure that the static local gets initialized by making sure the parent
317
+ // function gets emitted eventually.
318
+ const Decl *dc = cast<Decl>(d.getDeclContext ());
319
+
320
+ // We can't name blocks or captured statements directly, so try to emit their
321
+ // parents.
322
+ if (isa<BlockDecl>(dc) || isa<CapturedDecl>(dc)) {
323
+ dc = dc->getNonClosureContext ();
324
+ // FIXME: Ensure that global blocks get emitted.
325
+ if (!dc)
326
+ errorNYI (d.getSourceRange (), " non-closure context" );
327
+ }
328
+
329
+ GlobalDecl gd;
330
+ if (isa<CXXConstructorDecl>(dc))
331
+ errorNYI (d.getSourceRange (), " C++ constructors static var context" );
332
+ else if (isa<CXXDestructorDecl>(dc))
333
+ errorNYI (d.getSourceRange (), " C++ destructors static var context" );
334
+ else if (const auto *fd = dyn_cast<FunctionDecl>(dc))
335
+ gd = GlobalDecl (fd);
336
+ else {
337
+ // Don't do anything for Obj-C method decls or global closures. We should
338
+ // never defer them.
339
+ assert (isa<ObjCMethodDecl>(dc) && " unexpected parent code decl" );
340
+ }
341
+ if (gd.getDecl () && cir::MissingFeatures::openMP ()) {
342
+ // Disable emission of the parent function for the OpenMP device codegen.
343
+ errorNYI (d.getSourceRange (), " OpenMP" );
344
+ }
345
+
346
+ return gv;
347
+ }
348
+
349
+ // / Add the initializer for 'd' to the global variable that has already been
350
+ // / created for it. If the initializer has a different type than gv does, this
351
+ // / may free gv and return a different one. Otherwise it just returns gv.
352
+ cir::GlobalOp CIRGenFunction::addInitializerToStaticVarDecl (
353
+ const VarDecl &d, cir::GlobalOp gv, cir::GetGlobalOp gvAddr) {
354
+ ConstantEmitter emitter (*this );
355
+ mlir::TypedAttr init =
356
+ mlir::cast<mlir::TypedAttr>(emitter.tryEmitForInitializer (d));
357
+
358
+ // If constant emission failed, then this should be a C++ static
359
+ // initializer.
360
+ if (!init) {
361
+ cgm.errorNYI (d.getSourceRange (), " static var without initializer" );
362
+ return gv;
363
+ }
364
+
365
+ // TODO(cir): There should be debug code here to assert that the decl size
366
+ // matches the CIR data layout type alloc size, but the code for calculating
367
+ // the type alloc size is not implemented yet.
368
+ assert (!cir::MissingFeatures::dataLayoutTypeAllocSize ());
369
+
370
+ // The initializer may differ in type from the global. Rewrite
371
+ // the global to match the initializer. (We have to do this
372
+ // because some types, like unions, can't be completely represented
373
+ // in the LLVM type system.)
374
+ if (gv.getSymType () != init.getType ()) {
375
+ gv.setSymType (init.getType ());
376
+
377
+ // Normally this should be done with a call to cgm.replaceGlobal(oldGV, gv),
378
+ // but since at this point the current block hasn't been really attached,
379
+ // there's no visibility into the GetGlobalOp corresponding to this Global.
380
+ // Given those constraints, thread in the GetGlobalOp and update it
381
+ // directly.
382
+ assert (!cir::MissingFeatures::addressSpace ());
383
+ gvAddr.getAddr ().setType (builder.getPointerTo (init.getType ()));
384
+ }
385
+
386
+ bool needsDtor =
387
+ d.needsDestruction (getContext ()) == QualType::DK_cxx_destructor;
388
+
389
+ assert (!cir::MissingFeatures::opGlobalConstant ());
390
+ gv.setInitialValueAttr (init);
391
+
392
+ emitter.finalize (gv);
393
+
394
+ if (needsDtor) {
395
+ // We have a constant initializer, but a nontrivial destructor. We still
396
+ // need to perform a guarded "initialization" in order to register the
397
+ // destructor.
398
+ cgm.errorNYI (d.getSourceRange (), " C++ guarded init" );
399
+ }
400
+
401
+ return gv;
402
+ }
403
+
404
+ void CIRGenFunction::emitStaticVarDecl (const VarDecl &d,
405
+ cir::GlobalLinkageKind linkage) {
406
+ // Check to see if we already have a global variable for this
407
+ // declaration. This can happen when double-emitting function
408
+ // bodies, e.g. with complete and base constructors.
409
+ cir::GlobalOp globalOp = cgm.getOrCreateStaticVarDecl (d, linkage);
410
+ // TODO(cir): we should have a way to represent global ops as values without
411
+ // having to emit a get global op. Sometimes these emissions are not used.
412
+ mlir::Value addr = builder.createGetGlobal (globalOp);
413
+ auto getAddrOp = mlir::cast<cir::GetGlobalOp>(addr.getDefiningOp ());
414
+
415
+ CharUnits alignment = getContext ().getDeclAlign (&d);
416
+
417
+ // Store into LocalDeclMap before generating initializer to handle
418
+ // circular references.
419
+ mlir::Type elemTy = convertTypeForMem (d.getType ());
420
+ setAddrOfLocalVar (&d, Address (addr, elemTy, alignment));
421
+
422
+ // We can't have a VLA here, but we can have a pointer to a VLA,
423
+ // even though that doesn't really make any sense.
424
+ // Make sure to evaluate VLA bounds now so that we have them for later.
425
+ if (d.getType ()->isVariablyModifiedType ()) {
426
+ cgm.errorNYI (d.getSourceRange (),
427
+ " emitStaticVarDecl: variably modified type" );
428
+ }
429
+
430
+ // Save the type in case adding the initializer forces a type change.
431
+ mlir::Type expectedType = addr.getType ();
432
+
433
+ cir::GlobalOp var = globalOp;
434
+
435
+ assert (!cir::MissingFeatures::cudaSupport ());
436
+
437
+ // If this value has an initializer, emit it.
438
+ if (d.getInit ())
439
+ var = addInitializerToStaticVarDecl (d, var, getAddrOp);
440
+
441
+ var.setAlignment (alignment.getAsAlign ().value ());
442
+
443
+ // There are a lot of attributes that need to be handled here. Until
444
+ // we start to support them, we just report an error if there are any.
445
+ if (d.hasAttrs ())
446
+ cgm.errorNYI (d.getSourceRange (), " static var with attrs" );
447
+
448
+ if (cgm.getCodeGenOpts ().KeepPersistentStorageVariables )
449
+ cgm.errorNYI (d.getSourceRange (), " static var keep persistent storage" );
450
+
451
+ // From traditional codegen:
452
+ // We may have to cast the constant because of the initializer
453
+ // mismatch above.
454
+ //
455
+ // FIXME: It is really dangerous to store this in the map; if anyone
456
+ // RAUW's the GV uses of this constant will be invalid.
457
+ mlir::Value castedAddr =
458
+ builder.createBitcast (getAddrOp.getAddr (), expectedType);
459
+ localDeclMap.find (&d)->second = Address (castedAddr, elemTy, alignment);
460
+ cgm.setStaticLocalDeclAddress (&d, var);
461
+
462
+ assert (!cir::MissingFeatures::sanitizers ());
463
+ assert (!cir::MissingFeatures::generateDebugInfo ());
464
+ }
465
+
222
466
void CIRGenFunction::emitScalarInit (const Expr *init, mlir::Location loc,
223
467
LValue lvalue, bool capturedByInit) {
224
468
assert (!cir::MissingFeatures::objCLifetime ());
0 commit comments