#include "llvm/Transforms/IPO/FunctionImport.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/IR/AutoUpgrade.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalAlias.h"
#include "llvm/IR/GlobalObject.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/ModuleSummaryIndex.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Linker/IRMover.h"
#include "llvm/ProfileData/PGOCtxProfReader.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/IPO/Internalize.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Transforms/Utils/FunctionImportUtils.h"
#include "llvm/Transforms/Utils/ValueMapper.h"
#include <cassert>
#include <memory>
#include <string>
#include <system_error>
#include <tuple>
#include <utility>
usingnamespacellvm;
#define DEBUG_TYPE …
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");
static cl::opt<unsigned> ImportInstrLimit(
"import-instr-limit", cl::init(100), cl::Hidden, cl::value_desc("N"),
cl::desc("Only import functions with less than N instructions"));
static cl::opt<int> ImportCutoff(
"import-cutoff", cl::init(-1), cl::Hidden, cl::value_desc("N"),
cl::desc("Only import first N functions if N>=0 (default -1)"));
static cl::opt<bool>
ForceImportAll("force-import-all", cl::init(false), cl::Hidden,
cl::desc("Import functions with noinline attribute"));
static cl::opt<float>
ImportInstrFactor("import-instr-evolution-factor", cl::init(0.7),
cl::Hidden, cl::value_desc("x"),
cl::desc("As we import functions, multiply the "
"`import-instr-limit` threshold by this factor "
"before processing newly imported functions"));
static cl::opt<float> ImportHotInstrFactor(
"import-hot-evolution-factor", cl::init(1.0), cl::Hidden,
cl::value_desc("x"),
cl::desc("As we import functions called from hot callsite, multiply the "
"`import-instr-limit` threshold by this factor "
"before processing newly imported functions"));
static cl::opt<float> ImportHotMultiplier(
"import-hot-multiplier", cl::init(10.0), cl::Hidden, cl::value_desc("x"),
cl::desc("Multiply the `import-instr-limit` threshold for hot callsites"));
static cl::opt<float> ImportCriticalMultiplier(
"import-critical-multiplier", cl::init(100.0), cl::Hidden,
cl::value_desc("x"),
cl::desc(
"Multiply the `import-instr-limit` threshold for critical callsites"));
static cl::opt<float> ImportColdMultiplier(
"import-cold-multiplier", cl::init(0), cl::Hidden, cl::value_desc("N"),
cl::desc("Multiply the `import-instr-limit` threshold for cold callsites"));
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"));
static cl::opt<bool> EnableImportMetadata(
"enable-import-metadata", cl::init(false), cl::Hidden,
cl::desc("Enable import metadata like 'thinlto_src_module' and "
"'thinlto_src_file'"));
static cl::opt<std::string>
SummaryFile("summary-file",
cl::desc("The summary file to use for function importing."));
static cl::opt<bool>
ImportAllIndex("import-all-index",
cl::desc("Import all external functions in index."));
static cl::opt<bool> ImportDeclaration(
"import-declaration", cl::init(false), cl::Hidden,
cl::desc("If true, import function declaration as fallback if the function "
"definition is not imported."));
static cl::opt<std::string> WorkloadDefinitions(
"thinlto-workload-def",
cl::desc("Pass a workload definition. This is a file containing a JSON "
"dictionary. The keys are root functions, the values are lists of "
"functions to import in the module defining the root. It is "
"assumed -funique-internal-linkage-names was used, to ensure "
"local linkage functions have unique names. For example: \n"
"{\n"
" \"rootFunction_1\": [\"function_to_import_1\", "
"\"function_to_import_2\"], \n"
" \"rootFunction_2\": [\"function_to_import_3\", "
"\"function_to_import_4\"] \n"
"}"),
cl::Hidden);
extern cl::opt<std::string> UseCtxProfile;
namespace llvm {
extern cl::opt<bool> EnableMemProfContextDisambiguation;
}
static std::unique_ptr<Module> loadFile(const std::string &FileName,
LLVMContext &Context) { … }
static bool shouldSkipLocalInAnotherModule(const GlobalValueSummary *RefSummary,
size_t NumDefs,
StringRef ImporterModule) { … }
static auto qualifyCalleeCandidates(
const ModuleSummaryIndex &Index,
ArrayRef<std::unique_ptr<GlobalValueSummary>> CalleeSummaryList,
StringRef CallerModulePath) { … }
static const GlobalValueSummary *
selectCallee(const ModuleSummaryIndex &Index,
ArrayRef<std::unique_ptr<GlobalValueSummary>> CalleeSummaryList,
unsigned Threshold, StringRef CallerModulePath,
const GlobalValueSummary *&TooLargeOrNoInlineSummary,
FunctionImporter::ImportFailureReason &Reason) { … }
namespace {
EdgeInfo;
}
FunctionImporter::ImportMapTy::AddDefinitionStatus
FunctionImporter::ImportMapTy::addDefinition(StringRef FromModule,
GlobalValue::GUID GUID) { … }
void FunctionImporter::ImportMapTy::maybeAddDeclaration(
StringRef FromModule, GlobalValue::GUID GUID) { … }
SmallVector<StringRef, 0>
FunctionImporter::ImportMapTy::getSourceModules() const { … }
std::optional<GlobalValueSummary::ImportKind>
FunctionImporter::ImportMapTy::getImportType(StringRef FromModule,
GlobalValue::GUID GUID) const { … }
class GlobalsImporter final { … };
static const char *getFailureName(FunctionImporter::ImportFailureReason Reason);
class ModuleImportsManager { … };
class WorkloadImportsManager : public ModuleImportsManager { … };
std::unique_ptr<ModuleImportsManager> ModuleImportsManager::create(
function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
IsPrevailing,
const ModuleSummaryIndex &Index,
DenseMap<StringRef, FunctionImporter::ExportSetTy> *ExportLists) { … }
static const char *
getFailureName(FunctionImporter::ImportFailureReason Reason) { … }
static void computeImportForFunction(
const FunctionSummary &Summary, const ModuleSummaryIndex &Index,
const unsigned Threshold, const GVSummaryMapTy &DefinedGVSummaries,
function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
isPrevailing,
SmallVectorImpl<EdgeInfo> &Worklist, GlobalsImporter &GVImporter,
FunctionImporter::ImportMapTy &ImportList,
DenseMap<StringRef, FunctionImporter::ExportSetTy> *ExportLists,
FunctionImporter::ImportThresholdsTy &ImportThresholds) { … }
void ModuleImportsManager::computeImportForModule(
const GVSummaryMapTy &DefinedGVSummaries, StringRef ModName,
FunctionImporter::ImportMapTy &ImportList) { … }
#ifndef NDEBUG
static bool isGlobalVarSummary(const ModuleSummaryIndex &Index, ValueInfo VI) {
auto SL = VI.getSummaryList();
return SL.empty()
? false
: SL[0]->getSummaryKind() == GlobalValueSummary::GlobalVarKind;
}
static bool isGlobalVarSummary(const ModuleSummaryIndex &Index,
GlobalValue::GUID G) {
if (const auto &VI = Index.getValueInfo(G))
return isGlobalVarSummary(Index, VI);
return false;
}
static unsigned
numGlobalVarSummaries(const ModuleSummaryIndex &Index,
FunctionImporter::ExportSetTy &ExportSet) {
unsigned NumGVS = 0;
for (auto &VI : ExportSet)
if (isGlobalVarSummary(Index, VI.getGUID()))
++NumGVS;
return NumGVS;
}
struct ImportStatistics {
unsigned NumGVS = 0;
unsigned DefinedFS = 0;
unsigned Count = 0;
};
static DenseMap<StringRef, ImportStatistics>
collectImportStatistics(const ModuleSummaryIndex &Index,
const FunctionImporter::ImportMapTy &ImportList) {
DenseMap<StringRef, ImportStatistics> Histogram;
for (const auto &[FromModule, GUID, Type] : ImportList) {
ImportStatistics &Entry = Histogram[FromModule];
++Entry.Count;
if (isGlobalVarSummary(Index, GUID))
++Entry.NumGVS;
else if (Type == GlobalValueSummary::Definition)
++Entry.DefinedFS;
}
return Histogram;
}
#endif
#ifndef NDEBUG
static bool checkVariableImport(
const ModuleSummaryIndex &Index,
FunctionImporter::ImportListsTy &ImportLists,
DenseMap<StringRef, FunctionImporter::ExportSetTy> &ExportLists) {
DenseSet<GlobalValue::GUID> FlattenedImports;
for (const auto &ImportPerModule : ImportLists)
for (const auto &[FromModule, GUID, ImportType] : ImportPerModule.second)
FlattenedImports.insert(GUID);
auto IsReadOrWriteOnlyVarNeedingImporting = [&](StringRef ModulePath,
const ValueInfo &VI) {
auto *GVS = dyn_cast_or_null<GlobalVarSummary>(
Index.findSummaryInModule(VI, ModulePath));
return GVS && (Index.isReadOnly(GVS) || Index.isWriteOnly(GVS)) &&
!(GVS->linkage() == GlobalValue::AvailableExternallyLinkage ||
GVS->linkage() == GlobalValue::WeakODRLinkage ||
GVS->linkage() == GlobalValue::LinkOnceODRLinkage);
};
for (auto &ExportPerModule : ExportLists)
for (auto &VI : ExportPerModule.second)
if (!FlattenedImports.count(VI.getGUID()) &&
IsReadOrWriteOnlyVarNeedingImporting(ExportPerModule.first, VI))
return false;
return true;
}
#endif
void llvm::ComputeCrossModuleImport(
const ModuleSummaryIndex &Index,
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
isPrevailing,
FunctionImporter::ImportListsTy &ImportLists,
DenseMap<StringRef, FunctionImporter::ExportSetTy> &ExportLists) { … }
#ifndef NDEBUG
static void dumpImportListForModule(const ModuleSummaryIndex &Index,
StringRef ModulePath,
FunctionImporter::ImportMapTy &ImportList) {
DenseMap<StringRef, ImportStatistics> Histogram =
collectImportStatistics(Index, ImportList);
LLVM_DEBUG(dbgs() << "* Module " << ModulePath << " imports from "
<< Histogram.size() << " modules.\n");
for (const auto &[SrcModName, Stats] : Histogram) {
LLVM_DEBUG(dbgs() << " - " << Stats.DefinedFS
<< " function definitions and "
<< Stats.Count - Stats.DefinedFS - Stats.NumGVS
<< " function declarations imported from " << SrcModName
<< "\n");
LLVM_DEBUG(dbgs() << " - " << Stats.NumGVS << " vars imported from "
<< SrcModName << "\n");
}
}
#endif
static void ComputeCrossModuleImportForModuleForTest(
StringRef ModulePath,
function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
isPrevailing,
const ModuleSummaryIndex &Index,
FunctionImporter::ImportMapTy &ImportList) { … }
static void ComputeCrossModuleImportForModuleFromIndexForTest(
StringRef ModulePath, const ModuleSummaryIndex &Index,
FunctionImporter::ImportMapTy &ImportList) { … }
void updateValueInfoForIndirectCalls(ModuleSummaryIndex &Index,
FunctionSummary *FS) { … }
void llvm::updateIndirectCalls(ModuleSummaryIndex &Index) { … }
void llvm::computeDeadSymbolsAndUpdateIndirectCalls(
ModuleSummaryIndex &Index,
const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols,
function_ref<PrevailingType(GlobalValue::GUID)> isPrevailing) { … }
void llvm::computeDeadSymbolsWithConstProp(
ModuleSummaryIndex &Index,
const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols,
function_ref<PrevailingType(GlobalValue::GUID)> isPrevailing,
bool ImportEnabled) { … }
void llvm::gatherImportedSummariesForModule(
StringRef ModulePath,
const DenseMap<StringRef, GVSummaryMapTy> &ModuleToDefinedGVSummaries,
const FunctionImporter::ImportMapTy &ImportList,
ModuleToSummariesForIndexTy &ModuleToSummariesForIndex,
GVSummaryPtrSet &DecSummaries) { … }
Error llvm::EmitImportsFiles(
StringRef ModulePath, StringRef OutputFilename,
const ModuleToSummariesForIndexTy &ModuleToSummariesForIndex) { … }
bool llvm::convertToDeclaration(GlobalValue &GV) { … }
void llvm::thinLTOFinalizeInModule(Module &TheModule,
const GVSummaryMapTy &DefinedGlobals,
bool PropagateAttrs) { … }
void llvm::thinLTOInternalizeModule(Module &TheModule,
const GVSummaryMapTy &DefinedGlobals) { … }
static Function *replaceAliasWithAliasee(Module *SrcModule, GlobalAlias *GA) { … }
static void internalizeGVsAfterImport(Module &M) { … }
Expected<bool> FunctionImporter::importFunctions(
Module &DestModule, const FunctionImporter::ImportMapTy &ImportList) { … }
static bool doImportingForModuleForTest(
Module &M, function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
isPrevailing) { … }
PreservedAnalyses FunctionImportPass::run(Module &M,
ModuleAnalysisManager &AM) { … }