[llvm] r344720 - [BuildingAJIT] Update the Ch1 KaleidoscopeJIT class to expose errors to clients.

Lang Hames via llvm-commits llvm-commits at lists.llvm.org
Wed Oct 17 17:51:38 PDT 2018


Author: lhames
Date: Wed Oct 17 17:51:38 2018
New Revision: 344720

URL: http://llvm.org/viewvc/llvm-project?rev=344720&view=rev
Log:
[BuildingAJIT] Update the Ch1 KaleidoscopeJIT class to expose errors to clients.

Returning the error to clients provides an opportunity to introduce readers to
the Expected and Error APIs and makes the tutorial more useful as a starting
point for a real JIT class, while only slightly complicating the code.

Modified:
    llvm/trunk/docs/tutorial/BuildingAJIT1.rst
    llvm/trunk/examples/Kaleidoscope/BuildingAJIT/Chapter1/KaleidoscopeJIT.h
    llvm/trunk/examples/Kaleidoscope/BuildingAJIT/Chapter1/toy.cpp

Modified: llvm/trunk/docs/tutorial/BuildingAJIT1.rst
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/docs/tutorial/BuildingAJIT1.rst?rev=344720&r1=344719&r2=344720&view=diff
==============================================================================
--- llvm/trunk/docs/tutorial/BuildingAJIT1.rst (original)
+++ llvm/trunk/docs/tutorial/BuildingAJIT1.rst Wed Oct 17 17:51:38 2018
@@ -9,9 +9,9 @@ Chapter 1 Introduction
 ======================
 
 **Warning: This tutorial is currently being updated to account for ORC API
-changes. Only Chapter 1 is up-to-date.**
+changes. Only Chapters 1 and 2 are up-to-date.**
 
-**Example code from Chapters 2 to 4 will compile and run, but has not been
+**Example code from Chapters 3 to 5 will compile and run, but has not been
 updated**
 
 Welcome to Chapter 1 of the "Building an ORC-based JIT in LLVM" tutorial. This
@@ -65,9 +65,9 @@ rather than compiling whole programs to
 compiler does. To support that aim our initial, bare-bones JIT API will have
 just two functions:
 
-1. void addModule(std::unique_ptr<Module> M) -- Make the given IR module
+1. ``Error addModule(std::unique_ptr<Module> M)``: Make the given IR module
    available for execution.
-2. Expected<JITSymbol> lookup() -- Search for pointers to
+2. ``Expected<JITEvaluatedSymbol> lookup()``: Search for pointers to
    symbols (functions or variables) that have been added to the JIT.
 
 A basic use-case for this API, executing the 'main' function from a module,
@@ -127,94 +127,95 @@ usual include guards and #includes [2]_,
 
   class KaleidoscopeJIT {
   private:
-
     ExecutionSession ES;
-    RTDyldObjectLinkingLayer ObjectLayer{ES, getMemoryMgr};
-    IRCompileLayer CompileLayer{ES, ObjectLayer,
-                                ConcurrentIRCompiler(getJTMB())};
-    DataLayout DL{cantFail(getJTMB().getDefaultDataLayoutForTarget())};
-    MangleAndInterner Mangle{ES, DL};
-    ThreadSafeContext Ctx{llvm::make_unique<LLVMContext>()};
+    RTDyldObjectLinkingLayer ObjectLayer;
+    IRCompileLayer CompileLayer;
 
-    static JITTargetMachineBuilder getJTMB() {
-      return cantFail(JITTargetMachineBuilder::detectHost());
-    }
+    DataLayout DL;
+    MangleAndInterner Mangle;
+    ThreadSafeContext Ctx;
 
-    static std::unique_ptr<SectionMemoryManager> getMemoryMgr(VModuleKey) {
-      return llvm::make_unique<SectionMemoryManager>();
+  public:
+    KaleidoscopeJIT(JITTargetMachineBuilder JTMB, DataLayout DL)
+        : ObjectLayer(ES,
+                      []() { return llvm::make_unique<SectionMemoryManager>(); }),
+          CompileLayer(ES, ObjectLayer, ConcurrentIRCompiler(std::move(JTMB))),
+          DL(std::move(DL)), Mangle(ES, this->DL),
+          Ctx(llvm::make_unique<LLVMContext>()) {
+      ES.getMainJITDylib().setGenerator(
+          cantFail(DynamicLibrarySearchGenerator::GetForCurrentProcess(DL)));
     }
 
-We begin with the ExecutionSession member, ``ES``, which provides context for
-our running JIT'd code. It holds the string pool for symbol names, the global
-mutex that guards the critical sections of JIT operations, error logging
-facilities, and other utilities. For basic use cases such as this, a default
-constructed ExecutionSession is all we will need. We will investigate more
-advanced uses of ExecutionSession in later chapters. Following our
-ExecutionSession we have two ORC *layers*: an RTDyldObjectLinkingLayer and an
-IRCompileLayer. We will be talking more about layers in the next chapter, but
-for now you can think of them as analogous to LLVM Passes: they wrap up useful
-JIT utilities behind an easy to compose interface. The first layer, ObjectLayer,
-is the foundation of our JIT: it takes in-memory object files produced by a
-compiler and links them on the fly to make them executable. This
-JIT-on-top-of-a-linker design was introduced in MCJIT, however the linker was
-hidden inside the MCJIT class. In ORC we expose the linker so that clients can
-access and configure it directly if they need to. In this tutorial our
-ObjectLayer will just be used to support the next layer in our stack: the
-CompileLayer, which will be responsible for taking LLVM IR, compiling it, and
-passing the resulting in-memory object files down to the object linking layer
-below. Our ObjectLayer is constructed with a reference to the ExecutionSession
-and the getMemoryMgr utility function, which it uses to generate a new memory
-manager for each object file as it is added. Next up is our CompileLayer, which
-is initialized with a reference to the ExecutionSession, a reference to the
-ObjectLayer (where it will send the objects produced by the compiler), and an IR
-compiler instance. In this case we are using the ConcurrentIRCompiler class
-which is constructed with a JITTargetMachineBuilder and can be called to compile
-IR concurrently from several threads (though in this chapter we will only use
-one).
-
-Following the ExecutionSession and layers we have three supporting member
-variables. The DataLayout, ``DL``; and MangleAndInterner, ``Mangle`` members are
-used to support portable lookups based on IR symbol names (more on that when we
-get to our ``lookup`` function below), and the ThreadSafeContext member,
-``Ctx``, manages an LLVMContext that can be used while building IR Modules for
-the JIT.
-
-After that, we have two static utility functions. The ``getJTMB()`` function
-returns a JITTargetMachineBuilder, which is a factory for building LLVM
-TargetMachine instances that are used by the compiler. In this first tutorial we
-will only need one (implicitly created) TargetMachine, but in future tutorials
-that enable concurrent compilation we will need one per thread. This is why we
-use a target machine builder, rather than a single TargetMachine. (note: Older
-LLVM JIT APIs that did not support concurrent compilation were constructed with
-a single TargetMachines). The ``getMemoryMgr()`` function constructs instances
-of RuntimeDyld::MemoryManager, and is used by the linking layer to generate a
-new memory manager for each object file.
+Our class begins with six member variables: An ExecutionSession member, ``ES``,
+which provides context for our running JIT'd code (including the string pool,
+global mutex, and error reporting facilities); An RTDyldObjectLinkingLayer,
+``ObjectLayer``, that can be used to add object files to our JIT (though we will
+not use it directly); An IRCompileLayer, ``CompileLayer``, that can be used to
+add LLVM Modules to our JIT (and which builds on the ObjectLayer), A DataLayout
+and MangleAndInterner, ``DL`` and ``Mangle``, that will be used for symbol mangling
+(more on that later); and finally an LLVMContext that clients will use when
+building IR files for the JIT.
+
+Next up we have our class constructor, which takes a `JITTargetMachineBuilder``
+that will be used by our IRCompiler, and a ``DataLayout`` that we will use to
+initialize our DL member. The constructor begins by initializing our
+ObjectLayer.  The ObjectLayer requires a reference to the ExecutionSession, and
+a function object that will build a JIT memory manager for each module that is
+added (a JIT memory manager manages memory allocations, memory permissions, and
+registration of exception handlers for JIT'd code). For this we use a lambda
+that returns a SectionMemoryManager, an off-the-shelf utility that provides all
+the basic memory management functionality required for this chapter. Next we
+initialize our CompileLayer. The CompileLayer needs three things: (1) A
+reference to the ExecutionSession, (2) A reference to our object layer, and (3)
+a compiler instance to use to perform the actual compilation from IR to object
+files. We use the off-the-shelf ConcurrentIRCompiler utility as our compiler,
+which we construct using this constructor's JITTargetMachineBuilder argument.
+The ConcurrentIRCompiler utility will use the JITTargetMachineBuilder to build
+llvm TargetMachines (which are not thread safe) as needed for compiles. After
+this, we initialize our supporting members: ``DL``, ``Mangler`` and ``Ctx`` with
+the input DataLayout, the ExecutionSession and DL member, and a new default
+constucted LLVMContext respectively. Now that our members have been initialized,
+so the one thing that remains to do is to tweak the configuration of the
+*JITDylib* that we will store our code in. We want to modify this dylib to
+contain not only the symbols that we add to it, but also the symbols from our
+REPL process as well. We do this by attaching a
+``DynamicLibrarySearchGenerator`` instance using the
+``DynamicLibrarySearchGenerator::GetForCurrentProcess`` method.
+
 
 .. code-block:: c++
 
-  public:
+  static Expected<std::unique_ptr<KaleidoscopeJIT>> Create() {
+    auto JTMB = JITTargetMachineBuilder::detectHost();
 
-    KaleidoscopeJIT() {
-      ES.getMainJITDylib().setGenerator(
-        cantFail(DynamicLibrarySearchGenerator::GetForCurrentProcess(DL)));
-    }
+    if (!JTMB)
+      return JTMB.takeError();
 
-    const DataLayout &getDataLayout() const { return DL; }
+    auto DL = JTMB->getDefaultDataLayoutForTarget();
+    if (!DL)
+      return DL.takeError();
 
-    LLVMContext &getContext() { return *Ctx.getContext(); }
+    return llvm::make_unique<KaleidoscopeJIT>(std::move(*JTMB), std::move(*DL));
+  }
 
-Next up we have our class constructor. Our members have already been
-initialized, so the one thing that remains to do is to tweak the configuration
-of the *JITDylib* that we will store our code in. We want to modify this dylib
-to contain not only the symbols that we add to it, but also the symbols from
-our REPL process as well. We do this by attaching a
-``DynamicLibrarySearchGenerator`` instance using the
-``DynamicLibrarySearchGenerator::GetForCurrentProcess`` method.
+  const DataLayout &getDataLayout() const { return DL; }
+
+  LLVMContext &getContext() { return *Ctx.getContext(); }
 
-Following the constructor we have the ``getDataLayout()`` and ``getContext()``
-methods. These are used to make data structures created and managed by the JIT
-(especially the LLVMContext) available to the REPL code that will build our
-IR modules.
+Next we have a named constructor, ``Create``, which will build a KaleidoscopeJIT
+instance that is configured to generate code for our host process. It does this
+by first generating a JITTargetMachineBuilder instance using that clases's
+detectHost method and then using that instance to generate a datalayout for
+the target process. Each of these operations can fail, so each returns its
+result wrapped in an Expected value [3]_ that we must check for error before
+continuing. If both operations succeed we can unwrap their results (using the
+dereference operator) and pass them into KaleidoscopeJIT's constructor on the
+last line of the function.
+
+Following the named constructor we have the ``getDataLayout()`` and
+``getContext()`` methods. These are used to make data structures created and
+managed by the JIT (especially the LLVMContext) available to the REPL code that
+will build our IR modules.
 
 .. code-block:: c++
 
@@ -317,3 +318,6 @@ Here is the code:
        +-----------------------------+-----------------------------------------------+
        |        LLVMContext.h        | Provides the LLVMContext class.               |
        +-----------------------------+-----------------------------------------------+
+
+.. [3] See the ErrorHandling section in the LLVM Programmer's Manual
+       (http://llvm.org/docs/ProgrammersManual.html#error-handling)
\ No newline at end of file

Modified: llvm/trunk/examples/Kaleidoscope/BuildingAJIT/Chapter1/KaleidoscopeJIT.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/examples/Kaleidoscope/BuildingAJIT/Chapter1/KaleidoscopeJIT.h?rev=344720&r1=344719&r2=344720&view=diff
==============================================================================
--- llvm/trunk/examples/Kaleidoscope/BuildingAJIT/Chapter1/KaleidoscopeJIT.h (original)
+++ llvm/trunk/examples/Kaleidoscope/BuildingAJIT/Chapter1/KaleidoscopeJIT.h Wed Oct 17 17:51:38 2018
@@ -32,37 +32,45 @@ namespace orc {
 
 class KaleidoscopeJIT {
 private:
-
   ExecutionSession ES;
-  RTDyldObjectLinkingLayer ObjectLayer{ES, getMemoryMgr};
-  IRCompileLayer CompileLayer{ES, ObjectLayer,
-                              ConcurrentIRCompiler(getJTMB())};
-  DataLayout DL{cantFail(getJTMB().getDefaultDataLayoutForTarget())};
-  MangleAndInterner Mangle{ES, DL};
-  ThreadSafeContext Ctx{llvm::make_unique<LLVMContext>()};
+  RTDyldObjectLinkingLayer ObjectLayer;
+  IRCompileLayer CompileLayer;
 
-  static JITTargetMachineBuilder getJTMB() {
-    return cantFail(JITTargetMachineBuilder::detectHost());
-  }
+  DataLayout DL;
+  MangleAndInterner Mangle;
+  ThreadSafeContext Ctx;
 
-  static std::unique_ptr<SectionMemoryManager> getMemoryMgr() {
-    return llvm::make_unique<SectionMemoryManager>();
+public:
+  KaleidoscopeJIT(JITTargetMachineBuilder JTMB, DataLayout DL)
+      : ObjectLayer(ES,
+                    []() { return llvm::make_unique<SectionMemoryManager>(); }),
+        CompileLayer(ES, ObjectLayer, ConcurrentIRCompiler(std::move(JTMB))),
+        DL(std::move(DL)), Mangle(ES, this->DL),
+        Ctx(llvm::make_unique<LLVMContext>()) {
+    ES.getMainJITDylib().setGenerator(
+        cantFail(DynamicLibrarySearchGenerator::GetForCurrentProcess(DL)));
   }
 
-public:
+  static Expected<std::unique_ptr<KaleidoscopeJIT>> Create() {
+    auto JTMB = JITTargetMachineBuilder::detectHost();
 
-  KaleidoscopeJIT() {
-    ES.getMainJITDylib().setGenerator(
-      cantFail(DynamicLibrarySearchGenerator::GetForCurrentProcess(DL)));
+    if (!JTMB)
+      return JTMB.takeError();
+
+    auto DL = JTMB->getDefaultDataLayoutForTarget();
+    if (!DL)
+      return DL.takeError();
+
+    return llvm::make_unique<KaleidoscopeJIT>(std::move(*JTMB), std::move(*DL));
   }
 
   const DataLayout &getDataLayout() const { return DL; }
 
   LLVMContext &getContext() { return *Ctx.getContext(); }
 
-  void addModule(std::unique_ptr<Module> M) {
-    cantFail(CompileLayer.add(ES.getMainJITDylib(),
-                              ThreadSafeModule(std::move(M), Ctx)));
+  Error addModule(std::unique_ptr<Module> M) {
+    return CompileLayer.add(ES.getMainJITDylib(),
+                            ThreadSafeModule(std::move(M), Ctx));
   }
 
   Expected<JITEvaluatedSymbol> lookup(StringRef Name) {

Modified: llvm/trunk/examples/Kaleidoscope/BuildingAJIT/Chapter1/toy.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/examples/Kaleidoscope/BuildingAJIT/Chapter1/toy.cpp?rev=344720&r1=344719&r2=344720&view=diff
==============================================================================
--- llvm/trunk/examples/Kaleidoscope/BuildingAJIT/Chapter1/toy.cpp (original)
+++ llvm/trunk/examples/Kaleidoscope/BuildingAJIT/Chapter1/toy.cpp Wed Oct 17 17:51:38 2018
@@ -703,6 +703,7 @@ static std::unique_ptr<IRBuilder<>> Buil
 static std::unique_ptr<Module> TheModule;
 static std::map<std::string, AllocaInst *> NamedValues;
 static std::map<std::string, std::unique_ptr<PrototypeAST>> FunctionProtos;
+static ExitOnError ExitOnErr;
 
 Value *LogErrorV(const char *Str) {
   LogError(Str);
@@ -1116,7 +1117,7 @@ static void HandleDefinition() {
       fprintf(stderr, "Read function definition:");
       FnIR->print(errs());
       fprintf(stderr, "\n");
-      TheJIT->addModule(std::move(TheModule));
+      ExitOnErr(TheJIT->addModule(std::move(TheModule)));
       InitializeModule();
     }
   } else {
@@ -1151,23 +1152,16 @@ static void HandleTopLevelExpression() {
     if (FnAST->codegen()) {
       // JIT the module containing the anonymous expression, keeping a handle so
       // we can free it later.
-      TheJIT->addModule(std::move(TheModule));
+      ExitOnErr(TheJIT->addModule(std::move(TheModule)));
       InitializeModule();
 
       // Get the anonymous expression's JITSymbol.
-      auto Sym =  TheJIT->lookup(("__anon_expr" + Twine(ExprCount)).str());
+      auto Sym =
+        ExitOnErr(TheJIT->lookup(("__anon_expr" + Twine(ExprCount)).str()));
 
-      if (Sym) {
-        // If the lookup succeeded, cast the symbol's address to a function
-        // pointer then call it.
-        auto *FP = (double (*)())(intptr_t)Sym->getAddress();
-        assert(FP && "Failed to codegen function");
-        fprintf(stderr, "Evaluated to %f\n", FP());
-      } else {
-        // Otherwise log the reason the symbol lookup failed.
-        logAllUnhandledErrors(Sym.takeError(), errs(),
-                              "Could not evaluate: ");
-      }
+      auto *FP = (double (*)())(intptr_t)Sym.getAddress();
+      assert(FP && "Failed to codegen function");
+      fprintf(stderr, "Evaluated to %f\n", FP());
     }
   } else {
     // Skip token for error recovery.
@@ -1235,7 +1229,7 @@ int main() {
   fprintf(stderr, "ready> ");
   getNextToken();
 
-  TheJIT = llvm::make_unique<KaleidoscopeJIT>();
+  TheJIT = ExitOnErr(KaleidoscopeJIT::Create());
   TheContext = &TheJIT->getContext();
 
   InitializeModule();




More information about the llvm-commits mailing list