@@ -47,92 +47,101 @@ class Value;
47
47
48
48
namespace orc {
49
49
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 {
52
56
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 () {}
62
58
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;
64
62
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
+ };
71
66
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
+ }
74
87
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 ()) {
78
93
if (auto Err = grow ())
79
94
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 ();
84
99
return TrampolineAddr;
85
100
}
86
101
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
+ }
89
107
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
+ }
91
115
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)) {
99
119
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.
111
123
std::error_code EC;
112
124
ResolverBlock = sys::OwningMemoryBlock (sys::Memory::allocateMappedMemory (
113
- TargetT ::ResolverCodeSize, nullptr ,
125
+ ORCABI ::ResolverCodeSize, nullptr ,
114
126
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
+ }
116
131
117
- TargetT ::writeResolverCode (static_cast <uint8_t *>(ResolverBlock.base ()),
118
- &reenter, this );
132
+ ORCABI ::writeResolverCode (static_cast <uint8_t *>(ResolverBlock.base ()),
133
+ &reenter, this );
119
134
120
135
EC = sys::Memory::protectMappedMemory (ResolverBlock.getMemoryBlock (),
121
136
sys::Memory::MF_READ |
122
137
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
+ }
133
142
}
134
143
135
- Error grow () override {
144
+ Error grow () {
136
145
assert (this ->AvailableTrampolines .empty () && " Growing prematurely?" );
137
146
138
147
std::error_code EC;
@@ -144,17 +153,17 @@ class LocalJITCompileCallbackManager : public JITCompileCallbackManager {
144
153
return errorCodeToError (EC);
145
154
146
155
unsigned NumTrampolines =
147
- (sys::Process::getPageSize () - TargetT ::PointerSize) /
148
- TargetT ::TrampolineSize;
156
+ (sys::Process::getPageSize () - ORCABI ::PointerSize) /
157
+ ORCABI ::TrampolineSize;
149
158
150
159
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);
153
162
154
163
for (unsigned I = 0 ; I < NumTrampolines; ++I)
155
164
this ->AvailableTrampolines .push_back (
156
165
static_cast <JITTargetAddress>(reinterpret_cast <uintptr_t >(
157
- TrampolineMem + (I * TargetT ::TrampolineSize))));
166
+ TrampolineMem + (I * ORCABI ::TrampolineSize))));
158
167
159
168
if (auto EC = sys::Memory::protectMappedMemory (
160
169
TrampolineBlock.getMemoryBlock (),
@@ -165,8 +174,87 @@ class LocalJITCompileCallbackManager : public JITCompileCallbackManager {
165
174
return Error::success ();
166
175
}
167
176
177
+ GetTrampolineLandingFunction GetTrampolineLanding;
178
+
179
+ std::mutex LTPMutex;
168
180
sys::OwningMemoryBlock ResolverBlock;
169
181
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
+ }
170
258
};
171
259
172
260
// / Base class for managing collections of named indirect stubs.
@@ -299,7 +387,7 @@ class LocalIndirectStubsManager : public IndirectStubsManager {
299
387
// / The given target triple will determine the ABI, and the given
300
388
// / ErrorHandlerAddress will be used by the resulting compile callback
301
389
// / manager if a compile callback fails.
302
- std::unique_ptr<JITCompileCallbackManager>
390
+ Expected< std::unique_ptr<JITCompileCallbackManager> >
303
391
createLocalCompileCallbackManager (const Triple &T, ExecutionSession &ES,
304
392
JITTargetAddress ErrorHandlerAddress);
305
393
0 commit comments