1 //===--- LLJITWithThinLTOSummaries.cpp - Module summaries as LLJIT input --===//
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
7 //===----------------------------------------------------------------------===//
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:
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.
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:
22 // llvm/test/Examples/OrcV2Examples/lljit-with-thinlto-summaries.test
24 // If you use "build" as the build directory, you can run the test from the root
25 // of the monorepo like this:
27 // > build/bin/llvm-lit -a \
28 // llvm/test/Examples/OrcV2Examples/lljit-with-thinlto-summaries.test
30 //===----------------------------------------------------------------------===//
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"
51 #include <system_error>
55 using namespace llvm::orc
;
57 // Path of the module summary index file.
58 cl::opt
<std::string
> IndexFile
{cl::desc("<module summary index>"),
59 cl::Positional
, cl::init("-")};
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
65 class DuplicateDefinitionInSummary
66 : public ErrorInfo
<DuplicateDefinitionInSummary
> {
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
);
78 void log(raw_ostream
&OS
) const override
{
79 OS
<< "Duplicate symbol for global value '" << GlobalValueName
80 << "' (GUID: " << GlobalValue::getGUID(GlobalValueName
) << ") in:\n";
81 for (const std::string
&Path
: ModulePaths
) {
82 OS
<< " " << Path
<< "\n";
86 std::error_code
convertToErrorCode() const override
{
87 return inconvertibleErrorCode();
91 std::string GlobalValueName
;
92 std::vector
<std::string
> ModulePaths
;
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.
98 class DefinitionNotFoundInSummary
99 : public ErrorInfo
<DefinitionNotFoundInSummary
> {
103 DefinitionNotFoundInSummary(std::string GlobalValueName
,
104 ModuleSummaryIndex
&Index
)
105 : GlobalValueName(std::move(GlobalValueName
)) {
106 ModulePaths
.reserve(Index
.modulePaths().size());
107 for (const auto &Entry
: Index
.modulePaths())
108 ModulePaths
.push_back(Entry
.first().str());
109 llvm::sort(ModulePaths
);
112 void log(raw_ostream
&OS
) const override
{
113 OS
<< "No symbol for global value '" << GlobalValueName
114 << "' (GUID: " << GlobalValue::getGUID(GlobalValueName
) << ") in:\n";
115 for (const std::string
&Path
: ModulePaths
) {
116 OS
<< " " << Path
<< "\n";
120 std::error_code
convertToErrorCode() const override
{
121 return llvm::inconvertibleErrorCode();
125 std::string GlobalValueName
;
126 std::vector
<std::string
> ModulePaths
;
129 char DuplicateDefinitionInSummary::ID
= 0;
130 char DefinitionNotFoundInSummary::ID
= 0;
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.
135 Expected
<StringRef
> getMainModulePath(StringRef FunctionName
,
136 ModuleSummaryIndex
&Index
) {
137 // Summaries use unmangled names.
138 GlobalValue::GUID G
= GlobalValue::getGUID(FunctionName
);
139 ValueInfo VI
= Index
.getValueInfo(G
);
141 // We need a unique definition, otherwise don't try further.
142 if (!VI
|| VI
.getSummaryList().empty())
143 return make_error
<DefinitionNotFoundInSummary
>(FunctionName
.str(), Index
);
144 if (VI
.getSummaryList().size() > 1)
145 return make_error
<DuplicateDefinitionInSummary
>(FunctionName
.str(), VI
);
147 GlobalValueSummary
*S
= VI
.getSummaryList().front()->getBaseObject();
148 if (!isa
<FunctionSummary
>(S
))
149 return createStringError(inconvertibleErrorCode(),
150 "Entry point is not a function: " + FunctionName
);
152 // Return a reference. ModuleSummaryIndex owns the module paths.
153 return S
->modulePath();
156 // Parse the bitcode module from the given path into a ThreadSafeModule.
157 Expected
<ThreadSafeModule
> loadModule(StringRef Path
,
158 orc::ThreadSafeContext TSCtx
) {
159 outs() << "About to load module: " << Path
<< "\n";
161 Expected
<std::unique_ptr
<MemoryBuffer
>> BitcodeBuffer
=
162 errorOrToExpected(MemoryBuffer::getFile(Path
));
164 return BitcodeBuffer
.takeError();
166 MemoryBufferRef BitcodeBufferRef
= (**BitcodeBuffer
).getMemBufferRef();
167 Expected
<std::unique_ptr
<Module
>> M
=
168 parseBitcodeFile(BitcodeBufferRef
, *TSCtx
.getContext());
170 return M
.takeError();
172 return ThreadSafeModule(std::move(*M
), std::move(TSCtx
));
175 int main(int Argc
, char *Argv
[]) {
176 InitLLVM
X(Argc
, Argv
);
178 InitializeNativeTarget();
179 InitializeNativeTargetAsmPrinter();
181 cl::ParseCommandLineOptions(Argc
, Argv
, "LLJITWithThinLTOSummaries");
183 ExitOnError ExitOnErr
;
184 ExitOnErr
.setBanner(std::string(Argv
[0]) + ": ");
186 // (1) Read the index file and parse the module summary index.
187 std::unique_ptr
<MemoryBuffer
> SummaryBuffer
=
188 ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(IndexFile
)));
190 std::unique_ptr
<ModuleSummaryIndex
> SummaryIndex
=
191 ExitOnErr(getModuleSummaryIndex(SummaryBuffer
->getMemBufferRef()));
193 // (2) Find the path of the module that defines "main".
194 std::string MainFunctionName
= "main";
195 StringRef MainModulePath
=
196 ExitOnErr(getMainModulePath(MainFunctionName
, *SummaryIndex
));
198 // (3) Parse the main module and create a matching LLJIT.
199 ThreadSafeContext
TSCtx(std::make_unique
<LLVMContext
>());
200 ThreadSafeModule MainModule
= ExitOnErr(loadModule(MainModulePath
, TSCtx
));
202 auto Builder
= LLJITBuilder();
204 MainModule
.withModuleDo([&](Module
&M
) {
205 if (M
.getTargetTriple().empty()) {
206 Builder
.setJITTargetMachineBuilder(
207 ExitOnErr(JITTargetMachineBuilder::detectHost()));
209 Builder
.setJITTargetMachineBuilder(
210 JITTargetMachineBuilder(Triple(M
.getTargetTriple())));
212 if (!M
.getDataLayout().getStringRepresentation().empty())
213 Builder
.setDataLayout(M
.getDataLayout());
216 auto J
= ExitOnErr(Builder
.create());
218 // (4) Add all modules to the LLJIT that are covered by the index.
219 JITDylib
&JD
= J
->getMainJITDylib();
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
, std::move(M
)));
229 // (5) Look up and run the JIT'd function.
230 auto MainAddr
= ExitOnErr(J
->lookup(MainFunctionName
));
232 using MainFnPtr
= int (*)(int, char *[]);
233 auto *MainFunction
= MainAddr
.toPtr
<MainFnPtr
>();
235 int Result
= runAsMain(MainFunction
, {}, MainModulePath
);
236 outs() << "'" << MainFunctionName
<< "' finished with exit code: " << Result