mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-25 20:36:06 +00:00
Reland "[ThinLTO] Populate declaration import status except for distributed ThinLTO under a default-off new option" (#92718)
The original PR is reviewed in
https://github.com/llvm/llvm-project/pull/88024, and this PR adds one
line (b9f04d199d
)
to fix test
Limit to one thread for in-process ThinLTO to test `LLVM_DEBUG` log.
- This should fix build bot failure like
https://lab.llvm.org/buildbot/#/builders/259/builds/4727 and
https://lab.llvm.org/buildbot/#/builders/9/builds/43876
- I could repro the failure and see interleaved log messages by using
`-thinlto-threads=all`
**Original Commit Message:**
The goal is to populate `declaration` import status if a new flag
`-import-declaration` is on.
* For in-process ThinLTO, the `declaration` status is visible to backend
`function-import` pass, so `FunctionImporter::importFunctions` should
read the import status and be no-op for declaration summaries.
Basically, the postlink pipeline is updated to keep its current behavior
(import definitions), but not updated to handle `declaration` summaries.
Two use cases ([better call-graph
sort](https://discourse.llvm.org/t/rfc-for-better-call-graph-sort-build-a-more-complete-call-graph-by-adding-more-indirect-call-edges/74029#support-cross-module-function-declaration-import-5)
or [cross-module
auto-init](https://github.com/llvm/llvm-project/pull/87597#discussion_r1556067195))
would use this bit differently.
* For distributed ThinLTO, the `declaration` status is not serialized to
bitcode. As discussed, https://github.com/llvm/llvm-project/pull/87600
will do this.
This commit is contained in:
parent
8b22bb8a62
commit
e33db249b5
@ -587,6 +587,10 @@ public:
|
||||
|
||||
void setImportKind(ImportKind IK) { Flags.ImportType = IK; }
|
||||
|
||||
GlobalValueSummary::ImportKind importType() const {
|
||||
return static_cast<ImportKind>(Flags.ImportType);
|
||||
}
|
||||
|
||||
GlobalValue::VisibilityTypes getVisibility() const {
|
||||
return (GlobalValue::VisibilityTypes)Flags.Visibility;
|
||||
}
|
||||
@ -1272,6 +1276,9 @@ using ModulePathStringTableTy = StringMap<ModuleHash>;
|
||||
/// a particular module, and provide efficient access to their summary.
|
||||
using GVSummaryMapTy = DenseMap<GlobalValue::GUID, GlobalValueSummary *>;
|
||||
|
||||
/// A set of global value summary pointers.
|
||||
using GVSummaryPtrSet = SmallPtrSet<GlobalValueSummary *, 4>;
|
||||
|
||||
/// Map of a type GUID to type id string and summary (multimap used
|
||||
/// in case of GUID conflicts).
|
||||
using TypeIdSummaryMapTy =
|
||||
|
@ -31,9 +31,9 @@ class Module;
|
||||
/// based on the provided summary informations.
|
||||
class FunctionImporter {
|
||||
public:
|
||||
/// Set of functions to import from a source module. Each entry is a set
|
||||
/// containing all the GUIDs of all functions to import for a source module.
|
||||
using FunctionsToImportTy = std::unordered_set<GlobalValue::GUID>;
|
||||
/// The functions to import from a source module and their import type.
|
||||
using FunctionsToImportTy =
|
||||
DenseMap<GlobalValue::GUID, GlobalValueSummary::ImportKind>;
|
||||
|
||||
/// The different reasons selectCallee will chose not to import a
|
||||
/// candidate.
|
||||
@ -99,8 +99,13 @@ public:
|
||||
/// index's module path string table).
|
||||
using ImportMapTy = DenseMap<StringRef, FunctionsToImportTy>;
|
||||
|
||||
/// The set contains an entry for every global value the module exports.
|
||||
using ExportSetTy = DenseSet<ValueInfo>;
|
||||
/// The map contains an entry for every global value the module exports.
|
||||
/// The key is ValueInfo, and the value indicates whether the definition
|
||||
/// or declaration is visible to another module. If a function's definition is
|
||||
/// visible to other modules, the global values this function referenced are
|
||||
/// visible and shouldn't be internalized.
|
||||
/// TODO: Rename to `ExportMapTy`.
|
||||
using ExportSetTy = DenseMap<ValueInfo, GlobalValueSummary::ImportKind>;
|
||||
|
||||
/// A function of this type is used to load modules referenced by the index.
|
||||
using ModuleLoaderTy =
|
||||
|
@ -121,6 +121,9 @@ void llvm::computeLTOCacheKey(
|
||||
support::endian::write64le(Data, I);
|
||||
Hasher.update(Data);
|
||||
};
|
||||
auto AddUint8 = [&](const uint8_t I) {
|
||||
Hasher.update(ArrayRef<uint8_t>((const 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
|
||||
@ -156,18 +159,18 @@ void llvm::computeLTOCacheKey(
|
||||
auto ModHash = Index.getModuleHash(ModuleID);
|
||||
Hasher.update(ArrayRef<uint8_t>((uint8_t *)&ModHash[0], sizeof(ModHash)));
|
||||
|
||||
std::vector<uint64_t> ExportsGUID;
|
||||
std::vector<std::pair<uint64_t, uint8_t>> ExportsGUID;
|
||||
ExportsGUID.reserve(ExportList.size());
|
||||
for (const auto &VI : ExportList) {
|
||||
auto GUID = VI.getGUID();
|
||||
ExportsGUID.push_back(GUID);
|
||||
}
|
||||
for (const auto &[VI, ExportType] : ExportList)
|
||||
ExportsGUID.push_back(
|
||||
std::make_pair(VI.getGUID(), static_cast<uint8_t>(ExportType)));
|
||||
|
||||
// Sort the export list elements GUIDs.
|
||||
llvm::sort(ExportsGUID);
|
||||
for (uint64_t GUID : ExportsGUID) {
|
||||
for (auto [GUID, ExportType] : ExportsGUID) {
|
||||
// The export list can impact the internalization, be conservative here
|
||||
Hasher.update(ArrayRef<uint8_t>((uint8_t *)&GUID, sizeof(GUID)));
|
||||
AddUint8(ExportType);
|
||||
}
|
||||
|
||||
// Include the hash for every module we import functions from. The set of
|
||||
@ -199,7 +202,7 @@ void llvm::computeLTOCacheKey(
|
||||
[](const ImportModule &Lhs, const ImportModule &Rhs) -> bool {
|
||||
return Lhs.getHash() < Rhs.getHash();
|
||||
});
|
||||
std::vector<uint64_t> ImportedGUIDs;
|
||||
std::vector<std::pair<uint64_t, uint8_t>> ImportedGUIDs;
|
||||
for (const ImportModule &Entry : ImportModulesVector) {
|
||||
auto ModHash = Entry.getHash();
|
||||
Hasher.update(ArrayRef<uint8_t>((uint8_t *)&ModHash[0], sizeof(ModHash)));
|
||||
@ -207,11 +210,13 @@ void llvm::computeLTOCacheKey(
|
||||
AddUint64(Entry.getFunctions().size());
|
||||
|
||||
ImportedGUIDs.clear();
|
||||
for (auto &Fn : Entry.getFunctions())
|
||||
ImportedGUIDs.push_back(Fn);
|
||||
for (auto &[Fn, ImportType] : Entry.getFunctions())
|
||||
ImportedGUIDs.push_back(std::make_pair(Fn, ImportType));
|
||||
llvm::sort(ImportedGUIDs);
|
||||
for (auto &GUID : ImportedGUIDs)
|
||||
for (auto &[GUID, Type] : ImportedGUIDs) {
|
||||
AddUint64(GUID);
|
||||
AddUint8(Type);
|
||||
}
|
||||
}
|
||||
|
||||
// Include the hash for the resolved ODR.
|
||||
@ -281,9 +286,9 @@ void llvm::computeLTOCacheKey(
|
||||
// Imported functions may introduce new uses of type identifier resolutions,
|
||||
// so we need to collect their used resolutions as well.
|
||||
for (const ImportModule &ImpM : ImportModulesVector)
|
||||
for (auto &ImpF : ImpM.getFunctions()) {
|
||||
for (auto &[GUID, UnusedImportType] : ImpM.getFunctions()) {
|
||||
GlobalValueSummary *S =
|
||||
Index.findSummaryInModule(ImpF, ImpM.getIdentifier());
|
||||
Index.findSummaryInModule(GUID, ImpM.getIdentifier());
|
||||
AddUsedThings(S);
|
||||
// If this is an alias, we also care about any types/etc. that the aliasee
|
||||
// may reference.
|
||||
@ -1395,6 +1400,7 @@ public:
|
||||
llvm::StringRef ModulePath,
|
||||
const std::string &NewModulePath) {
|
||||
std::map<std::string, GVSummaryMapTy> ModuleToSummariesForIndex;
|
||||
|
||||
std::error_code EC;
|
||||
gatherImportedSummariesForModule(ModulePath, ModuleToDefinedGVSummaries,
|
||||
ImportList, ModuleToSummariesForIndex);
|
||||
@ -1403,6 +1409,8 @@ public:
|
||||
sys::fs::OpenFlags::OF_None);
|
||||
if (EC)
|
||||
return errorCodeToError(EC);
|
||||
|
||||
// TODO: Serialize declaration bits to bitcode.
|
||||
writeIndexToFile(CombinedIndex, OS, &ModuleToSummariesForIndex);
|
||||
|
||||
if (ShouldEmitImportsFiles) {
|
||||
|
@ -720,7 +720,14 @@ bool lto::initImportList(const Module &M,
|
||||
if (Summary->modulePath() == M.getModuleIdentifier())
|
||||
continue;
|
||||
// Add an entry to provoke importing by thinBackend.
|
||||
ImportList[Summary->modulePath()].insert(GUID);
|
||||
// Try emplace the entry first. If an entry with the same key already
|
||||
// exists, set the value to 'std::min(existing-value, new-value)' to make
|
||||
// sure a definition takes precedence over a declaration.
|
||||
auto [Iter, Inserted] = ImportList[Summary->modulePath()].try_emplace(
|
||||
GUID, Summary->importType());
|
||||
|
||||
if (!Inserted)
|
||||
Iter->second = std::min(Iter->second, Summary->importType());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -140,6 +140,17 @@ static cl::opt<bool>
|
||||
ImportAllIndex("import-all-index",
|
||||
cl::desc("Import all external functions in index."));
|
||||
|
||||
/// This is a test-only option.
|
||||
/// If this option is enabled, the ThinLTO indexing step will import each
|
||||
/// function declaration as a fallback. In a real build this may increase ram
|
||||
/// usage of the indexing step unnecessarily.
|
||||
/// TODO: Implement selective import (based on combined summary analysis) to
|
||||
/// ensure the imported function has a use case in the postlink pipeline.
|
||||
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."));
|
||||
|
||||
/// Pass a workload description file - an example of workload would be the
|
||||
/// functions executed to satisfy a RPC request. A workload is defined by a root
|
||||
/// function and the list of functions that are (frequently) needed to satisfy
|
||||
@ -245,8 +256,12 @@ static auto qualifyCalleeCandidates(
|
||||
}
|
||||
|
||||
/// Given a list of possible callee implementation for a call site, select one
|
||||
/// that fits the \p Threshold. If none are found, the Reason will give the last
|
||||
/// reason for the failure (last, in the order of CalleeSummaryList entries).
|
||||
/// that fits the \p Threshold for function definition import. If none are
|
||||
/// found, the Reason will give the last reason for the failure (last, in the
|
||||
/// order of CalleeSummaryList entries). While looking for a callee definition,
|
||||
/// sets \p TooLargeOrNoInlineSummary to the last seen too-large or noinline
|
||||
/// candidate; other modules may want to know the function summary or
|
||||
/// declaration even if a definition is not needed.
|
||||
///
|
||||
/// FIXME: select "best" instead of first that fits. But what is "best"?
|
||||
/// - The smallest: more likely to be inlined.
|
||||
@ -259,24 +274,32 @@ static const GlobalValueSummary *
|
||||
selectCallee(const ModuleSummaryIndex &Index,
|
||||
ArrayRef<std::unique_ptr<GlobalValueSummary>> CalleeSummaryList,
|
||||
unsigned Threshold, StringRef CallerModulePath,
|
||||
const GlobalValueSummary *&TooLargeOrNoInlineSummary,
|
||||
FunctionImporter::ImportFailureReason &Reason) {
|
||||
// Records the last summary with reason noinline or too-large.
|
||||
TooLargeOrNoInlineSummary = nullptr;
|
||||
auto QualifiedCandidates =
|
||||
qualifyCalleeCandidates(Index, CalleeSummaryList, CallerModulePath);
|
||||
for (auto QualifiedValue : QualifiedCandidates) {
|
||||
Reason = QualifiedValue.first;
|
||||
// Skip a summary if its import is not (proved to be) legal.
|
||||
if (Reason != FunctionImporter::ImportFailureReason::None)
|
||||
continue;
|
||||
auto *Summary =
|
||||
cast<FunctionSummary>(QualifiedValue.second->getBaseObject());
|
||||
|
||||
// Don't bother importing the definition if the chance of inlining it is
|
||||
// not high enough (except under `--force-import-all`).
|
||||
if ((Summary->instCount() > Threshold) && !Summary->fflags().AlwaysInline &&
|
||||
!ForceImportAll) {
|
||||
TooLargeOrNoInlineSummary = Summary;
|
||||
Reason = FunctionImporter::ImportFailureReason::TooLarge;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't bother importing if we can't inline it anyway.
|
||||
// Don't bother importing the definition if we can't inline it anyway.
|
||||
if (Summary->fflags().NoInline && !ForceImportAll) {
|
||||
TooLargeOrNoInlineSummary = Summary;
|
||||
Reason = FunctionImporter::ImportFailureReason::NoInline;
|
||||
continue;
|
||||
}
|
||||
@ -358,17 +381,27 @@ class GlobalsImporter final {
|
||||
if (!GVS || !Index.canImportGlobalVar(GVS, /* AnalyzeRefs */ true) ||
|
||||
LocalNotInModule(GVS))
|
||||
continue;
|
||||
auto ILI = ImportList[RefSummary->modulePath()].insert(VI.getGUID());
|
||||
|
||||
// If there isn't an entry for GUID, insert <GUID, Definition> pair.
|
||||
// Otherwise, definition should take precedence over declaration.
|
||||
auto [Iter, Inserted] =
|
||||
ImportList[RefSummary->modulePath()].try_emplace(
|
||||
VI.getGUID(), GlobalValueSummary::Definition);
|
||||
// Only update stat and exports if we haven't already imported this
|
||||
// variable.
|
||||
if (!ILI.second)
|
||||
if (!Inserted) {
|
||||
// Set the value to 'std::min(existing-value, new-value)' to make
|
||||
// sure a definition takes precedence over a declaration.
|
||||
Iter->second = std::min(GlobalValueSummary::Definition, Iter->second);
|
||||
break;
|
||||
}
|
||||
NumImportedGlobalVarsThinLink++;
|
||||
// Any references made by this variable will be marked exported
|
||||
// later, in ComputeCrossModuleImport, after import decisions are
|
||||
// complete, which is more efficient than adding them here.
|
||||
if (ExportLists)
|
||||
(*ExportLists)[RefSummary->modulePath()].insert(VI);
|
||||
(*ExportLists)[RefSummary->modulePath()][VI] =
|
||||
GlobalValueSummary::Definition;
|
||||
|
||||
// If variable is not writeonly we attempt to recursively analyze
|
||||
// its references in order to import referenced constants.
|
||||
@ -545,10 +578,11 @@ class WorkloadImportsManager : public ModuleImportsManager {
|
||||
LLVM_DEBUG(dbgs() << "[Workload][Including]" << VI.name() << " from "
|
||||
<< ExportingModule << " : "
|
||||
<< Function::getGUID(VI.name()) << "\n");
|
||||
ImportList[ExportingModule].insert(VI.getGUID());
|
||||
ImportList[ExportingModule][VI.getGUID()] =
|
||||
GlobalValueSummary::Definition;
|
||||
GVI.onImportingSummary(*GVS);
|
||||
if (ExportLists)
|
||||
(*ExportLists)[ExportingModule].insert(VI);
|
||||
(*ExportLists)[ExportingModule][VI] = GlobalValueSummary::Definition;
|
||||
}
|
||||
LLVM_DEBUG(dbgs() << "[Workload] Done\n");
|
||||
}
|
||||
@ -769,9 +803,28 @@ static void computeImportForFunction(
|
||||
}
|
||||
|
||||
FunctionImporter::ImportFailureReason Reason{};
|
||||
CalleeSummary = selectCallee(Index, VI.getSummaryList(), NewThreshold,
|
||||
Summary.modulePath(), Reason);
|
||||
|
||||
// `SummaryForDeclImport` is an summary eligible for declaration import.
|
||||
const GlobalValueSummary *SummaryForDeclImport = nullptr;
|
||||
CalleeSummary =
|
||||
selectCallee(Index, VI.getSummaryList(), NewThreshold,
|
||||
Summary.modulePath(), SummaryForDeclImport, Reason);
|
||||
if (!CalleeSummary) {
|
||||
// There isn't a callee for definition import but one for declaration
|
||||
// import.
|
||||
if (ImportDeclaration && SummaryForDeclImport) {
|
||||
StringRef DeclSourceModule = SummaryForDeclImport->modulePath();
|
||||
|
||||
// Since definition takes precedence over declaration for the same VI,
|
||||
// try emplace <VI, declaration> pair without checking insert result.
|
||||
// If insert doesn't happen, there must be an existing entry keyed by
|
||||
// VI.
|
||||
if (ExportLists)
|
||||
(*ExportLists)[DeclSourceModule].try_emplace(
|
||||
VI, GlobalValueSummary::Declaration);
|
||||
ImportList[DeclSourceModule].try_emplace(
|
||||
VI.getGUID(), GlobalValueSummary::Declaration);
|
||||
}
|
||||
// Update with new larger threshold if this was a retry (otherwise
|
||||
// we would have already inserted with NewThreshold above). Also
|
||||
// update failure info if requested.
|
||||
@ -816,11 +869,15 @@ static void computeImportForFunction(
|
||||
"selectCallee() didn't honor the threshold");
|
||||
|
||||
auto ExportModulePath = ResolvedCalleeSummary->modulePath();
|
||||
auto ILI = ImportList[ExportModulePath].insert(VI.getGUID());
|
||||
|
||||
// Try emplace the definition entry, and update stats based on insertion
|
||||
// status.
|
||||
auto [Iter, Inserted] = ImportList[ExportModulePath].try_emplace(
|
||||
VI.getGUID(), GlobalValueSummary::Definition);
|
||||
|
||||
// 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) {
|
||||
if (Inserted || Iter->second == GlobalValueSummary::Declaration) {
|
||||
NumImportedFunctionsThinLink++;
|
||||
if (IsHotCallsite)
|
||||
NumImportedHotFunctionsThinLink++;
|
||||
@ -828,11 +885,14 @@ static void computeImportForFunction(
|
||||
NumImportedCriticalFunctionsThinLink++;
|
||||
}
|
||||
|
||||
if (Iter->second == GlobalValueSummary::Declaration)
|
||||
Iter->second = GlobalValueSummary::Definition;
|
||||
|
||||
// Any calls/references made by this function will be marked exported
|
||||
// later, in ComputeCrossModuleImport, after import decisions are
|
||||
// complete, which is more efficient than adding them here.
|
||||
if (ExportLists)
|
||||
(*ExportLists)[ExportModulePath].insert(VI);
|
||||
(*ExportLists)[ExportModulePath][VI] = GlobalValueSummary::Definition;
|
||||
}
|
||||
|
||||
auto GetAdjustedThreshold = [](unsigned Threshold, bool IsHotCallsite) {
|
||||
@ -939,12 +999,20 @@ static bool isGlobalVarSummary(const ModuleSummaryIndex &Index,
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static unsigned numGlobalVarSummaries(const ModuleSummaryIndex &Index,
|
||||
T &Cont) {
|
||||
static unsigned numGlobalVarSummaries(const ModuleSummaryIndex &Index, T &Cont,
|
||||
unsigned &DefinedGVS,
|
||||
unsigned &DefinedFS) {
|
||||
unsigned NumGVS = 0;
|
||||
for (auto &V : Cont)
|
||||
if (isGlobalVarSummary(Index, V))
|
||||
DefinedGVS = 0;
|
||||
DefinedFS = 0;
|
||||
for (auto &[GUID, Type] : Cont) {
|
||||
if (isGlobalVarSummary(Index, GUID)) {
|
||||
if (Type == GlobalValueSummary::Definition)
|
||||
++DefinedGVS;
|
||||
++NumGVS;
|
||||
} else if (Type == GlobalValueSummary::Definition)
|
||||
++DefinedFS;
|
||||
}
|
||||
return NumGVS;
|
||||
}
|
||||
#endif
|
||||
@ -954,13 +1022,12 @@ static bool checkVariableImport(
|
||||
const ModuleSummaryIndex &Index,
|
||||
DenseMap<StringRef, FunctionImporter::ImportMapTy> &ImportLists,
|
||||
DenseMap<StringRef, FunctionImporter::ExportSetTy> &ExportLists) {
|
||||
|
||||
DenseSet<GlobalValue::GUID> FlattenedImports;
|
||||
|
||||
for (auto &ImportPerModule : ImportLists)
|
||||
for (auto &ExportPerModule : ImportPerModule.second)
|
||||
FlattenedImports.insert(ExportPerModule.second.begin(),
|
||||
ExportPerModule.second.end());
|
||||
for (auto &[GUID, Type] : ExportPerModule.second)
|
||||
FlattenedImports.insert(GUID);
|
||||
|
||||
// Checks that all GUIDs of read/writeonly vars we see in export lists
|
||||
// are also in the import lists. Otherwise we my face linker undefs,
|
||||
@ -979,7 +1046,7 @@ static bool checkVariableImport(
|
||||
};
|
||||
|
||||
for (auto &ExportPerModule : ExportLists)
|
||||
for (auto &VI : ExportPerModule.second)
|
||||
for (auto &[VI, Unused] : ExportPerModule.second)
|
||||
if (!FlattenedImports.count(VI.getGUID()) &&
|
||||
IsReadOrWriteOnlyVarNeedingImporting(ExportPerModule.first, VI))
|
||||
return false;
|
||||
@ -1015,7 +1082,11 @@ void llvm::ComputeCrossModuleImport(
|
||||
FunctionImporter::ExportSetTy NewExports;
|
||||
const auto &DefinedGVSummaries =
|
||||
ModuleToDefinedGVSummaries.lookup(ELI.first);
|
||||
for (auto &EI : ELI.second) {
|
||||
for (auto &[EI, Type] : ELI.second) {
|
||||
// If a variable is exported as a declaration, its 'refs' and 'calls' are
|
||||
// not further exported.
|
||||
if (Type == GlobalValueSummary::Declaration)
|
||||
continue;
|
||||
// Find the copy defined in the exporting module so that we can mark the
|
||||
// values it references in that specific definition as exported.
|
||||
// Below we will add all references and called values, without regard to
|
||||
@ -1034,22 +1105,31 @@ void llvm::ComputeCrossModuleImport(
|
||||
// we convert such variables initializers to "zeroinitializer".
|
||||
// See processGlobalForThinLTO.
|
||||
if (!Index.isWriteOnly(GVS))
|
||||
for (const auto &VI : GVS->refs())
|
||||
NewExports.insert(VI);
|
||||
for (const auto &VI : GVS->refs()) {
|
||||
// Try to emplace the declaration entry. If a definition entry
|
||||
// already exists for key `VI`, this is a no-op.
|
||||
NewExports.try_emplace(VI, GlobalValueSummary::Declaration);
|
||||
}
|
||||
} else {
|
||||
auto *FS = cast<FunctionSummary>(S);
|
||||
for (const auto &Edge : FS->calls())
|
||||
NewExports.insert(Edge.first);
|
||||
for (const auto &Ref : FS->refs())
|
||||
NewExports.insert(Ref);
|
||||
for (const auto &Edge : FS->calls()) {
|
||||
// Try to emplace the declaration entry. If a definition entry
|
||||
// already exists for key `VI`, this is a no-op.
|
||||
NewExports.try_emplace(Edge.first, GlobalValueSummary::Declaration);
|
||||
}
|
||||
for (const auto &Ref : FS->refs()) {
|
||||
// Try to emplace the declaration entry. If a definition entry
|
||||
// already exists for key `VI`, this is a no-op.
|
||||
NewExports.try_emplace(Ref, GlobalValueSummary::Declaration);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Prune list computed above to only include values defined in the exporting
|
||||
// module. We do this after the above insertion since we may hit the same
|
||||
// ref/call target multiple times in above loop, and it is more efficient to
|
||||
// avoid a set lookup each time.
|
||||
// Prune list computed above to only include values defined in the
|
||||
// exporting module. We do this after the above insertion since we may hit
|
||||
// the same ref/call target multiple times in above loop, and it is more
|
||||
// efficient to avoid a set lookup each time.
|
||||
for (auto EI = NewExports.begin(); EI != NewExports.end();) {
|
||||
if (!DefinedGVSummaries.count(EI->getGUID()))
|
||||
if (!DefinedGVSummaries.count(EI->first.getGUID()))
|
||||
NewExports.erase(EI++);
|
||||
else
|
||||
++EI;
|
||||
@ -1064,18 +1144,29 @@ void llvm::ComputeCrossModuleImport(
|
||||
for (auto &ModuleImports : ImportLists) {
|
||||
auto ModName = ModuleImports.first;
|
||||
auto &Exports = ExportLists[ModName];
|
||||
unsigned NumGVS = numGlobalVarSummaries(Index, Exports);
|
||||
LLVM_DEBUG(dbgs() << "* Module " << ModName << " exports "
|
||||
<< Exports.size() - NumGVS << " functions and " << NumGVS
|
||||
<< " vars. Imports from " << ModuleImports.second.size()
|
||||
<< " modules.\n");
|
||||
unsigned DefinedGVS = 0, DefinedFS = 0;
|
||||
unsigned NumGVS =
|
||||
numGlobalVarSummaries(Index, Exports, DefinedGVS, DefinedFS);
|
||||
LLVM_DEBUG(dbgs() << "* Module " << ModName << " exports " << DefinedFS
|
||||
<< " function as definitions, "
|
||||
<< Exports.size() - NumGVS - DefinedFS
|
||||
<< " functions as declarations, " << DefinedGVS
|
||||
<< " var definitions and " << NumGVS - DefinedGVS
|
||||
<< " var declarations. Imports from "
|
||||
<< ModuleImports.second.size() << " modules.\n");
|
||||
for (auto &Src : ModuleImports.second) {
|
||||
auto SrcModName = Src.first;
|
||||
unsigned NumGVSPerMod = numGlobalVarSummaries(Index, Src.second);
|
||||
LLVM_DEBUG(dbgs() << " - " << Src.second.size() - NumGVSPerMod
|
||||
<< " functions imported from " << SrcModName << "\n");
|
||||
LLVM_DEBUG(dbgs() << " - " << NumGVSPerMod
|
||||
<< " global vars imported from " << SrcModName << "\n");
|
||||
unsigned DefinedGVS = 0, DefinedFS = 0;
|
||||
unsigned NumGVSPerMod =
|
||||
numGlobalVarSummaries(Index, Src.second, DefinedGVS, DefinedFS);
|
||||
LLVM_DEBUG(dbgs() << " - " << DefinedFS << " function definitions and "
|
||||
<< Src.second.size() - NumGVSPerMod - DefinedFS
|
||||
<< " function declarations imported from " << SrcModName
|
||||
<< "\n");
|
||||
LLVM_DEBUG(dbgs() << " - " << DefinedGVS << " global vars definition and "
|
||||
<< NumGVSPerMod - DefinedGVS
|
||||
<< " global vars declaration imported from "
|
||||
<< SrcModName << "\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -1089,11 +1180,17 @@ static void dumpImportListForModule(const ModuleSummaryIndex &Index,
|
||||
<< ImportList.size() << " modules.\n");
|
||||
for (auto &Src : ImportList) {
|
||||
auto SrcModName = Src.first;
|
||||
unsigned NumGVSPerMod = numGlobalVarSummaries(Index, Src.second);
|
||||
LLVM_DEBUG(dbgs() << " - " << Src.second.size() - NumGVSPerMod
|
||||
<< " functions imported from " << SrcModName << "\n");
|
||||
LLVM_DEBUG(dbgs() << " - " << NumGVSPerMod << " vars imported from "
|
||||
<< SrcModName << "\n");
|
||||
unsigned DefinedGVS = 0, DefinedFS = 0;
|
||||
unsigned NumGVSPerMod =
|
||||
numGlobalVarSummaries(Index, Src.second, DefinedGVS, DefinedFS);
|
||||
LLVM_DEBUG(dbgs() << " - " << DefinedFS << " function definitions and "
|
||||
<< Src.second.size() - DefinedFS - NumGVSPerMod
|
||||
<< " function declarations imported from " << SrcModName
|
||||
<< "\n");
|
||||
LLVM_DEBUG(dbgs() << " - " << DefinedGVS << " var definitions and "
|
||||
<< NumGVSPerMod - DefinedGVS
|
||||
<< " var declarations imported from " << SrcModName
|
||||
<< "\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -1149,7 +1246,13 @@ static void ComputeCrossModuleImportForModuleFromIndexForTest(
|
||||
if (Summary->modulePath() == ModulePath)
|
||||
continue;
|
||||
// Add an entry to provoke importing by thinBackend.
|
||||
ImportList[Summary->modulePath()].insert(GUID);
|
||||
auto [Iter, Inserted] = ImportList[Summary->modulePath()].try_emplace(
|
||||
GUID, Summary->importType());
|
||||
if (!Inserted) {
|
||||
// Use 'std::min' to make sure definition (with enum value 0) takes
|
||||
// precedence over declaration (with enum value 1).
|
||||
Iter->second = std::min(Iter->second, Summary->importType());
|
||||
}
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
dumpImportListForModule(Index, ModulePath, ImportList);
|
||||
@ -1339,13 +1442,17 @@ void llvm::gatherImportedSummariesForModule(
|
||||
// Include summaries for imports.
|
||||
for (const auto &ILI : ImportList) {
|
||||
auto &SummariesForIndex = ModuleToSummariesForIndex[std::string(ILI.first)];
|
||||
|
||||
const auto &DefinedGVSummaries =
|
||||
ModuleToDefinedGVSummaries.lookup(ILI.first);
|
||||
for (const auto &GI : ILI.second) {
|
||||
const auto &DS = DefinedGVSummaries.find(GI);
|
||||
for (const auto &[GUID, Type] : ILI.second) {
|
||||
const auto &DS = DefinedGVSummaries.find(GUID);
|
||||
assert(DS != DefinedGVSummaries.end() &&
|
||||
"Expected a defined summary for imported global value");
|
||||
SummariesForIndex[GI] = DS->second;
|
||||
if (Type == GlobalValueSummary::Declaration)
|
||||
continue;
|
||||
|
||||
SummariesForIndex[GUID] = DS->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1617,6 +1724,16 @@ Expected<bool> FunctionImporter::importFunctions(
|
||||
for (const auto &FunctionsToImportPerModule : ImportList) {
|
||||
ModuleNameOrderedList.insert(FunctionsToImportPerModule.first);
|
||||
}
|
||||
|
||||
auto getImportType = [&](const FunctionsToImportTy &GUIDToImportType,
|
||||
GlobalValue::GUID GUID)
|
||||
-> std::optional<GlobalValueSummary::ImportKind> {
|
||||
auto Iter = GUIDToImportType.find(GUID);
|
||||
if (Iter == GUIDToImportType.end())
|
||||
return std::nullopt;
|
||||
return Iter->second;
|
||||
};
|
||||
|
||||
for (const auto &Name : ModuleNameOrderedList) {
|
||||
// Get the module for the import
|
||||
const auto &FunctionsToImportPerModule = ImportList.find(Name);
|
||||
@ -1634,17 +1751,27 @@ Expected<bool> FunctionImporter::importFunctions(
|
||||
return std::move(Err);
|
||||
|
||||
auto &ImportGUIDs = FunctionsToImportPerModule->second;
|
||||
|
||||
// Find the globals to import
|
||||
SetVector<GlobalValue *> GlobalsToImport;
|
||||
for (Function &F : *SrcModule) {
|
||||
if (!F.hasName())
|
||||
continue;
|
||||
auto GUID = F.getGUID();
|
||||
auto Import = ImportGUIDs.count(GUID);
|
||||
LLVM_DEBUG(dbgs() << (Import ? "Is" : "Not") << " importing function "
|
||||
auto MaybeImportType = getImportType(ImportGUIDs, GUID);
|
||||
|
||||
bool ImportDefinition =
|
||||
(MaybeImportType &&
|
||||
(*MaybeImportType == GlobalValueSummary::Definition));
|
||||
|
||||
LLVM_DEBUG(dbgs() << (MaybeImportType ? "Is" : "Not")
|
||||
<< " importing function"
|
||||
<< (ImportDefinition
|
||||
? " definition "
|
||||
: (MaybeImportType ? " declaration " : " "))
|
||||
<< GUID << " " << F.getName() << " from "
|
||||
<< SrcModule->getSourceFileName() << "\n");
|
||||
if (Import) {
|
||||
if (ImportDefinition) {
|
||||
if (Error Err = F.materialize())
|
||||
return std::move(Err);
|
||||
// MemProf should match function's definition and summary,
|
||||
@ -1670,11 +1797,20 @@ Expected<bool> FunctionImporter::importFunctions(
|
||||
if (!GV.hasName())
|
||||
continue;
|
||||
auto GUID = GV.getGUID();
|
||||
auto Import = ImportGUIDs.count(GUID);
|
||||
LLVM_DEBUG(dbgs() << (Import ? "Is" : "Not") << " importing global "
|
||||
auto MaybeImportType = getImportType(ImportGUIDs, GUID);
|
||||
|
||||
bool ImportDefinition =
|
||||
(MaybeImportType &&
|
||||
(*MaybeImportType == GlobalValueSummary::Definition));
|
||||
|
||||
LLVM_DEBUG(dbgs() << (MaybeImportType ? "Is" : "Not")
|
||||
<< " importing global"
|
||||
<< (ImportDefinition
|
||||
? " definition "
|
||||
: (MaybeImportType ? " declaration " : " "))
|
||||
<< GUID << " " << GV.getName() << " from "
|
||||
<< SrcModule->getSourceFileName() << "\n");
|
||||
if (Import) {
|
||||
if (ImportDefinition) {
|
||||
if (Error Err = GV.materialize())
|
||||
return std::move(Err);
|
||||
ImportedGVCount += GlobalsToImport.insert(&GV);
|
||||
@ -1684,11 +1820,20 @@ Expected<bool> FunctionImporter::importFunctions(
|
||||
if (!GA.hasName() || isa<GlobalIFunc>(GA.getAliaseeObject()))
|
||||
continue;
|
||||
auto GUID = GA.getGUID();
|
||||
auto Import = ImportGUIDs.count(GUID);
|
||||
LLVM_DEBUG(dbgs() << (Import ? "Is" : "Not") << " importing alias "
|
||||
auto MaybeImportType = getImportType(ImportGUIDs, GUID);
|
||||
|
||||
bool ImportDefinition =
|
||||
(MaybeImportType &&
|
||||
(*MaybeImportType == GlobalValueSummary::Definition));
|
||||
|
||||
LLVM_DEBUG(dbgs() << (MaybeImportType ? "Is" : "Not")
|
||||
<< " importing alias"
|
||||
<< (ImportDefinition
|
||||
? " definition "
|
||||
: (MaybeImportType ? " declaration " : " "))
|
||||
<< GUID << " " << GA.getName() << " from "
|
||||
<< SrcModule->getSourceFileName() << "\n");
|
||||
if (Import) {
|
||||
if (ImportDefinition) {
|
||||
if (Error Err = GA.materialize())
|
||||
return std::move(Err);
|
||||
// Import alias as a copy of its aliasee.
|
||||
@ -1754,6 +1899,7 @@ Expected<bool> FunctionImporter::importFunctions(
|
||||
NumImportedFunctions += (ImportedCount - ImportedGVCount);
|
||||
NumImportedGlobalVars += ImportedGVCount;
|
||||
|
||||
// TODO: Print counters for definitions and declarations in the debugging log.
|
||||
LLVM_DEBUG(dbgs() << "Imported " << ImportedCount - ImportedGVCount
|
||||
<< " functions for Module "
|
||||
<< DestModule.getModuleIdentifier() << "\n");
|
||||
|
@ -9,8 +9,8 @@
|
||||
; RUN: cat %t4 | grep 'Is importing aliasee' | count 1
|
||||
; RUN: cat %t4 | FileCheck %s
|
||||
|
||||
; CHECK: - [[NUM_FUNCS:[0-9]+]] functions imported from
|
||||
; CHECK-NEXT: - [[NUM_VARS:[0-9]+]] global vars imported from
|
||||
; CHECK: - [[NUM_FUNCS:[0-9]+]] function definitions and 0 function declarations imported from
|
||||
; CHECK-NEXT: - [[NUM_VARS:[0-9]+]] global vars definition and 0 global vars declaration imported from
|
||||
|
||||
; CHECK: [[NUM_FUNCS]] function-import - Number of functions imported in backend
|
||||
; CHECK-NEXT: [[NUM_FUNCS]] function-import - Number of functions thin link decided to import
|
||||
|
181
llvm/test/ThinLTO/X86/import_callee_declaration.ll
Normal file
181
llvm/test/ThinLTO/X86/import_callee_declaration.ll
Normal file
@ -0,0 +1,181 @@
|
||||
; "-debug-only" requires asserts.
|
||||
; REQUIRES: asserts
|
||||
; RUN: rm -rf %t && split-file %s %t && cd %t
|
||||
|
||||
; Generate per-module summaries.
|
||||
; RUN: opt -module-summary main.ll -o main.bc
|
||||
; RUN: opt -module-summary lib.ll -o lib.bc
|
||||
|
||||
; Generate the combined summary and distributed indices.
|
||||
|
||||
; - For function import, set 'import-instr-limit' to 7 and fall back to import
|
||||
; function declarations.
|
||||
; - In main.ll, function 'main' calls 'small_func' and 'large_func'. Both callees
|
||||
; are defined in lib.ll. 'small_func' has two indirect callees, one is smaller
|
||||
; and the other one is larger. Both callees of 'small_func' are defined in lib.ll.
|
||||
; - Given the import limit, in main's combined summary, the import type of 'small_func'
|
||||
; and 'small_indirect_callee' will be 'definition', and the import type of
|
||||
; 'large_func' and 'large_indirect_callee' will be 'declaration'.
|
||||
;
|
||||
; The test will disassemble combined summaries and check the import type is
|
||||
; correct. Right now postlink optimizer pipeline doesn't do anything (e.g.,
|
||||
; import the declaration or de-serialize summary attributes yet) so there is
|
||||
; nothing to test more than the summary content.
|
||||
;
|
||||
; RUN: llvm-lto2 run \
|
||||
; RUN: -debug-only=function-import \
|
||||
; RUN: -import-instr-limit=7 \
|
||||
; RUN: -import-declaration \
|
||||
; RUN: -thinlto-distributed-indexes \
|
||||
; RUN: -r=main.bc,main,px \
|
||||
; RUN: -r=main.bc,small_func, \
|
||||
; RUN: -r=main.bc,large_func, \
|
||||
; RUN: -r=lib.bc,callee,pl \
|
||||
; RUN: -r=lib.bc,large_indirect_callee,px \
|
||||
; RUN: -r=lib.bc,small_func,px \
|
||||
; RUN: -r=lib.bc,large_func,px \
|
||||
; RUN: -r=lib.bc,large_indirect_callee_alias,px \
|
||||
; RUN: -r=lib.bc,calleeAddrs,px -o summary main.bc lib.bc 2>&1 | FileCheck %s --check-prefix=DUMP
|
||||
;
|
||||
; RUN: llvm-lto -thinlto-action=thinlink -import-declaration -import-instr-limit=7 -o combined.index.bc main.bc lib.bc
|
||||
; RUN: llvm-lto -thinlto-action=distributedindexes -debug-only=function-import -import-declaration -import-instr-limit=7 -thinlto-index combined.index.bc main.bc lib.bc 2>&1 | FileCheck %s --check-prefix=DUMP
|
||||
|
||||
; DUMP: - 2 function definitions and 3 function declarations imported from lib.bc
|
||||
|
||||
; First disassemble per-module summary and find out the GUID for {large_func, large_indirect_callee}.
|
||||
;
|
||||
; RUN: llvm-dis lib.bc -o - | FileCheck %s --check-prefix=LIB-DIS
|
||||
; LIB-DIS: [[LARGEFUNC:\^[0-9]+]] = gv: (name: "large_func", summaries: {{.*}}) ; guid = 2418497564662708935
|
||||
; LIB-DIS: [[LARGEINDIRECT:\^[0-9]+]] = gv: (name: "large_indirect_callee", summaries: {{.*}}) ; guid = 14343440786664691134
|
||||
; LIB-DIS: [[LARGEINDIRECTALIAS:\^[0-9]+]] = gv: (name: "large_indirect_callee_alias", summaries: {{.*}}, aliasee: [[LARGEINDIRECT]]
|
||||
;
|
||||
; Secondly disassemble main's combined summary and test that large callees are
|
||||
; not imported as declarations yet.
|
||||
;
|
||||
; RUN: llvm-dis main.bc.thinlto.bc -o - | FileCheck %s --check-prefix=MAIN-DIS
|
||||
;
|
||||
; MAIN-DIS: [[LIBMOD:\^[0-9]+]] = module: (path: "lib.bc", hash: (0, 0, 0, 0, 0))
|
||||
; MAIN-DIS-NOT: [[LARGEFUNC:\^[0-9]+]] = gv: (guid: 2418497564662708935, summaries: (function: (module: [[LIBMOD]], flags: ({{.*}} importType: declaration), insts: 8, {{.*}})))
|
||||
; MAIN-DIS-NOT: [[LARGEINDIRECT:\^[0-9]+]] = gv: (guid: 14343440786664691134, summaries: (function: (module: [[LIBMOD]], flags: ({{.*}} importType: declaration), insts: 8, {{.*}})))
|
||||
; MAIN-DIS-NOT: [[LARGEINDIRECTALIAS:\^[0-9]+]] = gv: (guid: 16730173943625350469, summaries: (alias: (module: [[LIBMOD]], flags: ({{.*}} importType: declaration)
|
||||
|
||||
; Run in-process ThinLTO and tests that
|
||||
; 1. `callee` remains internalized even if the symbols of its callers
|
||||
; (large_func and large_indirect_callee) are exported as declarations and visible to main module.
|
||||
; 2. the debugging logs from `function-import` pass are expected.
|
||||
|
||||
; RUN: llvm-lto2 run \
|
||||
; RUN: -debug-only=function-import \
|
||||
; RUN: -save-temps \
|
||||
; RUN: -thinlto-threads=1 \
|
||||
; RUN: -import-instr-limit=7 \
|
||||
; RUN: -import-declaration \
|
||||
; RUN: -r=main.bc,main,px \
|
||||
; RUN: -r=main.bc,small_func, \
|
||||
; RUN: -r=main.bc,large_func, \
|
||||
; RUN: -r=lib.bc,callee,pl \
|
||||
; RUN: -r=lib.bc,large_indirect_callee,px \
|
||||
; RUN: -r=lib.bc,small_func,px \
|
||||
; RUN: -r=lib.bc,large_func,px \
|
||||
; RUN: -r=lib.bc,large_indirect_callee_alias,px \
|
||||
; RUN: -r=lib.bc,calleeAddrs,px -o in-process main.bc lib.bc 2>&1 | FileCheck %s --check-prefix=IMPORTDUMP
|
||||
|
||||
; Test import status from debugging logs.
|
||||
; TODO: Serialize declaration bit and test declaration bits are correctly set,
|
||||
; and extend this test case to test IR once postlink optimizer makes use of
|
||||
; the import type for declarations.
|
||||
; IMPORTDUMP-DAG: Not importing function 11825436545918268459 callee from lib.cc
|
||||
; IMPORTDUMP-DAG: Is importing function declaration 14343440786664691134 large_indirect_callee from lib.cc
|
||||
; IMPORTDUMP-DAG: Is importing function definition 13568239288960714650 small_indirect_callee from lib.cc
|
||||
; IMPORTDUMP-DAG: Is importing function definition 6976996067367342685 small_func from lib.cc
|
||||
; IMPORTDUMP-DAG: Is importing function declaration 2418497564662708935 large_func from lib.cc
|
||||
; IMPORTDUMP-DAG: Not importing global 7680325410415171624 calleeAddrs from lib.cc
|
||||
; IMPORTDUMP-DAG: Is importing alias declaration 16730173943625350469 large_indirect_callee_alias from lib.cc
|
||||
|
||||
; RUN: llvm-dis in-process.1.3.import.bc -o - | FileCheck %s --check-prefix=IMPORT
|
||||
|
||||
; RUN: llvm-dis in-process.2.2.internalize.bc -o - | FileCheck %s --check-prefix=INTERNALIZE
|
||||
|
||||
; IMPORT-DAG: define available_externally void @small_func
|
||||
; IMPORT-DAG: define available_externally hidden void @small_indirect_callee
|
||||
; IMPORT-DAG: declare void @large_func
|
||||
; IMPORT-NOT: large_indirect_callee
|
||||
; IMPORT-NOT: large_indirect_callee_alias
|
||||
|
||||
; INTERNALIZE: define internal void @callee()
|
||||
|
||||
;--- main.ll
|
||||
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
|
||||
define i32 @main() {
|
||||
call void @small_func()
|
||||
call void @large_func()
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
declare void @small_func()
|
||||
|
||||
; large_func without attributes
|
||||
declare void @large_func()
|
||||
|
||||
;--- lib.ll
|
||||
source_filename = "lib.cc"
|
||||
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
|
||||
@calleeAddrs = global [3 x ptr] [ptr @large_indirect_callee, ptr @small_indirect_callee, ptr @large_indirect_callee_alias]
|
||||
|
||||
define void @callee() #1 {
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @large_indirect_callee()#2 {
|
||||
call void @callee()
|
||||
call void @callee()
|
||||
call void @callee()
|
||||
call void @callee()
|
||||
call void @callee()
|
||||
call void @callee()
|
||||
call void @callee()
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @small_indirect_callee() #0 {
|
||||
ret void
|
||||
}
|
||||
|
||||
@large_indirect_callee_alias = alias void(), ptr @large_indirect_callee
|
||||
|
||||
define void @small_func() {
|
||||
entry:
|
||||
%0 = load ptr, ptr @calleeAddrs
|
||||
call void %0(), !prof !0
|
||||
%1 = load ptr, ptr getelementptr inbounds ([3 x ptr], ptr @calleeAddrs, i64 0, i64 1)
|
||||
call void %1(), !prof !1
|
||||
%2 = load ptr, ptr getelementptr inbounds ([3 x ptr], ptr @calleeAddrs, i64 0, i64 2)
|
||||
call void %2(), !prof !2
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @large_func() #0 {
|
||||
entry:
|
||||
call void @callee()
|
||||
call void @callee()
|
||||
call void @callee()
|
||||
call void @callee()
|
||||
call void @callee()
|
||||
call void @callee()
|
||||
call void @callee()
|
||||
ret void
|
||||
}
|
||||
|
||||
attributes #0 = { nounwind norecurse }
|
||||
|
||||
attributes #1 = { noinline }
|
||||
|
||||
attributes #2 = { norecurse }
|
||||
|
||||
!0 = !{!"VP", i32 0, i64 1, i64 14343440786664691134, i64 1}
|
||||
!1 = !{!"VP", i32 0, i64 1, i64 13568239288960714650, i64 1}
|
||||
!2 = !{!"VP", i32 0, i64 1, i64 16730173943625350469, i64 1}
|
@ -166,7 +166,8 @@ declare void @variadic_va_start(...)
|
||||
; GUID-DAG: GUID {{.*}} is linkoncefunc
|
||||
|
||||
; DUMP: Module [[M1:.*]] imports from 1 module
|
||||
; DUMP-NEXT: 15 functions imported from [[M2:.*]]
|
||||
; DUMP-NEXT: 4 vars imported from [[M2]]
|
||||
; DUMP-NEXT: 15 function definitions and 0 function declarations imported from [[M2:.*]]
|
||||
; DUMP-NEXT: 4 var definitions and 0 var declarations imported from [[M2]]
|
||||
|
||||
; DUMP: Imported 15 functions for Module [[M1]]
|
||||
; DUMP-NEXT: Imported 4 global variables for Module [[M1]]
|
||||
|
@ -377,9 +377,13 @@ static bool importFunctions(const char *argv0, Module &DestModule) {
|
||||
if (Verbose)
|
||||
errs() << "Importing " << FunctionName << " from " << FileName << "\n";
|
||||
|
||||
// `-import` specifies the `<filename,function-name>` pairs to import as
|
||||
// definition, so make the import type definition directly.
|
||||
// FIXME: A follow-up patch should add test coverage for import declaration
|
||||
// in `llvm-link` CLI (e.g., by introducing a new command line option).
|
||||
auto &Entry =
|
||||
ImportList[FileNameStringCache.insert(FileName).first->getKey()];
|
||||
Entry.insert(F->getGUID());
|
||||
Entry[F->getGUID()] = GlobalValueSummary::Definition;
|
||||
}
|
||||
auto CachedModuleLoader = [&](StringRef Identifier) {
|
||||
return ModuleLoaderCache.takeModule(std::string(Identifier));
|
||||
|
Loading…
x
Reference in New Issue
Block a user