614 lines
20 KiB
C++
614 lines
20 KiB
C++
//===---------- LazyReexports.cpp - Utilities for lazy reexports ----------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/ExecutionEngine/Orc/LazyReexports.h"
|
|
|
|
#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
|
|
#include "llvm/ExecutionEngine/Orc/OrcABISupport.h"
|
|
#include "llvm/ExecutionEngine/Orc/Shared/SimplePackedSerialization.h"
|
|
#include "llvm/TargetParser/Triple.h"
|
|
|
|
#define DEBUG_TYPE "orc"
|
|
|
|
namespace llvm {
|
|
namespace orc {
|
|
|
|
LazyCallThroughManager::LazyCallThroughManager(ExecutionSession &ES,
|
|
ExecutorAddr ErrorHandlerAddr,
|
|
TrampolinePool *TP)
|
|
: ES(ES), ErrorHandlerAddr(ErrorHandlerAddr), TP(TP) {}
|
|
|
|
Expected<ExecutorAddr> LazyCallThroughManager::getCallThroughTrampoline(
|
|
JITDylib &SourceJD, SymbolStringPtr SymbolName,
|
|
NotifyResolvedFunction NotifyResolved) {
|
|
assert(TP && "TrampolinePool not set");
|
|
|
|
std::lock_guard<std::mutex> Lock(LCTMMutex);
|
|
auto Trampoline = TP->getTrampoline();
|
|
|
|
if (!Trampoline)
|
|
return Trampoline.takeError();
|
|
|
|
Reexports[*Trampoline] = ReexportsEntry{&SourceJD, std::move(SymbolName)};
|
|
Notifiers[*Trampoline] = std::move(NotifyResolved);
|
|
return *Trampoline;
|
|
}
|
|
|
|
ExecutorAddr LazyCallThroughManager::reportCallThroughError(Error Err) {
|
|
ES.reportError(std::move(Err));
|
|
return ErrorHandlerAddr;
|
|
}
|
|
|
|
Expected<LazyCallThroughManager::ReexportsEntry>
|
|
LazyCallThroughManager::findReexport(ExecutorAddr TrampolineAddr) {
|
|
std::lock_guard<std::mutex> Lock(LCTMMutex);
|
|
auto I = Reexports.find(TrampolineAddr);
|
|
if (I == Reexports.end())
|
|
return createStringError(inconvertibleErrorCode(),
|
|
"Missing reexport for trampoline address %p" +
|
|
formatv("{0:x}", TrampolineAddr));
|
|
return I->second;
|
|
}
|
|
|
|
Error LazyCallThroughManager::notifyResolved(ExecutorAddr TrampolineAddr,
|
|
ExecutorAddr ResolvedAddr) {
|
|
NotifyResolvedFunction NotifyResolved;
|
|
{
|
|
std::lock_guard<std::mutex> Lock(LCTMMutex);
|
|
auto I = Notifiers.find(TrampolineAddr);
|
|
if (I != Notifiers.end()) {
|
|
NotifyResolved = std::move(I->second);
|
|
Notifiers.erase(I);
|
|
}
|
|
}
|
|
|
|
return NotifyResolved ? NotifyResolved(ResolvedAddr) : Error::success();
|
|
}
|
|
|
|
void LazyCallThroughManager::resolveTrampolineLandingAddress(
|
|
ExecutorAddr TrampolineAddr,
|
|
NotifyLandingResolvedFunction NotifyLandingResolved) {
|
|
|
|
auto Entry = findReexport(TrampolineAddr);
|
|
if (!Entry)
|
|
return NotifyLandingResolved(reportCallThroughError(Entry.takeError()));
|
|
|
|
// Declaring SLS and the callback outside of the call to ES.lookup is a
|
|
// workaround to fix build failures on AIX and on z/OS platforms.
|
|
SymbolLookupSet SLS({Entry->SymbolName});
|
|
auto Callback = [this, TrampolineAddr, SymbolName = Entry->SymbolName,
|
|
NotifyLandingResolved = std::move(NotifyLandingResolved)](
|
|
Expected<SymbolMap> Result) mutable {
|
|
if (Result) {
|
|
assert(Result->size() == 1 && "Unexpected result size");
|
|
assert(Result->count(SymbolName) && "Unexpected result value");
|
|
ExecutorAddr LandingAddr = (*Result)[SymbolName].getAddress();
|
|
|
|
if (auto Err = notifyResolved(TrampolineAddr, LandingAddr))
|
|
NotifyLandingResolved(reportCallThroughError(std::move(Err)));
|
|
else
|
|
NotifyLandingResolved(LandingAddr);
|
|
} else {
|
|
NotifyLandingResolved(reportCallThroughError(Result.takeError()));
|
|
}
|
|
};
|
|
|
|
ES.lookup(LookupKind::Static,
|
|
makeJITDylibSearchOrder(Entry->SourceJD,
|
|
JITDylibLookupFlags::MatchAllSymbols),
|
|
std::move(SLS), SymbolState::Ready, std::move(Callback),
|
|
NoDependenciesToRegister);
|
|
}
|
|
|
|
Expected<std::unique_ptr<LazyCallThroughManager>>
|
|
createLocalLazyCallThroughManager(const Triple &T, ExecutionSession &ES,
|
|
ExecutorAddr ErrorHandlerAddr) {
|
|
switch (T.getArch()) {
|
|
default:
|
|
return make_error<StringError>(
|
|
std::string("No callback manager available for ") + T.str(),
|
|
inconvertibleErrorCode());
|
|
|
|
case Triple::aarch64:
|
|
case Triple::aarch64_32:
|
|
return LocalLazyCallThroughManager::Create<OrcAArch64>(ES,
|
|
ErrorHandlerAddr);
|
|
|
|
case Triple::x86:
|
|
return LocalLazyCallThroughManager::Create<OrcI386>(ES, ErrorHandlerAddr);
|
|
|
|
case Triple::loongarch64:
|
|
return LocalLazyCallThroughManager::Create<OrcLoongArch64>(
|
|
ES, ErrorHandlerAddr);
|
|
|
|
case Triple::mips:
|
|
return LocalLazyCallThroughManager::Create<OrcMips32Be>(ES,
|
|
ErrorHandlerAddr);
|
|
|
|
case Triple::mipsel:
|
|
return LocalLazyCallThroughManager::Create<OrcMips32Le>(ES,
|
|
ErrorHandlerAddr);
|
|
|
|
case Triple::mips64:
|
|
case Triple::mips64el:
|
|
return LocalLazyCallThroughManager::Create<OrcMips64>(ES, ErrorHandlerAddr);
|
|
|
|
case Triple::riscv64:
|
|
return LocalLazyCallThroughManager::Create<OrcRiscv64>(ES,
|
|
ErrorHandlerAddr);
|
|
|
|
case Triple::x86_64:
|
|
if (T.getOS() == Triple::OSType::Win32)
|
|
return LocalLazyCallThroughManager::Create<OrcX86_64_Win32>(
|
|
ES, ErrorHandlerAddr);
|
|
else
|
|
return LocalLazyCallThroughManager::Create<OrcX86_64_SysV>(
|
|
ES, ErrorHandlerAddr);
|
|
}
|
|
}
|
|
|
|
LazyReexportsMaterializationUnit::LazyReexportsMaterializationUnit(
|
|
LazyCallThroughManager &LCTManager, RedirectableSymbolManager &RSManager,
|
|
JITDylib &SourceJD, SymbolAliasMap CallableAliases, ImplSymbolMap *SrcJDLoc)
|
|
: MaterializationUnit(extractFlags(CallableAliases)),
|
|
LCTManager(LCTManager), RSManager(RSManager), SourceJD(SourceJD),
|
|
CallableAliases(std::move(CallableAliases)), AliaseeTable(SrcJDLoc) {}
|
|
|
|
StringRef LazyReexportsMaterializationUnit::getName() const {
|
|
return "<Lazy Reexports>";
|
|
}
|
|
|
|
void LazyReexportsMaterializationUnit::materialize(
|
|
std::unique_ptr<MaterializationResponsibility> R) {
|
|
auto RequestedSymbols = R->getRequestedSymbols();
|
|
|
|
SymbolAliasMap RequestedAliases;
|
|
for (auto &RequestedSymbol : RequestedSymbols) {
|
|
auto I = CallableAliases.find(RequestedSymbol);
|
|
assert(I != CallableAliases.end() && "Symbol not found in alias map?");
|
|
RequestedAliases[I->first] = std::move(I->second);
|
|
CallableAliases.erase(I);
|
|
}
|
|
|
|
if (!CallableAliases.empty())
|
|
if (auto Err = R->replace(lazyReexports(LCTManager, RSManager, SourceJD,
|
|
std::move(CallableAliases),
|
|
AliaseeTable))) {
|
|
R->getExecutionSession().reportError(std::move(Err));
|
|
R->failMaterialization();
|
|
return;
|
|
}
|
|
|
|
SymbolMap Inits;
|
|
for (auto &Alias : RequestedAliases) {
|
|
auto CallThroughTrampoline = LCTManager.getCallThroughTrampoline(
|
|
SourceJD, Alias.second.Aliasee,
|
|
[&TargetJD = R->getTargetJITDylib(), &RSManager = this->RSManager,
|
|
StubSym = Alias.first](ExecutorAddr ResolvedAddr) -> Error {
|
|
return RSManager.redirect(TargetJD, StubSym,
|
|
ExecutorSymbolDef(ResolvedAddr, {}));
|
|
});
|
|
|
|
if (!CallThroughTrampoline) {
|
|
R->getExecutionSession().reportError(CallThroughTrampoline.takeError());
|
|
R->failMaterialization();
|
|
return;
|
|
}
|
|
|
|
Inits[Alias.first] = {*CallThroughTrampoline, Alias.second.AliasFlags};
|
|
}
|
|
|
|
if (AliaseeTable != nullptr && !RequestedAliases.empty())
|
|
AliaseeTable->trackImpls(RequestedAliases, &SourceJD);
|
|
|
|
if (auto Err = R->replace(std::make_unique<RedirectableMaterializationUnit>(
|
|
RSManager, std::move(Inits)))) {
|
|
R->getExecutionSession().reportError(std::move(Err));
|
|
return R->failMaterialization();
|
|
}
|
|
}
|
|
|
|
void LazyReexportsMaterializationUnit::discard(const JITDylib &JD,
|
|
const SymbolStringPtr &Name) {
|
|
assert(CallableAliases.count(Name) &&
|
|
"Symbol not covered by this MaterializationUnit");
|
|
CallableAliases.erase(Name);
|
|
}
|
|
|
|
MaterializationUnit::Interface
|
|
LazyReexportsMaterializationUnit::extractFlags(const SymbolAliasMap &Aliases) {
|
|
SymbolFlagsMap SymbolFlags;
|
|
for (auto &KV : Aliases) {
|
|
assert(KV.second.AliasFlags.isCallable() &&
|
|
"Lazy re-exports must be callable symbols");
|
|
SymbolFlags[KV.first] = KV.second.AliasFlags;
|
|
}
|
|
return MaterializationUnit::Interface(std::move(SymbolFlags), nullptr);
|
|
}
|
|
|
|
class LazyReexportsManager::MU : public MaterializationUnit {
|
|
public:
|
|
MU(LazyReexportsManager &LRMgr, SymbolAliasMap Reexports)
|
|
: MaterializationUnit(getInterface(Reexports)), LRMgr(LRMgr),
|
|
Reexports(std::move(Reexports)) {}
|
|
|
|
private:
|
|
Interface getInterface(const SymbolAliasMap &Reexports) {
|
|
SymbolFlagsMap SF;
|
|
for (auto &[Alias, AI] : Reexports)
|
|
SF[Alias] = AI.AliasFlags;
|
|
return {std::move(SF), nullptr};
|
|
}
|
|
|
|
StringRef getName() const override { return "LazyReexportsManager::MU"; }
|
|
|
|
void materialize(std::unique_ptr<MaterializationResponsibility> R) override {
|
|
LRMgr.emitReentryTrampolines(std::move(R), std::move(Reexports));
|
|
}
|
|
|
|
void discard(const JITDylib &JD, const SymbolStringPtr &Name) override {
|
|
Reexports.erase(Name);
|
|
}
|
|
|
|
LazyReexportsManager &LRMgr;
|
|
SymbolAliasMap Reexports;
|
|
};
|
|
|
|
class LazyReexportsManager::Plugin : public ObjectLinkingLayer::Plugin {
|
|
public:
|
|
void modifyPassConfig(MaterializationResponsibility &MR,
|
|
jitlink::LinkGraph &G,
|
|
jitlink::PassConfiguration &Config) override {}
|
|
|
|
Error notifyFailed(MaterializationResponsibility &MR) override {
|
|
return Error::success();
|
|
}
|
|
|
|
Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override {
|
|
return Error::success();
|
|
}
|
|
|
|
void notifyTransferringResources(JITDylib &JD, ResourceKey DstKey,
|
|
ResourceKey SrcKey) override {}
|
|
|
|
private:
|
|
std::mutex M;
|
|
};
|
|
|
|
LazyReexportsManager::Listener::~Listener() = default;
|
|
|
|
Expected<std::unique_ptr<LazyReexportsManager>>
|
|
LazyReexportsManager::Create(EmitTrampolinesFn EmitTrampolines,
|
|
RedirectableSymbolManager &RSMgr,
|
|
JITDylib &PlatformJD, Listener *L) {
|
|
Error Err = Error::success();
|
|
std::unique_ptr<LazyReexportsManager> LRM(new LazyReexportsManager(
|
|
std::move(EmitTrampolines), RSMgr, PlatformJD, L, Err));
|
|
if (Err)
|
|
return std::move(Err);
|
|
return std::move(LRM);
|
|
}
|
|
|
|
Error LazyReexportsManager::handleRemoveResources(JITDylib &JD, ResourceKey K) {
|
|
return JD.getExecutionSession().runSessionLocked([&]() -> Error {
|
|
auto I = KeyToReentryAddrs.find(K);
|
|
if (I == KeyToReentryAddrs.end())
|
|
return Error::success();
|
|
|
|
auto &ReentryAddrs = I->second;
|
|
for (auto &ReentryAddr : ReentryAddrs) {
|
|
assert(CallThroughs.count(ReentryAddr) && "CallTrhough missing");
|
|
CallThroughs.erase(ReentryAddr);
|
|
}
|
|
KeyToReentryAddrs.erase(I);
|
|
return L ? L->onLazyReexportsRemoved(JD, K) : Error::success();
|
|
});
|
|
}
|
|
|
|
void LazyReexportsManager::handleTransferResources(JITDylib &JD,
|
|
ResourceKey DstK,
|
|
ResourceKey SrcK) {
|
|
auto I = KeyToReentryAddrs.find(SrcK);
|
|
if (I != KeyToReentryAddrs.end()) {
|
|
auto J = KeyToReentryAddrs.find(DstK);
|
|
if (J == KeyToReentryAddrs.end()) {
|
|
auto Tmp = std::move(I->second);
|
|
KeyToReentryAddrs.erase(I);
|
|
KeyToReentryAddrs[DstK] = std::move(Tmp);
|
|
} else {
|
|
auto &SrcAddrs = I->second;
|
|
auto &DstAddrs = J->second;
|
|
llvm::append_range(DstAddrs, SrcAddrs);
|
|
KeyToReentryAddrs.erase(I);
|
|
}
|
|
if (L)
|
|
L->onLazyReexportsTransfered(JD, DstK, SrcK);
|
|
}
|
|
}
|
|
|
|
LazyReexportsManager::LazyReexportsManager(EmitTrampolinesFn EmitTrampolines,
|
|
RedirectableSymbolManager &RSMgr,
|
|
JITDylib &PlatformJD, Listener *L,
|
|
Error &Err)
|
|
: ES(PlatformJD.getExecutionSession()),
|
|
EmitTrampolines(std::move(EmitTrampolines)), RSMgr(RSMgr), L(L) {
|
|
|
|
using namespace shared;
|
|
|
|
ErrorAsOutParameter _(&Err);
|
|
|
|
ExecutionSession::JITDispatchHandlerAssociationMap WFs;
|
|
|
|
WFs[ES.intern("__orc_rt_resolve_tag")] =
|
|
ES.wrapAsyncWithSPS<SPSExpected<SPSExecutorSymbolDef>(SPSExecutorAddr)>(
|
|
this, &LazyReexportsManager::resolve);
|
|
|
|
Err = ES.registerJITDispatchHandlers(PlatformJD, std::move(WFs));
|
|
}
|
|
|
|
std::unique_ptr<MaterializationUnit>
|
|
LazyReexportsManager::createLazyReexports(SymbolAliasMap Reexports) {
|
|
return std::make_unique<MU>(*this, std::move(Reexports));
|
|
}
|
|
|
|
void LazyReexportsManager::emitReentryTrampolines(
|
|
std::unique_ptr<MaterializationResponsibility> MR,
|
|
SymbolAliasMap Reexports) {
|
|
size_t NumTrampolines = Reexports.size();
|
|
auto RT = MR->getResourceTracker();
|
|
EmitTrampolines(
|
|
std::move(RT), NumTrampolines,
|
|
[this, MR = std::move(MR), Reexports = std::move(Reexports)](
|
|
Expected<std::vector<ExecutorSymbolDef>> ReentryPoints) mutable {
|
|
emitRedirectableSymbols(std::move(MR), std::move(Reexports),
|
|
std::move(ReentryPoints));
|
|
});
|
|
}
|
|
|
|
void LazyReexportsManager::emitRedirectableSymbols(
|
|
std::unique_ptr<MaterializationResponsibility> MR, SymbolAliasMap Reexports,
|
|
Expected<std::vector<ExecutorSymbolDef>> ReentryPoints) {
|
|
|
|
if (!ReentryPoints) {
|
|
MR->getExecutionSession().reportError(ReentryPoints.takeError());
|
|
MR->failMaterialization();
|
|
return;
|
|
}
|
|
|
|
assert(Reexports.size() == ReentryPoints->size() &&
|
|
"Number of reentry points doesn't match number of reexports");
|
|
|
|
// Bind entry points to names.
|
|
SymbolMap Redirs;
|
|
size_t I = 0;
|
|
for (auto &[Name, AI] : Reexports)
|
|
Redirs[Name] = {(*ReentryPoints)[I++].getAddress(), AI.AliasFlags};
|
|
|
|
I = 0;
|
|
if (!Reexports.empty()) {
|
|
if (auto Err = MR->withResourceKeyDo([&](ResourceKey K) {
|
|
auto &JD = MR->getTargetJITDylib();
|
|
auto &ReentryAddrsForK = KeyToReentryAddrs[K];
|
|
for (auto &[Name, AI] : Reexports) {
|
|
const auto &ReentryPoint = (*ReentryPoints)[I++];
|
|
CallThroughs[ReentryPoint.getAddress()] = {&JD, Name, AI.Aliasee};
|
|
ReentryAddrsForK.push_back(ReentryPoint.getAddress());
|
|
}
|
|
if (L)
|
|
L->onLazyReexportsCreated(JD, K, Reexports);
|
|
})) {
|
|
MR->getExecutionSession().reportError(std::move(Err));
|
|
MR->failMaterialization();
|
|
return;
|
|
}
|
|
}
|
|
|
|
RSMgr.emitRedirectableSymbols(std::move(MR), std::move(Redirs));
|
|
}
|
|
|
|
void LazyReexportsManager::resolve(ResolveSendResultFn SendResult,
|
|
ExecutorAddr ReentryStubAddr) {
|
|
|
|
CallThroughInfo LandingInfo;
|
|
|
|
ES.runSessionLocked([&]() {
|
|
auto I = CallThroughs.find(ReentryStubAddr);
|
|
if (I == CallThroughs.end())
|
|
return SendResult(make_error<StringError>(
|
|
"Reentry address " + formatv("{0:x}", ReentryStubAddr) +
|
|
" not registered",
|
|
inconvertibleErrorCode()));
|
|
LandingInfo = I->second;
|
|
});
|
|
|
|
if (L)
|
|
L->onLazyReexportCalled(LandingInfo);
|
|
|
|
SymbolInstance LandingSym(LandingInfo.JD, std::move(LandingInfo.BodyName));
|
|
LandingSym.lookupAsync([this, JD = std::move(LandingInfo.JD),
|
|
ReentryName = std::move(LandingInfo.Name),
|
|
SendResult = std::move(SendResult)](
|
|
Expected<ExecutorSymbolDef> Result) mutable {
|
|
if (Result) {
|
|
// FIXME: Make RedirectionManager operations async, then use the async
|
|
// APIs here.
|
|
if (auto Err = RSMgr.redirect(*JD, ReentryName, *Result))
|
|
SendResult(std::move(Err));
|
|
else
|
|
SendResult(std::move(Result));
|
|
} else
|
|
SendResult(std::move(Result));
|
|
});
|
|
}
|
|
|
|
class SimpleLazyReexportsSpeculator::SpeculateTask : public IdleTask {
|
|
public:
|
|
SpeculateTask(std::weak_ptr<SimpleLazyReexportsSpeculator> Speculator)
|
|
: Speculator(std::move(Speculator)) {}
|
|
|
|
void printDescription(raw_ostream &OS) override {
|
|
OS << "Speculative Lookup Task";
|
|
}
|
|
|
|
void run() override {
|
|
if (auto S = Speculator.lock())
|
|
S->doNextSpeculativeLookup();
|
|
}
|
|
|
|
private:
|
|
std::weak_ptr<SimpleLazyReexportsSpeculator> Speculator;
|
|
};
|
|
|
|
SimpleLazyReexportsSpeculator::~SimpleLazyReexportsSpeculator() {
|
|
for (auto &[JD, _] : LazyReexports)
|
|
JITDylibSP(JD)->Release();
|
|
}
|
|
|
|
void SimpleLazyReexportsSpeculator::onLazyReexportsCreated(
|
|
JITDylib &JD, ResourceKey K, const SymbolAliasMap &Reexports) {
|
|
if (!LazyReexports.count(&JD))
|
|
JD.Retain();
|
|
auto &BodiesVec = LazyReexports[&JD][K];
|
|
for (auto &[Name, AI] : Reexports)
|
|
BodiesVec.push_back(AI.Aliasee);
|
|
if (!SpeculateTaskActive) {
|
|
SpeculateTaskActive = true;
|
|
ES.dispatchTask(std::make_unique<SpeculateTask>(WeakThis));
|
|
}
|
|
}
|
|
|
|
void SimpleLazyReexportsSpeculator::onLazyReexportsTransfered(
|
|
JITDylib &JD, ResourceKey DstK, ResourceKey SrcK) {
|
|
|
|
auto I = LazyReexports.find(&JD);
|
|
if (I == LazyReexports.end())
|
|
return;
|
|
|
|
auto &MapForJD = I->second;
|
|
auto J = MapForJD.find(SrcK);
|
|
if (J == MapForJD.end())
|
|
return;
|
|
|
|
// We have something to transfer.
|
|
auto K = MapForJD.find(DstK);
|
|
if (K == MapForJD.end()) {
|
|
auto Tmp = std::move(J->second);
|
|
MapForJD.erase(J);
|
|
MapForJD[DstK] = std::move(Tmp);
|
|
} else {
|
|
auto &SrcNames = J->second;
|
|
auto &DstNames = K->second;
|
|
llvm::append_range(DstNames, SrcNames);
|
|
MapForJD.erase(J);
|
|
}
|
|
}
|
|
|
|
Error SimpleLazyReexportsSpeculator::onLazyReexportsRemoved(JITDylib &JD,
|
|
ResourceKey K) {
|
|
|
|
auto I = LazyReexports.find(&JD);
|
|
if (I == LazyReexports.end())
|
|
return Error::success();
|
|
|
|
auto &MapForJD = I->second;
|
|
MapForJD.erase(K);
|
|
|
|
if (MapForJD.empty()) {
|
|
LazyReexports.erase(I);
|
|
JD.Release();
|
|
}
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
void SimpleLazyReexportsSpeculator::onLazyReexportCalled(
|
|
const CallThroughInfo &CTI) {
|
|
if (RecordExec)
|
|
RecordExec(CTI);
|
|
}
|
|
|
|
void SimpleLazyReexportsSpeculator::addSpeculationSuggestions(
|
|
std::vector<std::pair<std::string, SymbolStringPtr>> NewSuggestions) {
|
|
ES.runSessionLocked([&]() {
|
|
for (auto &[JDName, SymbolName] : NewSuggestions)
|
|
SpeculateSuggestions.push_back(
|
|
{std::move(JDName), std::move(SymbolName)});
|
|
});
|
|
}
|
|
|
|
bool SimpleLazyReexportsSpeculator::doNextSpeculativeLookup() {
|
|
// Use existing speculation queue if available, otherwise take the next
|
|
// element from LazyReexports.
|
|
JITDylibSP SpeculateJD = nullptr;
|
|
SymbolStringPtr SpeculateFn;
|
|
|
|
auto SpeculateAgain = ES.runSessionLocked([&]() {
|
|
while (!SpeculateSuggestions.empty()) {
|
|
auto [JDName, SymbolName] = std::move(SpeculateSuggestions.front());
|
|
SpeculateSuggestions.pop_front();
|
|
|
|
if (auto *JD = ES.getJITDylibByName(JDName)) {
|
|
SpeculateJD = JD;
|
|
SpeculateFn = std::move(SymbolName);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!SpeculateJD) {
|
|
assert(!LazyReexports.empty() && "LazyReexports map is empty");
|
|
auto LRItr =
|
|
std::next(LazyReexports.begin(), rand() % LazyReexports.size());
|
|
auto &[JD, KeyToFnBodies] = *LRItr;
|
|
|
|
assert(!KeyToFnBodies.empty() && "Key to function bodies map empty");
|
|
auto KeyToFnBodiesItr =
|
|
std::next(KeyToFnBodies.begin(), rand() % KeyToFnBodies.size());
|
|
auto &[Key, FnBodies] = *KeyToFnBodiesItr;
|
|
|
|
assert(!FnBodies.empty() && "Function bodies list empty");
|
|
auto FnBodyItr = std::next(FnBodies.begin(), rand() % FnBodies.size());
|
|
|
|
SpeculateJD = JITDylibSP(JD);
|
|
SpeculateFn = std::move(*FnBodyItr);
|
|
|
|
FnBodies.erase(FnBodyItr);
|
|
if (FnBodies.empty()) {
|
|
KeyToFnBodies.erase(KeyToFnBodiesItr);
|
|
if (KeyToFnBodies.empty()) {
|
|
LRItr->first->Release();
|
|
LazyReexports.erase(LRItr);
|
|
}
|
|
}
|
|
}
|
|
|
|
SpeculateTaskActive =
|
|
!SpeculateSuggestions.empty() || !LazyReexports.empty();
|
|
return SpeculateTaskActive;
|
|
});
|
|
|
|
LLVM_DEBUG({
|
|
dbgs() << "Issuing speculative lookup for ( " << SpeculateJD->getName()
|
|
<< ", " << SpeculateFn << " )...\n";
|
|
});
|
|
|
|
ES.lookup(
|
|
LookupKind::Static, makeJITDylibSearchOrder(SpeculateJD.get()),
|
|
{{std::move(SpeculateFn), SymbolLookupFlags::WeaklyReferencedSymbol}},
|
|
SymbolState::Ready,
|
|
[](Expected<SymbolMap> Result) { consumeError(Result.takeError()); },
|
|
NoDependenciesToRegister);
|
|
|
|
if (SpeculateAgain)
|
|
ES.dispatchTask(std::make_unique<SpeculateTask>(WeakThis));
|
|
|
|
return false;
|
|
}
|
|
|
|
} // End namespace orc.
|
|
} // End namespace llvm.
|