comparison docs/tutorial/BuildingAJIT2.rst @ 121:803732b1fca8

LLVM 5.0
author kono
date Fri, 27 Oct 2017 17:07:41 +0900
parents 1172e4bd9c6f
children 3a76565eade5
comparison
equal deleted inserted replaced
120:1172e4bd9c6f 121:803732b1fca8
23 IRTransformLayer, to add IR optimization support to KaleidoscopeJIT. 23 IRTransformLayer, to add IR optimization support to KaleidoscopeJIT.
24 24
25 Optimizing Modules using the IRTransformLayer 25 Optimizing Modules using the IRTransformLayer
26 ============================================= 26 =============================================
27 27
28 In `Chapter 4 <LangImpl4.html>`_ of the "Implementing a language with LLVM" 28 In `Chapter 4 <LangImpl04.html>`_ of the "Implementing a language with LLVM"
29 tutorial series the llvm *FunctionPassManager* is introduced as a means for 29 tutorial series the llvm *FunctionPassManager* is introduced as a means for
30 optimizing LLVM IR. Interested readers may read that chapter for details, but 30 optimizing LLVM IR. Interested readers may read that chapter for details, but
31 in short: to optimize a Module we create an llvm::FunctionPassManager 31 in short: to optimize a Module we create an llvm::FunctionPassManager
32 instance, configure it with a set of optimizations, then run the PassManager on 32 instance, configure it with a set of optimizations, then run the PassManager on
33 a Module to mutate it into a (hopefully) more optimized but semantically 33 a Module to mutate it into a (hopefully) more optimized but semantically
44 To add optimization support to our JIT we will take the KaleidoscopeJIT from 44 To add optimization support to our JIT we will take the KaleidoscopeJIT from
45 Chapter 1 and compose an ORC *IRTransformLayer* on top. We will look at how the 45 Chapter 1 and compose an ORC *IRTransformLayer* on top. We will look at how the
46 IRTransformLayer works in more detail below, but the interface is simple: the 46 IRTransformLayer works in more detail below, but the interface is simple: the
47 constructor for this layer takes a reference to the layer below (as all layers 47 constructor for this layer takes a reference to the layer below (as all layers
48 do) plus an *IR optimization function* that it will apply to each Module that 48 do) plus an *IR optimization function* that it will apply to each Module that
49 is added via addModuleSet: 49 is added via addModule:
50 50
51 .. code-block:: c++ 51 .. code-block:: c++
52 52
53 class KaleidoscopeJIT { 53 class KaleidoscopeJIT {
54 private: 54 private:
55 std::unique_ptr<TargetMachine> TM; 55 std::unique_ptr<TargetMachine> TM;
56 const DataLayout DL; 56 const DataLayout DL;
57 ObjectLinkingLayer<> ObjectLayer; 57 RTDyldObjectLinkingLayer<> ObjectLayer;
58 IRCompileLayer<decltype(ObjectLayer)> CompileLayer; 58 IRCompileLayer<decltype(ObjectLayer)> CompileLayer;
59 59
60 typedef std::function<std::unique_ptr<Module>(std::unique_ptr<Module>)> 60 using OptimizeFunction =
61 OptimizeFunction; 61 std::function<std::shared_ptr<Module>(std::shared_ptr<Module>)>;
62 62
63 IRTransformLayer<decltype(CompileLayer), OptimizeFunction> OptimizeLayer; 63 IRTransformLayer<decltype(CompileLayer), OptimizeFunction> OptimizeLayer;
64 64
65 public: 65 public:
66 typedef decltype(OptimizeLayer)::ModuleSetHandleT ModuleHandle; 66 using ModuleHandle = decltype(OptimizeLayer)::ModuleHandleT;
67 67
68 KaleidoscopeJIT() 68 KaleidoscopeJIT()
69 : TM(EngineBuilder().selectTarget()), DL(TM->createDataLayout()), 69 : TM(EngineBuilder().selectTarget()), DL(TM->createDataLayout()),
70 ObjectLayer([]() { return std::make_shared<SectionMemoryManager>(); }),
70 CompileLayer(ObjectLayer, SimpleCompiler(*TM)), 71 CompileLayer(ObjectLayer, SimpleCompiler(*TM)),
71 OptimizeLayer(CompileLayer, 72 OptimizeLayer(CompileLayer,
72 [this](std::unique_ptr<Module> M) { 73 [this](std::unique_ptr<Module> M) {
73 return optimizeModule(std::move(M)); 74 return optimizeModule(std::move(M));
74 }) { 75 }) {
99 // ... 100 // ...
100 101
101 .. code-block:: c++ 102 .. code-block:: c++
102 103
103 // ... 104 // ...
104 return OptimizeLayer.addModuleSet(std::move(Ms), 105 return cantFail(OptimizeLayer.addModule(std::move(M),
105 make_unique<SectionMemoryManager>(), 106 std::move(Resolver)));
106 std::move(Resolver));
107 // ... 107 // ...
108 108
109 .. code-block:: c++ 109 .. code-block:: c++
110 110
111 // ... 111 // ...
113 // ... 113 // ...
114 114
115 .. code-block:: c++ 115 .. code-block:: c++
116 116
117 // ... 117 // ...
118 OptimizeLayer.removeModuleSet(H); 118 cantFail(OptimizeLayer.removeModule(H));
119 // ... 119 // ...
120 120
121 Next we need to replace references to 'CompileLayer' with references to 121 Next we need to replace references to 'CompileLayer' with references to
122 OptimizeLayer in our key methods: addModule, findSymbol, and removeModule. In 122 OptimizeLayer in our key methods: addModule, findSymbol, and removeModule. In
123 addModule we need to be careful to replace both references: the findSymbol call 123 addModule we need to be careful to replace both references: the findSymbol call
124 inside our resolver, and the call through to addModuleSet. 124 inside our resolver, and the call through to addModule.
125 125
126 .. code-block:: c++ 126 .. code-block:: c++
127 127
128 std::unique_ptr<Module> optimizeModule(std::unique_ptr<Module> M) { 128 std::shared_ptr<Module> optimizeModule(std::shared_ptr<Module> M) {
129 // Create a function pass manager. 129 // Create a function pass manager.
130 auto FPM = llvm::make_unique<legacy::FunctionPassManager>(M.get()); 130 auto FPM = llvm::make_unique<legacy::FunctionPassManager>(M.get());
131 131
132 // Add some optimizations. 132 // Add some optimizations.
133 FPM->add(createInstructionCombiningPass()); 133 FPM->add(createInstructionCombiningPass());
146 146
147 At the bottom of our JIT we add a private method to do the actual optimization: 147 At the bottom of our JIT we add a private method to do the actual optimization:
148 *optimizeModule*. This function sets up a FunctionPassManager, adds some passes 148 *optimizeModule*. This function sets up a FunctionPassManager, adds some passes
149 to it, runs it over every function in the module, and then returns the mutated 149 to it, runs it over every function in the module, and then returns the mutated
150 module. The specific optimizations are the same ones used in 150 module. The specific optimizations are the same ones used in
151 `Chapter 4 <LangImpl4.html>`_ of the "Implementing a language with LLVM" 151 `Chapter 4 <LangImpl04.html>`_ of the "Implementing a language with LLVM"
152 tutorial series. Readers may visit that chapter for a more in-depth 152 tutorial series. Readers may visit that chapter for a more in-depth
153 discussion of these, and of IR optimization in general. 153 discussion of these, and of IR optimization in general.
154 154
155 And that's it in terms of changes to KaleidoscopeJIT: When a module is added via 155 And that's it in terms of changes to KaleidoscopeJIT: When a module is added via
156 addModule the OptimizeLayer will call our optimizeModule function before passing 156 addModule the OptimizeLayer will call our optimizeModule function before passing
164 .. code-block:: c++ 164 .. code-block:: c++
165 165
166 template <typename BaseLayerT, typename TransformFtor> 166 template <typename BaseLayerT, typename TransformFtor>
167 class IRTransformLayer { 167 class IRTransformLayer {
168 public: 168 public:
169 typedef typename BaseLayerT::ModuleSetHandleT ModuleSetHandleT; 169 using ModuleHandleT = typename BaseLayerT::ModuleHandleT;
170 170
171 IRTransformLayer(BaseLayerT &BaseLayer, 171 IRTransformLayer(BaseLayerT &BaseLayer,
172 TransformFtor Transform = TransformFtor()) 172 TransformFtor Transform = TransformFtor())
173 : BaseLayer(BaseLayer), Transform(std::move(Transform)) {} 173 : BaseLayer(BaseLayer), Transform(std::move(Transform)) {}
174 174
175 template <typename ModuleSetT, typename MemoryManagerPtrT, 175 Expected<ModuleHandleT>
176 typename SymbolResolverPtrT> 176 addModule(std::shared_ptr<Module> M,
177 ModuleSetHandleT addModuleSet(ModuleSetT Ms, 177 std::shared_ptr<JITSymbolResolver> Resolver) {
178 MemoryManagerPtrT MemMgr, 178 return BaseLayer.addModule(Transform(std::move(M)), std::move(Resolver));
179 SymbolResolverPtrT Resolver) { 179 }
180 180
181 for (auto I = Ms.begin(), E = Ms.end(); I != E; ++I) 181 void removeModule(ModuleHandleT H) { BaseLayer.removeModule(H); }
182 *I = Transform(std::move(*I));
183
184 return BaseLayer.addModuleSet(std::move(Ms), std::move(MemMgr),
185 std::move(Resolver));
186 }
187
188 void removeModuleSet(ModuleSetHandleT H) { BaseLayer.removeModuleSet(H); }
189 182
190 JITSymbol findSymbol(const std::string &Name, bool ExportedSymbolsOnly) { 183 JITSymbol findSymbol(const std::string &Name, bool ExportedSymbolsOnly) {
191 return BaseLayer.findSymbol(Name, ExportedSymbolsOnly); 184 return BaseLayer.findSymbol(Name, ExportedSymbolsOnly);
192 } 185 }
193 186
194 JITSymbol findSymbolIn(ModuleSetHandleT H, const std::string &Name, 187 JITSymbol findSymbolIn(ModuleHandleT H, const std::string &Name,
195 bool ExportedSymbolsOnly) { 188 bool ExportedSymbolsOnly) {
196 return BaseLayer.findSymbolIn(H, Name, ExportedSymbolsOnly); 189 return BaseLayer.findSymbolIn(H, Name, ExportedSymbolsOnly);
197 } 190 }
198 191
199 void emitAndFinalize(ModuleSetHandleT H) { 192 void emitAndFinalize(ModuleHandleT H) {
200 BaseLayer.emitAndFinalize(H); 193 BaseLayer.emitAndFinalize(H);
201 } 194 }
202 195
203 TransformFtor& getTransform() { return Transform; } 196 TransformFtor& getTransform() { return Transform; }
204 197
213 ``llvm/include/llvm/ExecutionEngine/Orc/IRTransformLayer.h``, stripped of its 206 ``llvm/include/llvm/ExecutionEngine/Orc/IRTransformLayer.h``, stripped of its
214 comments. It is a template class with two template arguments: ``BaesLayerT`` and 207 comments. It is a template class with two template arguments: ``BaesLayerT`` and
215 ``TransformFtor`` that provide the type of the base layer and the type of the 208 ``TransformFtor`` that provide the type of the base layer and the type of the
216 "transform functor" (in our case a std::function) respectively. This class is 209 "transform functor" (in our case a std::function) respectively. This class is
217 concerned with two very simple jobs: (1) Running every IR Module that is added 210 concerned with two very simple jobs: (1) Running every IR Module that is added
218 with addModuleSet through the transform functor, and (2) conforming to the ORC 211 with addModule through the transform functor, and (2) conforming to the ORC
219 layer interface. The interface consists of one typedef and five methods: 212 layer interface. The interface consists of one typedef and five methods:
220 213
221 +------------------+-----------------------------------------------------------+ 214 +------------------+-----------------------------------------------------------+
222 | Interface | Description | 215 | Interface | Description |
223 +==================+===========================================================+ 216 +==================+===========================================================+
224 | | Provides a handle that can be used to identify a module | 217 | | Provides a handle that can be used to identify a module |
225 | ModuleSetHandleT | set when calling findSymbolIn, removeModuleSet, or | 218 | ModuleHandleT | set when calling findSymbolIn, removeModule, or |
226 | | emitAndFinalize. | 219 | | emitAndFinalize. |
227 +------------------+-----------------------------------------------------------+ 220 +------------------+-----------------------------------------------------------+
228 | | Takes a given set of Modules and makes them "available | 221 | | Takes a given set of Modules and makes them "available |
229 | | for execution. This means that symbols in those modules | 222 | | for execution. This means that symbols in those modules |
230 | | should be searchable via findSymbol and findSymbolIn, and | 223 | | should be searchable via findSymbol and findSymbolIn, and |
231 | | the address of the symbols should be read/writable (for | 224 | | the address of the symbols should be read/writable (for |
232 | | data symbols), or executable (for function symbols) after | 225 | | data symbols), or executable (for function symbols) after |
233 | | JITSymbol::getAddress() is called. Note: This means that | 226 | | JITSymbol::getAddress() is called. Note: This means that |
234 | addModuleSet | addModuleSet doesn't have to compile (or do any other | 227 | addModule | addModule doesn't have to compile (or do any other |
235 | | work) up-front. It *can*, like IRCompileLayer, act | 228 | | work) up-front. It *can*, like IRCompileLayer, act |
236 | | eagerly, but it can also simply record the module and | 229 | | eagerly, but it can also simply record the module and |
237 | | take no further action until somebody calls | 230 | | take no further action until somebody calls |
238 | | JITSymbol::getAddress(). In IRTransformLayer's case | 231 | | JITSymbol::getAddress(). In IRTransformLayer's case |
239 | | addModuleSet eagerly applies the transform functor to | 232 | | addModule eagerly applies the transform functor to |
240 | | each module in the set, then passes the resulting set | 233 | | each module in the set, then passes the resulting set |
241 | | of mutated modules down to the layer below. | 234 | | of mutated modules down to the layer below. |
242 +------------------+-----------------------------------------------------------+ 235 +------------------+-----------------------------------------------------------+
243 | | Removes a set of modules from the JIT. Code or data | 236 | | Removes a set of modules from the JIT. Code or data |
244 | removeModuleSet | defined in these modules will no longer be available, and | 237 | removeModule | defined in these modules will no longer be available, and |
245 | | the memory holding the JIT'd definitions will be freed. | 238 | | the memory holding the JIT'd definitions will be freed. |
246 +------------------+-----------------------------------------------------------+ 239 +------------------+-----------------------------------------------------------+
247 | | Searches for the named symbol in all modules that have | 240 | | Searches for the named symbol in all modules that have |
248 | | previously been added via addModuleSet (and not yet | 241 | | previously been added via addModule (and not yet |
249 | findSymbol | removed by a call to removeModuleSet). In | 242 | findSymbol | removed by a call to removeModule). In |
250 | | IRTransformLayer we just pass the query on to the layer | 243 | | IRTransformLayer we just pass the query on to the layer |
251 | | below. In our REPL this is our default way to search for | 244 | | below. In our REPL this is our default way to search for |
252 | | function definitions. | 245 | | function definitions. |
253 +------------------+-----------------------------------------------------------+ 246 +------------------+-----------------------------------------------------------+
254 | | Searches for the named symbol in the module set indicated | 247 | | Searches for the named symbol in the module set indicated |
255 | | by the given ModuleSetHandleT. This is just an optimized | 248 | | by the given ModuleHandleT. This is just an optimized |
256 | | search, better for lookup-speed when you know exactly | 249 | | search, better for lookup-speed when you know exactly |
257 | | a symbol definition should be found. In IRTransformLayer | 250 | | a symbol definition should be found. In IRTransformLayer |
258 | findSymbolIn | we just pass this query on to the layer below. In our | 251 | findSymbolIn | we just pass this query on to the layer below. In our |
259 | | REPL we use this method to search for functions | 252 | | REPL we use this method to search for functions |
260 | | representing top-level expressions, since we know exactly | 253 | | representing top-level expressions, since we know exactly |
261 | | where we'll find them: in the top-level expression module | 254 | | where we'll find them: in the top-level expression module |
262 | | we just added. | 255 | | we just added. |
263 +------------------+-----------------------------------------------------------+ 256 +------------------+-----------------------------------------------------------+
264 | | Forces all of the actions required to make the code and | 257 | | Forces all of the actions required to make the code and |
265 | | data in a module set (represented by a ModuleSetHandleT) | 258 | | data in a module set (represented by a ModuleHandleT) |
266 | | accessible. Behaves as if some symbol in the set had been | 259 | | accessible. Behaves as if some symbol in the set had been |
267 | | searched for and JITSymbol::getSymbolAddress called. This | 260 | | searched for and JITSymbol::getSymbolAddress called. This |
268 | emitAndFinalize | is rarely needed, but can be useful when dealing with | 261 | emitAndFinalize | is rarely needed, but can be useful when dealing with |
269 | | layers that usually behave lazily if the user wants to | 262 | | layers that usually behave lazily if the user wants to |
270 | | trigger early compilation (for example, to use idle CPU | 263 | | trigger early compilation (for example, to use idle CPU |
274 This interface attempts to capture the natural operations of a JIT (with some 267 This interface attempts to capture the natural operations of a JIT (with some
275 wrinkles like emitAndFinalize for performance), similar to the basic JIT API 268 wrinkles like emitAndFinalize for performance), similar to the basic JIT API
276 operations we identified in Chapter 1. Conforming to the layer concept allows 269 operations we identified in Chapter 1. Conforming to the layer concept allows
277 classes to compose neatly by implementing their behaviors in terms of the these 270 classes to compose neatly by implementing their behaviors in terms of the these
278 same operations, carried out on the layer below. For example, an eager layer 271 same operations, carried out on the layer below. For example, an eager layer
279 (like IRTransformLayer) can implement addModuleSet by running each module in the 272 (like IRTransformLayer) can implement addModule by running each module in the
280 set through its transform up-front and immediately passing the result to the 273 set through its transform up-front and immediately passing the result to the
281 layer below. A lazy layer, by contrast, could implement addModuleSet by 274 layer below. A lazy layer, by contrast, could implement addModule by
282 squirreling away the modules doing no other up-front work, but applying the 275 squirreling away the modules doing no other up-front work, but applying the
283 transform (and calling addModuleSet on the layer below) when the client calls 276 transform (and calling addModule on the layer below) when the client calls
284 findSymbol instead. The JIT'd program behavior will be the same either way, but 277 findSymbol instead. The JIT'd program behavior will be the same either way, but
285 these choices will have different performance characteristics: Doing work 278 these choices will have different performance characteristics: Doing work
286 eagerly means the JIT takes longer up-front, but proceeds smoothly once this is 279 eagerly means the JIT takes longer up-front, but proceeds smoothly once this is
287 done. Deferring work allows the JIT to get up-and-running quickly, but will 280 done. Deferring work allows the JIT to get up-and-running quickly, but will
288 force the JIT to pause and wait whenever some code or data is needed that hasn't 281 force the JIT to pause and wait whenever some code or data is needed that hasn't
317 IRTransformLayer added to enable optimization. To build this example, use: 310 IRTransformLayer added to enable optimization. To build this example, use:
318 311
319 .. code-block:: bash 312 .. code-block:: bash
320 313
321 # Compile 314 # Compile
322 clang++ -g toy.cpp `llvm-config --cxxflags --ldflags --system-libs --libs core orc native` -O3 -o toy 315 clang++ -g toy.cpp `llvm-config --cxxflags --ldflags --system-libs --libs core orcjit native` -O3 -o toy
323 # Run 316 # Run
324 ./toy 317 ./toy
325 318
326 Here is the code: 319 Here is the code:
327 320
328 .. literalinclude:: ../../examples/Kaleidoscope/BuildingAJIT/Chapter2/KaleidoscopeJIT.h 321 .. literalinclude:: ../../examples/Kaleidoscope/BuildingAJIT/Chapter2/KaleidoscopeJIT.h
329 :language: c++ 322 :language: c++
330 323
331 .. [1] When we add our top-level expression to the JIT, any calls to functions 324 .. [1] When we add our top-level expression to the JIT, any calls to functions
332 that we defined earlier will appear to the ObjectLinkingLayer as 325 that we defined earlier will appear to the RTDyldObjectLinkingLayer as
333 external symbols. The ObjectLinkingLayer will call the SymbolResolver 326 external symbols. The RTDyldObjectLinkingLayer will call the SymbolResolver
334 that we defined in addModuleSet, which in turn calls findSymbol on the 327 that we defined in addModule, which in turn calls findSymbol on the
335 OptimizeLayer, at which point even a lazy transform layer will have to 328 OptimizeLayer, at which point even a lazy transform layer will have to
336 do its work. 329 do its work.