llvm.org GIT mirror llvm / 5a04b09
[Kaleidoscope][BuildingAJIT] More work on the text for Chapter 3. Add an overview of stubs and compile callbacks before the discussion of the source changes. -- This line, and those below, will be ignored-- M docs/tutorial/BuildingAJIT3.rst git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@275933 91177308-0d34-0410-b5e6-96231b3b80d8 Lang Hames 3 years ago
1 changed file(s) with 46 addition(s) and 33 deletion(s). Raw diff Collapse all Expand all
2121 When we add a module to the KaleidoscopeJIT class described in Chapter 2 it is
2222 immediately optimized, compiled and linked for us by the IRTransformLayer,
2323 IRCompileLayer and ObjectLinkingLayer respectively. This scheme, where all the
24 work to make a Module executable is done up front, is relatively simple to
25 understand its performance characteristics are easy to reason about. However,
26 it will lead to very high startup times if the amount of code to be compiled is
27 large, and may also do a lot of unnecessary compilation if only a few compiled
28 functions are ever called at runtime. A truly "just-in-time" compiler should
29 allow us to defer the compilation of any given function until the moment that
30 function is first called, improving launch times and eliminating redundant work.
31 In fact, the ORC APIs provide us with a layer to lazily compile LLVM IR:
24 work to make a Module executable is done up front, is simple to understand its
25 performance characteristics are easy to reason about. However, it will lead to
26 very high startup times if the amount of code to be compiled is large, and may
27 also do a lot of unnecessary compilation if only a few compiled functions are
28 ever called at runtime. A truly "just-in-time" compiler should allow us to
29 defer the compilation of any given function until the moment that function is
30 first called, improving launch times and eliminating redundant work. In fact,
31 the ORC APIs provide us with a layer to lazily compile LLVM IR:
3232 *CompileOnDemandLayer*.
3333
34 The CompileOnDemandLayer conforms to the layer interface described in Chapter 2,
35 but the addModuleSet method behaves quite differently from the layers we have
36 seen so far: rather than doing any work up front, it just constructs a *stub*
37 for each function in the module and arranges for the stub to trigger compilation
38 of the actual function the first time it is called. Because stub functions are
39 very cheap to produce CompileOnDemand's addModuleSet method runs very quickly,
40 reducing the time required to launch the first function to be executed, and
41 saving us from doing any redundant compilation. By conforming to the layer
42 interface, CompileOnDemand can be easily added on top of our existing JIT class.
43 We just need a few changes:
34 The CompileOnDemandLayer class conforms to the layer interface described in
35 Chapter 2, but its addModuleSet method behaves quite differently from the layers
36 we have seen so far: rather than doing any work up front, it just scans the
37 Modules being added and arranges for each function in them to be compiled the
38 first time it is called. To do this, the CompileOnDemandLayer creates two small
39 utilities for each function that it scans: a *stub* and a *compile
40 callback*. The stub is a pair of a function pointer (which will be pointed at
41 the function's implementation once the function has been compiled) and an
42 indirect jump through the pointer. By fixing the address of the indirect jump
43 for the lifetime of the program we can give the function a permanent "effective
44 address", one that can be safely used for indirection and function pointer
45 comparison even if the function's implementation is never compiled, or if it is
46 compiled more than once (due to, for example, recompiling the function at a
47 higher optimization level) and changes address. The second utility, the compile
48 callback, represents a re-entry point from the program into the compiler that
49 will trigger compilation and then execution of a function. By initializing the
50 function's stub to point at the function's compile callback, we enable lazy
51 compilation: The first attempted call to the function will follow the function
52 pointer and trigger the compile callback instead. The compile callback will
53 compile the function, update the function pointer for the stub, then execute
54 the function. On all subsequent calls to the function, the function pointer
55 will point at the already-compiled function, so there is no further overhead
56 from the compiler. We will look at this process in more detail in the next
57 chapter of this tutorial, but for now we'll trust the CompileOnDemandLayer to
58 set all the stubs and callbacks up for us. All we need to do is to add the
59 CompileOnDemandLayer to the top of our stack and we'll get the benefits of
60 lazy compilation. We just need a few changes to the source:
4461
4562 .. code-block:: c++
4663
7087
7188 First we need to include the CompileOnDemandLayer.h header, then add two new
7289 members: a std::unique_ptr and a CompileOnDemandLayer,
73 to our class. The CompileCallbackManager is a utility that enables us to
74 create re-entry points into the compiler for functions that we want to lazily
75 compile. In the next chapter we'll be looking at this class in detail, but for
76 now we'll be treating it as an opaque utility: We just need to pass a reference
77 to it into our new CompileOnDemandLayer, and the layer will do all the work of
78 setting up the callbacks using the callback manager we gave it.
90 to our class. The CompileCallbackManager member is used by the CompileOnDemandLayer
91 to create the compile callback needed for each function.
7992
8093 .. code-block:: c++
8194
102115 TargetAddress to call if it receives a request to compile an unknown function.
103116 In our simple JIT this situation is unlikely to come up, so we'll cheat and
104117 just pass '0' here. In a production quality JIT you could give the address of a
105 function that throws an exception in order to unwind the JIT'd code stack.
118 function that throws an exception in order to unwind the JIT'd code's stack.
106119
107120 Now we can construct our CompileOnDemandLayer. Following the pattern from
108121 previous layers we start by passing a reference to the next layer down in our
115128 the function being called. For KaleidoscopeJIT we'll keep it simple and just
116129 request compilation of the function that was called. Next we pass a reference to
117130 our CompileCallbackManager. Finally, we need to supply an "indirect stubs
118 manager builder". This is a function that constructs IndirectStubManagers, which
119 are in turn used to build the stubs for each module. The CompileOnDemandLayer
120 will call the indirect stub manager builder once for each call to addModuleSet,
121 and use the resulting indirect stubs manager to create stubs for all functions
122 in all modules added. If/when the module set is removed from the JIT the
123 indirect stubs manager will be deleted, freeing any memory allocated to the
124 stubs. We supply this function by using the
131 manager builder": a utility function that constructs IndirectStubManagers, which
132 are in turn used to build the stubs for the functions in each module. The
133 CompileOnDemandLayer will call the indirect stub manager builder once for each
134 call to addModuleSet, and use the resulting indirect stubs manager to create
135 stubs for all functions in all modules in the set. If/when the module set is
136 removed from the JIT the indirect stubs manager will be deleted, freeing any
137 memory allocated to the stubs. We supply this function by using the
125138 createLocalIndirectStubsManagerBuilder utility.
126139
127140 .. code-block:: c++
147160
148161 **To be done:**
149162
150 ** Discuss CompileCallbackManagers and IndirectStubManagers in more detail.**
163 ** Chapter conclusion.**
151164
152165 Full Code Listing
153166 =================