llvm.org GIT mirror llvm / 009a762
[BuildingAJIT] Update the Ch1 KaleidoscopeJIT class to expose errors to clients. Returning the error to clients provides an opportunity to introduce readers to the Expected and Error APIs and makes the tutorial more useful as a starting point for a real JIT class, while only slightly complicating the code. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@344720 91177308-0d34-0410-b5e6-96231b3b80d8 Lang Hames 9 months ago
3 changed file(s) with 128 addition(s) and 122 deletion(s). Raw diff Collapse all Expand all
88 ======================
99
1010 **Warning: This tutorial is currently being updated to account for ORC API
11 changes. Only Chapter 1 is up-to-date.**
12
13 **Example code from Chapters 2 to 4 will compile and run, but has not been
11 changes. Only Chapters 1 and 2 are up-to-date.**
12
13 **Example code from Chapters 3 to 5 will compile and run, but has not been
1414 updated**
1515
1616 Welcome to Chapter 1 of the "Building an ORC-based JIT in LLVM" tutorial. This
6464 compiler does. To support that aim our initial, bare-bones JIT API will have
6565 just two functions:
6666
67 1. void addModule(std::unique_ptr M) -- Make the given IR module
67 1. ``Error addModule(std::unique_ptr M)``: Make the given IR module
6868 available for execution.
69 2. Expected lookup() -- Search for pointers to
69 2. ``Expected lookup()``: Search for pointers to
7070 symbols (functions or variables) that have been added to the JIT.
7171
7272 A basic use-case for this API, executing the 'main' function from a module,
126126
127127 class KaleidoscopeJIT {
128128 private:
129
130129 ExecutionSession ES;
131 RTDyldObjectLinkingLayer ObjectLayer{ES, getMemoryMgr};
132 IRCompileLayer CompileLayer{ES, ObjectLayer,
133 ConcurrentIRCompiler(getJTMB())};
134 DataLayout DL{cantFail(getJTMB().getDefaultDataLayoutForTarget())};
135 MangleAndInterner Mangle{ES, DL};
136 ThreadSafeContext Ctx{llvm::make_unique()};
137
138 static JITTargetMachineBuilder getJTMB() {
139 return cantFail(JITTargetMachineBuilder::detectHost());
130 RTDyldObjectLinkingLayer ObjectLayer;
131 IRCompileLayer CompileLayer;
132
133 DataLayout DL;
134 MangleAndInterner Mangle;
135 ThreadSafeContext Ctx;
136
137 public:
138 KaleidoscopeJIT(JITTargetMachineBuilder JTMB, DataLayout DL)
139 : ObjectLayer(ES,
140 []() { return llvm::make_unique(); }),
141 CompileLayer(ES, ObjectLayer, ConcurrentIRCompiler(std::move(JTMB))),
142 DL(std::move(DL)), Mangle(ES, this->DL),
143 Ctx(llvm::make_unique()) {
144 ES.getMainJITDylib().setGenerator(
145 cantFail(DynamicLibrarySearchGenerator::GetForCurrentProcess(DL)));
140146 }
141147
142 static std::unique_ptr getMemoryMgr(VModuleKey) {
143 return llvm::make_unique();
144 }
145
146 We begin with the ExecutionSession member, ``ES``, which provides context for
147 our running JIT'd code. It holds the string pool for symbol names, the global
148 mutex that guards the critical sections of JIT operations, error logging
149 facilities, and other utilities. For basic use cases such as this, a default
150 constructed ExecutionSession is all we will need. We will investigate more
151 advanced uses of ExecutionSession in later chapters. Following our
152 ExecutionSession we have two ORC *layers*: an RTDyldObjectLinkingLayer and an
153 IRCompileLayer. We will be talking more about layers in the next chapter, but
154 for now you can think of them as analogous to LLVM Passes: they wrap up useful
155 JIT utilities behind an easy to compose interface. The first layer, ObjectLayer,
156 is the foundation of our JIT: it takes in-memory object files produced by a
157 compiler and links them on the fly to make them executable. This
158 JIT-on-top-of-a-linker design was introduced in MCJIT, however the linker was
159 hidden inside the MCJIT class. In ORC we expose the linker so that clients can
160 access and configure it directly if they need to. In this tutorial our
161 ObjectLayer will just be used to support the next layer in our stack: the
162 CompileLayer, which will be responsible for taking LLVM IR, compiling it, and
163 passing the resulting in-memory object files down to the object linking layer
164 below. Our ObjectLayer is constructed with a reference to the ExecutionSession
165 and the getMemoryMgr utility function, which it uses to generate a new memory
166 manager for each object file as it is added. Next up is our CompileLayer, which
167 is initialized with a reference to the ExecutionSession, a reference to the
168 ObjectLayer (where it will send the objects produced by the compiler), and an IR
169 compiler instance. In this case we are using the ConcurrentIRCompiler class
170 which is constructed with a JITTargetMachineBuilder and can be called to compile
171 IR concurrently from several threads (though in this chapter we will only use
172 one).
173
174 Following the ExecutionSession and layers we have three supporting member
175 variables. The DataLayout, ``DL``; and MangleAndInterner, ``Mangle`` members are
176 used to support portable lookups based on IR symbol names (more on that when we
177 get to our ``lookup`` function below), and the ThreadSafeContext member,
178 ``Ctx``, manages an LLVMContext that can be used while building IR Modules for
179 the JIT.
180
181 After that, we have two static utility functions. The ``getJTMB()`` function
182 returns a JITTargetMachineBuilder, which is a factory for building LLVM
183 TargetMachine instances that are used by the compiler. In this first tutorial we
184 will only need one (implicitly created) TargetMachine, but in future tutorials
185 that enable concurrent compilation we will need one per thread. This is why we
186 use a target machine builder, rather than a single TargetMachine. (note: Older
187 LLVM JIT APIs that did not support concurrent compilation were constructed with
188 a single TargetMachines). The ``getMemoryMgr()`` function constructs instances
189 of RuntimeDyld::MemoryManager, and is used by the linking layer to generate a
190 new memory manager for each object file.
191
192 .. code-block:: c++
193
194 public:
195
196 KaleidoscopeJIT() {
197 ES.getMainJITDylib().setGenerator(
198 cantFail(DynamicLibrarySearchGenerator::GetForCurrentProcess(DL)));
199 }
200
201 const DataLayout &getDataLayout() const { return DL; }
202
203 LLVMContext &getContext() { return *Ctx.getContext(); }
204
205 Next up we have our class constructor. Our members have already been
206 initialized, so the one thing that remains to do is to tweak the configuration
207 of the *JITDylib* that we will store our code in. We want to modify this dylib
208 to contain not only the symbols that we add to it, but also the symbols from
209 our REPL process as well. We do this by attaching a
148 Our class begins with six member variables: An ExecutionSession member, ``ES``,
149 which provides context for our running JIT'd code (including the string pool,
150 global mutex, and error reporting facilities); An RTDyldObjectLinkingLayer,
151 ``ObjectLayer``, that can be used to add object files to our JIT (though we will
152 not use it directly); An IRCompileLayer, ``CompileLayer``, that can be used to
153 add LLVM Modules to our JIT (and which builds on the ObjectLayer), A DataLayout
154 and MangleAndInterner, ``DL`` and ``Mangle``, that will be used for symbol mangling
155 (more on that later); and finally an LLVMContext that clients will use when
156 building IR files for the JIT.
157
158 Next up we have our class constructor, which takes a `JITTargetMachineBuilder``
159 that will be used by our IRCompiler, and a ``DataLayout`` that we will use to
160 initialize our DL member. The constructor begins by initializing our
161 ObjectLayer. The ObjectLayer requires a reference to the ExecutionSession, and
162 a function object that will build a JIT memory manager for each module that is
163 added (a JIT memory manager manages memory allocations, memory permissions, and
164 registration of exception handlers for JIT'd code). For this we use a lambda
165 that returns a SectionMemoryManager, an off-the-shelf utility that provides all
166 the basic memory management functionality required for this chapter. Next we
167 initialize our CompileLayer. The CompileLayer needs three things: (1) A
168 reference to the ExecutionSession, (2) A reference to our object layer, and (3)
169 a compiler instance to use to perform the actual compilation from IR to object
170 files. We use the off-the-shelf ConcurrentIRCompiler utility as our compiler,
171 which we construct using this constructor's JITTargetMachineBuilder argument.
172 The ConcurrentIRCompiler utility will use the JITTargetMachineBuilder to build
173 llvm TargetMachines (which are not thread safe) as needed for compiles. After
174 this, we initialize our supporting members: ``DL``, ``Mangler`` and ``Ctx`` with
175 the input DataLayout, the ExecutionSession and DL member, and a new default
176 constucted LLVMContext respectively. Now that our members have been initialized,
177 so the one thing that remains to do is to tweak the configuration of the
178 *JITDylib* that we will store our code in. We want to modify this dylib to
179 contain not only the symbols that we add to it, but also the symbols from our
180 REPL process as well. We do this by attaching a
210181 ``DynamicLibrarySearchGenerator`` instance using the
211182 ``DynamicLibrarySearchGenerator::GetForCurrentProcess`` method.
212183
213 Following the constructor we have the ``getDataLayout()`` and ``getContext()``
214 methods. These are used to make data structures created and managed by the JIT
215 (especially the LLVMContext) available to the REPL code that will build our
216 IR modules.
184
185 .. code-block:: c++
186
187 static Expected> Create() {
188 auto JTMB = JITTargetMachineBuilder::detectHost();
189
190 if (!JTMB)
191 return JTMB.takeError();
192
193 auto DL = JTMB->getDefaultDataLayoutForTarget();
194 if (!DL)
195 return DL.takeError();
196
197 return llvm::make_unique(std::move(*JTMB), std::move(*DL));
198 }
199
200 const DataLayout &getDataLayout() const { return DL; }
201
202 LLVMContext &getContext() { return *Ctx.getContext(); }
203
204 Next we have a named constructor, ``Create``, which will build a KaleidoscopeJIT
205 instance that is configured to generate code for our host process. It does this
206 by first generating a JITTargetMachineBuilder instance using that clases's
207 detectHost method and then using that instance to generate a datalayout for
208 the target process. Each of these operations can fail, so each returns its
209 result wrapped in an Expected value [3]_ that we must check for error before
210 continuing. If both operations succeed we can unwrap their results (using the
211 dereference operator) and pass them into KaleidoscopeJIT's constructor on the
212 last line of the function.
213
214 Following the named constructor we have the ``getDataLayout()`` and
215 ``getContext()`` methods. These are used to make data structures created and
216 managed by the JIT (especially the LLVMContext) available to the REPL code that
217 will build our IR modules.
217218
218219 .. code-block:: c++
219220
316317 +-----------------------------+-----------------------------------------------+
317318 | LLVMContext.h | Provides the LLVMContext class. |
318319 +-----------------------------+-----------------------------------------------+
320
321 .. [3] See the ErrorHandling section in the LLVM Programmer's Manual
322 (http://llvm.org/docs/ProgrammersManual.html#error-handling)
3131
3232 class KaleidoscopeJIT {
3333 private:
34 ExecutionSession ES;
35 RTDyldObjectLinkingLayer ObjectLayer;
36 IRCompileLayer CompileLayer;
3437
35 ExecutionSession ES;
36 RTDyldObjectLinkingLayer ObjectLayer{ES, getMemoryMgr};
37 IRCompileLayer CompileLayer{ES, ObjectLayer,
38 ConcurrentIRCompiler(getJTMB())};
39 DataLayout DL{cantFail(getJTMB().getDefaultDataLayoutForTarget())};
40 MangleAndInterner Mangle{ES, DL};
41 ThreadSafeContext Ctx{llvm::make_unique()};
38 DataLayout DL;
39 MangleAndInterner Mangle;
40 ThreadSafeContext Ctx;
4241
43 static JITTargetMachineBuilder getJTMB() {
44 return cantFail(JITTargetMachineBuilder::detectHost());
42 public:
43 KaleidoscopeJIT(JITTargetMachineBuilder JTMB, DataLayout DL)
44 : ObjectLayer(ES,
45 []() { return llvm::make_unique(); }),
46 CompileLayer(ES, ObjectLayer, ConcurrentIRCompiler(std::move(JTMB))),
47 DL(std::move(DL)), Mangle(ES, this->DL),
48 Ctx(llvm::make_unique()) {
49 ES.getMainJITDylib().setGenerator(
50 cantFail(DynamicLibrarySearchGenerator::GetForCurrentProcess(DL)));
4551 }
4652
47 static std::unique_ptr getMemoryMgr() {
48 return llvm::make_unique();
49 }
53 static Expected> Create() {
54 auto JTMB = JITTargetMachineBuilder::detectHost();
5055
51 public:
56 if (!JTMB)
57 return JTMB.takeError();
5258
53 KaleidoscopeJIT() {
54 ES.getMainJITDylib().setGenerator(
55 cantFail(DynamicLibrarySearchGenerator::GetForCurrentProcess(DL)));
59 auto DL = JTMB->getDefaultDataLayoutForTarget();
60 if (!DL)
61 return DL.takeError();
62
63 return llvm::make_unique(std::move(*JTMB), std::move(*DL));
5664 }
5765
5866 const DataLayout &getDataLayout() const { return DL; }
5967
6068 LLVMContext &getContext() { return *Ctx.getContext(); }
6169
62 void addModule(std::unique_ptr M) {
63 cantFail(CompileLayer.add(ES.getMainJITDylib(),
64 ThreadSafeModule(std::move(M), Ctx)));
70 Error addModule(std::unique_ptr M) {
71 return CompileLayer.add(ES.getMainJITDylib(),
72 ThreadSafeModule(std::move(M), Ctx));
6573 }
6674
6775 Expected lookup(StringRef Name) {
702702 static std::unique_ptr TheModule;
703703 static std::map NamedValues;
704704 static std::map> FunctionProtos;
705 static ExitOnError ExitOnErr;
705706
706707 Value *LogErrorV(const char *Str) {
707708 LogError(Str);
11151116 fprintf(stderr, "Read function definition:");
11161117 FnIR->print(errs());
11171118 fprintf(stderr, "\n");
1118 TheJIT->addModule(std::move(TheModule));
1119 ExitOnErr(TheJIT->addModule(std::move(TheModule)));
11191120 InitializeModule();
11201121 }
11211122 } else {
11501151 if (FnAST->codegen()) {
11511152 // JIT the module containing the anonymous expression, keeping a handle so
11521153 // we can free it later.
1153 TheJIT->addModule(std::move(TheModule));
1154 ExitOnErr(TheJIT->addModule(std::move(TheModule)));
11541155 InitializeModule();
11551156
11561157 // Get the anonymous expression's JITSymbol.
1157 auto Sym = TheJIT->lookup(("__anon_expr" + Twine(ExprCount)).str());
1158
1159 if (Sym) {
1160 // If the lookup succeeded, cast the symbol's address to a function
1161 // pointer then call it.
1162 auto *FP = (double (*)())(intptr_t)Sym->getAddress();
1163 assert(FP && "Failed to codegen function");
1164 fprintf(stderr, "Evaluated to %f\n", FP());
1165 } else {
1166 // Otherwise log the reason the symbol lookup failed.
1167 logAllUnhandledErrors(Sym.takeError(), errs(),
1168 "Could not evaluate: ");
1169 }
1158 auto Sym =
1159 ExitOnErr(TheJIT->lookup(("__anon_expr" + Twine(ExprCount)).str()));
1160
1161 auto *FP = (double (*)())(intptr_t)Sym.getAddress();
1162 assert(FP && "Failed to codegen function");
1163 fprintf(stderr, "Evaluated to %f\n", FP());
11701164 }
11711165 } else {
11721166 // Skip token for error recovery.
12341228 fprintf(stderr, "ready> ");
12351229 getNextToken();
12361230
1237 TheJIT = llvm::make_unique();
1231 TheJIT = ExitOnErr(KaleidoscopeJIT::Create());
12381232 TheContext = &TheJIT->getContext();
12391233
12401234 InitializeModule();