Line data Source code
1 : //===------ LazyReexports.h -- Utilities for lazy reexports -----*- C++ -*-===//
2 : //
3 : // The LLVM Compiler Infrastructure
4 : //
5 : // This file is distributed under the University of Illinois Open Source
6 : // License. See LICENSE.TXT for details.
7 : //
8 : //===----------------------------------------------------------------------===//
9 : //
10 : // Lazy re-exports are similar to normal re-exports, except that for callable
11 : // symbols the definitions are replaced with trampolines that will look up and
12 : // call through to the re-exported symbol at runtime. This can be used to
13 : // enable lazy compilation.
14 : //
15 : //===----------------------------------------------------------------------===//
16 :
17 : #ifndef LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H
18 : #define LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H
19 :
20 : #include "llvm/ExecutionEngine/Orc/Core.h"
21 : #include "llvm/ExecutionEngine/Orc/IndirectionUtils.h"
22 :
23 : namespace llvm {
24 :
25 : class Triple;
26 :
27 : namespace orc {
28 :
29 : /// Manages a set of 'lazy call-through' trampolines. These are compiler
30 : /// re-entry trampolines that are pre-bound to look up a given symbol in a given
31 : /// JITDylib, then jump to that address. Since compilation of symbols is
32 : /// triggered on first lookup, these call-through trampolines can be used to
33 : /// implement lazy compilation.
34 : ///
35 : /// The easiest way to construct these call-throughs is using the lazyReexport
36 : /// function.
37 : class LazyCallThroughManager {
38 : public:
39 : /// Clients will want to take some action on first resolution, e.g. updating
40 : /// a stub pointer. Instances of this class can be used to implement this.
41 : class NotifyResolvedFunction {
42 : public:
43 0 : virtual ~NotifyResolvedFunction() {}
44 :
45 : /// Called the first time a lazy call through is executed and the target
46 : /// symbol resolved.
47 : virtual Error operator()(JITDylib &SourceJD,
48 : const SymbolStringPtr &SymbolName,
49 : JITTargetAddress ResolvedAddr) = 0;
50 :
51 : private:
52 : virtual void anchor();
53 : };
54 :
55 : template <typename NotifyResolvedImpl>
56 0 : class NotifyResolvedFunctionImpl : public NotifyResolvedFunction {
57 : public:
58 23 : NotifyResolvedFunctionImpl(NotifyResolvedImpl NotifyResolved)
59 23 : : NotifyResolved(std::move(NotifyResolved)) {}
60 19 : Error operator()(JITDylib &SourceJD, const SymbolStringPtr &SymbolName,
61 : JITTargetAddress ResolvedAddr) {
62 20 : 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 llvm::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 :
84 : protected:
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 :
95 : private:
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.
111 0 : class LocalLazyCallThroughManager : public LazyCallThroughManager {
112 : private:
113 14 : LocalLazyCallThroughManager(ExecutionSession &ES,
114 : JITTargetAddress ErrorHandlerAddr)
115 28 : : LazyCallThroughManager(ES, ErrorHandlerAddr, nullptr) {}
116 :
117 14 : template <typename ORCABI> Error init() {
118 42 : auto TP = LocalTrampolinePool<ORCABI>::Create(
119 : [this](JITTargetAddress TrampolineAddr) {
120 20 : return callThroughToSymbol(TrampolineAddr);
121 : });
122 :
123 14 : if (!TP)
124 : return TP.takeError();
125 :
126 : setTrampolinePool(std::move(*TP));
127 : return Error::success();
128 : }
129 14 :
130 42 : public:
131 : /// Create a LocalLazyCallThroughManager using the given ABI. See
132 : /// createLocalLazyCallThroughManager.
133 : template <typename ORCABI>
134 : static Expected<std::unique_ptr<LocalLazyCallThroughManager>>
135 14 : 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 0 :
142 0 : return std::move(LLCTM);
143 : }
144 : };
145 :
146 : /// Create a LocalLazyCallThroughManager from the given triple and execution
147 0 : /// session.
148 : Expected<std::unique_ptr<LazyCallThroughManager>>
149 : createLocalLazyCallThroughManager(const Triple &T, ExecutionSession &ES,
150 : JITTargetAddress ErrorHandlerAddr);
151 :
152 : /// A materialization unit that builds lazy re-exports. These are callable
153 0 : /// entry points that call through to the given symbols.
154 0 : /// 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.
157 : class LazyReexportsMaterializationUnit : public MaterializationUnit {
158 : public:
159 0 : LazyReexportsMaterializationUnit(LazyCallThroughManager &LCTManager,
160 : IndirectStubsManager &ISManager,
161 : JITDylib &SourceJD,
162 : SymbolAliasMap CallableAliases,
163 : VModuleKey K);
164 :
165 0 : StringRef getName() const override;
166 0 :
167 : private:
168 : void materialize(MaterializationResponsibility R) override;
169 : void discard(const JITDylib &JD, const SymbolStringPtr &Name) override;
170 : static SymbolFlagsMap extractFlags(const SymbolAliasMap &Aliases);
171 0 :
172 : LazyCallThroughManager &LCTManager;
173 : IndirectStubsManager &ISManager;
174 : JITDylib &SourceJD;
175 : SymbolAliasMap CallableAliases;
176 : std::shared_ptr<LazyCallThroughManager::NotifyResolvedFunction>
177 0 : NotifyResolved;
178 0 : };
179 :
180 : /// Define lazy-reexports based on the given SymbolAliasMap. Each lazy re-export
181 : /// is a callable symbol that will look up and dispatch to the given aliasee on
182 : /// first call. All subsequent calls will go directly to the aliasee.
183 0 : inline std::unique_ptr<LazyReexportsMaterializationUnit>
184 : lazyReexports(LazyCallThroughManager &LCTManager,
185 : IndirectStubsManager &ISManager, JITDylib &SourceJD,
186 : SymbolAliasMap CallableAliases, VModuleKey K = VModuleKey()) {
187 : return llvm::make_unique<LazyReexportsMaterializationUnit>(
188 : LCTManager, ISManager, SourceJD, std::move(CallableAliases),
189 13 : std::move(K));
190 0 : }
191 :
192 : } // End namespace orc
193 : } // End namespace llvm
194 :
195 0 : #endif // LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H
|