summaryrefslogtreecommitdiff
path: root/lib/Transforms/IPO/FunctionImport.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2019-01-19 10:01:25 +0000
committerDimitry Andric <dim@FreeBSD.org>2019-01-19 10:01:25 +0000
commitd8e91e46262bc44006913e6796843909f1ac7bcd (patch)
tree7d0c143d9b38190e0fa0180805389da22cd834c5 /lib/Transforms/IPO/FunctionImport.cpp
parentb7eb8e35e481a74962664b63dfb09483b200209a (diff)
Notes
Diffstat (limited to 'lib/Transforms/IPO/FunctionImport.cpp')
-rw-r--r--lib/Transforms/IPO/FunctionImport.cpp262
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;