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 | NotifyLandingResolved: 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 | (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 | args&: LCTManager, args&: ISManager, args&: SourceJD, args: std::move(CallableAliases), args&: SrcJDLoc); |
173 | } |
174 | |
175 | } // End namespace orc |
176 | } // End namespace llvm |
177 | |
178 | #endif // LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H |
179 | |