1 //===------ LazyReexports.h -- Utilities for lazy reexports -----*- 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 // Lazy re-exports are similar to normal re-exports, except that for callable 11 // symbols the definitions are replaced with trampolines that will look up and 12 // call through to the re-exported symbol at runtime. This can be used to 13 // enable lazy compilation. 14 // 15 //===----------------------------------------------------------------------===// 16 17 #ifndef LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H 18 #define LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H 19 20 #include "llvm/ExecutionEngine/Orc/Core.h" 21 #include "llvm/ExecutionEngine/Orc/IndirectionUtils.h" 22 23 namespace llvm { 24 25 class Triple; 26 27 namespace orc { 28 29 /// Manages a set of 'lazy call-through' trampolines. These are compiler 30 /// re-entry trampolines that are pre-bound to look up a given symbol in a given 31 /// JITDylib, then jump to that address. Since compilation of symbols is 32 /// triggered on first lookup, these call-through trampolines can be used to 33 /// implement lazy compilation. 34 /// 35 /// The easiest way to construct these call-throughs is using the lazyReexport 36 /// function. 37 class LazyCallThroughManager { 38 public: 39 /// Clients will want to take some action on first resolution, e.g. updating 40 /// a stub pointer. Instances of this class can be used to implement this. 41 class NotifyResolvedFunction { 42 public: ~NotifyResolvedFunction()43 virtual ~NotifyResolvedFunction() {} 44 45 /// Called the first time a lazy call through is executed and the target 46 /// symbol resolved. 47 virtual Error operator()(JITDylib &SourceJD, 48 const SymbolStringPtr &SymbolName, 49 JITTargetAddress ResolvedAddr) = 0; 50 51 private: 52 virtual void anchor(); 53 }; 54 55 template <typename NotifyResolvedImpl> 56 class NotifyResolvedFunctionImpl : public NotifyResolvedFunction { 57 public: NotifyResolvedFunctionImpl(NotifyResolvedImpl NotifyResolved)58 NotifyResolvedFunctionImpl(NotifyResolvedImpl NotifyResolved) 59 : NotifyResolved(std::move(NotifyResolved)) {} operator()60 Error operator()(JITDylib &SourceJD, const SymbolStringPtr &SymbolName, 61 JITTargetAddress ResolvedAddr) { 62 return NotifyResolved(SourceJD, SymbolName, ResolvedAddr); 63 } 64 65 private: 66 NotifyResolvedImpl NotifyResolved; 67 }; 68 69 /// Create a shared NotifyResolvedFunction from a given type that is 70 /// callable with the correct signature. 71 template <typename NotifyResolvedImpl> 72 static std::unique_ptr<NotifyResolvedFunction> createNotifyResolvedFunction(NotifyResolvedImpl NotifyResolved)73 createNotifyResolvedFunction(NotifyResolvedImpl NotifyResolved) { 74 return llvm::make_unique<NotifyResolvedFunctionImpl<NotifyResolvedImpl>>( 75 std::move(NotifyResolved)); 76 } 77 78 // Return a free call-through trampoline and bind it to look up and call 79 // through to the given symbol. 80 Expected<JITTargetAddress> getCallThroughTrampoline( 81 JITDylib &SourceJD, SymbolStringPtr SymbolName, 82 std::shared_ptr<NotifyResolvedFunction> NotifyResolved); 83 84 protected: 85 LazyCallThroughManager(ExecutionSession &ES, 86 JITTargetAddress ErrorHandlerAddr, 87 std::unique_ptr<TrampolinePool> TP); 88 89 JITTargetAddress callThroughToSymbol(JITTargetAddress TrampolineAddr); 90 setTrampolinePool(std::unique_ptr<TrampolinePool> TP)91 void setTrampolinePool(std::unique_ptr<TrampolinePool> TP) { 92 this->TP = std::move(TP); 93 } 94 95 private: 96 using ReexportsMap = 97 std::map<JITTargetAddress, std::pair<JITDylib *, SymbolStringPtr>>; 98 99 using NotifiersMap = 100 std::map<JITTargetAddress, std::shared_ptr<NotifyResolvedFunction>>; 101 102 std::mutex LCTMMutex; 103 ExecutionSession &ES; 104 JITTargetAddress ErrorHandlerAddr; 105 std::unique_ptr<TrampolinePool> TP; 106 ReexportsMap Reexports; 107 NotifiersMap Notifiers; 108 }; 109 110 /// A lazy call-through manager that builds trampolines in the current process. 111 class LocalLazyCallThroughManager : public LazyCallThroughManager { 112 private: LocalLazyCallThroughManager(ExecutionSession & ES,JITTargetAddress ErrorHandlerAddr)113 LocalLazyCallThroughManager(ExecutionSession &ES, 114 JITTargetAddress ErrorHandlerAddr) 115 : LazyCallThroughManager(ES, ErrorHandlerAddr, nullptr) {} 116 init()117 template <typename ORCABI> Error init() { 118 auto TP = LocalTrampolinePool<ORCABI>::Create( 119 [this](JITTargetAddress TrampolineAddr) { 120 return callThroughToSymbol(TrampolineAddr); 121 }); 122 123 if (!TP) 124 return TP.takeError(); 125 126 setTrampolinePool(std::move(*TP)); 127 return Error::success(); 128 } 129 130 public: 131 /// Create a LocalLazyCallThroughManager using the given ABI. See 132 /// createLocalLazyCallThroughManager. 133 template <typename ORCABI> 134 static Expected<std::unique_ptr<LocalLazyCallThroughManager>> Create(ExecutionSession & ES,JITTargetAddress ErrorHandlerAddr)135 Create(ExecutionSession &ES, JITTargetAddress ErrorHandlerAddr) { 136 auto LLCTM = std::unique_ptr<LocalLazyCallThroughManager>( 137 new LocalLazyCallThroughManager(ES, ErrorHandlerAddr)); 138 139 if (auto Err = LLCTM->init<ORCABI>()) 140 return std::move(Err); 141 142 return std::move(LLCTM); 143 } 144 }; 145 146 /// Create a LocalLazyCallThroughManager from the given triple and execution 147 /// session. 148 Expected<std::unique_ptr<LazyCallThroughManager>> 149 createLocalLazyCallThroughManager(const Triple &T, ExecutionSession &ES, 150 JITTargetAddress ErrorHandlerAddr); 151 152 /// A materialization unit that builds lazy re-exports. These are callable 153 /// entry points that call through to the given symbols. 154 /// Unlike a 'true' re-export, the address of the lazy re-export will not 155 /// match the address of the re-exported symbol, but calling it will behave 156 /// the same as calling the re-exported symbol. 157 class LazyReexportsMaterializationUnit : public MaterializationUnit { 158 public: 159 LazyReexportsMaterializationUnit(LazyCallThroughManager &LCTManager, 160 IndirectStubsManager &ISManager, 161 JITDylib &SourceJD, 162 SymbolAliasMap CallableAliases, 163 VModuleKey K); 164 165 StringRef getName() const override; 166 167 private: 168 void materialize(MaterializationResponsibility R) override; 169 void discard(const JITDylib &JD, const SymbolStringPtr &Name) override; 170 static SymbolFlagsMap extractFlags(const SymbolAliasMap &Aliases); 171 172 LazyCallThroughManager &LCTManager; 173 IndirectStubsManager &ISManager; 174 JITDylib &SourceJD; 175 SymbolAliasMap CallableAliases; 176 std::shared_ptr<LazyCallThroughManager::NotifyResolvedFunction> 177 NotifyResolved; 178 }; 179 180 /// Define lazy-reexports based on the given SymbolAliasMap. Each lazy re-export 181 /// is a callable symbol that will look up and dispatch to the given aliasee on 182 /// first call. All subsequent calls will go directly to the aliasee. 183 inline std::unique_ptr<LazyReexportsMaterializationUnit> 184 lazyReexports(LazyCallThroughManager &LCTManager, 185 IndirectStubsManager &ISManager, JITDylib &SourceJD, 186 SymbolAliasMap CallableAliases, VModuleKey K = VModuleKey()) { 187 return llvm::make_unique<LazyReexportsMaterializationUnit>( 188 LCTManager, ISManager, SourceJD, std::move(CallableAliases), 189 std::move(K)); 190 } 191 192 } // End namespace orc 193 } // End namespace llvm 194 195 #endif // LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H 196