Skip to content

Commit c1275e7

Browse files
committed
[ORC] Add a "lazy call-through" utility based on the same underlying trampoline
implementation as lazy compile callbacks, and a "lazy re-exports" utility that builds lazy call-throughs. Lazy call-throughs are similar to lazy compile callbacks (and are based on the same underlying state saving/restoring trampolines) but resolve their targets by performing a standard ORC lookup rather than invoking a user supplied compiler callback. This allows them to inherit the thread-safety of ORC lookups while blocking only the calling thread (whereas compile callbacks also block one compile thread). Lazy re-exports provide a simple way of building lazy call-throughs. Unlike a regular re-export, a lazy re-export generates a new address (a stub entry point) that will act like the re-exported symbol when called. The first call via a lazy re-export will trigger compilation of the re-exported symbol before calling through to it. llvm-svn: 343061
1 parent ea0b7bb commit c1275e7

File tree

7 files changed

+510
-38
lines changed

7 files changed

+510
-38
lines changed
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
//===------ LazyReexports.h -- Utilities for lazy reexports -----*- C++ -*-===//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
//
10+
// Lazy re-exports are similar to normal re-exports, except that for callable
11+
// symbols the definitions are replaced with trampolines that will look up and
12+
// call through to the re-exported symbol at runtime. This can be used to
13+
// enable lazy compilation.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#ifndef LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H
18+
#define LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H
19+
20+
#include "llvm/ExecutionEngine/Orc/Core.h"
21+
#include "llvm/ExecutionEngine/Orc/IndirectionUtils.h"
22+
23+
namespace llvm {
24+
25+
class Triple;
26+
27+
namespace orc {
28+
29+
/// Manages a set of 'lazy call-through' trampolines. These are compiler
30+
/// re-entry trampolines that are pre-bound to look up a given symbol in a given
31+
/// JITDylib, then jump to that address. Since compilation of symbols is
32+
/// triggered on first lookup, these call-through trampolines can be used to
33+
/// implement lazy compilation.
34+
///
35+
/// The easiest way to construct these call-throughs is using the lazyReexport
36+
/// function.
37+
class LazyCallThroughManager {
38+
public:
39+
/// Clients will want to take some action on first resolution, e.g. updating
40+
/// a stub pointer. Instances of this class can be used to implement this.
41+
class NotifyResolvedFunction {
42+
public:
43+
virtual ~NotifyResolvedFunction() {}
44+
45+
/// Called the first time a lazy call through is executed and the target
46+
/// symbol resolved.
47+
virtual Error operator()(JITDylib &SourceJD,
48+
const SymbolStringPtr &SymbolName,
49+
JITTargetAddress ResolvedAddr) = 0;
50+
51+
private:
52+
virtual void anchor();
53+
};
54+
55+
template <typename NotifyResolvedImpl>
56+
class NotifyResolvedFunctionImpl : public NotifyResolvedFunction {
57+
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 llvm::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+
84+
protected:
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+
95+
private:
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.
111+
class LocalLazyCallThroughManager : public LazyCallThroughManager {
112+
private:
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+
130+
public:
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.
148+
Expected<std::unique_ptr<LazyCallThroughManager>>
149+
createLocalLazyCallThroughManager(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.
157+
class LazyReexportsMaterializationUnit : public MaterializationUnit {
158+
public:
159+
LazyReexportsMaterializationUnit(LazyCallThroughManager &LCTManager,
160+
IndirectStubsManager &ISManager,
161+
JITDylib &SourceJD,
162+
SymbolAliasMap CallableAliases);
163+
164+
private:
165+
void materialize(MaterializationResponsibility R) override;
166+
void discard(const JITDylib &JD, SymbolStringPtr Name) override;
167+
static SymbolFlagsMap extractFlags(const SymbolAliasMap &Aliases);
168+
169+
LazyCallThroughManager &LCTManager;
170+
IndirectStubsManager &ISManager;
171+
JITDylib &SourceJD;
172+
SymbolAliasMap CallableAliases;
173+
std::shared_ptr<LazyCallThroughManager::NotifyResolvedFunction>
174+
NotifyResolved;
175+
};
176+
177+
/// Define lazy-reexports based on the given SymbolAliasMap. Each lazy re-export
178+
/// is a callable symbol that will look up and dispatch to the given aliasee on
179+
/// first call. All subsequent calls will go directly to the aliasee.
180+
inline std::unique_ptr<LazyReexportsMaterializationUnit>
181+
lazyReexports(LazyCallThroughManager &LCTManager,
182+
IndirectStubsManager &ISManager, JITDylib &SourceJD,
183+
SymbolAliasMap CallableAliases) {
184+
return llvm::make_unique<LazyReexportsMaterializationUnit>(
185+
LCTManager, ISManager, SourceJD, std::move(CallableAliases));
186+
}
187+
188+
} // End namespace orc
189+
} // End namespace llvm
190+
191+
#endif // LLVM_EXECUTIONENGINE_ORC_LAZYREEXPORTS_H

llvm/lib/ExecutionEngine/Orc/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ add_llvm_library(LLVMOrcJIT
55
IndirectionUtils.cpp
66
IRCompileLayer.cpp
77
IRTransformLayer.cpp
8+
LazyReexports.cpp
89
Legacy.cpp
910
Layer.cpp
1011
LLJIT.cpp

0 commit comments

Comments
 (0)