diff options
Diffstat (limited to 'gnu/llvm/docs/tutorial/BuildingAJIT2.rst')
| -rw-r--r-- | gnu/llvm/docs/tutorial/BuildingAJIT2.rst | 277 |
1 files changed, 0 insertions, 277 deletions
diff --git a/gnu/llvm/docs/tutorial/BuildingAJIT2.rst b/gnu/llvm/docs/tutorial/BuildingAJIT2.rst deleted file mode 100644 index 7d25ccaba0a..00000000000 --- a/gnu/llvm/docs/tutorial/BuildingAJIT2.rst +++ /dev/null @@ -1,277 +0,0 @@ -===================================================================== -Building a JIT: Adding Optimizations -- An introduction to ORC Layers -===================================================================== - -.. contents:: - :local: - -**This tutorial is under active development. It is incomplete and details may -change frequently.** Nonetheless we invite you to try it out as it stands, and -we welcome any feedback. - -Chapter 2 Introduction -====================== - -**Warning: This tutorial is currently being updated to account for ORC API -changes. Only Chapters 1 and 2 are up-to-date.** - -**Example code from Chapters 3 to 5 will compile and run, but has not been -updated** - -Welcome to Chapter 2 of the "Building an ORC-based JIT in LLVM" tutorial. In -`Chapter 1 <BuildingAJIT1.html>`_ of this series we examined a basic JIT -class, KaleidoscopeJIT, that could take LLVM IR modules as input and produce -executable code in memory. KaleidoscopeJIT was able to do this with relatively -little code by composing two off-the-shelf *ORC layers*: IRCompileLayer and -ObjectLinkingLayer, to do much of the heavy lifting. - -In this layer we'll learn more about the ORC layer concept by using a new layer, -IRTransformLayer, to add IR optimization support to KaleidoscopeJIT. - -Optimizing Modules using the IRTransformLayer -============================================= - -In `Chapter 4 <LangImpl04.html>`_ of the "Implementing a language with LLVM" -tutorial series the llvm *FunctionPassManager* is introduced as a means for -optimizing LLVM IR. Interested readers may read that chapter for details, but -in short: to optimize a Module we create an llvm::FunctionPassManager -instance, configure it with a set of optimizations, then run the PassManager on -a Module to mutate it into a (hopefully) more optimized but semantically -equivalent form. In the original tutorial series the FunctionPassManager was -created outside the KaleidoscopeJIT and modules were optimized before being -added to it. In this Chapter we will make optimization a phase of our JIT -instead. For now this will provide us a motivation to learn more about ORC -layers, but in the long term making optimization part of our JIT will yield an -important benefit: When we begin lazily compiling code (i.e. deferring -compilation of each function until the first time it's run) having -optimization managed by our JIT will allow us to optimize lazily too, rather -than having to do all our optimization up-front. - -To add optimization support to our JIT we will take the KaleidoscopeJIT from -Chapter 1 and compose an ORC *IRTransformLayer* on top. We will look at how the -IRTransformLayer works in more detail below, but the interface is simple: the -constructor for this layer takes a reference to the execution session and the -layer below (as all layers do) plus an *IR optimization function* that it will -apply to each Module that is added via addModule: - -.. code-block:: c++ - - class KaleidoscopeJIT { - private: - ExecutionSession ES; - RTDyldObjectLinkingLayer ObjectLayer; - IRCompileLayer CompileLayer; - IRTransformLayer TransformLayer; - - DataLayout DL; - MangleAndInterner Mangle; - ThreadSafeContext Ctx; - - public: - - KaleidoscopeJIT(JITTargetMachineBuilder JTMB, DataLayout DL) - : ObjectLayer(ES, - []() { return llvm::make_unique<SectionMemoryManager>(); }), - CompileLayer(ES, ObjectLayer, ConcurrentIRCompiler(std::move(JTMB))), - TransformLayer(ES, CompileLayer, optimizeModule), - DL(std::move(DL)), Mangle(ES, this->DL), - Ctx(llvm::make_unique<LLVMContext>()) { - ES.getMainJITDylib().setGenerator( - cantFail(DynamicLibrarySearchGenerator::GetForCurrentProcess(DL))); - } - -Our extended KaleidoscopeJIT class starts out the same as it did in Chapter 1, -but after the CompileLayer we introduce a new member, TransformLayer, which sits -on top of our CompileLayer. We initialize our OptimizeLayer with a reference to -the ExecutionSession and output layer (standard practice for layers), along with -a *transform function*. For our transform function we supply our classes -optimizeModule static method. - -.. code-block:: c++ - - // ... - return cantFail(OptimizeLayer.addModule(std::move(M), - std::move(Resolver))); - // ... - -Next we need to update our addModule method to replace the call to -``CompileLayer::add`` with a call to ``OptimizeLayer::add`` instead. - -.. code-block:: c++ - - static Expected<ThreadSafeModule> - optimizeModule(ThreadSafeModule M, const MaterializationResponsibility &R) { - // Create a function pass manager. - auto FPM = llvm::make_unique<legacy::FunctionPassManager>(M.get()); - - // Add some optimizations. - FPM->add(createInstructionCombiningPass()); - FPM->add(createReassociatePass()); - FPM->add(createGVNPass()); - FPM->add(createCFGSimplificationPass()); - FPM->doInitialization(); - - // Run the optimizations over all functions in the module being added to - // the JIT. - for (auto &F : *M) - FPM->run(F); - - return M; - } - -At the bottom of our JIT we add a private method to do the actual optimization: -*optimizeModule*. This function takes the module to be transformed as input (as -a ThreadSafeModule) along with a reference to a reference to a new class: -``MaterializationResponsibility``. The MaterializationResponsibility argument -can be used to query JIT state for the module being transformed, such as the set -of definitions in the module that JIT'd code is actively trying to call/access. -For now we will ignore this argument and use a standard optimization -pipeline. To do this we set up a FunctionPassManager, add some passes to it, run -it over every function in the module, and then return the mutated module. The -specific optimizations are the same ones used in `Chapter 4 <LangImpl04.html>`_ -of the "Implementing a language with LLVM" tutorial series. Readers may visit -that chapter for a more in-depth discussion of these, and of IR optimization in -general. - -And that's it in terms of changes to KaleidoscopeJIT: When a module is added via -addModule the OptimizeLayer will call our optimizeModule function before passing -the transformed module on to the CompileLayer below. Of course, we could have -called optimizeModule directly in our addModule function and not gone to the -bother of using the IRTransformLayer, but doing so gives us another opportunity -to see how layers compose. It also provides a neat entry point to the *layer* -concept itself, because IRTransformLayer is one of the simplest layers that -can be implemented. - -.. code-block:: c++ - - // From IRTransformLayer.h: - class IRTransformLayer : public IRLayer { - public: - using TransformFunction = std::function<Expected<ThreadSafeModule>( - ThreadSafeModule, const MaterializationResponsibility &R)>; - - IRTransformLayer(ExecutionSession &ES, IRLayer &BaseLayer, - TransformFunction Transform = identityTransform); - - void setTransform(TransformFunction Transform) { - this->Transform = std::move(Transform); - } - - static ThreadSafeModule - identityTransform(ThreadSafeModule TSM, - const MaterializationResponsibility &R) { - return TSM; - } - - void emit(MaterializationResponsibility R, ThreadSafeModule TSM) override; - - private: - IRLayer &BaseLayer; - TransformFunction Transform; - }; - - // From IRTransfomrLayer.cpp: - - IRTransformLayer::IRTransformLayer(ExecutionSession &ES, - IRLayer &BaseLayer, - TransformFunction Transform) - : IRLayer(ES), BaseLayer(BaseLayer), Transform(std::move(Transform)) {} - - void IRTransformLayer::emit(MaterializationResponsibility R, - ThreadSafeModule TSM) { - assert(TSM.getModule() && "Module must not be null"); - - if (auto TransformedTSM = Transform(std::move(TSM), R)) - BaseLayer.emit(std::move(R), std::move(*TransformedTSM)); - else { - R.failMaterialization(); - getExecutionSession().reportError(TransformedTSM.takeError()); - } - } - -This is the whole definition of IRTransformLayer, from -``llvm/include/llvm/ExecutionEngine/Orc/IRTransformLayer.h`` and -``llvm/lib/ExecutionEngine/Orc/IRTransformLayer.cpp``. This class is concerned -with two very simple jobs: (1) Running every IR Module that is emitted via this -layer through the transform function object, and (2) implementing the ORC -``IRLayer`` interface (which itself conforms to the general ORC Layer concept, -more on that below). Most of the class is straightforward: a typedef for the -transform function, a constructor to initialize the members, a setter for the -transform function value, and a default no-op transform. The most important -method is ``emit`` as this is half of our IRLayer interface. The emit method -applies our transform to each module that it is called on and, if the transform -succeeds, passes the transformed module to the base layer. If the transform -fails, our emit function calls -``MaterializationResponsibility::failMaterialization`` (this JIT clients who -may be waiting on other threads know that the code they were waiting for has -failed to compile) and logs the error with the execution session before bailing -out. - -The other half of the IRLayer interface we inherit unmodified from the IRLayer -class: - -.. code-block:: c++ - - Error IRLayer::add(JITDylib &JD, ThreadSafeModule TSM, VModuleKey K) { - return JD.define(llvm::make_unique<BasicIRLayerMaterializationUnit>( - *this, std::move(K), std::move(TSM))); - } - -This code, from ``llvm/lib/ExecutionEngine/Orc/Layer.cpp``, adds a -ThreadSafeModule to a given JITDylib by wrapping it up in a -``MaterializationUnit`` (in this case a ``BasicIRLayerMaterializationUnit``). -Most layers that derived from IRLayer can rely on this default implementation -of the ``add`` method. - -These two operations, ``add`` and ``emit``, together constitute the layer -concept: A layer is a way to wrap a portion of a compiler pipeline (in this case -the "opt" phase of an LLVM compiler) whose API is is opaque to ORC in an -interface that allows ORC to invoke it when needed. The add method takes an -module in some input program representation (in this case an LLVM IR module) and -stores it in the target JITDylib, arranging for it to be passed back to the -Layer's emit method when any symbol defined by that module is requested. Layers -can compose neatly by calling the 'emit' method of a base layer to complete -their work. For example, in this tutorial our IRTransformLayer calls through to -our IRCompileLayer to compile the transformed IR, and our IRCompileLayer in turn -calls our ObjectLayer to link the object file produced by our compiler. - - -So far we have learned how to optimize and compile our LLVM IR, but we have not -focused on when compilation happens. Our current REPL is eager: Each function -definition is optimized and compiled as soon as it is referenced by any other -code, regardless of whether it is ever called at runtime. In the next chapter we -will introduce fully lazy compilation, in which functions are not compiled until -they are first called at run-time. At this point the trade-offs get much more -interesting: the lazier we are, the quicker we can start executing the first -function, but the more often we will have to pause to compile newly encountered -functions. If we only code-gen lazily, but optimize eagerly, we will have a -longer startup time (as everything is optimized) but relatively short pauses as -each function just passes through code-gen. If we both optimize and code-gen -lazily we can start executing the first function more quickly, but we will have -longer pauses as each function has to be both optimized and code-gen'd when it -is first executed. Things become even more interesting if we consider -interproceedural optimizations like inlining, which must be performed eagerly. -These are complex trade-offs, and there is no one-size-fits all solution to -them, but by providing composable layers we leave the decisions to the person -implementing the JIT, and make it easy for them to experiment with different -configurations. - -`Next: Adding Per-function Lazy Compilation <BuildingAJIT3.html>`_ - -Full Code Listing -================= - -Here is the complete code listing for our running example with an -IRTransformLayer added to enable optimization. To build this example, use: - -.. code-block:: bash - - # Compile - clang++ -g toy.cpp `llvm-config --cxxflags --ldflags --system-libs --libs core orcjit native` -O3 -o toy - # Run - ./toy - -Here is the code: - -.. literalinclude:: ../../examples/Kaleidoscope/BuildingAJIT/Chapter2/KaleidoscopeJIT.h - :language: c++ |
