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(JITTargetAddress ResolvedAddr)>;
42 
43   LazyCallThroughManager(ExecutionSession &ES,
44                          JITTargetAddress ErrorHandlerAddr, 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<JITTargetAddress>
49   getCallThroughTrampoline(JITDylib &SourceJD, SymbolStringPtr SymbolName,
50                            NotifyResolvedFunction NotifyResolved);
51 
52   void resolveTrampolineLandingAddress(
53       JITTargetAddress 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   JITTargetAddress reportCallThroughError(Error Err);
68   Expected<ReexportsEntry> findReexport(JITTargetAddress TrampolineAddr);
69   Error notifyResolved(JITTargetAddress TrampolineAddr,
70                        JITTargetAddress ResolvedAddr);
71   void setTrampolinePool(TrampolinePool &TP) { this->TP = &TP; }
72 
73 private:
74   using ReexportsMap = std::map<JITTargetAddress, ReexportsEntry>;
75 
76   using NotifiersMap = std::map<JITTargetAddress, NotifyResolvedFunction>;
77 
78   std::mutex LCTMMutex;
79   ExecutionSession &ES;
80   JITTargetAddress ErrorHandlerAddr;
81   TrampolinePool *TP = nullptr;
82   ReexportsMap Reexports;
83   NotifiersMap Notifiers;
84 };
85 
86 /// A lazy call-through manager that builds trampolines in the current process.
87 class LocalLazyCallThroughManager : public LazyCallThroughManager {
88 private:
89   using NotifyTargetResolved = unique_function<void(JITTargetAddress)>;
90 
91   LocalLazyCallThroughManager(ExecutionSession &ES,
92                               JITTargetAddress ErrorHandlerAddr)
93       : LazyCallThroughManager(ES, ErrorHandlerAddr, nullptr) {}
94 
95   template <typename ORCABI> Error init() {
96     auto TP = LocalTrampolinePool<ORCABI>::Create(
97         [this](JITTargetAddress TrampolineAddr,
98                TrampolinePool::NotifyLandingResolvedFunction
99                    NotifyLandingResolved) {
100           resolveTrampolineLandingAddress(TrampolineAddr,
101                                           std::move(NotifyLandingResolved));
102         });
103 
104     if (!TP)
105       return TP.takeError();
106 
107     this->TP = std::move(*TP);
108     setTrampolinePool(*this->TP);
109     return Error::success();
110   }
111 
112   std::unique_ptr<TrampolinePool> TP;
113 
114 public:
115   /// Create a LocalLazyCallThroughManager using the given ABI. See
116   /// createLocalLazyCallThroughManager.
117   template <typename ORCABI>
118   static Expected<std::unique_ptr<LocalLazyCallThroughManager>>
119   Create(ExecutionSession &ES, JITTargetAddress ErrorHandlerAddr) {
120     auto LLCTM = std::unique_ptr<LocalLazyCallThroughManager>(
121         new LocalLazyCallThroughManager(ES, ErrorHandlerAddr));
122 
123     if (auto Err = LLCTM->init<ORCABI>())
124       return std::move(Err);
125 
126     return std::move(LLCTM);
127   }
128 };
129 
130 /// Create a LocalLazyCallThroughManager from the given triple and execution
131 /// session.
132 Expected<std::unique_ptr<LazyCallThroughManager>>
133 createLocalLazyCallThroughManager(const Triple &T, ExecutionSession &ES,
134                                   JITTargetAddress ErrorHandlerAddr);
135 
136 /// A materialization unit that builds lazy re-exports. These are callable
137 /// entry points that call through to the given symbols.
138 /// Unlike a 'true' re-export, the address of the lazy re-export will not
139 /// match the address of the re-exported symbol, but calling it will behave
140 /// the same as calling the re-exported symbol.
141 class LazyReexportsMaterializationUnit : public MaterializationUnit {
142 public:
143   LazyReexportsMaterializationUnit(LazyCallThroughManager &LCTManager,
144                                    IndirectStubsManager &ISManager,
145                                    JITDylib &SourceJD,
146                                    SymbolAliasMap CallableAliases,
147                                    ImplSymbolMap *SrcJDLoc);
148 
149   StringRef getName() const override;
150 
151 private:
152   void materialize(std::unique_ptr<MaterializationResponsibility> R) override;
153   void discard(const JITDylib &JD, const SymbolStringPtr &Name) override;
154   static MaterializationUnit::Interface
155   extractFlags(const SymbolAliasMap &Aliases);
156 
157   LazyCallThroughManager &LCTManager;
158   IndirectStubsManager &ISManager;
159   JITDylib &SourceJD;
160   SymbolAliasMap CallableAliases;
161   ImplSymbolMap *AliaseeTable;
162 };
163 
164 /// Define lazy-reexports based on the given SymbolAliasMap. Each lazy re-export
165 /// is a callable symbol that will look up and dispatch to the given aliasee on
166 /// first call. All subsequent calls will go directly to the aliasee.
167 inline std::unique_ptr<LazyReexportsMaterializationUnit>
168 lazyReexports(LazyCallThroughManager &LCTManager,
169               IndirectStubsManager &ISManager, JITDylib &SourceJD,
170               SymbolAliasMap CallableAliases,
171               ImplSymbolMap *SrcJDLoc = nullptr) {
172   return std::make_unique<LazyReexportsMaterializationUnit>(
173       LCTManager, ISManager, SourceJD, std::move(CallableAliases), SrcJDLoc);
174 }
175 
176 } // End namespace orc
177 } // End namespace llvm
178 
179 #endif // LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H
180