1======================================================= 2Building a JIT: Starting out with KaleidoscopeJIT 3======================================================= 4 5.. contents:: 6 :local: 7 8Chapter 1 Introduction 9====================== 10 11**Warning: This tutorial is currently being updated to account for ORC API 12changes. Only Chapter 1 is up-to-date.** 13 14**Example code from Chapters 2 to 4 will compile and run, but has not been 15updated** 16 17Welcome to Chapter 1 of the "Building an ORC-based JIT in LLVM" tutorial. This 18tutorial runs through the implementation of a JIT compiler using LLVM's 19On-Request-Compilation (ORC) APIs. It begins with a simplified version of the 20KaleidoscopeJIT class used in the 21`Implementing a language with LLVM <LangImpl01.html>`_ tutorials and then 22introduces new features like concurrent compilation, optimization, lazy 23compilation and remote execution. 24 25The goal of this tutorial is to introduce you to LLVM's ORC JIT APIs, show how 26these APIs interact with other parts of LLVM, and to teach you how to recombine 27them to build a custom JIT that is suited to your use-case. 28 29The structure of the tutorial is: 30 31- Chapter #1: Investigate the simple KaleidoscopeJIT class. This will 32 introduce some of the basic concepts of the ORC JIT APIs, including the 33 idea of an ORC *Layer*. 34 35- `Chapter #2 <BuildingAJIT2.html>`_: Extend the basic KaleidoscopeJIT by adding 36 a new layer that will optimize IR and generated code. 37 38- `Chapter #3 <BuildingAJIT3.html>`_: Further extend the JIT by adding a 39 Compile-On-Demand layer to lazily compile IR. 40 41- `Chapter #4 <BuildingAJIT4.html>`_: Improve the laziness of our JIT by 42 replacing the Compile-On-Demand layer with a custom layer that uses the ORC 43 Compile Callbacks API directly to defer IR-generation until functions are 44 called. 45 46- `Chapter #5 <BuildingAJIT5.html>`_: Add process isolation by JITing code into 47 a remote process with reduced privileges using the JIT Remote APIs. 48 49To provide input for our JIT we will use a lightly modified version of the 50Kaleidoscope REPL from `Chapter 7 <LangImpl07.html>`_ of the "Implementing a 51language in LLVM tutorial". 52 53Finally, a word on API generations: ORC is the 3rd generation of LLVM JIT API. 54It was preceded by MCJIT, and before that by the (now deleted) legacy JIT. 55These tutorials don't assume any experience with these earlier APIs, but 56readers acquainted with them will see many familiar elements. Where appropriate 57we will make this connection with the earlier APIs explicit to help people who 58are transitioning from them to ORC. 59 60JIT API Basics 61============== 62 63The purpose of a JIT compiler is to compile code "on-the-fly" as it is needed, 64rather than compiling whole programs to disk ahead of time as a traditional 65compiler does. To support that aim our initial, bare-bones JIT API will have 66just two functions: 67 681. void addModule(std::unique_ptr<Module> M) -- Make the given IR module 69 available for execution. 702. Expected<JITSymbol> lookup() -- Search for pointers to 71 symbols (functions or variables) that have been added to the JIT. 72 73A basic use-case for this API, executing the 'main' function from a module, 74will look like: 75 76.. code-block:: c++ 77 78 JIT J; 79 J.addModule(buildModule()); 80 auto *Main = (int(*)(int, char*[]))J.lookup("main").getAddress(); 81 int Result = Main(); 82 83The APIs that we build in these tutorials will all be variations on this simple 84theme. Behind this API we will refine the implementation of the JIT to add 85support for concurrent compilation, optimization and lazy compilation. 86Eventually we will extend the API itself to allow higher-level program 87representations (e.g. ASTs) to be added to the JIT. 88 89KaleidoscopeJIT 90=============== 91 92In the previous section we described our API, now we examine a simple 93implementation of it: The KaleidoscopeJIT class [1]_ that was used in the 94`Implementing a language with LLVM <LangImpl01.html>`_ tutorials. We will use 95the REPL code from `Chapter 7 <LangImpl07.html>`_ of that tutorial to supply the 96input for our JIT: Each time the user enters an expression the REPL will add a 97new IR module containing the code for that expression to the JIT. If the 98expression is a top-level expression like '1+1' or 'sin(x)', the REPL will also 99use the lookup method of our JIT class find and execute the code for the 100expression. In later chapters of this tutorial we will modify the REPL to enable 101new interactions with our JIT class, but for now we will take this setup for 102granted and focus our attention on the implementation of our JIT itself. 103 104Our KaleidoscopeJIT class is defined in the KaleidoscopeJIT.h header. After the 105usual include guards and #includes [2]_, we get to the definition of our class: 106 107.. code-block:: c++ 108 109 #ifndef LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H 110 #define LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H 111 112 #include "llvm/ADT/StringRef.h" 113 #include "llvm/ExecutionEngine/JITSymbol.h" 114 #include "llvm/ExecutionEngine/Orc/CompileUtils.h" 115 #include "llvm/ExecutionEngine/Orc/Core.h" 116 #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" 117 #include "llvm/ExecutionEngine/Orc/IRCompileLayer.h" 118 #include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h" 119 #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" 120 #include "llvm/ExecutionEngine/SectionMemoryManager.h" 121 #include "llvm/IR/DataLayout.h" 122 #include "llvm/IR/LLVMContext.h" 123 #include <memory> 124 125 namespace llvm { 126 namespace orc { 127 128 class KaleidoscopeJIT { 129 private: 130 131 ExecutionSession ES; 132 RTDyldObjectLinkingLayer ObjectLayer{ES, getMemoryMgr}; 133 IRCompileLayer CompileLayer{ES, ObjectLayer, 134 ConcurrentIRCompiler(getJTMB())}; 135 DataLayout DL{cantFail(getJTMB().getDefaultDataLayoutForTarget())}; 136 MangleAndInterner Mangle{ES, DL}; 137 ThreadSafeContext Ctx{llvm::make_unique<LLVMContext>()}; 138 139 static JITTargetMachineBuilder getJTMB() { 140 return cantFail(JITTargetMachineBuilder::detectHost()); 141 } 142 143 static std::unique_ptr<SectionMemoryManager> getMemoryMgr(VModuleKey) { 144 return llvm::make_unique<SectionMemoryManager>(); 145 } 146 147We begin with the ExecutionSession member, ``ES``, which provides context for 148our running JIT'd code. It holds the string pool for symbol names, the global 149mutex that guards the critical sections of JIT operations, error logging 150facilities, and other utilities. For basic use cases such as this, a default 151constructed ExecutionSession is all we will need. We will investigate more 152advanced uses of ExecutionSession in later chapters. Following our 153ExecutionSession we have two ORC *layers*: an RTDyldObjectLinkingLayer and an 154IRCompileLayer. We will be talking more about layers in the next chapter, but 155for now you can think of them as analogous to LLVM Passes: they wrap up useful 156JIT utilities behind an easy to compose interface. The first layer, ObjectLayer, 157is the foundation of our JIT: it takes in-memory object files produced by a 158compiler and links them on the fly to make them executable. This 159JIT-on-top-of-a-linker design was introduced in MCJIT, however the linker was 160hidden inside the MCJIT class. In ORC we expose the linker so that clients can 161access and configure it directly if they need to. In this tutorial our 162ObjectLayer will just be used to support the next layer in our stack: the 163CompileLayer, which will be responsible for taking LLVM IR, compiling it, and 164passing the resulting in-memory object files down to the object linking layer 165below. Our ObjectLayer is constructed with a reference to the ExecutionSession 166and the getMemoryMgr utility function, which it uses to generate a new memory 167manager for each object file as it is added. Next up is our CompileLayer, which 168is initialized with a reference to the ExecutionSession, a reference to the 169ObjectLayer (where it will send the objects produced by the compiler), and an IR 170compiler instance. In this case we are using the ConcurrentIRCompiler class 171which is constructed with a JITTargetMachineBuilder and can be called to compile 172IR concurrently from several threads (though in this chapter we will only use 173one). 174 175Following the ExecutionSession and layers we have three supporting member 176variables. The DataLayout, ``DL``; and MangleAndInterner, ``Mangle`` members are 177used to support portable lookups based on IR symbol names (more on that when we 178get to our ``lookup`` function below), and the ThreadSafeContext member, 179``Ctx``, manages an LLVMContext that can be used while building IR Modules for 180the JIT. 181 182After that, we have two static utility functions. The ``getJTMB()`` function 183returns a JITTargetMachineBuilder, which is a factory for building LLVM 184TargetMachine instances that are used by the compiler. In this first tutorial we 185will only need one (implicitly created) TargetMachine, but in future tutorials 186that enable concurrent compilation we will need one per thread. This is why we 187use a target machine builder, rather than a single TargetMachine. (note: Older 188LLVM JIT APIs that did not support concurrent compilation were constructed with 189a single TargetMachines). The ``getMemoryMgr()`` function constructs instances 190of RuntimeDyld::MemoryManager, and is used by the linking layer to generate a 191new memory manager for each object file. 192 193.. code-block:: c++ 194 195 public: 196 197 KaleidoscopeJIT() { 198 ES.getMainJITDylib().setGenerator( 199 cantFail(DynamicLibrarySearchGenerator::GetForCurrentProcess(DL))); 200 } 201 202 const DataLayout &getDataLayout() const { return DL; } 203 204 LLVMContext &getContext() { return *Ctx.getContext(); } 205 206Next up we have our class constructor. Our members have already been 207initialized, so the one thing that remains to do is to tweak the configuration 208of the *JITDylib* that we will store our code in. We want to modify this dylib 209to contain not only the symbols that we add to it, but also the symbols from 210our REPL process as well. We do this by attaching a 211``DynamicLibrarySearchGenerator`` instance using the 212``DynamicLibrarySearchGenerator::GetForCurrentProcess`` method. 213 214Following the constructor we have the ``getDataLayout()`` and ``getContext()`` 215methods. These are used to make data structures created and managed by the JIT 216(especially the LLVMContext) available to the REPL code that will build our 217IR modules. 218 219.. code-block:: c++ 220 221 void addModule(std::unique_ptr<Module> M) { 222 cantFail(CompileLayer.add(ES.getMainJITDylib(), 223 ThreadSafeModule(std::move(M), Ctx))); 224 } 225 226 Expected<JITEvaluatedSymbol> lookup(StringRef Name) { 227 return ES.lookup({&ES.getMainJITDylib()}, Mangle(Name.str())); 228 } 229 230Now we come to the first of our JIT API methods: addModule. This method is 231responsible for adding IR to the JIT and making it available for execution. In 232this initial implementation of our JIT we will make our modules "available for 233execution" by adding them to the CompileLayer, which will it turn store the 234Module in the main JITDylib. This process will create new symbol table entries 235in the JITDylib for each definition in the module, and will defer compilation of 236the module until any of its definitions is looked up. Note that this is not lazy 237compilation: just referencing a definition, even if it is never used, will be 238enough to trigger compilation. In later chapters we will teach our JIT to defer 239compilation of functions until they're actually called. To add our Module we 240must first wrap it in a ThreadSafeModule instance, which manages the lifetime of 241the Module's LLVMContext (our Ctx member) in a thread-friendly way. In our 242example, all modules will share the Ctx member, which will exist for the 243duration of the JIT. Once we switch to concurrent compilation in later chapters 244we will use a new context per module. 245 246Our last method is ``lookup``, which allows us to look up addresses for 247function and variable definitions added to the JIT based on their symbol names. 248As noted above, lookup will implicitly trigger compilation for any symbol 249that has not already been compiled. Our lookup method calls through to 250`ExecutionSession::lookup`, passing in a list of dylibs to search (in our case 251just the main dylib), and the symbol name to search for, with a twist: We have 252to *mangle* the name of the symbol we're searching for first. The ORC JIT 253components use mangled symbols internally the same way a static compiler and 254linker would, rather than using plain IR symbol names. This allows JIT'd code 255to interoperate easily with precompiled code in the application or shared 256libraries. The kind of mangling will depend on the DataLayout, which in turn 257depends on the target platform. To allow us to remain portable and search based 258on the un-mangled name, we just re-produce this mangling ourselves using our 259``Mangle`` member function object. 260 261This brings us to the end of Chapter 1 of Building a JIT. You now have a basic 262but fully functioning JIT stack that you can use to take LLVM IR and make it 263executable within the context of your JIT process. In the next chapter we'll 264look at how to extend this JIT to produce better quality code, and in the 265process take a deeper look at the ORC layer concept. 266 267`Next: Extending the KaleidoscopeJIT <BuildingAJIT2.html>`_ 268 269Full Code Listing 270================= 271 272Here is the complete code listing for our running example. To build this 273example, use: 274 275.. code-block:: bash 276 277 # Compile 278 clang++ -g toy.cpp `llvm-config --cxxflags --ldflags --system-libs --libs core orcjit native` -O3 -o toy 279 # Run 280 ./toy 281 282Here is the code: 283 284.. literalinclude:: ../../examples/Kaleidoscope/BuildingAJIT/Chapter1/KaleidoscopeJIT.h 285 :language: c++ 286 287.. [1] Actually we use a cut-down version of KaleidoscopeJIT that makes a 288 simplifying assumption: symbols cannot be re-defined. This will make it 289 impossible to re-define symbols in the REPL, but will make our symbol 290 lookup logic simpler. Re-introducing support for symbol redefinition is 291 left as an exercise for the reader. (The KaleidoscopeJIT.h used in the 292 original tutorials will be a helpful reference). 293 294.. [2] +-----------------------------+-----------------------------------------------+ 295 | File | Reason for inclusion | 296 +=============================+===============================================+ 297 | JITSymbol.h | Defines the lookup result type | 298 | | JITEvaluatedSymbol | 299 +-----------------------------+-----------------------------------------------+ 300 | CompileUtils.h | Provides the SimpleCompiler class. | 301 +-----------------------------+-----------------------------------------------+ 302 | Core.h | Core utilities such as ExecutionSession and | 303 | | JITDylib. | 304 +-----------------------------+-----------------------------------------------+ 305 | ExecutionUtils.h | Provides the DynamicLibrarySearchGenerator | 306 | | class. | 307 +-----------------------------+-----------------------------------------------+ 308 | IRCompileLayer.h | Provides the IRCompileLayer class. | 309 +-----------------------------+-----------------------------------------------+ 310 | JITTargetMachineBuilder.h | Provides the JITTargetMachineBuilder class. | 311 +-----------------------------+-----------------------------------------------+ 312 | RTDyldObjectLinkingLayer.h | Provides the RTDyldObjectLinkingLayer class. | 313 +-----------------------------+-----------------------------------------------+ 314 | SectionMemoryManager.h | Provides the SectionMemoryManager class. | 315 +-----------------------------+-----------------------------------------------+ 316 | DataLayout.h | Provides the DataLayout class. | 317 +-----------------------------+-----------------------------------------------+ 318 | LLVMContext.h | Provides the LLVMContext class. | 319 +-----------------------------+-----------------------------------------------+ 320