Skip to content

Commit 50b3344

Browse files
committed
[ORC] Refactor trampoline pool management out of JITCompileCallbackManager.
This will allow trampoline pools to be re-used for a new lazy-reexport utility that generates looks up function bodies using the standard symbol lookup process (rather than using a user provided compile function). This new utility provides the same capabilities (since MaterializationUnits already allow user supplied compile functions to be run) as JITCompileCallbackManager, but can use the new asynchronous lookup functions to avoid blocking a compile thread. This patch also updates createLocalCompileCallbackManager to return an error if a callback manager can not be created, and updates clients of that API to account for the change. Finally, the OrcCBindingsStack is updates so that if a callback manager is not available for the target platform a valid stack (without support for lazy compilation) can still be constructed. llvm-svn: 343059
1 parent 225a32a commit 50b3344

File tree

6 files changed

+285
-128
lines changed

6 files changed

+285
-128
lines changed

llvm/include/llvm/ExecutionEngine/Orc/IndirectionUtils.h

Lines changed: 157 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -47,92 +47,101 @@ class Value;
4747

4848
namespace orc {
4949

50-
/// Target-independent base class for compile callback management.
51-
class JITCompileCallbackManager {
50+
/// Base class for pools of compiler re-entry trampolines.
51+
/// These trampolines are callable addresses that save all register state
52+
/// before calling a supplied function to return the trampoline landing
53+
/// address, then restore all state before jumping to that address. They
54+
/// are used by various ORC APIs to support lazy compilation
55+
class TrampolinePool {
5256
public:
53-
using CompileFunction = std::function<JITTargetAddress()>;
54-
55-
/// Construct a JITCompileCallbackManager.
56-
/// @param ErrorHandlerAddress The address of an error handler in the target
57-
/// process to be used if a compile callback fails.
58-
JITCompileCallbackManager(ExecutionSession &ES,
59-
JITTargetAddress ErrorHandlerAddress)
60-
: ES(ES), CallbacksJD(ES.createJITDylib("<Callbacks>")),
61-
ErrorHandlerAddress(ErrorHandlerAddress) {}
57+
virtual ~TrampolinePool() {}
6258

63-
virtual ~JITCompileCallbackManager() = default;
59+
/// Get an available trampoline address.
60+
/// Returns an error if no trampoline can be created.
61+
virtual Expected<JITTargetAddress> getTrampoline() = 0;
6462

65-
/// Reserve a compile callback.
66-
Expected<JITTargetAddress> getCompileCallback(CompileFunction Compile);
67-
68-
/// Execute the callback for the given trampoline id. Called by the JIT
69-
/// to compile functions on demand.
70-
JITTargetAddress executeCompileCallback(JITTargetAddress TrampolineAddr);
63+
private:
64+
virtual void anchor();
65+
};
7166

72-
protected:
73-
std::vector<JITTargetAddress> AvailableTrampolines;
67+
/// A trampoline pool for trampolines within the current process.
68+
template <typename ORCABI> class LocalTrampolinePool : public TrampolinePool {
69+
public:
70+
using GetTrampolineLandingFunction =
71+
std::function<JITTargetAddress(JITTargetAddress TrampolineAddr)>;
72+
73+
/// Creates a LocalTrampolinePool with the given RunCallback function.
74+
/// Returns an error if this function is unable to correctly allocate, write
75+
/// and protect the resolver code block.
76+
static Expected<std::unique_ptr<LocalTrampolinePool>>
77+
Create(GetTrampolineLandingFunction GetTrampolineLanding) {
78+
Error Err = Error::success();
79+
80+
auto LTP = std::unique_ptr<LocalTrampolinePool>(
81+
new LocalTrampolinePool(std::move(GetTrampolineLanding), Err));
82+
83+
if (Err)
84+
return std::move(Err);
85+
return std::move(LTP);
86+
}
7487

75-
private:
76-
Expected<JITTargetAddress> getAvailableTrampolineAddr() {
77-
if (this->AvailableTrampolines.empty())
88+
/// Get a free trampoline. Returns an error if one can not be provide (e.g.
89+
/// because the pool is empty and can not be grown).
90+
Expected<JITTargetAddress> getTrampoline() override {
91+
std::lock_guard<std::mutex> Lock(LTPMutex);
92+
if (AvailableTrampolines.empty()) {
7893
if (auto Err = grow())
7994
return std::move(Err);
80-
assert(!this->AvailableTrampolines.empty() &&
81-
"Failed to grow available trampolines.");
82-
JITTargetAddress TrampolineAddr = this->AvailableTrampolines.back();
83-
this->AvailableTrampolines.pop_back();
95+
}
96+
assert(!AvailableTrampolines.empty() && "Failed to grow trampoline pool");
97+
auto TrampolineAddr = AvailableTrampolines.back();
98+
AvailableTrampolines.pop_back();
8499
return TrampolineAddr;
85100
}
86101

87-
// Create new trampolines - to be implemented in subclasses.
88-
virtual Error grow() = 0;
102+
/// Returns the given trampoline to the pool for re-use.
103+
void releaseTrampoline(JITTargetAddress TrampolineAddr) {
104+
std::lock_guard<std::mutex> Lock(LTPMutex);
105+
AvailableTrampolines.push_back(TrampolineAddr);
106+
}
89107

90-
virtual void anchor();
108+
private:
109+
static JITTargetAddress reenter(void *TrampolinePoolPtr, void *TrampolineId) {
110+
LocalTrampolinePool<ORCABI> *TrampolinePool =
111+
static_cast<LocalTrampolinePool *>(TrampolinePoolPtr);
112+
return TrampolinePool->GetTrampolineLanding(static_cast<JITTargetAddress>(
113+
reinterpret_cast<uintptr_t>(TrampolineId)));
114+
}
91115

92-
std::mutex CCMgrMutex;
93-
ExecutionSession &ES;
94-
JITDylib &CallbacksJD;
95-
JITTargetAddress ErrorHandlerAddress;
96-
std::map<JITTargetAddress, SymbolStringPtr> AddrToSymbol;
97-
size_t NextCallbackId = 0;
98-
};
116+
LocalTrampolinePool(GetTrampolineLandingFunction GetTrampolineLanding,
117+
Error &Err)
118+
: GetTrampolineLanding(std::move(GetTrampolineLanding)) {
99119

100-
/// Manage compile callbacks for in-process JITs.
101-
template <typename TargetT>
102-
class LocalJITCompileCallbackManager : public JITCompileCallbackManager {
103-
public:
104-
/// Construct a InProcessJITCompileCallbackManager.
105-
/// @param ErrorHandlerAddress The address of an error handler in the target
106-
/// process to be used if a compile callback fails.
107-
LocalJITCompileCallbackManager(ExecutionSession &ES,
108-
JITTargetAddress ErrorHandlerAddress)
109-
: JITCompileCallbackManager(ES, ErrorHandlerAddress) {
110-
/// Set up the resolver block.
120+
ErrorAsOutParameter _(&Err);
121+
122+
/// Try to set up the resolver block.
111123
std::error_code EC;
112124
ResolverBlock = sys::OwningMemoryBlock(sys::Memory::allocateMappedMemory(
113-
TargetT::ResolverCodeSize, nullptr,
125+
ORCABI::ResolverCodeSize, nullptr,
114126
sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC));
115-
assert(!EC && "Failed to allocate resolver block");
127+
if (EC) {
128+
Err = errorCodeToError(EC);
129+
return;
130+
}
116131

117-
TargetT::writeResolverCode(static_cast<uint8_t *>(ResolverBlock.base()),
118-
&reenter, this);
132+
ORCABI::writeResolverCode(static_cast<uint8_t *>(ResolverBlock.base()),
133+
&reenter, this);
119134

120135
EC = sys::Memory::protectMappedMemory(ResolverBlock.getMemoryBlock(),
121136
sys::Memory::MF_READ |
122137
sys::Memory::MF_EXEC);
123-
assert(!EC && "Failed to mprotect resolver block");
124-
}
125-
126-
private:
127-
static JITTargetAddress reenter(void *CCMgr, void *TrampolineId) {
128-
JITCompileCallbackManager *Mgr =
129-
static_cast<JITCompileCallbackManager *>(CCMgr);
130-
return Mgr->executeCompileCallback(
131-
static_cast<JITTargetAddress>(
132-
reinterpret_cast<uintptr_t>(TrampolineId)));
138+
if (EC) {
139+
Err = errorCodeToError(EC);
140+
return;
141+
}
133142
}
134143

135-
Error grow() override {
144+
Error grow() {
136145
assert(this->AvailableTrampolines.empty() && "Growing prematurely?");
137146

138147
std::error_code EC;
@@ -144,17 +153,17 @@ class LocalJITCompileCallbackManager : public JITCompileCallbackManager {
144153
return errorCodeToError(EC);
145154

146155
unsigned NumTrampolines =
147-
(sys::Process::getPageSize() - TargetT::PointerSize) /
148-
TargetT::TrampolineSize;
156+
(sys::Process::getPageSize() - ORCABI::PointerSize) /
157+
ORCABI::TrampolineSize;
149158

150159
uint8_t *TrampolineMem = static_cast<uint8_t *>(TrampolineBlock.base());
151-
TargetT::writeTrampolines(TrampolineMem, ResolverBlock.base(),
152-
NumTrampolines);
160+
ORCABI::writeTrampolines(TrampolineMem, ResolverBlock.base(),
161+
NumTrampolines);
153162

154163
for (unsigned I = 0; I < NumTrampolines; ++I)
155164
this->AvailableTrampolines.push_back(
156165
static_cast<JITTargetAddress>(reinterpret_cast<uintptr_t>(
157-
TrampolineMem + (I * TargetT::TrampolineSize))));
166+
TrampolineMem + (I * ORCABI::TrampolineSize))));
158167

159168
if (auto EC = sys::Memory::protectMappedMemory(
160169
TrampolineBlock.getMemoryBlock(),
@@ -165,8 +174,87 @@ class LocalJITCompileCallbackManager : public JITCompileCallbackManager {
165174
return Error::success();
166175
}
167176

177+
GetTrampolineLandingFunction GetTrampolineLanding;
178+
179+
std::mutex LTPMutex;
168180
sys::OwningMemoryBlock ResolverBlock;
169181
std::vector<sys::OwningMemoryBlock> TrampolineBlocks;
182+
std::vector<JITTargetAddress> AvailableTrampolines;
183+
};
184+
185+
/// Target-independent base class for compile callback management.
186+
class JITCompileCallbackManager {
187+
public:
188+
using CompileFunction = std::function<JITTargetAddress()>;
189+
190+
virtual ~JITCompileCallbackManager() = default;
191+
192+
/// Reserve a compile callback.
193+
Expected<JITTargetAddress> getCompileCallback(CompileFunction Compile);
194+
195+
/// Execute the callback for the given trampoline id. Called by the JIT
196+
/// to compile functions on demand.
197+
JITTargetAddress executeCompileCallback(JITTargetAddress TrampolineAddr);
198+
199+
protected:
200+
/// Construct a JITCompileCallbackManager.
201+
JITCompileCallbackManager(std::unique_ptr<TrampolinePool> TP,
202+
ExecutionSession &ES,
203+
JITTargetAddress ErrorHandlerAddress)
204+
: TP(std::move(TP)), ES(ES),
205+
CallbacksJD(ES.createJITDylib("<Callbacks>")),
206+
ErrorHandlerAddress(ErrorHandlerAddress) {}
207+
208+
void setTrampolinePool(std::unique_ptr<TrampolinePool> TP) {
209+
this->TP = std::move(TP);
210+
}
211+
212+
private:
213+
std::mutex CCMgrMutex;
214+
std::unique_ptr<TrampolinePool> TP;
215+
ExecutionSession &ES;
216+
JITDylib &CallbacksJD;
217+
JITTargetAddress ErrorHandlerAddress;
218+
std::map<JITTargetAddress, SymbolStringPtr> AddrToSymbol;
219+
size_t NextCallbackId = 0;
220+
};
221+
222+
/// Manage compile callbacks for in-process JITs.
223+
template <typename ORCABI>
224+
class LocalJITCompileCallbackManager : public JITCompileCallbackManager {
225+
public:
226+
/// Create a new LocalJITCompileCallbackManager.
227+
static Expected<std::unique_ptr<LocalJITCompileCallbackManager>>
228+
Create(ExecutionSession &ES, JITTargetAddress ErrorHandlerAddress) {
229+
Error Err = Error::success();
230+
auto CCMgr = std::unique_ptr<LocalJITCompileCallbackManager>(
231+
new LocalJITCompileCallbackManager(ES, ErrorHandlerAddress, Err));
232+
if (Err)
233+
return std::move(Err);
234+
return std::move(CCMgr);
235+
}
236+
237+
private:
238+
/// Construct a InProcessJITCompileCallbackManager.
239+
/// @param ErrorHandlerAddress The address of an error handler in the target
240+
/// process to be used if a compile callback fails.
241+
LocalJITCompileCallbackManager(ExecutionSession &ES,
242+
JITTargetAddress ErrorHandlerAddress,
243+
Error &Err)
244+
: JITCompileCallbackManager(nullptr, ES, ErrorHandlerAddress) {
245+
ErrorAsOutParameter _(&Err);
246+
auto TP = LocalTrampolinePool<ORCABI>::Create(
247+
[this](JITTargetAddress TrampolineAddr) {
248+
return executeCompileCallback(TrampolineAddr);
249+
});
250+
251+
if (!TP) {
252+
Err = TP.takeError();
253+
return;
254+
}
255+
256+
setTrampolinePool(std::move(*TP));
257+
}
170258
};
171259

172260
/// Base class for managing collections of named indirect stubs.
@@ -299,7 +387,7 @@ class LocalIndirectStubsManager : public IndirectStubsManager {
299387
/// The given target triple will determine the ABI, and the given
300388
/// ErrorHandlerAddress will be used by the resulting compile callback
301389
/// manager if a compile callback fails.
302-
std::unique_ptr<JITCompileCallbackManager>
390+
Expected<std::unique_ptr<JITCompileCallbackManager>>
303391
createLocalCompileCallbackManager(const Triple &T, ExecutionSession &ES,
304392
JITTargetAddress ErrorHandlerAddress);
305393

llvm/include/llvm/ExecutionEngine/Orc/OrcRemoteTargetClient.h

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -447,16 +447,24 @@ class OrcRemoteTargetClient
447447
StringMap<std::pair<StubKey, JITSymbolFlags>> StubIndexes;
448448
};
449449

450-
/// Remote compile callback manager.
451-
class RemoteCompileCallbackManager : public JITCompileCallbackManager {
450+
class RemoteTrampolinePool : public TrampolinePool {
452451
public:
453-
RemoteCompileCallbackManager(OrcRemoteTargetClient &Client,
454-
ExecutionSession &ES,
455-
JITTargetAddress ErrorHandlerAddress)
456-
: JITCompileCallbackManager(ES, ErrorHandlerAddress), Client(Client) {}
452+
RemoteTrampolinePool(OrcRemoteTargetClient &Client) : Client(Client) {}
453+
454+
Expected<JITTargetAddress> getTrampoline() override {
455+
std::lock_guard<std::mutex> Lock(RTPMutex);
456+
if (AvailableTrampolines.empty()) {
457+
if (auto Err = grow())
458+
return std::move(Err);
459+
}
460+
assert(!AvailableTrampolines.empty() && "Failed to grow trampoline pool");
461+
auto TrampolineAddr = AvailableTrampolines.back();
462+
AvailableTrampolines.pop_back();
463+
return TrampolineAddr;
464+
}
457465

458466
private:
459-
Error grow() override {
467+
Error grow() {
460468
JITTargetAddress BlockAddr = 0;
461469
uint32_t NumTrampolines = 0;
462470
if (auto TrampolineInfoOrErr = Client.emitTrampolineBlock())
@@ -471,7 +479,20 @@ class OrcRemoteTargetClient
471479
return Error::success();
472480
}
473481

482+
std::mutex RTPMutex;
474483
OrcRemoteTargetClient &Client;
484+
std::vector<JITTargetAddress> AvailableTrampolines;
485+
};
486+
487+
/// Remote compile callback manager.
488+
class RemoteCompileCallbackManager : public JITCompileCallbackManager {
489+
public:
490+
RemoteCompileCallbackManager(OrcRemoteTargetClient &Client,
491+
ExecutionSession &ES,
492+
JITTargetAddress ErrorHandlerAddress)
493+
: JITCompileCallbackManager(
494+
llvm::make_unique<RemoteTrampolinePool>(Client), ES,
495+
ErrorHandlerAddress) {}
475496
};
476497

477498
/// Create an OrcRemoteTargetClient.

0 commit comments

Comments
 (0)