1 //===------ LazyReexports.h -- Utilities for lazy reexports -----*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Lazy re-exports are similar to normal re-exports, except that for callable 10 // symbols the definitions are replaced with trampolines that will look up and 11 // call through to the re-exported symbol at runtime. This can be used to 12 // enable lazy compilation. 13 // 14 //===----------------------------------------------------------------------===// 15 16 #ifndef LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H 17 #define LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H 18 19 #include "llvm/ADT/STLExtras.h" 20 #include "llvm/ExecutionEngine/Orc/Core.h" 21 #include "llvm/ExecutionEngine/Orc/IndirectionUtils.h" 22 #include "llvm/ExecutionEngine/Orc/RedirectionManager.h" 23 #include "llvm/ExecutionEngine/Orc/Speculation.h" 24 25 namespace llvm { 26 27 class Triple; 28 29 namespace orc { 30 31 /// Manages a set of 'lazy call-through' trampolines. These are compiler 32 /// re-entry trampolines that are pre-bound to look up a given symbol in a given 33 /// JITDylib, then jump to that address. Since compilation of symbols is 34 /// triggered on first lookup, these call-through trampolines can be used to 35 /// implement lazy compilation. 36 /// 37 /// The easiest way to construct these call-throughs is using the lazyReexport 38 /// function. 39 class LazyCallThroughManager { 40 public: 41 using NotifyResolvedFunction = 42 unique_function<Error(ExecutorAddr ResolvedAddr)>; 43 44 LazyCallThroughManager(ExecutionSession &ES, ExecutorAddr ErrorHandlerAddr, 45 TrampolinePool *TP); 46 47 // Return a free call-through trampoline and bind it to look up and call 48 // through to the given symbol. 49 Expected<ExecutorAddr> 50 getCallThroughTrampoline(JITDylib &SourceJD, SymbolStringPtr SymbolName, 51 NotifyResolvedFunction NotifyResolved); 52 53 void resolveTrampolineLandingAddress( 54 ExecutorAddr TrampolineAddr, 55 TrampolinePool::NotifyLandingResolvedFunction NotifyLandingResolved); 56 57 virtual ~LazyCallThroughManager() = default; 58 59 protected: 60 using NotifyLandingResolvedFunction = 61 TrampolinePool::NotifyLandingResolvedFunction; 62 63 struct ReexportsEntry { 64 JITDylib *SourceJD; 65 SymbolStringPtr SymbolName; 66 }; 67 68 ExecutorAddr reportCallThroughError(Error Err); 69 Expected<ReexportsEntry> findReexport(ExecutorAddr TrampolineAddr); 70 Error notifyResolved(ExecutorAddr TrampolineAddr, ExecutorAddr ResolvedAddr); 71 void setTrampolinePool(TrampolinePool &TP) { this->TP = &TP; } 72 73 private: 74 using ReexportsMap = std::map<ExecutorAddr, ReexportsEntry>; 75 76 using NotifiersMap = std::map<ExecutorAddr, NotifyResolvedFunction>; 77 78 std::mutex LCTMMutex; 79 ExecutionSession &ES; 80 ExecutorAddr ErrorHandlerAddr; 81 TrampolinePool *TP = nullptr; 82 ReexportsMap Reexports; 83 NotifiersMap Notifiers; 84 }; 85 86 /// A lazy call-through manager that builds trampolines in the current process. 87 class LocalLazyCallThroughManager : public LazyCallThroughManager { 88 private: 89 using NotifyTargetResolved = unique_function<void(ExecutorAddr)>; 90 91 LocalLazyCallThroughManager(ExecutionSession &ES, 92 ExecutorAddr ErrorHandlerAddr) 93 : LazyCallThroughManager(ES, ErrorHandlerAddr, nullptr) {} 94 95 template <typename ORCABI> Error init() { 96 auto TP = LocalTrampolinePool<ORCABI>::Create( 97 [this](ExecutorAddr TrampolineAddr, 98 TrampolinePool::NotifyLandingResolvedFunction 99 NotifyLandingResolved) { 100 resolveTrampolineLandingAddress(TrampolineAddr, 101 std::move(NotifyLandingResolved)); 102 }); 103 104 if (!TP) 105 return TP.takeError(); 106 107 this->TP = std::move(*TP); 108 setTrampolinePool(*this->TP); 109 return Error::success(); 110 } 111 112 std::unique_ptr<TrampolinePool> TP; 113 114 public: 115 /// Create a LocalLazyCallThroughManager using the given ABI. See 116 /// createLocalLazyCallThroughManager. 117 template <typename ORCABI> 118 static Expected<std::unique_ptr<LocalLazyCallThroughManager>> 119 Create(ExecutionSession &ES, ExecutorAddr ErrorHandlerAddr) { 120 auto LLCTM = std::unique_ptr<LocalLazyCallThroughManager>( 121 new LocalLazyCallThroughManager(ES, ErrorHandlerAddr)); 122 123 if (auto Err = LLCTM->init<ORCABI>()) 124 return std::move(Err); 125 126 return std::move(LLCTM); 127 } 128 }; 129 130 /// Create a LocalLazyCallThroughManager from the given triple and execution 131 /// session. 132 Expected<std::unique_ptr<LazyCallThroughManager>> 133 createLocalLazyCallThroughManager(const Triple &T, ExecutionSession &ES, 134 ExecutorAddr ErrorHandlerAddr); 135 136 /// A materialization unit that builds lazy re-exports. These are callable 137 /// entry points that call through to the given symbols. 138 /// Unlike a 'true' re-export, the address of the lazy re-export will not 139 /// match the address of the re-exported symbol, but calling it will behave 140 /// the same as calling the re-exported symbol. 141 class LazyReexportsMaterializationUnit : public MaterializationUnit { 142 public: 143 LazyReexportsMaterializationUnit(LazyCallThroughManager &LCTManager, 144 RedirectableSymbolManager &RSManager, 145 JITDylib &SourceJD, 146 SymbolAliasMap CallableAliases, 147 ImplSymbolMap *SrcJDLoc); 148 149 StringRef getName() const override; 150 151 private: 152 void materialize(std::unique_ptr<MaterializationResponsibility> R) override; 153 void discard(const JITDylib &JD, const SymbolStringPtr &Name) override; 154 static MaterializationUnit::Interface 155 extractFlags(const SymbolAliasMap &Aliases); 156 157 LazyCallThroughManager &LCTManager; 158 RedirectableSymbolManager &RSManager; 159 JITDylib &SourceJD; 160 SymbolAliasMap CallableAliases; 161 ImplSymbolMap *AliaseeTable; 162 }; 163 164 /// Define lazy-reexports based on the given SymbolAliasMap. Each lazy re-export 165 /// is a callable symbol that will look up and dispatch to the given aliasee on 166 /// first call. All subsequent calls will go directly to the aliasee. 167 inline std::unique_ptr<LazyReexportsMaterializationUnit> 168 lazyReexports(LazyCallThroughManager &LCTManager, 169 RedirectableSymbolManager &RSManager, JITDylib &SourceJD, 170 SymbolAliasMap CallableAliases, 171 ImplSymbolMap *SrcJDLoc = nullptr) { 172 return std::make_unique<LazyReexportsMaterializationUnit>( 173 LCTManager, RSManager, SourceJD, std::move(CallableAliases), SrcJDLoc); 174 } 175 176 class LazyReexportsManager : public ResourceManager { 177 178 friend std::unique_ptr<MaterializationUnit> 179 lazyReexports(LazyReexportsManager &, SymbolAliasMap); 180 181 public: 182 struct CallThroughInfo { 183 JITDylibSP JD; 184 SymbolStringPtr Name; 185 SymbolStringPtr BodyName; 186 }; 187 188 class Listener { 189 public: 190 using CallThroughInfo = LazyReexportsManager::CallThroughInfo; 191 192 virtual ~Listener(); 193 194 /// Called under the session lock when new lazy reexports are created. 195 virtual void onLazyReexportsCreated(JITDylib &JD, ResourceKey K, 196 const SymbolAliasMap &Reexports) = 0; 197 198 /// Called under the session lock when lazy reexports have their ownership 199 /// transferred to a new ResourceKey. 200 virtual void onLazyReexportsTransfered(JITDylib &JD, ResourceKey DstK, 201 ResourceKey SrcK) = 0; 202 203 /// Called under the session lock when lazy reexports are removed. 204 virtual Error onLazyReexportsRemoved(JITDylib &JD, ResourceKey K) = 0; 205 206 /// Called outside the session lock when a lazy reexport is called. 207 /// NOTE: Since this is called outside the session lock there is a chance 208 /// that the reexport referred to has already been removed. Listeners 209 /// must be prepared to handle requests for stale reexports. 210 virtual void onLazyReexportCalled(const CallThroughInfo &CTI) = 0; 211 }; 212 213 using OnTrampolinesReadyFn = unique_function<void( 214 Expected<std::vector<ExecutorSymbolDef>> EntryAddrs)>; 215 using EmitTrampolinesFn = 216 unique_function<void(ResourceTrackerSP RT, size_t NumTrampolines, 217 OnTrampolinesReadyFn OnTrampolinesReady)>; 218 219 /// Create a LazyReexportsManager that uses the ORC runtime for reentry. 220 /// This will work both in-process and out-of-process. 221 static Expected<std::unique_ptr<LazyReexportsManager>> 222 Create(EmitTrampolinesFn EmitTrampolines, RedirectableSymbolManager &RSMgr, 223 JITDylib &PlatformJD, Listener *L = nullptr); 224 225 LazyReexportsManager(LazyReexportsManager &&) = delete; 226 LazyReexportsManager &operator=(LazyReexportsManager &&) = delete; 227 228 Error handleRemoveResources(JITDylib &JD, ResourceKey K) override; 229 void handleTransferResources(JITDylib &JD, ResourceKey DstK, 230 ResourceKey SrcK) override; 231 232 private: 233 class MU; 234 class Plugin; 235 236 using ResolveSendResultFn = 237 unique_function<void(Expected<ExecutorSymbolDef>)>; 238 239 LazyReexportsManager(EmitTrampolinesFn EmitTrampolines, 240 RedirectableSymbolManager &RSMgr, JITDylib &PlatformJD, 241 Listener *L, Error &Err); 242 243 std::unique_ptr<MaterializationUnit> 244 createLazyReexports(SymbolAliasMap Reexports); 245 246 void emitReentryTrampolines(std::unique_ptr<MaterializationResponsibility> MR, 247 SymbolAliasMap Reexports); 248 void emitRedirectableSymbols( 249 std::unique_ptr<MaterializationResponsibility> MR, 250 SymbolAliasMap Reexports, 251 Expected<std::vector<ExecutorSymbolDef>> ReentryPoints); 252 void resolve(ResolveSendResultFn SendResult, ExecutorAddr ReentryStubAddr); 253 254 ExecutionSession &ES; 255 EmitTrampolinesFn EmitTrampolines; 256 RedirectableSymbolManager &RSMgr; 257 Listener *L; 258 259 DenseMap<ResourceKey, std::vector<ExecutorAddr>> KeyToReentryAddrs; 260 DenseMap<ExecutorAddr, CallThroughInfo> CallThroughs; 261 }; 262 263 /// Define lazy-reexports based on the given SymbolAliasMap. Each lazy re-export 264 /// is a callable symbol that will look up and dispatch to the given aliasee on 265 /// first call. All subsequent calls will go directly to the aliasee. 266 inline std::unique_ptr<MaterializationUnit> 267 lazyReexports(LazyReexportsManager &LRM, SymbolAliasMap Reexports) { 268 return LRM.createLazyReexports(std::move(Reexports)); 269 } 270 271 class SimpleLazyReexportsSpeculator : public LazyReexportsManager::Listener { 272 public: 273 using RecordExecutionFunction = 274 unique_function<void(const CallThroughInfo &CTI)>; 275 276 static std::shared_ptr<SimpleLazyReexportsSpeculator> 277 Create(ExecutionSession &ES, RecordExecutionFunction RecordExec = {}) { 278 class make_shared_helper : public SimpleLazyReexportsSpeculator { 279 public: 280 make_shared_helper(ExecutionSession &ES, 281 RecordExecutionFunction RecordExec) 282 : SimpleLazyReexportsSpeculator(ES, std::move(RecordExec)) {} 283 }; 284 285 auto Instance = 286 std::make_shared<make_shared_helper>(ES, std::move(RecordExec)); 287 Instance->WeakThis = Instance; 288 return Instance; 289 } 290 291 SimpleLazyReexportsSpeculator(SimpleLazyReexportsSpeculator &&) = delete; 292 SimpleLazyReexportsSpeculator & 293 operator=(SimpleLazyReexportsSpeculator &&) = delete; 294 ~SimpleLazyReexportsSpeculator() override; 295 296 void onLazyReexportsCreated(JITDylib &JD, ResourceKey K, 297 const SymbolAliasMap &Reexports) override; 298 299 void onLazyReexportsTransfered(JITDylib &JD, ResourceKey DstK, 300 ResourceKey SrcK) override; 301 302 Error onLazyReexportsRemoved(JITDylib &JD, ResourceKey K) override; 303 304 void onLazyReexportCalled(const CallThroughInfo &CTI) override; 305 306 void addSpeculationSuggestions( 307 std::vector<std::pair<std::string, SymbolStringPtr>> NewSuggestions); 308 309 private: 310 SimpleLazyReexportsSpeculator(ExecutionSession &ES, 311 RecordExecutionFunction RecordExec) 312 : ES(ES), RecordExec(std::move(RecordExec)) {} 313 314 bool doNextSpeculativeLookup(); 315 316 class SpeculateTask; 317 318 using KeyToFunctionBodiesMap = 319 DenseMap<ResourceKey, std::vector<SymbolStringPtr>>; 320 321 ExecutionSession &ES; 322 RecordExecutionFunction RecordExec; 323 std::weak_ptr<SimpleLazyReexportsSpeculator> WeakThis; 324 DenseMap<JITDylib *, KeyToFunctionBodiesMap> LazyReexports; 325 std::deque<std::pair<std::string, SymbolStringPtr>> SpeculateSuggestions; 326 bool SpeculateTaskActive = false; 327 }; 328 329 } // End namespace orc 330 } // End namespace llvm 331 332 #endif // LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H 333