LazyReexports.h revision 360784
1255376Sdes//===------ LazyReexports.h -- Utilities for lazy reexports -----*- C++ -*-===//
2348980Sdes//
3348980Sdes// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
491094Sdes// See https://llvm.org/LICENSE.txt for license information.
591094Sdes// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
691094Sdes//
791094Sdes//===----------------------------------------------------------------------===//
891094Sdes//
991094Sdes// Lazy re-exports are similar to normal re-exports, except that for callable
10108794Sdes// symbols the definitions are replaced with trampolines that will look up and
1191094Sdes// call through to the re-exported symbol at runtime. This can be used to
12117610Sdes// enable lazy compilation.
13174832Sdes//
1491094Sdes//===----------------------------------------------------------------------===//
1591094Sdes
16236109Sdes#ifndef LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H
1791100Sdes#define LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H
1891100Sdes
1991100Sdes#include "llvm/ExecutionEngine/Orc/Core.h"
2091094Sdes#include "llvm/ExecutionEngine/Orc/IndirectionUtils.h"
2191094Sdes#include "llvm/ExecutionEngine/Orc/Speculation.h"
22236109Sdes
2391094Sdesnamespace llvm {
2491100Sdes
25348980Sdesclass Triple;
26348980Sdes
2791100Sdesnamespace orc {
2891100Sdes
2991100Sdes/// Manages a set of 'lazy call-through' trampolines. These are compiler
3091100Sdes/// re-entry trampolines that are pre-bound to look up a given symbol in a given
3191100Sdes/// JITDylib, then jump to that address. Since compilation of symbols is
3291100Sdes/// triggered on first lookup, these call-through trampolines can be used to
3391094Sdes/// implement lazy compilation.
3491094Sdes///
3591100Sdes/// The easiest way to construct these call-throughs is using the lazyReexport
3691100Sdes/// function.
3791100Sdesclass LazyCallThroughManager {
3891094Sdespublic:
3991100Sdes  /// Clients will want to take some action on first resolution, e.g. updating
4091094Sdes  /// a stub pointer. Instances of this class can be used to implement this.
4191100Sdes  class NotifyResolvedFunction {
42236109Sdes  public:
4391100Sdes    virtual ~NotifyResolvedFunction() {}
4491094Sdes
4591094Sdes    /// Called the first time a lazy call through is executed and the target
46236109Sdes    /// symbol resolved.
47236109Sdes    virtual Error operator()(JITDylib &SourceJD,
48236109Sdes                             const SymbolStringPtr &SymbolName,
49117610Sdes                             JITTargetAddress ResolvedAddr) = 0;
50236109Sdes
51147466Sdes  private:
52117610Sdes    virtual void anchor();
5391094Sdes  };
5491094Sdes
55255376Sdes  template <typename NotifyResolvedImpl>
56255376Sdes  class NotifyResolvedFunctionImpl : public NotifyResolvedFunction {
57348980Sdes  public:
58    NotifyResolvedFunctionImpl(NotifyResolvedImpl NotifyResolved)
59        : NotifyResolved(std::move(NotifyResolved)) {}
60    Error operator()(JITDylib &SourceJD, const SymbolStringPtr &SymbolName,
61                     JITTargetAddress ResolvedAddr) {
62      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 std::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
84protected:
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
95private:
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.
111class LocalLazyCallThroughManager : public LazyCallThroughManager {
112private:
113  LocalLazyCallThroughManager(ExecutionSession &ES,
114                              JITTargetAddress ErrorHandlerAddr)
115      : LazyCallThroughManager(ES, ErrorHandlerAddr, nullptr) {}
116
117  template <typename ORCABI> Error init() {
118    auto TP = LocalTrampolinePool<ORCABI>::Create(
119        [this](JITTargetAddress TrampolineAddr) {
120          return callThroughToSymbol(TrampolineAddr);
121        });
122
123    if (!TP)
124      return TP.takeError();
125
126    setTrampolinePool(std::move(*TP));
127    return Error::success();
128  }
129
130public:
131  /// Create a LocalLazyCallThroughManager using the given ABI. See
132  /// createLocalLazyCallThroughManager.
133  template <typename ORCABI>
134  static Expected<std::unique_ptr<LocalLazyCallThroughManager>>
135  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
142    return std::move(LLCTM);
143  }
144};
145
146/// Create a LocalLazyCallThroughManager from the given triple and execution
147/// session.
148Expected<std::unique_ptr<LazyCallThroughManager>>
149createLocalLazyCallThroughManager(const Triple &T, ExecutionSession &ES,
150                                  JITTargetAddress ErrorHandlerAddr);
151
152/// A materialization unit that builds lazy re-exports. These are callable
153/// entry points that call through to the given symbols.
154/// 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.
157class LazyReexportsMaterializationUnit : public MaterializationUnit {
158public:
159  LazyReexportsMaterializationUnit(LazyCallThroughManager &LCTManager,
160                                   IndirectStubsManager &ISManager,
161                                   JITDylib &SourceJD,
162                                   SymbolAliasMap CallableAliases,
163                                   ImplSymbolMap *SrcJDLoc, VModuleKey K);
164
165  StringRef getName() const override;
166
167private:
168  void materialize(MaterializationResponsibility R) override;
169  void discard(const JITDylib &JD, const SymbolStringPtr &Name) override;
170  static SymbolFlagsMap extractFlags(const SymbolAliasMap &Aliases);
171
172  LazyCallThroughManager &LCTManager;
173  IndirectStubsManager &ISManager;
174  JITDylib &SourceJD;
175  SymbolAliasMap CallableAliases;
176  std::shared_ptr<LazyCallThroughManager::NotifyResolvedFunction>
177      NotifyResolved;
178  ImplSymbolMap *AliaseeTable;
179};
180
181/// Define lazy-reexports based on the given SymbolAliasMap. Each lazy re-export
182/// is a callable symbol that will look up and dispatch to the given aliasee on
183/// first call. All subsequent calls will go directly to the aliasee.
184inline std::unique_ptr<LazyReexportsMaterializationUnit>
185lazyReexports(LazyCallThroughManager &LCTManager,
186              IndirectStubsManager &ISManager, JITDylib &SourceJD,
187              SymbolAliasMap CallableAliases, ImplSymbolMap *SrcJDLoc = nullptr,
188              VModuleKey K = VModuleKey()) {
189  return std::make_unique<LazyReexportsMaterializationUnit>(
190      LCTManager, ISManager, SourceJD, std::move(CallableAliases), SrcJDLoc,
191      std::move(K));
192}
193
194} // End namespace orc
195} // End namespace llvm
196
197#endif // LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H
198