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