/// Create a combined index file from the input IR files and write it. /// /// This is meant to enable testing of ThinLTO combined index generation, /// currently available via the gold plugin via -thinlto. static int createCombinedFunctionIndex(StringRef Command) { LLVMContext Context; FunctionInfoIndex CombinedIndex; uint64_t NextModuleId = 0; for (auto &Filename : InputFilenames) { std::string Error; std::unique_ptr<FunctionInfoIndex> Index = getFunctionIndexForFile(Filename, Error, Context); if (!Index) { errs() << Command << ": error loading file '" << Filename << "': " << Error << "\n"; return 1; } CombinedIndex.mergeFrom(std::move(Index), ++NextModuleId); } std::error_code EC; assert(!OutputFilename.empty()); raw_fd_ostream OS(OutputFilename + ".thinlto.bc", EC, sys::fs::OpenFlags::F_None); if (EC) { errs() << Command << ": error opening the file '" << OutputFilename << ".thinlto.bc': " << EC.message() << "\n"; return 1; } WriteFunctionSummaryToFile(CombinedIndex, OS); OS.close(); return 0; }
/// Create a combined index file from the input IR files and write it. /// /// This is meant to enable testing of ThinLTO combined index generation, /// currently available via the gold plugin via -thinlto. static int createCombinedFunctionIndex(StringRef Command) { FunctionInfoIndex CombinedIndex; uint64_t NextModuleId = 0; for (auto &Filename : InputFilenames) { ErrorOr<std::unique_ptr<FunctionInfoIndex>> IndexOrErr = getFunctionIndexForFile(Filename, diagnosticHandler); if (std::error_code EC = IndexOrErr.getError()) { std::string Error = EC.message(); errs() << Command << ": error loading file '" << Filename << "': " << Error << "\n"; return 1; } std::unique_ptr<FunctionInfoIndex> Index = std::move(IndexOrErr.get()); // Skip files without a function summary. if (!Index) continue; CombinedIndex.mergeFrom(std::move(Index), ++NextModuleId); } std::error_code EC; assert(!OutputFilename.empty()); raw_fd_ostream OS(OutputFilename + ".thinlto.bc", EC, sys::fs::OpenFlags::F_None); if (EC) { errs() << Command << ": error opening the file '" << OutputFilename << ".thinlto.bc': " << EC.message() << "\n"; return 1; } WriteFunctionSummaryToFile(CombinedIndex, OS); OS.close(); return 0; }
/// Walk through the instructions in \p F looking for external /// calls not already in the \p CalledFunctions set. If any are /// found they are added to the \p Worklist for importing. static void findExternalCalls(const Module &DestModule, Function &F, const FunctionInfoIndex &Index, StringSet<> &CalledFunctions, SmallVector<StringRef, 64> &Worklist) { // We need to suffix internal function calls imported from other modules, // prepare the suffix ahead of time. std::string Suffix; if (F.getParent() != &DestModule) Suffix = (Twine(".llvm.") + Twine(Index.getModuleId(F.getParent()->getModuleIdentifier()))).str(); for (auto &BB : F) { for (auto &I : BB) { if (isa<CallInst>(I)) { auto CalledFunction = cast<CallInst>(I).getCalledFunction(); // Insert any new external calls that have not already been // added to set/worklist. if (!CalledFunction || !CalledFunction->hasName()) continue; // Ignore intrinsics early if (CalledFunction->isIntrinsic()) { assert(CalledFunction->getIntrinsicID() != 0); continue; } auto ImportedName = CalledFunction->getName(); auto Renamed = (ImportedName + Suffix).str(); // Rename internal functions if (CalledFunction->hasInternalLinkage()) { ImportedName = Renamed; } auto It = CalledFunctions.insert(ImportedName); if (!It.second) { // This is a call to a function we already considered, skip. continue; } // Ignore functions already present in the destination module auto *SrcGV = DestModule.getNamedValue(ImportedName); if (SrcGV) { assert(isa<Function>(SrcGV) && "Name collision during import"); if (!cast<Function>(SrcGV)->isDeclaration()) { DEBUG(dbgs() << DestModule.getModuleIdentifier() << ": Ignoring " << ImportedName << " already in DestinationModule\n"); continue; } } Worklist.push_back(It.first->getKey()); DEBUG(dbgs() << DestModule.getModuleIdentifier() << ": Adding callee for : " << ImportedName << " : " << F.getName() << "\n"); } } } }
std::vector<std::unique_ptr<MemoryBuffer>> loadAllFilesForIndex(const FunctionInfoIndex &Index) { std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers; for (auto &ModPath : Index.modPathStringEntries()) { const auto &Filename = ModPath.first(); auto CurrentActivity = "loading file '" + Filename + "'"; auto InputOrErr = MemoryBuffer::getFile(Filename); error(InputOrErr, "error " + CurrentActivity); InputBuffers.push_back(std::move(*InputOrErr)); } return InputBuffers; }
/// Create a combined index file from the input IR files and write it. /// /// This is meant to enable testing of ThinLTO combined index generation, /// currently available via the gold plugin via -thinlto. static void createCombinedFunctionIndex() { FunctionInfoIndex CombinedIndex; uint64_t NextModuleId = 0; for (auto &Filename : InputFilenames) { CurrentActivity = "loading file '" + Filename + "'"; ErrorOr<std::unique_ptr<FunctionInfoIndex>> IndexOrErr = llvm::getFunctionIndexForFile(Filename, diagnosticHandler); std::unique_ptr<FunctionInfoIndex> Index = std::move(IndexOrErr.get()); CurrentActivity = ""; // Skip files without a function summary. if (!Index) continue; CombinedIndex.mergeFrom(std::move(Index), ++NextModuleId); } std::error_code EC; assert(!OutputFilename.empty()); raw_fd_ostream OS(OutputFilename + ".thinlto.bc", EC, sys::fs::OpenFlags::F_None); error(EC, "error opening the file '" + OutputFilename + ".thinlto.bc'"); WriteFunctionSummaryToFile(CombinedIndex, OS); OS.close(); }
// Helper function: given a worklist and an index, will process all the worklist // and decide what to import based on the summary information. // // Nothing is actually imported, functions are materialized in their source // module and analyzed there. // // \p ModuleToFunctionsToImportMap is filled with the set of Function to import // per Module. static void GetImportList(Module &DestModule, SmallVector<StringRef, 64> &Worklist, StringSet<> &CalledFunctions, std::map<StringRef, DenseSet<const GlobalValue *>> &ModuleToFunctionsToImportMap, const FunctionInfoIndex &Index, ModuleLazyLoaderCache &ModuleLoaderCache) { while (!Worklist.empty()) { auto CalledFunctionName = Worklist.pop_back_val(); DEBUG(dbgs() << DestModule.getModuleIdentifier() << ": Process import for " << CalledFunctionName << "\n"); // Try to get a summary for this function call. auto InfoList = Index.findFunctionInfoList(CalledFunctionName); if (InfoList == Index.end()) { DEBUG(dbgs() << DestModule.getModuleIdentifier() << ": No summary for " << CalledFunctionName << " Ignoring.\n"); continue; } assert(!InfoList->second.empty() && "No summary, error at import?"); // Comdat can have multiple entries, FIXME: what do we do with them? auto &Info = InfoList->second[0]; assert(Info && "Nullptr in list, error importing summaries?\n"); auto *Summary = Info->functionSummary(); if (!Summary) { // FIXME: in case we are lazyloading summaries, we can do it now. DEBUG(dbgs() << DestModule.getModuleIdentifier() << ": Missing summary for " << CalledFunctionName << ", error at import?\n"); llvm_unreachable("Missing summary"); } if (Summary->instCount() > ImportInstrLimit) { DEBUG(dbgs() << DestModule.getModuleIdentifier() << ": Skip import of " << CalledFunctionName << " with " << Summary->instCount() << " instructions (limit " << ImportInstrLimit << ")\n"); continue; } // Get the module path from the summary. auto ModuleIdentifier = Summary->modulePath(); DEBUG(dbgs() << DestModule.getModuleIdentifier() << ": Importing " << CalledFunctionName << " from " << ModuleIdentifier << "\n"); auto &SrcModule = ModuleLoaderCache(ModuleIdentifier); // The function that we will import! GlobalValue *SGV = SrcModule.getNamedValue(CalledFunctionName); if (!SGV) { // The destination module is referencing function using their renamed name // when importing a function that was originally local in the source // module. The source module we have might not have been renamed so we try // to remove the suffix added during the renaming to recover the original // name in the source module. std::pair<StringRef, StringRef> Split = CalledFunctionName.split(".llvm."); SGV = SrcModule.getNamedValue(Split.first); assert(SGV && "Can't find function to import in source module"); } if (!SGV) { report_fatal_error(Twine("Can't load function '") + CalledFunctionName + "' in Module '" + SrcModule.getModuleIdentifier() + "', error in the summary?\n"); } Function *F = dyn_cast<Function>(SGV); if (!F && isa<GlobalAlias>(SGV)) { auto *SGA = dyn_cast<GlobalAlias>(SGV); F = dyn_cast<Function>(SGA->getBaseObject()); CalledFunctionName = F->getName(); } assert(F && "Imported Function is ... not a Function"); // We cannot import weak_any functions/aliases without possibly affecting // the order they are seen and selected by the linker, changing program // semantics. if (SGV->hasWeakAnyLinkage()) { DEBUG(dbgs() << DestModule.getModuleIdentifier() << ": Ignoring import request for weak-any " << (isa<Function>(SGV) ? "function " : "alias ") << CalledFunctionName << " from " << SrcModule.getModuleIdentifier() << "\n"); continue; } // Add the function to the import list auto &Entry = ModuleToFunctionsToImportMap[SrcModule.getModuleIdentifier()]; Entry.insert(F); // Process the newly imported functions and add callees to the worklist. F->materialize(); findExternalCalls(DestModule, *F, Index, CalledFunctions, Worklist); } }
/// gold informs us that all symbols have been read. At this point, we use /// get_symbols to see if any of our definitions have been overridden by a /// native object file. Then, perform optimization and codegen. static ld_plugin_status allSymbolsReadHook(raw_fd_ostream *ApiFile) { if (Modules.empty()) return LDPS_OK; LLVMContext Context; Context.setDiagnosticHandler(diagnosticHandlerForContext, nullptr, true); // If we are doing ThinLTO compilation, simply build the combined // function index/summary and emit it. We don't need to parse the modules // and link them in this case. if (options::thinlto) { FunctionInfoIndex CombinedIndex; uint64_t NextModuleId = 0; for (claimed_file &F : Modules) { ld_plugin_input_file File; if (get_input_file(F.handle, &File) != LDPS_OK) message(LDPL_FATAL, "Failed to get file information"); std::unique_ptr<FunctionInfoIndex> Index = getFunctionIndexForFile(F, File); // Skip files without a function summary. if (!Index) continue; CombinedIndex.mergeFrom(std::move(Index), ++NextModuleId); } std::error_code EC; raw_fd_ostream OS(output_name + ".thinlto.bc", EC, sys::fs::OpenFlags::F_None); if (EC) message(LDPL_FATAL, "Unable to open %s.thinlto.bc for writing: %s", output_name.data(), EC.message().c_str()); WriteFunctionSummaryToFile(CombinedIndex, OS); OS.close(); cleanup_hook(); exit(0); } std::unique_ptr<Module> Combined(new Module("ld-temp.o", Context)); Linker L(Combined.get()); std::string DefaultTriple = sys::getDefaultTargetTriple(); StringSet<> Internalize; StringSet<> Maybe; for (claimed_file &F : Modules) { ld_plugin_input_file File; if (get_input_file(F.handle, &File) != LDPS_OK) message(LDPL_FATAL, "Failed to get file information"); std::unique_ptr<Module> M = getModuleForFile(Context, F, File, ApiFile, Internalize, Maybe); if (!options::triple.empty()) M->setTargetTriple(options::triple.c_str()); else if (M->getTargetTriple().empty()) { M->setTargetTriple(DefaultTriple); } if (L.linkInModule(M.get())) message(LDPL_FATAL, "Failed to link module"); if (release_input_file(F.handle) != LDPS_OK) message(LDPL_FATAL, "Failed to release file information"); } for (const auto &Name : Internalize) { GlobalValue *GV = Combined->getNamedValue(Name.first()); if (GV) internalize(*GV); } for (const auto &Name : Maybe) { GlobalValue *GV = Combined->getNamedValue(Name.first()); if (!GV) continue; GV->setLinkage(GlobalValue::LinkOnceODRLinkage); if (canBeOmittedFromSymbolTable(GV)) internalize(*GV); } if (options::TheOutputType == options::OT_DISABLE) return LDPS_OK; if (options::TheOutputType != options::OT_NORMAL) { std::string path; if (options::TheOutputType == options::OT_BC_ONLY) path = output_name; else path = output_name + ".bc"; saveBCFile(path, *L.getModule()); if (options::TheOutputType == options::OT_BC_ONLY) return LDPS_OK; } codegen(std::move(Combined)); if (!options::extra_library_path.empty() && set_extra_library_path(options::extra_library_path.c_str()) != LDPS_OK) message(LDPL_FATAL, "Unable to set the extra library path."); return LDPS_OK; }