| //===--- LLJITWithThinLTOSummaries.cpp - Module summaries as LLJIT input --===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // In this example we will use a module summary index file produced for ThinLTO |
| // to (A) find the module that defines the main entry point and (B) find all |
| // extra modules that we need. We will do this in five steps: |
| // |
| // (1) Read the index file and parse the module summary index. |
| // (2) Find the path of the module that defines "main". |
| // (3) Parse the main module and create a matching LLJIT. |
| // (4) Add all modules to the LLJIT that are covered by the index. |
| // (5) Look up and run the JIT'd function. |
| // |
| // The index file name must be passed in as command line argument. Please find |
| // this test for instructions on creating the index file: |
| // |
| // llvm/test/Examples/OrcV2Examples/lljit-with-thinlto-summaries.test |
| // |
| // If you use "build" as the build directory, you can run the test from the root |
| // of the monorepo like this: |
| // |
| // > build/bin/llvm-lit -a \ |
| // llvm/test/Examples/OrcV2Examples/lljit-with-thinlto-summaries.test |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/Bitcode/BitcodeReader.h" |
| #include "llvm/ExecutionEngine/Orc/Core.h" |
| #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" |
| #include "llvm/ExecutionEngine/Orc/LLJIT.h" |
| #include "llvm/ExecutionEngine/Orc/ThreadSafeModule.h" |
| #include "llvm/ExecutionEngine/Orc/TargetProcess/TargetExecutionUtils.h" |
| #include "llvm/IR/GlobalValue.h" |
| #include "llvm/IR/LLVMContext.h" |
| #include "llvm/IR/ModuleSummaryIndex.h" |
| #include "llvm/Support/CommandLine.h" |
| #include "llvm/Support/Error.h" |
| #include "llvm/Support/InitLLVM.h" |
| #include "llvm/Support/MemoryBuffer.h" |
| #include "llvm/Support/TargetSelect.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| #include <string> |
| #include <system_error> |
| #include <vector> |
| |
| using namespace llvm; |
| using namespace llvm::orc; |
| |
| // Path of the module summary index file. |
| cl::opt<std::string> IndexFile{cl::desc("<module summary index>"), |
| cl::Positional, cl::init("-")}; |
| |
| // Describe a fail state that is caused by the given ModuleSummaryIndex |
| // providing multiple definitions of the given global value name. It will dump |
| // name and GUID for the global value and list the paths of the modules covered |
| // by the index. |
| class DuplicateDefinitionInSummary |
| : public ErrorInfo<DuplicateDefinitionInSummary> { |
| public: |
| static char ID; |
| |
| DuplicateDefinitionInSummary(std::string GlobalValueName, ValueInfo VI) |
| : GlobalValueName(std::move(GlobalValueName)) { |
| ModulePaths.reserve(VI.getSummaryList().size()); |
| for (const auto &S : VI.getSummaryList()) |
| ModulePaths.push_back(S->modulePath().str()); |
| llvm::sort(ModulePaths); |
| } |
| |
| void log(raw_ostream &OS) const override { |
| OS << "Duplicate symbol for global value '" << GlobalValueName |
| << "' (GUID: " << GlobalValue::getGUID(GlobalValueName) << ") in:\n"; |
| for (const std::string &Path : ModulePaths) { |
| OS << " " << Path << "\n"; |
| } |
| } |
| |
| std::error_code convertToErrorCode() const override { |
| return inconvertibleErrorCode(); |
| } |
| |
| private: |
| std::string GlobalValueName; |
| std::vector<std::string> ModulePaths; |
| }; |
| |
| // Describe a fail state where the given global value name was not found in the |
| // given ModuleSummaryIndex. It will dump name and GUID for the global value and |
| // list the paths of the modules covered by the index. |
| class DefinitionNotFoundInSummary |
| : public ErrorInfo<DefinitionNotFoundInSummary> { |
| public: |
| static char ID; |
| |
| DefinitionNotFoundInSummary(std::string GlobalValueName, |
| ModuleSummaryIndex &Index) |
| : GlobalValueName(std::move(GlobalValueName)) { |
| ModulePaths.reserve(Index.modulePaths().size()); |
| for (const auto &Entry : Index.modulePaths()) |
| ModulePaths.push_back(Entry.first().str()); |
| llvm::sort(ModulePaths); |
| } |
| |
| void log(raw_ostream &OS) const override { |
| OS << "No symbol for global value '" << GlobalValueName |
| << "' (GUID: " << GlobalValue::getGUID(GlobalValueName) << ") in:\n"; |
| for (const std::string &Path : ModulePaths) { |
| OS << " " << Path << "\n"; |
| } |
| } |
| |
| std::error_code convertToErrorCode() const override { |
| return llvm::inconvertibleErrorCode(); |
| } |
| |
| private: |
| std::string GlobalValueName; |
| std::vector<std::string> ModulePaths; |
| }; |
| |
| char DuplicateDefinitionInSummary::ID = 0; |
| char DefinitionNotFoundInSummary::ID = 0; |
| |
| // Lookup the a function in the ModuleSummaryIndex and return the path of the |
| // module that defines it. Paths in the ModuleSummaryIndex are relative to the |
| // build directory of the covered modules. |
| Expected<StringRef> getMainModulePath(StringRef FunctionName, |
| ModuleSummaryIndex &Index) { |
| // Summaries use unmangled names. |
| GlobalValue::GUID G = GlobalValue::getGUID(FunctionName); |
| ValueInfo VI = Index.getValueInfo(G); |
| |
| // We need a unique definition, otherwise don't try further. |
| if (!VI || VI.getSummaryList().empty()) |
| return make_error<DefinitionNotFoundInSummary>(FunctionName.str(), Index); |
| if (VI.getSummaryList().size() > 1) |
| return make_error<DuplicateDefinitionInSummary>(FunctionName.str(), VI); |
| |
| GlobalValueSummary *S = VI.getSummaryList().front()->getBaseObject(); |
| if (!isa<FunctionSummary>(S)) |
| return createStringError(inconvertibleErrorCode(), |
| "Entry point is not a function: " + FunctionName); |
| |
| // Return a reference. ModuleSummaryIndex owns the module paths. |
| return S->modulePath(); |
| } |
| |
| // Parse the bitcode module from the given path into a ThreadSafeModule. |
| Expected<ThreadSafeModule> loadModule(StringRef Path, |
| orc::ThreadSafeContext TSCtx) { |
| outs() << "About to load module: " << Path << "\n"; |
| |
| Expected<std::unique_ptr<MemoryBuffer>> BitcodeBuffer = |
| errorOrToExpected(MemoryBuffer::getFile(Path)); |
| if (!BitcodeBuffer) |
| return BitcodeBuffer.takeError(); |
| |
| MemoryBufferRef BitcodeBufferRef = (**BitcodeBuffer).getMemBufferRef(); |
| Expected<std::unique_ptr<Module>> M = |
| parseBitcodeFile(BitcodeBufferRef, *TSCtx.getContext()); |
| if (!M) |
| return M.takeError(); |
| |
| return ThreadSafeModule(std::move(*M), std::move(TSCtx)); |
| } |
| |
| int main(int Argc, char *Argv[]) { |
| InitLLVM X(Argc, Argv); |
| |
| InitializeNativeTarget(); |
| InitializeNativeTargetAsmPrinter(); |
| |
| cl::ParseCommandLineOptions(Argc, Argv, "LLJITWithThinLTOSummaries"); |
| |
| ExitOnError ExitOnErr; |
| ExitOnErr.setBanner(std::string(Argv[0]) + ": "); |
| |
| // (1) Read the index file and parse the module summary index. |
| std::unique_ptr<MemoryBuffer> SummaryBuffer = |
| ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(IndexFile))); |
| |
| std::unique_ptr<ModuleSummaryIndex> SummaryIndex = |
| ExitOnErr(getModuleSummaryIndex(SummaryBuffer->getMemBufferRef())); |
| |
| // (2) Find the path of the module that defines "main". |
| std::string MainFunctionName = "main"; |
| StringRef MainModulePath = |
| ExitOnErr(getMainModulePath(MainFunctionName, *SummaryIndex)); |
| |
| // (3) Parse the main module and create a matching LLJIT. |
| ThreadSafeContext TSCtx(std::make_unique<LLVMContext>()); |
| ThreadSafeModule MainModule = ExitOnErr(loadModule(MainModulePath, TSCtx)); |
| |
| auto Builder = LLJITBuilder(); |
| |
| MainModule.withModuleDo([&](Module &M) { |
| if (M.getTargetTriple().empty()) { |
| Builder.setJITTargetMachineBuilder( |
| ExitOnErr(JITTargetMachineBuilder::detectHost())); |
| } else { |
| Builder.setJITTargetMachineBuilder( |
| JITTargetMachineBuilder(Triple(M.getTargetTriple()))); |
| } |
| if (!M.getDataLayout().getStringRepresentation().empty()) |
| Builder.setDataLayout(M.getDataLayout()); |
| }); |
| |
| auto J = ExitOnErr(Builder.create()); |
| |
| // (4) Add all modules to the LLJIT that are covered by the index. |
| JITDylib &JD = J->getMainJITDylib(); |
| |
| for (const auto &Entry : SummaryIndex->modulePaths()) { |
| StringRef Path = Entry.first(); |
| ThreadSafeModule M = (Path == MainModulePath) |
| ? std::move(MainModule) |
| : ExitOnErr(loadModule(Path, TSCtx)); |
| ExitOnErr(J->addIRModule(JD, std::move(M))); |
| } |
| |
| // (5) Look up and run the JIT'd function. |
| auto MainSym = ExitOnErr(J->lookup(MainFunctionName)); |
| |
| using MainFnPtr = int (*)(int, char *[]); |
| MainFnPtr MainFunction = |
| jitTargetAddressToFunction<MainFnPtr>(MainSym.getAddress()); |
| |
| int Result = runAsMain(MainFunction, {}, MainModulePath); |
| outs() << "'" << MainFunctionName << "' finished with exit code: " << Result |
| << "\n"; |
| |
| return 0; |
| } |