diff options
Diffstat (limited to 'clang/lib/CIR/CodeGen')
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenModule.cpp | 218 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenModule.h | 36 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenerator.cpp | 10 |
3 files changed, 261 insertions, 3 deletions
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 0ec5dce..eb291de 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -202,6 +202,99 @@ mlir::Location CIRGenModule::getLoc(SourceRange cRange) { return mlir::FusedLoc::get({begin, end}, metadata, builder.getContext()); } +mlir::Operation * +CIRGenModule::getAddrOfGlobal(GlobalDecl gd, ForDefinition_t isForDefinition) { + const Decl *d = gd.getDecl(); + + if (isa<CXXConstructorDecl>(d) || isa<CXXDestructorDecl>(d)) { + errorNYI(d->getSourceRange(), + "getAddrOfGlobal: C++ constructor/destructor"); + return nullptr; + } + + if (isa<CXXMethodDecl>(d)) { + errorNYI(d->getSourceRange(), "getAddrOfGlobal: C++ method decl"); + return nullptr; + } + + if (isa<FunctionDecl>(d)) { + errorNYI(d->getSourceRange(), "getAddrOfGlobal: function decl"); + return nullptr; + } + + return getAddrOfGlobalVar(cast<VarDecl>(d), /*ty=*/nullptr, isForDefinition) + .getDefiningOp(); +} + +void CIRGenModule::emitGlobalDecl(const clang::GlobalDecl &d) { + // We call getAddrOfGlobal with isForDefinition set to ForDefinition in + // order to get a Value with exactly the type we need, not something that + // might have been created for another decl with the same mangled name but + // different type. + mlir::Operation *op = getAddrOfGlobal(d, ForDefinition); + + // In case of different address spaces, we may still get a cast, even with + // IsForDefinition equal to ForDefinition. Query mangled names table to get + // GlobalValue. + if (!op) + op = getGlobalValue(getMangledName(d)); + + assert(op && "expected a valid global op"); + + // Check to see if we've already emitted this. This is necessary for a + // couple of reasons: first, decls can end up in deferred-decls queue + // multiple times, and second, decls can end up with definitions in unusual + // ways (e.g. by an extern inline function acquiring a strong function + // redefinition). Just ignore those cases. + // TODO: Not sure what to map this to for MLIR + mlir::Operation *globalValueOp = op; + if (auto gv = dyn_cast<cir::GetGlobalOp>(op)) + globalValueOp = + mlir::SymbolTable::lookupSymbolIn(getModule(), gv.getNameAttr()); + + if (auto cirGlobalValue = + dyn_cast<cir::CIRGlobalValueInterface>(globalValueOp)) + if (!cirGlobalValue.isDeclaration()) + return; + + // If this is OpenMP, check if it is legal to emit this global normally. + assert(!cir::MissingFeatures::openMP()); + + // Otherwise, emit the definition and move on to the next one. + emitGlobalDefinition(d, op); +} + +void CIRGenModule::emitDeferred() { + // Emit code for any potentially referenced deferred decls. Since a previously + // unused static decl may become used during the generation of code for a + // static function, iterate until no changes are made. + + assert(!cir::MissingFeatures::openMP()); + assert(!cir::MissingFeatures::deferredVtables()); + assert(!cir::MissingFeatures::cudaSupport()); + + // Stop if we're out of both deferred vtables and deferred declarations. + if (deferredDeclsToEmit.empty()) + return; + + // Grab the list of decls to emit. If emitGlobalDefinition schedules more + // work, it will not interfere with this. + std::vector<GlobalDecl> curDeclsToEmit; + curDeclsToEmit.swap(deferredDeclsToEmit); + + for (const GlobalDecl &d : curDeclsToEmit) { + emitGlobalDecl(d); + + // If we found out that we need to emit more decls, do that recursively. + // This has the advantage that the decls are emitted in a DFS and related + // ones are close together, which is convenient for testing. + if (!deferredDeclsToEmit.empty()) { + emitDeferred(); + assert(deferredDeclsToEmit.empty()); + } + } +} + void CIRGenModule::emitGlobal(clang::GlobalDecl gd) { if (const auto *cd = dyn_cast<clang::OpenACCConstructDecl>(gd.getDecl())) { emitGlobalOpenACCDecl(cd); @@ -240,8 +333,33 @@ void CIRGenModule::emitGlobal(clang::GlobalDecl gd) { } } - // TODO(CIR): Defer emitting some global definitions until later - emitGlobalDefinition(gd); + // Defer code generation to first use when possible, e.g. if this is an inline + // function. If the global must always be emitted, do it eagerly if possible + // to benefit from cache locality. Deferring code generation is necessary to + // avoid adding initializers to external declarations. + if (mustBeEmitted(global) && mayBeEmittedEagerly(global)) { + // Emit the definition if it can't be deferred. + emitGlobalDefinition(gd); + return; + } + + // If we're deferring emission of a C++ variable with an initializer, remember + // the order in which it appeared on the file. + assert(!cir::MissingFeatures::deferredCXXGlobalInit()); + + llvm::StringRef mangledName = getMangledName(gd); + if (getGlobalValue(mangledName) != nullptr) { + // The value has already been used and should therefore be emitted. + addDeferredDeclToEmit(gd); + } else if (mustBeEmitted(global)) { + // The value must be emitted, but cannot be emitted eagerly. + assert(!mayBeEmittedEagerly(global)); + addDeferredDeclToEmit(gd); + } else { + // Otherwise, remember that we saw a deferred decl with this name. The first + // use of the mangled name will cause it to move into deferredDeclsToEmit. + deferredDecls[mangledName] = gd; + } } void CIRGenModule::emitGlobalFunctionDefinition(clang::GlobalDecl gd, @@ -402,6 +520,17 @@ CIRGenModule::getOrCreateCIRGlobal(StringRef mangledName, mlir::Type ty, CIRGenModule::createGlobalOp(*this, loc, mangledName, ty, /*insertPoint=*/entry.getOperation()); + // This is the first use or definition of a mangled name. If there is a + // deferred decl with this name, remember that we need to emit it at the end + // of the file. + auto ddi = deferredDecls.find(mangledName); + if (ddi != deferredDecls.end()) { + // Move the potentially referenced deferred decl to the DeferredDeclsToEmit + // list, and remove it from DeferredDecls (since we don't need it anymore). + addDeferredDeclToEmit(ddi->second); + deferredDecls.erase(ddi); + } + // Handle things which are present even on external declarations. if (d) { if (langOpts.OpenMP && !langOpts.OpenMPSimd) @@ -1121,12 +1250,88 @@ void CIRGenModule::emitTentativeDefinition(const VarDecl *d) { if (gv && !mlir::cast<cir::GlobalOp>(gv).isDeclaration()) return; - assert(!cir::MissingFeatures::deferredDecls()); + // If we have not seen a reference to this variable yet, place it into the + // deferred declarations table to be emitted if needed later. + if (!mustBeEmitted(d) && !gv) { + deferredDecls[mangledName] = d; + return; + } // The tentative definition is the only definition. emitGlobalVarDefinition(d); } +bool CIRGenModule::mustBeEmitted(const ValueDecl *global) { + // Never defer when EmitAllDecls is specified. + if (langOpts.EmitAllDecls) + return true; + + const auto *vd = dyn_cast<VarDecl>(global); + if (vd && + ((codeGenOpts.KeepPersistentStorageVariables && + (vd->getStorageDuration() == SD_Static || + vd->getStorageDuration() == SD_Thread)) || + (codeGenOpts.KeepStaticConsts && vd->getStorageDuration() == SD_Static && + vd->getType().isConstQualified()))) + return true; + + // TODO(cir): We do want to defer function decls, but it's not implemented. + assert(!cir::MissingFeatures::deferredFuncDecls()); + if (isa<FunctionDecl>(global)) + return true; + + return getASTContext().DeclMustBeEmitted(global); +} + +bool CIRGenModule::mayBeEmittedEagerly(const ValueDecl *global) { + // In OpenMP 5.0 variables and function may be marked as + // device_type(host/nohost) and we should not emit them eagerly unless we sure + // that they must be emitted on the host/device. To be sure we need to have + // seen a declare target with an explicit mentioning of the function, we know + // we have if the level of the declare target attribute is -1. Note that we + // check somewhere else if we should emit this at all. + if (langOpts.OpenMP >= 50 && !langOpts.OpenMPSimd) { + std::optional<OMPDeclareTargetDeclAttr *> activeAttr = + OMPDeclareTargetDeclAttr::getActiveAttr(global); + if (!activeAttr || (*activeAttr)->getLevel() != (unsigned)-1) + return false; + } + + const auto *fd = dyn_cast<FunctionDecl>(global); + if (fd) { + // Implicit template instantiations may change linkage if they are later + // explicitly instantiated, so they should not be emitted eagerly. + if (fd->getTemplateSpecializationKind() == TSK_ImplicitInstantiation) + return false; + // Defer until all versions have been semantically checked. + if (fd->hasAttr<TargetVersionAttr>() && !fd->isMultiVersion()) + return false; + if (langOpts.SYCLIsDevice) { + errorNYI(fd->getSourceRange(), "mayBeEmittedEagerly: SYCL"); + return false; + } + } + const auto *vd = dyn_cast<VarDecl>(global); + if (vd) + if (astContext.getInlineVariableDefinitionKind(vd) == + ASTContext::InlineVariableDefinitionKind::WeakUnknown) + // A definition of an inline constexpr static data member may change + // linkage later if it's redeclared outside the class. + return false; + + // If OpenMP is enabled and threadprivates must be generated like TLS, delay + // codegen for global variables, because they may be marked as threadprivate. + if (langOpts.OpenMP && langOpts.OpenMPUseTLS && + astContext.getTargetInfo().isTLSSupported() && isa<VarDecl>(global) && + !global->getType().isConstantStorage(astContext, false, false) && + !OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration(global)) + return false; + + assert((fd || vd) && + "Only FunctionDecl and VarDecl should hit this path so far."); + return true; +} + static bool shouldAssumeDSOLocal(const CIRGenModule &cgm, cir::CIRGlobalValueInterface gv) { if (gv.hasLocalLinkage()) @@ -1394,6 +1599,13 @@ CIRGenModule::getGlobalVisibilityAttrFromDecl(const Decl *decl) { return cirVisibility; } +void CIRGenModule::release() { + emitDeferred(); + + // There's a lot of code that is not implemented yet. + assert(!cir::MissingFeatures::cgmRelease()); +} + mlir::Type CIRGenModule::convertType(QualType type) { return genTypes.convertType(type); } diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 234c5f1..7055170 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -167,8 +167,31 @@ public: clang::CharUnits getNaturalTypeAlignment(clang::QualType t, LValueBaseInfo *baseInfo); + /// This contains all the decls which have definitions but which are deferred + /// for emission and therefore should only be output if they are actually + /// used. If a decl is in this, then it is known to have not been referenced + /// yet. + std::map<llvm::StringRef, clang::GlobalDecl> deferredDecls; + + // This is a list of deferred decls which we have seen that *are* actually + // referenced. These get code generated when the module is done. + std::vector<clang::GlobalDecl> deferredDeclsToEmit; + void addDeferredDeclToEmit(clang::GlobalDecl GD) { + deferredDeclsToEmit.emplace_back(GD); + } + void emitTopLevelDecl(clang::Decl *decl); + /// Determine whether the definition must be emitted; if this returns \c + /// false, the definition can be emitted lazily if it's used. + bool mustBeEmitted(const clang::ValueDecl *d); + + /// Determine whether the definition can be emitted eagerly, or should be + /// delayed until the end of the translation unit. This is relevant for + /// definitions whose linkage can change, e.g. implicit function + /// instantiations which may later be explicitly instantiated. + bool mayBeEmittedEagerly(const clang::ValueDecl *d); + bool verifyModule() const; /// Return the address of the given function. If funcType is non-null, then @@ -179,6 +202,10 @@ public: bool forVTable = false, bool dontDefer = false, ForDefinition_t isForDefinition = NotForDefinition); + mlir::Operation * + getAddrOfGlobal(clang::GlobalDecl gd, + ForDefinition_t isForDefinition = NotForDefinition); + /// Emit code for a single global function or variable declaration. Forward /// declarations are emitted lazily. void emitGlobal(clang::GlobalDecl gd); @@ -234,8 +261,17 @@ public: return builder.getSizeFromCharUnits(size); } + /// Emit any needed decls for which code generation was deferred. + void emitDeferred(); + + /// Helper for `emitDeferred` to apply actual codegen. + void emitGlobalDecl(const clang::GlobalDecl &d); + const llvm::Triple &getTriple() const { return target.getTriple(); } + // Finalize CIR code generation. + void release(); + /// ------- /// Visibility and Linkage /// ------- diff --git a/clang/lib/CIR/CodeGen/CIRGenerator.cpp b/clang/lib/CIR/CodeGen/CIRGenerator.cpp index 512009a..0925ba6 100644 --- a/clang/lib/CIR/CodeGen/CIRGenerator.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenerator.cpp @@ -69,6 +69,16 @@ bool CIRGenerator::HandleTopLevelDecl(DeclGroupRef group) { return true; } +void CIRGenerator::HandleTranslationUnit(ASTContext &astContext) { + // Release the Builder when there is no error. + if (!diags.hasErrorOccurred() && cgm) + cgm->release(); + + // If there are errors before or when releasing the cgm, reset the module to + // stop here before invoking the backend. + assert(!cir::MissingFeatures::cleanupAfterErrorDiags()); +} + void CIRGenerator::HandleInlineFunctionDefinition(FunctionDecl *d) { if (diags.hasErrorOccurred()) return; |