blob: ba0dab91c4ef58e225aa123132290bb8ac83a244 [file] [log] [blame]
Lang Hames42c9b592016-05-26 21:17:06 +00001=============================================
2Building a JIT: Per-function Lazy Compilation
3=============================================
4
5.. contents::
6 :local:
7
8**This tutorial is under active development. It is incomplete and details may
9change frequently.** Nonetheless we invite you to try it out as it stands, and
10we welcome any feedback.
11
12Chapter 3 Introduction
13======================
14
15Welcome to Chapter 3 of the "Building an ORC-based JIT in LLVM" tutorial. This
16chapter discusses lazy JITing and shows you how to enable it by adding an ORC
17CompileOnDemand layer the JIT from `Chapter 2 <BuildingAJIT2.html>`_.
18
Lang Hames7cd3ac72016-07-15 01:39:49 +000019Lazy Compilation
20================
21
22When we add a module to the KaleidoscopeJIT class described in Chapter 2 it is
23immediately optimized, compiled and linked for us by the IRTransformLayer,
24IRCompileLayer and ObjectLinkingLayer respectively. This scheme, where all the
25work to make a Module executable is done up front, is relatively simple to
26understand its performance characteristics are easy to reason about. However,
27it will lead to very high startup times if the amount of code to be compiled is
28large, and may also do a lot of unnecessary compilation if only a few compiled
29functions are ever called at runtime. A truly "just-in-time" compiler should
30allow us to defer the compilation of any given function until the moment that
31function is first called, improving launch times and eliminating redundant work.
32In fact, the ORC APIs provide us with a layer to lazily compile LLVM IR:
33*CompileOnDemandLayer*.
34
35The CompileOnDemandLayer conforms to the layer interface described in Chapter 2,
36but the addModuleSet method behaves quite differently from the layers we have
37seen so far: rather than doing any work up front, it just constructs a *stub*
38for each function in the module and arranges for the stub to trigger compilation
39of the actual function the first time it is called. Because stub functions are
40very cheap to produce CompileOnDemand's addModuleSet method runs very quickly,
41reducing the time required to launch the first function to be executed, and
42saving us from doing any redundant compilation. By conforming to the layer
43interface, CompileOnDemand can be easily added on top of our existing JIT class.
44We just need a few changes:
45
46.. code-block:: c++
47
48 ...
49 #include "llvm/ExecutionEngine/SectionMemoryManager.h"
50 #include "llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h"
51 #include "llvm/ExecutionEngine/Orc/CompileUtils.h"
52 ...
53
54 ...
55 class KaleidoscopeJIT {
56 private:
57 std::unique_ptr<TargetMachine> TM;
58 const DataLayout DL;
59 std::unique_ptr<JITCompileCallbackManager> CompileCallbackManager;
60 ObjectLinkingLayer<> ObjectLayer;
61 IRCompileLayer<decltype(ObjectLayer)> CompileLayer;
62
63 typedef std::function<std::unique_ptr<Module>(std::unique_ptr<Module>)>
64 OptimizeFunction;
65
66 IRTransformLayer<decltype(CompileLayer), OptimizeFunction> OptimizeLayer;
67 CompileOnDemandLayer<decltype(OptimizeLayer)> CODLayer;
68
69 public:
70 typedef decltype(CODLayer)::ModuleSetHandleT ModuleHandle;
71
72First we need to include the CompileOnDemandLayer.h header, then add two new
73members: a std::unique_ptr<CompileCallbackManager> and a CompileOnDemandLayer,
74to our class. The CompileCallbackManager is a utility that enables us to
75create re-entry points into the compiler for functions that we want to lazily
76compile. In the next chapter we'll be looking at this class in detail, but for
77now we'll be treating it as an opaque utility: We just need to pass a reference
78to it into our new CompileOnDemandLayer, and the layer will do all the work of
79setting up the callbacks using the callback manager we gave it.
80
Alexander Kornienkod80f6262016-07-18 14:13:18 +000081.. code-block:: c++
Lang Hames7cd3ac72016-07-15 01:39:49 +000082
83 KaleidoscopeJIT()
84 : TM(EngineBuilder().selectTarget()), DL(TM->createDataLayout()),
85 CompileLayer(ObjectLayer, SimpleCompiler(*TM)),
86 OptimizeLayer(CompileLayer,
87 [this](std::unique_ptr<Module> M) {
88 return optimizeModule(std::move(M));
89 }),
90 CompileCallbackManager(
91 orc::createLocalCompileCallbackManager(TM->getTargetTriple(), 0)),
92 CODLayer(OptimizeLayer,
93 [this](Function &F) { return std::set<Function*>({&F}); },
94 *CompileCallbackManager,
95 orc::createLocalIndirectStubsManagerBuilder(
96 TM->getTargetTriple())) {
97 llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr);
98 }
99
100Next we have to update our constructor to initialize the new members. To create
101an appropriate compile callback manager we use the
102createLocalCompileCallbackManager function, which takes a TargetMachine and a
103TargetAddress to call if it receives a request to compile an unknown function.
104In our simple JIT this situation is unlikely to come up, so we'll cheat and
105just pass '0' here. In a production quality JIT you could give the address of a
106function that throws an exception in order to unwind the JIT'd code stack.
107
108Now we can construct our CompileOnDemandLayer. Following the pattern from
109previous layers we start by passing a reference to the next layer down in our
110stack -- the OptimizeLayer. Next we need to supply a 'partitioning function':
111when a not-yet-compiled function is called, the CompileOnDemandLayer will call
112this function to ask us what we would like to compile. At a minimum we need to
113compile the function being called (given by the argument to the partitioning
114function), but we could also request that the CompileOnDemandLayer compile other
115functions that are unconditionally called (or highly likely to be called) from
116the function being called. For KaleidoscopeJIT we'll keep it simple and just
117request compilation of the function that was called. Next we pass a reference to
118our CompileCallbackManager. Finally, we need to supply an "indirect stubs
119manager builder". This is a function that constructs IndirectStubManagers, which
120are in turn used to build the stubs for each module. The CompileOnDemandLayer
121will call the indirect stub manager builder once for each call to addModuleSet,
122and use the resulting indirect stubs manager to create stubs for all functions
123in all modules added. If/when the module set is removed from the JIT the
124indirect stubs manager will be deleted, freeing any memory allocated to the
125stubs. We supply this function by using the
126createLocalIndirectStubsManagerBuilder utility.
127
Alexander Kornienkod80f6262016-07-18 14:13:18 +0000128.. code-block:: c++
129
Lang Hames7cd3ac72016-07-15 01:39:49 +0000130 // ...
131 if (auto Sym = CODLayer.findSymbol(Name, false))
132 // ...
133 return CODLayer.addModuleSet(std::move(Ms),
134 make_unique<SectionMemoryManager>(),
135 std::move(Resolver));
136 // ...
137
138 // ...
139 return CODLayer.findSymbol(MangledNameStream.str(), true);
140 // ...
141
142 // ...
143 CODLayer.removeModuleSet(H);
144 // ...
145
146Finally, we need to replace the references to OptimizeLayer in our addModule,
147findSymbol, and removeModule methods. With that, we're up and running.
148
Lang Hames42c9b592016-05-26 21:17:06 +0000149**To be done:**
150
Lang Hames7cd3ac72016-07-15 01:39:49 +0000151** Discuss CompileCallbackManagers and IndirectStubManagers in more detail.**
Lang Hames42c9b592016-05-26 21:17:06 +0000152
153Full Code Listing
154=================
155
156Here is the complete code listing for our running example with a CompileOnDemand
157layer added to enable lazy function-at-a-time compilation. To build this example, use:
158
159.. code-block:: bash
160
161 # Compile
162 clang++ -g toy.cpp `llvm-config --cxxflags --ldflags --system-libs --libs core orc native` -O3 -o toy
163 # Run
164 ./toy
165
166Here is the code:
167
168.. literalinclude:: ../../examples/Kaleidoscope/BuildingAJIT/Chapter3/KaleidoscopeJIT.h
169 :language: c++
170
171`Next: Extreme Laziness -- Using Compile Callbacks to JIT directly from ASTs <BuildingAJIT4.html>`_