Mercurial > hg > CbC > CbC_llvm
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. |