diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2019-01-19 10:01:25 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2019-01-19 10:01:25 +0000 |
commit | d8e91e46262bc44006913e6796843909f1ac7bcd (patch) | |
tree | 7d0c143d9b38190e0fa0180805389da22cd834c5 /lib/Transforms/IPO/FunctionImport.cpp | |
parent | b7eb8e35e481a74962664b63dfb09483b200209a (diff) |
Notes
Diffstat (limited to 'lib/Transforms/IPO/FunctionImport.cpp')
-rw-r--r-- | lib/Transforms/IPO/FunctionImport.cpp | 262 |
1 files changed, 218 insertions, 44 deletions
diff --git a/lib/Transforms/IPO/FunctionImport.cpp b/lib/Transforms/IPO/FunctionImport.cpp index 15808a073894..1223a23512ed 100644 --- a/lib/Transforms/IPO/FunctionImport.cpp +++ b/lib/Transforms/IPO/FunctionImport.cpp @@ -60,8 +60,17 @@ using namespace llvm; #define DEBUG_TYPE "function-import" -STATISTIC(NumImportedFunctions, "Number of functions imported"); -STATISTIC(NumImportedGlobalVars, "Number of global variables imported"); +STATISTIC(NumImportedFunctionsThinLink, + "Number of functions thin link decided to import"); +STATISTIC(NumImportedHotFunctionsThinLink, + "Number of hot functions thin link decided to import"); +STATISTIC(NumImportedCriticalFunctionsThinLink, + "Number of critical functions thin link decided to import"); +STATISTIC(NumImportedGlobalVarsThinLink, + "Number of global variables thin link decided to import"); +STATISTIC(NumImportedFunctions, "Number of functions imported in backend"); +STATISTIC(NumImportedGlobalVars, + "Number of global variables imported in backend"); STATISTIC(NumImportedModules, "Number of modules imported from"); STATISTIC(NumDeadSymbols, "Number of dead stripped symbols in index"); STATISTIC(NumLiveSymbols, "Number of live symbols in index"); @@ -107,6 +116,10 @@ static cl::opt<float> ImportColdMultiplier( static cl::opt<bool> PrintImports("print-imports", cl::init(false), cl::Hidden, cl::desc("Print imported functions")); +static cl::opt<bool> PrintImportFailures( + "print-import-failures", cl::init(false), cl::Hidden, + cl::desc("Print information for functions rejected for importing")); + static cl::opt<bool> ComputeDead("compute-dead", cl::init(true), cl::Hidden, cl::desc("Compute dead symbols")); @@ -163,13 +176,18 @@ static std::unique_ptr<Module> loadFile(const std::string &FileName, static const GlobalValueSummary * selectCallee(const ModuleSummaryIndex &Index, ArrayRef<std::unique_ptr<GlobalValueSummary>> CalleeSummaryList, - unsigned Threshold, StringRef CallerModulePath) { + unsigned Threshold, StringRef CallerModulePath, + FunctionImporter::ImportFailureReason &Reason, + GlobalValue::GUID GUID) { + Reason = FunctionImporter::ImportFailureReason::None; auto It = llvm::find_if( CalleeSummaryList, [&](const std::unique_ptr<GlobalValueSummary> &SummaryPtr) { auto *GVSummary = SummaryPtr.get(); - if (!Index.isGlobalValueLive(GVSummary)) + if (!Index.isGlobalValueLive(GVSummary)) { + Reason = FunctionImporter::ImportFailureReason::NotLive; return false; + } // For SamplePGO, in computeImportForFunction the OriginalId // may have been used to locate the callee summary list (See @@ -184,11 +202,15 @@ selectCallee(const ModuleSummaryIndex &Index, // When this happens, the logic for SamplePGO kicks in and // the static variable in 2) will be found, which needs to be // filtered out. - if (GVSummary->getSummaryKind() == GlobalValueSummary::GlobalVarKind) + if (GVSummary->getSummaryKind() == GlobalValueSummary::GlobalVarKind) { + Reason = FunctionImporter::ImportFailureReason::GlobalVar; return false; - if (GlobalValue::isInterposableLinkage(GVSummary->linkage())) + } + if (GlobalValue::isInterposableLinkage(GVSummary->linkage())) { + Reason = FunctionImporter::ImportFailureReason::InterposableLinkage; // There is no point in importing these, we can't inline them return false; + } auto *Summary = cast<FunctionSummary>(GVSummary->getBaseObject()); @@ -204,14 +226,29 @@ selectCallee(const ModuleSummaryIndex &Index, // a local in another module. if (GlobalValue::isLocalLinkage(Summary->linkage()) && CalleeSummaryList.size() > 1 && - Summary->modulePath() != CallerModulePath) + Summary->modulePath() != CallerModulePath) { + Reason = + FunctionImporter::ImportFailureReason::LocalLinkageNotInModule; return false; + } - if (Summary->instCount() > Threshold) + if (Summary->instCount() > Threshold) { + Reason = FunctionImporter::ImportFailureReason::TooLarge; return false; + } - if (Summary->notEligibleToImport()) + // Skip if it isn't legal to import (e.g. may reference unpromotable + // locals). + if (Summary->notEligibleToImport()) { + Reason = FunctionImporter::ImportFailureReason::NotEligible; return false; + } + + // Don't bother importing if we can't inline it anyway. + if (Summary->fflags().NoInline) { + Reason = FunctionImporter::ImportFailureReason::NoInline; + return false; + } return true; }); @@ -256,13 +293,25 @@ static void computeImportForReferencedGlobals( LLVM_DEBUG(dbgs() << " ref -> " << VI << "\n"); + // If this is a local variable, make sure we import the copy + // in the caller's module. The only time a local variable can + // share an entry in the index is if there is a local with the same name + // in another module that had the same source file name (in a different + // directory), where each was compiled in their own directory so there + // was not distinguishing path. + auto LocalNotInModule = [&](const GlobalValueSummary *RefSummary) -> bool { + return GlobalValue::isLocalLinkage(RefSummary->linkage()) && + RefSummary->modulePath() != Summary.modulePath(); + }; + for (auto &RefSummary : VI.getSummaryList()) - if (RefSummary->getSummaryKind() == GlobalValueSummary::GlobalVarKind && - // Don't try to import regular LTO summaries added to dummy module. - !RefSummary->modulePath().empty() && - !GlobalValue::isInterposableLinkage(RefSummary->linkage()) && - RefSummary->refs().empty()) { - ImportList[RefSummary->modulePath()].insert(VI.getGUID()); + if (isa<GlobalVarSummary>(RefSummary.get()) && + canImportGlobalVar(RefSummary.get()) && + !LocalNotInModule(RefSummary.get())) { + auto ILI = ImportList[RefSummary->modulePath()].insert(VI.getGUID()); + // Only update stat if we haven't already imported this variable. + if (ILI.second) + NumImportedGlobalVarsThinLink++; if (ExportLists) (*ExportLists)[RefSummary->modulePath()].insert(VI.getGUID()); break; @@ -270,6 +319,29 @@ static void computeImportForReferencedGlobals( } } +static const char * +getFailureName(FunctionImporter::ImportFailureReason Reason) { + switch (Reason) { + case FunctionImporter::ImportFailureReason::None: + return "None"; + case FunctionImporter::ImportFailureReason::GlobalVar: + return "GlobalVar"; + case FunctionImporter::ImportFailureReason::NotLive: + return "NotLive"; + case FunctionImporter::ImportFailureReason::TooLarge: + return "TooLarge"; + case FunctionImporter::ImportFailureReason::InterposableLinkage: + return "InterposableLinkage"; + case FunctionImporter::ImportFailureReason::LocalLinkageNotInModule: + return "LocalLinkageNotInModule"; + case FunctionImporter::ImportFailureReason::NotEligible: + return "NotEligible"; + case FunctionImporter::ImportFailureReason::NoInline: + return "NoInline"; + } + llvm_unreachable("invalid reason"); +} + /// Compute the list of functions to import for a given caller. Mark these /// imported functions and the symbols they reference in their source module as /// exported from their source module. @@ -316,11 +388,17 @@ static void computeImportForFunction( const auto NewThreshold = Threshold * GetBonusMultiplier(Edge.second.getHotness()); - auto IT = ImportThresholds.insert( - std::make_pair(VI.getGUID(), std::make_pair(NewThreshold, nullptr))); + auto IT = ImportThresholds.insert(std::make_pair( + VI.getGUID(), std::make_tuple(NewThreshold, nullptr, nullptr))); bool PreviouslyVisited = !IT.second; - auto &ProcessedThreshold = IT.first->second.first; - auto &CalleeSummary = IT.first->second.second; + auto &ProcessedThreshold = std::get<0>(IT.first->second); + auto &CalleeSummary = std::get<1>(IT.first->second); + auto &FailureInfo = std::get<2>(IT.first->second); + + bool IsHotCallsite = + Edge.second.getHotness() == CalleeInfo::HotnessType::Hot; + bool IsCriticalCallsite = + Edge.second.getHotness() == CalleeInfo::HotnessType::Critical; const FunctionSummary *ResolvedCalleeSummary = nullptr; if (CalleeSummary) { @@ -345,16 +423,37 @@ static void computeImportForFunction( LLVM_DEBUG( dbgs() << "ignored! Target was already rejected with Threshold " << ProcessedThreshold << "\n"); + if (PrintImportFailures) { + assert(FailureInfo && + "Expected FailureInfo for previously rejected candidate"); + FailureInfo->Attempts++; + } continue; } + FunctionImporter::ImportFailureReason Reason; CalleeSummary = selectCallee(Index, VI.getSummaryList(), NewThreshold, - Summary.modulePath()); + Summary.modulePath(), Reason, VI.getGUID()); if (!CalleeSummary) { // Update with new larger threshold if this was a retry (otherwise - // we would have already inserted with NewThreshold above). - if (PreviouslyVisited) + // we would have already inserted with NewThreshold above). Also + // update failure info if requested. + if (PreviouslyVisited) { ProcessedThreshold = NewThreshold; + if (PrintImportFailures) { + assert(FailureInfo && + "Expected FailureInfo for previously rejected candidate"); + FailureInfo->Reason = Reason; + FailureInfo->Attempts++; + FailureInfo->MaxHotness = + std::max(FailureInfo->MaxHotness, Edge.second.getHotness()); + } + } else if (PrintImportFailures) { + assert(!FailureInfo && + "Expected no FailureInfo for newly rejected candidate"); + FailureInfo = llvm::make_unique<FunctionImporter::ImportFailureInfo>( + VI, Edge.second.getHotness(), Reason, 1); + } LLVM_DEBUG( dbgs() << "ignored! No qualifying callee with summary found.\n"); continue; @@ -372,6 +471,13 @@ static void computeImportForFunction( // We previously decided to import this GUID definition if it was already // inserted in the set of imports from the exporting module. bool PreviouslyImported = !ILI.second; + if (!PreviouslyImported) { + NumImportedFunctionsThinLink++; + if (IsHotCallsite) + NumImportedHotFunctionsThinLink++; + if (IsCriticalCallsite) + NumImportedCriticalFunctionsThinLink++; + } // Make exports in the source module. if (ExportLists) { @@ -405,8 +511,6 @@ static void computeImportForFunction( return Threshold * ImportInstrFactor; }; - bool IsHotCallsite = - Edge.second.getHotness() == CalleeInfo::HotnessType::Hot; const auto AdjThreshold = GetAdjustedThreshold(Threshold, IsHotCallsite); ImportCount++; @@ -421,7 +525,7 @@ static void computeImportForFunction( /// another module (that may require promotion). static void ComputeImportForModule( const GVSummaryMapTy &DefinedGVSummaries, const ModuleSummaryIndex &Index, - FunctionImporter::ImportMapTy &ImportList, + StringRef ModName, FunctionImporter::ImportMapTy &ImportList, StringMap<FunctionImporter::ExportSetTy> *ExportLists = nullptr) { // Worklist contains the list of function imported in this module, for which // we will analyse the callees and may import further down the callgraph. @@ -461,6 +565,30 @@ static void ComputeImportForModule( Worklist, ImportList, ExportLists, ImportThresholds); } + + // Print stats about functions considered but rejected for importing + // when requested. + if (PrintImportFailures) { + dbgs() << "Missed imports into module " << ModName << "\n"; + for (auto &I : ImportThresholds) { + auto &ProcessedThreshold = std::get<0>(I.second); + auto &CalleeSummary = std::get<1>(I.second); + auto &FailureInfo = std::get<2>(I.second); + if (CalleeSummary) + continue; // We are going to import. + assert(FailureInfo); + FunctionSummary *FS = nullptr; + if (!FailureInfo->VI.getSummaryList().empty()) + FS = dyn_cast<FunctionSummary>( + FailureInfo->VI.getSummaryList()[0]->getBaseObject()); + dbgs() << FailureInfo->VI + << ": Reason = " << getFailureName(FailureInfo->Reason) + << ", Threshold = " << ProcessedThreshold + << ", Size = " << (FS ? (int)FS->instCount() : -1) + << ", MaxHotness = " << getHotnessName(FailureInfo->MaxHotness) + << ", Attempts = " << FailureInfo->Attempts << "\n"; + } + } } #ifndef NDEBUG @@ -498,7 +626,8 @@ void llvm::ComputeCrossModuleImport( auto &ImportList = ImportLists[DefinedGVSummaries.first()]; LLVM_DEBUG(dbgs() << "Computing import for Module '" << DefinedGVSummaries.first() << "'\n"); - ComputeImportForModule(DefinedGVSummaries.second, Index, ImportList, + ComputeImportForModule(DefinedGVSummaries.second, Index, + DefinedGVSummaries.first(), ImportList, &ExportLists); } @@ -569,7 +698,7 @@ void llvm::ComputeCrossModuleImportForModule( // Compute the import list for this module. LLVM_DEBUG(dbgs() << "Computing import for Module '" << ModulePath << "'\n"); - ComputeImportForModule(FunctionSummaryMap, Index, ImportList); + ComputeImportForModule(FunctionSummaryMap, Index, ModulePath, ImportList); #ifndef NDEBUG dumpImportListForModule(Index, ModulePath, ImportList); @@ -648,29 +777,38 @@ void llvm::computeDeadSymbols( VI = updateValueInfoForIndirectCalls(Index, VI); if (!VI) return; - for (auto &S : VI.getSummaryList()) - if (S->isLive()) - return; + + // We need to make sure all variants of the symbol are scanned, alias can + // make one (but not all) alive. + if (llvm::all_of(VI.getSummaryList(), + [](const std::unique_ptr<llvm::GlobalValueSummary> &S) { + return S->isLive(); + })) + return; // We only keep live symbols that are known to be non-prevailing if any are - // available_externally. Those symbols are discarded later in the - // EliminateAvailableExternally pass and setting them to not-live breaks - // downstreams users of liveness information (PR36483). + // available_externally, linkonceodr, weakodr. Those symbols are discarded + // later in the EliminateAvailableExternally pass and setting them to + // not-live could break downstreams users of liveness information (PR36483) + // or limit optimization opportunities. if (isPrevailing(VI.getGUID()) == PrevailingType::No) { - bool AvailableExternally = false; + bool KeepAliveLinkage = false; bool Interposable = false; for (auto &S : VI.getSummaryList()) { - if (S->linkage() == GlobalValue::AvailableExternallyLinkage) - AvailableExternally = true; + if (S->linkage() == GlobalValue::AvailableExternallyLinkage || + S->linkage() == GlobalValue::WeakODRLinkage || + S->linkage() == GlobalValue::LinkOnceODRLinkage) + KeepAliveLinkage = true; else if (GlobalValue::isInterposableLinkage(S->linkage())) Interposable = true; } - if (!AvailableExternally) + if (!KeepAliveLinkage) return; if (Interposable) - report_fatal_error("Interposable and available_externally symbol"); + report_fatal_error( + "Interposable and available_externally/linkonce_odr/weak_odr symbol"); } for (auto &S : VI.getSummaryList()) @@ -701,6 +839,25 @@ void llvm::computeDeadSymbols( NumLiveSymbols += LiveSymbols; } +// Compute dead symbols and propagate constants in combined index. +void llvm::computeDeadSymbolsWithConstProp( + ModuleSummaryIndex &Index, + const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols, + function_ref<PrevailingType(GlobalValue::GUID)> isPrevailing, + bool ImportEnabled) { + computeDeadSymbols(Index, GUIDPreservedSymbols, isPrevailing); + if (ImportEnabled) { + Index.propagateConstants(GUIDPreservedSymbols); + } else { + // If import is disabled we should drop read-only attribute + // from all summaries to prevent internalization. + for (auto &P : Index) + for (auto &S : P.second.SummaryList) + if (auto *GVS = dyn_cast<GlobalVarSummary>(S.get())) + GVS->setReadOnly(false); + } +} + /// Compute the set of summaries needed for a ThinLTO backend compilation of /// \p ModulePath. void llvm::gatherImportedSummariesForModule( @@ -759,7 +916,8 @@ bool llvm::convertToDeclaration(GlobalValue &GV) { if (GV.getValueType()->isFunctionTy()) NewGV = Function::Create(cast<FunctionType>(GV.getValueType()), - GlobalValue::ExternalLinkage, "", GV.getParent()); + GlobalValue::ExternalLinkage, GV.getAddressSpace(), + "", GV.getParent()); else NewGV = new GlobalVariable(*GV.getParent(), GV.getValueType(), @@ -774,8 +932,8 @@ bool llvm::convertToDeclaration(GlobalValue &GV) { return true; } -/// Fixup WeakForLinker linkages in \p TheModule based on summary analysis. -void llvm::thinLTOResolveWeakForLinkerModule( +/// Fixup prevailing symbol linkages in \p TheModule based on summary analysis. +void llvm::thinLTOResolvePrevailingInModule( Module &TheModule, const GVSummaryMapTy &DefinedGlobals) { auto updateLinkage = [&](GlobalValue &GV) { // See if the global summary analysis computed a new resolved linkage. @@ -792,13 +950,15 @@ void llvm::thinLTOResolveWeakForLinkerModule( // as we need access to the resolution vectors for each input file in // order to find which symbols have been redefined. // We may consider reorganizing this code and moving the linkage recording - // somewhere else, e.g. in thinLTOResolveWeakForLinkerInIndex. + // somewhere else, e.g. in thinLTOResolvePrevailingInIndex. if (NewLinkage == GlobalValue::WeakAnyLinkage) { GV.setLinkage(NewLinkage); return; } - if (!GlobalValue::isWeakForLinker(GV.getLinkage())) + if (GlobalValue::isLocalLinkage(GV.getLinkage()) || + // In case it was dead and already converted to declaration. + GV.isDeclaration()) return; // Check for a non-prevailing def that has interposable linkage // (e.g. non-odr weak or linkonce). In that case we can't simply @@ -809,7 +969,7 @@ void llvm::thinLTOResolveWeakForLinkerModule( GlobalValue::isInterposableLinkage(GV.getLinkage())) { if (!convertToDeclaration(GV)) // FIXME: Change this to collect replaced GVs and later erase - // them from the parent module once thinLTOResolveWeakForLinkerGUID is + // them from the parent module once thinLTOResolvePrevailingGUID is // changed to enable this for aliases. llvm_unreachable("Expected GV to be converted"); } else { @@ -895,6 +1055,18 @@ static Function *replaceAliasWithAliasee(Module *SrcModule, GlobalAlias *GA) { return NewFn; } +// Internalize values that we marked with specific attribute +// in processGlobalForThinLTO. +static void internalizeImmutableGVs(Module &M) { + for (auto &GV : M.globals()) + // Skip GVs which have been converted to declarations + // by dropDeadSymbols. + if (!GV.isDeclaration() && GV.hasAttribute("thinlto-internalize")) { + GV.setLinkage(GlobalValue::InternalLinkage); + GV.setVisibility(GlobalValue::DefaultVisibility); + } +} + // Automatically import functions in Module \p DestModule based on the summaries // index. Expected<bool> FunctionImporter::importFunctions( @@ -1018,6 +1190,8 @@ Expected<bool> FunctionImporter::importFunctions( NumImportedModules++; } + internalizeImmutableGVs(DestModule); + NumImportedFunctions += (ImportedCount - ImportedGVCount); NumImportedGlobalVars += ImportedGVCount; |