[llvm] r270778 - [Kaleidoscope][BuildingAJIT] Add a description of the KaleidoscopeJIT addModule
Lang Hames via llvm-commits
llvm-commits at lists.llvm.org
Wed May 25 15:27:27 PDT 2016
Author: lhames
Date: Wed May 25 17:27:25 2016
New Revision: 270778
URL: http://llvm.org/viewvc/llvm-project?rev=270778&view=rev
Log:
[Kaleidoscope][BuildingAJIT] Add a description of the KaleidoscopeJIT addModule
method to Chapter1 of the BuildingAJIT tutorial.
Modified:
llvm/trunk/docs/tutorial/BuildingAJIT1.rst
llvm/trunk/examples/Kaleidoscope/BuildingAJIT/Chapter1/KaleidoscopeJIT.h
Modified: llvm/trunk/docs/tutorial/BuildingAJIT1.rst
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/docs/tutorial/BuildingAJIT1.rst?rev=270778&r1=270777&r2=270778&view=diff
==============================================================================
--- llvm/trunk/docs/tutorial/BuildingAJIT1.rst (original)
+++ llvm/trunk/docs/tutorial/BuildingAJIT1.rst Wed May 25 17:27:25 2016
@@ -186,26 +186,102 @@ available for execution.
.. code-block:: c++
- ModuleHandleT addModule(std::unique_ptr<Module> M) {
- // We need a memory manager to allocate memory and resolve symbols for this
- // new module. Create one that resolves symbols by looking back into the
- // JIT.
- auto Resolver = createLambdaResolver(
- [&](const std::string &Name) {
- if (auto Sym = CompileLayer.findSymbol(Name, false))
- return RuntimeDyld::SymbolInfo(Sym.getAddress(), Sym.getFlags());
- return RuntimeDyld::SymbolInfo(nullptr);
- },
- [](const std::string &S) { return nullptr; });
- std::vector<std::unique_ptr<Module>> Ms;
- Ms.push_back(std::move(M));
- return CompileLayer.addModuleSet(singletonSet(std::move(M)),
- make_unique<SectionMemoryManager>(),
- std::move(Resolver));
- }
+ ModuleHandle addModule(std::unique_ptr<Module> M) {
+ // Build our symbol resolver:
+ // Lambda 1: Look back into the JIT itself to find symbols that are part of
+ // the same "logical dylib".
+ // Lambda 2: Search for external symbols in the host process.
+ auto Resolver = createLambdaResolver(
+ [&](const std::string &Name) {
+ if (auto Sym = CompileLayer.findSymbol(Name, false))
+ return RuntimeDyld::SymbolInfo(Sym.getAddress(), Sym.getFlags());
+ return RuntimeDyld::SymbolInfo(nullptr);
+ },
+ [](const std::string &S) {
+ if (auto SymAddr =
+ RTDyldMemoryManager::getSymbolAddressInProcess(Name))
+ return RuntimeDyld::SymbolInfo(SymAddr, JITSymbolFlags::Exported);
+ return RuntimeDyld::SymbolInfo(nullptr);
+ });
+
+ // Build a singlton module set to hold our module.
+ std::vector<std::unique_ptr<Module>> Ms;
+ Ms.push_back(std::move(M));
+
+ // Add the set to the JIT with the resolver we created above and a newly
+ // created SectionMemoryManager.
+ return CompileLayer.addModuleSet(std::move(Ms),
+ make_unique<SectionMemoryManager>(),
+ std::move(Resolver));
+ }
-*To be done: describe addModule -- createLambdaResolver, resolvers, memory
-managers, why 'module set' rather than a single module...*
+Now we come to the first of our central JIT API methods: addModule. This method
+is responsible for adding IR to the JIT and making it available for execution.
+In this initial implementation of our JIT we will make our modules "available
+for execution" by compiling them immediately as they are added to the JIT. In
+later chapters we will teach our JIT to be lazier and instead add the Modules
+to a "pending" list to be compiled if and when they are first executed.
+
+To add our module to the IRCompileLayer we need to supply two auxiliary
+objects: a memory manager and a symbol resolver. The memory manager will be
+responsible for managing the memory allocated to JIT'd machine code, applying
+memory protection permissions, and registering JIT'd exception handling tables
+(if the JIT'd code uses exceptions). In our simple use-case we can just supply
+an off-the-shelf SectionMemoryManager instance. The memory, exception handling
+tables, etc. will be released when we remove the module from the JIT again
+(using removeModule) or, if removeModule is never called, when the JIT class
+itself is destructed.
+
+The second auxiliary class, the symbol resolver, is more interesting for us. It
+exists to tell the JIT where to look when it encounters an *external symbol* in
+the module we are adding. External symbols are any symbol not defined within the
+module itself, including calls to functions outside the JIT and calls to
+functions defined in other modules that have already been added to the JIT. It
+may seem as though modules added to the JIT should "know about one another" by
+default, but since we would still have to supply a symbol resolver for
+references to code outside the JIT it turns out to re-use this one mechanism
+for all symbol resolution. This has the added benefit that the user has full
+control over the symbol resolution process. Should we search for definitions
+within the JIT first, then fall back on external definitions? Or should we
+prefer external definitions where available and only JIT code if we don't
+already have an available implementation? By using a single symbol resolution
+scheme we are free to choose whatever makes the most sense for any given use
+case.
+
+Building a symbol resolver is made especially easy by the
+*createLambdaResolver* function. This function takes two lambdas (actually
+they don't have to be lambdas, any object with a call operator will do) and
+returns a RuntimeDyld::SymbolResolver instance. The first lambda is used as
+the implementation of the resolver's findSymbolInLogicalDylib method. This
+method searches for symbol definitions that should be thought of as being part
+of the same "logical" dynamic library as this Module. If you are familiar with
+static linking: this means that findSymbolInLogicalDylib should expose symbols
+with common linkage and hidden visibility. If all this sounds foreign you can
+ignore the details and just remember that this is the first method that the
+linker will use to try to find a symbol definition. If the
+findSymbolInLogicalDylib method returns a null result then the linker will
+call the second symbol resolver method, called findSymbol. This searches for
+symbols that should be thought of as external to (but visibile from) the module
+and its logical dylib.
+
+In this tutorial we will use the following simple breakdown: All modules added
+to the JIT will behave as if they were linked into a single, ever-growing
+logical dylib. To implement this our first lambda (the one defining
+findSymbolInLogicalDylib) will just search for JIT'd code by calling the
+CompileLayer's findSymbol method. If we don't find a symbol in the JIT itself
+we'll fall back to our second lambda, which implements findSymbol. This will
+use the RTDyldMemoyrManager::getSymbolAddressInProcess method to search for
+the symbol within the program itself. If we can't find a symbol definition
+via either of these paths the JIT will refuse to accept our moudle, returning
+a "symbol not found" error.
+
+Now that we've built our symbol resolver we're ready to add our module to the
+JIT. We do this by calling the CompileLayer's addModuleSet method [3]_. Since
+we only have a single Module and addModuleSet expects a collection, we will
+create a vector of modules and add our module as the only member. Since we
+have already typedef'd our ModuleHandle type to be the same as the
+CompileLayer's handle type, we can return the handle from addModuleSet
+directly from our addModule method.
.. code-block:: c++
@@ -279,3 +355,7 @@ Here is the code:
| DynamicLibrary.h | Provides the DynamicLibrary class, which |
| | makes symbols in the host process searchable. |
+-----------------------+-----------------------------------------------+
+
+.. [3] ORC layers accept sets of Modules, rather than individual ones, so that
+ all Modules in the set could be co-located by the memory manager, though
+ this feature is not yet implemented.
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=270778&r1=270777&r2=270778&view=diff
==============================================================================
--- llvm/trunk/examples/Kaleidoscope/BuildingAJIT/Chapter1/KaleidoscopeJIT.h (original)
+++ llvm/trunk/examples/Kaleidoscope/BuildingAJIT/Chapter1/KaleidoscopeJIT.h Wed May 25 17:27:25 2016
@@ -55,18 +55,29 @@ public:
TargetMachine &getTargetMachine() { return *TM; }
ModuleHandle addModule(std::unique_ptr<Module> M) {
- // We need a memory manager to allocate memory and resolve symbols for this
- // new module. Create one that resolves symbols by looking back into the
- // JIT.
+ // Build our symbol resolver:
+ // Lambda 1: Look back into the JIT itself to find symbols that are part of
+ // the same "logical dylib".
+ // Lambda 2: Search for external symbols in the host process.
auto Resolver = createLambdaResolver(
[&](const std::string &Name) {
if (auto Sym = CompileLayer.findSymbol(Name, false))
return RuntimeDyld::SymbolInfo(Sym.getAddress(), Sym.getFlags());
return RuntimeDyld::SymbolInfo(nullptr);
},
- [](const std::string &S) { return nullptr; });
+ [](const std::string &Name) {
+ if (auto SymAddr =
+ RTDyldMemoryManager::getSymbolAddressInProcess(Name))
+ return RuntimeDyld::SymbolInfo(SymAddr, JITSymbolFlags::Exported);
+ return RuntimeDyld::SymbolInfo(nullptr);
+ });
+
+ // Build a singlton module set to hold our module.
std::vector<std::unique_ptr<Module>> Ms;
Ms.push_back(std::move(M));
+
+ // Add the set to the JIT with the resolver we created above and a newly
+ // created SectionMemoryManager.
return CompileLayer.addModuleSet(std::move(Ms),
make_unique<SectionMemoryManager>(),
std::move(Resolver));
More information about the llvm-commits
mailing list