diff options
Diffstat (limited to 'clang/lib/CIR/CodeGen/CIRGenModule.cpp')
| -rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenModule.cpp | 286 |
1 files changed, 282 insertions, 4 deletions
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 0ec5dcec9928..e5eae31e937d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -202,6 +202,104 @@ 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)) { + const CIRGenFunctionInfo &fi = + getTypes().arrangeCXXMethodDeclaration(cast<CXXMethodDecl>(d)); + cir::FuncType ty = getTypes().getFunctionType(fi); + return getAddrOfFunction(gd, ty, /*ForVTable=*/false, /*DontDefer=*/false, + isForDefinition); + } + + if (isa<FunctionDecl>(d)) { + const CIRGenFunctionInfo &fi = getTypes().arrangeGlobalDeclaration(gd); + cir::FuncType ty = getTypes().getFunctionType(fi); + return getAddrOfFunction(gd, ty, /*ForVTable=*/false, /*DontDefer=*/false, + isForDefinition); + } + + 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 +338,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 +525,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) @@ -704,6 +838,11 @@ void CIRGenModule::maybeSetTrivialComdat(const Decl &d, mlir::Operation *op) { assert(!cir::MissingFeatures::opFuncSetComdat()); } +void CIRGenModule::updateCompletedType(const TagDecl *td) { + // Make sure that this type is translated. + genTypes.updateCompletedType(td); +} + // TODO(CIR): this could be a common method between LLVM codegen. static bool isVarDeclStrongDefinition(const ASTContext &astContext, CIRGenModule &cgm, const VarDecl *vd, @@ -1001,19 +1140,30 @@ void CIRGenModule::emitTopLevelDecl(Decl *decl) { emitGlobalOpenACCDecl(cast<OpenACCDeclareDecl>(decl)); break; case Decl::Enum: + case Decl::Using: // using X; [C++] case Decl::UsingDirective: // using namespace X; [C++] case Decl::Typedef: case Decl::TypeAlias: // using foo = bar; [C++11] case Decl::Record: - case Decl::CXXRecord: assert(!cir::MissingFeatures::generateDebugInfo()); break; + // No code generation needed. + case Decl::UsingShadow: + case Decl::Empty: + break; + // C++ Decls case Decl::LinkageSpec: case Decl::Namespace: emitDeclContext(Decl::castToDeclContext(decl)); break; + + case Decl::ClassTemplateSpecialization: + case Decl::CXXRecord: + assert(!cir::MissingFeatures::generateDebugInfo()); + assert(!cir::MissingFeatures::cxxRecordStaticMembers()); + break; } } @@ -1121,12 +1271,83 @@ 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; + + 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()) @@ -1318,6 +1539,56 @@ cir::FuncOp CIRGenModule::getOrCreateCIRFunction( cir::FuncOp funcOp = createCIRFunction( invalidLoc ? theModule->getLoc() : getLoc(funcDecl->getSourceRange()), mangledName, mlir::cast<cir::FuncType>(funcType), funcDecl); + + // 'dontDefer' actually means don't move this to the deferredDeclsToEmit list. + if (dontDefer) { + // TODO(cir): This assertion will need an additional condition when we + // support incomplete functions. + assert(funcOp.getFunctionType() == funcType); + return funcOp; + } + + // All MSVC dtors other than the base dtor are linkonce_odr and delegate to + // each other bottoming out wiht the base dtor. Therefore we emit non-base + // dtors on usage, even if there is no dtor definition in the TU. + if (isa_and_nonnull<CXXDestructorDecl>(d)) + errorNYI(d->getSourceRange(), "getOrCreateCIRFunction: dtor"); + + // 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); + + // Otherwise, there are cases we have to worry about where we're using a + // declaration for which we must emit a definition but where we might not + // find a top-level definition. + // - member functions defined inline in their classes + // - friend functions defined inline in some class + // - special member functions with implicit definitions + // If we ever change our AST traversal to walk into class methods, this + // will be unnecessary. + // + // We also don't emit a definition for a function if it's going to be an + // entry in a vtable, unless it's already marked as used. + } else if (getLangOpts().CPlusPlus && d) { + // Look for a declaration that's lexically in a record. + for (const auto *fd = cast<FunctionDecl>(d)->getMostRecentDecl(); fd; + fd = fd->getPreviousDecl()) { + if (isa<CXXRecordDecl>(fd->getLexicalDeclContext())) { + if (fd->doesThisDeclarationHaveABody()) { + addDeferredDeclToEmit(gd.getWithDecl(fd)); + break; + } + } + } + } + return funcOp; } @@ -1394,6 +1665,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); } |
