//===- 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 #include #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 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(V)) return cloneGlobalVariableDecl(Dst, *GV); else if (auto *F = dyn_cast(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 SrcM; BaseLayerModuleSetHandleT GVsAndStubsHandle; std::vector 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 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 SrcM); // Take ownership of the given UncompiledPartition from the logical dylib. std::unique_ptr 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> UncompiledPartitions; }; template 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 static std::unique_ptr createLogicalDylib(CompileOnDemandLayer &CODLayer, ResolverPtrT Resolver) { typedef LogicalDylibImpl Impl; return llvm::make_unique(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 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 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 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( static_cast(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(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(); 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> 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 SrcM; typename LogicalDylib::UncompiledPartitionID ID; PartitionEntryList PartitionEntries; }; typedef std::list> 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 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> Partitioning; for (auto &F : *M) { if (F.isDeclaration()) continue; Partitioning.emplace_back(1, &F); } addLogicalModule(*LogicalDylibs.back(), std::shared_ptr(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 SrcM, std::vector> 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( (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> GVsAndStubsMSet; GVsAndStubsMSet.push_back(std::move(GVsAndStubsM)); auto GVsAndStubsH = BaseLayer.addModuleSet(std::move(GVsAndStubsMSet), llvm::make_unique(), 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 CompileOnDemandLayer:: UncompiledPartition& CompileOnDemandLayer::LogicalDylib:: createUncompiledPartition(LMHandle LMH, std::shared_ptr SrcM) { UncompiledPartitions.push_back( llvm::make_unique(*this, LMH, std::move(SrcM))); UncompiledPartitions.back()->setID(UncompiledPartitions.size() - 1); return *UncompiledPartitions.back(); } template std::unique_ptr:: UncompiledPartition> CompileOnDemandLayer::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