1 //===-- IndirectionUtils.h - Utilities for adding indirections --*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // Contains utilities for adding indirections and breaking up modules. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #ifndef LLVM_EXECUTIONENGINE_ORC_INDIRECTIONUTILS_H 15 #define LLVM_EXECUTIONENGINE_ORC_INDIRECTIONUTILS_H 16 17 #include "JITSymbol.h" 18 #include "llvm/ADT/DenseSet.h" 19 #include "llvm/ExecutionEngine/RuntimeDyld.h" 20 #include "llvm/IR/IRBuilder.h" 21 #include "llvm/IR/Mangler.h" 22 #include "llvm/IR/Module.h" 23 #include <sstream> 24 25 namespace llvm { 26 namespace orc { 27 28 /// @brief Base class for JITLayer independent aspects of 29 /// JITCompileCallbackManager. 30 class JITCompileCallbackManagerBase { 31 public: 32 33 typedef std::function<TargetAddress()> CompileFtor; 34 typedef std::function<void(TargetAddress)> UpdateFtor; 35 36 /// @brief Handle to a newly created compile callback. Can be used to get an 37 /// IR constant representing the address of the trampoline, and to set 38 /// the compile and update actions for the callback. 39 class CompileCallbackInfo { 40 public: 41 CompileCallbackInfo(TargetAddress Addr, CompileFtor &Compile, 42 UpdateFtor &Update) 43 : Addr(Addr), Compile(Compile), Update(Update) {} 44 45 TargetAddress getAddress() const { return Addr; } 46 void setCompileAction(CompileFtor Compile) { 47 this->Compile = std::move(Compile); 48 } 49 void setUpdateAction(UpdateFtor Update) { 50 this->Update = std::move(Update); 51 } 52 private: 53 TargetAddress Addr; 54 CompileFtor &Compile; 55 UpdateFtor &Update; 56 }; 57 58 /// @brief Construct a JITCompileCallbackManagerBase. 59 /// @param ErrorHandlerAddress The address of an error handler in the target 60 /// process to be used if a compile callback fails. 61 /// @param NumTrampolinesPerBlock Number of trampolines to emit if there is no 62 /// available trampoline when getCompileCallback is 63 /// called. 64 JITCompileCallbackManagerBase(TargetAddress ErrorHandlerAddress, 65 unsigned NumTrampolinesPerBlock) 66 : ErrorHandlerAddress(ErrorHandlerAddress), 67 NumTrampolinesPerBlock(NumTrampolinesPerBlock) {} 68 69 virtual ~JITCompileCallbackManagerBase() {} 70 71 /// @brief Execute the callback for the given trampoline id. Called by the JIT 72 /// to compile functions on demand. 73 TargetAddress executeCompileCallback(TargetAddress TrampolineID) { 74 TrampolineMapT::iterator I = ActiveTrampolines.find(TrampolineID); 75 // FIXME: Also raise an error in the Orc error-handler when we finally have 76 // one. 77 if (I == ActiveTrampolines.end()) 78 return ErrorHandlerAddress; 79 80 // Found a callback handler. Yank this trampoline out of the active list and 81 // put it back in the available trampolines list, then try to run the 82 // handler's compile and update actions. 83 // Moving the trampoline ID back to the available list first means there's at 84 // least one available trampoline if the compile action triggers a request for 85 // a new one. 86 AvailableTrampolines.push_back(I->first); 87 auto CallbackHandler = std::move(I->second); 88 ActiveTrampolines.erase(I); 89 90 if (auto Addr = CallbackHandler.Compile()) { 91 CallbackHandler.Update(Addr); 92 return Addr; 93 } 94 return ErrorHandlerAddress; 95 } 96 97 /// @brief Get/create a compile callback with the given signature. 98 virtual CompileCallbackInfo getCompileCallback(LLVMContext &Context) = 0; 99 100 protected: 101 102 struct CallbackHandler { 103 CompileFtor Compile; 104 UpdateFtor Update; 105 }; 106 107 TargetAddress ErrorHandlerAddress; 108 unsigned NumTrampolinesPerBlock; 109 110 typedef std::map<TargetAddress, CallbackHandler> TrampolineMapT; 111 TrampolineMapT ActiveTrampolines; 112 std::vector<TargetAddress> AvailableTrampolines; 113 }; 114 115 /// @brief Manage compile callbacks. 116 template <typename JITLayerT, typename TargetT> 117 class JITCompileCallbackManager : public JITCompileCallbackManagerBase { 118 public: 119 120 /// @brief Construct a JITCompileCallbackManager. 121 /// @param JIT JIT layer to emit callback trampolines, etc. into. 122 /// @param Context LLVMContext to use for trampoline & resolve block modules. 123 /// @param ErrorHandlerAddress The address of an error handler in the target 124 /// process to be used if a compile callback fails. 125 /// @param NumTrampolinesPerBlock Number of trampolines to allocate whenever 126 /// there is no existing callback trampoline. 127 /// (Trampolines are allocated in blocks for 128 /// efficiency.) 129 JITCompileCallbackManager(JITLayerT &JIT, RuntimeDyld::MemoryManager &MemMgr, 130 LLVMContext &Context, 131 TargetAddress ErrorHandlerAddress, 132 unsigned NumTrampolinesPerBlock) 133 : JITCompileCallbackManagerBase(ErrorHandlerAddress, 134 NumTrampolinesPerBlock), 135 JIT(JIT), MemMgr(MemMgr) { 136 emitResolverBlock(Context); 137 } 138 139 /// @brief Get/create a compile callback with the given signature. 140 CompileCallbackInfo getCompileCallback(LLVMContext &Context) final { 141 TargetAddress TrampolineAddr = getAvailableTrampolineAddr(Context); 142 auto &CallbackHandler = 143 this->ActiveTrampolines[TrampolineAddr]; 144 145 return CompileCallbackInfo(TrampolineAddr, CallbackHandler.Compile, 146 CallbackHandler.Update); 147 } 148 149 private: 150 151 std::vector<std::unique_ptr<Module>> 152 SingletonSet(std::unique_ptr<Module> M) { 153 std::vector<std::unique_ptr<Module>> Ms; 154 Ms.push_back(std::move(M)); 155 return Ms; 156 } 157 158 void emitResolverBlock(LLVMContext &Context) { 159 std::unique_ptr<Module> M(new Module("resolver_block_module", 160 Context)); 161 TargetT::insertResolverBlock(*M, *this); 162 auto H = JIT.addModuleSet(SingletonSet(std::move(M)), &MemMgr, 163 static_cast<RuntimeDyld::SymbolResolver*>( 164 nullptr)); 165 JIT.emitAndFinalize(H); 166 auto ResolverBlockSymbol = 167 JIT.findSymbolIn(H, TargetT::ResolverBlockName, false); 168 assert(ResolverBlockSymbol && "Failed to insert resolver block"); 169 ResolverBlockAddr = ResolverBlockSymbol.getAddress(); 170 } 171 172 TargetAddress getAvailableTrampolineAddr(LLVMContext &Context) { 173 if (this->AvailableTrampolines.empty()) 174 grow(Context); 175 assert(!this->AvailableTrampolines.empty() && 176 "Failed to grow available trampolines."); 177 TargetAddress TrampolineAddr = this->AvailableTrampolines.back(); 178 this->AvailableTrampolines.pop_back(); 179 return TrampolineAddr; 180 } 181 182 void grow(LLVMContext &Context) { 183 assert(this->AvailableTrampolines.empty() && "Growing prematurely?"); 184 std::unique_ptr<Module> M(new Module("trampoline_block", Context)); 185 auto GetLabelName = 186 TargetT::insertCompileCallbackTrampolines(*M, ResolverBlockAddr, 187 this->NumTrampolinesPerBlock, 188 this->ActiveTrampolines.size()); 189 auto H = JIT.addModuleSet(SingletonSet(std::move(M)), &MemMgr, 190 static_cast<RuntimeDyld::SymbolResolver*>( 191 nullptr)); 192 JIT.emitAndFinalize(H); 193 for (unsigned I = 0; I < this->NumTrampolinesPerBlock; ++I) { 194 std::string Name = GetLabelName(I); 195 auto TrampolineSymbol = JIT.findSymbolIn(H, Name, false); 196 assert(TrampolineSymbol && "Failed to emit trampoline."); 197 this->AvailableTrampolines.push_back(TrampolineSymbol.getAddress()); 198 } 199 } 200 201 JITLayerT &JIT; 202 RuntimeDyld::MemoryManager &MemMgr; 203 TargetAddress ResolverBlockAddr; 204 }; 205 206 /// @brief Get an update functor that updates the value of a named function 207 /// pointer. 208 template <typename JITLayerT> 209 JITCompileCallbackManagerBase::UpdateFtor 210 getLocalFPUpdater(JITLayerT &JIT, typename JITLayerT::ModuleSetHandleT H, 211 std::string Name) { 212 // FIXME: Move-capture Name once we can use C++14. 213 return [=,&JIT](TargetAddress Addr) { 214 auto FPSym = JIT.findSymbolIn(H, Name, true); 215 assert(FPSym && "Cannot find function pointer to update."); 216 void *FPAddr = reinterpret_cast<void*>( 217 static_cast<uintptr_t>(FPSym.getAddress())); 218 memcpy(FPAddr, &Addr, sizeof(uintptr_t)); 219 }; 220 } 221 222 /// @brief Build a function pointer of FunctionType with the given constant 223 /// address. 224 /// 225 /// Usage example: Turn a trampoline address into a function pointer constant 226 /// for use in a stub. 227 Constant* createIRTypedAddress(FunctionType &FT, TargetAddress Addr); 228 229 /// @brief Create a function pointer with the given type, name, and initializer 230 /// in the given Module. 231 GlobalVariable* createImplPointer(PointerType &PT, Module &M, 232 const Twine &Name, Constant *Initializer); 233 234 /// @brief Turn a function declaration into a stub function that makes an 235 /// indirect call using the given function pointer. 236 void makeStub(Function &F, GlobalVariable &ImplPointer); 237 238 typedef std::map<Module*, DenseSet<const GlobalValue*>> ModulePartitionMap; 239 240 /// @brief Extract subsections of a Module into the given Module according to 241 /// the given ModulePartitionMap. 242 void partition(Module &M, const ModulePartitionMap &PMap); 243 244 /// @brief Struct for trivial "complete" partitioning of a module. 245 class FullyPartitionedModule { 246 public: 247 std::unique_ptr<Module> GlobalVars; 248 std::unique_ptr<Module> Commons; 249 std::vector<std::unique_ptr<Module>> Functions; 250 251 FullyPartitionedModule() = default; 252 FullyPartitionedModule(FullyPartitionedModule &&S) 253 : GlobalVars(std::move(S.GlobalVars)), Commons(std::move(S.Commons)), 254 Functions(std::move(S.Functions)) {} 255 }; 256 257 /// @brief Extract every function in M into a separate module. 258 FullyPartitionedModule fullyPartition(Module &M); 259 260 } // End namespace orc. 261 } // End namespace llvm. 262 263 #endif // LLVM_EXECUTIONENGINE_ORC_INDIRECTIONUTILS_H 264