diff options
author | Andy Kaylor <akaylor@nvidia.com> | 2025-05-02 16:54:29 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-05-02 16:54:29 -0700 |
commit | e8825900838a11afe326e202d19a3df6e3408422 (patch) | |
tree | 59d4d0616da3b6363b723abb2e2f0812aee0ef26 /clang/lib/CIR/CodeGen/CIRGenModule.cpp | |
parent | 880de1cae2e50e28984e56f97689e39bd60e12a3 (diff) | |
download | llvm-e8825900838a11afe326e202d19a3df6e3408422.zip llvm-e8825900838a11afe326e202d19a3df6e3408422.tar.gz llvm-e8825900838a11afe326e202d19a3df6e3408422.tar.bz2 |
[CIR] Refactor global variable emission and initialization (#138222)
When global variable support was initially upstreamed, we took some
shortcuts and only implemented the minimum support for simple variables
and constant initializers.
This change refactors the code that creates global variables to
introduce more of the complexities that are present in the incubator and
the classic codegen. I can't really say this is NFC, because the code
executed is very different and it will report different NYI diagnostics,
but for the currently implemented cases, it results in the same output.
Diffstat (limited to 'clang/lib/CIR/CodeGen/CIRGenModule.cpp')
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenModule.cpp | 190 |
1 files changed, 158 insertions, 32 deletions
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index a6a4330..82bd139 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -269,6 +269,40 @@ mlir::Operation *CIRGenModule::getGlobalValue(StringRef name) { return mlir::SymbolTable::lookupSymbolIn(theModule, name); } +cir::GlobalOp CIRGenModule::createGlobalOp(CIRGenModule &cgm, + mlir::Location loc, StringRef name, + mlir::Type t, + mlir::Operation *insertPoint) { + cir::GlobalOp g; + CIRGenBuilderTy &builder = cgm.getBuilder(); + + { + mlir::OpBuilder::InsertionGuard guard(builder); + + // Some global emissions are triggered while emitting a function, e.g. + // void s() { const char *s = "yolo"; ... } + // + // Be sure to insert global before the current function + CIRGenFunction *curCGF = cgm.curCGF; + if (curCGF) + builder.setInsertionPoint(curCGF->curFn); + + g = builder.create<cir::GlobalOp>(loc, name, t); + if (!curCGF) { + if (insertPoint) + cgm.getModule().insert(insertPoint, g); + else + cgm.getModule().push_back(g); + } + + // Default to private until we can judge based on the initializer, + // since MLIR doesn't allow public declarations. + mlir::SymbolTable::setSymbolVisibility( + g, mlir::SymbolTable::Visibility::Private); + } + return g; +} + /// If the specified mangled name is not in the module, /// create and return an mlir GlobalOp with the specified type (TODO(cir): /// address space). @@ -324,8 +358,15 @@ CIRGenModule::getOrCreateCIRGlobal(StringRef mangledName, mlir::Type ty, return entry; } - errorNYI(d->getSourceRange(), "reference of undeclared global"); - return {}; + mlir::Location loc = getLoc(d->getSourceRange()); + + // mlir::SymbolTable::Visibility::Public is the default, no need to explicitly + // mark it as such. + cir::GlobalOp gv = + CIRGenModule::createGlobalOp(*this, loc, mangledName, ty, + /*insertPoint=*/entry.getOperation()); + + return gv; } cir::GlobalOp @@ -365,46 +406,125 @@ mlir::Value CIRGenModule::getAddrOfGlobalVar(const VarDecl *d, mlir::Type ty, void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd, bool isTentative) { const QualType astTy = vd->getType(); - const mlir::Type type = convertType(vd->getType()); - if (vd->getIdentifier()) { - StringRef name = getMangledName(GlobalDecl(vd)); - auto varOp = - builder.create<cir::GlobalOp>(getLoc(vd->getSourceRange()), name, type); - // TODO(CIR): This code for processing initial values is a placeholder - // until class ConstantEmitter is upstreamed and the code for processing - // constant expressions is filled out. Only the most basic handling of - // certain constant expressions is implemented for now. - const VarDecl *initDecl; - const Expr *initExpr = vd->getAnyInitializer(initDecl); - mlir::Attribute initializer; - if (initExpr) { - if (APValue *value = initDecl->evaluateValue()) { - ConstantEmitter emitter(*this); - initializer = emitter.tryEmitPrivateForMemory(*value, astTy); + + if (getLangOpts().OpenCL || getLangOpts().OpenMPIsTargetDevice) { + errorNYI(vd->getSourceRange(), "emit OpenCL/OpenMP global variable"); + return; + } + + // Whether the definition of the variable is available externally. + // If yes, we shouldn't emit the GloablCtor and GlobalDtor for the variable + // since this is the job for its original source. + bool isDefinitionAvailableExternally = + astContext.GetGVALinkageForVariable(vd) == GVA_AvailableExternally; + assert(!cir::MissingFeatures::needsGlobalCtorDtor()); + + // It is useless to emit the definition for an available_externally variable + // which can't be marked as const. + if (isDefinitionAvailableExternally && + (!vd->hasConstantInitialization() || + // TODO: Update this when we have interface to check constexpr + // destructor. + vd->needsDestruction(astContext) || + !vd->getType().isConstantStorage(astContext, true, true))) + return; + + mlir::Attribute init; + const VarDecl *initDecl; + const Expr *initExpr = vd->getAnyInitializer(initDecl); + + std::optional<ConstantEmitter> emitter; + + assert(!cir::MissingFeatures::cudaSupport()); + + if (vd->hasAttr<LoaderUninitializedAttr>()) { + errorNYI(vd->getSourceRange(), "loader uninitialized attribute"); + return; + } else if (!initExpr) { + // This is a tentative definition; tentative definitions are + // implicitly initialized with { 0 }. + // + // Note that tentative definitions are only emitted at the end of + // a translation unit, so they should never have incomplete + // type. In addition, EmitTentativeDefinition makes sure that we + // never attempt to emit a tentative definition if a real one + // exists. A use may still exists, however, so we still may need + // to do a RAUW. + assert(!astTy->isIncompleteType() && "Unexpected incomplete type"); + init = builder.getZeroInitAttr(convertType(vd->getType())); + } else { + emitter.emplace(*this); + mlir::Attribute initializer = emitter->tryEmitForInitializer(*initDecl); + if (!initializer) { + QualType qt = initExpr->getType(); + if (vd->getType()->isReferenceType()) + qt = vd->getType(); + + if (getLangOpts().CPlusPlus) { + if (initDecl->hasFlexibleArrayInit(astContext)) + errorNYI(vd->getSourceRange(), "flexible array initializer"); + init = builder.getZeroInitAttr(convertType(qt)); + if (astContext.GetGVALinkageForVariable(vd) != GVA_AvailableExternally) + errorNYI(vd->getSourceRange(), "global constructor"); } else { - errorNYI(initExpr->getSourceRange(), "non-constant initializer"); + errorNYI(vd->getSourceRange(), "static initializer"); } } else { - initializer = builder.getZeroInitAttr(convertType(astTy)); + init = initializer; + // We don't need an initializer, so remove the entry for the delayed + // initializer position (just in case this entry was delayed) if we + // also don't need to register a destructor. + if (vd->needsDestruction(astContext) == QualType::DK_cxx_destructor) + errorNYI(vd->getSourceRange(), "delayed destructor"); } + } + + mlir::Type initType; + if (mlir::isa<mlir::SymbolRefAttr>(init)) { + errorNYI(vd->getSourceRange(), "global initializer is a symbol reference"); + return; + } else { + assert(mlir::isa<mlir::TypedAttr>(init) && "This should have a type"); + auto typedInitAttr = mlir::cast<mlir::TypedAttr>(init); + initType = typedInitAttr.getType(); + } + assert(!mlir::isa<mlir::NoneType>(initType) && "Should have a type by now"); + + cir::GlobalOp gv = + getOrCreateCIRGlobal(vd, initType, ForDefinition_t(!isTentative)); + // TODO(cir): Strip off pointer casts from Entry if we get them? - varOp.setInitialValueAttr(initializer); + if (!gv || gv.getSymType() != initType) { + errorNYI(vd->getSourceRange(), "global initializer with type mismatch"); + return; + } - // Set CIR's linkage type as appropriate. - cir::GlobalLinkageKind linkage = - getCIRLinkageVarDefinition(vd, /*IsConstant=*/false); + assert(!cir::MissingFeatures::maybeHandleStaticInExternC()); - // Set CIR linkage and DLL storage class. - varOp.setLinkage(linkage); + if (vd->hasAttr<AnnotateAttr>()) { + errorNYI(vd->getSourceRange(), "annotate global variable"); + } - if (linkage == cir::GlobalLinkageKind::CommonLinkage) - errorNYI(initExpr->getSourceRange(), "common linkage"); + assert(!cir::MissingFeatures::opGlobalLinkage()); - theModule.push_back(varOp); - } else { - errorNYI(vd->getSourceRange().getBegin(), - "variable definition with a non-identifier for a name"); + if (langOpts.CUDA) { + errorNYI(vd->getSourceRange(), "CUDA global variable"); } + + // Set initializer and finalize emission + CIRGenModule::setInitializer(gv, init); + if (emitter) + emitter->finalize(gv); + + // Set CIR's linkage type as appropriate. + cir::GlobalLinkageKind linkage = + getCIRLinkageVarDefinition(vd, /*IsConstant=*/false); + + // Set CIR linkage and DLL storage class. + gv.setLinkage(linkage); + + if (linkage == cir::GlobalLinkageKind::CommonLinkage) + errorNYI(initExpr->getSourceRange(), "common linkage"); } void CIRGenModule::emitGlobalDefinition(clang::GlobalDecl gd, @@ -684,6 +804,12 @@ void CIRGenModule::emitTopLevelDecl(Decl *decl) { } } +void CIRGenModule::setInitializer(cir::GlobalOp &op, mlir::Attribute value) { + // Recompute visibility when updating initializer. + op.setInitialValueAttr(value); + assert(!cir::MissingFeatures::opGlobalSetVisitibility()); +} + cir::FuncOp CIRGenModule::getAddrOfFunction(clang::GlobalDecl gd, mlir::Type funcType, bool forVTable, bool dontDefer, |