Skip to content

Commit 6d72bf4

Browse files
committed
[ORC][llvm-jitlink] Add SimpleLazyReexportsSpeculator, use in llvm-jitlink.
Also adds a new IdleTask type and updates DynamicThreadPoolTaskDispatcher to schedule IdleTasks whenever the total number of threads running is less than the maximum number of MaterializationThreads. A SimpleLazyReexportsSpeculator instance maintains a list of speculation suggestions ((JITDylib, Function) pairs) and registered lazy reexports. When speculation opportunities are available (having been added via addSpeculationSuggestions or when lazy reexports were created) it schedules an IdleTask that triggers the next speculative lookup as soon as resources are available. Speculation suggestions are processed first, followed by lookups for lazy reexport bodies. A callback can be registered at object construction time to record lazy reexport executions as they happen, and these executions can be fed back into the speculator as suggestions on subsequent executions. The llvm-jitlink tool is updated to support speculation when lazy linking is used via three new arguments: -speculate=[none|simple] : When the 'simple' value is specified a SimpleLazyReexportsSpeculator instances is used for speculation. -speculate-order <path> : Specifies a path to a CSV containing (jit dylib name, function name) triples to use as speculative suggestions in the current run. -record-lazy-execs <path> : Specifies a path in which to record lazy function executions as a CSV of (jit dylib name, function name) pairs, suitable for use with -speculate-order. The same path can be passed to -speculate-order and -record-lazy-execs, in which case the file will be overwritten at the end of the execution. No testcase yet: Speculative linking is difficult to test (since by definition execution behavior should be unaffected by speculation) and this is an new prototype of the concept*. Tests will be added in the future once the interface and behavior settle down. * An earlier implementation of the speculation concept can be found in llvm/include/llvm/ExecutionEngine/Orc/Speculation.h. Both systems have the same goal (hiding compilation latency) but different mechanisms. This patch relies entirely on information available in the controller, where the old system could receive additional information from the JIT'd runtime via callbacks. I aim to combine the two in the future, but want to gain more practical experience with speculation first.
1 parent d0373db commit 6d72bf4

File tree

8 files changed

+509
-65
lines changed

8 files changed

+509
-65
lines changed

llvm/include/llvm/ExecutionEngine/Orc/JITLinkReentryTrampolines.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ class JITLinkReentryTrampolines {
6565
Expected<std::unique_ptr<LazyReexportsManager>>
6666
createJITLinkLazyReexportsManager(ObjectLinkingLayer &ObjLinkingLayer,
6767
RedirectableSymbolManager &RSMgr,
68-
JITDylib &PlatformJD);
68+
JITDylib &PlatformJD,
69+
LazyReexportsManager::Listener *L = nullptr);
6970

7071
} // namespace llvm::orc
7172

llvm/include/llvm/ExecutionEngine/Orc/LazyReexports.h

Lines changed: 94 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,37 @@ class LazyReexportsManager : public ResourceManager {
179179
lazyReexports(LazyReexportsManager &, SymbolAliasMap);
180180

181181
public:
182+
struct CallThroughInfo {
183+
JITDylibSP JD;
184+
SymbolStringPtr Name;
185+
SymbolStringPtr BodyName;
186+
};
187+
188+
class Listener {
189+
public:
190+
using CallThroughInfo = LazyReexportsManager::CallThroughInfo;
191+
192+
virtual ~Listener();
193+
194+
/// Called under the session lock when new lazy reexports are created.
195+
virtual void onLazyReexportsCreated(JITDylib &JD, ResourceKey K,
196+
const SymbolAliasMap &Reexports) = 0;
197+
198+
/// Called under the session lock when lazy reexports have their ownership
199+
/// transferred to a new ResourceKey.
200+
virtual void onLazyReexportsTransfered(JITDylib &JD, ResourceKey DstK,
201+
ResourceKey SrcK) = 0;
202+
203+
/// Called under the session lock when lazy reexports are removed.
204+
virtual Error onLazyReexportsRemoved(JITDylib &JD, ResourceKey K) = 0;
205+
206+
/// Called outside the session lock when a lazy reexport is called.
207+
/// NOTE: Since this is called outside the session lock there is a chance
208+
/// that the reexport referred to has already been removed. Listeners
209+
/// must be prepared to handle requests for stale reexports.
210+
virtual void onLazyReexportCalled(const CallThroughInfo &CTI) = 0;
211+
};
212+
182213
using OnTrampolinesReadyFn = unique_function<void(
183214
Expected<std::vector<ExecutorSymbolDef>> EntryAddrs)>;
184215
using EmitTrampolinesFn =
@@ -189,7 +220,7 @@ class LazyReexportsManager : public ResourceManager {
189220
/// This will work both in-process and out-of-process.
190221
static Expected<std::unique_ptr<LazyReexportsManager>>
191222
Create(EmitTrampolinesFn EmitTrampolines, RedirectableSymbolManager &RSMgr,
192-
JITDylib &PlatformJD);
223+
JITDylib &PlatformJD, Listener *L = nullptr);
193224

194225
LazyReexportsManager(LazyReexportsManager &&) = delete;
195226
LazyReexportsManager &operator=(LazyReexportsManager &&) = delete;
@@ -199,12 +230,6 @@ class LazyReexportsManager : public ResourceManager {
199230
ResourceKey SrcK) override;
200231

201232
private:
202-
struct CallThroughInfo {
203-
SymbolStringPtr Name;
204-
SymbolStringPtr BodyName;
205-
JITDylibSP JD;
206-
};
207-
208233
class MU;
209234
class Plugin;
210235

@@ -213,7 +238,7 @@ class LazyReexportsManager : public ResourceManager {
213238

214239
LazyReexportsManager(EmitTrampolinesFn EmitTrampolines,
215240
RedirectableSymbolManager &RSMgr, JITDylib &PlatformJD,
216-
Error &Err);
241+
Listener *L, Error &Err);
217242

218243
std::unique_ptr<MaterializationUnit>
219244
createLazyReexports(SymbolAliasMap Reexports);
@@ -229,6 +254,7 @@ class LazyReexportsManager : public ResourceManager {
229254
ExecutionSession &ES;
230255
EmitTrampolinesFn EmitTrampolines;
231256
RedirectableSymbolManager &RSMgr;
257+
Listener *L;
232258

233259
DenseMap<ResourceKey, std::vector<ExecutorAddr>> KeyToReentryAddrs;
234260
DenseMap<ExecutorAddr, CallThroughInfo> CallThroughs;
@@ -242,6 +268,66 @@ lazyReexports(LazyReexportsManager &LRM, SymbolAliasMap Reexports) {
242268
return LRM.createLazyReexports(std::move(Reexports));
243269
}
244270

271+
class SimpleLazyReexportsSpeculator : public LazyReexportsManager::Listener {
272+
friend std::shared_ptr<SimpleLazyReexportsSpeculator> std::make_shared();
273+
274+
public:
275+
using RecordExecutionFunction =
276+
unique_function<void(const CallThroughInfo &CTI)>;
277+
278+
static std::shared_ptr<SimpleLazyReexportsSpeculator>
279+
Create(ExecutionSession &ES, RecordExecutionFunction RecordExec = {}) {
280+
class make_shared_helper : public SimpleLazyReexportsSpeculator {
281+
public:
282+
make_shared_helper(ExecutionSession &ES,
283+
RecordExecutionFunction RecordExec)
284+
: SimpleLazyReexportsSpeculator(ES, std::move(RecordExec)) {}
285+
};
286+
287+
auto Instance =
288+
std::make_shared<make_shared_helper>(ES, std::move(RecordExec));
289+
Instance->WeakThis = Instance;
290+
return Instance;
291+
}
292+
293+
SimpleLazyReexportsSpeculator(SimpleLazyReexportsSpeculator &&) = delete;
294+
SimpleLazyReexportsSpeculator &
295+
operator=(SimpleLazyReexportsSpeculator &&) = delete;
296+
~SimpleLazyReexportsSpeculator() override;
297+
298+
void onLazyReexportsCreated(JITDylib &JD, ResourceKey K,
299+
const SymbolAliasMap &Reexports) override;
300+
301+
void onLazyReexportsTransfered(JITDylib &JD, ResourceKey DstK,
302+
ResourceKey SrcK) override;
303+
304+
Error onLazyReexportsRemoved(JITDylib &JD, ResourceKey K) override;
305+
306+
void onLazyReexportCalled(const CallThroughInfo &CTI) override;
307+
308+
void addSpeculationSuggestions(
309+
std::vector<std::pair<std::string, SymbolStringPtr>> NewSuggestions);
310+
311+
private:
312+
SimpleLazyReexportsSpeculator(ExecutionSession &ES,
313+
RecordExecutionFunction RecordExec)
314+
: ES(ES), RecordExec(std::move(RecordExec)) {}
315+
316+
bool doNextSpeculativeLookup();
317+
318+
class SpeculateTask;
319+
320+
using KeyToFunctionBodiesMap =
321+
DenseMap<ResourceKey, std::vector<SymbolStringPtr>>;
322+
323+
ExecutionSession &ES;
324+
RecordExecutionFunction RecordExec;
325+
std::weak_ptr<SimpleLazyReexportsSpeculator> WeakThis;
326+
DenseMap<JITDylib *, KeyToFunctionBodiesMap> LazyReexports;
327+
std::deque<std::pair<std::string, SymbolStringPtr>> SpeculateSuggestions;
328+
bool SpeculateTaskActive = false;
329+
};
330+
245331
} // End namespace orc
246332
} // End namespace llvm
247333

llvm/include/llvm/ExecutionEngine/Orc/TaskDispatch.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,16 @@ makeGenericNamedTask(FnT &&Fn, const char *Desc = nullptr) {
9292
Desc);
9393
}
9494

95+
/// IdleTask can be used as the basis for low-priority tasks, e.g. speculative
96+
/// lookup.
97+
class IdleTask : public RTTIExtends<IdleTask, Task> {
98+
public:
99+
static char ID;
100+
101+
private:
102+
void anchor() override;
103+
};
104+
95105
/// Abstract base for classes that dispatch ORC Tasks.
96106
class TaskDispatcher {
97107
public:
@@ -118,9 +128,13 @@ class DynamicThreadPoolTaskDispatcher : public TaskDispatcher {
118128
DynamicThreadPoolTaskDispatcher(
119129
std::optional<size_t> MaxMaterializationThreads)
120130
: MaxMaterializationThreads(MaxMaterializationThreads) {}
131+
121132
void dispatch(std::unique_ptr<Task> T) override;
122133
void shutdown() override;
123134
private:
135+
bool canRunMaterializationTaskNow();
136+
bool canRunIdleTaskNow();
137+
124138
std::mutex DispatchMutex;
125139
bool Shutdown = false;
126140
size_t Outstanding = 0;
@@ -129,6 +143,7 @@ class DynamicThreadPoolTaskDispatcher : public TaskDispatcher {
129143
std::optional<size_t> MaxMaterializationThreads;
130144
size_t NumMaterializationThreads = 0;
131145
std::deque<std::unique_ptr<Task>> MaterializationTaskQueue;
146+
std::deque<std::unique_ptr<Task>> IdleTaskQueue;
132147
};
133148

134149
#endif // LLVM_ENABLE_THREADS

llvm/lib/ExecutionEngine/Orc/JITLinkReentryTrampolines.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,8 @@ void JITLinkReentryTrampolines::emit(ResourceTrackerSP RT,
173173
Expected<std::unique_ptr<LazyReexportsManager>>
174174
createJITLinkLazyReexportsManager(ObjectLinkingLayer &ObjLinkingLayer,
175175
RedirectableSymbolManager &RSMgr,
176-
JITDylib &PlatformJD) {
176+
JITDylib &PlatformJD,
177+
LazyReexportsManager::Listener *L) {
177178
auto JLT = JITLinkReentryTrampolines::Create(ObjLinkingLayer);
178179
if (!JLT)
179180
return JLT.takeError();
@@ -184,7 +185,7 @@ createJITLinkLazyReexportsManager(ObjectLinkingLayer &ObjLinkingLayer,
184185
OnTrampolinesReady) mutable {
185186
JLT->emit(std::move(RT), NumTrampolines, std::move(OnTrampolinesReady));
186187
},
187-
RSMgr, PlatformJD);
188+
RSMgr, PlatformJD, L);
188189
}
189190

190191
} // namespace llvm::orc

0 commit comments

Comments
 (0)