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/Speculation.h" 23 24 namespace llvm { 25 26 class Triple; 27 28 namespace orc { 29 30 /// Manages a set of 'lazy call-through' trampolines. These are compiler 31 /// re-entry trampolines that are pre-bound to look up a given symbol in a given 32 /// JITDylib, then jump to that address. Since compilation of symbols is 33 /// triggered on first lookup, these call-through trampolines can be used to 34 /// implement lazy compilation. 35 /// 36 /// The easiest way to construct these call-throughs is using the lazyReexport 37 /// function. 38 class LazyCallThroughManager { 39 public: 40 using NotifyResolvedFunction = 41 unique_function<Error(ExecutorAddr ResolvedAddr)>; 42 43 LazyCallThroughManager(ExecutionSession &ES, ExecutorAddr ErrorHandlerAddr, 44 TrampolinePool *TP); 45 46 // Return a free call-through trampoline and bind it to look up and call 47 // through to the given symbol. 48 Expected<ExecutorAddr> 49 getCallThroughTrampoline(JITDylib &SourceJD, SymbolStringPtr SymbolName, 50 NotifyResolvedFunction NotifyResolved); 51 52 void resolveTrampolineLandingAddress( 53 ExecutorAddr TrampolineAddr, 54 TrampolinePool::NotifyLandingResolvedFunction NotifyLandingResolved); 55 56 virtual ~LazyCallThroughManager() = default; 57 58 protected: 59 using NotifyLandingResolvedFunction = 60 TrampolinePool::NotifyLandingResolvedFunction; 61 62 struct ReexportsEntry { 63 JITDylib *SourceJD; 64 SymbolStringPtr SymbolName; 65 }; 66 67 ExecutorAddr reportCallThroughError(Error Err); 68 Expected<ReexportsEntry> findReexport(ExecutorAddr TrampolineAddr); 69 Error notifyResolved(ExecutorAddr TrampolineAddr, ExecutorAddr ResolvedAddr); 70 void setTrampolinePool(TrampolinePool &TP) { this->TP = &TP; } 71 72 private: 73 using ReexportsMap = std::map<ExecutorAddr, ReexportsEntry>; 74 75 using NotifiersMap = std::map<ExecutorAddr, NotifyResolvedFunction>; 76 77 std::mutex LCTMMutex; 78 ExecutionSession &ES; 79 ExecutorAddr ErrorHandlerAddr; 80 TrampolinePool *TP = nullptr; 81 ReexportsMap Reexports; 82 NotifiersMap Notifiers; 83 }; 84 85 /// A lazy call-through manager that builds trampolines in the current process. 86 class LocalLazyCallThroughManager : public LazyCallThroughManager { 87 private: 88 using NotifyTargetResolved = unique_function<void(ExecutorAddr)>; 89 90 LocalLazyCallThroughManager(ExecutionSession &ES, 91 ExecutorAddr ErrorHandlerAddr) 92 : LazyCallThroughManager(ES, ErrorHandlerAddr, nullptr) {} 93 94 template <typename ORCABI> Error init() { 95 auto TP = LocalTrampolinePool<ORCABI>::Create( 96 [this](ExecutorAddr TrampolineAddr, 97 TrampolinePool::NotifyLandingResolvedFunction 98 NotifyLandingResolved) { 99 resolveTrampolineLandingAddress(TrampolineAddr, 100 std::move(NotifyLandingResolved)); 101 }); 102 103 if (!TP) 104 return TP.takeError(); 105 106 this->TP = std::move(*TP); 107 setTrampolinePool(*this->TP); 108 return Error::success(); 109 } 110 111 std::unique_ptr<TrampolinePool> TP; 112 113 public: 114 /// Create a LocalLazyCallThroughManager using the given ABI. See 115 /// createLocalLazyCallThroughManager. 116 template <typename ORCABI> 117 static Expected<std::unique_ptr<LocalLazyCallThroughManager>> 118 Create(ExecutionSession &ES, ExecutorAddr ErrorHandlerAddr) { 119 auto LLCTM = std::unique_ptr<LocalLazyCallThroughManager>( 120 new LocalLazyCallThroughManager(ES, ErrorHandlerAddr)); 121 122 if (auto Err = LLCTM->init<ORCABI>()) 123 return std::move(Err); 124 125 return std::move(LLCTM); 126 } 127 }; 128 129 /// Create a LocalLazyCallThroughManager from the given triple and execution 130 /// session. 131 Expected<std::unique_ptr<LazyCallThroughManager>> 132 createLocalLazyCallThroughManager(const Triple &T, ExecutionSession &ES, 133 ExecutorAddr ErrorHandlerAddr); 134 135 /// A materialization unit that builds lazy re-exports. These are callable 136 /// entry points that call through to the given symbols. 137 /// Unlike a 'true' re-export, the address of the lazy re-export will not 138 /// match the address of the re-exported symbol, but calling it will behave 139 /// the same as calling the re-exported symbol. 140 class LazyReexportsMaterializationUnit : public MaterializationUnit { 141 public: 142 LazyReexportsMaterializationUnit(LazyCallThroughManager &LCTManager, 143 IndirectStubsManager &ISManager, 144 JITDylib &SourceJD, 145 SymbolAliasMap CallableAliases, 146 ImplSymbolMap *SrcJDLoc); 147 148 StringRef getName() const override; 149 150 private: 151 void materialize(std::unique_ptr<MaterializationResponsibility> R) override; 152 void discard(const JITDylib &JD, const SymbolStringPtr &Name) override; 153 static MaterializationUnit::Interface 154 extractFlags(const SymbolAliasMap &Aliases); 155 156 LazyCallThroughManager &LCTManager; 157 IndirectStubsManager &ISManager; 158 JITDylib &SourceJD; 159 SymbolAliasMap CallableAliases; 160 ImplSymbolMap *AliaseeTable; 161 }; 162 163 /// Define lazy-reexports based on the given SymbolAliasMap. Each lazy re-export 164 /// is a callable symbol that will look up and dispatch to the given aliasee on 165 /// first call. All subsequent calls will go directly to the aliasee. 166 inline std::unique_ptr<LazyReexportsMaterializationUnit> 167 lazyReexports(LazyCallThroughManager &LCTManager, 168 IndirectStubsManager &ISManager, JITDylib &SourceJD, 169 SymbolAliasMap CallableAliases, 170 ImplSymbolMap *SrcJDLoc = nullptr) { 171 return std::make_unique<LazyReexportsMaterializationUnit>( 172 LCTManager, ISManager, SourceJD, std::move(CallableAliases), SrcJDLoc); 173 } 174 175 } // End namespace orc 176 } // End namespace llvm 177 178 #endif // LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H 179