xref: /llvm-project/llvm/lib/ExecutionEngine/Orc/LazyReexports.cpp (revision 2d10b7b750f97b42055d5b9b08a88c18ff811cd2)
1 //===---------- LazyReexports.cpp - Utilities for lazy reexports ----------===//
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 #include "llvm/ExecutionEngine/Orc/LazyReexports.h"
10 
11 #include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
12 #include "llvm/ExecutionEngine/Orc/OrcABISupport.h"
13 #include "llvm/ExecutionEngine/Orc/Shared/SimplePackedSerialization.h"
14 #include "llvm/TargetParser/Triple.h"
15 
16 #define DEBUG_TYPE "orc"
17 
18 namespace llvm {
19 namespace orc {
20 
21 LazyCallThroughManager::LazyCallThroughManager(ExecutionSession &ES,
22                                                ExecutorAddr ErrorHandlerAddr,
23                                                TrampolinePool *TP)
24     : ES(ES), ErrorHandlerAddr(ErrorHandlerAddr), TP(TP) {}
25 
26 Expected<ExecutorAddr> LazyCallThroughManager::getCallThroughTrampoline(
27     JITDylib &SourceJD, SymbolStringPtr SymbolName,
28     NotifyResolvedFunction NotifyResolved) {
29   assert(TP && "TrampolinePool not set");
30 
31   std::lock_guard<std::mutex> Lock(LCTMMutex);
32   auto Trampoline = TP->getTrampoline();
33 
34   if (!Trampoline)
35     return Trampoline.takeError();
36 
37   Reexports[*Trampoline] = ReexportsEntry{&SourceJD, std::move(SymbolName)};
38   Notifiers[*Trampoline] = std::move(NotifyResolved);
39   return *Trampoline;
40 }
41 
42 ExecutorAddr LazyCallThroughManager::reportCallThroughError(Error Err) {
43   ES.reportError(std::move(Err));
44   return ErrorHandlerAddr;
45 }
46 
47 Expected<LazyCallThroughManager::ReexportsEntry>
48 LazyCallThroughManager::findReexport(ExecutorAddr TrampolineAddr) {
49   std::lock_guard<std::mutex> Lock(LCTMMutex);
50   auto I = Reexports.find(TrampolineAddr);
51   if (I == Reexports.end())
52     return createStringError(inconvertibleErrorCode(),
53                              "Missing reexport for trampoline address %p" +
54                                  formatv("{0:x}", TrampolineAddr));
55   return I->second;
56 }
57 
58 Error LazyCallThroughManager::notifyResolved(ExecutorAddr TrampolineAddr,
59                                              ExecutorAddr ResolvedAddr) {
60   NotifyResolvedFunction NotifyResolved;
61   {
62     std::lock_guard<std::mutex> Lock(LCTMMutex);
63     auto I = Notifiers.find(TrampolineAddr);
64     if (I != Notifiers.end()) {
65       NotifyResolved = std::move(I->second);
66       Notifiers.erase(I);
67     }
68   }
69 
70   return NotifyResolved ? NotifyResolved(ResolvedAddr) : Error::success();
71 }
72 
73 void LazyCallThroughManager::resolveTrampolineLandingAddress(
74     ExecutorAddr TrampolineAddr,
75     NotifyLandingResolvedFunction NotifyLandingResolved) {
76 
77   auto Entry = findReexport(TrampolineAddr);
78   if (!Entry)
79     return NotifyLandingResolved(reportCallThroughError(Entry.takeError()));
80 
81   // Declaring SLS and the callback outside of the call to ES.lookup is a
82   // workaround to fix build failures on AIX and on z/OS platforms.
83   SymbolLookupSet SLS({Entry->SymbolName});
84   auto Callback = [this, TrampolineAddr, SymbolName = Entry->SymbolName,
85                    NotifyLandingResolved = std::move(NotifyLandingResolved)](
86                       Expected<SymbolMap> Result) mutable {
87     if (Result) {
88       assert(Result->size() == 1 && "Unexpected result size");
89       assert(Result->count(SymbolName) && "Unexpected result value");
90       ExecutorAddr LandingAddr = (*Result)[SymbolName].getAddress();
91 
92       if (auto Err = notifyResolved(TrampolineAddr, LandingAddr))
93         NotifyLandingResolved(reportCallThroughError(std::move(Err)));
94       else
95         NotifyLandingResolved(LandingAddr);
96     } else {
97       NotifyLandingResolved(reportCallThroughError(Result.takeError()));
98     }
99   };
100 
101   ES.lookup(LookupKind::Static,
102             makeJITDylibSearchOrder(Entry->SourceJD,
103                                     JITDylibLookupFlags::MatchAllSymbols),
104             std::move(SLS), SymbolState::Ready, std::move(Callback),
105             NoDependenciesToRegister);
106 }
107 
108 Expected<std::unique_ptr<LazyCallThroughManager>>
109 createLocalLazyCallThroughManager(const Triple &T, ExecutionSession &ES,
110                                   ExecutorAddr ErrorHandlerAddr) {
111   switch (T.getArch()) {
112   default:
113     return make_error<StringError>(
114         std::string("No callback manager available for ") + T.str(),
115         inconvertibleErrorCode());
116 
117   case Triple::aarch64:
118   case Triple::aarch64_32:
119     return LocalLazyCallThroughManager::Create<OrcAArch64>(ES,
120                                                            ErrorHandlerAddr);
121 
122   case Triple::x86:
123     return LocalLazyCallThroughManager::Create<OrcI386>(ES, ErrorHandlerAddr);
124 
125   case Triple::loongarch64:
126     return LocalLazyCallThroughManager::Create<OrcLoongArch64>(
127         ES, ErrorHandlerAddr);
128 
129   case Triple::mips:
130     return LocalLazyCallThroughManager::Create<OrcMips32Be>(ES,
131                                                             ErrorHandlerAddr);
132 
133   case Triple::mipsel:
134     return LocalLazyCallThroughManager::Create<OrcMips32Le>(ES,
135                                                             ErrorHandlerAddr);
136 
137   case Triple::mips64:
138   case Triple::mips64el:
139     return LocalLazyCallThroughManager::Create<OrcMips64>(ES, ErrorHandlerAddr);
140 
141   case Triple::riscv64:
142     return LocalLazyCallThroughManager::Create<OrcRiscv64>(ES,
143                                                            ErrorHandlerAddr);
144 
145   case Triple::x86_64:
146     if (T.getOS() == Triple::OSType::Win32)
147       return LocalLazyCallThroughManager::Create<OrcX86_64_Win32>(
148           ES, ErrorHandlerAddr);
149     else
150       return LocalLazyCallThroughManager::Create<OrcX86_64_SysV>(
151           ES, ErrorHandlerAddr);
152   }
153 }
154 
155 LazyReexportsMaterializationUnit::LazyReexportsMaterializationUnit(
156     LazyCallThroughManager &LCTManager, RedirectableSymbolManager &RSManager,
157     JITDylib &SourceJD, SymbolAliasMap CallableAliases, ImplSymbolMap *SrcJDLoc)
158     : MaterializationUnit(extractFlags(CallableAliases)),
159       LCTManager(LCTManager), RSManager(RSManager), SourceJD(SourceJD),
160       CallableAliases(std::move(CallableAliases)), AliaseeTable(SrcJDLoc) {}
161 
162 StringRef LazyReexportsMaterializationUnit::getName() const {
163   return "<Lazy Reexports>";
164 }
165 
166 void LazyReexportsMaterializationUnit::materialize(
167     std::unique_ptr<MaterializationResponsibility> R) {
168   auto RequestedSymbols = R->getRequestedSymbols();
169 
170   SymbolAliasMap RequestedAliases;
171   for (auto &RequestedSymbol : RequestedSymbols) {
172     auto I = CallableAliases.find(RequestedSymbol);
173     assert(I != CallableAliases.end() && "Symbol not found in alias map?");
174     RequestedAliases[I->first] = std::move(I->second);
175     CallableAliases.erase(I);
176   }
177 
178   if (!CallableAliases.empty())
179     if (auto Err = R->replace(lazyReexports(LCTManager, RSManager, SourceJD,
180                                             std::move(CallableAliases),
181                                             AliaseeTable))) {
182       R->getExecutionSession().reportError(std::move(Err));
183       R->failMaterialization();
184       return;
185     }
186 
187   SymbolMap Inits;
188   for (auto &Alias : RequestedAliases) {
189     auto CallThroughTrampoline = LCTManager.getCallThroughTrampoline(
190         SourceJD, Alias.second.Aliasee,
191         [&TargetJD = R->getTargetJITDylib(), &RSManager = this->RSManager,
192          StubSym = Alias.first](ExecutorAddr ResolvedAddr) -> Error {
193           return RSManager.redirect(TargetJD, StubSym,
194                                     ExecutorSymbolDef(ResolvedAddr, {}));
195         });
196 
197     if (!CallThroughTrampoline) {
198       R->getExecutionSession().reportError(CallThroughTrampoline.takeError());
199       R->failMaterialization();
200       return;
201     }
202 
203     Inits[Alias.first] = {*CallThroughTrampoline, Alias.second.AliasFlags};
204   }
205 
206   if (AliaseeTable != nullptr && !RequestedAliases.empty())
207     AliaseeTable->trackImpls(RequestedAliases, &SourceJD);
208 
209   if (auto Err = R->replace(std::make_unique<RedirectableMaterializationUnit>(
210           RSManager, std::move(Inits)))) {
211     R->getExecutionSession().reportError(std::move(Err));
212     return R->failMaterialization();
213   }
214 }
215 
216 void LazyReexportsMaterializationUnit::discard(const JITDylib &JD,
217                                                const SymbolStringPtr &Name) {
218   assert(CallableAliases.count(Name) &&
219          "Symbol not covered by this MaterializationUnit");
220   CallableAliases.erase(Name);
221 }
222 
223 MaterializationUnit::Interface
224 LazyReexportsMaterializationUnit::extractFlags(const SymbolAliasMap &Aliases) {
225   SymbolFlagsMap SymbolFlags;
226   for (auto &KV : Aliases) {
227     assert(KV.second.AliasFlags.isCallable() &&
228            "Lazy re-exports must be callable symbols");
229     SymbolFlags[KV.first] = KV.second.AliasFlags;
230   }
231   return MaterializationUnit::Interface(std::move(SymbolFlags), nullptr);
232 }
233 
234 class LazyReexportsManager::MU : public MaterializationUnit {
235 public:
236   MU(LazyReexportsManager &LRMgr, SymbolAliasMap Reexports)
237       : MaterializationUnit(getInterface(Reexports)), LRMgr(LRMgr),
238         Reexports(std::move(Reexports)) {}
239 
240 private:
241   Interface getInterface(const SymbolAliasMap &Reexports) {
242     SymbolFlagsMap SF;
243     for (auto &[Alias, AI] : Reexports)
244       SF[Alias] = AI.AliasFlags;
245     return {std::move(SF), nullptr};
246   }
247 
248   StringRef getName() const override { return "LazyReexportsManager::MU"; }
249 
250   void materialize(std::unique_ptr<MaterializationResponsibility> R) override {
251     LRMgr.emitReentryTrampolines(std::move(R), std::move(Reexports));
252   }
253 
254   void discard(const JITDylib &JD, const SymbolStringPtr &Name) override {
255     Reexports.erase(Name);
256   }
257 
258   LazyReexportsManager &LRMgr;
259   SymbolAliasMap Reexports;
260 };
261 
262 class LazyReexportsManager::Plugin : public ObjectLinkingLayer::Plugin {
263 public:
264   void modifyPassConfig(MaterializationResponsibility &MR,
265                         jitlink::LinkGraph &G,
266                         jitlink::PassConfiguration &Config) override {}
267 
268   Error notifyFailed(MaterializationResponsibility &MR) override {
269     return Error::success();
270   }
271 
272   Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override {
273     return Error::success();
274   }
275 
276   void notifyTransferringResources(JITDylib &JD, ResourceKey DstKey,
277                                    ResourceKey SrcKey) override {}
278 
279 private:
280   std::mutex M;
281 };
282 
283 LazyReexportsManager::Listener::~Listener() = default;
284 
285 Expected<std::unique_ptr<LazyReexportsManager>>
286 LazyReexportsManager::Create(EmitTrampolinesFn EmitTrampolines,
287                              RedirectableSymbolManager &RSMgr,
288                              JITDylib &PlatformJD, Listener *L) {
289   Error Err = Error::success();
290   std::unique_ptr<LazyReexportsManager> LRM(new LazyReexportsManager(
291       std::move(EmitTrampolines), RSMgr, PlatformJD, L, Err));
292   if (Err)
293     return std::move(Err);
294   return std::move(LRM);
295 }
296 
297 Error LazyReexportsManager::handleRemoveResources(JITDylib &JD, ResourceKey K) {
298   return JD.getExecutionSession().runSessionLocked([&]() -> Error {
299     auto I = KeyToReentryAddrs.find(K);
300     if (I == KeyToReentryAddrs.end())
301       return Error::success();
302 
303     auto &ReentryAddrs = I->second;
304     for (auto &ReentryAddr : ReentryAddrs) {
305       assert(CallThroughs.count(ReentryAddr) && "CallTrhough missing");
306       CallThroughs.erase(ReentryAddr);
307     }
308     KeyToReentryAddrs.erase(I);
309     return L ? L->onLazyReexportsRemoved(JD, K) : Error::success();
310   });
311 }
312 
313 void LazyReexportsManager::handleTransferResources(JITDylib &JD,
314                                                    ResourceKey DstK,
315                                                    ResourceKey SrcK) {
316   auto I = KeyToReentryAddrs.find(SrcK);
317   if (I != KeyToReentryAddrs.end()) {
318     auto J = KeyToReentryAddrs.find(DstK);
319     if (J == KeyToReentryAddrs.end()) {
320       auto Tmp = std::move(I->second);
321       KeyToReentryAddrs.erase(I);
322       KeyToReentryAddrs[DstK] = std::move(Tmp);
323     } else {
324       auto &SrcAddrs = I->second;
325       auto &DstAddrs = J->second;
326       DstAddrs.insert(DstAddrs.end(), SrcAddrs.begin(), SrcAddrs.end());
327       KeyToReentryAddrs.erase(I);
328     }
329     if (L)
330       L->onLazyReexportsTransfered(JD, DstK, SrcK);
331   }
332 }
333 
334 LazyReexportsManager::LazyReexportsManager(EmitTrampolinesFn EmitTrampolines,
335                                            RedirectableSymbolManager &RSMgr,
336                                            JITDylib &PlatformJD, Listener *L,
337                                            Error &Err)
338     : ES(PlatformJD.getExecutionSession()),
339       EmitTrampolines(std::move(EmitTrampolines)), RSMgr(RSMgr), L(L) {
340 
341   using namespace shared;
342 
343   ErrorAsOutParameter _(&Err);
344 
345   ExecutionSession::JITDispatchHandlerAssociationMap WFs;
346 
347   WFs[ES.intern("__orc_rt_resolve_tag")] =
348       ES.wrapAsyncWithSPS<SPSExpected<SPSExecutorSymbolDef>(SPSExecutorAddr)>(
349           this, &LazyReexportsManager::resolve);
350 
351   Err = ES.registerJITDispatchHandlers(PlatformJD, std::move(WFs));
352 }
353 
354 std::unique_ptr<MaterializationUnit>
355 LazyReexportsManager::createLazyReexports(SymbolAliasMap Reexports) {
356   return std::make_unique<MU>(*this, std::move(Reexports));
357 }
358 
359 void LazyReexportsManager::emitReentryTrampolines(
360     std::unique_ptr<MaterializationResponsibility> MR,
361     SymbolAliasMap Reexports) {
362   size_t NumTrampolines = Reexports.size();
363   auto RT = MR->getResourceTracker();
364   EmitTrampolines(
365       std::move(RT), NumTrampolines,
366       [this, MR = std::move(MR), Reexports = std::move(Reexports)](
367           Expected<std::vector<ExecutorSymbolDef>> ReentryPoints) mutable {
368         emitRedirectableSymbols(std::move(MR), std::move(Reexports),
369                                 std::move(ReentryPoints));
370       });
371 }
372 
373 void LazyReexportsManager::emitRedirectableSymbols(
374     std::unique_ptr<MaterializationResponsibility> MR, SymbolAliasMap Reexports,
375     Expected<std::vector<ExecutorSymbolDef>> ReentryPoints) {
376 
377   if (!ReentryPoints) {
378     MR->getExecutionSession().reportError(ReentryPoints.takeError());
379     MR->failMaterialization();
380     return;
381   }
382 
383   assert(Reexports.size() == ReentryPoints->size() &&
384          "Number of reentry points doesn't match number of reexports");
385 
386   // Bind entry points to names.
387   SymbolMap Redirs;
388   size_t I = 0;
389   for (auto &[Name, AI] : Reexports)
390     Redirs[Name] = (*ReentryPoints)[I++];
391 
392   I = 0;
393   if (!Reexports.empty()) {
394     if (auto Err = MR->withResourceKeyDo([&](ResourceKey K) {
395           auto &JD = MR->getTargetJITDylib();
396           auto &ReentryAddrsForK = KeyToReentryAddrs[K];
397           for (auto &[Name, AI] : Reexports) {
398             const auto &ReentryPoint = (*ReentryPoints)[I++];
399             CallThroughs[ReentryPoint.getAddress()] = {&JD, Name, AI.Aliasee};
400             ReentryAddrsForK.push_back(ReentryPoint.getAddress());
401           }
402           if (L)
403             L->onLazyReexportsCreated(JD, K, Reexports);
404         })) {
405       MR->getExecutionSession().reportError(std::move(Err));
406       MR->failMaterialization();
407       return;
408     }
409   }
410 
411   RSMgr.emitRedirectableSymbols(std::move(MR), std::move(Redirs));
412 }
413 
414 void LazyReexportsManager::resolve(ResolveSendResultFn SendResult,
415                                    ExecutorAddr ReentryStubAddr) {
416 
417   CallThroughInfo LandingInfo;
418 
419   ES.runSessionLocked([&]() {
420     auto I = CallThroughs.find(ReentryStubAddr);
421     if (I == CallThroughs.end())
422       return SendResult(make_error<StringError>(
423           "Reentry address " + formatv("{0:x}", ReentryStubAddr) +
424               " not registered",
425           inconvertibleErrorCode()));
426     LandingInfo = I->second;
427   });
428 
429   if (L)
430     L->onLazyReexportCalled(LandingInfo);
431 
432   SymbolInstance LandingSym(LandingInfo.JD, std::move(LandingInfo.BodyName));
433   LandingSym.lookupAsync([this, JD = std::move(LandingInfo.JD),
434                           ReentryName = std::move(LandingInfo.Name),
435                           SendResult = std::move(SendResult)](
436                              Expected<ExecutorSymbolDef> Result) mutable {
437     if (Result) {
438       // FIXME: Make RedirectionManager operations async, then use the async
439       //        APIs here.
440       if (auto Err = RSMgr.redirect(*JD, ReentryName, *Result))
441         SendResult(std::move(Err));
442       else
443         SendResult(std::move(Result));
444     } else
445       SendResult(std::move(Result));
446   });
447 }
448 
449 class SimpleLazyReexportsSpeculator::SpeculateTask : public IdleTask {
450 public:
451   SpeculateTask(std::weak_ptr<SimpleLazyReexportsSpeculator> Speculator)
452       : Speculator(std::move(Speculator)) {}
453 
454   void printDescription(raw_ostream &OS) override {
455     OS << "Speculative Lookup Task";
456   }
457 
458   void run() override {
459     if (auto S = Speculator.lock())
460       S->doNextSpeculativeLookup();
461   }
462 
463 private:
464   std::weak_ptr<SimpleLazyReexportsSpeculator> Speculator;
465 };
466 
467 SimpleLazyReexportsSpeculator::~SimpleLazyReexportsSpeculator() {
468   for (auto &[JD, _] : LazyReexports)
469     JITDylibSP(JD)->Release();
470 }
471 
472 void SimpleLazyReexportsSpeculator::onLazyReexportsCreated(
473     JITDylib &JD, ResourceKey K, const SymbolAliasMap &Reexports) {
474   if (!LazyReexports.count(&JD))
475     JD.Retain();
476   auto &BodiesVec = LazyReexports[&JD][K];
477   for (auto &[Name, AI] : Reexports)
478     BodiesVec.push_back(AI.Aliasee);
479   if (!SpeculateTaskActive) {
480     SpeculateTaskActive = true;
481     ES.dispatchTask(std::make_unique<SpeculateTask>(WeakThis));
482   }
483 }
484 
485 void SimpleLazyReexportsSpeculator::onLazyReexportsTransfered(
486     JITDylib &JD, ResourceKey DstK, ResourceKey SrcK) {
487 
488   auto I = LazyReexports.find(&JD);
489   if (I == LazyReexports.end())
490     return;
491 
492   auto &MapForJD = I->second;
493   auto J = MapForJD.find(SrcK);
494   if (J == MapForJD.end())
495     return;
496 
497   // We have something to transfer.
498   auto K = MapForJD.find(DstK);
499   if (K == MapForJD.end()) {
500     auto Tmp = std::move(J->second);
501     MapForJD.erase(J);
502     MapForJD[DstK] = std::move(Tmp);
503   } else {
504     auto &SrcNames = J->second;
505     auto &DstNames = K->second;
506     DstNames.insert(DstNames.end(), SrcNames.begin(), SrcNames.end());
507     MapForJD.erase(J);
508   }
509 }
510 
511 Error SimpleLazyReexportsSpeculator::onLazyReexportsRemoved(JITDylib &JD,
512                                                             ResourceKey K) {
513 
514   auto I = LazyReexports.find(&JD);
515   if (I == LazyReexports.end())
516     return Error::success();
517 
518   auto &MapForJD = I->second;
519   MapForJD.erase(K);
520 
521   if (MapForJD.empty()) {
522     LazyReexports.erase(I);
523     JD.Release();
524   }
525 
526   return Error::success();
527 }
528 
529 void SimpleLazyReexportsSpeculator::onLazyReexportCalled(
530     const CallThroughInfo &CTI) {
531   if (RecordExec)
532     RecordExec(CTI);
533 }
534 
535 void SimpleLazyReexportsSpeculator::addSpeculationSuggestions(
536     std::vector<std::pair<std::string, SymbolStringPtr>> NewSuggestions) {
537   ES.runSessionLocked([&]() {
538     for (auto &[JDName, SymbolName] : NewSuggestions)
539       SpeculateSuggestions.push_back(
540           {std::move(JDName), std::move(SymbolName)});
541   });
542 }
543 
544 bool SimpleLazyReexportsSpeculator::doNextSpeculativeLookup() {
545   // Use existing speculation queue if available, otherwise take the next
546   // element from LazyReexports.
547   JITDylibSP SpeculateJD = nullptr;
548   SymbolStringPtr SpeculateFn;
549 
550   auto SpeculateAgain = ES.runSessionLocked([&]() {
551     while (!SpeculateSuggestions.empty()) {
552       auto [JDName, SymbolName] = std::move(SpeculateSuggestions.front());
553       SpeculateSuggestions.pop_front();
554 
555       if (auto *JD = ES.getJITDylibByName(JDName)) {
556         SpeculateJD = JD;
557         SpeculateFn = std::move(SymbolName);
558         break;
559       }
560     }
561 
562     if (!SpeculateJD) {
563       assert(!LazyReexports.empty() && "LazyReexports map is empty");
564       auto LRItr =
565           std::next(LazyReexports.begin(), rand() % LazyReexports.size());
566       auto &[JD, KeyToFnBodies] = *LRItr;
567 
568       assert(!KeyToFnBodies.empty() && "Key to function bodies map empty");
569       auto KeyToFnBodiesItr =
570           std::next(KeyToFnBodies.begin(), rand() % KeyToFnBodies.size());
571       auto &[Key, FnBodies] = *KeyToFnBodiesItr;
572 
573       assert(!FnBodies.empty() && "Function bodies list empty");
574       auto FnBodyItr = std::next(FnBodies.begin(), rand() % FnBodies.size());
575 
576       SpeculateJD = JITDylibSP(JD);
577       SpeculateFn = std::move(*FnBodyItr);
578 
579       FnBodies.erase(FnBodyItr);
580       if (FnBodies.empty()) {
581         KeyToFnBodies.erase(KeyToFnBodiesItr);
582         if (KeyToFnBodies.empty()) {
583           LRItr->first->Release();
584           LazyReexports.erase(LRItr);
585         }
586       }
587     }
588 
589     SpeculateTaskActive =
590         !SpeculateSuggestions.empty() || !LazyReexports.empty();
591     return SpeculateTaskActive;
592   });
593 
594   LLVM_DEBUG({
595     dbgs() << "Issuing speculative lookup for ( " << SpeculateJD->getName()
596            << ", " << SpeculateFn << " )...\n";
597   });
598 
599   ES.lookup(
600       LookupKind::Static, makeJITDylibSearchOrder(SpeculateJD.get()),
601       {{std::move(SpeculateFn), SymbolLookupFlags::WeaklyReferencedSymbol}},
602       SymbolState::Ready,
603       [](Expected<SymbolMap> Result) { consumeError(Result.takeError()); },
604       NoDependenciesToRegister);
605 
606   if (SpeculateAgain)
607     ES.dispatchTask(std::make_unique<SpeculateTask>(WeakThis));
608 
609   return false;
610 }
611 
612 } // End namespace orc.
613 } // End namespace llvm.
614