| //===-LTO.cpp - LLVM Link Time Optimizer ----------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file implements functions and classes used to support LTO. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/LTO/LTO.h" | |
| #include "llvm/ADT/ArrayRef.h" | |
| #include "llvm/ADT/ScopeExit.h" | |
| #include "llvm/ADT/SmallSet.h" | |
| #include "llvm/ADT/StableHashing.h" | |
| #include "llvm/ADT/Statistic.h" | |
| #include "llvm/ADT/StringExtras.h" | |
| #include "llvm/Analysis/OptimizationRemarkEmitter.h" | |
| #include "llvm/Analysis/StackSafetyAnalysis.h" | |
| #include "llvm/Analysis/TargetTransformInfo.h" | |
| #include "llvm/Bitcode/BitcodeReader.h" | |
| #include "llvm/Bitcode/BitcodeWriter.h" | |
| #include "llvm/CGData/CodeGenData.h" | |
| #include "llvm/CodeGen/Analysis.h" | |
| #include "llvm/Config/llvm-config.h" | |
| #include "llvm/IR/AutoUpgrade.h" | |
| #include "llvm/IR/DiagnosticPrinter.h" | |
| #include "llvm/IR/GlobalValue.h" | |
| #include "llvm/IR/Intrinsics.h" | |
| #include "llvm/IR/LLVMRemarkStreamer.h" | |
| #include "llvm/IR/LegacyPassManager.h" | |
| #include "llvm/IR/Mangler.h" | |
| #include "llvm/IR/Metadata.h" | |
| #include "llvm/IR/RuntimeLibcalls.h" | |
| #include "llvm/LTO/LTOBackend.h" | |
| #include "llvm/Linker/IRMover.h" | |
| #include "llvm/MC/TargetRegistry.h" | |
| #include "llvm/Object/IRObjectFile.h" | |
| #include "llvm/Support/Caching.h" | |
| #include "llvm/Support/CommandLine.h" | |
| #include "llvm/Support/Compiler.h" | |
| #include "llvm/Support/Error.h" | |
| #include "llvm/Support/FileSystem.h" | |
| #include "llvm/Support/MemoryBuffer.h" | |
| #include "llvm/Support/Path.h" | |
| #include "llvm/Support/Process.h" | |
| #include "llvm/Support/SHA1.h" | |
| #include "llvm/Support/Signals.h" | |
| #include "llvm/Support/SourceMgr.h" | |
| #include "llvm/Support/ThreadPool.h" | |
| #include "llvm/Support/Threading.h" | |
| #include "llvm/Support/TimeProfiler.h" | |
| #include "llvm/Support/ToolOutputFile.h" | |
| #include "llvm/Support/VCSRevision.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include "llvm/Target/TargetOptions.h" | |
| #include "llvm/Transforms/IPO.h" | |
| #include "llvm/Transforms/IPO/MemProfContextDisambiguation.h" | |
| #include "llvm/Transforms/IPO/WholeProgramDevirt.h" | |
| #include "llvm/Transforms/Utils/FunctionImportUtils.h" | |
| #include "llvm/Transforms/Utils/SplitModule.h" | |
| #include <optional> | |
| #include <set> | |
| using namespace llvm; | |
| using namespace lto; | |
| using namespace object; | |
| #define DEBUG_TYPE "lto" | |
| Error LTO::setupOptimizationRemarks() { | |
| // Setup the remark streamer according to the provided configuration. | |
| auto DiagFileOrErr = lto::setupLLVMOptimizationRemarks( | |
| RegularLTO.Ctx, Conf.RemarksFilename, Conf.RemarksPasses, | |
| Conf.RemarksFormat, Conf.RemarksWithHotness, | |
| Conf.RemarksHotnessThreshold); | |
| if (!DiagFileOrErr) | |
| return DiagFileOrErr.takeError(); | |
| DiagnosticOutputFile = std::move(*DiagFileOrErr); | |
| // Create a dummy function to serve as a context for LTO-link remarks. | |
| // This is required because OptimizationRemark requires a valid Function, | |
| // and in ThinLTO we may not have any IR functions available during the | |
| // thin link. Host it in a private module to avoid interfering with the LTO | |
| // process. | |
| if (!LinkerRemarkFunction) { | |
| DummyModule = std::make_unique<Module>("remark_dummy", RegularLTO.Ctx); | |
| LinkerRemarkFunction = Function::Create( | |
| FunctionType::get(Type::getVoidTy(RegularLTO.Ctx), false), | |
| GlobalValue::ExternalLinkage, "thinlto_remark_dummy", | |
| DummyModule.get()); | |
| } | |
| return Error::success(); | |
| } | |
| void LTO::emitRemark(OptimizationRemark &Remark) { | |
| const Function &F = Remark.getFunction(); | |
| OptimizationRemarkEmitter ORE(const_cast<Function *>(&F)); | |
| ORE.emit(Remark); | |
| } | |
| static cl::opt<bool> | |
| DumpThinCGSCCs("dump-thin-cg-sccs", cl::init(false), cl::Hidden, | |
| cl::desc("Dump the SCCs in the ThinLTO index's callgraph")); | |
| namespace llvm { | |
| extern cl::opt<bool> ForceImportAll; | |
| extern cl::opt<bool> AlwaysRenamePromotedLocals; | |
| } // end namespace llvm | |
| namespace llvm { | |
| /// Enable global value internalization in LTO. | |
| cl::opt<bool> EnableLTOInternalization( | |
| "enable-lto-internalization", cl::init(true), cl::Hidden, | |
| cl::desc("Enable global value internalization in LTO")); | |
| static cl::opt<bool> | |
| LTOKeepSymbolCopies("lto-keep-symbol-copies", cl::init(false), cl::Hidden, | |
| cl::desc("Keep copies of symbols in LTO indexing")); | |
| /// Indicate we are linking with an allocator that supports hot/cold operator | |
| /// new interfaces. | |
| extern cl::opt<bool> SupportsHotColdNew; | |
| /// Enable MemProf context disambiguation for thin link. | |
| extern cl::opt<bool> EnableMemProfContextDisambiguation; | |
| } // namespace llvm | |
| // Computes a unique hash for the Module considering the current list of | |
| // export/import and other global analysis results. | |
| // Returns the hash in its hexadecimal representation. | |
| std::string llvm::computeLTOCacheKey( | |
| const Config &Conf, const ModuleSummaryIndex &Index, StringRef ModuleID, | |
| const FunctionImporter::ImportMapTy &ImportList, | |
| const FunctionImporter::ExportSetTy &ExportList, | |
| const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR, | |
| const GVSummaryMapTy &DefinedGlobals, | |
| const DenseSet<GlobalValue::GUID> &CfiFunctionDefs, | |
| const DenseSet<GlobalValue::GUID> &CfiFunctionDecls) { | |
| // Compute the unique hash for this entry. | |
| // This is based on the current compiler version, the module itself, the | |
| // export list, the hash for every single module in the import list, the | |
| // list of ResolvedODR for the module, and the list of preserved symbols. | |
| SHA1 Hasher; | |
| // Start with the compiler revision | |
| Hasher.update(LLVM_VERSION_STRING); | |
| #ifdef LLVM_REVISION | |
| Hasher.update(LLVM_REVISION); | |
| #endif | |
| // Include the parts of the LTO configuration that affect code generation. | |
| auto AddString = [&](StringRef Str) { | |
| Hasher.update(Str); | |
| Hasher.update(ArrayRef<uint8_t>{0}); | |
| }; | |
| auto AddUnsigned = [&](unsigned I) { | |
| uint8_t Data[4]; | |
| support::endian::write32le(Data, I); | |
| Hasher.update(Data); | |
| }; | |
| auto AddUint64 = [&](uint64_t I) { | |
| uint8_t Data[8]; | |
| support::endian::write64le(Data, I); | |
| Hasher.update(Data); | |
| }; | |
| auto AddUint8 = [&](const uint8_t I) { | |
| Hasher.update(ArrayRef<uint8_t>(&I, 1)); | |
| }; | |
| AddString(Conf.CPU); | |
| // FIXME: Hash more of Options. For now all clients initialize Options from | |
| // command-line flags (which is unsupported in production), but may set | |
| // X86RelaxRelocations. The clang driver can also pass FunctionSections, | |
| // DataSections and DebuggerTuning via command line flags. | |
| AddUnsigned(Conf.Options.MCOptions.X86RelaxRelocations); | |
| AddUnsigned(Conf.Options.FunctionSections); | |
| AddUnsigned(Conf.Options.DataSections); | |
| AddUnsigned((unsigned)Conf.Options.DebuggerTuning); | |
| for (auto &A : Conf.MAttrs) | |
| AddString(A); | |
| if (Conf.RelocModel) | |
| AddUnsigned(*Conf.RelocModel); | |
| else | |
| AddUnsigned(-1); | |
| if (Conf.CodeModel) | |
| AddUnsigned(*Conf.CodeModel); | |
| else | |
| AddUnsigned(-1); | |
| for (const auto &S : Conf.MllvmArgs) | |
| AddString(S); | |
| AddUnsigned(static_cast<int>(Conf.CGOptLevel)); | |
| AddUnsigned(static_cast<int>(Conf.CGFileType)); | |
| AddUnsigned(Conf.OptLevel); | |
| AddUnsigned(Conf.Freestanding); | |
| AddString(Conf.OptPipeline); | |
| AddString(Conf.AAPipeline); | |
| AddString(Conf.OverrideTriple); | |
| AddString(Conf.DefaultTriple); | |
| AddString(Conf.DwoDir); | |
| AddUint8(Conf.Dtlto); | |
| // Include the hash for the current module | |
| auto ModHash = Index.getModuleHash(ModuleID); | |
| Hasher.update(ArrayRef<uint8_t>((uint8_t *)&ModHash[0], sizeof(ModHash))); | |
| // TODO: `ExportList` is determined by `ImportList`. Since `ImportList` is | |
| // used to compute cache key, we could omit hashing `ExportList` here. | |
| std::vector<uint64_t> ExportsGUID; | |
| ExportsGUID.reserve(ExportList.size()); | |
| for (const auto &VI : ExportList) | |
| ExportsGUID.push_back(VI.getGUID()); | |
| // Sort the export list elements GUIDs. | |
| llvm::sort(ExportsGUID); | |
| for (auto GUID : ExportsGUID) | |
| Hasher.update(ArrayRef<uint8_t>((uint8_t *)&GUID, sizeof(GUID))); | |
| // Order using module hash, to be both independent of module name and | |
| // module order. | |
| auto Comp = [&](const std::pair<StringRef, GlobalValue::GUID> &L, | |
| const std::pair<StringRef, GlobalValue::GUID> &R) { | |
| return std::make_pair(Index.getModule(L.first)->second, L.second) < | |
| std::make_pair(Index.getModule(R.first)->second, R.second); | |
| }; | |
| FunctionImporter::SortedImportList SortedImportList(ImportList, Comp); | |
| // Count the number of imports for each source module. | |
| DenseMap<StringRef, unsigned> ModuleToNumImports; | |
| for (const auto &[FromModule, GUID, Type] : SortedImportList) | |
| ++ModuleToNumImports[FromModule]; | |
| std::optional<StringRef> LastModule; | |
| for (const auto &[FromModule, GUID, Type] : SortedImportList) { | |
| if (LastModule != FromModule) { | |
| // Include the hash for every module we import functions from. The set of | |
| // imported symbols for each module may affect code generation and is | |
| // sensitive to link order, so include that as well. | |
| LastModule = FromModule; | |
| auto ModHash = Index.getModule(FromModule)->second; | |
| Hasher.update(ArrayRef<uint8_t>((uint8_t *)&ModHash[0], sizeof(ModHash))); | |
| AddUint64(ModuleToNumImports[FromModule]); | |
| } | |
| AddUint64(GUID); | |
| AddUint8(Type); | |
| } | |
| // Include the hash for the resolved ODR. | |
| for (auto &Entry : ResolvedODR) { | |
| Hasher.update(ArrayRef<uint8_t>((const uint8_t *)&Entry.first, | |
| sizeof(GlobalValue::GUID))); | |
| Hasher.update(ArrayRef<uint8_t>((const uint8_t *)&Entry.second, | |
| sizeof(GlobalValue::LinkageTypes))); | |
| } | |
| // Members of CfiFunctionDefs and CfiFunctionDecls that are referenced or | |
| // defined in this module. | |
| std::set<GlobalValue::GUID> UsedCfiDefs; | |
| std::set<GlobalValue::GUID> UsedCfiDecls; | |
| // Typeids used in this module. | |
| std::set<GlobalValue::GUID> UsedTypeIds; | |
| auto AddUsedCfiGlobal = [&](GlobalValue::GUID ValueGUID) { | |
| if (CfiFunctionDefs.contains(ValueGUID)) | |
| UsedCfiDefs.insert(ValueGUID); | |
| if (CfiFunctionDecls.contains(ValueGUID)) | |
| UsedCfiDecls.insert(ValueGUID); | |
| }; | |
| auto AddUsedThings = [&](GlobalValueSummary *GS) { | |
| if (!GS) return; | |
| AddUnsigned(GS->getVisibility()); | |
| AddUnsigned(GS->isLive()); | |
| AddUnsigned(GS->canAutoHide()); | |
| for (const ValueInfo &VI : GS->refs()) { | |
| AddUnsigned(VI.isDSOLocal(Index.withDSOLocalPropagation())); | |
| AddUsedCfiGlobal(VI.getGUID()); | |
| } | |
| if (auto *GVS = dyn_cast<GlobalVarSummary>(GS)) { | |
| AddUnsigned(GVS->maybeReadOnly()); | |
| AddUnsigned(GVS->maybeWriteOnly()); | |
| } | |
| if (auto *FS = dyn_cast<FunctionSummary>(GS)) { | |
| for (auto &TT : FS->type_tests()) | |
| UsedTypeIds.insert(TT); | |
| for (auto &TT : FS->type_test_assume_vcalls()) | |
| UsedTypeIds.insert(TT.GUID); | |
| for (auto &TT : FS->type_checked_load_vcalls()) | |
| UsedTypeIds.insert(TT.GUID); | |
| for (auto &TT : FS->type_test_assume_const_vcalls()) | |
| UsedTypeIds.insert(TT.VFunc.GUID); | |
| for (auto &TT : FS->type_checked_load_const_vcalls()) | |
| UsedTypeIds.insert(TT.VFunc.GUID); | |
| for (auto &ET : FS->calls()) { | |
| AddUnsigned(ET.first.isDSOLocal(Index.withDSOLocalPropagation())); | |
| AddUsedCfiGlobal(ET.first.getGUID()); | |
| } | |
| } | |
| }; | |
| // Sort the defined globals by GUID to be independent of the insertion order, | |
| // which may depend on the order that modules are added. | |
| SmallVector<std::pair<GlobalValue::GUID, GlobalValueSummary *>> | |
| SortedDefinedGlobals(DefinedGlobals.begin(), DefinedGlobals.end()); | |
| llvm::sort(SortedDefinedGlobals, llvm::less_first()); | |
| for (auto &GS : SortedDefinedGlobals) { | |
| // Include the hash for the linkage type to reflect internalization and weak | |
| // resolution, and collect any used type identifier resolutions. | |
| GlobalValue::LinkageTypes Linkage = GS.second->linkage(); | |
| Hasher.update( | |
| ArrayRef<uint8_t>((const uint8_t *)&Linkage, sizeof(Linkage))); | |
| AddUsedCfiGlobal(GS.first); | |
| AddUsedThings(GS.second); | |
| } | |
| // Imported functions may introduce new uses of type identifier resolutions, | |
| // so we need to collect their used resolutions as well. | |
| for (const auto &[FromModule, GUID, Type] : SortedImportList) { | |
| GlobalValueSummary *S = Index.findSummaryInModule(GUID, FromModule); | |
| AddUsedThings(S); | |
| // If this is an alias, we also care about any types/etc. that the aliasee | |
| // may reference. | |
| if (auto *AS = dyn_cast_or_null<AliasSummary>(S)) | |
| AddUsedThings(AS->getBaseObject()); | |
| } | |
| auto AddTypeIdSummary = [&](StringRef TId, const TypeIdSummary &S) { | |
| AddString(TId); | |
| AddUnsigned(S.TTRes.TheKind); | |
| AddUnsigned(S.TTRes.SizeM1BitWidth); | |
| AddUint64(S.TTRes.AlignLog2); | |
| AddUint64(S.TTRes.SizeM1); | |
| AddUint64(S.TTRes.BitMask); | |
| AddUint64(S.TTRes.InlineBits); | |
| AddUint64(S.WPDRes.size()); | |
| for (auto &WPD : S.WPDRes) { | |
| AddUnsigned(WPD.first); | |
| AddUnsigned(WPD.second.TheKind); | |
| AddString(WPD.second.SingleImplName); | |
| AddUint64(WPD.second.ResByArg.size()); | |
| for (auto &ByArg : WPD.second.ResByArg) { | |
| AddUint64(ByArg.first.size()); | |
| for (uint64_t Arg : ByArg.first) | |
| AddUint64(Arg); | |
| AddUnsigned(ByArg.second.TheKind); | |
| AddUint64(ByArg.second.Info); | |
| AddUnsigned(ByArg.second.Byte); | |
| AddUnsigned(ByArg.second.Bit); | |
| } | |
| } | |
| }; | |
| // Include the hash for all type identifiers used by this module. | |
| for (GlobalValue::GUID TId : UsedTypeIds) { | |
| auto TidIter = Index.typeIds().equal_range(TId); | |
| for (const auto &I : make_range(TidIter)) | |
| AddTypeIdSummary(I.second.first, I.second.second); | |
| } | |
| AddUnsigned(UsedCfiDefs.size()); | |
| for (auto &V : UsedCfiDefs) | |
| AddUint64(V); | |
| AddUnsigned(UsedCfiDecls.size()); | |
| for (auto &V : UsedCfiDecls) | |
| AddUint64(V); | |
| if (!Conf.SampleProfile.empty()) { | |
| auto FileOrErr = MemoryBuffer::getFile(Conf.SampleProfile); | |
| if (FileOrErr) { | |
| Hasher.update(FileOrErr.get()->getBuffer()); | |
| if (!Conf.ProfileRemapping.empty()) { | |
| FileOrErr = MemoryBuffer::getFile(Conf.ProfileRemapping); | |
| if (FileOrErr) | |
| Hasher.update(FileOrErr.get()->getBuffer()); | |
| } | |
| } | |
| } | |
| return toHex(Hasher.result()); | |
| } | |
| std::string llvm::recomputeLTOCacheKey(const std::string &Key, | |
| StringRef ExtraID) { | |
| SHA1 Hasher; | |
| auto AddString = [&](StringRef Str) { | |
| Hasher.update(Str); | |
| Hasher.update(ArrayRef<uint8_t>{0}); | |
| }; | |
| AddString(Key); | |
| AddString(ExtraID); | |
| return toHex(Hasher.result()); | |
| } | |
| static void thinLTOResolvePrevailingGUID( | |
| const Config &C, ValueInfo VI, | |
| DenseSet<GlobalValueSummary *> &GlobalInvolvedWithAlias, | |
| function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)> | |
| isPrevailing, | |
| function_ref<void(StringRef, GlobalValue::GUID, GlobalValue::LinkageTypes)> | |
| recordNewLinkage, | |
| const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols) { | |
| GlobalValue::VisibilityTypes Visibility = | |
| C.VisibilityScheme == Config::ELF ? VI.getELFVisibility() | |
| : GlobalValue::DefaultVisibility; | |
| for (auto &S : VI.getSummaryList()) { | |
| GlobalValue::LinkageTypes OriginalLinkage = S->linkage(); | |
| // Ignore local and appending linkage values since the linker | |
| // doesn't resolve them. | |
| if (GlobalValue::isLocalLinkage(OriginalLinkage) || | |
| GlobalValue::isAppendingLinkage(S->linkage())) | |
| continue; | |
| // We need to emit only one of these. The prevailing module will keep it, | |
| // but turned into a weak, while the others will drop it when possible. | |
| // This is both a compile-time optimization and a correctness | |
| // transformation. This is necessary for correctness when we have exported | |
| // a reference - we need to convert the linkonce to weak to | |
| // ensure a copy is kept to satisfy the exported reference. | |
| // FIXME: We may want to split the compile time and correctness | |
| // aspects into separate routines. | |
| if (isPrevailing(VI.getGUID(), S.get())) { | |
| assert(!S->wasPromoted() && | |
| "promoted symbols used to be internal linkage and shouldn't have " | |
| "a prevailing variant"); | |
| if (GlobalValue::isLinkOnceLinkage(OriginalLinkage)) { | |
| S->setLinkage(GlobalValue::getWeakLinkage( | |
| GlobalValue::isLinkOnceODRLinkage(OriginalLinkage))); | |
| // The kept copy is eligible for auto-hiding (hidden visibility) if all | |
| // copies were (i.e. they were all linkonce_odr global unnamed addr). | |
| // If any copy is not (e.g. it was originally weak_odr), then the symbol | |
| // must remain externally available (e.g. a weak_odr from an explicitly | |
| // instantiated template). Additionally, if it is in the | |
| // GUIDPreservedSymbols set, that means that it is visibile outside | |
| // the summary (e.g. in a native object or a bitcode file without | |
| // summary), and in that case we cannot hide it as it isn't possible to | |
| // check all copies. | |
| S->setCanAutoHide(VI.canAutoHide() && | |
| !GUIDPreservedSymbols.count(VI.getGUID())); | |
| } | |
| if (C.VisibilityScheme == Config::FromPrevailing) | |
| Visibility = S->getVisibility(); | |
| } | |
| // Alias and aliasee can't be turned into available_externally. | |
| // When force-import-all is used, it indicates that object linking is not | |
| // supported by the target. In this case, we can't change the linkage as | |
| // well in case the global is converted to declaration. | |
| // Also, if the symbol was promoted, it wouldn't have a prevailing variant, | |
| // but also its linkage is set correctly (to External) already. | |
| else if (!isa<AliasSummary>(S.get()) && | |
| !GlobalInvolvedWithAlias.count(S.get()) && !ForceImportAll && | |
| !S->wasPromoted()) | |
| S->setLinkage(GlobalValue::AvailableExternallyLinkage); | |
| // For ELF, set visibility to the computed visibility from summaries. We | |
| // don't track visibility from declarations so this may be more relaxed than | |
| // the most constraining one. | |
| if (C.VisibilityScheme == Config::ELF) | |
| S->setVisibility(Visibility); | |
| if (S->linkage() != OriginalLinkage) | |
| recordNewLinkage(S->modulePath(), VI.getGUID(), S->linkage()); | |
| } | |
| if (C.VisibilityScheme == Config::FromPrevailing) { | |
| for (auto &S : VI.getSummaryList()) { | |
| GlobalValue::LinkageTypes OriginalLinkage = S->linkage(); | |
| if (GlobalValue::isLocalLinkage(OriginalLinkage) || | |
| GlobalValue::isAppendingLinkage(S->linkage())) | |
| continue; | |
| S->setVisibility(Visibility); | |
| } | |
| } | |
| } | |
| /// Resolve linkage for prevailing symbols in the \p Index. | |
| // | |
| // We'd like to drop these functions if they are no longer referenced in the | |
| // current module. However there is a chance that another module is still | |
| // referencing them because of the import. We make sure we always emit at least | |
| // one copy. | |
| void llvm::thinLTOResolvePrevailingInIndex( | |
| const Config &C, ModuleSummaryIndex &Index, | |
| function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)> | |
| isPrevailing, | |
| function_ref<void(StringRef, GlobalValue::GUID, GlobalValue::LinkageTypes)> | |
| recordNewLinkage, | |
| const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols) { | |
| // We won't optimize the globals that are referenced by an alias for now | |
| // Ideally we should turn the alias into a global and duplicate the definition | |
| // when needed. | |
| DenseSet<GlobalValueSummary *> GlobalInvolvedWithAlias; | |
| for (auto &I : Index) | |
| for (auto &S : I.second.getSummaryList()) | |
| if (auto AS = dyn_cast<AliasSummary>(S.get())) | |
| GlobalInvolvedWithAlias.insert(&AS->getAliasee()); | |
| for (auto &I : Index) | |
| thinLTOResolvePrevailingGUID(C, Index.getValueInfo(I), | |
| GlobalInvolvedWithAlias, isPrevailing, | |
| recordNewLinkage, GUIDPreservedSymbols); | |
| } | |
| static void thinLTOInternalizeAndPromoteGUID( | |
| ValueInfo VI, function_ref<bool(StringRef, ValueInfo)> isExported, | |
| function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)> | |
| isPrevailing, | |
| DenseSet<StringRef> *ExternallyVisibleSymbolNamesPtr) { | |
| // Before performing index-based internalization and promotion for this GUID, | |
| // the local flag should be consistent with the summary list linkage types. | |
| VI.verifyLocal(); | |
| const bool SingleExternallyVisibleCopy = | |
| VI.getSummaryList().size() == 1 && | |
| !GlobalValue::isLocalLinkage(VI.getSummaryList().front()->linkage()); | |
| bool NameRecorded = false; | |
| for (auto &S : VI.getSummaryList()) { | |
| // First see if we need to promote an internal value because it is not | |
| // exported. | |
| if (isExported(S->modulePath(), VI)) { | |
| if (GlobalValue::isLocalLinkage(S->linkage())) { | |
| // Only the first local GlobalValue in a list of summaries does not | |
| // need renaming. In rare cases if there exist more than one summaries | |
| // in the list, the rest of them must have renaming (through promotion) | |
| // to avoid conflict. | |
| if (ExternallyVisibleSymbolNamesPtr && !NameRecorded) { | |
| NameRecorded = true; | |
| if (ExternallyVisibleSymbolNamesPtr->insert(VI.name()).second) | |
| S->setNoRenameOnPromotion(true); | |
| } | |
| S->promote(); | |
| } | |
| continue; | |
| } | |
| // Otherwise, see if we can internalize. | |
| if (!EnableLTOInternalization) | |
| continue; | |
| // Non-exported values with external linkage can be internalized. | |
| if (GlobalValue::isExternalLinkage(S->linkage())) { | |
| S->setLinkage(GlobalValue::InternalLinkage); | |
| continue; | |
| } | |
| // Non-exported function and variable definitions with a weak-for-linker | |
| // linkage can be internalized in certain cases. The minimum legality | |
| // requirements would be that they are not address taken to ensure that we | |
| // don't break pointer equality checks, and that variables are either read- | |
| // or write-only. For functions, this is the case if either all copies are | |
| // [local_]unnamed_addr, or we can propagate reference edge attributes | |
| // (which is how this is guaranteed for variables, when analyzing whether | |
| // they are read or write-only). | |
| // | |
| // However, we only get to this code for weak-for-linkage values in one of | |
| // two cases: | |
| // 1) The prevailing copy is not in IR (it is in native code). | |
| // 2) The prevailing copy in IR is not exported from its module. | |
| // Additionally, at least for the new LTO API, case 2 will only happen if | |
| // there is exactly one definition of the value (i.e. in exactly one | |
| // module), as duplicate defs are result in the value being marked exported. | |
| // Likely, users of the legacy LTO API are similar, however, currently there | |
| // are llvm-lto based tests of the legacy LTO API that do not mark | |
| // duplicate linkonce_odr copies as exported via the tool, so we need | |
| // to handle that case below by checking the number of copies. | |
| // | |
| // Generally, we only want to internalize a weak-for-linker value in case | |
| // 2, because in case 1 we cannot see how the value is used to know if it | |
| // is read or write-only. We also don't want to bloat the binary with | |
| // multiple internalized copies of non-prevailing linkonce/weak functions. | |
| // Note if we don't internalize, we will convert non-prevailing copies to | |
| // available_externally anyway, so that we drop them after inlining. The | |
| // only reason to internalize such a function is if we indeed have a single | |
| // copy, because internalizing it won't increase binary size, and enables | |
| // use of inliner heuristics that are more aggressive in the face of a | |
| // single call to a static (local). For variables, internalizing a read or | |
| // write only variable can enable more aggressive optimization. However, we | |
| // already perform this elsewhere in the ThinLTO backend handling for | |
| // read or write-only variables (processGlobalForThinLTO). | |
| // | |
| // Therefore, only internalize linkonce/weak if there is a single copy, that | |
| // is prevailing in this IR module. We can do so aggressively, without | |
| // requiring the address to be insignificant, or that a variable be read or | |
| // write-only. | |
| if (!GlobalValue::isWeakForLinker(S->linkage()) || | |
| GlobalValue::isExternalWeakLinkage(S->linkage())) | |
| continue; | |
| // We may have a single summary copy that is externally visible but not | |
| // prevailing if the prevailing copy is in a native object. | |
| if (SingleExternallyVisibleCopy && isPrevailing(VI.getGUID(), S.get())) | |
| S->setLinkage(GlobalValue::InternalLinkage); | |
| } | |
| } | |
| // Update the linkages in the given \p Index to mark exported values | |
| // as external and non-exported values as internal. | |
| void llvm::thinLTOInternalizeAndPromoteInIndex( | |
| ModuleSummaryIndex &Index, | |
| function_ref<bool(StringRef, ValueInfo)> isExported, | |
| function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)> | |
| isPrevailing, | |
| DenseSet<StringRef> *ExternallyVisibleSymbolNamesPtr) { | |
| assert(!Index.withInternalizeAndPromote()); | |
| for (auto &I : Index) | |
| thinLTOInternalizeAndPromoteGUID(Index.getValueInfo(I), isExported, | |
| isPrevailing, | |
| ExternallyVisibleSymbolNamesPtr); | |
| Index.setWithInternalizeAndPromote(); | |
| } | |
| // Requires a destructor for std::vector<InputModule>. | |
| InputFile::~InputFile() = default; | |
| Expected<std::unique_ptr<InputFile>> InputFile::create(MemoryBufferRef Object) { | |
| std::unique_ptr<InputFile> File(new InputFile); | |
| Expected<IRSymtabFile> FOrErr = readIRSymtab(Object); | |
| if (!FOrErr) | |
| return FOrErr.takeError(); | |
| File->TargetTriple = FOrErr->TheReader.getTargetTriple(); | |
| File->SourceFileName = FOrErr->TheReader.getSourceFileName(); | |
| File->COFFLinkerOpts = FOrErr->TheReader.getCOFFLinkerOpts(); | |
| File->DependentLibraries = FOrErr->TheReader.getDependentLibraries(); | |
| File->ComdatTable = FOrErr->TheReader.getComdatTable(); | |
| File->MbRef = | |
| Object; // Save a memory buffer reference to an input file object. | |
| for (unsigned I = 0; I != FOrErr->Mods.size(); ++I) { | |
| size_t Begin = File->Symbols.size(); | |
| for (const irsymtab::Reader::SymbolRef &Sym : | |
| FOrErr->TheReader.module_symbols(I)) | |
| // Skip symbols that are irrelevant to LTO. Note that this condition needs | |
| // to match the one in Skip() in LTO::addRegularLTO(). | |
| if (Sym.isGlobal() && !Sym.isFormatSpecific()) | |
| File->Symbols.push_back(Sym); | |
| File->ModuleSymIndices.push_back({Begin, File->Symbols.size()}); | |
| } | |
| File->Mods = FOrErr->Mods; | |
| File->Strtab = std::move(FOrErr->Strtab); | |
| return std::move(File); | |
| } | |
| bool InputFile::Symbol::isLibcall( | |
| const TargetLibraryInfo &TLI, | |
| const RTLIB::RuntimeLibcallsInfo &Libcalls) const { | |
| if (TLI.has(TLI.getLibFunc(IRName))) | |
| return true; | |
| return Libcalls.getSupportedLibcallImpl(IRName) != RTLIB::Unsupported; | |
| } | |
| StringRef InputFile::getName() const { | |
| return Mods[0].getModuleIdentifier(); | |
| } | |
| BitcodeModule &InputFile::getSingleBitcodeModule() { | |
| assert(Mods.size() == 1 && "Expect only one bitcode module"); | |
| return Mods[0]; | |
| } | |
| BitcodeModule &InputFile::getPrimaryBitcodeModule() { return Mods[0]; } | |
| LTO::RegularLTOState::RegularLTOState(unsigned ParallelCodeGenParallelismLevel, | |
| const Config &Conf) | |
| : ParallelCodeGenParallelismLevel(ParallelCodeGenParallelismLevel), | |
| Ctx(Conf), CombinedModule(std::make_unique<Module>("ld-temp.o", Ctx)), | |
| Mover(std::make_unique<IRMover>(*CombinedModule)) {} | |
| LTO::ThinLTOState::ThinLTOState(ThinBackend BackendParam) | |
| : Backend(std::move(BackendParam)), CombinedIndex(/*HaveGVs*/ false) { | |
| if (!Backend.isValid()) | |
| Backend = | |
| createInProcessThinBackend(llvm::heavyweight_hardware_concurrency()); | |
| } | |
| LTO::LTO(Config Conf, ThinBackend Backend, | |
| unsigned ParallelCodeGenParallelismLevel, LTOKind LTOMode) | |
| : Conf(std::move(Conf)), | |
| RegularLTO(ParallelCodeGenParallelismLevel, this->Conf), | |
| ThinLTO(std::move(Backend)), | |
| GlobalResolutions( | |
| std::make_unique<DenseMap<StringRef, GlobalResolution>>()), | |
| LTOMode(LTOMode) { | |
| if (Conf.KeepSymbolNameCopies || LTOKeepSymbolCopies) { | |
| Alloc = std::make_unique<BumpPtrAllocator>(); | |
| GlobalResolutionSymbolSaver = std::make_unique<llvm::StringSaver>(*Alloc); | |
| } | |
| } | |
| // Requires a destructor for MapVector<BitcodeModule>. | |
| LTO::~LTO() = default; | |
| void LTO::cleanup() { | |
| DummyModule.reset(); | |
| LinkerRemarkFunction = nullptr; | |
| consumeError(finalizeOptimizationRemarks(std::move(DiagnosticOutputFile))); | |
| } | |
| // Add the symbols in the given module to the GlobalResolutions map, and resolve | |
| // their partitions. | |
| void LTO::addModuleToGlobalRes(ArrayRef<InputFile::Symbol> Syms, | |
| ArrayRef<SymbolResolution> Res, | |
| unsigned Partition, bool InSummary, | |
| const Triple &TT) { | |
| llvm::TimeTraceScope timeScope("LTO add module to global resolution"); | |
| auto *ResI = Res.begin(); | |
| auto *ResE = Res.end(); | |
| (void)ResE; | |
| RTLIB::RuntimeLibcallsInfo Libcalls(TT); | |
| TargetLibraryInfoImpl TLII(TT); | |
| TargetLibraryInfo TLI(TLII); | |
| for (const InputFile::Symbol &Sym : Syms) { | |
| assert(ResI != ResE); | |
| SymbolResolution Res = *ResI++; | |
| StringRef SymbolName = Sym.getName(); | |
| // Keep copies of symbols if the client of LTO says so. | |
| if (GlobalResolutionSymbolSaver && !GlobalResolutions->contains(SymbolName)) | |
| SymbolName = GlobalResolutionSymbolSaver->save(SymbolName); | |
| auto &GlobalRes = (*GlobalResolutions)[SymbolName]; | |
| GlobalRes.UnnamedAddr &= Sym.isUnnamedAddr(); | |
| if (Res.Prevailing) { | |
| assert(!GlobalRes.Prevailing && | |
| "Multiple prevailing defs are not allowed"); | |
| GlobalRes.Prevailing = true; | |
| GlobalRes.IRName = std::string(Sym.getIRName()); | |
| } else if (!GlobalRes.Prevailing && GlobalRes.IRName.empty()) { | |
| // Sometimes it can be two copies of symbol in a module and prevailing | |
| // symbol can have no IR name. That might happen if symbol is defined in | |
| // module level inline asm block. In case we have multiple modules with | |
| // the same symbol we want to use IR name of the prevailing symbol. | |
| // Otherwise, if we haven't seen a prevailing symbol, set the name so that | |
| // we can later use it to check if there is any prevailing copy in IR. | |
| GlobalRes.IRName = std::string(Sym.getIRName()); | |
| } | |
| // In rare occasion, the symbol used to initialize GlobalRes has a different | |
| // IRName from the inspected Symbol. This can happen on macOS + iOS, when a | |
| // symbol is referenced through its mangled name, say @"\01_symbol" while | |
| // the IRName is @symbol (the prefix underscore comes from MachO mangling). | |
| // In that case, we have the same actual Symbol that can get two different | |
| // GUID, leading to some invalid internalization. Workaround this by marking | |
| // the GlobalRes external. | |
| // FIXME: instead of this check, it would be desirable to compute GUIDs | |
| // based on mangled name, but this requires an access to the Target Triple | |
| // and would be relatively invasive on the codebase. | |
| // FIXME: use the GUID member of GlobalRes. | |
| if (GlobalRes.IRName != Sym.getIRName()) { | |
| GlobalRes.Partition = GlobalResolution::External; | |
| GlobalRes.VisibleOutsideSummary = true; | |
| } | |
| bool IsLibcall = Sym.isLibcall(TLI, Libcalls); | |
| // Set the partition to external if we know it is re-defined by the linker | |
| // with -defsym or -wrap options, used elsewhere, e.g. it is visible to a | |
| // regular object, is referenced from llvm.compiler.used/llvm.used, or was | |
| // already recorded as being referenced from a different partition. | |
| if (Res.LinkerRedefined || Res.VisibleToRegularObj || Sym.isUsed() || | |
| IsLibcall || | |
| (GlobalRes.Partition != GlobalResolution::Unknown && | |
| GlobalRes.Partition != Partition)) { | |
| GlobalRes.Partition = GlobalResolution::External; | |
| } else | |
| // First recorded reference, save the current partition. | |
| GlobalRes.Partition = Partition; | |
| // Flag as visible outside of summary if visible from a regular object or | |
| // from a module that does not have a summary. | |
| GlobalRes.VisibleOutsideSummary |= | |
| (Res.VisibleToRegularObj || Sym.isUsed() || IsLibcall || !InSummary); | |
| GlobalRes.ExportDynamic |= Res.ExportDynamic; | |
| } | |
| } | |
| void LTO::releaseGlobalResolutionsMemory() { | |
| // Release GlobalResolutions dense-map itself. | |
| GlobalResolutions.reset(); | |
| // Release the string saver memory. | |
| GlobalResolutionSymbolSaver.reset(); | |
| Alloc.reset(); | |
| } | |
| static void writeToResolutionFile(raw_ostream &OS, InputFile *Input, | |
| ArrayRef<SymbolResolution> Res) { | |
| StringRef Path = Input->getName(); | |
| OS << Path << '\n'; | |
| auto ResI = Res.begin(); | |
| for (const InputFile::Symbol &Sym : Input->symbols()) { | |
| assert(ResI != Res.end()); | |
| SymbolResolution Res = *ResI++; | |
| OS << "-r=" << Path << ',' << Sym.getName() << ','; | |
| if (Res.Prevailing) | |
| OS << 'p'; | |
| if (Res.FinalDefinitionInLinkageUnit) | |
| OS << 'l'; | |
| if (Res.VisibleToRegularObj) | |
| OS << 'x'; | |
| if (Res.LinkerRedefined) | |
| OS << 'r'; | |
| OS << '\n'; | |
| } | |
| OS.flush(); | |
| assert(ResI == Res.end()); | |
| } | |
| Error LTO::add(std::unique_ptr<InputFile> InputPtr, | |
| ArrayRef<SymbolResolution> Res) { | |
| llvm::TimeTraceScope timeScope("LTO add input", InputPtr->getName()); | |
| assert(!CalledGetMaxTasks); | |
| Expected<std::shared_ptr<InputFile>> InputOrErr = | |
| addInput(std::move(InputPtr)); | |
| if (!InputOrErr) | |
| return InputOrErr.takeError(); | |
| InputFile *Input = (*InputOrErr).get(); | |
| if (Conf.ResolutionFile) | |
| writeToResolutionFile(*Conf.ResolutionFile, Input, Res); | |
| if (RegularLTO.CombinedModule->getTargetTriple().empty()) { | |
| Triple InputTriple(Input->getTargetTriple()); | |
| RegularLTO.CombinedModule->setTargetTriple(InputTriple); | |
| if (InputTriple.isOSBinFormatELF()) | |
| Conf.VisibilityScheme = Config::ELF; | |
| } | |
| ArrayRef<SymbolResolution> InputRes = Res; | |
| for (unsigned I = 0; I != Input->Mods.size(); ++I) { | |
| if (auto Err = addModule(*Input, InputRes, I, Res).moveInto(Res)) | |
| return Err; | |
| } | |
| assert(Res.empty()); | |
| return Error::success(); | |
| } | |
| void LTO::setBitcodeLibFuncs(ArrayRef<StringRef> BitcodeLibFuncs) { | |
| assert(this->BitcodeLibFuncs.empty() && | |
| "bitcode libfuncs were set twice; maybe accidentally clobbered?"); | |
| this->BitcodeLibFuncs.append(BitcodeLibFuncs.begin(), BitcodeLibFuncs.end()); | |
| } | |
| Expected<ArrayRef<SymbolResolution>> | |
| LTO::addModule(InputFile &Input, ArrayRef<SymbolResolution> InputRes, | |
| unsigned ModI, ArrayRef<SymbolResolution> Res) { | |
| llvm::TimeTraceScope timeScope("LTO add module", Input.getName()); | |
| Expected<BitcodeLTOInfo> LTOInfo = Input.Mods[ModI].getLTOInfo(); | |
| if (!LTOInfo) | |
| return LTOInfo.takeError(); | |
| if (EnableSplitLTOUnit) { | |
| // If only some modules were split, flag this in the index so that | |
| // we can skip or error on optimizations that need consistently split | |
| // modules (whole program devirt and lower type tests). | |
| if (*EnableSplitLTOUnit != LTOInfo->EnableSplitLTOUnit) | |
| ThinLTO.CombinedIndex.setPartiallySplitLTOUnits(); | |
| } else | |
| EnableSplitLTOUnit = LTOInfo->EnableSplitLTOUnit; | |
| BitcodeModule BM = Input.Mods[ModI]; | |
| if ((LTOMode == LTOK_UnifiedRegular || LTOMode == LTOK_UnifiedThin) && | |
| !LTOInfo->UnifiedLTO) | |
| return make_error<StringError>( | |
| "unified LTO compilation must use " | |
| "compatible bitcode modules (use -funified-lto)", | |
| inconvertibleErrorCode()); | |
| if (LTOInfo->UnifiedLTO && LTOMode == LTOK_Default) | |
| LTOMode = LTOK_UnifiedThin; | |
| bool IsThinLTO = LTOInfo->IsThinLTO && (LTOMode != LTOK_UnifiedRegular); | |
| // If any of the modules inside of a input bitcode file was compiled with | |
| // ThinLTO, we assume that the whole input file also was compiled with | |
| // ThinLTO. | |
| Input.IsThinLTO |= IsThinLTO; | |
| auto ModSyms = Input.module_symbols(ModI); | |
| addModuleToGlobalRes(ModSyms, Res, | |
| IsThinLTO ? ThinLTO.ModuleMap.size() + 1 : 0, | |
| LTOInfo->HasSummary, Triple(Input.getTargetTriple())); | |
| if (IsThinLTO) | |
| return addThinLTO(BM, ModSyms, Res); | |
| RegularLTO.EmptyCombinedModule = false; | |
| auto ModOrErr = addRegularLTO(Input, InputRes, BM, ModSyms, Res); | |
| if (!ModOrErr) | |
| return ModOrErr.takeError(); | |
| Res = ModOrErr->second; | |
| if (!LTOInfo->HasSummary) { | |
| if (Error Err = linkRegularLTO(std::move(ModOrErr->first), | |
| /*LivenessFromIndex=*/false)) | |
| return Err; | |
| return Res; | |
| } | |
| // Regular LTO module summaries are added to a dummy module that represents | |
| // the combined regular LTO module. | |
| if (Error Err = BM.readSummary(ThinLTO.CombinedIndex, "")) | |
| return Err; | |
| RegularLTO.ModsWithSummaries.push_back(std::move(ModOrErr->first)); | |
| return Res; | |
| } | |
| // Checks whether the given global value is in a non-prevailing comdat | |
| // (comdat containing values the linker indicated were not prevailing, | |
| // which we then dropped to available_externally), and if so, removes | |
| // it from the comdat. This is called for all global values to ensure the | |
| // comdat is empty rather than leaving an incomplete comdat. It is needed for | |
| // regular LTO modules, in case we are in a mixed-LTO mode (both regular | |
| // and thin LTO modules) compilation. Since the regular LTO module will be | |
| // linked first in the final native link, we want to make sure the linker | |
| // doesn't select any of these incomplete comdats that would be left | |
| // in the regular LTO module without this cleanup. | |
| static void | |
| handleNonPrevailingComdat(GlobalValue &GV, | |
| std::set<const Comdat *> &NonPrevailingComdats) { | |
| Comdat *C = GV.getComdat(); | |
| if (!C) | |
| return; | |
| if (!NonPrevailingComdats.count(C)) | |
| return; | |
| // Additionally need to drop all global values from the comdat to | |
| // available_externally, to satisfy the COMDAT requirement that all members | |
| // are discarded as a unit. The non-local linkage global values avoid | |
| // duplicate definition linker errors. | |
| GV.setLinkage(GlobalValue::AvailableExternallyLinkage); | |
| if (auto GO = dyn_cast<GlobalObject>(&GV)) | |
| GO->setComdat(nullptr); | |
| } | |
| // Add a regular LTO object to the link. | |
| // The resulting module needs to be linked into the combined LTO module with | |
| // linkRegularLTO. | |
| Expected< | |
| std::pair<LTO::RegularLTOState::AddedModule, ArrayRef<SymbolResolution>>> | |
| LTO::addRegularLTO(InputFile &Input, ArrayRef<SymbolResolution> InputRes, | |
| BitcodeModule BM, ArrayRef<InputFile::Symbol> Syms, | |
| ArrayRef<SymbolResolution> Res) { | |
| llvm::TimeTraceScope timeScope("LTO add regular LTO"); | |
| RegularLTOState::AddedModule Mod; | |
| Expected<std::unique_ptr<Module>> MOrErr = | |
| BM.getLazyModule(RegularLTO.Ctx, /*ShouldLazyLoadMetadata*/ true, | |
| /*IsImporting*/ false); | |
| if (!MOrErr) | |
| return MOrErr.takeError(); | |
| Module &M = **MOrErr; | |
| Mod.M = std::move(*MOrErr); | |
| if (Error Err = M.materializeMetadata()) | |
| return std::move(Err); | |
| if (LTOMode == LTOK_UnifiedRegular) { | |
| // cfi.functions metadata is intended to be used with ThinLTO and may | |
| // trigger invalid IR transformations if they are present when doing regular | |
| // LTO, so delete it. | |
| if (NamedMDNode *CfiFunctionsMD = M.getNamedMetadata("cfi.functions")) | |
| M.eraseNamedMetadata(CfiFunctionsMD); | |
| } else if (NamedMDNode *AliasesMD = M.getNamedMetadata("aliases")) { | |
| // Delete aliases entries for non-prevailing symbols on the ThinLTO side of | |
| // this input file. | |
| DenseSet<StringRef> Prevailing; | |
| for (auto [I, R] : zip(Input.symbols(), InputRes)) | |
| if (R.Prevailing && !I.getIRName().empty()) | |
| Prevailing.insert(I.getIRName()); | |
| std::vector<MDNode *> AliasGroups; | |
| for (MDNode *AliasGroup : AliasesMD->operands()) { | |
| std::vector<Metadata *> Aliases; | |
| for (Metadata *Alias : AliasGroup->operands()) { | |
| if (isa<MDString>(Alias) && | |
| Prevailing.count(cast<MDString>(Alias)->getString())) | |
| Aliases.push_back(Alias); | |
| } | |
| if (Aliases.size() > 1) | |
| AliasGroups.push_back(MDTuple::get(RegularLTO.Ctx, Aliases)); | |
| } | |
| AliasesMD->clearOperands(); | |
| for (MDNode *G : AliasGroups) | |
| AliasesMD->addOperand(G); | |
| } | |
| UpgradeDebugInfo(M); | |
| ModuleSymbolTable SymTab; | |
| SymTab.addModule(&M); | |
| for (GlobalVariable &GV : M.globals()) | |
| if (GV.hasAppendingLinkage()) | |
| Mod.Keep.push_back(&GV); | |
| DenseSet<GlobalObject *> AliasedGlobals; | |
| for (auto &GA : M.aliases()) | |
| if (GlobalObject *GO = GA.getAliaseeObject()) | |
| AliasedGlobals.insert(GO); | |
| // In this function we need IR GlobalValues matching the symbols in Syms | |
| // (which is not backed by a module), so we need to enumerate them in the same | |
| // order. The symbol enumeration order of a ModuleSymbolTable intentionally | |
| // matches the order of an irsymtab, but when we read the irsymtab in | |
| // InputFile::create we omit some symbols that are irrelevant to LTO. The | |
| // Skip() function skips the same symbols from the module as InputFile does | |
| // from the symbol table. | |
| auto MsymI = SymTab.symbols().begin(), MsymE = SymTab.symbols().end(); | |
| auto Skip = [&]() { | |
| while (MsymI != MsymE) { | |
| auto Flags = SymTab.getSymbolFlags(*MsymI); | |
| if ((Flags & object::BasicSymbolRef::SF_Global) && | |
| !(Flags & object::BasicSymbolRef::SF_FormatSpecific)) | |
| return; | |
| ++MsymI; | |
| } | |
| }; | |
| Skip(); | |
| std::set<const Comdat *> NonPrevailingComdats; | |
| SmallSet<StringRef, 2> NonPrevailingAsmSymbols; | |
| for (const InputFile::Symbol &Sym : Syms) { | |
| assert(!Res.empty()); | |
| const SymbolResolution &R = Res.consume_front(); | |
| assert(MsymI != MsymE); | |
| ModuleSymbolTable::Symbol Msym = *MsymI++; | |
| Skip(); | |
| if (GlobalValue *GV = dyn_cast_if_present<GlobalValue *>(Msym)) { | |
| if (R.Prevailing) { | |
| if (Sym.isUndefined()) | |
| continue; | |
| Mod.Keep.push_back(GV); | |
| // For symbols re-defined with linker -wrap and -defsym options, | |
| // set the linkage to weak to inhibit IPO. The linkage will be | |
| // restored by the linker. | |
| if (R.LinkerRedefined) | |
| GV->setLinkage(GlobalValue::WeakAnyLinkage); | |
| GlobalValue::LinkageTypes OriginalLinkage = GV->getLinkage(); | |
| if (GlobalValue::isLinkOnceLinkage(OriginalLinkage)) | |
| GV->setLinkage(GlobalValue::getWeakLinkage( | |
| GlobalValue::isLinkOnceODRLinkage(OriginalLinkage))); | |
| } else if (isa<GlobalObject>(GV) && | |
| (GV->hasLinkOnceODRLinkage() || GV->hasWeakODRLinkage() || | |
| GV->hasAvailableExternallyLinkage()) && | |
| !AliasedGlobals.count(cast<GlobalObject>(GV))) { | |
| // Any of the above three types of linkage indicates that the | |
| // chosen prevailing symbol will have the same semantics as this copy of | |
| // the symbol, so we may be able to link it with available_externally | |
| // linkage. We will decide later whether to do that when we link this | |
| // module (in linkRegularLTO), based on whether it is undefined. | |
| Mod.Keep.push_back(GV); | |
| GV->setLinkage(GlobalValue::AvailableExternallyLinkage); | |
| if (GV->hasComdat()) | |
| NonPrevailingComdats.insert(GV->getComdat()); | |
| cast<GlobalObject>(GV)->setComdat(nullptr); | |
| } | |
| // Set the 'local' flag based on the linker resolution for this symbol. | |
| if (R.FinalDefinitionInLinkageUnit) { | |
| GV->setDSOLocal(true); | |
| if (GV->hasDLLImportStorageClass()) | |
| GV->setDLLStorageClass(GlobalValue::DLLStorageClassTypes:: | |
| DefaultStorageClass); | |
| } | |
| } else if (auto *AS = | |
| dyn_cast_if_present<ModuleSymbolTable::AsmSymbol *>(Msym)) { | |
| // Collect non-prevailing symbols. | |
| if (!R.Prevailing) | |
| NonPrevailingAsmSymbols.insert(AS->first); | |
| } else { | |
| llvm_unreachable("unknown symbol type"); | |
| } | |
| // Common resolution: collect the maximum size/alignment over all commons. | |
| // We also record if we see an instance of a common as prevailing, so that | |
| // if none is prevailing we can ignore it later. | |
| if (Sym.isCommon()) { | |
| // FIXME: We should figure out what to do about commons defined by asm. | |
| // For now they aren't reported correctly by ModuleSymbolTable. | |
| auto &CommonRes = RegularLTO.Commons[std::string(Sym.getIRName())]; | |
| CommonRes.Size = std::max(CommonRes.Size, Sym.getCommonSize()); | |
| if (uint32_t SymAlignValue = Sym.getCommonAlignment()) { | |
| CommonRes.Alignment = | |
| std::max(Align(SymAlignValue), CommonRes.Alignment); | |
| } | |
| CommonRes.Prevailing |= R.Prevailing; | |
| } | |
| } | |
| if (!M.getComdatSymbolTable().empty()) | |
| for (GlobalValue &GV : M.global_values()) | |
| handleNonPrevailingComdat(GV, NonPrevailingComdats); | |
| // Prepend ".lto_discard <sym>, <sym>*" directive to each module inline asm | |
| // block. | |
| if (M.hasModuleInlineAsm()) { | |
| std::string NewIA = ".lto_discard"; | |
| if (!NonPrevailingAsmSymbols.empty()) { | |
| // Don't dicard a symbol if there is a live .symver for it. | |
| ModuleSymbolTable::CollectAsmSymvers( | |
| M, [&](StringRef Name, StringRef Alias) { | |
| if (!NonPrevailingAsmSymbols.count(Alias)) | |
| NonPrevailingAsmSymbols.erase(Name); | |
| }); | |
| NewIA += " " + llvm::join(NonPrevailingAsmSymbols, ", "); | |
| } | |
| NewIA += "\n"; | |
| M.prependModuleInlineAsm({NewIA, M.getModuleInlineAsm().front().Props}); | |
| } | |
| assert(MsymI == MsymE); | |
| return std::make_pair(std::move(Mod), Res); | |
| } | |
| Error LTO::linkRegularLTO(RegularLTOState::AddedModule Mod, | |
| bool LivenessFromIndex) { | |
| llvm::TimeTraceScope timeScope("LTO link regular LTO"); | |
| std::vector<GlobalValue *> Keep; | |
| for (GlobalValue *GV : Mod.Keep) { | |
| if (LivenessFromIndex) { | |
| const auto GUID = GV->getGUIDOrFallback(); | |
| if (!ThinLTO.CombinedIndex.isGUIDLive(GUID)) { | |
| if (Function *F = dyn_cast<Function>(GV)) { | |
| if (DiagnosticOutputFile) { | |
| if (Error Err = F->materialize()) | |
| return Err; | |
| auto R = OptimizationRemark(DEBUG_TYPE, "deadfunction", F); | |
| R << ore::NV("Function", F) << " not added to the combined module "; | |
| emitRemark(R); | |
| } | |
| } | |
| continue; | |
| } | |
| } | |
| if (!GV->hasAvailableExternallyLinkage()) { | |
| Keep.push_back(GV); | |
| continue; | |
| } | |
| // Only link available_externally definitions if we don't already have a | |
| // definition. | |
| GlobalValue *CombinedGV = | |
| RegularLTO.CombinedModule->getNamedValue(GV->getName()); | |
| if (CombinedGV && !CombinedGV->isDeclaration()) | |
| continue; | |
| Keep.push_back(GV); | |
| } | |
| return RegularLTO.Mover->move(std::move(Mod.M), Keep, nullptr, | |
| /* IsPerformingImport */ false); | |
| } | |
| // Add a ThinLTO module to the link. | |
| Expected<ArrayRef<SymbolResolution>> | |
| LTO::addThinLTO(BitcodeModule BM, ArrayRef<InputFile::Symbol> Syms, | |
| ArrayRef<SymbolResolution> Res) { | |
| llvm::TimeTraceScope timeScope("LTO add thin LTO"); | |
| const auto BMID = BM.getModuleIdentifier(); | |
| ArrayRef<SymbolResolution> ResTmp = Res; | |
| DenseSet<StringRef> Prevailing; | |
| for (const InputFile::Symbol &Sym : Syms) { | |
| assert(!ResTmp.empty()); | |
| const SymbolResolution &R = ResTmp.consume_front(); | |
| if (!Sym.getIRName().empty() && R.Prevailing) | |
| Prevailing.insert(Sym.getIRName()); | |
| } | |
| // Track the GUIDs stored in the bitcode GUID table. | |
| StringMap<GlobalValue::GUID> IRSpecifiedGUIDs; | |
| if (Error Err = BM.readSummary( | |
| ThinLTO.CombinedIndex, BMID, | |
| [&](StringRef Name) { return (Prevailing.count(Name) > 0); }, | |
| [&](ValueInfo VI) { | |
| auto IT = IRSpecifiedGUIDs.insert({VI.name(), VI.getGUID()}); | |
| (void)IT; | |
| assert(IT.second); | |
| if (auto GRIt = GlobalResolutions->find(VI.name()); | |
| GRIt != GlobalResolutions->end() && | |
| Prevailing.count(VI.name())) { | |
| GRIt->second.setGUID(VI.getGUID()); | |
| } | |
| })) | |
| return Err; | |
| LLVM_DEBUG(dbgs() << "Module " << BMID << "\n"); | |
| for (const InputFile::Symbol &Sym : Syms) { | |
| assert(!Res.empty()); | |
| const SymbolResolution &R = Res.consume_front(); | |
| auto GUIDIter = IRSpecifiedGUIDs.find(Sym.getIRName()); | |
| // The bitcode GUID table might not be present if this is an old bitcode | |
| // file. For backwards-compatibility, just compute the GUID now in that | |
| // case. | |
| auto GUID = | |
| GUIDIter == IRSpecifiedGUIDs.end() | |
| ? GlobalValue::getGUIDAssumingExternalLinkage( | |
| GlobalValue::getGlobalIdentifier( | |
| Sym.getIRName(), GlobalValue::ExternalLinkage, "")) | |
| : GUIDIter->second; | |
| if (!Sym.getIRName().empty() && | |
| (R.Prevailing || R.FinalDefinitionInLinkageUnit)) { | |
| if (R.Prevailing) { | |
| ThinLTO.setPrevailingModuleForGUID(GUID, BMID); | |
| // For linker redefined symbols (via --wrap or --defsym) we want to | |
| // switch the linkage to `weak` to prevent IPOs from happening. | |
| // Find the summary in the module for this very GV and record the new | |
| // linkage so that we can switch it when we import the GV. | |
| if (R.LinkerRedefined) | |
| if (auto *S = ThinLTO.CombinedIndex.findSummaryInModule(GUID, BMID)) | |
| S->setLinkage(GlobalValue::WeakAnyLinkage); | |
| } | |
| // If the linker resolved the symbol to a local definition then mark it | |
| // as local in the summary for the module we are adding. | |
| if (R.FinalDefinitionInLinkageUnit) { | |
| if (auto *S = ThinLTO.CombinedIndex.findSummaryInModule(GUID, BMID)) { | |
| S->setDSOLocal(true); | |
| } | |
| } | |
| } | |
| } | |
| if (!ThinLTO.ModuleMap.insert({BMID, BM}).second) | |
| return make_error<StringError>( | |
| "Expected at most one ThinLTO module per bitcode file", | |
| inconvertibleErrorCode()); | |
| if (!Conf.ThinLTOModulesToCompile.empty()) { | |
| if (!ThinLTO.ModulesToCompile) | |
| ThinLTO.ModulesToCompile = ModuleMapType(); | |
| // This is a fuzzy name matching where only modules with name containing the | |
| // specified switch values are going to be compiled. | |
| for (const std::string &Name : Conf.ThinLTOModulesToCompile) { | |
| if (BMID.contains(Name)) { | |
| ThinLTO.ModulesToCompile->insert({BMID, BM}); | |
| LLVM_DEBUG(dbgs() << "[ThinLTO] Selecting " << BMID << " to compile\n"); | |
| break; | |
| } | |
| } | |
| } | |
| return Res; | |
| } | |
| unsigned LTO::getMaxTasks() const { | |
| CalledGetMaxTasks = true; | |
| auto ModuleCount = ThinLTO.ModulesToCompile ? ThinLTO.ModulesToCompile->size() | |
| : ThinLTO.ModuleMap.size(); | |
| return RegularLTO.ParallelCodeGenParallelismLevel + ModuleCount; | |
| } | |
| // If only some of the modules were split, we cannot correctly handle | |
| // code that contains type tests or type checked loads. | |
| Error LTO::checkPartiallySplit() { | |
| if (!ThinLTO.CombinedIndex.partiallySplitLTOUnits()) | |
| return Error::success(); | |
| const Module *Combined = RegularLTO.CombinedModule.get(); | |
| Function *TypeTestFunc = | |
| Intrinsic::getDeclarationIfExists(Combined, Intrinsic::type_test); | |
| Function *TypeCheckedLoadFunc = | |
| Intrinsic::getDeclarationIfExists(Combined, Intrinsic::type_checked_load); | |
| Function *TypeCheckedLoadRelativeFunc = Intrinsic::getDeclarationIfExists( | |
| Combined, Intrinsic::type_checked_load_relative); | |
| // First check if there are type tests / type checked loads in the | |
| // merged regular LTO module IR. | |
| if ((TypeTestFunc && !TypeTestFunc->use_empty()) || | |
| (TypeCheckedLoadFunc && !TypeCheckedLoadFunc->use_empty()) || | |
| (TypeCheckedLoadRelativeFunc && | |
| !TypeCheckedLoadRelativeFunc->use_empty())) | |
| return make_error<StringError>( | |
| "inconsistent LTO Unit splitting (recompile with -fsplit-lto-unit)", | |
| inconvertibleErrorCode()); | |
| // Otherwise check if there are any recorded in the combined summary from the | |
| // ThinLTO modules. | |
| for (auto &P : ThinLTO.CombinedIndex) { | |
| for (auto &S : P.second.getSummaryList()) { | |
| auto *FS = dyn_cast<FunctionSummary>(S.get()); | |
| if (!FS) | |
| continue; | |
| if (!FS->type_test_assume_vcalls().empty() || | |
| !FS->type_checked_load_vcalls().empty() || | |
| !FS->type_test_assume_const_vcalls().empty() || | |
| !FS->type_checked_load_const_vcalls().empty() || | |
| !FS->type_tests().empty()) | |
| return make_error<StringError>( | |
| "inconsistent LTO Unit splitting (recompile with -fsplit-lto-unit)", | |
| inconvertibleErrorCode()); | |
| } | |
| } | |
| return Error::success(); | |
| } | |
| Error LTO::run(AddStreamFn AddStream, FileCache Cache) { | |
| // Call the base class cleanup() explicitly since run() may be invoked on a | |
| // derived LTO object. | |
| llvm::scope_exit CleanUp([this]() { LTO::cleanup(); }); | |
| // Compute "dead" symbols, we don't want to import/export these! | |
| DenseSet<GlobalValue::GUID> GUIDPreservedSymbols; | |
| DenseMap<GlobalValue::GUID, PrevailingType> GUIDPrevailingResolutions; | |
| for (auto &Res : *GlobalResolutions) { | |
| // Normally resolution have IR name of symbol. We can do nothing here | |
| // otherwise. See comments in GlobalResolution struct for more details. | |
| if (Res.second.IRName.empty()) | |
| continue; | |
| GlobalValue::GUID GUID = Res.second.getGUID(); | |
| if (Res.second.VisibleOutsideSummary && Res.second.Prevailing) | |
| GUIDPreservedSymbols.insert(GUID); | |
| if (Res.second.ExportDynamic) | |
| DynamicExportSymbols.insert(GUID); | |
| GUIDPrevailingResolutions[GUID] = | |
| Res.second.Prevailing ? PrevailingType::Yes : PrevailingType::No; | |
| } | |
| auto isPrevailing = [&](GlobalValue::GUID G) { | |
| auto It = GUIDPrevailingResolutions.find(G); | |
| if (It == GUIDPrevailingResolutions.end()) | |
| return PrevailingType::Unknown; | |
| return It->second; | |
| }; | |
| computeDeadSymbolsWithConstProp(ThinLTO.CombinedIndex, GUIDPreservedSymbols, | |
| isPrevailing, Conf.OptLevel > 0); | |
| // Setup output file to emit statistics. | |
| auto StatsFileOrErr = setupStatsFile(Conf.StatsFile); | |
| if (!StatsFileOrErr) | |
| return StatsFileOrErr.takeError(); | |
| std::unique_ptr<ToolOutputFile> StatsFile = std::move(StatsFileOrErr.get()); | |
| if (Error Err = setupOptimizationRemarks()) | |
| return Err; | |
| // TODO: Ideally this would be controlled automatically by detecting that we | |
| // are linking with an allocator that supports these interfaces, rather than | |
| // an internal option (which would still be needed for tests, however). For | |
| // example, if the library exported a symbol like __malloc_hot_cold the linker | |
| // could recognize that and set a flag in the lto::Config. | |
| if (SupportsHotColdNew) | |
| ThinLTO.CombinedIndex.setWithSupportsHotColdNew(); | |
| Error Result = runRegularLTO(AddStream); | |
| if (!Result) | |
| // This will reset the GlobalResolutions optional once done with it to | |
| // reduce peak memory before importing. | |
| Result = runThinLTO(AddStream, Cache, GUIDPreservedSymbols); | |
| if (StatsFile) | |
| PrintStatisticsJSON(StatsFile->os()); | |
| return Result; | |
| } | |
| Error LTO::runRegularLTO(AddStreamFn AddStream) { | |
| llvm::TimeTraceScope timeScope("Run regular LTO"); | |
| LLVM_DEBUG(dbgs() << "Running regular LTO\n"); | |
| // Finalize linking of regular LTO modules containing summaries now that | |
| // we have computed liveness information. | |
| { | |
| llvm::TimeTraceScope timeScope("Link regular LTO"); | |
| for (auto &M : RegularLTO.ModsWithSummaries) | |
| if (Error Err = linkRegularLTO(std::move(M), /*LivenessFromIndex=*/true)) | |
| return Err; | |
| } | |
| // Ensure we don't have inconsistently split LTO units with type tests. | |
| // FIXME: this checks both LTO and ThinLTO. It happens to work as we take | |
| // this path both cases but eventually this should be split into two and | |
| // do the ThinLTO checks in `runThinLTO`. | |
| if (Error Err = checkPartiallySplit()) | |
| return Err; | |
| // Make sure commons have the right size/alignment: we kept the largest from | |
| // all the prevailing when adding the inputs, and we apply it here. | |
| const DataLayout &DL = RegularLTO.CombinedModule->getDataLayout(); | |
| for (auto &I : RegularLTO.Commons) { | |
| if (!I.second.Prevailing) | |
| // Don't do anything if no instance of this common was prevailing. | |
| continue; | |
| GlobalVariable *OldGV = RegularLTO.CombinedModule->getNamedGlobal(I.first); | |
| if (OldGV && OldGV->getGlobalSize(DL) == I.second.Size) { | |
| // Don't create a new global if the type is already correct, just make | |
| // sure the alignment is correct. | |
| OldGV->setAlignment(I.second.Alignment); | |
| continue; | |
| } | |
| ArrayType *Ty = | |
| ArrayType::get(Type::getInt8Ty(RegularLTO.Ctx), I.second.Size); | |
| auto *GV = new GlobalVariable(*RegularLTO.CombinedModule, Ty, false, | |
| GlobalValue::CommonLinkage, | |
| ConstantAggregateZero::get(Ty), ""); | |
| GV->setAlignment(I.second.Alignment); | |
| if (OldGV) { | |
| OldGV->replaceAllUsesWith(GV); | |
| GV->takeName(OldGV); | |
| OldGV->eraseFromParent(); | |
| } else { | |
| GV->setName(I.first); | |
| } | |
| } | |
| bool WholeProgramVisibilityEnabledInLTO = | |
| Conf.HasWholeProgramVisibility && | |
| // If validation is enabled, upgrade visibility only when all vtables | |
| // have typeinfos. | |
| (!Conf.ValidateAllVtablesHaveTypeInfos || Conf.AllVtablesHaveTypeInfos); | |
| // This returns true when the name is local or not defined. Locals are | |
| // expected to be handled separately. | |
| auto IsVisibleToRegularObj = [&](StringRef name) { | |
| auto It = GlobalResolutions->find(name); | |
| return (It == GlobalResolutions->end() || | |
| It->second.VisibleOutsideSummary || !It->second.Prevailing); | |
| }; | |
| // If allowed, upgrade public vcall visibility metadata to linkage unit | |
| // visibility before whole program devirtualization in the optimizer. | |
| updateVCallVisibilityInModule( | |
| *RegularLTO.CombinedModule, WholeProgramVisibilityEnabledInLTO, | |
| DynamicExportSymbols, Conf.ValidateAllVtablesHaveTypeInfos, | |
| IsVisibleToRegularObj); | |
| updatePublicTypeTestCalls(*RegularLTO.CombinedModule, | |
| WholeProgramVisibilityEnabledInLTO); | |
| if (Conf.PreOptModuleHook && | |
| !Conf.PreOptModuleHook(0, *RegularLTO.CombinedModule)) | |
| return Error::success(); | |
| if (!Conf.CodeGenOnly) { | |
| for (const auto &R : *GlobalResolutions) { | |
| GlobalValue *GV = | |
| RegularLTO.CombinedModule->getNamedValue(R.second.IRName); | |
| if (!R.second.isPrevailingIRSymbol()) | |
| continue; | |
| if (R.second.Partition != 0 && | |
| R.second.Partition != GlobalResolution::External) | |
| continue; | |
| // Ignore symbols defined in other partitions. | |
| // Also skip declarations, which are not allowed to have internal linkage. | |
| if (!GV || GV->hasLocalLinkage() || GV->isDeclaration()) | |
| continue; | |
| // Symbols that are marked DLLImport or DLLExport should not be | |
| // internalized, as they are either externally visible or referencing | |
| // external symbols. Symbols that have AvailableExternally or Appending | |
| // linkage might be used by future passes and should be kept as is. | |
| // These linkages are seen in Unified regular LTO, because the process | |
| // of creating split LTO units introduces symbols with that linkage into | |
| // one of the created modules. Normally, only the ThinLTO backend would | |
| // compile this module, but Unified Regular LTO processes both | |
| // modules created by the splitting process as regular LTO modules. | |
| if ((LTOMode == LTOKind::LTOK_UnifiedRegular) && | |
| ((GV->getDLLStorageClass() != GlobalValue::DefaultStorageClass) || | |
| GV->hasAvailableExternallyLinkage() || GV->hasAppendingLinkage())) | |
| continue; | |
| GV->setUnnamedAddr(R.second.UnnamedAddr ? GlobalValue::UnnamedAddr::Global | |
| : GlobalValue::UnnamedAddr::None); | |
| if (EnableLTOInternalization && R.second.Partition == 0) | |
| GV->setLinkage(GlobalValue::InternalLinkage); | |
| } | |
| if (Conf.PostInternalizeModuleHook && | |
| !Conf.PostInternalizeModuleHook(0, *RegularLTO.CombinedModule)) | |
| return Error::success(); | |
| } | |
| if (!RegularLTO.EmptyCombinedModule || Conf.AlwaysEmitRegularLTOObj) { | |
| if (Error Err = backend( | |
| Conf, AddStream, RegularLTO.ParallelCodeGenParallelismLevel, | |
| *RegularLTO.CombinedModule, ThinLTO.CombinedIndex, BitcodeLibFuncs)) | |
| return Err; | |
| } | |
| return Error::success(); | |
| } | |
| SmallVector<const char *> LTO::getRuntimeLibcallSymbols(const Triple &TT) { | |
| RTLIB::RuntimeLibcallsInfo Libcalls(TT); | |
| SmallVector<const char *> LibcallSymbols; | |
| LibcallSymbols.reserve(Libcalls.getNumAvailableLibcallImpls()); | |
| for (RTLIB::LibcallImpl Impl : RTLIB::libcall_impls()) { | |
| if (Libcalls.isAvailable(Impl)) | |
| LibcallSymbols.push_back(Libcalls.getLibcallImplName(Impl).data()); | |
| } | |
| return LibcallSymbols; | |
| } | |
| SmallVector<StringRef> LTO::getLibFuncSymbols(const Triple &TT, | |
| StringSaver &Saver) { | |
| auto TLII = std::make_unique<TargetLibraryInfoImpl>(TT); | |
| TargetLibraryInfo TLI(*TLII); | |
| SmallVector<StringRef> LibFuncSymbols; | |
| LibFuncSymbols.reserve(LibFunc::NumLibFuncs); | |
| for (unsigned I = LibFunc::Begin_LibFunc; I != LibFunc::End_LibFunc; ++I) { | |
| LibFunc F = static_cast<LibFunc>(I); | |
| if (TLI.has(F)) | |
| LibFuncSymbols.push_back(Saver.save(TLI.getName(F)).data()); | |
| } | |
| return LibFuncSymbols; | |
| } | |
| Error ThinBackendProc::emitFiles( | |
| const FunctionImporter::ImportMapTy &ImportList, unsigned Task, | |
| llvm::StringRef ModulePath, const std::string &NewModulePath) const { | |
| return emitFiles(ImportList, Task, ModulePath, NewModulePath, | |
| NewModulePath + ".thinlto.bc"); | |
| } | |
| Error ThinBackendProc::emitFiles( | |
| const FunctionImporter::ImportMapTy &ImportList, unsigned Task, | |
| llvm::StringRef ModulePath, const std::string &NewModulePath, | |
| StringRef SummaryPath) const { | |
| ModuleToSummariesForIndexTy ModuleToSummariesForIndex; | |
| GVSummaryPtrSet DeclarationSummaries; | |
| std::error_code EC; | |
| gatherImportedSummariesForModule(ModulePath, ModuleToDefinedGVSummaries, | |
| ImportList, ModuleToSummariesForIndex, | |
| DeclarationSummaries); | |
| // Resolve the output stream (either file-backed or callback-provided) for the | |
| // index file. | |
| std::unique_ptr<raw_pwrite_stream> OS; | |
| if (Conf.GetSummaryIndexOutputStream) { | |
| OS = Conf.GetSummaryIndexOutputStream(Task); | |
| assert(OS && "GetSummaryIndexOutputStream returned null"); | |
| } else { | |
| auto FileOS = std::make_unique<raw_fd_ostream>(SummaryPath, EC, | |
| sys::fs::OpenFlags::OF_None); | |
| if (EC) | |
| return createFileError("cannot open " + Twine(SummaryPath), EC); | |
| OS = std::move(FileOS); | |
| } | |
| writeIndexToFile(CombinedIndex, *OS, &ModuleToSummariesForIndex, | |
| &DeclarationSummaries); | |
| // Emit imports files if requested, using callback if provided. | |
| if (Conf.GetImportsListOutputArray) { | |
| std::vector<std::string> &ImportsListRef = | |
| Conf.GetImportsListOutputArray(Task); | |
| processImportsFiles( | |
| ModulePath, ModuleToSummariesForIndex, | |
| [&](StringRef M) { ImportsListRef.push_back(M.str()); }); | |
| } else if (ShouldEmitImportsFiles) { | |
| if (Error E = EmitImportsFiles(ModulePath, NewModulePath + ".imports", | |
| ModuleToSummariesForIndex)) | |
| return E; | |
| } | |
| return Error::success(); | |
| } | |
| namespace { | |
| /// Base class for ThinLTO backends that perform code generation and insert the | |
| /// generated files back into the link. | |
| class CGThinBackend : public ThinBackendProc { | |
| protected: | |
| DenseSet<GlobalValue::GUID> CfiFunctionDefs; | |
| DenseSet<GlobalValue::GUID> CfiFunctionDecls; | |
| bool ShouldEmitIndexFiles; | |
| public: | |
| CGThinBackend( | |
| const Config &Conf, ModuleSummaryIndex &CombinedIndex, | |
| const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, | |
| lto::IndexWriteCallback OnWrite, bool ShouldEmitIndexFiles, | |
| bool ShouldEmitImportsFiles, ThreadPoolStrategy ThinLTOParallelism) | |
| : ThinBackendProc(Conf, CombinedIndex, ModuleToDefinedGVSummaries, | |
| OnWrite, ShouldEmitImportsFiles, ThinLTOParallelism), | |
| ShouldEmitIndexFiles(ShouldEmitIndexFiles) { | |
| auto &Defs = CombinedIndex.cfiFunctionDefs(); | |
| CfiFunctionDefs.insert_range(Defs.getExportedThinLTOGUIDs()); | |
| auto &Decls = CombinedIndex.cfiFunctionDecls(); | |
| CfiFunctionDecls.insert_range(Decls.getExportedThinLTOGUIDs()); | |
| } | |
| }; | |
| /// This backend performs code generation by scheduling a job to run on | |
| /// an in-process thread when invoked for each task. | |
| class InProcessThinBackend : public CGThinBackend { | |
| protected: | |
| // Callback used to add generated native object files to the link by code | |
| // generating directly into the returned output stream. | |
| AddStreamFn AddStream; | |
| FileCache Cache; | |
| ArrayRef<StringRef> BitcodeLibFuncs; | |
| public: | |
| InProcessThinBackend( | |
| const Config &Conf, ModuleSummaryIndex &CombinedIndex, | |
| ThreadPoolStrategy ThinLTOParallelism, | |
| const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, | |
| AddStreamFn AddStream, FileCache Cache, lto::IndexWriteCallback OnWrite, | |
| bool ShouldEmitIndexFiles, bool ShouldEmitImportsFiles, | |
| ArrayRef<StringRef> BitcodeLibFuncs) | |
| : CGThinBackend(Conf, CombinedIndex, ModuleToDefinedGVSummaries, OnWrite, | |
| ShouldEmitIndexFiles, ShouldEmitImportsFiles, | |
| ThinLTOParallelism), | |
| AddStream(std::move(AddStream)), Cache(std::move(Cache)), | |
| BitcodeLibFuncs(BitcodeLibFuncs) {} | |
| virtual Error runThinLTOBackendThread( | |
| AddStreamFn AddStream, FileCache Cache, unsigned Task, BitcodeModule BM, | |
| ModuleSummaryIndex &CombinedIndex, | |
| const FunctionImporter::ImportMapTy &ImportList, | |
| const FunctionImporter::ExportSetTy &ExportList, | |
| const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR, | |
| const GVSummaryMapTy &DefinedGlobals, | |
| MapVector<StringRef, BitcodeModule> &ModuleMap) { | |
| auto ModuleID = BM.getModuleIdentifier(); | |
| llvm::TimeTraceScope timeScope("Run ThinLTO backend thread (in-process)", | |
| ModuleID); | |
| auto RunThinBackend = [&](AddStreamFn AddStream) { | |
| LTOLLVMContext BackendContext(Conf); | |
| Expected<std::unique_ptr<Module>> MOrErr = BM.parseModule(BackendContext); | |
| if (!MOrErr) | |
| return MOrErr.takeError(); | |
| return thinBackend(Conf, Task, AddStream, **MOrErr, CombinedIndex, | |
| ImportList, DefinedGlobals, &ModuleMap, | |
| Conf.CodeGenOnly, BitcodeLibFuncs); | |
| }; | |
| if (ShouldEmitIndexFiles) { | |
| if (auto E = emitFiles(ImportList, Task, ModuleID, ModuleID.str())) | |
| return E; | |
| } | |
| if (!Cache.isValid() || !CombinedIndex.modulePaths().count(ModuleID) || | |
| all_of(CombinedIndex.getModuleHash(ModuleID), | |
| [](uint32_t V) { return V == 0; })) | |
| // Cache disabled or no entry for this module in the combined index or | |
| // no module hash. | |
| return RunThinBackend(AddStream); | |
| // The module may be cached, this helps handling it. | |
| std::string Key = computeLTOCacheKey( | |
| Conf, CombinedIndex, ModuleID, ImportList, ExportList, ResolvedODR, | |
| DefinedGlobals, CfiFunctionDefs, CfiFunctionDecls); | |
| Expected<AddStreamFn> CacheAddStreamOrErr = Cache(Task, Key, ModuleID); | |
| if (Error Err = CacheAddStreamOrErr.takeError()) | |
| return Err; | |
| AddStreamFn &CacheAddStream = *CacheAddStreamOrErr; | |
| if (CacheAddStream) | |
| return RunThinBackend(CacheAddStream); | |
| return Error::success(); | |
| } | |
| Error start( | |
| unsigned Task, BitcodeModule BM, | |
| const FunctionImporter::ImportMapTy &ImportList, | |
| const FunctionImporter::ExportSetTy &ExportList, | |
| const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR, | |
| MapVector<StringRef, BitcodeModule> &ModuleMap) override { | |
| StringRef ModulePath = BM.getModuleIdentifier(); | |
| assert(ModuleToDefinedGVSummaries.count(ModulePath)); | |
| const GVSummaryMapTy &DefinedGlobals = | |
| ModuleToDefinedGVSummaries.find(ModulePath)->second; | |
| BackendThreadPool.async( | |
| [=](BitcodeModule BM, ModuleSummaryIndex &CombinedIndex, | |
| const FunctionImporter::ImportMapTy &ImportList, | |
| const FunctionImporter::ExportSetTy &ExportList, | |
| const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> | |
| &ResolvedODR, | |
| const GVSummaryMapTy &DefinedGlobals, | |
| MapVector<StringRef, BitcodeModule> &ModuleMap) { | |
| if (LLVM_ENABLE_THREADS && Conf.TimeTraceEnabled) | |
| timeTraceProfilerInitialize(Conf.TimeTraceGranularity, | |
| "thin backend"); | |
| Error E = runThinLTOBackendThread( | |
| AddStream, Cache, Task, BM, CombinedIndex, ImportList, ExportList, | |
| ResolvedODR, DefinedGlobals, ModuleMap); | |
| if (E) { | |
| std::unique_lock<std::mutex> L(ErrMu); | |
| if (Err) | |
| Err = joinErrors(std::move(*Err), std::move(E)); | |
| else | |
| Err = std::move(E); | |
| } | |
| if (LLVM_ENABLE_THREADS && Conf.TimeTraceEnabled) | |
| timeTraceProfilerFinishThread(); | |
| }, | |
| BM, std::ref(CombinedIndex), std::ref(ImportList), std::ref(ExportList), | |
| std::ref(ResolvedODR), std::ref(DefinedGlobals), std::ref(ModuleMap)); | |
| if (OnWrite) | |
| OnWrite(std::string(ModulePath)); | |
| return Error::success(); | |
| } | |
| }; | |
| /// This backend is utilized in the first round of a two-codegen round process. | |
| /// It first saves optimized bitcode files to disk before the codegen process | |
| /// begins. After codegen, it stores the resulting object files in a scratch | |
| /// buffer. Note the codegen data stored in the scratch buffer will be extracted | |
| /// and merged in the subsequent step. | |
| class FirstRoundThinBackend : public InProcessThinBackend { | |
| AddStreamFn IRAddStream; | |
| FileCache IRCache; | |
| public: | |
| FirstRoundThinBackend( | |
| const Config &Conf, ModuleSummaryIndex &CombinedIndex, | |
| ThreadPoolStrategy ThinLTOParallelism, | |
| const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, | |
| AddStreamFn CGAddStream, FileCache CGCache, | |
| ArrayRef<StringRef> BitcodeLibFuncs, AddStreamFn IRAddStream, | |
| FileCache IRCache) | |
| : InProcessThinBackend(Conf, CombinedIndex, ThinLTOParallelism, | |
| ModuleToDefinedGVSummaries, std::move(CGAddStream), | |
| std::move(CGCache), /*OnWrite=*/nullptr, | |
| /*ShouldEmitIndexFiles=*/false, | |
| /*ShouldEmitImportsFiles=*/false, BitcodeLibFuncs), | |
| IRAddStream(std::move(IRAddStream)), IRCache(std::move(IRCache)) {} | |
| Error runThinLTOBackendThread( | |
| AddStreamFn CGAddStream, FileCache CGCache, unsigned Task, | |
| BitcodeModule BM, ModuleSummaryIndex &CombinedIndex, | |
| const FunctionImporter::ImportMapTy &ImportList, | |
| const FunctionImporter::ExportSetTy &ExportList, | |
| const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR, | |
| const GVSummaryMapTy &DefinedGlobals, | |
| MapVector<StringRef, BitcodeModule> &ModuleMap) override { | |
| auto ModuleID = BM.getModuleIdentifier(); | |
| llvm::TimeTraceScope timeScope("Run ThinLTO backend thread (first round)", | |
| ModuleID); | |
| auto RunThinBackend = [&](AddStreamFn CGAddStream, | |
| AddStreamFn IRAddStream) { | |
| LTOLLVMContext BackendContext(Conf); | |
| Expected<std::unique_ptr<Module>> MOrErr = BM.parseModule(BackendContext); | |
| if (!MOrErr) | |
| return MOrErr.takeError(); | |
| return thinBackend(Conf, Task, CGAddStream, **MOrErr, CombinedIndex, | |
| ImportList, DefinedGlobals, &ModuleMap, | |
| Conf.CodeGenOnly, BitcodeLibFuncs, IRAddStream); | |
| }; | |
| // Like InProcessThinBackend, we produce index files as needed for | |
| // FirstRoundThinBackend. However, these files are not generated for | |
| // SecondRoundThinBackend. | |
| if (ShouldEmitIndexFiles) { | |
| if (auto E = emitFiles(ImportList, Task, ModuleID, ModuleID.str())) | |
| return E; | |
| } | |
| assert((CGCache.isValid() == IRCache.isValid()) && | |
| "Both caches for CG and IR should have matching availability"); | |
| if (!CGCache.isValid() || !CombinedIndex.modulePaths().count(ModuleID) || | |
| all_of(CombinedIndex.getModuleHash(ModuleID), | |
| [](uint32_t V) { return V == 0; })) | |
| // Cache disabled or no entry for this module in the combined index or | |
| // no module hash. | |
| return RunThinBackend(CGAddStream, IRAddStream); | |
| // Get CGKey for caching object in CGCache. | |
| std::string CGKey = computeLTOCacheKey( | |
| Conf, CombinedIndex, ModuleID, ImportList, ExportList, ResolvedODR, | |
| DefinedGlobals, CfiFunctionDefs, CfiFunctionDecls); | |
| Expected<AddStreamFn> CacheCGAddStreamOrErr = | |
| CGCache(Task, CGKey, ModuleID); | |
| if (Error Err = CacheCGAddStreamOrErr.takeError()) | |
| return Err; | |
| AddStreamFn &CacheCGAddStream = *CacheCGAddStreamOrErr; | |
| // Get IRKey for caching (optimized) IR in IRCache with an extra ID. | |
| std::string IRKey = recomputeLTOCacheKey(CGKey, /*ExtraID=*/"IR"); | |
| Expected<AddStreamFn> CacheIRAddStreamOrErr = | |
| IRCache(Task, IRKey, ModuleID); | |
| if (Error Err = CacheIRAddStreamOrErr.takeError()) | |
| return Err; | |
| AddStreamFn &CacheIRAddStream = *CacheIRAddStreamOrErr; | |
| // Ideally, both CG and IR caching should be synchronized. However, in | |
| // practice, their availability may differ due to different expiration | |
| // times. Therefore, if either cache is missing, the backend process is | |
| // triggered. | |
| if (CacheCGAddStream || CacheIRAddStream) { | |
| LLVM_DEBUG(dbgs() << "[FirstRound] Cache Miss for " | |
| << BM.getModuleIdentifier() << "\n"); | |
| return RunThinBackend(CacheCGAddStream ? CacheCGAddStream : CGAddStream, | |
| CacheIRAddStream ? CacheIRAddStream : IRAddStream); | |
| } | |
| return Error::success(); | |
| } | |
| }; | |
| /// This backend operates in the second round of a two-codegen round process. | |
| /// It starts by reading the optimized bitcode files that were saved during the | |
| /// first round. The backend then executes the codegen only to further optimize | |
| /// the code, utilizing the codegen data merged from the first round. Finally, | |
| /// it writes the resulting object files as usual. | |
| class SecondRoundThinBackend : public InProcessThinBackend { | |
| std::unique_ptr<SmallVector<StringRef>> IRFiles; | |
| stable_hash CombinedCGDataHash; | |
| public: | |
| SecondRoundThinBackend( | |
| const Config &Conf, ModuleSummaryIndex &CombinedIndex, | |
| ThreadPoolStrategy ThinLTOParallelism, | |
| const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, | |
| AddStreamFn AddStream, FileCache Cache, | |
| ArrayRef<StringRef> BitcodeLibFuncs, | |
| std::unique_ptr<SmallVector<StringRef>> IRFiles, | |
| stable_hash CombinedCGDataHash) | |
| : InProcessThinBackend(Conf, CombinedIndex, ThinLTOParallelism, | |
| ModuleToDefinedGVSummaries, std::move(AddStream), | |
| std::move(Cache), | |
| /*OnWrite=*/nullptr, | |
| /*ShouldEmitIndexFiles=*/false, | |
| /*ShouldEmitImportsFiles=*/false, BitcodeLibFuncs), | |
| IRFiles(std::move(IRFiles)), CombinedCGDataHash(CombinedCGDataHash) {} | |
| Error runThinLTOBackendThread( | |
| AddStreamFn AddStream, FileCache Cache, unsigned Task, BitcodeModule BM, | |
| ModuleSummaryIndex &CombinedIndex, | |
| const FunctionImporter::ImportMapTy &ImportList, | |
| const FunctionImporter::ExportSetTy &ExportList, | |
| const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR, | |
| const GVSummaryMapTy &DefinedGlobals, | |
| MapVector<StringRef, BitcodeModule> &ModuleMap) override { | |
| auto ModuleID = BM.getModuleIdentifier(); | |
| llvm::TimeTraceScope timeScope("Run ThinLTO backend thread (second round)", | |
| ModuleID); | |
| auto RunThinBackend = [&](AddStreamFn AddStream) { | |
| LTOLLVMContext BackendContext(Conf); | |
| std::unique_ptr<Module> LoadedModule = | |
| cgdata::loadModuleForTwoRounds(BM, Task, BackendContext, *IRFiles); | |
| return thinBackend(Conf, Task, AddStream, *LoadedModule, CombinedIndex, | |
| ImportList, DefinedGlobals, &ModuleMap, | |
| /*CodeGenOnly=*/true, BitcodeLibFuncs); | |
| }; | |
| if (!Cache.isValid() || !CombinedIndex.modulePaths().count(ModuleID) || | |
| all_of(CombinedIndex.getModuleHash(ModuleID), | |
| [](uint32_t V) { return V == 0; })) | |
| // Cache disabled or no entry for this module in the combined index or | |
| // no module hash. | |
| return RunThinBackend(AddStream); | |
| // Get Key for caching the final object file in Cache with the combined | |
| // CGData hash. | |
| std::string Key = computeLTOCacheKey( | |
| Conf, CombinedIndex, ModuleID, ImportList, ExportList, ResolvedODR, | |
| DefinedGlobals, CfiFunctionDefs, CfiFunctionDecls); | |
| Key = recomputeLTOCacheKey(Key, | |
| /*ExtraID=*/std::to_string(CombinedCGDataHash)); | |
| Expected<AddStreamFn> CacheAddStreamOrErr = Cache(Task, Key, ModuleID); | |
| if (Error Err = CacheAddStreamOrErr.takeError()) | |
| return Err; | |
| AddStreamFn &CacheAddStream = *CacheAddStreamOrErr; | |
| if (CacheAddStream) { | |
| LLVM_DEBUG(dbgs() << "[SecondRound] Cache Miss for " | |
| << BM.getModuleIdentifier() << "\n"); | |
| return RunThinBackend(CacheAddStream); | |
| } | |
| return Error::success(); | |
| } | |
| }; | |
| } // end anonymous namespace | |
| ThinBackend lto::createInProcessThinBackend(ThreadPoolStrategy Parallelism, | |
| lto::IndexWriteCallback OnWrite, | |
| bool ShouldEmitIndexFiles, | |
| bool ShouldEmitImportsFiles) { | |
| auto Func = | |
| [=](const Config &Conf, ModuleSummaryIndex &CombinedIndex, | |
| const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, | |
| AddStreamFn AddStream, FileCache Cache, | |
| ArrayRef<StringRef> BitcodeLibFuncs) { | |
| return std::make_unique<InProcessThinBackend>( | |
| Conf, CombinedIndex, Parallelism, ModuleToDefinedGVSummaries, | |
| AddStream, Cache, OnWrite, ShouldEmitIndexFiles, | |
| ShouldEmitImportsFiles, BitcodeLibFuncs); | |
| }; | |
| return ThinBackend(Func, Parallelism); | |
| } | |
| StringLiteral lto::getThinLTODefaultCPU(const Triple &TheTriple) { | |
| if (!TheTriple.isOSDarwin()) | |
| return ""; | |
| if (TheTriple.getArch() == Triple::x86_64) | |
| return "core2"; | |
| if (TheTriple.getArch() == Triple::x86) | |
| return "yonah"; | |
| if (TheTriple.isArm64e()) | |
| return "apple-a12"; | |
| if (TheTriple.getArch() == Triple::aarch64 || | |
| TheTriple.getArch() == Triple::aarch64_32) | |
| return "cyclone"; | |
| return ""; | |
| } | |
| // Given the original \p Path to an output file, replace any path | |
| // prefix matching \p OldPrefix with \p NewPrefix. Also, create the | |
| // resulting directory if it does not yet exist. | |
| std::string lto::getThinLTOOutputFile(StringRef Path, StringRef OldPrefix, | |
| StringRef NewPrefix) { | |
| if (OldPrefix.empty() && NewPrefix.empty()) | |
| return std::string(Path); | |
| SmallString<128> NewPath(Path); | |
| llvm::sys::path::replace_path_prefix(NewPath, OldPrefix, NewPrefix); | |
| StringRef ParentPath = llvm::sys::path::parent_path(NewPath.str()); | |
| if (!ParentPath.empty()) { | |
| // Make sure the new directory exists, creating it if necessary. | |
| if (std::error_code EC = llvm::sys::fs::create_directories(ParentPath)) | |
| llvm::errs() << "warning: could not create directory '" << ParentPath | |
| << "': " << EC.message() << '\n'; | |
| } | |
| return std::string(NewPath); | |
| } | |
| namespace { | |
| class WriteIndexesThinBackend : public ThinBackendProc { | |
| std::string OldPrefix, NewPrefix, NativeObjectPrefix; | |
| raw_fd_ostream *LinkedObjectsFile; | |
| DenseSet<GlobalValue::GUID> CfiFunctionDefs; | |
| DenseSet<GlobalValue::GUID> CfiFunctionDecls; | |
| public: | |
| WriteIndexesThinBackend( | |
| const Config &Conf, ModuleSummaryIndex &CombinedIndex, | |
| ThreadPoolStrategy ThinLTOParallelism, | |
| const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, | |
| std::string OldPrefix, std::string NewPrefix, | |
| std::string NativeObjectPrefix, bool ShouldEmitImportsFiles, | |
| raw_fd_ostream *LinkedObjectsFile, lto::IndexWriteCallback OnWrite) | |
| : ThinBackendProc(Conf, CombinedIndex, ModuleToDefinedGVSummaries, | |
| OnWrite, ShouldEmitImportsFiles, ThinLTOParallelism), | |
| OldPrefix(OldPrefix), NewPrefix(NewPrefix), | |
| NativeObjectPrefix(NativeObjectPrefix), | |
| LinkedObjectsFile(LinkedObjectsFile) { | |
| auto Defs = CombinedIndex.cfiFunctionDefs().getExportedThinLTOGUIDs(); | |
| CfiFunctionDefs.insert(Defs.begin(), Defs.end()); | |
| auto Decls = CombinedIndex.cfiFunctionDecls().getExportedThinLTOGUIDs(); | |
| CfiFunctionDecls.insert(Decls.begin(), Decls.end()); | |
| } | |
| Error start( | |
| unsigned Task, BitcodeModule BM, | |
| const FunctionImporter::ImportMapTy &ImportList, | |
| const FunctionImporter::ExportSetTy &ExportList, | |
| const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> &ResolvedODR, | |
| MapVector<StringRef, BitcodeModule> &ModuleMap) override { | |
| StringRef ModulePath = BM.getModuleIdentifier(); | |
| // The contents of this file may be used as input to a native link, and must | |
| // therefore contain the processed modules in a determinstic order that | |
| // match the order they are provided on the command line. For that reason, | |
| // we cannot include this in the asynchronously executed lambda below. | |
| if (LinkedObjectsFile) { | |
| std::string ObjectPrefix = | |
| NativeObjectPrefix.empty() ? NewPrefix : NativeObjectPrefix; | |
| std::string LinkedObjectsFilePath = | |
| getThinLTOOutputFile(ModulePath, OldPrefix, ObjectPrefix); | |
| *LinkedObjectsFile << LinkedObjectsFilePath << '\n'; | |
| } | |
| BackendThreadPool.async( | |
| [this](unsigned Task, const StringRef ModulePath, | |
| const FunctionImporter::ImportMapTy &ImportList, | |
| const FunctionImporter::ExportSetTy &ExportList, | |
| const std::map<GlobalValue::GUID, GlobalValue::LinkageTypes> | |
| &ResolvedODR, | |
| const std::string &OldPrefix, const std::string &NewPrefix) { | |
| std::string NewModulePath = | |
| getThinLTOOutputFile(ModulePath, OldPrefix, NewPrefix); | |
| auto E = emitFiles(ImportList, Task, ModulePath, NewModulePath); | |
| if (E) { | |
| std::unique_lock<std::mutex> L(ErrMu); | |
| if (Err) | |
| Err = joinErrors(std::move(*Err), std::move(E)); | |
| else | |
| Err = std::move(E); | |
| } | |
| assert(ModuleToDefinedGVSummaries.count(ModulePath)); | |
| const GVSummaryMapTy &DefinedGlobals = | |
| ModuleToDefinedGVSummaries.find(ModulePath)->second; | |
| // DTLTO needs the per-module LTO cache key to probe the cache. | |
| if (Conf.GetCacheKeyOutputString) { | |
| std::string &CacheKey = Conf.GetCacheKeyOutputString(Task); | |
| CacheKey = computeLTOCacheKey( | |
| Conf, CombinedIndex, ModulePath, ImportList, ExportList, | |
| ResolvedODR, DefinedGlobals, CfiFunctionDefs, CfiFunctionDecls); | |
| } | |
| }, | |
| Task, ModulePath, ImportList, ExportList, ResolvedODR, OldPrefix, | |
| NewPrefix); | |
| if (OnWrite) | |
| OnWrite(std::string(ModulePath)); | |
| return Error::success(); | |
| } | |
| bool isSensitiveToInputOrder() override { | |
| // The order which modules are written to LinkedObjectsFile should be | |
| // deterministic and match the order they are passed on the command line. | |
| return true; | |
| } | |
| }; | |
| } // end anonymous namespace | |
| ThinBackend lto::createWriteIndexesThinBackend( | |
| ThreadPoolStrategy Parallelism, std::string OldPrefix, | |
| std::string NewPrefix, std::string NativeObjectPrefix, | |
| bool ShouldEmitImportsFiles, raw_fd_ostream *LinkedObjectsFile, | |
| IndexWriteCallback OnWrite) { | |
| auto Func = | |
| [=](const Config &Conf, ModuleSummaryIndex &CombinedIndex, | |
| const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries, | |
| AddStreamFn AddStream, FileCache Cache, | |
| ArrayRef<StringRef> BitcodeLibFuncs) { | |
| return std::make_unique<WriteIndexesThinBackend>( | |
| Conf, CombinedIndex, Parallelism, ModuleToDefinedGVSummaries, | |
| OldPrefix, NewPrefix, NativeObjectPrefix, ShouldEmitImportsFiles, | |
| LinkedObjectsFile, OnWrite); | |
| }; | |
| return ThinBackend(Func, Parallelism); | |
| } | |
| Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache, | |
| const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols) { | |
| llvm::TimeTraceScope timeScope("Run ThinLTO"); | |
| LLVM_DEBUG(dbgs() << "Running ThinLTO\n"); | |
| ThinLTO.CombinedIndex.releaseTemporaryMemory(); | |
| timeTraceProfilerBegin("ThinLink", StringRef("")); | |
| llvm::scope_exit TimeTraceScopeExit([]() { | |
| if (llvm::timeTraceProfilerEnabled()) | |
| llvm::timeTraceProfilerEnd(); | |
| }); | |
| if (ThinLTO.ModuleMap.empty()) | |
| return Error::success(); | |
| if (ThinLTO.ModulesToCompile && ThinLTO.ModulesToCompile->empty()) { | |
| llvm::errs() << "warning: [ThinLTO] No module compiled\n"; | |
| return Error::success(); | |
| } | |
| if (Conf.CombinedIndexHook && | |
| !Conf.CombinedIndexHook(ThinLTO.CombinedIndex, GUIDPreservedSymbols)) | |
| return Error::success(); | |
| // Collect for each module the list of function it defines (GUID -> | |
| // Summary). | |
| DenseMap<StringRef, GVSummaryMapTy> ModuleToDefinedGVSummaries( | |
| ThinLTO.ModuleMap.size()); | |
| ThinLTO.CombinedIndex.collectDefinedGVSummariesPerModule( | |
| ModuleToDefinedGVSummaries); | |
| // Create entries for any modules that didn't have any GV summaries | |
| // (either they didn't have any GVs to start with, or we suppressed | |
| // generation of the summaries because they e.g. had inline assembly | |
| // uses that couldn't be promoted/renamed on export). This is so | |
| // InProcessThinBackend::start can still launch a backend thread, which | |
| // is passed the map of summaries for the module, without any special | |
| // handling for this case. | |
| for (auto &Mod : ThinLTO.ModuleMap) | |
| if (!ModuleToDefinedGVSummaries.count(Mod.first)) | |
| ModuleToDefinedGVSummaries.try_emplace(Mod.first); | |
| FunctionImporter::ImportListsTy ImportLists(ThinLTO.ModuleMap.size()); | |
| DenseMap<StringRef, FunctionImporter::ExportSetTy> ExportLists( | |
| ThinLTO.ModuleMap.size()); | |
| StringMap<std::map<GlobalValue::GUID, GlobalValue::LinkageTypes>> ResolvedODR; | |
| if (DumpThinCGSCCs) | |
| ThinLTO.CombinedIndex.dumpSCCs(outs()); | |
| std::set<GlobalValue::GUID> ExportedGUIDs; | |
| bool WholeProgramVisibilityEnabledInLTO = | |
| Conf.HasWholeProgramVisibility && | |
| // If validation is enabled, upgrade visibility only when all vtables | |
| // have typeinfos. | |
| (!Conf.ValidateAllVtablesHaveTypeInfos || Conf.AllVtablesHaveTypeInfos); | |
| if (hasWholeProgramVisibility(WholeProgramVisibilityEnabledInLTO)) | |
| ThinLTO.CombinedIndex.setWithWholeProgramVisibility(); | |
| // If we're validating, get the vtable symbols that should not be | |
| // upgraded because they correspond to typeIDs outside of index-based | |
| // WPD info. | |
| DenseSet<GlobalValue::GUID> VisibleToRegularObjSymbols; | |
| if (WholeProgramVisibilityEnabledInLTO && | |
| Conf.ValidateAllVtablesHaveTypeInfos) { | |
| // This returns true when the name is local or not defined. Locals are | |
| // expected to be handled separately. | |
| auto IsVisibleToRegularObj = [&](StringRef name) { | |
| auto It = GlobalResolutions->find(name); | |
| return (It == GlobalResolutions->end() || | |
| It->second.VisibleOutsideSummary || !It->second.Prevailing); | |
| }; | |
| getVisibleToRegularObjVtableGUIDs(ThinLTO.CombinedIndex, | |
| VisibleToRegularObjSymbols, | |
| IsVisibleToRegularObj); | |
| } | |
| // If allowed, upgrade public vcall visibility to linkage unit visibility in | |
| // the summaries before whole program devirtualization below. | |
| updateVCallVisibilityInIndex( | |
| ThinLTO.CombinedIndex, WholeProgramVisibilityEnabledInLTO, | |
| DynamicExportSymbols, VisibleToRegularObjSymbols); | |
| // Perform index-based WPD. This will return immediately if there are | |
| // no index entries in the typeIdMetadata map (e.g. if we are instead | |
| // performing IR-based WPD in hybrid regular/thin LTO mode). | |
| std::map<ValueInfo, std::vector<VTableSlotSummary>> LocalWPDTargetsMap; | |
| DenseSet<StringRef> ExternallyVisibleSymbolNames; | |
| // Used by the promotion-time renaming logic. When non-null, this set | |
| // identifies symbols that should not be renamed during promotion. | |
| // It is non-null only when whole-program visibility is enabled and | |
| // renaming is not forced. Otherwise, the default renaming behavior applies. | |
| DenseSet<StringRef> *ExternallyVisibleSymbolNamesPtr = | |
| (WholeProgramVisibilityEnabledInLTO && !AlwaysRenamePromotedLocals) | |
| ? &ExternallyVisibleSymbolNames | |
| : nullptr; | |
| runWholeProgramDevirtOnIndex(ThinLTO.CombinedIndex, ExportedGUIDs, | |
| LocalWPDTargetsMap, | |
| ExternallyVisibleSymbolNamesPtr); | |
| auto isPrevailing = [&](GlobalValue::GUID GUID, const GlobalValueSummary *S) { | |
| return ThinLTO.isPrevailingModuleForGUID(GUID, S->modulePath()); | |
| }; | |
| if (EnableMemProfContextDisambiguation) { | |
| MemProfContextDisambiguation ContextDisambiguation; | |
| ContextDisambiguation.run( | |
| ThinLTO.CombinedIndex, isPrevailing, RegularLTO.Ctx, | |
| [&](StringRef PassName, StringRef RemarkName, const Twine &Msg) { | |
| auto R = OptimizationRemark(PassName.data(), RemarkName, | |
| LinkerRemarkFunction); | |
| R << Msg.str(); | |
| emitRemark(R); | |
| }); | |
| } | |
| // Figure out which symbols need to be internalized. This also needs to happen | |
| // at -O0 because summary-based DCE is implemented using internalization, and | |
| // we must apply DCE consistently with the full LTO module in order to avoid | |
| // undefined references during the final link. | |
| for (auto &Res : *GlobalResolutions) { | |
| // If the symbol does not have external references or it is not prevailing, | |
| // then not need to mark it as exported from a ThinLTO partition. | |
| if (Res.second.Partition != GlobalResolution::External || | |
| !Res.second.isPrevailingIRSymbol()) | |
| continue; | |
| auto GUID = Res.second.getGUID(); | |
| // Mark exported unless index-based analysis determined it to be dead. | |
| if (ThinLTO.CombinedIndex.isGUIDLive(GUID)) | |
| ExportedGUIDs.insert(GUID); | |
| } | |
| // Reset the GlobalResolutions to deallocate the associated memory, as there | |
| // are no further accesses. We specifically want to do this before computing | |
| // cross module importing, which adds to peak memory via the computed import | |
| // and export lists. | |
| releaseGlobalResolutionsMemory(); | |
| if (Conf.OptLevel > 0) | |
| ComputeCrossModuleImport(ThinLTO.CombinedIndex, ModuleToDefinedGVSummaries, | |
| isPrevailing, ImportLists, ExportLists); | |
| // Any functions referenced by the jump table in the regular LTO object must | |
| // be exported. | |
| auto Defs = ThinLTO.CombinedIndex.cfiFunctionDefs().getExportedThinLTOGUIDs(); | |
| ExportedGUIDs.insert(Defs.begin(), Defs.end()); | |
| auto Decls = | |
| ThinLTO.CombinedIndex.cfiFunctionDecls().getExportedThinLTOGUIDs(); | |
| ExportedGUIDs.insert(Decls.begin(), Decls.end()); | |
| auto isExported = [&](StringRef ModuleIdentifier, ValueInfo VI) { | |
| const auto &ExportList = ExportLists.find(ModuleIdentifier); | |
| return (ExportList != ExportLists.end() && ExportList->second.count(VI)) || | |
| ExportedGUIDs.count(VI.getGUID()); | |
| }; | |
| // Update local devirtualized targets that were exported by cross-module | |
| // importing or by other devirtualizations marked in the ExportedGUIDs set. | |
| updateIndexWPDForExports(ThinLTO.CombinedIndex, isExported, | |
| LocalWPDTargetsMap, ExternallyVisibleSymbolNamesPtr); | |
| if (ExternallyVisibleSymbolNamesPtr) { | |
| // Add to ExternallyVisibleSymbolNames the set of unique names used by all | |
| // externally visible symbols in the index. | |
| for (auto &I : ThinLTO.CombinedIndex) { | |
| ValueInfo VI = ThinLTO.CombinedIndex.getValueInfo(I); | |
| for (const auto &Summary : VI.getSummaryList()) { | |
| const GlobalValueSummary *Base = Summary->getBaseObject(); | |
| if (GlobalValue::isLocalLinkage(Base->linkage())) | |
| continue; | |
| ExternallyVisibleSymbolNamesPtr->insert(VI.name()); | |
| break; | |
| } | |
| } | |
| } | |
| thinLTOInternalizeAndPromoteInIndex(ThinLTO.CombinedIndex, isExported, | |
| isPrevailing, | |
| ExternallyVisibleSymbolNamesPtr); | |
| auto recordNewLinkage = [&](StringRef ModuleIdentifier, | |
| GlobalValue::GUID GUID, | |
| GlobalValue::LinkageTypes NewLinkage) { | |
| ResolvedODR[ModuleIdentifier][GUID] = NewLinkage; | |
| }; | |
| thinLTOResolvePrevailingInIndex(Conf, ThinLTO.CombinedIndex, isPrevailing, | |
| recordNewLinkage, GUIDPreservedSymbols); | |
| thinLTOPropagateFunctionAttrs(ThinLTO.CombinedIndex, isPrevailing); | |
| generateParamAccessSummary(ThinLTO.CombinedIndex); | |
| if (llvm::timeTraceProfilerEnabled()) | |
| llvm::timeTraceProfilerEnd(); | |
| TimeTraceScopeExit.release(); | |
| auto &ModuleMap = | |
| ThinLTO.ModulesToCompile ? *ThinLTO.ModulesToCompile : ThinLTO.ModuleMap; | |
| auto RunBackends = [&](ThinBackendProc *BackendProcess) -> Error { | |
| auto ProcessOneModule = [&](int I) -> Error { | |
| auto &Mod = *(ModuleMap.begin() + I); | |
| // Tasks 0 through ParallelCodeGenParallelismLevel-1 are reserved for | |
| // combined module and parallel code generation partitions. | |
| return BackendProcess->start( | |
| RegularLTO.ParallelCodeGenParallelismLevel + I, Mod.second, | |
| ImportLists[Mod.first], ExportLists[Mod.first], | |
| ResolvedODR[Mod.first], ThinLTO.ModuleMap); | |
| }; | |
| BackendProcess->setup(ModuleMap.size(), | |
| RegularLTO.ParallelCodeGenParallelismLevel, | |
| RegularLTO.CombinedModule->getTargetTriple()); | |
| if (BackendProcess->getThreadCount() == 1 || | |
| BackendProcess->isSensitiveToInputOrder()) { | |
| // Process the modules in the order they were provided on the | |
| // command-line. It is important for this codepath to be used for | |
| // WriteIndexesThinBackend, to ensure the emitted LinkedObjectsFile lists | |
| // ThinLTO objects in the same order as the inputs, which otherwise would | |
| // affect the final link order. | |
| for (int I = 0, E = ModuleMap.size(); I != E; ++I) | |
| if (Error E = ProcessOneModule(I)) | |
| return E; | |
| } else { | |
| // When executing in parallel, process largest bitsize modules first to | |
| // improve parallelism, and avoid starving the thread pool near the end. | |
| // This saves about 15 sec on a 36-core machine while link `clang.exe` | |
| // (out of 100 sec). | |
| std::vector<BitcodeModule *> ModulesVec; | |
| ModulesVec.reserve(ModuleMap.size()); | |
| for (auto &Mod : ModuleMap) | |
| ModulesVec.push_back(&Mod.second); | |
| for (int I : generateModulesOrdering(ModulesVec)) | |
| if (Error E = ProcessOneModule(I)) | |
| return E; | |
| } | |
| return BackendProcess->wait(); | |
| }; | |
| if (!cgdata::thinLTOTwoRounds()) { | |
| std::unique_ptr<ThinBackendProc> BackendProc = | |
| ThinLTO.Backend(Conf, ThinLTO.CombinedIndex, ModuleToDefinedGVSummaries, | |
| AddStream, Cache, BitcodeLibFuncs); | |
| return RunBackends(BackendProc.get()); | |
| } | |
| // Perform two rounds of code generation for ThinLTO: | |
| // 1. First round: Perform optimization and code generation, outputting to | |
| // temporary scratch objects. | |
| // 2. Merge code generation data extracted from the temporary scratch objects. | |
| // 3. Second round: Execute code generation again using the merged data. | |
| LLVM_DEBUG(dbgs() << "[TwoRounds] Initializing ThinLTO two-codegen rounds\n"); | |
| unsigned MaxTasks = getMaxTasks(); | |
| auto Parallelism = ThinLTO.Backend.getParallelism(); | |
| // Set up two additional streams and caches for storing temporary scratch | |
| // objects and optimized IRs, using the same cache directory as the original. | |
| cgdata::StreamCacheData CG(MaxTasks, Cache, "CG"), IR(MaxTasks, Cache, "IR"); | |
| // First round: Execute optimization and code generation, outputting to | |
| // temporary scratch objects. Serialize the optimized IRs before initiating | |
| // code generation. | |
| LLVM_DEBUG(dbgs() << "[TwoRounds] Running the first round of codegen\n"); | |
| auto FirstRoundLTO = std::make_unique<FirstRoundThinBackend>( | |
| Conf, ThinLTO.CombinedIndex, Parallelism, ModuleToDefinedGVSummaries, | |
| CG.AddStream, CG.Cache, BitcodeLibFuncs, IR.AddStream, IR.Cache); | |
| if (Error E = RunBackends(FirstRoundLTO.get())) | |
| return E; | |
| LLVM_DEBUG(dbgs() << "[TwoRounds] Merging codegen data\n"); | |
| auto CombinedHashOrErr = cgdata::mergeCodeGenData(*CG.getResult()); | |
| if (Error E = CombinedHashOrErr.takeError()) | |
| return E; | |
| auto CombinedHash = *CombinedHashOrErr; | |
| LLVM_DEBUG(dbgs() << "[TwoRounds] CGData hash: " << CombinedHash << "\n"); | |
| // Second round: Read the optimized IRs and execute code generation using the | |
| // merged data. | |
| LLVM_DEBUG(dbgs() << "[TwoRounds] Running the second round of codegen\n"); | |
| auto SecondRoundLTO = std::make_unique<SecondRoundThinBackend>( | |
| Conf, ThinLTO.CombinedIndex, Parallelism, ModuleToDefinedGVSummaries, | |
| AddStream, Cache, BitcodeLibFuncs, IR.getResult(), CombinedHash); | |
| return RunBackends(SecondRoundLTO.get()); | |
| } | |
| Expected<LLVMRemarkFileHandle> lto::setupLLVMOptimizationRemarks( | |
| LLVMContext &Context, StringRef RemarksFilename, StringRef RemarksPasses, | |
| StringRef RemarksFormat, bool RemarksWithHotness, | |
| std::optional<uint64_t> RemarksHotnessThreshold, int Count) { | |
| std::string Filename = std::string(RemarksFilename); | |
| // For ThinLTO, file.opt.<format> becomes | |
| // file.opt.<format>.thin.<num>.<format>. | |
| if (!Filename.empty() && Count != -1) | |
| Filename = | |
| (Twine(Filename) + ".thin." + llvm::utostr(Count) + "." + RemarksFormat) | |
| .str(); | |
| auto ResultOrErr = llvm::setupLLVMOptimizationRemarks( | |
| Context, Filename, RemarksPasses, RemarksFormat, RemarksWithHotness, | |
| RemarksHotnessThreshold); | |
| if (Error E = ResultOrErr.takeError()) | |
| return std::move(E); | |
| if (*ResultOrErr) | |
| (*ResultOrErr)->keep(); | |
| return ResultOrErr; | |
| } | |
| Expected<std::unique_ptr<ToolOutputFile>> | |
| lto::setupStatsFile(StringRef StatsFilename) { | |
| // Setup output file to emit statistics. | |
| if (StatsFilename.empty()) | |
| return nullptr; | |
| llvm::EnableStatistics(false); | |
| std::error_code EC; | |
| auto StatsFile = | |
| std::make_unique<ToolOutputFile>(StatsFilename, EC, sys::fs::OF_None); | |
| if (EC) | |
| return errorCodeToError(EC); | |
| StatsFile->keep(); | |
| return std::move(StatsFile); | |
| } | |
| // Compute the ordering we will process the inputs: the rough heuristic here | |
| // is to sort them per size so that the largest module get schedule as soon as | |
| // possible. This is purely a compile-time optimization. | |
| std::vector<int> lto::generateModulesOrdering(ArrayRef<BitcodeModule *> R) { | |
| auto Seq = llvm::seq<int>(0, R.size()); | |
| std::vector<int> ModulesOrdering(Seq.begin(), Seq.end()); | |
| llvm::sort(ModulesOrdering, [&](int LeftIndex, int RightIndex) { | |
| auto LSize = R[LeftIndex]->getBuffer().size(); | |
| auto RSize = R[RightIndex]->getBuffer().size(); | |
| return LSize > RSize; | |
| }); | |
| return ModulesOrdering; | |
| } |