@@ -208,8 +208,25 @@ void CIRGenFunction::emitVarDecl(const VarDecl &d) {
208208 if (d.hasExternalStorage ())
209209 return ;
210210
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+
213230 if (d.getType ().getAddressSpace () == LangAS::opencl_local)
214231 cgm.errorNYI (d.getSourceRange (), " emitVarDecl openCL address space" );
215232
@@ -219,6 +236,233 @@ void CIRGenFunction::emitVarDecl(const VarDecl &d) {
219236 return emitAutoVarDecl (d);
220237}
221238
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+
222466void CIRGenFunction::emitScalarInit (const Expr *init, mlir::Location loc,
223467 LValue lvalue, bool capturedByInit) {
224468 assert (!cir::MissingFeatures::objCLifetime ());
0 commit comments