247 lines · cpp
1//===--- LLJITWithThinLTOSummaries.cpp - Module summaries as LLJIT input --===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// In this example we will use a module summary index file produced for ThinLTO10// to (A) find the module that defines the main entry point and (B) find all11// extra modules that we need. We will do this in five steps:12//13// (1) Read the index file and parse the module summary index.14// (2) Find the path of the module that defines "main".15// (3) Parse the main module and create a matching LLJIT.16// (4) Add all modules to the LLJIT that are covered by the index.17// (5) Look up and run the JIT'd function.18//19// The index file name must be passed in as command line argument. Please find20// this test for instructions on creating the index file:21//22// llvm/test/Examples/OrcV2Examples/lljit-with-thinlto-summaries.test23//24// If you use "build" as the build directory, you can run the test from the root25// of the monorepo like this:26//27// > build/bin/llvm-lit -a \28// llvm/test/Examples/OrcV2Examples/lljit-with-thinlto-summaries.test29//30//===----------------------------------------------------------------------===//31 32#include "llvm/ADT/STLExtras.h"33#include "llvm/ADT/StringRef.h"34#include "llvm/Bitcode/BitcodeReader.h"35#include "llvm/ExecutionEngine/Orc/Core.h"36#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"37#include "llvm/ExecutionEngine/Orc/LLJIT.h"38#include "llvm/ExecutionEngine/Orc/ThreadSafeModule.h"39#include "llvm/ExecutionEngine/Orc/TargetProcess/TargetExecutionUtils.h"40#include "llvm/IR/GlobalValue.h"41#include "llvm/IR/LLVMContext.h"42#include "llvm/IR/ModuleSummaryIndex.h"43#include "llvm/Support/CommandLine.h"44#include "llvm/Support/Error.h"45#include "llvm/Support/InitLLVM.h"46#include "llvm/Support/MemoryBuffer.h"47#include "llvm/Support/TargetSelect.h"48#include "llvm/Support/raw_ostream.h"49 50#include <string>51#include <system_error>52#include <vector>53 54using namespace llvm;55using namespace llvm::orc;56 57// Path of the module summary index file.58static cl::opt<std::string> IndexFile{cl::desc("<module summary index>"),59 cl::Positional, cl::init("-")};60 61// Describe a fail state that is caused by the given ModuleSummaryIndex62// providing multiple definitions of the given global value name. It will dump63// name and GUID for the global value and list the paths of the modules covered64// by the index.65class DuplicateDefinitionInSummary66 : public ErrorInfo<DuplicateDefinitionInSummary> {67public:68 static char ID;69 70 DuplicateDefinitionInSummary(std::string GlobalValueName, ValueInfo VI)71 : GlobalValueName(std::move(GlobalValueName)) {72 ModulePaths.reserve(VI.getSummaryList().size());73 for (const auto &S : VI.getSummaryList())74 ModulePaths.push_back(S->modulePath().str());75 llvm::sort(ModulePaths);76 }77 78 void log(raw_ostream &OS) const override {79 OS << "Duplicate symbol for global value '" << GlobalValueName80 << "' (GUID: "81 << GlobalValue::getGUIDAssumingExternalLinkage(GlobalValueName)82 << ") in:\n";83 for (const std::string &Path : ModulePaths) {84 OS << " " << Path << "\n";85 }86 }87 88 std::error_code convertToErrorCode() const override {89 return inconvertibleErrorCode();90 }91 92private:93 std::string GlobalValueName;94 std::vector<std::string> ModulePaths;95};96 97// Describe a fail state where the given global value name was not found in the98// given ModuleSummaryIndex. It will dump name and GUID for the global value and99// list the paths of the modules covered by the index.100class DefinitionNotFoundInSummary101 : public ErrorInfo<DefinitionNotFoundInSummary> {102public:103 static char ID;104 105 DefinitionNotFoundInSummary(std::string GlobalValueName,106 ModuleSummaryIndex &Index)107 : GlobalValueName(std::move(GlobalValueName)) {108 ModulePaths.reserve(Index.modulePaths().size());109 for (const auto &Entry : Index.modulePaths())110 ModulePaths.push_back(Entry.first().str());111 llvm::sort(ModulePaths);112 }113 114 void log(raw_ostream &OS) const override {115 OS << "No symbol for global value '" << GlobalValueName << "' (GUID: "116 << GlobalValue::getGUIDAssumingExternalLinkage(GlobalValueName)117 << ") in:\n";118 for (const std::string &Path : ModulePaths) {119 OS << " " << Path << "\n";120 }121 }122 123 std::error_code convertToErrorCode() const override {124 return llvm::inconvertibleErrorCode();125 }126 127private:128 std::string GlobalValueName;129 std::vector<std::string> ModulePaths;130};131 132char DuplicateDefinitionInSummary::ID = 0;133char DefinitionNotFoundInSummary::ID = 0;134 135// Lookup the a function in the ModuleSummaryIndex and return the path of the136// module that defines it. Paths in the ModuleSummaryIndex are relative to the137// build directory of the covered modules.138Expected<StringRef> getMainModulePath(StringRef FunctionName,139 ModuleSummaryIndex &Index) {140 // Summaries use unmangled names.141 GlobalValue::GUID G =142 GlobalValue::getGUIDAssumingExternalLinkage(FunctionName);143 ValueInfo VI = Index.getValueInfo(G);144 145 // We need a unique definition, otherwise don't try further.146 if (!VI || VI.getSummaryList().empty())147 return make_error<DefinitionNotFoundInSummary>(FunctionName.str(), Index);148 if (VI.getSummaryList().size() > 1)149 return make_error<DuplicateDefinitionInSummary>(FunctionName.str(), VI);150 151 GlobalValueSummary *S = VI.getSummaryList().front()->getBaseObject();152 if (!isa<FunctionSummary>(S))153 return createStringError(inconvertibleErrorCode(),154 "Entry point is not a function: " + FunctionName);155 156 // Return a reference. ModuleSummaryIndex owns the module paths.157 return S->modulePath();158}159 160// Parse the bitcode module from the given path into a ThreadSafeModule.161Expected<ThreadSafeModule> loadModule(StringRef Path,162 orc::ThreadSafeContext TSCtx) {163 outs() << "About to load module: " << Path << "\n";164 165 Expected<std::unique_ptr<MemoryBuffer>> BitcodeBuffer =166 errorOrToExpected(MemoryBuffer::getFile(Path));167 if (!BitcodeBuffer)168 return BitcodeBuffer.takeError();169 170 MemoryBufferRef BitcodeBufferRef = (**BitcodeBuffer).getMemBufferRef();171 Expected<std::unique_ptr<Module>> M =172 TSCtx.withContextDo([&](LLVMContext *Ctx) {173 return parseBitcodeFile(BitcodeBufferRef, *Ctx);174 });175 if (!M)176 return M.takeError();177 178 return ThreadSafeModule(std::move(*M), std::move(TSCtx));179}180 181int main(int Argc, char *Argv[]) {182 InitLLVM X(Argc, Argv);183 184 InitializeNativeTarget();185 InitializeNativeTargetAsmPrinter();186 187 cl::ParseCommandLineOptions(Argc, Argv, "LLJITWithThinLTOSummaries");188 189 ExitOnError ExitOnErr;190 ExitOnErr.setBanner(std::string(Argv[0]) + ": ");191 192 // (1) Read the index file and parse the module summary index.193 std::unique_ptr<MemoryBuffer> SummaryBuffer =194 ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(IndexFile)));195 196 std::unique_ptr<ModuleSummaryIndex> SummaryIndex =197 ExitOnErr(getModuleSummaryIndex(SummaryBuffer->getMemBufferRef()));198 199 // (2) Find the path of the module that defines "main".200 std::string MainFunctionName = "main";201 StringRef MainModulePath =202 ExitOnErr(getMainModulePath(MainFunctionName, *SummaryIndex));203 204 // (3) Parse the main module and create a matching LLJIT.205 ThreadSafeContext TSCtx(std::make_unique<LLVMContext>());206 ThreadSafeModule MainModule = ExitOnErr(loadModule(MainModulePath, TSCtx));207 208 auto Builder = LLJITBuilder();209 210 MainModule.withModuleDo([&](Module &M) {211 if (M.getTargetTriple().empty()) {212 Builder.setJITTargetMachineBuilder(213 ExitOnErr(JITTargetMachineBuilder::detectHost()));214 } else {215 Builder.setJITTargetMachineBuilder(216 JITTargetMachineBuilder(M.getTargetTriple()));217 }218 if (!M.getDataLayout().getStringRepresentation().empty())219 Builder.setDataLayout(M.getDataLayout());220 });221 222 auto J = ExitOnErr(Builder.create());223 224 // (4) Add all modules to the LLJIT that are covered by the index.225 JITDylib &JD = J->getMainJITDylib();226 227 for (const auto &Entry : SummaryIndex->modulePaths()) {228 StringRef Path = Entry.first();229 ThreadSafeModule M = (Path == MainModulePath)230 ? std::move(MainModule)231 : ExitOnErr(loadModule(Path, TSCtx));232 ExitOnErr(J->addIRModule(JD, std::move(M)));233 }234 235 // (5) Look up and run the JIT'd function.236 auto MainAddr = ExitOnErr(J->lookup(MainFunctionName));237 238 using MainFnPtr = int (*)(int, char *[]);239 auto *MainFunction = MainAddr.toPtr<MainFnPtr>();240 241 int Result = runAsMain(MainFunction, {}, MainModulePath);242 outs() << "'" << MainFunctionName << "' finished with exit code: " << Result243 << "\n";244 245 return 0;246}247