LazyReexports.h revision 360784
1255376Sdes//===------ LazyReexports.h -- Utilities for lazy reexports -----*- C++ -*-===// 2348980Sdes// 3348980Sdes// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 491094Sdes// See https://llvm.org/LICENSE.txt for license information. 591094Sdes// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 691094Sdes// 791094Sdes//===----------------------------------------------------------------------===// 891094Sdes// 991094Sdes// Lazy re-exports are similar to normal re-exports, except that for callable 10108794Sdes// symbols the definitions are replaced with trampolines that will look up and 1191094Sdes// call through to the re-exported symbol at runtime. This can be used to 12117610Sdes// enable lazy compilation. 13174832Sdes// 1491094Sdes//===----------------------------------------------------------------------===// 1591094Sdes 16236109Sdes#ifndef LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H 1791100Sdes#define LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H 1891100Sdes 1991100Sdes#include "llvm/ExecutionEngine/Orc/Core.h" 2091094Sdes#include "llvm/ExecutionEngine/Orc/IndirectionUtils.h" 2191094Sdes#include "llvm/ExecutionEngine/Orc/Speculation.h" 22236109Sdes 2391094Sdesnamespace llvm { 2491100Sdes 25348980Sdesclass Triple; 26348980Sdes 2791100Sdesnamespace orc { 2891100Sdes 2991100Sdes/// Manages a set of 'lazy call-through' trampolines. These are compiler 3091100Sdes/// re-entry trampolines that are pre-bound to look up a given symbol in a given 3191100Sdes/// JITDylib, then jump to that address. Since compilation of symbols is 3291100Sdes/// triggered on first lookup, these call-through trampolines can be used to 3391094Sdes/// implement lazy compilation. 3491094Sdes/// 3591100Sdes/// The easiest way to construct these call-throughs is using the lazyReexport 3691100Sdes/// function. 3791100Sdesclass LazyCallThroughManager { 3891094Sdespublic: 3991100Sdes /// Clients will want to take some action on first resolution, e.g. updating 4091094Sdes /// a stub pointer. Instances of this class can be used to implement this. 4191100Sdes class NotifyResolvedFunction { 42236109Sdes public: 4391100Sdes virtual ~NotifyResolvedFunction() {} 4491094Sdes 4591094Sdes /// Called the first time a lazy call through is executed and the target 46236109Sdes /// symbol resolved. 47236109Sdes virtual Error operator()(JITDylib &SourceJD, 48236109Sdes const SymbolStringPtr &SymbolName, 49117610Sdes JITTargetAddress ResolvedAddr) = 0; 50236109Sdes 51147466Sdes private: 52117610Sdes virtual void anchor(); 5391094Sdes }; 5491094Sdes 55255376Sdes template <typename NotifyResolvedImpl> 56255376Sdes class NotifyResolvedFunctionImpl : public NotifyResolvedFunction { 57348980Sdes public: 58 NotifyResolvedFunctionImpl(NotifyResolvedImpl NotifyResolved) 59 : NotifyResolved(std::move(NotifyResolved)) {} 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> 73 createNotifyResolvedFunction(NotifyResolvedImpl NotifyResolved) { 74 return std::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 84protected: 85 LazyCallThroughManager(ExecutionSession &ES, 86 JITTargetAddress ErrorHandlerAddr, 87 std::unique_ptr<TrampolinePool> TP); 88 89 JITTargetAddress callThroughToSymbol(JITTargetAddress TrampolineAddr); 90 91 void setTrampolinePool(std::unique_ptr<TrampolinePool> TP) { 92 this->TP = std::move(TP); 93 } 94 95private: 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. 111class LocalLazyCallThroughManager : public LazyCallThroughManager { 112private: 113 LocalLazyCallThroughManager(ExecutionSession &ES, 114 JITTargetAddress ErrorHandlerAddr) 115 : LazyCallThroughManager(ES, ErrorHandlerAddr, nullptr) {} 116 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 130public: 131 /// Create a LocalLazyCallThroughManager using the given ABI. See 132 /// createLocalLazyCallThroughManager. 133 template <typename ORCABI> 134 static Expected<std::unique_ptr<LocalLazyCallThroughManager>> 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. 148Expected<std::unique_ptr<LazyCallThroughManager>> 149createLocalLazyCallThroughManager(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. 157class LazyReexportsMaterializationUnit : public MaterializationUnit { 158public: 159 LazyReexportsMaterializationUnit(LazyCallThroughManager &LCTManager, 160 IndirectStubsManager &ISManager, 161 JITDylib &SourceJD, 162 SymbolAliasMap CallableAliases, 163 ImplSymbolMap *SrcJDLoc, VModuleKey K); 164 165 StringRef getName() const override; 166 167private: 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 ImplSymbolMap *AliaseeTable; 179}; 180 181/// Define lazy-reexports based on the given SymbolAliasMap. Each lazy re-export 182/// is a callable symbol that will look up and dispatch to the given aliasee on 183/// first call. All subsequent calls will go directly to the aliasee. 184inline std::unique_ptr<LazyReexportsMaterializationUnit> 185lazyReexports(LazyCallThroughManager &LCTManager, 186 IndirectStubsManager &ISManager, JITDylib &SourceJD, 187 SymbolAliasMap CallableAliases, ImplSymbolMap *SrcJDLoc = nullptr, 188 VModuleKey K = VModuleKey()) { 189 return std::make_unique<LazyReexportsMaterializationUnit>( 190 LCTManager, ISManager, SourceJD, std::move(CallableAliases), SrcJDLoc, 191 std::move(K)); 192} 193 194} // End namespace orc 195} // End namespace llvm 196 197#endif // LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H 198