summaryrefslogtreecommitdiff
path: root/include/llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h')
-rw-r--r--include/llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h547
1 files changed, 547 insertions, 0 deletions
diff --git a/include/llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h b/include/llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h
new file mode 100644
index 0000000000000..719adbc562c21
--- /dev/null
+++ b/include/llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h
@@ -0,0 +1,547 @@
+//===- CompileOnDemandLayer.h - Compile each function on demand -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// JIT layer for breaking up modules and inserting callbacks to allow
+// individual functions to be compiled on demand.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_EXECUTIONENGINE_ORC_COMPILEONDEMANDLAYER_H
+#define LLVM_EXECUTIONENGINE_ORC_COMPILEONDEMANDLAYER_H
+
+//#include "CloneSubModule.h"
+#include "IndirectionUtils.h"
+#include "LambdaResolver.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ExecutionEngine/SectionMemoryManager.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include <list>
+#include <set>
+
+#include "llvm/Support/Debug.h"
+
+namespace llvm {
+namespace orc {
+
+/// @brief Compile-on-demand layer.
+///
+/// When a module is added to this layer a stub is created for each of its
+/// function definitions. The stubs and other global values are immediately
+/// added to the layer below. When a stub is called it triggers the extraction
+/// of the function body from the original module. The extracted body is then
+/// compiled and executed.
+template <typename BaseLayerT, typename CompileCallbackMgrT>
+class CompileOnDemandLayer {
+private:
+
+ // Utility class for MapValue. Only materializes declarations for global
+ // variables.
+ class GlobalDeclMaterializer : public ValueMaterializer {
+ public:
+ GlobalDeclMaterializer(Module &Dst) : Dst(Dst) {}
+ Value* materializeValueFor(Value *V) final {
+ if (auto *GV = dyn_cast<GlobalVariable>(V))
+ return cloneGlobalVariableDecl(Dst, *GV);
+ else if (auto *F = dyn_cast<Function>(V))
+ return cloneFunctionDecl(Dst, *F);
+ // Else.
+ return nullptr;
+ }
+ private:
+ Module &Dst;
+ };
+
+ typedef typename BaseLayerT::ModuleSetHandleT BaseLayerModuleSetHandleT;
+ class UncompiledPartition;
+
+ // Logical module.
+ //
+ // This struct contains the handles for the global values and stubs (which
+ // cover the external symbols of the original module), plus the handes for
+ // each of the extracted partitions. These handleds are used for lookup (only
+ // the globals/stubs module is searched) and memory management. The actual
+ // searching and resource management are handled by the LogicalDylib that owns
+ // the LogicalModule.
+ struct LogicalModule {
+ LogicalModule() {}
+
+ LogicalModule(LogicalModule &&Other)
+ : SrcM(std::move(Other.SrcM)),
+ GVsAndStubsHandle(std::move(Other.GVsAndStubsHandle)),
+ ImplHandles(std::move(Other.ImplHandles)) {}
+
+ std::unique_ptr<Module> SrcM;
+ BaseLayerModuleSetHandleT GVsAndStubsHandle;
+ std::vector<BaseLayerModuleSetHandleT> ImplHandles;
+ };
+
+ // Logical dylib.
+ //
+ // This class handles symbol resolution and resource management for a set of
+ // modules that were added together as a logical dylib.
+ //
+ // A logical dylib contains one-or-more LogicalModules plus a set of
+ // UncompiledPartitions. LogicalModules support symbol resolution and resource
+ // management for for code that has already been emitted. UncompiledPartitions
+ // represent code that has not yet been compiled.
+ class LogicalDylib {
+ private:
+ friend class UncompiledPartition;
+ typedef std::list<LogicalModule> LogicalModuleList;
+ public:
+
+ typedef unsigned UncompiledPartitionID;
+ typedef typename LogicalModuleList::iterator LMHandle;
+
+ // Construct a logical dylib.
+ LogicalDylib(CompileOnDemandLayer &CODLayer) : CODLayer(CODLayer) { }
+
+ // Delete this logical dylib, release logical module resources.
+ virtual ~LogicalDylib() {
+ releaseLogicalModuleResources();
+ }
+
+ // Get a reference to the containing layer.
+ CompileOnDemandLayer& getCODLayer() { return CODLayer; }
+
+ // Get a reference to the base layer.
+ BaseLayerT& getBaseLayer() { return CODLayer.BaseLayer; }
+
+ // Start a new context for a single logical module.
+ LMHandle createLogicalModule() {
+ LogicalModules.push_back(LogicalModule());
+ return std::prev(LogicalModules.end());
+ }
+
+ // Set the global-values-and-stubs module handle for this logical module.
+ void setGVsAndStubsHandle(LMHandle LMH, BaseLayerModuleSetHandleT H) {
+ LMH->GVsAndStubsHandle = H;
+ }
+
+ // Return the global-values-and-stubs module handle for this logical module.
+ BaseLayerModuleSetHandleT getGVsAndStubsHandle(LMHandle LMH) {
+ return LMH->GVsAndStubsHandle;
+ }
+
+ // Add a handle to a module containing lazy function bodies to the given
+ // logical module.
+ void addToLogicalModule(LMHandle LMH, BaseLayerModuleSetHandleT H) {
+ LMH->ImplHandles.push_back(H);
+ }
+
+ // Create an UncompiledPartition attached to this LogicalDylib.
+ UncompiledPartition& createUncompiledPartition(LMHandle LMH,
+ std::shared_ptr<Module> SrcM);
+
+ // Take ownership of the given UncompiledPartition from the logical dylib.
+ std::unique_ptr<UncompiledPartition>
+ takeUPOwnership(UncompiledPartitionID ID);
+
+ // Look up a symbol in this context.
+ JITSymbol findSymbolInternally(LMHandle LMH, const std::string &Name) {
+ if (auto Symbol = getBaseLayer().findSymbolIn(LMH->GVsAndStubsHandle,
+ Name, false))
+ return Symbol;
+
+ for (auto I = LogicalModules.begin(), E = LogicalModules.end(); I != E;
+ ++I)
+ if (I != LMH)
+ if (auto Symbol = getBaseLayer().findSymbolIn(I->GVsAndStubsHandle,
+ Name, false))
+ return Symbol;
+
+ return nullptr;
+ }
+
+ JITSymbol findSymbol(const std::string &Name, bool ExportedSymbolsOnly) {
+ for (auto &LM : LogicalModules)
+ if (auto Symbol = getBaseLayer().findSymbolIn(LM.GVsAndStubsHandle,
+ Name,
+ ExportedSymbolsOnly))
+ return Symbol;
+ return nullptr;
+ }
+
+ // Find an external symbol (via the user supplied SymbolResolver).
+ virtual RuntimeDyld::SymbolInfo
+ findSymbolExternally(const std::string &Name) const = 0;
+
+ private:
+
+ void releaseLogicalModuleResources() {
+ for (auto I = LogicalModules.begin(), E = LogicalModules.end(); I != E;
+ ++I) {
+ getBaseLayer().removeModuleSet(I->GVsAndStubsHandle);
+ for (auto H : I->ImplHandles)
+ getBaseLayer().removeModuleSet(H);
+ }
+ }
+
+ CompileOnDemandLayer &CODLayer;
+ LogicalModuleList LogicalModules;
+ std::vector<std::unique_ptr<UncompiledPartition>> UncompiledPartitions;
+ };
+
+ template <typename ResolverPtrT>
+ class LogicalDylibImpl : public LogicalDylib {
+ public:
+ LogicalDylibImpl(CompileOnDemandLayer &CODLayer, ResolverPtrT Resolver)
+ : LogicalDylib(CODLayer), Resolver(std::move(Resolver)) {}
+
+ RuntimeDyld::SymbolInfo
+ findSymbolExternally(const std::string &Name) const override {
+ return Resolver->findSymbol(Name);
+ }
+
+ private:
+ ResolverPtrT Resolver;
+ };
+
+ template <typename ResolverPtrT>
+ static std::unique_ptr<LogicalDylib>
+ createLogicalDylib(CompileOnDemandLayer &CODLayer,
+ ResolverPtrT Resolver) {
+ typedef LogicalDylibImpl<ResolverPtrT> Impl;
+ return llvm::make_unique<Impl>(CODLayer, std::move(Resolver));
+ }
+
+ // Uncompiled partition.
+ //
+ // Represents one as-yet uncompiled portion of a module.
+ class UncompiledPartition {
+ public:
+
+ struct PartitionEntry {
+ PartitionEntry(Function *F, TargetAddress CallbackID)
+ : F(F), CallbackID(CallbackID) {}
+ Function *F;
+ TargetAddress CallbackID;
+ };
+
+ typedef std::vector<PartitionEntry> PartitionEntryList;
+
+ // Creates an uncompiled partition with the list of functions that make up
+ // this partition.
+ UncompiledPartition(LogicalDylib &LD, typename LogicalDylib::LMHandle LMH,
+ std::shared_ptr<Module> SrcM)
+ : LD(LD), LMH(LMH), SrcM(std::move(SrcM)), ID(~0U) {}
+
+ ~UncompiledPartition() {
+ // FIXME: When we want to support threaded lazy compilation we'll need to
+ // lock the callback manager here.
+ auto &CCMgr = LD.getCODLayer().CompileCallbackMgr;
+ for (auto PEntry : PartitionEntries)
+ CCMgr.releaseCompileCallback(PEntry.CallbackID);
+ }
+
+ // Set the ID for this partition.
+ void setID(typename LogicalDylib::UncompiledPartitionID ID) {
+ this->ID = ID;
+ }
+
+ // Set the function set and callbacks for this partition.
+ void setPartitionEntries(PartitionEntryList PartitionEntries) {
+ this->PartitionEntries = std::move(PartitionEntries);
+ }
+
+ // Handle a compile callback for the function at index FnIdx.
+ TargetAddress compile(unsigned FnIdx) {
+ // Take ownership of self. This will ensure we delete the partition and
+ // free all its resources once we're done compiling.
+ std::unique_ptr<UncompiledPartition> This = LD.takeUPOwnership(ID);
+
+ // Release all other compile callbacks for this partition.
+ // We skip the callback for this function because that's the one that
+ // called us, and the callback manager will already have removed it.
+ auto &CCMgr = LD.getCODLayer().CompileCallbackMgr;
+ for (unsigned I = 0; I < PartitionEntries.size(); ++I)
+ if (I != FnIdx)
+ CCMgr.releaseCompileCallback(PartitionEntries[I].CallbackID);
+
+ // Grab the name of the function being called here.
+ Function *F = PartitionEntries[FnIdx].F;
+ std::string CalledFnName = Mangle(F->getName(), SrcM->getDataLayout());
+
+ // Extract the function and add it to the base layer.
+ auto PartitionImplH = emitPartition();
+ LD.addToLogicalModule(LMH, PartitionImplH);
+
+ // Update body pointers.
+ // FIXME: When we start supporting remote lazy jitting this will need to
+ // be replaced with a user-supplied callback for updating the
+ // remote pointers.
+ TargetAddress CalledAddr = 0;
+ for (unsigned I = 0; I < PartitionEntries.size(); ++I) {
+ auto F = PartitionEntries[I].F;
+ std::string FName(F->getName());
+ auto FnBodySym =
+ LD.getBaseLayer().findSymbolIn(PartitionImplH,
+ Mangle(FName, SrcM->getDataLayout()),
+ false);
+ auto FnPtrSym =
+ LD.getBaseLayer().findSymbolIn(LD.getGVsAndStubsHandle(LMH),
+ Mangle(FName + "$orc_addr",
+ SrcM->getDataLayout()),
+ false);
+ assert(FnBodySym && "Couldn't find function body.");
+ assert(FnPtrSym && "Couldn't find function body pointer.");
+
+ auto FnBodyAddr = FnBodySym.getAddress();
+ void *FnPtrAddr = reinterpret_cast<void*>(
+ static_cast<uintptr_t>(FnPtrSym.getAddress()));
+
+ // If this is the function we're calling record the address so we can
+ // return it from this function.
+ if (I == FnIdx)
+ CalledAddr = FnBodyAddr;
+
+ memcpy(FnPtrAddr, &FnBodyAddr, sizeof(uintptr_t));
+ }
+
+ // Finally, clear the partition structure so we don't try to
+ // double-release the callbacks in the UncompiledPartition destructor.
+ PartitionEntries.clear();
+
+ return CalledAddr;
+ }
+
+ private:
+
+ BaseLayerModuleSetHandleT emitPartition() {
+ // Create the module.
+ std::string NewName(SrcM->getName());
+ for (auto &PEntry : PartitionEntries) {
+ NewName += ".";
+ NewName += PEntry.F->getName();
+ }
+ auto PM = llvm::make_unique<Module>(NewName, SrcM->getContext());
+ PM->setDataLayout(SrcM->getDataLayout());
+ ValueToValueMapTy VMap;
+ GlobalDeclMaterializer GDM(*PM);
+
+ // Create decls in the new module.
+ for (auto &PEntry : PartitionEntries)
+ cloneFunctionDecl(*PM, *PEntry.F, &VMap);
+
+ // Move the function bodies.
+ for (auto &PEntry : PartitionEntries)
+ moveFunctionBody(*PEntry.F, VMap);
+
+ // Create memory manager and symbol resolver.
+ auto MemMgr = llvm::make_unique<SectionMemoryManager>();
+ auto Resolver = createLambdaResolver(
+ [this](const std::string &Name) {
+ if (auto Symbol = LD.findSymbolInternally(LMH, Name))
+ return RuntimeDyld::SymbolInfo(Symbol.getAddress(),
+ Symbol.getFlags());
+ return LD.findSymbolExternally(Name);
+ },
+ [this](const std::string &Name) {
+ if (auto Symbol = LD.findSymbolInternally(LMH, Name))
+ return RuntimeDyld::SymbolInfo(Symbol.getAddress(),
+ Symbol.getFlags());
+ return RuntimeDyld::SymbolInfo(nullptr);
+ });
+ std::vector<std::unique_ptr<Module>> PartMSet;
+ PartMSet.push_back(std::move(PM));
+ return LD.getBaseLayer().addModuleSet(std::move(PartMSet),
+ std::move(MemMgr),
+ std::move(Resolver));
+ }
+
+ LogicalDylib &LD;
+ typename LogicalDylib::LMHandle LMH;
+ std::shared_ptr<Module> SrcM;
+ typename LogicalDylib::UncompiledPartitionID ID;
+ PartitionEntryList PartitionEntries;
+ };
+
+ typedef std::list<std::unique_ptr<LogicalDylib>> LogicalDylibList;
+
+public:
+ /// @brief Handle to a set of loaded modules.
+ typedef typename LogicalDylibList::iterator ModuleSetHandleT;
+
+ /// @brief Construct a compile-on-demand layer instance.
+ CompileOnDemandLayer(BaseLayerT &BaseLayer, CompileCallbackMgrT &CallbackMgr)
+ : BaseLayer(BaseLayer), CompileCallbackMgr(CallbackMgr) {}
+
+ /// @brief Add a module to the compile-on-demand layer.
+ template <typename ModuleSetT, typename MemoryManagerPtrT,
+ typename SymbolResolverPtrT>
+ ModuleSetHandleT addModuleSet(ModuleSetT Ms,
+ MemoryManagerPtrT MemMgr,
+ SymbolResolverPtrT Resolver) {
+
+ assert(MemMgr == nullptr &&
+ "User supplied memory managers not supported with COD yet.");
+
+ LogicalDylibs.push_back(createLogicalDylib(*this, std::move(Resolver)));
+
+ // Process each of the modules in this module set.
+ for (auto &M : Ms) {
+ std::vector<std::vector<Function*>> Partitioning;
+ for (auto &F : *M) {
+ if (F.isDeclaration())
+ continue;
+ Partitioning.push_back(std::vector<Function*>());
+ Partitioning.back().push_back(&F);
+ }
+ addLogicalModule(*LogicalDylibs.back(),
+ std::shared_ptr<Module>(std::move(M)),
+ std::move(Partitioning));
+ }
+
+ return std::prev(LogicalDylibs.end());
+ }
+
+ /// @brief Remove the module represented by the given handle.
+ ///
+ /// This will remove all modules in the layers below that were derived from
+ /// the module represented by H.
+ void removeModuleSet(ModuleSetHandleT H) {
+ LogicalDylibs.erase(H);
+ }
+
+ /// @brief Search for the given named symbol.
+ /// @param Name The name of the symbol to search for.
+ /// @param ExportedSymbolsOnly If true, search only for exported symbols.
+ /// @return A handle for the given named symbol, if it exists.
+ JITSymbol findSymbol(StringRef Name, bool ExportedSymbolsOnly) {
+ return BaseLayer.findSymbol(Name, ExportedSymbolsOnly);
+ }
+
+ /// @brief Get the address of a symbol provided by this layer, or some layer
+ /// below this one.
+ JITSymbol findSymbolIn(ModuleSetHandleT H, const std::string &Name,
+ bool ExportedSymbolsOnly) {
+ return (*H)->findSymbol(Name, ExportedSymbolsOnly);
+ }
+
+private:
+
+ void addLogicalModule(LogicalDylib &LD, std::shared_ptr<Module> SrcM,
+ std::vector<std::vector<Function*>> Partitions) {
+
+ // Bump the linkage and rename any anonymous/privote members in SrcM to
+ // ensure that everything will resolve properly after we partition SrcM.
+ makeAllSymbolsExternallyAccessible(*SrcM);
+
+ // Create a logical module handle for SrcM within the logical dylib.
+ auto LMH = LD.createLogicalModule();
+
+ // Create the GVs-and-stubs module.
+ auto GVsAndStubsM = llvm::make_unique<Module>(
+ (SrcM->getName() + ".globals_and_stubs").str(),
+ SrcM->getContext());
+ GVsAndStubsM->setDataLayout(SrcM->getDataLayout());
+ ValueToValueMapTy VMap;
+
+ // Process partitions and create stubs.
+ // We create the stubs before copying the global variables as we know the
+ // stubs won't refer to any globals (they only refer to their implementation
+ // pointer) so there's no ordering/value-mapping issues.
+ for (auto& Partition : Partitions) {
+ auto &UP = LD.createUncompiledPartition(LMH, SrcM);
+ typename UncompiledPartition::PartitionEntryList PartitionEntries;
+ for (auto &F : Partition) {
+ assert(!F->isDeclaration() &&
+ "Partition should only contain definitions");
+ unsigned FnIdx = PartitionEntries.size();
+ auto CCI = CompileCallbackMgr.getCompileCallback(SrcM->getContext());
+ PartitionEntries.push_back(
+ typename UncompiledPartition::PartitionEntry(F, CCI.getAddress()));
+ Function *StubF = cloneFunctionDecl(*GVsAndStubsM, *F, &VMap);
+ GlobalVariable *FnBodyPtr =
+ createImplPointer(*StubF->getType(), *StubF->getParent(),
+ StubF->getName() + "$orc_addr",
+ createIRTypedAddress(*StubF->getFunctionType(),
+ CCI.getAddress()));
+ makeStub(*StubF, *FnBodyPtr);
+ CCI.setCompileAction([&UP, FnIdx]() { return UP.compile(FnIdx); });
+ }
+
+ UP.setPartitionEntries(std::move(PartitionEntries));
+ }
+
+ // Now clone the global variable declarations.
+ GlobalDeclMaterializer GDMat(*GVsAndStubsM);
+ for (auto &GV : SrcM->globals())
+ if (!GV.isDeclaration())
+ cloneGlobalVariableDecl(*GVsAndStubsM, GV, &VMap);
+
+ // Then clone the initializers.
+ for (auto &GV : SrcM->globals())
+ if (!GV.isDeclaration())
+ moveGlobalVariableInitializer(GV, VMap, &GDMat);
+
+ // Build a resolver for the stubs module and add it to the base layer.
+ auto GVsAndStubsResolver = createLambdaResolver(
+ [&LD](const std::string &Name) {
+ if (auto Symbol = LD.findSymbol(Name, false))
+ return RuntimeDyld::SymbolInfo(Symbol.getAddress(),
+ Symbol.getFlags());
+ return LD.findSymbolExternally(Name);
+ },
+ [&LD](const std::string &Name) {
+ return RuntimeDyld::SymbolInfo(nullptr);
+ });
+
+ std::vector<std::unique_ptr<Module>> GVsAndStubsMSet;
+ GVsAndStubsMSet.push_back(std::move(GVsAndStubsM));
+ auto GVsAndStubsH =
+ BaseLayer.addModuleSet(std::move(GVsAndStubsMSet),
+ llvm::make_unique<SectionMemoryManager>(),
+ std::move(GVsAndStubsResolver));
+ LD.setGVsAndStubsHandle(LMH, GVsAndStubsH);
+ }
+
+ static std::string Mangle(StringRef Name, const DataLayout &DL) {
+ Mangler M(&DL);
+ std::string MangledName;
+ {
+ raw_string_ostream MangledNameStream(MangledName);
+ M.getNameWithPrefix(MangledNameStream, Name);
+ }
+ return MangledName;
+ }
+
+ BaseLayerT &BaseLayer;
+ CompileCallbackMgrT &CompileCallbackMgr;
+ LogicalDylibList LogicalDylibs;
+};
+
+template <typename BaseLayerT, typename CompileCallbackMgrT>
+typename CompileOnDemandLayer<BaseLayerT, CompileCallbackMgrT>::
+ UncompiledPartition&
+CompileOnDemandLayer<BaseLayerT, CompileCallbackMgrT>::LogicalDylib::
+ createUncompiledPartition(LMHandle LMH, std::shared_ptr<Module> SrcM) {
+ UncompiledPartitions.push_back(
+ llvm::make_unique<UncompiledPartition>(*this, LMH, std::move(SrcM)));
+ UncompiledPartitions.back()->setID(UncompiledPartitions.size() - 1);
+ return *UncompiledPartitions.back();
+}
+
+template <typename BaseLayerT, typename CompileCallbackMgrT>
+std::unique_ptr<typename CompileOnDemandLayer<BaseLayerT, CompileCallbackMgrT>::
+ UncompiledPartition>
+CompileOnDemandLayer<BaseLayerT, CompileCallbackMgrT>::LogicalDylib::
+ takeUPOwnership(UncompiledPartitionID ID) {
+
+ std::swap(UncompiledPartitions[ID], UncompiledPartitions.back());
+ UncompiledPartitions[ID]->setID(ID);
+ auto UP = std::move(UncompiledPartitions.back());
+ UncompiledPartitions.pop_back();
+ return UP;
+}
+
+} // End namespace orc.
+} // End namespace llvm.
+
+#endif // LLVM_EXECUTIONENGINE_ORC_COMPILEONDEMANDLAYER_H