//===-- IndirectionUtils.h - Utilities for adding indirections --*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Contains utilities for adding indirections and breaking up modules. // //===----------------------------------------------------------------------===// #ifndef LLVM_EXECUTIONENGINE_ORC_INDIRECTIONUTILS_H #define LLVM_EXECUTIONENGINE_ORC_INDIRECTIONUTILS_H #include "JITSymbol.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ExecutionEngine/RuntimeDyld.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Mangler.h" #include "llvm/IR/Module.h" #include namespace llvm { namespace orc { /// @brief Base class for JITLayer independent aspects of /// JITCompileCallbackManager. class JITCompileCallbackManagerBase { public: typedef std::function CompileFtor; typedef std::function UpdateFtor; /// @brief Handle to a newly created compile callback. Can be used to get an /// IR constant representing the address of the trampoline, and to set /// the compile and update actions for the callback. class CompileCallbackInfo { public: CompileCallbackInfo(TargetAddress Addr, CompileFtor &Compile, UpdateFtor &Update) : Addr(Addr), Compile(Compile), Update(Update) {} TargetAddress getAddress() const { return Addr; } void setCompileAction(CompileFtor Compile) { this->Compile = std::move(Compile); } void setUpdateAction(UpdateFtor Update) { this->Update = std::move(Update); } private: TargetAddress Addr; CompileFtor &Compile; UpdateFtor &Update; }; /// @brief Construct a JITCompileCallbackManagerBase. /// @param ErrorHandlerAddress The address of an error handler in the target /// process to be used if a compile callback fails. /// @param NumTrampolinesPerBlock Number of trampolines to emit if there is no /// available trampoline when getCompileCallback is /// called. JITCompileCallbackManagerBase(TargetAddress ErrorHandlerAddress, unsigned NumTrampolinesPerBlock) : ErrorHandlerAddress(ErrorHandlerAddress), NumTrampolinesPerBlock(NumTrampolinesPerBlock) {} virtual ~JITCompileCallbackManagerBase() {} /// @brief Execute the callback for the given trampoline id. Called by the JIT /// to compile functions on demand. TargetAddress executeCompileCallback(TargetAddress TrampolineID) { TrampolineMapT::iterator I = ActiveTrampolines.find(TrampolineID); // FIXME: Also raise an error in the Orc error-handler when we finally have // one. if (I == ActiveTrampolines.end()) return ErrorHandlerAddress; // Found a callback handler. Yank this trampoline out of the active list and // put it back in the available trampolines list, then try to run the // handler's compile and update actions. // Moving the trampoline ID back to the available list first means there's at // least one available trampoline if the compile action triggers a request for // a new one. AvailableTrampolines.push_back(I->first); auto CallbackHandler = std::move(I->second); ActiveTrampolines.erase(I); if (auto Addr = CallbackHandler.Compile()) { CallbackHandler.Update(Addr); return Addr; } return ErrorHandlerAddress; } /// @brief Get/create a compile callback with the given signature. virtual CompileCallbackInfo getCompileCallback(LLVMContext &Context) = 0; protected: struct CallbackHandler { CompileFtor Compile; UpdateFtor Update; }; TargetAddress ErrorHandlerAddress; unsigned NumTrampolinesPerBlock; typedef std::map TrampolineMapT; TrampolineMapT ActiveTrampolines; std::vector AvailableTrampolines; }; /// @brief Manage compile callbacks. template class JITCompileCallbackManager : public JITCompileCallbackManagerBase { public: /// @brief Construct a JITCompileCallbackManager. /// @param JIT JIT layer to emit callback trampolines, etc. into. /// @param Context LLVMContext to use for trampoline & resolve block modules. /// @param ErrorHandlerAddress The address of an error handler in the target /// process to be used if a compile callback fails. /// @param NumTrampolinesPerBlock Number of trampolines to allocate whenever /// there is no existing callback trampoline. /// (Trampolines are allocated in blocks for /// efficiency.) JITCompileCallbackManager(JITLayerT &JIT, RuntimeDyld::MemoryManager &MemMgr, LLVMContext &Context, TargetAddress ErrorHandlerAddress, unsigned NumTrampolinesPerBlock) : JITCompileCallbackManagerBase(ErrorHandlerAddress, NumTrampolinesPerBlock), JIT(JIT), MemMgr(MemMgr) { emitResolverBlock(Context); } /// @brief Get/create a compile callback with the given signature. CompileCallbackInfo getCompileCallback(LLVMContext &Context) final { TargetAddress TrampolineAddr = getAvailableTrampolineAddr(Context); auto &CallbackHandler = this->ActiveTrampolines[TrampolineAddr]; return CompileCallbackInfo(TrampolineAddr, CallbackHandler.Compile, CallbackHandler.Update); } private: std::vector> SingletonSet(std::unique_ptr M) { std::vector> Ms; Ms.push_back(std::move(M)); return Ms; } void emitResolverBlock(LLVMContext &Context) { std::unique_ptr M(new Module("resolver_block_module", Context)); TargetT::insertResolverBlock(*M, *this); auto H = JIT.addModuleSet(SingletonSet(std::move(M)), &MemMgr, static_cast( nullptr)); JIT.emitAndFinalize(H); auto ResolverBlockSymbol = JIT.findSymbolIn(H, TargetT::ResolverBlockName, false); assert(ResolverBlockSymbol && "Failed to insert resolver block"); ResolverBlockAddr = ResolverBlockSymbol.getAddress(); } TargetAddress getAvailableTrampolineAddr(LLVMContext &Context) { if (this->AvailableTrampolines.empty()) grow(Context); assert(!this->AvailableTrampolines.empty() && "Failed to grow available trampolines."); TargetAddress TrampolineAddr = this->AvailableTrampolines.back(); this->AvailableTrampolines.pop_back(); return TrampolineAddr; } void grow(LLVMContext &Context) { assert(this->AvailableTrampolines.empty() && "Growing prematurely?"); std::unique_ptr M(new Module("trampoline_block", Context)); auto GetLabelName = TargetT::insertCompileCallbackTrampolines(*M, ResolverBlockAddr, this->NumTrampolinesPerBlock, this->ActiveTrampolines.size()); auto H = JIT.addModuleSet(SingletonSet(std::move(M)), &MemMgr, static_cast( nullptr)); JIT.emitAndFinalize(H); for (unsigned I = 0; I < this->NumTrampolinesPerBlock; ++I) { std::string Name = GetLabelName(I); auto TrampolineSymbol = JIT.findSymbolIn(H, Name, false); assert(TrampolineSymbol && "Failed to emit trampoline."); this->AvailableTrampolines.push_back(TrampolineSymbol.getAddress()); } } JITLayerT &JIT; RuntimeDyld::MemoryManager &MemMgr; TargetAddress ResolverBlockAddr; }; /// @brief Get an update functor that updates the value of a named function /// pointer. template JITCompileCallbackManagerBase::UpdateFtor getLocalFPUpdater(JITLayerT &JIT, typename JITLayerT::ModuleSetHandleT H, std::string Name) { // FIXME: Move-capture Name once we can use C++14. return [=,&JIT](TargetAddress Addr) { auto FPSym = JIT.findSymbolIn(H, Name, true); assert(FPSym && "Cannot find function pointer to update."); void *FPAddr = reinterpret_cast( static_cast(FPSym.getAddress())); memcpy(FPAddr, &Addr, sizeof(uintptr_t)); }; } /// @brief Build a function pointer of FunctionType with the given constant /// address. /// /// Usage example: Turn a trampoline address into a function pointer constant /// for use in a stub. Constant* createIRTypedAddress(FunctionType &FT, TargetAddress Addr); /// @brief Create a function pointer with the given type, name, and initializer /// in the given Module. GlobalVariable* createImplPointer(PointerType &PT, Module &M, const Twine &Name, Constant *Initializer); /// @brief Turn a function declaration into a stub function that makes an /// indirect call using the given function pointer. void makeStub(Function &F, GlobalVariable &ImplPointer); typedef std::map> ModulePartitionMap; /// @brief Extract subsections of a Module into the given Module according to /// the given ModulePartitionMap. void partition(Module &M, const ModulePartitionMap &PMap); /// @brief Struct for trivial "complete" partitioning of a module. class FullyPartitionedModule { public: std::unique_ptr GlobalVars; std::unique_ptr Commons; std::vector> Functions; FullyPartitionedModule() = default; FullyPartitionedModule(FullyPartitionedModule &&S) : GlobalVars(std::move(S.GlobalVars)), Commons(std::move(S.Commons)), Functions(std::move(S.Functions)) {} }; /// @brief Extract every function in M into a separate module. FullyPartitionedModule fullyPartition(Module &M); } // End namespace orc. } // End namespace llvm. #endif // LLVM_EXECUTIONENGINE_ORC_INDIRECTIONUTILS_H