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