1//===- LLJITWithExecutorProcessControl.cpp - LLJIT example with EPC utils -===//
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 the lazy re-exports utility to lazily compile
10// IR modules. We will do this in seven steps:
11//
12// 1. Create an LLJIT instance.
13// 2. Install a transform so that we can see what is being compiled.
14// 3. Create an indirect stubs manager and lazy call-through manager.
15// 4. Add two modules that will be conditionally compiled, plus a main module.
16// 5. Add lazy-rexports of the symbols in the conditionally compiled modules.
17// 6. Dump the ExecutionSession state to see the symbol table prior to
18// executing any code.
19// 7. Verify that only modules containing executed code are compiled.
20//
21//===----------------------------------------------------------------------===//
22
23#include "llvm/ADT/StringMap.h"
24#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
25#include "llvm/ExecutionEngine/Orc/EPCIndirectionUtils.h"
26#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
27#include "llvm/ExecutionEngine/Orc/LLJIT.h"
28#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
29#include "llvm/ExecutionEngine/Orc/OrcABISupport.h"
30#include "llvm/Support/InitLLVM.h"
31#include "llvm/Support/TargetSelect.h"
32#include "llvm/Support/raw_ostream.h"
33
34#include "../ExampleModules.h"
35
36#include <future>
37
38using namespace llvm;
39using namespace llvm::orc;
40
41ExitOnError ExitOnErr;
42
43// Example IR modules.
44//
45// Note that in the conditionally compiled modules, FooMod and BarMod, functions
46// have been given an _body suffix. This is to ensure that their names do not
47// clash with their lazy-reexports.
48// For clients who do not wish to rename function bodies (e.g. because they want
49// to re-use cached objects between static and JIT compiles) techniques exist to
50// avoid renaming. See the lazy-reexports section of the ORCv2 design doc.
51
52const llvm::StringRef FooMod =
53 R"(
54 declare i32 @return1()
55
56 define i32 @foo_body() {
57 entry:
58 %0 = call i32 @return1()
59 ret i32 %0
60 }
61)";
62
63const llvm::StringRef BarMod =
64 R"(
65 declare i32 @return2()
66
67 define i32 @bar_body() {
68 entry:
69 %0 = call i32 @return2()
70 ret i32 %0
71 }
72)";
73
74const llvm::StringRef MainMod =
75 R"(
76
77 define i32 @entry(i32 %argc) {
78 entry:
79 %and = and i32 %argc, 1
80 %tobool = icmp eq i32 %and, 0
81 br i1 %tobool, label %if.end, label %if.then
82
83 if.then: ; preds = %entry
84 %call = tail call i32 @foo() #2
85 br label %return
86
87 if.end: ; preds = %entry
88 %call1 = tail call i32 @bar() #2
89 br label %return
90
91 return: ; preds = %if.end, %if.then
92 %retval.0 = phi i32 [ %call, %if.then ], [ %call1, %if.end ]
93 ret i32 %retval.0
94 }
95
96 declare i32 @foo()
97 declare i32 @bar()
98)";
99
100extern "C" int32_t return1() { return 1; }
101extern "C" int32_t return2() { return 2; }
102
103static void *reenter(void *Ctx, void *TrampolineAddr) {
104 std::promise<void *> LandingAddressP;
105 auto LandingAddressF = LandingAddressP.get_future();
106
107 auto *EPCIU = static_cast<EPCIndirectionUtils *>(Ctx);
108 EPCIU->getLazyCallThroughManager().resolveTrampolineLandingAddress(
109 TrampolineAddr: ExecutorAddr::fromPtr(Ptr: TrampolineAddr), NotifyLandingResolved: [&](ExecutorAddr LandingAddress) {
110 LandingAddressP.set_value(LandingAddress.toPtr<void *>());
111 });
112 return LandingAddressF.get();
113}
114
115static void reportErrorAndExit() {
116 errs() << "Unable to lazily compile function. Exiting.\n";
117 exit(status: 1);
118}
119
120cl::list<std::string> InputArgv(cl::Positional,
121 cl::desc("<program arguments>..."));
122
123int main(int argc, char *argv[]) {
124 // Initialize LLVM.
125 InitLLVM X(argc, argv);
126
127 InitializeNativeTarget();
128 InitializeNativeTargetAsmPrinter();
129
130 cl::ParseCommandLineOptions(argc, argv, Overview: "LLJITWithLazyReexports");
131 ExitOnErr.setBanner(std::string(argv[0]) + ": ");
132
133 // (1) Create LLJIT instance.
134 auto EPC = ExitOnErr(SelfExecutorProcessControl::Create());
135 auto J = ExitOnErr(
136 LLJITBuilder().setExecutorProcessControl(std::move(EPC)).create());
137
138 // (2) Install transform to print modules as they are compiled:
139 J->getIRTransformLayer().setTransform(
140 [](ThreadSafeModule TSM,
141 const MaterializationResponsibility &R) -> Expected<ThreadSafeModule> {
142 TSM.withModuleDo(F: [](Module &M) { dbgs() << "---Compiling---\n" << M; });
143 return std::move(TSM); // Not a redundant move: fix build on gcc-7.5
144 });
145
146 // (3) Create stubs and call-through managers:
147 auto EPCIU = ExitOnErr(EPCIndirectionUtils::Create(ES&: J->getExecutionSession()));
148 ExitOnErr(EPCIU->writeResolverBlock(ReentryFnAddr: ExecutorAddr::fromPtr(Ptr: &reenter),
149 ReentryCtxAddr: ExecutorAddr::fromPtr(Ptr: EPCIU.get())));
150 EPCIU->createLazyCallThroughManager(
151 ES&: J->getExecutionSession(), ErrorHandlerAddr: ExecutorAddr::fromPtr(Ptr: &reportErrorAndExit));
152 auto ISM = EPCIU->createIndirectStubsManager();
153
154 // (4) Add modules.
155 ExitOnErr(J->addIRModule(TSM: ExitOnErr(parseExampleModule(Source: FooMod, Name: "foo-mod"))));
156 ExitOnErr(J->addIRModule(TSM: ExitOnErr(parseExampleModule(Source: BarMod, Name: "bar-mod"))));
157 ExitOnErr(J->addIRModule(TSM: ExitOnErr(parseExampleModule(Source: MainMod, Name: "main-mod"))));
158
159 // (5) Add lazy reexports.
160 MangleAndInterner Mangle(J->getExecutionSession(), J->getDataLayout());
161 SymbolAliasMap ReExports(
162 {{Mangle("foo"),
163 {Mangle("foo_body"),
164 JITSymbolFlags::Exported | JITSymbolFlags::Callable}},
165 {Mangle("bar"),
166 {Mangle("bar_body"),
167 JITSymbolFlags::Exported | JITSymbolFlags::Callable}}});
168 ExitOnErr(J->getMainJITDylib().define(
169 MU: lazyReexports(LCTManager&: EPCIU->getLazyCallThroughManager(), ISManager&: *ISM,
170 SourceJD&: J->getMainJITDylib(), CallableAliases: std::move(ReExports))));
171
172 // (6) Dump the ExecutionSession state.
173 dbgs() << "---Session state---\n";
174 J->getExecutionSession().dump(OS&: dbgs());
175 dbgs() << "\n";
176
177 // (7) Execute the JIT'd main function and pass the example's command line
178 // arguments unmodified. This should cause either ExampleMod1 or ExampleMod2
179 // to be compiled, and either "1" or "2" returned depending on the number of
180 // arguments passed.
181
182 // Look up the JIT'd function, cast it to a function pointer, then call it.
183 auto EntryAddr = ExitOnErr(J->lookup(UnmangledName: "entry"));
184 auto *Entry = EntryAddr.toPtr<int(int)>();
185
186 int Result = Entry(argc);
187 outs() << "---Result---\n"
188 << "entry(" << argc << ") = " << Result << "\n";
189
190 // Destroy the EPCIndirectionUtils utility.
191 ExitOnErr(EPCIU->cleanup());
192
193 return 0;
194}
195

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