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 "LambdaResolver.h" 19 #include "llvm/ADT/DenseSet.h" 20 #include "llvm/ExecutionEngine/RuntimeDyld.h" 21 #include "llvm/IR/IRBuilder.h" 22 #include "llvm/IR/Mangler.h" 23 #include "llvm/IR/Module.h" 24 #include "llvm/Transforms/Utils/ValueMapper.h" 25 #include <sstream> 26 27 namespace llvm { 28 namespace orc { 29 30 /// @brief Base class for JITLayer independent aspects of 31 /// JITCompileCallbackManager. 32 class JITCompileCallbackManagerBase { 33 public: 34 35 typedef std::function<TargetAddress()> CompileFtor; 36 37 /// @brief Handle to a newly created compile callback. Can be used to get an 38 /// IR constant representing the address of the trampoline, and to set 39 /// the compile action for the callback. 40 class CompileCallbackInfo { 41 public: CompileCallbackInfo(TargetAddress Addr,CompileFtor & Compile)42 CompileCallbackInfo(TargetAddress Addr, CompileFtor &Compile) 43 : Addr(Addr), Compile(Compile) {} 44 getAddress()45 TargetAddress getAddress() const { return Addr; } setCompileAction(CompileFtor Compile)46 void setCompileAction(CompileFtor Compile) { 47 this->Compile = std::move(Compile); 48 } 49 private: 50 TargetAddress Addr; 51 CompileFtor &Compile; 52 }; 53 54 /// @brief Construct a JITCompileCallbackManagerBase. 55 /// @param ErrorHandlerAddress The address of an error handler in the target 56 /// process to be used if a compile callback fails. 57 /// @param NumTrampolinesPerBlock Number of trampolines to emit if there is no 58 /// available trampoline when getCompileCallback is 59 /// called. JITCompileCallbackManagerBase(TargetAddress ErrorHandlerAddress,unsigned NumTrampolinesPerBlock)60 JITCompileCallbackManagerBase(TargetAddress ErrorHandlerAddress, 61 unsigned NumTrampolinesPerBlock) 62 : ErrorHandlerAddress(ErrorHandlerAddress), 63 NumTrampolinesPerBlock(NumTrampolinesPerBlock) {} 64 ~JITCompileCallbackManagerBase()65 virtual ~JITCompileCallbackManagerBase() {} 66 67 /// @brief Execute the callback for the given trampoline id. Called by the JIT 68 /// to compile functions on demand. executeCompileCallback(TargetAddress TrampolineAddr)69 TargetAddress executeCompileCallback(TargetAddress TrampolineAddr) { 70 auto I = ActiveTrampolines.find(TrampolineAddr); 71 // FIXME: Also raise an error in the Orc error-handler when we finally have 72 // one. 73 if (I == ActiveTrampolines.end()) 74 return ErrorHandlerAddress; 75 76 // Found a callback handler. Yank this trampoline out of the active list and 77 // put it back in the available trampolines list, then try to run the 78 // handler's compile and update actions. 79 // Moving the trampoline ID back to the available list first means there's at 80 // least one available trampoline if the compile action triggers a request for 81 // a new one. 82 auto Compile = std::move(I->second); 83 ActiveTrampolines.erase(I); 84 AvailableTrampolines.push_back(TrampolineAddr); 85 86 if (auto Addr = Compile()) 87 return Addr; 88 89 return ErrorHandlerAddress; 90 } 91 92 /// @brief Reserve a compile callback. 93 virtual CompileCallbackInfo getCompileCallback(LLVMContext &Context) = 0; 94 95 /// @brief Get a CompileCallbackInfo for an existing callback. getCompileCallbackInfo(TargetAddress TrampolineAddr)96 CompileCallbackInfo getCompileCallbackInfo(TargetAddress TrampolineAddr) { 97 auto I = ActiveTrampolines.find(TrampolineAddr); 98 assert(I != ActiveTrampolines.end() && "Not an active trampoline."); 99 return CompileCallbackInfo(I->first, I->second); 100 } 101 102 /// @brief Release a compile callback. 103 /// 104 /// Note: Callbacks are auto-released after they execute. This method should 105 /// only be called to manually release a callback that is not going to 106 /// execute. releaseCompileCallback(TargetAddress TrampolineAddr)107 void releaseCompileCallback(TargetAddress TrampolineAddr) { 108 auto I = ActiveTrampolines.find(TrampolineAddr); 109 assert(I != ActiveTrampolines.end() && "Not an active trampoline."); 110 ActiveTrampolines.erase(I); 111 AvailableTrampolines.push_back(TrampolineAddr); 112 } 113 114 protected: 115 TargetAddress ErrorHandlerAddress; 116 unsigned NumTrampolinesPerBlock; 117 118 typedef std::map<TargetAddress, CompileFtor> TrampolineMapT; 119 TrampolineMapT ActiveTrampolines; 120 std::vector<TargetAddress> AvailableTrampolines; 121 }; 122 123 /// @brief Manage compile callbacks. 124 template <typename JITLayerT, typename TargetT> 125 class JITCompileCallbackManager : public JITCompileCallbackManagerBase { 126 public: 127 128 /// @brief Construct a JITCompileCallbackManager. 129 /// @param JIT JIT layer to emit callback trampolines, etc. into. 130 /// @param Context LLVMContext to use for trampoline & resolve block modules. 131 /// @param ErrorHandlerAddress The address of an error handler in the target 132 /// process to be used if a compile callback fails. 133 /// @param NumTrampolinesPerBlock Number of trampolines to allocate whenever 134 /// there is no existing callback trampoline. 135 /// (Trampolines are allocated in blocks for 136 /// efficiency.) JITCompileCallbackManager(JITLayerT & JIT,RuntimeDyld::MemoryManager & MemMgr,LLVMContext & Context,TargetAddress ErrorHandlerAddress,unsigned NumTrampolinesPerBlock)137 JITCompileCallbackManager(JITLayerT &JIT, RuntimeDyld::MemoryManager &MemMgr, 138 LLVMContext &Context, 139 TargetAddress ErrorHandlerAddress, 140 unsigned NumTrampolinesPerBlock) 141 : JITCompileCallbackManagerBase(ErrorHandlerAddress, 142 NumTrampolinesPerBlock), 143 JIT(JIT), MemMgr(MemMgr) { 144 emitResolverBlock(Context); 145 } 146 147 /// @brief Get/create a compile callback with the given signature. getCompileCallback(LLVMContext & Context)148 CompileCallbackInfo getCompileCallback(LLVMContext &Context) final { 149 TargetAddress TrampolineAddr = getAvailableTrampolineAddr(Context); 150 auto &Compile = this->ActiveTrampolines[TrampolineAddr]; 151 return CompileCallbackInfo(TrampolineAddr, Compile); 152 } 153 154 private: 155 156 std::vector<std::unique_ptr<Module>> SingletonSet(std::unique_ptr<Module> M)157 SingletonSet(std::unique_ptr<Module> M) { 158 std::vector<std::unique_ptr<Module>> Ms; 159 Ms.push_back(std::move(M)); 160 return Ms; 161 } 162 emitResolverBlock(LLVMContext & Context)163 void emitResolverBlock(LLVMContext &Context) { 164 std::unique_ptr<Module> M(new Module("resolver_block_module", 165 Context)); 166 TargetT::insertResolverBlock(*M, *this); 167 auto NonResolver = 168 createLambdaResolver( 169 [](const std::string &Name) -> RuntimeDyld::SymbolInfo { 170 llvm_unreachable("External symbols in resolver block?"); 171 }, 172 [](const std::string &Name) -> RuntimeDyld::SymbolInfo { 173 llvm_unreachable("Dylib symbols in resolver block?"); 174 }); 175 auto H = JIT.addModuleSet(SingletonSet(std::move(M)), &MemMgr, 176 std::move(NonResolver)); 177 JIT.emitAndFinalize(H); 178 auto ResolverBlockSymbol = 179 JIT.findSymbolIn(H, TargetT::ResolverBlockName, false); 180 assert(ResolverBlockSymbol && "Failed to insert resolver block"); 181 ResolverBlockAddr = ResolverBlockSymbol.getAddress(); 182 } 183 getAvailableTrampolineAddr(LLVMContext & Context)184 TargetAddress getAvailableTrampolineAddr(LLVMContext &Context) { 185 if (this->AvailableTrampolines.empty()) 186 grow(Context); 187 assert(!this->AvailableTrampolines.empty() && 188 "Failed to grow available trampolines."); 189 TargetAddress TrampolineAddr = this->AvailableTrampolines.back(); 190 this->AvailableTrampolines.pop_back(); 191 return TrampolineAddr; 192 } 193 grow(LLVMContext & Context)194 void grow(LLVMContext &Context) { 195 assert(this->AvailableTrampolines.empty() && "Growing prematurely?"); 196 std::unique_ptr<Module> M(new Module("trampoline_block", Context)); 197 auto GetLabelName = 198 TargetT::insertCompileCallbackTrampolines(*M, ResolverBlockAddr, 199 this->NumTrampolinesPerBlock, 200 this->ActiveTrampolines.size()); 201 auto NonResolver = 202 createLambdaResolver( 203 [](const std::string &Name) -> RuntimeDyld::SymbolInfo { 204 llvm_unreachable("External symbols in trampoline block?"); 205 }, 206 [](const std::string &Name) -> RuntimeDyld::SymbolInfo { 207 llvm_unreachable("Dylib symbols in trampoline block?"); 208 }); 209 auto H = JIT.addModuleSet(SingletonSet(std::move(M)), &MemMgr, 210 std::move(NonResolver)); 211 JIT.emitAndFinalize(H); 212 for (unsigned I = 0; I < this->NumTrampolinesPerBlock; ++I) { 213 std::string Name = GetLabelName(I); 214 auto TrampolineSymbol = JIT.findSymbolIn(H, Name, false); 215 assert(TrampolineSymbol && "Failed to emit trampoline."); 216 this->AvailableTrampolines.push_back(TrampolineSymbol.getAddress()); 217 } 218 } 219 220 JITLayerT &JIT; 221 RuntimeDyld::MemoryManager &MemMgr; 222 TargetAddress ResolverBlockAddr; 223 }; 224 225 /// @brief Build a function pointer of FunctionType with the given constant 226 /// address. 227 /// 228 /// Usage example: Turn a trampoline address into a function pointer constant 229 /// for use in a stub. 230 Constant* createIRTypedAddress(FunctionType &FT, TargetAddress Addr); 231 232 /// @brief Create a function pointer with the given type, name, and initializer 233 /// in the given Module. 234 GlobalVariable* createImplPointer(PointerType &PT, Module &M, 235 const Twine &Name, Constant *Initializer); 236 237 /// @brief Turn a function declaration into a stub function that makes an 238 /// indirect call using the given function pointer. 239 void makeStub(Function &F, GlobalVariable &ImplPointer); 240 241 /// @brief Raise linkage types and rename as necessary to ensure that all 242 /// symbols are accessible for other modules. 243 /// 244 /// This should be called before partitioning a module to ensure that the 245 /// partitions retain access to each other's symbols. 246 void makeAllSymbolsExternallyAccessible(Module &M); 247 248 /// @brief Clone a function declaration into a new module. 249 /// 250 /// This function can be used as the first step towards creating a callback 251 /// stub (see makeStub), or moving a function body (see moveFunctionBody). 252 /// 253 /// If the VMap argument is non-null, a mapping will be added between F and 254 /// the new declaration, and between each of F's arguments and the new 255 /// declaration's arguments. This map can then be passed in to moveFunction to 256 /// move the function body if required. Note: When moving functions between 257 /// modules with these utilities, all decls should be cloned (and added to a 258 /// single VMap) before any bodies are moved. This will ensure that references 259 /// between functions all refer to the versions in the new module. 260 Function* cloneFunctionDecl(Module &Dst, const Function &F, 261 ValueToValueMapTy *VMap = nullptr); 262 263 /// @brief Move the body of function 'F' to a cloned function declaration in a 264 /// different module (See related cloneFunctionDecl). 265 /// 266 /// If the target function declaration is not supplied via the NewF parameter 267 /// then it will be looked up via the VMap. 268 /// 269 /// This will delete the body of function 'F' from its original parent module, 270 /// but leave its declaration. 271 void moveFunctionBody(Function &OrigF, ValueToValueMapTy &VMap, 272 ValueMaterializer *Materializer = nullptr, 273 Function *NewF = nullptr); 274 275 /// @brief Clone a global variable declaration into a new module. 276 GlobalVariable* cloneGlobalVariableDecl(Module &Dst, const GlobalVariable &GV, 277 ValueToValueMapTy *VMap = nullptr); 278 279 /// @brief Move global variable GV from its parent module to cloned global 280 /// declaration in a different module. 281 /// 282 /// If the target global declaration is not supplied via the NewGV parameter 283 /// then it will be looked up via the VMap. 284 /// 285 /// This will delete the initializer of GV from its original parent module, 286 /// but leave its declaration. 287 void moveGlobalVariableInitializer(GlobalVariable &OrigGV, 288 ValueToValueMapTy &VMap, 289 ValueMaterializer *Materializer = nullptr, 290 GlobalVariable *NewGV = nullptr); 291 292 } // End namespace orc. 293 } // End namespace llvm. 294 295 #endif // LLVM_EXECUTIONENGINE_ORC_INDIRECTIONUTILS_H 296