1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-23 03:02:36 +01:00

[ORC] Merge ExecutionSessionBase with ExecutionSession by moving a couple of

template methods in JITDylib out-of-line.

This also splits JITDylib::define into a pair of template methods, one taking an
lvalue reference and the other an rvalue reference. This simplifies the
templates at the cost of a small amount of code duplication.

llvm-svn: 342087
This commit is contained in:
Lang Hames 2018-09-12 21:49:02 +00:00
parent 0c2a797c97
commit 1771029439
3 changed files with 576 additions and 540 deletions

View File

@ -366,146 +366,12 @@ private:
SymbolPredicate Allow;
};
/// Base utilities for ExecutionSession.
class ExecutionSessionBase {
// FIXME: Remove this when we remove the old ORC layers.
friend class JITDylib;
public:
/// For reporting errors.
using ErrorReporter = std::function<void(Error)>;
/// For dispatching MaterializationUnit::materialize calls.
using DispatchMaterializationFunction = std::function<void(
JITDylib &JD, std::unique_ptr<MaterializationUnit> MU)>;
/// Construct an ExecutionSessionBase.
///
/// SymbolStringPools may be shared between ExecutionSessions.
ExecutionSessionBase(std::shared_ptr<SymbolStringPool> SSP = nullptr)
: SSP(SSP ? std::move(SSP) : std::make_shared<SymbolStringPool>()) {}
/// Returns the SymbolStringPool for this ExecutionSession.
SymbolStringPool &getSymbolStringPool() const { return *SSP; }
/// Run the given lambda with the session mutex locked.
template <typename Func> auto runSessionLocked(Func &&F) -> decltype(F()) {
std::lock_guard<std::recursive_mutex> Lock(SessionMutex);
return F();
}
/// Set the error reporter function.
ExecutionSessionBase &setErrorReporter(ErrorReporter ReportError) {
this->ReportError = std::move(ReportError);
return *this;
}
/// Set the materialization dispatch function.
ExecutionSessionBase &setDispatchMaterialization(
DispatchMaterializationFunction DispatchMaterialization) {
this->DispatchMaterialization = std::move(DispatchMaterialization);
return *this;
}
/// Report a error for this execution session.
///
/// Unhandled errors can be sent here to log them.
void reportError(Error Err) { ReportError(std::move(Err)); }
/// Allocate a module key for a new module to add to the JIT.
VModuleKey allocateVModule() { return ++LastKey; }
/// Return a module key to the ExecutionSession so that it can be
/// re-used. This should only be done once all resources associated
/// with the original key have been released.
void releaseVModule(VModuleKey Key) { /* FIXME: Recycle keys */
}
void legacyFailQuery(AsynchronousSymbolQuery &Q, Error Err);
using LegacyAsyncLookupFunction = std::function<SymbolNameSet(
std::shared_ptr<AsynchronousSymbolQuery> Q, SymbolNameSet Names)>;
/// A legacy lookup function for JITSymbolResolverAdapter.
/// Do not use -- this will be removed soon.
Expected<SymbolMap>
legacyLookup(ExecutionSessionBase &ES, LegacyAsyncLookupFunction AsyncLookup,
SymbolNameSet Names, bool WaiUntilReady,
RegisterDependenciesFunction RegisterDependencies);
/// Search the given JITDylib list for the given symbols.
///
///
/// The OnResolve callback will be called once all requested symbols are
/// resolved, or if an error occurs prior to resolution.
///
/// The OnReady callback will be called once all requested symbols are ready,
/// or if an error occurs after resolution but before all symbols are ready.
///
/// If all symbols are found, the RegisterDependencies function will be called
/// while the session lock is held. This gives clients a chance to register
/// dependencies for on the queried symbols for any symbols they are
/// materializing (if a MaterializationResponsibility instance is present,
/// this can be implemented by calling
/// MaterializationResponsibility::addDependencies). If there are no
/// dependenant symbols for this query (e.g. it is being made by a top level
/// client to get an address to call) then the value NoDependenciesToRegister
/// can be used.
void lookup(const JITDylibList &JDs, const SymbolNameSet &Symbols,
SymbolsResolvedCallback OnResolve, SymbolsReadyCallback OnReady,
RegisterDependenciesFunction RegisterDependencies);
/// Blocking version of lookup above. Returns the resolved symbol map.
/// If WaitUntilReady is true (the default), will not return until all
/// requested symbols are ready (or an error occurs). If WaitUntilReady is
/// false, will return as soon as all requested symbols are resolved,
/// or an error occurs. If WaitUntilReady is false and an error occurs
/// after resolution, the function will return a success value, but the
/// error will be reported via reportErrors.
Expected<SymbolMap> lookup(const JITDylibList &JDs,
const SymbolNameSet &Symbols,
RegisterDependenciesFunction RegisterDependencies,
bool WaitUntilReady = true);
/// Materialize the given unit.
void dispatchMaterialization(JITDylib &JD,
std::unique_ptr<MaterializationUnit> MU) {
DispatchMaterialization(JD, std::move(MU));
}
private:
static void logErrorsToStdErr(Error Err) {
logAllUnhandledErrors(std::move(Err), errs(), "JIT session error: ");
}
static void
materializeOnCurrentThread(JITDylib &JD,
std::unique_ptr<MaterializationUnit> MU) {
MU->doMaterialize(JD);
}
void runOutstandingMUs();
mutable std::recursive_mutex SessionMutex;
std::shared_ptr<SymbolStringPool> SSP;
VModuleKey LastKey = 0;
ErrorReporter ReportError = logErrorsToStdErr;
DispatchMaterializationFunction DispatchMaterialization =
materializeOnCurrentThread;
// FIXME: Remove this (and runOutstandingMUs) once the linking layer works
// with callbacks from asynchronous queries.
mutable std::recursive_mutex OutstandingMUsMutex;
std::vector<std::pair<JITDylib *, std::unique_ptr<MaterializationUnit>>>
OutstandingMUs;
};
/// A symbol query that returns results via a callback when results are
/// ready.
///
/// makes a callback when all symbols are available.
class AsynchronousSymbolQuery {
friend class ExecutionSessionBase;
friend class ExecutionSession;
friend class JITDylib;
public:
@ -568,7 +434,6 @@ private:
class JITDylib {
friend class AsynchronousSymbolQuery;
friend class ExecutionSession;
friend class ExecutionSessionBase;
friend class MaterializationResponsibility;
public:
using FallbackDefinitionGeneratorFunction = std::function<SymbolNameSet(
@ -586,7 +451,7 @@ public:
const std::string &getName() const { return JITDylibName; }
/// Get a reference to the ExecutionSession for this JITDylib.
ExecutionSessionBase &getExecutionSession() const { return ES; }
ExecutionSession &getExecutionSession() const { return ES; }
/// Set a fallback defenition generator. If set, lookup and lookupFlags will
/// pass the unresolved symbols set to the fallback definition generator,
@ -633,33 +498,25 @@ public:
/// Do something with the search order (run under the session lock).
template <typename Func>
auto withSearchOrderDo(Func &&F)
-> decltype(F(std::declval<const JITDylibList &>())) {
return ES.runSessionLocked([&]() { return F(SearchOrder); });
}
-> decltype(F(std::declval<const JITDylibList &>()));
/// Define all symbols provided by the materialization unit to be part
/// of the given JITDylib.
template <typename UniquePtrToMaterializationUnit>
typename std::enable_if<
std::is_convertible<
typename std::decay<UniquePtrToMaterializationUnit>::type,
std::unique_ptr<MaterializationUnit>>::value,
Error>::type
define(UniquePtrToMaterializationUnit &&MU) {
return ES.runSessionLocked([&, this]() -> Error {
assert(MU && "Can't define with a null MU");
/// Define all symbols provided by the materialization unit to be part of this
/// JITDylib.
///
/// This overload always takes ownership of the MaterializationUnit. If any
/// errors occur, the MaterializationUnit consumed.
template <typename MaterializationUnitType>
Error define(std::unique_ptr<MaterializationUnitType> &&MU);
if (auto Err = defineImpl(*MU))
return Err;
/// defineImpl succeeded.
auto UMI = std::make_shared<UnmaterializedInfo>(std::move(MU));
for (auto &KV : UMI->MU->getSymbols())
UnmaterializedInfos[KV.first] = UMI;
return Error::success();
});
}
/// Define all symbols provided by the materialization unit to be part of this
/// JITDylib.
///
/// This overload only takes ownership of the MaterializationUnit no error is
/// generated. If an error occurs, ownership remains with the caller. This
/// may allow the caller to modify the MaterializationUnit to correct the
/// issue, then re-call define.
template <typename MaterializationUnitType>
Error define(std::unique_ptr<MaterializationUnitType> &MU);
/// Search the given JITDylib for the symbols in Symbols. If found, store
/// the flags for each symbol in Flags. Returns any unresolved symbols.
@ -709,7 +566,7 @@ private:
LLVM_MARK_AS_BITMASK_ENUM(NotifyFullyReady)
};
JITDylib(ExecutionSessionBase &ES, std::string Name);
JITDylib(ExecutionSession &ES, std::string Name);
Error defineImpl(MaterializationUnit &MU);
@ -749,7 +606,7 @@ private:
void notifyFailed(const SymbolNameSet &FailedSymbols);
ExecutionSessionBase &ES;
ExecutionSession &ES;
std::string JITDylibName;
SymbolMap Symbols;
UnmaterializedInfosMap UnmaterializedInfos;
@ -759,15 +616,32 @@ private:
};
/// An ExecutionSession represents a running JIT program.
class ExecutionSession : public ExecutionSessionBase {
public:
using ExecutionSessionBase::lookup;
class ExecutionSession {
// FIXME: Remove this when we remove the old ORC layers.
friend class JITDylib;
/// Construct an ExecutionEngine.
public:
/// For reporting errors.
using ErrorReporter = std::function<void(Error)>;
/// For dispatching MaterializationUnit::materialize calls.
using DispatchMaterializationFunction = std::function<void(
JITDylib &JD, std::unique_ptr<MaterializationUnit> MU)>;
/// Construct an ExecutionSession.
///
/// SymbolStringPools may be shared between ExecutionSessions.
ExecutionSession(std::shared_ptr<SymbolStringPool> SSP = nullptr);
/// Returns the SymbolStringPool for this ExecutionSession.
SymbolStringPool &getSymbolStringPool() const { return *SSP; }
/// Run the given lambda with the session mutex locked.
template <typename Func> auto runSessionLocked(Func &&F) -> decltype(F()) {
std::lock_guard<std::recursive_mutex> Lock(SessionMutex);
return F();
}
/// Get the "main" JITDylib, which is created automatically on construction of
/// the ExecutionSession.
JITDylib &getMainJITDylib();
@ -776,21 +650,163 @@ public:
JITDylib &createJITDylib(std::string Name,
bool AddToMainDylibSearchOrder = true);
/// Convenience version of the blocking version of lookup in
/// ExecutionSessionBase. Uses the main JITDylib's search order as the lookup
/// order, and registers no dependencies.
/// Allocate a module key for a new module to add to the JIT.
VModuleKey allocateVModule() { return ++LastKey; }
/// Return a module key to the ExecutionSession so that it can be
/// re-used. This should only be done once all resources associated
/// with the original key have been released.
void releaseVModule(VModuleKey Key) { /* FIXME: Recycle keys */
}
/// Set the error reporter function.
ExecutionSession &setErrorReporter(ErrorReporter ReportError) {
this->ReportError = std::move(ReportError);
return *this;
}
/// Report a error for this execution session.
///
/// Unhandled errors can be sent here to log them.
void reportError(Error Err) { ReportError(std::move(Err)); }
/// Set the materialization dispatch function.
ExecutionSession &setDispatchMaterialization(
DispatchMaterializationFunction DispatchMaterialization) {
this->DispatchMaterialization = std::move(DispatchMaterialization);
return *this;
}
void legacyFailQuery(AsynchronousSymbolQuery &Q, Error Err);
using LegacyAsyncLookupFunction = std::function<SymbolNameSet(
std::shared_ptr<AsynchronousSymbolQuery> Q, SymbolNameSet Names)>;
/// A legacy lookup function for JITSymbolResolverAdapter.
/// Do not use -- this will be removed soon.
Expected<SymbolMap>
legacyLookup(LegacyAsyncLookupFunction AsyncLookup, SymbolNameSet Names,
bool WaiUntilReady,
RegisterDependenciesFunction RegisterDependencies);
/// Search the given JITDylib list for the given symbols.
///
///
/// The OnResolve callback will be called once all requested symbols are
/// resolved, or if an error occurs prior to resolution.
///
/// The OnReady callback will be called once all requested symbols are ready,
/// or if an error occurs after resolution but before all symbols are ready.
///
/// If all symbols are found, the RegisterDependencies function will be called
/// while the session lock is held. This gives clients a chance to register
/// dependencies for on the queried symbols for any symbols they are
/// materializing (if a MaterializationResponsibility instance is present,
/// this can be implemented by calling
/// MaterializationResponsibility::addDependencies). If there are no
/// dependenant symbols for this query (e.g. it is being made by a top level
/// client to get an address to call) then the value NoDependenciesToRegister
/// can be used.
void lookup(const JITDylibList &JDs, const SymbolNameSet &Symbols,
SymbolsResolvedCallback OnResolve, SymbolsReadyCallback OnReady,
RegisterDependenciesFunction RegisterDependencies);
/// Blocking version of lookup above. Returns the resolved symbol map.
/// If WaitUntilReady is true (the default), will not return until all
/// requested symbols are ready (or an error occurs). If WaitUntilReady is
/// false, will return as soon as all requested symbols are resolved,
/// or an error occurs. If WaitUntilReady is false and an error occurs
/// after resolution, the function will return a success value, but the
/// error will be reported via reportErrors.
Expected<SymbolMap> lookup(const JITDylibList &JDs,
const SymbolNameSet &Symbols,
RegisterDependenciesFunction RegisterDependencies,
bool WaitUntilReady = true);
/// Convenience version of the blocking version of lookup above. Uses the main
/// JITDylib's search order as the lookup order, and registers no
/// dependencies.
Expected<SymbolMap> lookup(const SymbolNameSet &Symbols) {
return getMainJITDylib().withSearchOrderDo(
[&](const JITDylibList &SearchOrder) {
return ExecutionSessionBase::lookup(SearchOrder, Symbols,
NoDependenciesToRegister, true);
return lookup(SearchOrder, Symbols, NoDependenciesToRegister, true);
});
}
/// Materialize the given unit.
void dispatchMaterialization(JITDylib &JD,
std::unique_ptr<MaterializationUnit> MU) {
DispatchMaterialization(JD, std::move(MU));
}
private:
static void logErrorsToStdErr(Error Err) {
logAllUnhandledErrors(std::move(Err), errs(), "JIT session error: ");
}
static void
materializeOnCurrentThread(JITDylib &JD,
std::unique_ptr<MaterializationUnit> MU) {
MU->doMaterialize(JD);
}
void runOutstandingMUs();
mutable std::recursive_mutex SessionMutex;
std::shared_ptr<SymbolStringPool> SSP;
VModuleKey LastKey = 0;
ErrorReporter ReportError = logErrorsToStdErr;
DispatchMaterializationFunction DispatchMaterialization =
materializeOnCurrentThread;
std::vector<std::unique_ptr<JITDylib>> JDs;
// FIXME: Remove this (and runOutstandingMUs) once the linking layer works
// with callbacks from asynchronous queries.
mutable std::recursive_mutex OutstandingMUsMutex;
std::vector<std::pair<JITDylib *, std::unique_ptr<MaterializationUnit>>>
OutstandingMUs;
};
template <typename Func>
auto JITDylib::withSearchOrderDo(Func &&F)
-> decltype(F(std::declval<const JITDylibList &>())) {
return ES.runSessionLocked([&]() { return F(SearchOrder); });
}
template <typename MaterializationUnitType>
Error JITDylib::define(std::unique_ptr<MaterializationUnitType> &&MU) {
assert(MU && "Can not define with a null MU");
return ES.runSessionLocked([&, this]() -> Error {
if (auto Err = defineImpl(*MU))
return Err;
/// defineImpl succeeded.
auto UMI = std::make_shared<UnmaterializedInfo>(std::move(MU));
for (auto &KV : UMI->MU->getSymbols())
UnmaterializedInfos[KV.first] = UMI;
return Error::success();
});
}
template <typename MaterializationUnitType>
Error JITDylib::define(std::unique_ptr<MaterializationUnitType> &MU) {
assert(MU && "Can not define with a null MU");
return ES.runSessionLocked([&, this]() -> Error {
if (auto Err = defineImpl(*MU))
return Err;
/// defineImpl succeeded.
auto UMI = std::make_shared<UnmaterializedInfo>(std::move(MU));
for (auto &KV : UMI->MU->getSymbols())
UnmaterializedInfos[KV.first] = UMI;
return Error::success();
});
}
/// Look up the given names in the given JITDylibs.
/// JDs will be searched in order and no JITDylib pointer may be null.
/// All symbols must be found within the given JITDylibs or an error
@ -805,11 +821,11 @@ Expected<JITEvaluatedSymbol> lookup(const JITDylibList &JDs,
/// ExecutionSession.
class MangleAndInterner {
public:
MangleAndInterner(ExecutionSessionBase &ES, const DataLayout &DL);
MangleAndInterner(ExecutionSession &ES, const DataLayout &DL);
SymbolStringPtr operator()(StringRef Name);
private:
ExecutionSessionBase &ES;
ExecutionSession &ES;
const DataLayout &DL;
};

View File

@ -142,361 +142,6 @@ void SymbolsNotFound::log(raw_ostream &OS) const {
OS << "Symbols not found: " << Symbols;
}
void ExecutionSessionBase::legacyFailQuery(AsynchronousSymbolQuery &Q,
Error Err) {
assert(!!Err && "Error should be in failure state");
bool SendErrorToQuery;
runSessionLocked([&]() {
Q.detach();
SendErrorToQuery = Q.canStillFail();
});
if (SendErrorToQuery)
Q.handleFailed(std::move(Err));
else
reportError(std::move(Err));
}
Expected<SymbolMap> ExecutionSessionBase::legacyLookup(
ExecutionSessionBase &ES, LegacyAsyncLookupFunction AsyncLookup,
SymbolNameSet Names, bool WaitUntilReady,
RegisterDependenciesFunction RegisterDependencies) {
#if LLVM_ENABLE_THREADS
// In the threaded case we use promises to return the results.
std::promise<SymbolMap> PromisedResult;
std::mutex ErrMutex;
Error ResolutionError = Error::success();
std::promise<void> PromisedReady;
Error ReadyError = Error::success();
auto OnResolve = [&](Expected<SymbolMap> R) {
if (R)
PromisedResult.set_value(std::move(*R));
else {
{
ErrorAsOutParameter _(&ResolutionError);
std::lock_guard<std::mutex> Lock(ErrMutex);
ResolutionError = R.takeError();
}
PromisedResult.set_value(SymbolMap());
}
};
std::function<void(Error)> OnReady;
if (WaitUntilReady) {
OnReady = [&](Error Err) {
if (Err) {
ErrorAsOutParameter _(&ReadyError);
std::lock_guard<std::mutex> Lock(ErrMutex);
ReadyError = std::move(Err);
}
PromisedReady.set_value();
};
} else {
OnReady = [&](Error Err) {
if (Err)
ES.reportError(std::move(Err));
};
}
#else
SymbolMap Result;
Error ResolutionError = Error::success();
Error ReadyError = Error::success();
auto OnResolve = [&](Expected<SymbolMap> R) {
ErrorAsOutParameter _(&ResolutionError);
if (R)
Result = std::move(*R);
else
ResolutionError = R.takeError();
};
std::function<void(Error)> OnReady;
if (WaitUntilReady) {
OnReady = [&](Error Err) {
ErrorAsOutParameter _(&ReadyError);
if (Err)
ReadyError = std::move(Err);
};
} else {
OnReady = [&](Error Err) {
if (Err)
ES.reportError(std::move(Err));
};
}
#endif
auto Query = std::make_shared<AsynchronousSymbolQuery>(
Names, std::move(OnResolve), std::move(OnReady));
// FIXME: This should be run session locked along with the registration code
// and error reporting below.
SymbolNameSet UnresolvedSymbols = AsyncLookup(Query, std::move(Names));
// If the query was lodged successfully then register the dependencies,
// otherwise fail it with an error.
if (UnresolvedSymbols.empty())
RegisterDependencies(Query->QueryRegistrations);
else {
bool DeliverError = runSessionLocked([&]() {
Query->detach();
return Query->canStillFail();
});
auto Err = make_error<SymbolsNotFound>(std::move(UnresolvedSymbols));
if (DeliverError)
Query->handleFailed(std::move(Err));
else
ES.reportError(std::move(Err));
}
#if LLVM_ENABLE_THREADS
auto ResultFuture = PromisedResult.get_future();
auto Result = ResultFuture.get();
{
std::lock_guard<std::mutex> Lock(ErrMutex);
if (ResolutionError) {
// ReadyError will never be assigned. Consume the success value.
cantFail(std::move(ReadyError));
return std::move(ResolutionError);
}
}
if (WaitUntilReady) {
auto ReadyFuture = PromisedReady.get_future();
ReadyFuture.get();
{
std::lock_guard<std::mutex> Lock(ErrMutex);
if (ReadyError)
return std::move(ReadyError);
}
} else
cantFail(std::move(ReadyError));
return std::move(Result);
#else
if (ResolutionError) {
// ReadyError will never be assigned. Consume the success value.
cantFail(std::move(ReadyError));
return std::move(ResolutionError);
}
if (ReadyError)
return std::move(ReadyError);
return Result;
#endif
}
void ExecutionSessionBase::lookup(
const JITDylibList &JDs, const SymbolNameSet &Symbols,
SymbolsResolvedCallback OnResolve, SymbolsReadyCallback OnReady,
RegisterDependenciesFunction RegisterDependencies) {
// lookup can be re-entered recursively if running on a single thread. Run any
// outstanding MUs in case this query depends on them, otherwise the main
// thread will starve waiting for a result from an MU that it failed to run.
runOutstandingMUs();
auto Unresolved = std::move(Symbols);
std::map<JITDylib *, MaterializationUnitList> MUsMap;
auto Q = std::make_shared<AsynchronousSymbolQuery>(
Symbols, std::move(OnResolve), std::move(OnReady));
bool QueryIsFullyResolved = false;
bool QueryIsFullyReady = false;
bool QueryFailed = false;
runSessionLocked([&]() {
for (auto *JD : JDs) {
assert(JD && "JITDylibList entries must not be null");
assert(!MUsMap.count(JD) &&
"JITDylibList should not contain duplicate entries");
JD->lodgeQuery(Q, Unresolved, MUsMap[JD]);
}
if (Unresolved.empty()) {
// Query lodged successfully.
// Record whether this query is fully ready / resolved. We will use
// this to call handleFullyResolved/handleFullyReady outside the session
// lock.
QueryIsFullyResolved = Q->isFullyResolved();
QueryIsFullyReady = Q->isFullyReady();
// Call the register dependencies function.
if (RegisterDependencies && !Q->QueryRegistrations.empty())
RegisterDependencies(Q->QueryRegistrations);
} else {
// Query failed due to unresolved symbols.
QueryFailed = true;
// Disconnect the query from its dependencies.
Q->detach();
// Replace the MUs.
for (auto &KV : MUsMap)
for (auto &MU : KV.second)
KV.first->replace(std::move(MU));
}
});
if (QueryFailed) {
Q->handleFailed(make_error<SymbolsNotFound>(std::move(Unresolved)));
return;
} else {
if (QueryIsFullyResolved)
Q->handleFullyResolved();
if (QueryIsFullyReady)
Q->handleFullyReady();
}
// Move the MUs to the OutstandingMUs list, then materialize.
{
std::lock_guard<std::recursive_mutex> Lock(OutstandingMUsMutex);
for (auto &KV : MUsMap)
for (auto &MU : KV.second)
OutstandingMUs.push_back(std::make_pair(KV.first, std::move(MU)));
}
runOutstandingMUs();
}
Expected<SymbolMap> ExecutionSessionBase::lookup(
const JITDylibList &JDs, const SymbolNameSet &Symbols,
RegisterDependenciesFunction RegisterDependencies, bool WaitUntilReady) {
#if LLVM_ENABLE_THREADS
// In the threaded case we use promises to return the results.
std::promise<SymbolMap> PromisedResult;
std::mutex ErrMutex;
Error ResolutionError = Error::success();
std::promise<void> PromisedReady;
Error ReadyError = Error::success();
auto OnResolve = [&](Expected<SymbolMap> R) {
if (R)
PromisedResult.set_value(std::move(*R));
else {
{
ErrorAsOutParameter _(&ResolutionError);
std::lock_guard<std::mutex> Lock(ErrMutex);
ResolutionError = R.takeError();
}
PromisedResult.set_value(SymbolMap());
}
};
std::function<void(Error)> OnReady;
if (WaitUntilReady) {
OnReady = [&](Error Err) {
if (Err) {
ErrorAsOutParameter _(&ReadyError);
std::lock_guard<std::mutex> Lock(ErrMutex);
ReadyError = std::move(Err);
}
PromisedReady.set_value();
};
} else {
OnReady = [&](Error Err) {
if (Err)
reportError(std::move(Err));
};
}
#else
SymbolMap Result;
Error ResolutionError = Error::success();
Error ReadyError = Error::success();
auto OnResolve = [&](Expected<SymbolMap> R) {
ErrorAsOutParameter _(&ResolutionError);
if (R)
Result = std::move(*R);
else
ResolutionError = R.takeError();
};
std::function<void(Error)> OnReady;
if (WaitUntilReady) {
OnReady = [&](Error Err) {
ErrorAsOutParameter _(&ReadyError);
if (Err)
ReadyError = std::move(Err);
};
} else {
OnReady = [&](Error Err) {
if (Err)
reportError(std::move(Err));
};
}
#endif
// Perform the asynchronous lookup.
lookup(JDs, Symbols, OnResolve, OnReady, RegisterDependencies);
#if LLVM_ENABLE_THREADS
auto ResultFuture = PromisedResult.get_future();
auto Result = ResultFuture.get();
{
std::lock_guard<std::mutex> Lock(ErrMutex);
if (ResolutionError) {
// ReadyError will never be assigned. Consume the success value.
cantFail(std::move(ReadyError));
return std::move(ResolutionError);
}
}
if (WaitUntilReady) {
auto ReadyFuture = PromisedReady.get_future();
ReadyFuture.get();
{
std::lock_guard<std::mutex> Lock(ErrMutex);
if (ReadyError)
return std::move(ReadyError);
}
} else
cantFail(std::move(ReadyError));
return std::move(Result);
#else
if (ResolutionError) {
// ReadyError will never be assigned. Consume the success value.
cantFail(std::move(ReadyError));
return std::move(ResolutionError);
}
if (ReadyError)
return std::move(ReadyError);
return Result;
#endif
}
void ExecutionSessionBase::runOutstandingMUs() {
while (1) {
std::pair<JITDylib *, std::unique_ptr<MaterializationUnit>> JITDylibAndMU;
{
std::lock_guard<std::recursive_mutex> Lock(OutstandingMUsMutex);
if (!OutstandingMUs.empty()) {
JITDylibAndMU = std::move(OutstandingMUs.back());
OutstandingMUs.pop_back();
}
}
if (JITDylibAndMU.first) {
assert(JITDylibAndMU.second && "JITDylib, but no MU?");
dispatchMaterialization(*JITDylibAndMU.first,
std::move(JITDylibAndMU.second));
} else
break;
}
}
AsynchronousSymbolQuery::AsynchronousSymbolQuery(
const SymbolNameSet &Symbols, SymbolsResolvedCallback NotifySymbolsResolved,
SymbolsReadyCallback NotifySymbolsReady)
@ -520,10 +165,18 @@ void AsynchronousSymbolQuery::resolve(const SymbolStringPtr &Name,
void AsynchronousSymbolQuery::handleFullyResolved() {
assert(NotYetResolvedCount == 0 && "Not fully resolved?");
assert(NotifySymbolsResolved &&
"NotifySymbolsResolved already called or error occurred");
NotifySymbolsResolved(std::move(ResolvedSymbols));
if (!NotifySymbolsResolved) {
// handleFullyResolved may be called by handleFullyReady (see comments in
// that method), in which case this is a no-op, so bail out.
assert(!NotifySymbolsReady &&
"NotifySymbolsResolved already called or an error occurred");
return;
}
auto TmpNotifySymbolsResolved = std::move(NotifySymbolsResolved);
NotifySymbolsResolved = SymbolsResolvedCallback();
TmpNotifySymbolsResolved(std::move(ResolvedSymbols));
}
void AsynchronousSymbolQuery::notifySymbolReady() {
@ -532,11 +185,25 @@ void AsynchronousSymbolQuery::notifySymbolReady() {
}
void AsynchronousSymbolQuery::handleFullyReady() {
assert(NotifySymbolsReady &&
"NotifySymbolsReady already called or an error occurred");
auto TmpNotifySymbolsReady = std::move(NotifySymbolsReady);
NotifySymbolsReady = SymbolsReadyCallback();
if (NotYetResolvedCount == 0 && NotifySymbolsResolved) {
// The NotifyResolved callback of one query must have caused this query to
// become ready (i.e. there is still a handleFullyResolved callback waiting
// to be made back up the stack). Fold the handleFullyResolved call into
// this one before proceeding. This will cause the call further up the
// stack to become a no-op.
handleFullyResolved();
}
assert(QueryRegistrations.empty() &&
"Query is still registered with some symbols");
assert(!NotifySymbolsResolved && "Resolution not applied yet");
NotifySymbolsReady(Error::success());
NotifySymbolsReady = SymbolsReadyCallback();
TmpNotifySymbolsReady(Error::success());
}
bool AsynchronousSymbolQuery::canStillFail() {
@ -1549,7 +1216,7 @@ void JITDylib::dump(raw_ostream &OS) {
});
}
JITDylib::JITDylib(ExecutionSessionBase &ES, std::string Name)
JITDylib::JITDylib(ExecutionSession &ES, std::string Name)
: ES(ES), JITDylibName(std::move(Name)) {
SearchOrder.push_back(this);
}
@ -1678,7 +1345,7 @@ void JITDylib::transferEmittedNodeDependencies(
}
ExecutionSession::ExecutionSession(std::shared_ptr<SymbolStringPool> SSP)
: ExecutionSessionBase(std::move(SSP)) {
: SSP(SSP ? std::move(SSP) : std::make_shared<SymbolStringPool>()) {
// Construct the main dylib.
JDs.push_back(std::unique_ptr<JITDylib>(new JITDylib(*this, "<main>")));
}
@ -1698,6 +1365,360 @@ JITDylib &ExecutionSession::createJITDylib(std::string Name,
});
}
void ExecutionSession::legacyFailQuery(AsynchronousSymbolQuery &Q, Error Err) {
assert(!!Err && "Error should be in failure state");
bool SendErrorToQuery;
runSessionLocked([&]() {
Q.detach();
SendErrorToQuery = Q.canStillFail();
});
if (SendErrorToQuery)
Q.handleFailed(std::move(Err));
else
reportError(std::move(Err));
}
Expected<SymbolMap> ExecutionSession::legacyLookup(
LegacyAsyncLookupFunction AsyncLookup, SymbolNameSet Names,
bool WaitUntilReady, RegisterDependenciesFunction RegisterDependencies) {
#if LLVM_ENABLE_THREADS
// In the threaded case we use promises to return the results.
std::promise<SymbolMap> PromisedResult;
std::mutex ErrMutex;
Error ResolutionError = Error::success();
std::promise<void> PromisedReady;
Error ReadyError = Error::success();
auto OnResolve = [&](Expected<SymbolMap> R) {
if (R)
PromisedResult.set_value(std::move(*R));
else {
{
ErrorAsOutParameter _(&ResolutionError);
std::lock_guard<std::mutex> Lock(ErrMutex);
ResolutionError = R.takeError();
}
PromisedResult.set_value(SymbolMap());
}
};
std::function<void(Error)> OnReady;
if (WaitUntilReady) {
OnReady = [&](Error Err) {
if (Err) {
ErrorAsOutParameter _(&ReadyError);
std::lock_guard<std::mutex> Lock(ErrMutex);
ReadyError = std::move(Err);
}
PromisedReady.set_value();
};
} else {
OnReady = [&](Error Err) {
if (Err)
reportError(std::move(Err));
};
}
#else
SymbolMap Result;
Error ResolutionError = Error::success();
Error ReadyError = Error::success();
auto OnResolve = [&](Expected<SymbolMap> R) {
ErrorAsOutParameter _(&ResolutionError);
if (R)
Result = std::move(*R);
else
ResolutionError = R.takeError();
};
std::function<void(Error)> OnReady;
if (WaitUntilReady) {
OnReady = [&](Error Err) {
ErrorAsOutParameter _(&ReadyError);
if (Err)
ReadyError = std::move(Err);
};
} else {
OnReady = [&](Error Err) {
if (Err)
reportError(std::move(Err));
};
}
#endif
auto Query = std::make_shared<AsynchronousSymbolQuery>(
Names, std::move(OnResolve), std::move(OnReady));
// FIXME: This should be run session locked along with the registration code
// and error reporting below.
SymbolNameSet UnresolvedSymbols = AsyncLookup(Query, std::move(Names));
// If the query was lodged successfully then register the dependencies,
// otherwise fail it with an error.
if (UnresolvedSymbols.empty())
RegisterDependencies(Query->QueryRegistrations);
else {
bool DeliverError = runSessionLocked([&]() {
Query->detach();
return Query->canStillFail();
});
auto Err = make_error<SymbolsNotFound>(std::move(UnresolvedSymbols));
if (DeliverError)
Query->handleFailed(std::move(Err));
else
reportError(std::move(Err));
}
#if LLVM_ENABLE_THREADS
auto ResultFuture = PromisedResult.get_future();
auto Result = ResultFuture.get();
{
std::lock_guard<std::mutex> Lock(ErrMutex);
if (ResolutionError) {
// ReadyError will never be assigned. Consume the success value.
cantFail(std::move(ReadyError));
return std::move(ResolutionError);
}
}
if (WaitUntilReady) {
auto ReadyFuture = PromisedReady.get_future();
ReadyFuture.get();
{
std::lock_guard<std::mutex> Lock(ErrMutex);
if (ReadyError)
return std::move(ReadyError);
}
} else
cantFail(std::move(ReadyError));
return std::move(Result);
#else
if (ResolutionError) {
// ReadyError will never be assigned. Consume the success value.
cantFail(std::move(ReadyError));
return std::move(ResolutionError);
}
if (ReadyError)
return std::move(ReadyError);
return Result;
#endif
}
void ExecutionSession::lookup(
const JITDylibList &JDs, const SymbolNameSet &Symbols,
SymbolsResolvedCallback OnResolve, SymbolsReadyCallback OnReady,
RegisterDependenciesFunction RegisterDependencies) {
// lookup can be re-entered recursively if running on a single thread. Run any
// outstanding MUs in case this query depends on them, otherwise the main
// thread will starve waiting for a result from an MU that it failed to run.
runOutstandingMUs();
auto Unresolved = std::move(Symbols);
std::map<JITDylib *, MaterializationUnitList> MUsMap;
auto Q = std::make_shared<AsynchronousSymbolQuery>(
Symbols, std::move(OnResolve), std::move(OnReady));
bool QueryIsFullyResolved = false;
bool QueryIsFullyReady = false;
bool QueryFailed = false;
runSessionLocked([&]() {
for (auto *JD : JDs) {
assert(JD && "JITDylibList entries must not be null");
assert(!MUsMap.count(JD) &&
"JITDylibList should not contain duplicate entries");
JD->lodgeQuery(Q, Unresolved, MUsMap[JD]);
}
if (Unresolved.empty()) {
// Query lodged successfully.
// Record whether this query is fully ready / resolved. We will use
// this to call handleFullyResolved/handleFullyReady outside the session
// lock.
QueryIsFullyResolved = Q->isFullyResolved();
QueryIsFullyReady = Q->isFullyReady();
// Call the register dependencies function.
if (RegisterDependencies && !Q->QueryRegistrations.empty())
RegisterDependencies(Q->QueryRegistrations);
} else {
// Query failed due to unresolved symbols.
QueryFailed = true;
// Disconnect the query from its dependencies.
Q->detach();
// Replace the MUs.
for (auto &KV : MUsMap)
for (auto &MU : KV.second)
KV.first->replace(std::move(MU));
}
});
if (QueryFailed) {
Q->handleFailed(make_error<SymbolsNotFound>(std::move(Unresolved)));
return;
} else {
if (QueryIsFullyResolved)
Q->handleFullyResolved();
if (QueryIsFullyReady)
Q->handleFullyReady();
}
// Move the MUs to the OutstandingMUs list, then materialize.
{
std::lock_guard<std::recursive_mutex> Lock(OutstandingMUsMutex);
for (auto &KV : MUsMap)
for (auto &MU : KV.second)
OutstandingMUs.push_back(std::make_pair(KV.first, std::move(MU)));
}
runOutstandingMUs();
}
Expected<SymbolMap>
ExecutionSession::lookup(const JITDylibList &JDs, const SymbolNameSet &Symbols,
RegisterDependenciesFunction RegisterDependencies,
bool WaitUntilReady) {
#if LLVM_ENABLE_THREADS
// In the threaded case we use promises to return the results.
std::promise<SymbolMap> PromisedResult;
std::mutex ErrMutex;
Error ResolutionError = Error::success();
std::promise<void> PromisedReady;
Error ReadyError = Error::success();
auto OnResolve = [&](Expected<SymbolMap> R) {
if (R)
PromisedResult.set_value(std::move(*R));
else {
{
ErrorAsOutParameter _(&ResolutionError);
std::lock_guard<std::mutex> Lock(ErrMutex);
ResolutionError = R.takeError();
}
PromisedResult.set_value(SymbolMap());
}
};
std::function<void(Error)> OnReady;
if (WaitUntilReady) {
OnReady = [&](Error Err) {
if (Err) {
ErrorAsOutParameter _(&ReadyError);
std::lock_guard<std::mutex> Lock(ErrMutex);
ReadyError = std::move(Err);
}
PromisedReady.set_value();
};
} else {
OnReady = [&](Error Err) {
if (Err)
reportError(std::move(Err));
};
}
#else
SymbolMap Result;
Error ResolutionError = Error::success();
Error ReadyError = Error::success();
auto OnResolve = [&](Expected<SymbolMap> R) {
ErrorAsOutParameter _(&ResolutionError);
if (R)
Result = std::move(*R);
else
ResolutionError = R.takeError();
};
std::function<void(Error)> OnReady;
if (WaitUntilReady) {
OnReady = [&](Error Err) {
ErrorAsOutParameter _(&ReadyError);
if (Err)
ReadyError = std::move(Err);
};
} else {
OnReady = [&](Error Err) {
if (Err)
reportError(std::move(Err));
};
}
#endif
// Perform the asynchronous lookup.
lookup(JDs, Symbols, OnResolve, OnReady, RegisterDependencies);
#if LLVM_ENABLE_THREADS
auto ResultFuture = PromisedResult.get_future();
auto Result = ResultFuture.get();
{
std::lock_guard<std::mutex> Lock(ErrMutex);
if (ResolutionError) {
// ReadyError will never be assigned. Consume the success value.
cantFail(std::move(ReadyError));
return std::move(ResolutionError);
}
}
if (WaitUntilReady) {
auto ReadyFuture = PromisedReady.get_future();
ReadyFuture.get();
{
std::lock_guard<std::mutex> Lock(ErrMutex);
if (ReadyError)
return std::move(ReadyError);
}
} else
cantFail(std::move(ReadyError));
return std::move(Result);
#else
if (ResolutionError) {
// ReadyError will never be assigned. Consume the success value.
cantFail(std::move(ReadyError));
return std::move(ResolutionError);
}
if (ReadyError)
return std::move(ReadyError);
return Result;
#endif
}
void ExecutionSession::runOutstandingMUs() {
while (1) {
std::pair<JITDylib *, std::unique_ptr<MaterializationUnit>> JITDylibAndMU;
{
std::lock_guard<std::recursive_mutex> Lock(OutstandingMUsMutex);
if (!OutstandingMUs.empty()) {
JITDylibAndMU = std::move(OutstandingMUs.back());
OutstandingMUs.pop_back();
}
}
if (JITDylibAndMU.first) {
assert(JITDylibAndMU.second && "JITDylib, but no MU?");
dispatchMaterialization(*JITDylibAndMU.first,
std::move(JITDylibAndMU.second));
} else
break;
}
}
Expected<SymbolMap> lookup(const JITDylibList &JDs, SymbolNameSet Names) {
if (JDs.empty())
@ -1720,8 +1741,7 @@ Expected<JITEvaluatedSymbol> lookup(const JITDylibList &JDs,
return ResultMap.takeError();
}
MangleAndInterner::MangleAndInterner(ExecutionSessionBase &ES,
const DataLayout &DL)
MangleAndInterner::MangleAndInterner(ExecutionSession &ES, const DataLayout &DL)
: ES(ES), DL(DL) {}
SymbolStringPtr MangleAndInterner::operator()(StringRef Name) {

View File

@ -35,7 +35,7 @@ JITSymbolResolverAdapter::lookup(const LookupSet &Symbols) {
};
auto InternedResult =
ES.legacyLookup(ES, std::move(LookupFn), std::move(InternedSymbols),
ES.legacyLookup(std::move(LookupFn), std::move(InternedSymbols),
false, RegisterDependencies);
if (!InternedResult)