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(JITTargetAddress ResolvedAddr)>; 42 43 LazyCallThroughManager(ExecutionSession &ES, 44 JITTargetAddress ErrorHandlerAddr, 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<JITTargetAddress> 49 getCallThroughTrampoline(JITDylib &SourceJD, SymbolStringPtr SymbolName, 50 NotifyResolvedFunction NotifyResolved); 51 52 void resolveTrampolineLandingAddress( 53 JITTargetAddress 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 JITTargetAddress reportCallThroughError(Error Err); 68 Expected<ReexportsEntry> findReexport(JITTargetAddress TrampolineAddr); 69 Error notifyResolved(JITTargetAddress TrampolineAddr, 70 JITTargetAddress ResolvedAddr); 71 void setTrampolinePool(TrampolinePool &TP) { this->TP = &TP; } 72 73 private: 74 using ReexportsMap = std::map<JITTargetAddress, ReexportsEntry>; 75 76 using NotifiersMap = std::map<JITTargetAddress, NotifyResolvedFunction>; 77 78 std::mutex LCTMMutex; 79 ExecutionSession &ES; 80 JITTargetAddress 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(JITTargetAddress)>; 90 91 LocalLazyCallThroughManager(ExecutionSession &ES, 92 JITTargetAddress ErrorHandlerAddr) 93 : LazyCallThroughManager(ES, ErrorHandlerAddr, nullptr) {} 94 95 template <typename ORCABI> Error init() { 96 auto TP = LocalTrampolinePool<ORCABI>::Create( 97 [this](JITTargetAddress 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, JITTargetAddress 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 JITTargetAddress 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 IndirectStubsManager &ISManager, 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 IndirectStubsManager &ISManager; 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 IndirectStubsManager &ISManager, JITDylib &SourceJD, 170 SymbolAliasMap CallableAliases, 171 ImplSymbolMap *SrcJDLoc = nullptr) { 172 return std::make_unique<LazyReexportsMaterializationUnit>( 173 LCTManager, ISManager, SourceJD, std::move(CallableAliases), SrcJDLoc); 174 } 175 176 } // End namespace orc 177 } // End namespace llvm 178 179 #endif // LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H 180