|
37 | 37 | #include "llvm/Support/Error.h"
|
38 | 38 | #include "llvm/Support/ErrorHandling.h"
|
39 | 39 | #include "llvm/Support/FileSystem.h"
|
| 40 | +#include "llvm/Support/JSON.h" |
40 | 41 | #include "llvm/Support/SourceMgr.h"
|
41 | 42 | #include "llvm/Support/raw_ostream.h"
|
42 | 43 | #include "llvm/Transforms/IPO/Internalize.h"
|
@@ -138,6 +139,9 @@ static cl::opt<bool>
|
138 | 139 | ImportAllIndex("import-all-index",
|
139 | 140 | cl::desc("Import all external functions in index."));
|
140 | 141 |
|
| 142 | +static cl::opt<std::string> WorkloadDefinitions("thinlto-workload-def", |
| 143 | + cl::Hidden); |
| 144 | + |
141 | 145 | // Load lazily a module from \p FileName in \p Context.
|
142 | 146 | static std::unique_ptr<Module> loadFile(const std::string &FileName,
|
143 | 147 | LLVMContext &Context) {
|
@@ -369,29 +373,238 @@ class GlobalsImporter final {
|
369 | 373 | }
|
370 | 374 | };
|
371 | 375 |
|
| 376 | +static const char *getFailureName(FunctionImporter::ImportFailureReason Reason); |
| 377 | + |
372 | 378 | /// Determine the list of imports and exports for each module.
|
373 |
| -class ModuleImportsManager final { |
| 379 | +class ModuleImportsManager { |
| 380 | +protected: |
374 | 381 | function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
|
375 | 382 | IsPrevailing;
|
376 | 383 | const ModuleSummaryIndex &Index;
|
377 | 384 | DenseMap<StringRef, FunctionImporter::ExportSetTy> *const ExportLists;
|
378 | 385 |
|
379 |
| -public: |
380 | 386 | ModuleImportsManager(
|
381 | 387 | function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
|
382 | 388 | IsPrevailing,
|
383 | 389 | const ModuleSummaryIndex &Index,
|
384 | 390 | DenseMap<StringRef, FunctionImporter::ExportSetTy> *ExportLists = nullptr)
|
385 | 391 | : IsPrevailing(IsPrevailing), Index(Index), ExportLists(ExportLists) {}
|
386 | 392 |
|
| 393 | +public: |
| 394 | + virtual ~ModuleImportsManager() = default; |
| 395 | + |
387 | 396 | /// Given the list of globals defined in a module, compute the list of imports
|
388 | 397 | /// as well as the list of "exports", i.e. the list of symbols referenced from
|
389 | 398 | /// another module (that may require promotion).
|
390 |
| - void computeImportForModule(const GVSummaryMapTy &DefinedGVSummaries, |
391 |
| - StringRef ModName, |
392 |
| - FunctionImporter::ImportMapTy &ImportList); |
| 399 | + virtual void |
| 400 | + computeImportForModule(const GVSummaryMapTy &DefinedGVSummaries, |
| 401 | + StringRef ModName, |
| 402 | + FunctionImporter::ImportMapTy &ImportList); |
| 403 | + |
| 404 | + static std::unique_ptr<ModuleImportsManager> |
| 405 | + create(function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)> |
| 406 | + IsPrevailing, |
| 407 | + const ModuleSummaryIndex &Index, |
| 408 | + DenseMap<StringRef, FunctionImporter::ExportSetTy> *ExportLists = |
| 409 | + nullptr); |
393 | 410 | };
|
394 | 411 |
|
| 412 | +class WorkloadImportsManager : public ModuleImportsManager { |
| 413 | + // Keep a module name -> defined value infos association. We use it to |
| 414 | + // determine if a module's import list should be done by the base |
| 415 | + // ModuleImportsManager or by us. |
| 416 | + StringMap<DenseSet<ValueInfo>> Workloads; |
| 417 | + |
| 418 | + void |
| 419 | + computeImportForModule(const GVSummaryMapTy &DefinedGVSummaries, |
| 420 | + StringRef ModName, |
| 421 | + FunctionImporter::ImportMapTy &ImportList) override { |
| 422 | + auto SetIter = Workloads.find(ModName); |
| 423 | + if (SetIter == Workloads.end()) { |
| 424 | + LLVM_DEBUG(dbgs() << "[Workload] " << ModName |
| 425 | + << " does not contain the root of any context.\n"); |
| 426 | + return ModuleImportsManager::computeImportForModule(DefinedGVSummaries, |
| 427 | + ModName, ImportList); |
| 428 | + } |
| 429 | + LLVM_DEBUG(dbgs() << "[Workload] " << ModName |
| 430 | + << " contains the root(s) of context(s).\n"); |
| 431 | + |
| 432 | + GlobalsImporter GVI(Index, DefinedGVSummaries, IsPrevailing, ImportList, |
| 433 | + ExportLists); |
| 434 | + auto &ValueInfos = SetIter->second; |
| 435 | + SmallVector<EdgeInfo, 128> GlobWorklist; |
| 436 | + for (auto &VI : llvm::make_early_inc_range(ValueInfos)) { |
| 437 | + auto Candidates = |
| 438 | + qualifyCalleeCandidates(Index, VI.getSummaryList(), ModName); |
| 439 | + |
| 440 | + const GlobalValueSummary *GVS = nullptr; |
| 441 | + FunctionImporter::ImportFailureReason LastReason = |
| 442 | + FunctionImporter::ImportFailureReason::None; |
| 443 | + for (const auto &Candidate : Candidates) { |
| 444 | + LastReason = Candidate.first; |
| 445 | + if (Candidate.first == FunctionImporter::ImportFailureReason::None) { |
| 446 | + const bool Prevailing = IsPrevailing(VI.getGUID(), Candidate.second); |
| 447 | + if (Prevailing || !GVS) { |
| 448 | + if (!GVS && !Prevailing) |
| 449 | + LLVM_DEBUG(dbgs() |
| 450 | + << "[Workload] Considering " << VI.name() << " from " |
| 451 | + << Candidate.second->modulePath() << " with linkage " |
| 452 | + << Candidate.second->linkage() |
| 453 | + << " although it's not prevailing, but it's the " |
| 454 | + "first available candidate.\n"); |
| 455 | + GVS = Candidate.second; |
| 456 | + if (Prevailing) { |
| 457 | + LLVM_DEBUG(dbgs() |
| 458 | + << "[Workload] Considering " << VI.name() << " from " |
| 459 | + << GVS->modulePath() << " with linkage " |
| 460 | + << GVS->linkage() << " because it's prevailing.\n"); |
| 461 | + break; |
| 462 | + } |
| 463 | + } else { |
| 464 | + LLVM_DEBUG(dbgs() << "[Workload] Skipping " << VI.name() << " from " |
| 465 | + << Candidate.second->modulePath() |
| 466 | + << " with linkage " << Candidate.second->linkage() |
| 467 | + << " because it's not prevailing\n"); |
| 468 | + } |
| 469 | + } |
| 470 | + } |
| 471 | + if (!GVS) { |
| 472 | + LLVM_DEBUG(dbgs() << "[Workload] Not importing " << VI.name() |
| 473 | + << " because can't select Callee. Guid is: " |
| 474 | + << Function::getGUID(VI.name()) |
| 475 | + << ". The reason was: " << getFailureName(LastReason) |
| 476 | + << "\n"); |
| 477 | + continue; |
| 478 | + } |
| 479 | + const auto *CFS = cast<FunctionSummary>(GVS->getBaseObject()); |
| 480 | + auto ExportingModule = CFS->modulePath(); |
| 481 | + if (ExportingModule == ModName) { |
| 482 | + LLVM_DEBUG(dbgs() << "[Workload] Not importing " << VI.name() |
| 483 | + << " because its defining module is the same as the " |
| 484 | + "current module\n"); |
| 485 | + continue; |
| 486 | + } |
| 487 | + if (!shouldImport(DefinedGVSummaries, VI.getGUID(), CFS)) { |
| 488 | + LLVM_DEBUG(dbgs() << "[Workload] Not importing " << VI.name() |
| 489 | + << " because we have a local copy.\n"); |
| 490 | + continue; |
| 491 | + } |
| 492 | + |
| 493 | + LLVM_DEBUG(dbgs() << "[Workload][Including]" << VI.name() << " from " |
| 494 | + << ExportingModule << " : " |
| 495 | + << Function::getGUID(VI.name()) << "\n"); |
| 496 | + ImportList[ExportingModule].insert(VI.getGUID()); |
| 497 | + GVI.onImportingSummary(*GVS); |
| 498 | + if (ExportLists) |
| 499 | + (*ExportLists)[ExportingModule].insert(VI); |
| 500 | + } |
| 501 | + LLVM_DEBUG(dbgs() << "[Workload] Done\n"); |
| 502 | + } |
| 503 | + |
| 504 | + bool shouldImport(const GVSummaryMapTy &DefinedGVSummaries, |
| 505 | + Function::GUID Guid, const GlobalValueSummary *Candidate) { |
| 506 | + auto DefinedSummary = DefinedGVSummaries.find(Guid); |
| 507 | + if (DefinedSummary == DefinedGVSummaries.end()) |
| 508 | + return true; |
| 509 | + |
| 510 | + // See shouldImportGlobal for the justificaton of the isInterposableLinkage. |
| 511 | + if (!IsPrevailing(Guid, DefinedSummary->second) && |
| 512 | + GlobalValue::isInterposableLinkage(DefinedSummary->second->linkage()) && |
| 513 | + IsPrevailing(Guid, Candidate)) { |
| 514 | + LLVM_DEBUG(dbgs() << "[Workload] " << Guid |
| 515 | + << ": local non-prevailing in module. Importing from " |
| 516 | + << Candidate->modulePath() << "\n"); |
| 517 | + return true; |
| 518 | + } |
| 519 | + LLVM_DEBUG(dbgs() << "[Workload] " << Guid |
| 520 | + << ": ignored! Target already in destination module.\n"); |
| 521 | + return false; |
| 522 | + } |
| 523 | + |
| 524 | +public: |
| 525 | + WorkloadImportsManager( |
| 526 | + function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)> |
| 527 | + IsPrevailing, |
| 528 | + const ModuleSummaryIndex &Index, |
| 529 | + DenseMap<StringRef, FunctionImporter::ExportSetTy> *ExportLists) |
| 530 | + : ModuleImportsManager(IsPrevailing, Index, ExportLists) { |
| 531 | + StringMap<ValueInfo> CtxGuidToValueInfo; |
| 532 | + for (auto &I : Index) { |
| 533 | + ValueInfo VI(Index.haveGVs(), &I); |
| 534 | + CtxGuidToValueInfo[VI.name()] = VI; |
| 535 | + } |
| 536 | + std::error_code EC; |
| 537 | + auto BufferOrErr = MemoryBuffer::getFileOrSTDIN(WorkloadDefinitions); |
| 538 | + if (std::error_code EC = BufferOrErr.getError()) { |
| 539 | + report_fatal_error("Failed to open context file"); |
| 540 | + return; |
| 541 | + } |
| 542 | + auto Buffer = std::move(BufferOrErr.get()); |
| 543 | + std::map<std::string, std::vector<std::string>> WorkloadDefs; |
| 544 | + json::Path::Root NullRoot; |
| 545 | + auto Parsed = json::parse(Buffer->getBuffer()); |
| 546 | + if (!Parsed) |
| 547 | + report_fatal_error(Parsed.takeError()); |
| 548 | + if (!json::fromJSON(*Parsed, WorkloadDefs, NullRoot)) |
| 549 | + report_fatal_error("Invalid thinlto contextual profile format."); |
| 550 | + for (const auto &Workload : WorkloadDefs) { |
| 551 | + const auto &Root = Workload.first; |
| 552 | + LLVM_DEBUG(dbgs() << "[Workload] Root: " << Root << "\n"); |
| 553 | + const auto &AllCallees = Workload.second; |
| 554 | + auto RootIt = CtxGuidToValueInfo.find(Root); |
| 555 | + if (RootIt == CtxGuidToValueInfo.end()) { |
| 556 | + LLVM_DEBUG(dbgs() << "[Workload] Root " << Root |
| 557 | + << " not found in this linkage unit.\n"); |
| 558 | + continue; |
| 559 | + } |
| 560 | + auto RootVI = RootIt->second; |
| 561 | + if (RootVI.getSummaryList().size() != 1) { |
| 562 | + LLVM_DEBUG(dbgs() << "[Workload] Root " << Root |
| 563 | + << " should have exactly one summary, but has " |
| 564 | + << RootVI.getSummaryList().size() << ". Skipping.\n"); |
| 565 | + continue; |
| 566 | + } |
| 567 | + StringRef RootDefiningModule = |
| 568 | + RootVI.getSummaryList().front()->modulePath(); |
| 569 | + LLVM_DEBUG(dbgs() << "[Workload] Root defining module for " << Root |
| 570 | + << " is : " << RootDefiningModule << "\n"); |
| 571 | + auto &Set = Workloads[RootDefiningModule]; |
| 572 | + for (const auto &Callee : AllCallees) { |
| 573 | + LLVM_DEBUG(dbgs() << "[Workload] " << Callee << "\n"); |
| 574 | + auto ElemIt = CtxGuidToValueInfo.find(Callee); |
| 575 | + if (ElemIt == CtxGuidToValueInfo.end()) { |
| 576 | + LLVM_DEBUG(dbgs() << "[Workload] " << Callee << " not found\n"); |
| 577 | + continue; |
| 578 | + } |
| 579 | + Set.insert(ElemIt->second); |
| 580 | + } |
| 581 | + LLVM_DEBUG(dbgs() << "[Workload] Root: " << Root << " we have " |
| 582 | + << Set.size() << " distinct callees.\n"); |
| 583 | + LLVM_DEBUG( // |
| 584 | + for (const auto &VI |
| 585 | + : Set) { |
| 586 | + dbgs() << "[Workload] Root: " << Root |
| 587 | + << " Would include: " << VI.getGUID() << "\n"; |
| 588 | + }); |
| 589 | + } |
| 590 | + } |
| 591 | +}; |
| 592 | + |
| 593 | +std::unique_ptr<ModuleImportsManager> ModuleImportsManager::create( |
| 594 | + function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)> |
| 595 | + IsPrevailing, |
| 596 | + const ModuleSummaryIndex &Index, |
| 597 | + DenseMap<StringRef, FunctionImporter::ExportSetTy> *ExportLists) { |
| 598 | + if (WorkloadDefinitions.empty()) { |
| 599 | + LLVM_DEBUG(dbgs() << "[Workload] Using the regular imports manager.\n"); |
| 600 | + return std::unique_ptr<ModuleImportsManager>( |
| 601 | + new ModuleImportsManager(IsPrevailing, Index, ExportLists)); |
| 602 | + } |
| 603 | + LLVM_DEBUG(dbgs() << "[Workload] Using the contextual imports manager.\n"); |
| 604 | + return std::make_unique<WorkloadImportsManager>(IsPrevailing, Index, |
| 605 | + ExportLists); |
| 606 | +} |
| 607 | + |
395 | 608 | static const char *
|
396 | 609 | getFailureName(FunctionImporter::ImportFailureReason Reason) {
|
397 | 610 | switch (Reason) {
|
@@ -732,14 +945,14 @@ void llvm::ComputeCrossModuleImport(
|
732 | 945 | isPrevailing,
|
733 | 946 | DenseMap<StringRef, FunctionImporter::ImportMapTy> &ImportLists,
|
734 | 947 | DenseMap<StringRef, FunctionImporter::ExportSetTy> &ExportLists) {
|
735 |
| - ModuleImportsManager MIS(isPrevailing, Index, &ExportLists); |
| 948 | + auto MIS = ModuleImportsManager::create(isPrevailing, Index, &ExportLists); |
736 | 949 | // For each module that has function defined, compute the import/export lists.
|
737 | 950 | for (const auto &DefinedGVSummaries : ModuleToDefinedGVSummaries) {
|
738 | 951 | auto &ImportList = ImportLists[DefinedGVSummaries.first];
|
739 | 952 | LLVM_DEBUG(dbgs() << "Computing import for Module '"
|
740 | 953 | << DefinedGVSummaries.first << "'\n");
|
741 |
| - MIS.computeImportForModule(DefinedGVSummaries.second, |
742 |
| - DefinedGVSummaries.first, ImportList); |
| 954 | + MIS->computeImportForModule(DefinedGVSummaries.second, |
| 955 | + DefinedGVSummaries.first, ImportList); |
743 | 956 | }
|
744 | 957 |
|
745 | 958 | // When computing imports we only added the variables and functions being
|
@@ -855,8 +1068,8 @@ static void ComputeCrossModuleImportForModuleForTest(
|
855 | 1068 |
|
856 | 1069 | // Compute the import list for this module.
|
857 | 1070 | LLVM_DEBUG(dbgs() << "Computing import for Module '" << ModulePath << "'\n");
|
858 |
| - ModuleImportsManager MIS(isPrevailing, Index); |
859 |
| - MIS.computeImportForModule(FunctionSummaryMap, ModulePath, ImportList); |
| 1071 | + auto MIS = ModuleImportsManager::create(isPrevailing, Index); |
| 1072 | + MIS->computeImportForModule(FunctionSummaryMap, ModulePath, ImportList); |
860 | 1073 |
|
861 | 1074 | #ifndef NDEBUG
|
862 | 1075 | dumpImportListForModule(Index, ModulePath, ImportList);
|
|
0 commit comments