1 //===------ LazyReexports.h -- Utilities for lazy reexports -----*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
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.
14 //===----------------------------------------------------------------------===//
16 #ifndef LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H
17 #define LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H
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"
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.
36 /// The easiest way to construct these call-throughs is using the lazyReexport
38 class LazyCallThroughManager {
40 using NotifyResolvedFunction =
41 unique_function<Error(ExecutorAddr ResolvedAddr)>;
43 LazyCallThroughManager(ExecutionSession &ES, ExecutorAddr ErrorHandlerAddr,
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);
52 void resolveTrampolineLandingAddress(
53 ExecutorAddr TrampolineAddr,
54 TrampolinePool::NotifyLandingResolvedFunction NotifyLandingResolved);
56 virtual ~LazyCallThroughManager() = default;
59 using NotifyLandingResolvedFunction =
60 TrampolinePool::NotifyLandingResolvedFunction;
62 struct ReexportsEntry {
64 SymbolStringPtr SymbolName;
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; }
73 using ReexportsMap = std::map<ExecutorAddr, ReexportsEntry>;
75 using NotifiersMap = std::map<ExecutorAddr, NotifyResolvedFunction>;
79 ExecutorAddr ErrorHandlerAddr;
80 TrampolinePool *TP = nullptr;
81 ReexportsMap Reexports;
82 NotifiersMap Notifiers;
85 /// A lazy call-through manager that builds trampolines in the current process.
86 class LocalLazyCallThroughManager : public LazyCallThroughManager {
88 using NotifyTargetResolved = unique_function<void(ExecutorAddr)>;
90 LocalLazyCallThroughManager(ExecutionSession &ES,
91 ExecutorAddr ErrorHandlerAddr)
92 : LazyCallThroughManager(ES, ErrorHandlerAddr, nullptr) {}
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 std::move(NotifyLandingResolved));
104 return TP.takeError();
106 this->TP = std::move(*TP);
107 setTrampolinePool(*this->TP);
108 return Error::success();
111 std::unique_ptr<TrampolinePool> TP;
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));
122 if (auto Err = LLCTM->init<ORCABI>())
123 return std::move(Err);
125 return std::move(LLCTM);
129 /// Create a LocalLazyCallThroughManager from the given triple and execution
131 Expected<std::unique_ptr<LazyCallThroughManager>>
132 createLocalLazyCallThroughManager(const Triple &T, ExecutionSession &ES,
133 ExecutorAddr ErrorHandlerAddr);
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 {
142 LazyReexportsMaterializationUnit(LazyCallThroughManager &LCTManager,
143 IndirectStubsManager &ISManager,
145 SymbolAliasMap CallableAliases,
146 ImplSymbolMap *SrcJDLoc);
148 StringRef getName() const override;
151 void materialize(std::unique_ptr<MaterializationResponsibility> R) override;
152 void discard(const JITDylib &JD, const SymbolStringPtr &Name) override;
153 static MaterializationUnit::Interface
154 extractFlags(const SymbolAliasMap &Aliases);
156 LazyCallThroughManager &LCTManager;
157 IndirectStubsManager &ISManager;
159 SymbolAliasMap CallableAliases;
160 ImplSymbolMap *AliaseeTable;
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 LCTManager, ISManager, SourceJD, std::move(CallableAliases), SrcJDLoc);
175 } // End namespace orc
176 } // End namespace llvm
178 #endif // LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H