//===--- 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 #include #include using namespace llvm; using namespace llvm::orc; // Path of the module summary index file. cl::opt IndexFile{cl::desc(""), 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 { 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 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 { 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 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 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(FunctionName.str(), Index); if (VI.getSummaryList().size() > 1) return make_error(FunctionName.str(), VI); GlobalValueSummary *S = VI.getSummaryList().front()->getBaseObject(); if (!isa(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 loadModule(StringRef Path, orc::ThreadSafeContext TSCtx) { outs() << "About to load module: " << Path << "\n"; Expected> BitcodeBuffer = errorOrToExpected(MemoryBuffer::getFile(Path)); if (!BitcodeBuffer) return BitcodeBuffer.takeError(); MemoryBufferRef BitcodeBufferRef = (**BitcodeBuffer).getMemBufferRef(); Expected> 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 SummaryBuffer = ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(IndexFile))); std::unique_ptr 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()); 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(MainSym.getAddress()); int Result = runAsMain(MainFunction, {}, MainModulePath); outs() << "'" << MainFunctionName << "' finished with exit code: " << Result << "\n"; return 0; }