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-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// In this example we will use a module summary index file produced for ThinLTO
10// to (A) find the module that defines the main entry point and (B) find all
11// 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 find
20// this test for instructions on creating the index file:
21//
22// llvm/test/Examples/OrcV2Examples/lljit-with-thinlto-summaries.test
23//
24// If you use "build" as the build directory, you can run the test from the root
25// of the monorepo like this:
26//
27// > build/bin/llvm-lit -a \
28// llvm/test/Examples/OrcV2Examples/lljit-with-thinlto-summaries.test
29//
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.
58cl::opt<std::string> IndexFile{cl::desc("<module summary index>"),
59 cl::Positional, cl::init(Val: "-")};
60
61// Describe a fail state that is caused by the given ModuleSummaryIndex
62// providing multiple definitions of the given global value name. It will dump
63// name and GUID for the global value and list the paths of the modules covered
64// by the index.
65class DuplicateDefinitionInSummary
66 : public ErrorInfo<DuplicateDefinitionInSummary> {
67public:
68 static char ID;
69
70 DuplicateDefinitionInSummary(std::string GlobalValueName, ValueInfo VI)
71 : GlobalValueName(std::move(GlobalValueName)) {
72 ModulePaths.reserve(n: VI.getSummaryList().size());
73 for (const auto &S : VI.getSummaryList())
74 ModulePaths.push_back(x: S->modulePath().str());
75 llvm::sort(C&: ModulePaths);
76 }
77
78 void log(raw_ostream &OS) const override {
79 OS << "Duplicate symbol for global value '" << GlobalValueName
80 << "' (GUID: " << GlobalValue::getGUID(GlobalName: GlobalValueName) << ") in:\n";
81 for (const std::string &Path : ModulePaths) {
82 OS << " " << Path << "\n";
83 }
84 }
85
86 std::error_code convertToErrorCode() const override {
87 return inconvertibleErrorCode();
88 }
89
90private:
91 std::string GlobalValueName;
92 std::vector<std::string> ModulePaths;
93};
94
95// Describe a fail state where the given global value name was not found in the
96// given ModuleSummaryIndex. It will dump name and GUID for the global value and
97// list the paths of the modules covered by the index.
98class DefinitionNotFoundInSummary
99 : public ErrorInfo<DefinitionNotFoundInSummary> {
100public:
101 static char ID;
102
103 DefinitionNotFoundInSummary(std::string GlobalValueName,
104 ModuleSummaryIndex &Index)
105 : GlobalValueName(std::move(GlobalValueName)) {
106 ModulePaths.reserve(n: Index.modulePaths().size());
107 for (const auto &Entry : Index.modulePaths())
108 ModulePaths.push_back(x: Entry.first().str());
109 llvm::sort(C&: ModulePaths);
110 }
111
112 void log(raw_ostream &OS) const override {
113 OS << "No symbol for global value '" << GlobalValueName
114 << "' (GUID: " << GlobalValue::getGUID(GlobalName: GlobalValueName) << ") in:\n";
115 for (const std::string &Path : ModulePaths) {
116 OS << " " << Path << "\n";
117 }
118 }
119
120 std::error_code convertToErrorCode() const override {
121 return llvm::inconvertibleErrorCode();
122 }
123
124private:
125 std::string GlobalValueName;
126 std::vector<std::string> ModulePaths;
127};
128
129char DuplicateDefinitionInSummary::ID = 0;
130char DefinitionNotFoundInSummary::ID = 0;
131
132// Lookup the a function in the ModuleSummaryIndex and return the path of the
133// module that defines it. Paths in the ModuleSummaryIndex are relative to the
134// build directory of the covered modules.
135Expected<StringRef> getMainModulePath(StringRef FunctionName,
136 ModuleSummaryIndex &Index) {
137 // Summaries use unmangled names.
138 GlobalValue::GUID G = GlobalValue::getGUID(GlobalName: FunctionName);
139 ValueInfo VI = Index.getValueInfo(GUID: G);
140
141 // We need a unique definition, otherwise don't try further.
142 if (!VI || VI.getSummaryList().empty())
143 return make_error<DefinitionNotFoundInSummary>(Args: FunctionName.str(), Args&: Index);
144 if (VI.getSummaryList().size() > 1)
145 return make_error<DuplicateDefinitionInSummary>(Args: FunctionName.str(), Args&: VI);
146
147 GlobalValueSummary *S = VI.getSummaryList().front()->getBaseObject();
148 if (!isa<FunctionSummary>(Val: S))
149 return createStringError(EC: inconvertibleErrorCode(),
150 S: "Entry point is not a function: " + FunctionName);
151
152 // Return a reference. ModuleSummaryIndex owns the module paths.
153 return S->modulePath();
154}
155
156// Parse the bitcode module from the given path into a ThreadSafeModule.
157Expected<ThreadSafeModule> loadModule(StringRef Path,
158 orc::ThreadSafeContext TSCtx) {
159 outs() << "About to load module: " << Path << "\n";
160
161 Expected<std::unique_ptr<MemoryBuffer>> BitcodeBuffer =
162 errorOrToExpected(EO: MemoryBuffer::getFile(Filename: Path));
163 if (!BitcodeBuffer)
164 return BitcodeBuffer.takeError();
165
166 MemoryBufferRef BitcodeBufferRef = (**BitcodeBuffer).getMemBufferRef();
167 Expected<std::unique_ptr<Module>> M =
168 parseBitcodeFile(Buffer: BitcodeBufferRef, Context&: *TSCtx.getContext());
169 if (!M)
170 return M.takeError();
171
172 return ThreadSafeModule(std::move(*M), std::move(TSCtx));
173}
174
175int main(int Argc, char *Argv[]) {
176 InitLLVM X(Argc, Argv);
177
178 InitializeNativeTarget();
179 InitializeNativeTargetAsmPrinter();
180
181 cl::ParseCommandLineOptions(argc: Argc, argv: Argv, Overview: "LLJITWithThinLTOSummaries");
182
183 ExitOnError ExitOnErr;
184 ExitOnErr.setBanner(std::string(Argv[0]) + ": ");
185
186 // (1) Read the index file and parse the module summary index.
187 std::unique_ptr<MemoryBuffer> SummaryBuffer =
188 ExitOnErr(errorOrToExpected(EO: MemoryBuffer::getFile(Filename: IndexFile)));
189
190 std::unique_ptr<ModuleSummaryIndex> SummaryIndex =
191 ExitOnErr(getModuleSummaryIndex(Buffer: SummaryBuffer->getMemBufferRef()));
192
193 // (2) Find the path of the module that defines "main".
194 std::string MainFunctionName = "main";
195 StringRef MainModulePath =
196 ExitOnErr(getMainModulePath(FunctionName: MainFunctionName, Index&: *SummaryIndex));
197
198 // (3) Parse the main module and create a matching LLJIT.
199 ThreadSafeContext TSCtx(std::make_unique<LLVMContext>());
200 ThreadSafeModule MainModule = ExitOnErr(loadModule(Path: MainModulePath, TSCtx));
201
202 auto Builder = LLJITBuilder();
203
204 MainModule.withModuleDo(F: [&](Module &M) {
205 if (M.getTargetTriple().empty()) {
206 Builder.setJITTargetMachineBuilder(
207 ExitOnErr(JITTargetMachineBuilder::detectHost()));
208 } else {
209 Builder.setJITTargetMachineBuilder(
210 JITTargetMachineBuilder(Triple(M.getTargetTriple())));
211 }
212 if (!M.getDataLayout().getStringRepresentation().empty())
213 Builder.setDataLayout(M.getDataLayout());
214 });
215
216 auto J = ExitOnErr(Builder.create());
217
218 // (4) Add all modules to the LLJIT that are covered by the index.
219 JITDylib &JD = J->getMainJITDylib();
220
221 for (const auto &Entry : SummaryIndex->modulePaths()) {
222 StringRef Path = Entry.first();
223 ThreadSafeModule M = (Path == MainModulePath)
224 ? std::move(MainModule)
225 : ExitOnErr(loadModule(Path, TSCtx));
226 ExitOnErr(J->addIRModule(JD, TSM: std::move(M)));
227 }
228
229 // (5) Look up and run the JIT'd function.
230 auto MainAddr = ExitOnErr(J->lookup(UnmangledName: MainFunctionName));
231
232 using MainFnPtr = int (*)(int, char *[]);
233 auto *MainFunction = MainAddr.toPtr<MainFnPtr>();
234
235 int Result = runAsMain(Main: MainFunction, Args: {}, ProgramName: MainModulePath);
236 outs() << "'" << MainFunctionName << "' finished with exit code: " << Result
237 << "\n";
238
239 return 0;
240}
241

source code of llvm/examples/OrcV2Examples/LLJITWithThinLTOSummaries/LLJITWithThinLTOSummaries.cpp