[llvm] [ThinLTO] Allow importing based on a workload definition (PR #74545)
Mircea Trofin via llvm-commits
llvm-commits at lists.llvm.org
Tue Dec 12 15:26:31 PST 2023
================
@@ -369,29 +387,273 @@ class GlobalsImporter final {
}
};
+static const char *getFailureName(FunctionImporter::ImportFailureReason Reason);
+
/// Determine the list of imports and exports for each module.
-class ModuleImportsManager final {
+class ModuleImportsManager {
+protected:
function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
IsPrevailing;
const ModuleSummaryIndex &Index;
DenseMap<StringRef, FunctionImporter::ExportSetTy> *const ExportLists;
-public:
ModuleImportsManager(
function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
IsPrevailing,
const ModuleSummaryIndex &Index,
DenseMap<StringRef, FunctionImporter::ExportSetTy> *ExportLists = nullptr)
: IsPrevailing(IsPrevailing), Index(Index), ExportLists(ExportLists) {}
+public:
+ virtual ~ModuleImportsManager() = default;
+
/// Given the list of globals defined in a module, compute the list of imports
/// as well as the list of "exports", i.e. the list of symbols referenced from
/// another module (that may require promotion).
- void computeImportForModule(const GVSummaryMapTy &DefinedGVSummaries,
- StringRef ModName,
- FunctionImporter::ImportMapTy &ImportList);
+ virtual void
+ computeImportForModule(const GVSummaryMapTy &DefinedGVSummaries,
+ StringRef ModName,
+ FunctionImporter::ImportMapTy &ImportList);
+
+ static std::unique_ptr<ModuleImportsManager>
+ create(function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
+ IsPrevailing,
+ const ModuleSummaryIndex &Index,
+ DenseMap<StringRef, FunctionImporter::ExportSetTy> *ExportLists =
+ nullptr);
};
+/// A ModuleImportsManager that operates based on a workload definition (see
+/// -thinlto-workload-def). For modules that do not define workload roots, it
+/// applies the base ModuleImportsManager import policy.
+class WorkloadImportsManager : public ModuleImportsManager {
+ // Keep a module name -> value infos to import association. We use it to
+ // determine if a module's import list should be done by the base
+ // ModuleImportsManager or by us.
+ StringMap<DenseSet<ValueInfo>> Workloads;
+
+ void
+ computeImportForModule(const GVSummaryMapTy &DefinedGVSummaries,
+ StringRef ModName,
+ FunctionImporter::ImportMapTy &ImportList) override {
+ auto SetIter = Workloads.find(ModName);
+ if (SetIter == Workloads.end()) {
+ LLVM_DEBUG(dbgs() << "[Workload] " << ModName
+ << " does not contain the root of any context.\n");
+ return ModuleImportsManager::computeImportForModule(DefinedGVSummaries,
+ ModName, ImportList);
+ }
+ LLVM_DEBUG(dbgs() << "[Workload] " << ModName
+ << " contains the root(s) of context(s).\n");
+
+ GlobalsImporter GVI(Index, DefinedGVSummaries, IsPrevailing, ImportList,
+ ExportLists);
+ auto &ValueInfos = SetIter->second;
+ SmallVector<EdgeInfo, 128> GlobWorklist;
+ for (auto &VI : llvm::make_early_inc_range(ValueInfos)) {
+ auto Candidates =
+ qualifyCalleeCandidates(Index, VI.getSummaryList(), ModName);
+
+ const GlobalValueSummary *GVS = nullptr;
+ FunctionImporter::ImportFailureReason LastReason =
+ FunctionImporter::ImportFailureReason::None;
+ for (const auto &Candidate : Candidates) {
+ /// We will prefer importing the prevailing candidate, if not, we'll
+ /// still pick the first available candidate. The reason we want to make
+ /// sure we do import the prevailing candidate is because the goal of
+ /// workload-awareness is to enable optimizations specializing the call
+ /// graph of that workload. Suppose a function is already defined in the
+ /// module, but it's not the prevailing variant. Suppose also we do not
+ /// inline it, but we could specialize it to the workload in other ways.
+ /// However, the linker would drop it in the favor of the prevailing
+ /// copy. Instead, by importing the prevailing variant (assuming also
+ /// the use of `-avail-extern-to-local`), we keep the specialization. We
+ /// could alteranatively make the non-prevailing variant local, but the
+ /// prevailing one is also the one for which we would have previously
+ /// collected profiles, making it preferrable.
+ LastReason = Candidate.first;
+ if (Candidate.first == FunctionImporter::ImportFailureReason::None) {
+ const bool Prevailing = IsPrevailing(VI.getGUID(), Candidate.second);
+ if (Prevailing || !GVS) {
+ if (!GVS && !Prevailing)
+ LLVM_DEBUG(dbgs()
+ << "[Workload] Considering " << VI.name() << " from "
+ << Candidate.second->modulePath() << " with linkage "
+ << Candidate.second->linkage()
+ << " although it's not prevailing, but it's the "
+ "first available candidate.\n");
+ GVS = Candidate.second;
+ if (Prevailing) {
+ LLVM_DEBUG(dbgs()
+ << "[Workload] Considering " << VI.name() << " from "
+ << GVS->modulePath() << " with linkage "
+ << GVS->linkage() << " because it's prevailing.\n");
+ break;
+ }
+ } else {
+ LLVM_DEBUG(dbgs() << "[Workload] Skipping " << VI.name() << " from "
+ << Candidate.second->modulePath()
+ << " with linkage " << Candidate.second->linkage()
+ << " because it's not prevailing\n");
+ }
+ }
+ }
+ if (!GVS) {
+ LLVM_DEBUG(dbgs() << "[Workload] Not importing " << VI.name()
+ << " because can't find eligible Callee. Guid is: "
+ << Function::getGUID(VI.name())
+ << ". The reason was: " << getFailureName(LastReason)
+ << "\n");
+ continue;
+ }
+ const auto *CFS = cast<FunctionSummary>(GVS->getBaseObject());
+ auto ExportingModule = CFS->modulePath();
+ if (ExportingModule == ModName) {
+ LLVM_DEBUG(dbgs() << "[Workload] Not importing " << VI.name()
+ << " because its defining module is the same as the "
+ "current module\n");
+ continue;
+ }
+ if (moduleAlreadyHasPreferredDef(DefinedGVSummaries, VI.getGUID(), CFS)) {
+ LLVM_DEBUG(
+ dbgs() << "[Workload] Not importing " << VI.name()
+ << " because we have a copy already in this module.\n");
+ continue;
+ }
+
+ LLVM_DEBUG(dbgs() << "[Workload][Including]" << VI.name() << " from "
+ << ExportingModule << " : "
+ << Function::getGUID(VI.name()) << "\n");
+ ImportList[ExportingModule].insert(VI.getGUID());
+ GVI.onImportingSummary(*GVS);
+ if (ExportLists)
+ (*ExportLists)[ExportingModule].insert(VI);
+ }
+ LLVM_DEBUG(dbgs() << "[Workload] Done\n");
+ }
+
+ bool moduleAlreadyHasPreferredDef(const GVSummaryMapTy &DefinedGVSummaries,
+ Function::GUID Guid,
+ const FunctionSummary *Candidate) {
+ auto DefinedSummary = DefinedGVSummaries.find(Guid);
+ if (DefinedSummary == DefinedGVSummaries.end())
+ return false;
+
+ // See shouldImportGlobal for the justificaton of the isInterposableLinkage.
+ if (!IsPrevailing(Guid, DefinedSummary->second) &&
+ GlobalValue::isInterposableLinkage(DefinedSummary->second->linkage()) &&
+ IsPrevailing(Guid, Candidate)) {
+ LLVM_DEBUG(dbgs() << "[Workload] " << Guid
+ << ": non-prevailing preexisting definition in module. "
+ "Importing from "
+ << Candidate->modulePath() << "\n");
+ return false;
+ }
+ LLVM_DEBUG(dbgs() << "[Workload] " << Guid
+ << ": ignored! Target already in destination module.\n");
+ return true;
+ }
+
+public:
+ WorkloadImportsManager(
+ function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
+ IsPrevailing,
+ const ModuleSummaryIndex &Index,
+ DenseMap<StringRef, FunctionImporter::ExportSetTy> *ExportLists)
+ : ModuleImportsManager(IsPrevailing, Index, ExportLists) {
+ // Since the workload def uses names, we need a quick lookup
+ // name->ValueInfo.
+ StringMap<ValueInfo> NameToValueInfo;
+ StringSet<> AmbiguousNames;
+ for (auto &I : Index) {
+ ValueInfo VI = Index.getValueInfo(I);
+ if (!NameToValueInfo.insert(std::make_pair(VI.name(), VI)).second)
+ AmbiguousNames.insert(VI.name());
----------------
mtrofin wrote:
done
https://github.com/llvm/llvm-project/pull/74545
More information about the llvm-commits
mailing list