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
24namespace llvm {
25
26class Triple;
27
28namespace 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.
38class LazyCallThroughManager {
39public:
40  using NotifyResolvedFunction =
41      unique_function<Error(JITTargetAddress ResolvedAddr)>;
42
43  // Return a free call-through trampoline and bind it to look up and call
44  // through to the given symbol.
45  Expected<JITTargetAddress>
46  getCallThroughTrampoline(JITDylib &SourceJD, SymbolStringPtr SymbolName,
47                           NotifyResolvedFunction NotifyResolved);
48
49  void resolveTrampolineLandingAddress(
50      JITTargetAddress TrampolineAddr,
51      TrampolinePool::NotifyLandingResolvedFunction NotifyLandingResolved);
52
53  virtual ~LazyCallThroughManager() = default;
54
55protected:
56  using NotifyLandingResolvedFunction =
57      TrampolinePool::NotifyLandingResolvedFunction;
58
59  LazyCallThroughManager(ExecutionSession &ES,
60                         JITTargetAddress ErrorHandlerAddr, TrampolinePool *TP);
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
73private:
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.
87class LocalLazyCallThroughManager : public LazyCallThroughManager {
88private:
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
114public:
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.
132Expected<std::unique_ptr<LazyCallThroughManager>>
133createLocalLazyCallThroughManager(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.
141class LazyReexportsMaterializationUnit : public MaterializationUnit {
142public:
143  LazyReexportsMaterializationUnit(LazyCallThroughManager &LCTManager,
144                                   IndirectStubsManager &ISManager,
145                                   JITDylib &SourceJD,
146                                   SymbolAliasMap CallableAliases,
147                                   ImplSymbolMap *SrcJDLoc, VModuleKey K);
148
149  StringRef getName() const override;
150
151private:
152  void materialize(MaterializationResponsibility R) override;
153  void discard(const JITDylib &JD, const SymbolStringPtr &Name) override;
154  static SymbolFlagsMap 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.
166inline std::unique_ptr<LazyReexportsMaterializationUnit>
167lazyReexports(LazyCallThroughManager &LCTManager,
168              IndirectStubsManager &ISManager, JITDylib &SourceJD,
169              SymbolAliasMap CallableAliases, ImplSymbolMap *SrcJDLoc = nullptr,
170              VModuleKey K = VModuleKey()) {
171  return std::make_unique<LazyReexportsMaterializationUnit>(
172      LCTManager, ISManager, SourceJD, std::move(CallableAliases), SrcJDLoc,
173      std::move(K));
174}
175
176} // End namespace orc
177} // End namespace llvm
178
179#endif // LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H
180