1 | //===--- LLJITWithLazyReexports.cpp - LLJIT example with custom laziness --===// |
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/LLJIT.h" |
26 | #include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" |
27 | #include "llvm/Support/InitLLVM.h" |
28 | #include "llvm/Support/TargetSelect.h" |
29 | #include "llvm/Support/raw_ostream.h" |
30 | |
31 | #include "../ExampleModules.h" |
32 | |
33 | using namespace llvm; |
34 | using namespace llvm::orc; |
35 | |
36 | ExitOnError ExitOnErr; |
37 | |
38 | // Example IR modules. |
39 | // |
40 | // Note that in the conditionally compiled modules, FooMod and BarMod, functions |
41 | // have been given an _body suffix. This is to ensure that their names do not |
42 | // clash with their lazy-reexports. |
43 | // For clients who do not wish to rename function bodies (e.g. because they want |
44 | // to re-use cached objects between static and JIT compiles) techniques exist to |
45 | // avoid renaming. See the lazy-reexports section of the ORCv2 design doc. |
46 | |
47 | const llvm::StringRef FooMod = |
48 | R"( |
49 | define i32 @foo_body() { |
50 | entry: |
51 | ret i32 1 |
52 | } |
53 | )" ; |
54 | |
55 | const llvm::StringRef BarMod = |
56 | R"( |
57 | define i32 @bar_body() { |
58 | entry: |
59 | ret i32 2 |
60 | } |
61 | )" ; |
62 | |
63 | const llvm::StringRef MainMod = |
64 | R"( |
65 | |
66 | define i32 @entry(i32 %argc) { |
67 | entry: |
68 | %and = and i32 %argc, 1 |
69 | %tobool = icmp eq i32 %and, 0 |
70 | br i1 %tobool, label %if.end, label %if.then |
71 | |
72 | if.then: ; preds = %entry |
73 | %call = tail call i32 @foo() #2 |
74 | br label %return |
75 | |
76 | if.end: ; preds = %entry |
77 | %call1 = tail call i32 @bar() #2 |
78 | br label %return |
79 | |
80 | return: ; preds = %if.end, %if.then |
81 | %retval.0 = phi i32 [ %call, %if.then ], [ %call1, %if.end ] |
82 | ret i32 %retval.0 |
83 | } |
84 | |
85 | declare i32 @foo() |
86 | declare i32 @bar() |
87 | )" ; |
88 | |
89 | cl::list<std::string> InputArgv(cl::Positional, |
90 | cl::desc("<program arguments>..." )); |
91 | |
92 | int main(int argc, char *argv[]) { |
93 | // Initialize LLVM. |
94 | InitLLVM X(argc, argv); |
95 | |
96 | InitializeNativeTarget(); |
97 | InitializeNativeTargetAsmPrinter(); |
98 | |
99 | cl::ParseCommandLineOptions(argc, argv, Overview: "LLJITWithLazyReexports" ); |
100 | ExitOnErr.setBanner(std::string(argv[0]) + ": " ); |
101 | |
102 | // (1) Create LLJIT instance. |
103 | auto J = ExitOnErr(LLJITBuilder().create()); |
104 | |
105 | // (2) Install transform to print modules as they are compiled: |
106 | J->getIRTransformLayer().setTransform( |
107 | [](ThreadSafeModule TSM, |
108 | const MaterializationResponsibility &R) -> Expected<ThreadSafeModule> { |
109 | TSM.withModuleDo(F: [](Module &M) { dbgs() << "---Compiling---\n" << M; }); |
110 | return std::move(TSM); // Not a redundant move: fix build on gcc-7.5 |
111 | }); |
112 | |
113 | // (3) Create stubs and call-through managers: |
114 | std::unique_ptr<IndirectStubsManager> ISM; |
115 | { |
116 | auto ISMBuilder = |
117 | createLocalIndirectStubsManagerBuilder(T: J->getTargetTriple()); |
118 | if (!ISMBuilder()) |
119 | ExitOnErr(make_error<StringError>(Args: "Could not create stubs manager for " + |
120 | J->getTargetTriple().str(), |
121 | Args: inconvertibleErrorCode())); |
122 | ISM = ISMBuilder(); |
123 | } |
124 | auto LCTM = ExitOnErr(createLocalLazyCallThroughManager( |
125 | T: J->getTargetTriple(), ES&: J->getExecutionSession(), ErrorHandlerAddr: ExecutorAddr())); |
126 | |
127 | // (4) Add modules. |
128 | ExitOnErr(J->addIRModule(TSM: ExitOnErr(parseExampleModule(Source: FooMod, Name: "foo-mod" )))); |
129 | ExitOnErr(J->addIRModule(TSM: ExitOnErr(parseExampleModule(Source: BarMod, Name: "bar-mod" )))); |
130 | ExitOnErr(J->addIRModule(TSM: ExitOnErr(parseExampleModule(Source: MainMod, Name: "main-mod" )))); |
131 | |
132 | // (5) Add lazy reexports. |
133 | MangleAndInterner Mangle(J->getExecutionSession(), J->getDataLayout()); |
134 | SymbolAliasMap ReExports( |
135 | {{Mangle("foo" ), |
136 | {Mangle("foo_body" ), |
137 | JITSymbolFlags::Exported | JITSymbolFlags::Callable}}, |
138 | {Mangle("bar" ), |
139 | {Mangle("bar_body" ), |
140 | JITSymbolFlags::Exported | JITSymbolFlags::Callable}}}); |
141 | ExitOnErr(J->getMainJITDylib().define( |
142 | MU: lazyReexports(LCTManager&: *LCTM, ISManager&: *ISM, SourceJD&: J->getMainJITDylib(), CallableAliases: std::move(ReExports)))); |
143 | |
144 | // (6) Dump the ExecutionSession state. |
145 | dbgs() << "---Session state---\n" ; |
146 | J->getExecutionSession().dump(OS&: dbgs()); |
147 | dbgs() << "\n" ; |
148 | |
149 | // (7) Execute the JIT'd main function and pass the example's command line |
150 | // arguments unmodified. This should cause either ExampleMod1 or ExampleMod2 |
151 | // to be compiled, and either "1" or "2" returned depending on the number of |
152 | // arguments passed. |
153 | |
154 | // Look up the JIT'd function, cast it to a function pointer, then call it. |
155 | auto EntryAddr = ExitOnErr(J->lookup(UnmangledName: "entry" )); |
156 | auto *Entry = EntryAddr.toPtr<int(int)>(); |
157 | |
158 | int Result = Entry(argc); |
159 | outs() << "---Result---\n" |
160 | << "entry(" << argc << ") = " << Result << "\n" ; |
161 | |
162 | return 0; |
163 | } |
164 | |