forked from OSchip/llvm-project
[ORC] Fix a FIXME: Propagate errors to dependencies.
When symbols are failed (via MaterializationResponsibility::failMaterialization) any symbols depending on them will now be moved to an error state. Attempting to resolve or emit a symbol in the error state (via the notifyResolved or notifyEmitted methods on MaterializationResponsibility) will result in an error. If notifyResolved or notifyEmitted return an error due to failure of a dependence then the caller should log or discard the error and call failMaterialization to propagate the failure to any queries waiting on the symbols being resolved/emitted (plus their dependencies). llvm-svn: 369808
This commit is contained in:
parent
a47d622240
commit
e00585c77c
|
@ -123,13 +123,13 @@ class FailedToMaterialize : public ErrorInfo<FailedToMaterialize> {
|
|||
public:
|
||||
static char ID;
|
||||
|
||||
FailedToMaterialize(SymbolNameSet Symbols);
|
||||
FailedToMaterialize(std::shared_ptr<SymbolDependenceMap> Symbols);
|
||||
std::error_code convertToErrorCode() const override;
|
||||
void log(raw_ostream &OS) const override;
|
||||
const SymbolNameSet &getSymbols() const { return Symbols; }
|
||||
const SymbolDependenceMap &getSymbols() const { return *Symbols; }
|
||||
|
||||
private:
|
||||
SymbolNameSet Symbols;
|
||||
std::shared_ptr<SymbolDependenceMap> Symbols;
|
||||
};
|
||||
|
||||
/// Used to notify clients when symbols can not be found during a lookup.
|
||||
|
@ -204,12 +204,26 @@ public:
|
|||
/// symbols must be ones covered by this MaterializationResponsibility
|
||||
/// instance. Individual calls to this method may resolve a subset of the
|
||||
/// symbols, but all symbols must have been resolved prior to calling emit.
|
||||
void notifyResolved(const SymbolMap &Symbols);
|
||||
///
|
||||
/// This method will return an error if any symbols being resolved have been
|
||||
/// moved to the error state due to the failure of a dependency. If this
|
||||
/// method returns an error then clients should log it and call
|
||||
/// failMaterialize. If no dependencies have been registered for the
|
||||
/// symbols covered by this MaterializationResponsibiility then this method
|
||||
/// is guaranteed to return Error::success() and can be wrapped with cantFail.
|
||||
Error notifyResolved(const SymbolMap &Symbols);
|
||||
|
||||
/// Notifies the target JITDylib (and any pending queries on that JITDylib)
|
||||
/// that all symbols covered by this MaterializationResponsibility instance
|
||||
/// have been emitted.
|
||||
void notifyEmitted();
|
||||
///
|
||||
/// This method will return an error if any symbols being resolved have been
|
||||
/// moved to the error state due to the failure of a dependency. If this
|
||||
/// method returns an error then clients should log it and call
|
||||
/// failMaterialize. If no dependencies have been registered for the
|
||||
/// symbols covered by this MaterializationResponsibiility then this method
|
||||
/// is guaranteed to return Error::success() and can be wrapped with cantFail.
|
||||
Error notifyEmitted();
|
||||
|
||||
/// Adds new symbols to the JITDylib and this responsibility instance.
|
||||
/// JITDylib entries start out in the materializing state.
|
||||
|
@ -628,11 +642,13 @@ private:
|
|||
void addQuery(std::shared_ptr<AsynchronousSymbolQuery> Q);
|
||||
void removeQuery(const AsynchronousSymbolQuery &Q);
|
||||
AsynchronousSymbolQueryList takeQueriesMeeting(SymbolState RequiredState);
|
||||
AsynchronousSymbolQueryList takeAllPendingQueries() {
|
||||
return std::move(PendingQueries);
|
||||
}
|
||||
bool hasQueriesPending() const { return !PendingQueries.empty(); }
|
||||
const AsynchronousSymbolQueryList &pendingQueries() const {
|
||||
return PendingQueries;
|
||||
}
|
||||
|
||||
private:
|
||||
AsynchronousSymbolQueryList PendingQueries;
|
||||
};
|
||||
|
@ -699,9 +715,9 @@ private:
|
|||
SymbolNameSet &Unresolved, bool MatchNonExported,
|
||||
MaterializationUnitList &MUs);
|
||||
|
||||
void lodgeQueryImpl(std::shared_ptr<AsynchronousSymbolQuery> &Q,
|
||||
SymbolNameSet &Unresolved, bool MatchNonExported,
|
||||
MaterializationUnitList &MUs);
|
||||
Error lodgeQueryImpl(std::shared_ptr<AsynchronousSymbolQuery> &Q,
|
||||
SymbolNameSet &Unresolved, bool MatchNonExported,
|
||||
MaterializationUnitList &MUs);
|
||||
|
||||
bool lookupImpl(std::shared_ptr<AsynchronousSymbolQuery> &Q,
|
||||
std::vector<std::unique_ptr<MaterializationUnit>> &MUs,
|
||||
|
@ -720,14 +736,21 @@ private:
|
|||
|
||||
SymbolNameSet getRequestedSymbols(const SymbolFlagsMap &SymbolFlags) const;
|
||||
|
||||
// Move a symbol to the failure state.
|
||||
// Detaches the symbol from all dependencies, moves all dependants to the
|
||||
// error state (but does not fail them), deletes the MaterializingInfo for
|
||||
// the symbol (if present) and returns the set of queries that need to be
|
||||
// notified of the failure.
|
||||
AsynchronousSymbolQuerySet failSymbol(const SymbolStringPtr &Name);
|
||||
|
||||
void addDependencies(const SymbolStringPtr &Name,
|
||||
const SymbolDependenceMap &Dependants);
|
||||
|
||||
void resolve(const SymbolMap &Resolved);
|
||||
Error resolve(const SymbolMap &Resolved);
|
||||
|
||||
void emit(const SymbolFlagsMap &Emitted);
|
||||
Error emit(const SymbolFlagsMap &Emitted);
|
||||
|
||||
void notifyFailed(const SymbolNameSet &FailedSymbols);
|
||||
void notifyFailed(const SymbolFlagsMap &FailedSymbols);
|
||||
|
||||
ExecutionSession &ES;
|
||||
std::string JITDylibName;
|
||||
|
|
|
@ -151,6 +151,8 @@ raw_ostream &operator<<(raw_ostream &OS, const SymbolNameSet &Symbols) {
|
|||
}
|
||||
|
||||
raw_ostream &operator<<(raw_ostream &OS, const JITSymbolFlags &Flags) {
|
||||
if (Flags.hasError())
|
||||
OS << "[*ERROR*]";
|
||||
if (Flags.isCallable())
|
||||
OS << "[Callable]";
|
||||
else
|
||||
|
@ -244,9 +246,10 @@ raw_ostream &operator<<(raw_ostream &OS, const SymbolState &S) {
|
|||
llvm_unreachable("Invalid state");
|
||||
}
|
||||
|
||||
FailedToMaterialize::FailedToMaterialize(SymbolNameSet Symbols)
|
||||
FailedToMaterialize::FailedToMaterialize(
|
||||
std::shared_ptr<SymbolDependenceMap> Symbols)
|
||||
: Symbols(std::move(Symbols)) {
|
||||
assert(!this->Symbols.empty() && "Can not fail to resolve an empty set");
|
||||
assert(!this->Symbols->empty() && "Can not fail to resolve an empty set");
|
||||
}
|
||||
|
||||
std::error_code FailedToMaterialize::convertToErrorCode() const {
|
||||
|
@ -254,7 +257,7 @@ std::error_code FailedToMaterialize::convertToErrorCode() const {
|
|||
}
|
||||
|
||||
void FailedToMaterialize::log(raw_ostream &OS) const {
|
||||
OS << "Failed to materialize symbols: " << Symbols;
|
||||
OS << "Failed to materialize symbols: " << *Symbols;
|
||||
}
|
||||
|
||||
SymbolsNotFound::SymbolsNotFound(SymbolNameSet Symbols)
|
||||
|
@ -367,7 +370,7 @@ SymbolNameSet MaterializationResponsibility::getRequestedSymbols() const {
|
|||
return JD.getRequestedSymbols(SymbolFlags);
|
||||
}
|
||||
|
||||
void MaterializationResponsibility::notifyResolved(const SymbolMap &Symbols) {
|
||||
Error MaterializationResponsibility::notifyResolved(const SymbolMap &Symbols) {
|
||||
LLVM_DEBUG({
|
||||
dbgs() << "In " << JD.getName() << " resolving " << Symbols << "\n";
|
||||
});
|
||||
|
@ -385,17 +388,20 @@ void MaterializationResponsibility::notifyResolved(const SymbolMap &Symbols) {
|
|||
}
|
||||
#endif
|
||||
|
||||
JD.resolve(Symbols);
|
||||
return JD.resolve(Symbols);
|
||||
}
|
||||
|
||||
void MaterializationResponsibility::notifyEmitted() {
|
||||
Error MaterializationResponsibility::notifyEmitted() {
|
||||
|
||||
LLVM_DEBUG({
|
||||
dbgs() << "In " << JD.getName() << " emitting " << SymbolFlags << "\n";
|
||||
});
|
||||
|
||||
JD.emit(SymbolFlags);
|
||||
if (auto Err = JD.emit(SymbolFlags))
|
||||
return Err;
|
||||
|
||||
SymbolFlags.clear();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error MaterializationResponsibility::defineMaterializing(
|
||||
|
@ -417,11 +423,7 @@ void MaterializationResponsibility::failMaterialization() {
|
|||
<< SymbolFlags << "\n";
|
||||
});
|
||||
|
||||
SymbolNameSet FailedSymbols;
|
||||
for (auto &KV : SymbolFlags)
|
||||
FailedSymbols.insert(KV.first);
|
||||
|
||||
JD.notifyFailed(FailedSymbols);
|
||||
JD.notifyFailed(SymbolFlags);
|
||||
SymbolFlags.clear();
|
||||
}
|
||||
|
||||
|
@ -485,8 +487,9 @@ StringRef AbsoluteSymbolsMaterializationUnit::getName() const {
|
|||
|
||||
void AbsoluteSymbolsMaterializationUnit::materialize(
|
||||
MaterializationResponsibility R) {
|
||||
R.notifyResolved(Symbols);
|
||||
R.notifyEmitted();
|
||||
// No dependencies, so these calls can't fail.
|
||||
cantFail(R.notifyResolved(Symbols));
|
||||
cantFail(R.notifyEmitted());
|
||||
}
|
||||
|
||||
void AbsoluteSymbolsMaterializationUnit::discard(const JITDylib &JD,
|
||||
|
@ -625,6 +628,7 @@ void ReExportsMaterializationUnit::materialize(
|
|||
};
|
||||
|
||||
auto OnComplete = [QueryInfo](Expected<SymbolMap> Result) {
|
||||
auto &ES = QueryInfo->R.getTargetJITDylib().getExecutionSession();
|
||||
if (Result) {
|
||||
SymbolMap ResolutionMap;
|
||||
for (auto &KV : QueryInfo->Aliases) {
|
||||
|
@ -633,10 +637,17 @@ void ReExportsMaterializationUnit::materialize(
|
|||
ResolutionMap[KV.first] = JITEvaluatedSymbol(
|
||||
(*Result)[KV.second.Aliasee].getAddress(), KV.second.AliasFlags);
|
||||
}
|
||||
QueryInfo->R.notifyResolved(ResolutionMap);
|
||||
QueryInfo->R.notifyEmitted();
|
||||
if (auto Err = QueryInfo->R.notifyResolved(ResolutionMap)) {
|
||||
ES.reportError(std::move(Err));
|
||||
QueryInfo->R.failMaterialization();
|
||||
return;
|
||||
}
|
||||
if (auto Err = QueryInfo->R.notifyEmitted()) {
|
||||
ES.reportError(std::move(Err));
|
||||
QueryInfo->R.failMaterialization();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
auto &ES = QueryInfo->R.getTargetJITDylib().getExecutionSession();
|
||||
ES.reportError(Result.takeError());
|
||||
QueryInfo->R.failMaterialization();
|
||||
}
|
||||
|
@ -830,29 +841,115 @@ JITDylib::getRequestedSymbols(const SymbolFlagsMap &SymbolFlags) const {
|
|||
});
|
||||
}
|
||||
|
||||
JITDylib::AsynchronousSymbolQuerySet
|
||||
JITDylib::failSymbol(const SymbolStringPtr &Name) {
|
||||
assert(Symbols.count(Name) && "Name not in symbol table");
|
||||
assert(Symbols[Name].getFlags().hasError() &&
|
||||
"Failing symbol not in the error state");
|
||||
|
||||
auto MII = MaterializingInfos.find(Name);
|
||||
if (MII == MaterializingInfos.end())
|
||||
return AsynchronousSymbolQuerySet();
|
||||
|
||||
auto &MI = MII->second;
|
||||
|
||||
// Visit all dependants.
|
||||
for (auto &KV : MI.Dependants) {
|
||||
auto &DependantJD = *KV.first;
|
||||
for (auto &DependantName : KV.second) {
|
||||
assert(DependantJD.Symbols.count(DependantName) &&
|
||||
"No symbol with DependantName in DependantJD");
|
||||
auto &DependantSymTabEntry = DependantJD.Symbols[DependantName];
|
||||
DependantSymTabEntry.setFlags(DependantSymTabEntry.getFlags() |
|
||||
JITSymbolFlags::HasError);
|
||||
|
||||
assert(DependantJD.MaterializingInfos.count(DependantName) &&
|
||||
"Dependant symbol does not have MaterializingInfo?");
|
||||
auto &DependantMI = DependantJD.MaterializingInfos[DependantName];
|
||||
|
||||
assert(DependantMI.UnemittedDependencies.count(this) &&
|
||||
"No unemitted dependency recorded for this JD?");
|
||||
auto UnemittedDepsI = DependantMI.UnemittedDependencies.find(this);
|
||||
assert(UnemittedDepsI != DependantMI.UnemittedDependencies.end() &&
|
||||
"No unemitted dependency on this JD");
|
||||
assert(UnemittedDepsI->second.count(Name) &&
|
||||
"No unemitted dependency on symbol Name in this JD");
|
||||
|
||||
UnemittedDepsI->second.erase(Name);
|
||||
if (UnemittedDepsI->second.empty())
|
||||
DependantMI.UnemittedDependencies.erase(UnemittedDepsI);
|
||||
}
|
||||
}
|
||||
|
||||
// Visit all unemitted dependencies and disconnect from them.
|
||||
for (auto &KV : MI.UnemittedDependencies) {
|
||||
auto &DependencyJD = *KV.first;
|
||||
for (auto &DependencyName : KV.second) {
|
||||
assert(DependencyJD.MaterializingInfos.count(DependencyName) &&
|
||||
"Dependency does not have MaterializingInfo");
|
||||
auto &DependencyMI = DependencyJD.MaterializingInfos[DependencyName];
|
||||
auto DependantsI = DependencyMI.Dependants.find(this);
|
||||
assert(DependantsI != DependencyMI.Dependants.end() &&
|
||||
"No dependnts entry recorded for this JD");
|
||||
assert(DependantsI->second.count(Name) &&
|
||||
"No dependants entry recorded for Name");
|
||||
DependantsI->second.erase(Name);
|
||||
if (DependantsI->second.empty())
|
||||
DependencyMI.Dependants.erase(DependantsI);
|
||||
}
|
||||
}
|
||||
|
||||
AsynchronousSymbolQuerySet QueriesToFail;
|
||||
for (auto &Q : MI.takeAllPendingQueries())
|
||||
QueriesToFail.insert(std::move(Q));
|
||||
return QueriesToFail;
|
||||
}
|
||||
|
||||
void JITDylib::addDependencies(const SymbolStringPtr &Name,
|
||||
const SymbolDependenceMap &Dependencies) {
|
||||
assert(Symbols.count(Name) && "Name not in symbol table");
|
||||
assert(Symbols[Name].isInMaterializationPhase() &&
|
||||
"Can not add dependencies for a symbol that is not materializing");
|
||||
|
||||
// If Name is already in an error state then just bail out.
|
||||
if (Symbols[Name].getFlags().hasError())
|
||||
return;
|
||||
|
||||
auto &MI = MaterializingInfos[Name];
|
||||
assert(!MI.IsEmitted && "Can not add dependencies to an emitted symbol");
|
||||
|
||||
bool DependsOnSymbolInErrorState = false;
|
||||
|
||||
// Register dependencies, record whether any depenendency is in the error
|
||||
// state.
|
||||
for (auto &KV : Dependencies) {
|
||||
assert(KV.first && "Null JITDylib in dependency?");
|
||||
auto &OtherJITDylib = *KV.first;
|
||||
auto &DepsOnOtherJITDylib = MI.UnemittedDependencies[&OtherJITDylib];
|
||||
|
||||
for (auto &OtherSymbol : KV.second) {
|
||||
|
||||
// Check the sym entry for the dependency.
|
||||
auto SymI = OtherJITDylib.Symbols.find(OtherSymbol);
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Assert that this symbol exists and has not been emitted already.
|
||||
auto SymI = OtherJITDylib.Symbols.find(OtherSymbol);
|
||||
assert(SymI != OtherJITDylib.Symbols.end() &&
|
||||
(SymI->second.getState() != SymbolState::Ready &&
|
||||
"Dependency on emitted symbol"));
|
||||
#endif
|
||||
|
||||
// If the dependency is in an error state then note this and continue,
|
||||
// we will move this symbol to the error state below.
|
||||
if (SymI->second.getFlags().hasError()) {
|
||||
DependsOnSymbolInErrorState = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the dependency was not in the error state then add it to
|
||||
// our list of dependencies.
|
||||
assert(OtherJITDylib.MaterializingInfos.count(OtherSymbol) &&
|
||||
"No MaterializingInfo for dependency");
|
||||
auto &OtherMI = OtherJITDylib.MaterializingInfos[OtherSymbol];
|
||||
|
||||
if (OtherMI.IsEmitted)
|
||||
|
@ -866,63 +963,133 @@ void JITDylib::addDependencies(const SymbolStringPtr &Name,
|
|||
if (DepsOnOtherJITDylib.empty())
|
||||
MI.UnemittedDependencies.erase(&OtherJITDylib);
|
||||
}
|
||||
|
||||
// If this symbol dependended on any symbols in the error state then move
|
||||
// this symbol to the error state too.
|
||||
if (DependsOnSymbolInErrorState)
|
||||
Symbols[Name].setFlags(Symbols[Name].getFlags() | JITSymbolFlags::HasError);
|
||||
}
|
||||
|
||||
void JITDylib::resolve(const SymbolMap &Resolved) {
|
||||
auto CompletedQueries = ES.runSessionLocked([&, this]() {
|
||||
AsynchronousSymbolQuerySet CompletedQueries;
|
||||
Error JITDylib::resolve(const SymbolMap &Resolved) {
|
||||
SymbolNameSet SymbolsInErrorState;
|
||||
AsynchronousSymbolQuerySet CompletedQueries;
|
||||
|
||||
ES.runSessionLocked([&, this]() {
|
||||
struct WorklistEntry {
|
||||
SymbolTable::iterator SymI;
|
||||
JITEvaluatedSymbol ResolvedSym;
|
||||
};
|
||||
|
||||
std::vector<WorklistEntry> Worklist;
|
||||
Worklist.reserve(Resolved.size());
|
||||
|
||||
// Build worklist and check for any symbols in the error state.
|
||||
for (const auto &KV : Resolved) {
|
||||
auto &Name = KV.first;
|
||||
auto Sym = KV.second;
|
||||
|
||||
auto I = Symbols.find(Name);
|
||||
assert(!KV.second.getFlags().hasError() &&
|
||||
"Resolution result can not have error flag set");
|
||||
|
||||
assert(I != Symbols.end() && "Symbol not found");
|
||||
assert(!I->second.hasMaterializerAttached() &&
|
||||
auto SymI = Symbols.find(KV.first);
|
||||
|
||||
assert(SymI != Symbols.end() && "Symbol not found");
|
||||
assert(!SymI->second.hasMaterializerAttached() &&
|
||||
"Resolving symbol with materializer attached?");
|
||||
assert(I->second.getState() == SymbolState::Materializing &&
|
||||
assert(SymI->second.getState() == SymbolState::Materializing &&
|
||||
"Symbol should be materializing");
|
||||
assert(I->second.getAddress() == 0 && "Symbol has already been resolved");
|
||||
assert(SymI->second.getAddress() == 0 &&
|
||||
"Symbol has already been resolved");
|
||||
|
||||
assert((Sym.getFlags() & ~JITSymbolFlags::Weak) ==
|
||||
(I->second.getFlags() & ~JITSymbolFlags::Weak) &&
|
||||
"Resolved flags should match the declared flags");
|
||||
if (SymI->second.getFlags().hasError())
|
||||
SymbolsInErrorState.insert(KV.first);
|
||||
else {
|
||||
assert((KV.second.getFlags() & ~JITSymbolFlags::Weak) ==
|
||||
(SymI->second.getFlags() & ~JITSymbolFlags::Weak) &&
|
||||
"Resolved flags should match the declared flags");
|
||||
|
||||
// Once resolved, symbols can never be weak.
|
||||
JITSymbolFlags ResolvedFlags = Sym.getFlags();
|
||||
Worklist.push_back({SymI, KV.second});
|
||||
}
|
||||
}
|
||||
|
||||
// If any symbols were in the error state then bail out.
|
||||
if (!SymbolsInErrorState.empty())
|
||||
return;
|
||||
|
||||
while (!Worklist.empty()) {
|
||||
auto SymI = Worklist.back().SymI;
|
||||
auto ResolvedSym = Worklist.back().ResolvedSym;
|
||||
Worklist.pop_back();
|
||||
|
||||
auto &Name = SymI->first;
|
||||
|
||||
// Resolved symbols can not be weak: discard the weak flag.
|
||||
JITSymbolFlags ResolvedFlags = ResolvedSym.getFlags();
|
||||
ResolvedFlags &= ~JITSymbolFlags::Weak;
|
||||
I->second.setAddress(Sym.getAddress());
|
||||
I->second.setFlags(ResolvedFlags);
|
||||
I->second.setState(SymbolState::Resolved);
|
||||
SymI->second.setAddress(ResolvedSym.getAddress());
|
||||
SymI->second.setFlags(ResolvedFlags);
|
||||
SymI->second.setState(SymbolState::Resolved);
|
||||
|
||||
auto &MI = MaterializingInfos[Name];
|
||||
for (auto &Q : MI.takeQueriesMeeting(SymbolState::Resolved)) {
|
||||
Q->notifySymbolMetRequiredState(Name, Sym);
|
||||
Q->notifySymbolMetRequiredState(Name, ResolvedSym);
|
||||
if (Q->isComplete())
|
||||
CompletedQueries.insert(std::move(Q));
|
||||
}
|
||||
}
|
||||
|
||||
return CompletedQueries;
|
||||
});
|
||||
|
||||
assert((SymbolsInErrorState.empty() || CompletedQueries.empty()) &&
|
||||
"Can't fail symbols and completed queries at the same time");
|
||||
|
||||
// If we failed any symbols then return an error.
|
||||
if (!SymbolsInErrorState.empty()) {
|
||||
auto FailedSymbolsDepMap = std::make_shared<SymbolDependenceMap>();
|
||||
(*FailedSymbolsDepMap)[this] = std::move(SymbolsInErrorState);
|
||||
return make_error<FailedToMaterialize>(std::move(FailedSymbolsDepMap));
|
||||
}
|
||||
|
||||
// Otherwise notify all the completed queries.
|
||||
for (auto &Q : CompletedQueries) {
|
||||
assert(Q->isComplete() && "Q not completed");
|
||||
Q->handleComplete();
|
||||
}
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
void JITDylib::emit(const SymbolFlagsMap &Emitted) {
|
||||
auto CompletedQueries = ES.runSessionLocked([&, this]() {
|
||||
AsynchronousSymbolQuerySet CompletedQueries;
|
||||
Error JITDylib::emit(const SymbolFlagsMap &Emitted) {
|
||||
AsynchronousSymbolQuerySet CompletedQueries;
|
||||
SymbolNameSet SymbolsInErrorState;
|
||||
|
||||
ES.runSessionLocked([&, this]() {
|
||||
std::vector<SymbolTable::iterator> Worklist;
|
||||
|
||||
// Scan to build worklist, record any symbols in the erorr state.
|
||||
for (const auto &KV : Emitted) {
|
||||
const auto &Name = KV.first;
|
||||
auto &Name = KV.first;
|
||||
|
||||
auto SymI = Symbols.find(Name);
|
||||
assert(SymI != Symbols.end() && "No symbol table entry for Name");
|
||||
|
||||
if (SymI->second.getFlags().hasError())
|
||||
SymbolsInErrorState.insert(Name);
|
||||
else
|
||||
Worklist.push_back(SymI);
|
||||
}
|
||||
|
||||
// If any symbols were in the error state then bail out.
|
||||
if (!SymbolsInErrorState.empty())
|
||||
return;
|
||||
|
||||
// Otherwise update dependencies and move to the emitted state.
|
||||
while (!Worklist.empty()) {
|
||||
auto SymI = Worklist.back();
|
||||
Worklist.pop_back();
|
||||
|
||||
auto &Name = SymI->first;
|
||||
|
||||
auto MII = MaterializingInfos.find(Name);
|
||||
assert(MII != MaterializingInfos.end() &&
|
||||
"Missing MaterializingInfo entry");
|
||||
|
||||
auto &MI = MII->second;
|
||||
|
||||
// For each dependant, transfer this node's emitted dependencies to
|
||||
|
@ -939,8 +1106,12 @@ void JITDylib::emit(const SymbolFlagsMap &Emitted) {
|
|||
auto &DependantMI = DependantMII->second;
|
||||
|
||||
// Remove the dependant's dependency on this node.
|
||||
assert(DependantMI.UnemittedDependencies.count(this) &&
|
||||
"Dependant does not have an unemitted dependencies record for "
|
||||
"this JITDylib");
|
||||
assert(DependantMI.UnemittedDependencies[this].count(Name) &&
|
||||
"Dependant does not count this symbol as a dependency?");
|
||||
|
||||
DependantMI.UnemittedDependencies[this].erase(Name);
|
||||
if (DependantMI.UnemittedDependencies[this].empty())
|
||||
DependantMI.UnemittedDependencies.erase(this);
|
||||
|
@ -980,8 +1151,6 @@ void JITDylib::emit(const SymbolFlagsMap &Emitted) {
|
|||
MI.IsEmitted = true;
|
||||
|
||||
if (MI.UnemittedDependencies.empty()) {
|
||||
auto SymI = Symbols.find(Name);
|
||||
assert(SymI != Symbols.end() && "Symbol has no entry in Symbols table");
|
||||
SymI->second.setState(SymbolState::Ready);
|
||||
for (auto &Q : MI.takeQueriesMeeting(SymbolState::Ready)) {
|
||||
Q->notifySymbolMetRequiredState(Name, SymI->second.getSymbol());
|
||||
|
@ -992,61 +1161,98 @@ void JITDylib::emit(const SymbolFlagsMap &Emitted) {
|
|||
MaterializingInfos.erase(MII);
|
||||
}
|
||||
}
|
||||
|
||||
return CompletedQueries;
|
||||
});
|
||||
|
||||
assert((SymbolsInErrorState.empty() || CompletedQueries.empty()) &&
|
||||
"Can't fail symbols and completed queries at the same time");
|
||||
|
||||
// If we failed any symbols then return an error.
|
||||
if (!SymbolsInErrorState.empty()) {
|
||||
auto FailedSymbolsDepMap = std::make_shared<SymbolDependenceMap>();
|
||||
(*FailedSymbolsDepMap)[this] = std::move(SymbolsInErrorState);
|
||||
return make_error<FailedToMaterialize>(std::move(FailedSymbolsDepMap));
|
||||
}
|
||||
|
||||
// Otherwise notify all the completed queries.
|
||||
for (auto &Q : CompletedQueries) {
|
||||
assert(Q->isComplete() && "Q is not complete");
|
||||
Q->handleComplete();
|
||||
}
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
void JITDylib::notifyFailed(const SymbolNameSet &FailedSymbols) {
|
||||
void JITDylib::notifyFailed(const SymbolFlagsMap &FailedSymbols) {
|
||||
AsynchronousSymbolQuerySet FailedQueries;
|
||||
|
||||
// FIXME: This should fail any transitively dependant symbols too.
|
||||
ES.runSessionLocked([&]() {
|
||||
for (auto &KV : FailedSymbols) {
|
||||
auto &Name = KV.first;
|
||||
|
||||
auto FailedQueriesToNotify = ES.runSessionLocked([&, this]() {
|
||||
AsynchronousSymbolQuerySet FailedQueries;
|
||||
std::vector<MaterializingInfosMap::iterator> MIIsToRemove;
|
||||
assert(Symbols.count(Name) && "No symbol table entry for Name");
|
||||
auto &Sym = Symbols[Name];
|
||||
|
||||
for (auto &Name : FailedSymbols) {
|
||||
auto I = Symbols.find(Name);
|
||||
assert(I != Symbols.end() && "Symbol not present in this JITDylib");
|
||||
Symbols.erase(I);
|
||||
// Move the symbol into the error state.
|
||||
// Note that this may be redundant: The symbol might already have been
|
||||
// moved to this state in response to the failure of a dependence.
|
||||
Sym.setFlags(Sym.getFlags() | JITSymbolFlags::HasError);
|
||||
|
||||
// FIXME: Come up with a sane mapping of state to
|
||||
// presence-of-MaterializingInfo so that we can assert presence / absence
|
||||
// here, rather than testing it.
|
||||
auto MII = MaterializingInfos.find(Name);
|
||||
|
||||
// If we have not created a MaterializingInfo for this symbol yet then
|
||||
// there is nobody to notify.
|
||||
if (MII == MaterializingInfos.end())
|
||||
continue;
|
||||
|
||||
// Remove this symbol from the dependants list of any dependencies.
|
||||
for (auto &KV : MII->second.UnemittedDependencies) {
|
||||
auto *DependencyJD = KV.first;
|
||||
auto &Dependencies = KV.second;
|
||||
for (auto &DependencyName : Dependencies) {
|
||||
auto DependencyMII =
|
||||
DependencyJD->MaterializingInfos.find(DependencyName);
|
||||
assert(DependencyMII != DependencyJD->MaterializingInfos.end() &&
|
||||
"Unemitted dependency must have a MaterializingInfo entry");
|
||||
assert(DependencyMII->second.Dependants.count(this) &&
|
||||
"Dependency's dependants list does not contain this JITDylib");
|
||||
assert(DependencyMII->second.Dependants[this].count(Name) &&
|
||||
"Dependency's dependants list does not contain dependant");
|
||||
DependencyMII->second.Dependants[this].erase(Name);
|
||||
auto &MI = MII->second;
|
||||
|
||||
// Move all dependants to the error state and disconnect from them.
|
||||
for (auto &KV : MI.Dependants) {
|
||||
auto &DependantJD = *KV.first;
|
||||
for (auto &DependantName : KV.second) {
|
||||
assert(DependantJD.Symbols.count(DependantName) &&
|
||||
"No symbol table entry for DependantName");
|
||||
auto &DependantSym = DependantJD.Symbols[DependantName];
|
||||
DependantSym.setFlags(DependantSym.getFlags() |
|
||||
JITSymbolFlags::HasError);
|
||||
|
||||
assert(DependantJD.MaterializingInfos.count(DependantName) &&
|
||||
"No MaterializingInfo for dependant");
|
||||
auto &DependantMI = DependantJD.MaterializingInfos[DependantName];
|
||||
|
||||
auto UnemittedDepI = DependantMI.UnemittedDependencies.find(this);
|
||||
assert(UnemittedDepI != DependantMI.UnemittedDependencies.end() &&
|
||||
"No UnemittedDependencies entry for this JITDylib");
|
||||
assert(UnemittedDepI->second.count(Name) &&
|
||||
"No UnemittedDependencies entry for this symbol");
|
||||
UnemittedDepI->second.erase(Name);
|
||||
if (UnemittedDepI->second.empty())
|
||||
DependantMI.UnemittedDependencies.erase(UnemittedDepI);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy all the queries to the FailedQueries list, then abandon them.
|
||||
// This has to be a copy, and the copy has to come before the abandon
|
||||
// operation: Each Q.detach() call will reach back into this
|
||||
// PendingQueries list to remove Q.
|
||||
// Disconnect from all unemitted depenencies.
|
||||
for (auto &KV : MI.UnemittedDependencies) {
|
||||
auto &UnemittedDepJD = *KV.first;
|
||||
for (auto &UnemittedDepName : KV.second) {
|
||||
auto UnemittedDepMII =
|
||||
UnemittedDepJD.MaterializingInfos.find(UnemittedDepName);
|
||||
assert(UnemittedDepMII != UnemittedDepJD.MaterializingInfos.end() &&
|
||||
"Missing MII for unemitted dependency");
|
||||
assert(UnemittedDepMII->second.Dependants.count(this) &&
|
||||
"JD not listed as a dependant of unemitted dependency");
|
||||
assert(UnemittedDepMII->second.Dependants[this].count(Name) &&
|
||||
"Name is not listed as a dependant of unemitted dependency");
|
||||
UnemittedDepMII->second.Dependants[this].erase(Name);
|
||||
if (UnemittedDepMII->second.Dependants[this].empty())
|
||||
UnemittedDepMII->second.Dependants.erase(this);
|
||||
}
|
||||
}
|
||||
|
||||
// Collect queries to be failed for this MII.
|
||||
for (auto &Q : MII->second.pendingQueries())
|
||||
FailedQueries.insert(Q);
|
||||
|
||||
MIIsToRemove.push_back(std::move(MII));
|
||||
}
|
||||
|
||||
// Detach failed queries.
|
||||
|
@ -1054,18 +1260,17 @@ void JITDylib::notifyFailed(const SymbolNameSet &FailedSymbols) {
|
|||
Q->detach();
|
||||
|
||||
// Remove the MaterializingInfos.
|
||||
for (auto &MII : MIIsToRemove) {
|
||||
assert(!MII->second.hasQueriesPending() &&
|
||||
"Queries remain after symbol was failed");
|
||||
|
||||
MaterializingInfos.erase(MII);
|
||||
for (auto &KV : FailedSymbols) {
|
||||
assert(MaterializingInfos.count(KV.first) && "Expected MI for Name");
|
||||
MaterializingInfos.erase(KV.first);
|
||||
}
|
||||
|
||||
return FailedQueries;
|
||||
});
|
||||
|
||||
for (auto &Q : FailedQueriesToNotify)
|
||||
Q->handleFailed(make_error<FailedToMaterialize>(FailedSymbols));
|
||||
auto FailedSymbolsMap = std::make_shared<SymbolDependenceMap>();
|
||||
for (auto &KV : FailedSymbols)
|
||||
(*FailedSymbolsMap)[this].insert(KV.first);
|
||||
for (auto &Q : FailedQueries)
|
||||
Q->handleFailed(make_error<FailedToMaterialize>(FailedSymbolsMap));
|
||||
}
|
||||
|
||||
void JITDylib::setSearchOrder(JITDylibSearchList NewSearchOrder,
|
||||
|
@ -1221,7 +1426,8 @@ Error JITDylib::lodgeQuery(std::shared_ptr<AsynchronousSymbolQuery> &Q,
|
|||
MaterializationUnitList &MUs) {
|
||||
assert(Q && "Query can not be null");
|
||||
|
||||
lodgeQueryImpl(Q, Unresolved, MatchNonExported, MUs);
|
||||
if (auto Err = lodgeQueryImpl(Q, Unresolved, MatchNonExported, MUs))
|
||||
return Err;
|
||||
|
||||
// Run any definition generators.
|
||||
for (auto &DG : DefGenerators) {
|
||||
|
@ -1233,13 +1439,21 @@ Error JITDylib::lodgeQuery(std::shared_ptr<AsynchronousSymbolQuery> &Q,
|
|||
// Run the generator.
|
||||
auto NewDefs = DG->tryToGenerate(*this, Unresolved);
|
||||
|
||||
// If the generator returns an error then bail out.
|
||||
if (!NewDefs)
|
||||
return NewDefs.takeError();
|
||||
|
||||
// If the generator was able to generate new definitions for any of the
|
||||
// unresolved symbols then lodge the query against them.
|
||||
if (!NewDefs->empty()) {
|
||||
for (auto &D : *NewDefs)
|
||||
Unresolved.erase(D);
|
||||
lodgeQueryImpl(Q, *NewDefs, MatchNonExported, MUs);
|
||||
|
||||
// Lodge query. This can not fail as any new definitions were added
|
||||
// by the generator under the session locked. Since they can't have
|
||||
// started materializing yet the can not have failed.
|
||||
cantFail(lodgeQueryImpl(Q, *NewDefs, MatchNonExported, MUs));
|
||||
|
||||
assert(NewDefs->empty() &&
|
||||
"All fallback defs should have been found by lookupImpl");
|
||||
}
|
||||
|
@ -1248,7 +1462,7 @@ Error JITDylib::lodgeQuery(std::shared_ptr<AsynchronousSymbolQuery> &Q,
|
|||
return Error::success();
|
||||
}
|
||||
|
||||
void JITDylib::lodgeQueryImpl(
|
||||
Error JITDylib::lodgeQueryImpl(
|
||||
std::shared_ptr<AsynchronousSymbolQuery> &Q, SymbolNameSet &Unresolved,
|
||||
bool MatchNonExported,
|
||||
std::vector<std::unique_ptr<MaterializationUnit>> &MUs) {
|
||||
|
@ -1269,6 +1483,14 @@ void JITDylib::lodgeQueryImpl(
|
|||
// Unresolved set.
|
||||
ToRemove.push_back(Name);
|
||||
|
||||
// If we matched against this symbol but it is in the error state then
|
||||
// bail out and treat it as a failure to materialize.
|
||||
if (SymI->second.getFlags().hasError()) {
|
||||
auto FailedSymbolsMap = std::make_shared<SymbolDependenceMap>();
|
||||
(*FailedSymbolsMap)[this] = {Name};
|
||||
return make_error<FailedToMaterialize>(std::move(FailedSymbolsMap));
|
||||
}
|
||||
|
||||
// If this symbol already meets the required state for then notify the
|
||||
// query and continue.
|
||||
if (SymI->second.getState() >= Q->getRequiredState()) {
|
||||
|
@ -1311,6 +1533,8 @@ void JITDylib::lodgeQueryImpl(
|
|||
// Remove any symbols that we found.
|
||||
for (auto &Name : ToRemove)
|
||||
Unresolved.erase(Name);
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Expected<SymbolNameSet>
|
||||
|
|
|
@ -37,8 +37,9 @@ private:
|
|||
void materialize(MaterializationResponsibility R) override {
|
||||
SymbolMap Result;
|
||||
Result[Name] = JITEvaluatedSymbol(Compile(), JITSymbolFlags::Exported);
|
||||
R.notifyResolved(Result);
|
||||
R.notifyEmitted();
|
||||
// No dependencies, so these calls cannot fail.
|
||||
cantFail(R.notifyResolved(Result));
|
||||
cantFail(R.notifyEmitted());
|
||||
}
|
||||
|
||||
void discard(const JITDylib &JD, const SymbolStringPtr &Name) override {
|
||||
|
|
|
@ -182,8 +182,9 @@ void LazyReexportsMaterializationUnit::materialize(
|
|||
for (auto &Alias : RequestedAliases)
|
||||
Stubs[Alias.first] = ISManager.findStub(*Alias.first, false);
|
||||
|
||||
R.notifyResolved(Stubs);
|
||||
R.notifyEmitted();
|
||||
// No registered dependencies, so these calls cannot fail.
|
||||
cantFail(R.notifyResolved(Stubs));
|
||||
cantFail(R.notifyEmitted());
|
||||
}
|
||||
|
||||
void LazyReexportsMaterializationUnit::discard(const JITDylib &JD,
|
||||
|
|
|
@ -127,7 +127,11 @@ public:
|
|||
if (auto Err = MR.defineMaterializing(ExtraSymbolsToClaim))
|
||||
return notifyFailed(std::move(Err));
|
||||
|
||||
MR.notifyResolved(InternedResult);
|
||||
if (auto Err = MR.notifyResolved(InternedResult)) {
|
||||
Layer.getExecutionSession().reportError(std::move(Err));
|
||||
MR.failMaterialization();
|
||||
return;
|
||||
}
|
||||
|
||||
Layer.notifyLoaded(MR);
|
||||
}
|
||||
|
@ -138,10 +142,12 @@ public:
|
|||
if (auto Err = Layer.notifyEmitted(MR, std::move(A))) {
|
||||
Layer.getExecutionSession().reportError(std::move(Err));
|
||||
MR.failMaterialization();
|
||||
|
||||
return;
|
||||
}
|
||||
MR.notifyEmitted();
|
||||
if (auto Err = MR.notifyEmitted()) {
|
||||
Layer.getExecutionSession().reportError(std::move(Err));
|
||||
MR.failMaterialization();
|
||||
}
|
||||
}
|
||||
|
||||
AtomGraphPassFunction getMarkLivePass(const Triple &TT) const override {
|
||||
|
|
|
@ -184,7 +184,10 @@ Error RTDyldObjectLinkingLayer::onObjLoad(
|
|||
if (auto Err = R.defineMaterializing(ExtraSymbolsToClaim))
|
||||
return Err;
|
||||
|
||||
R.notifyResolved(Symbols);
|
||||
if (auto Err = R.notifyResolved(Symbols)) {
|
||||
R.failMaterialization();
|
||||
return Err;
|
||||
}
|
||||
|
||||
if (NotifyLoaded)
|
||||
NotifyLoaded(K, Obj, *LoadedObjInfo);
|
||||
|
@ -201,7 +204,11 @@ void RTDyldObjectLinkingLayer::onObjEmit(
|
|||
return;
|
||||
}
|
||||
|
||||
R.notifyEmitted();
|
||||
if (auto Err = R.notifyEmitted()) {
|
||||
getExecutionSession().reportError(std::move(Err));
|
||||
R.failMaterialization();
|
||||
return;
|
||||
}
|
||||
|
||||
if (NotifyEmitted)
|
||||
NotifyEmitted(K, std::move(ObjBuffer));
|
||||
|
|
|
@ -48,11 +48,11 @@ TEST_F(CoreAPIsStandardTest, BasicSuccessfulLookup) {
|
|||
|
||||
EXPECT_FALSE(OnCompletionRun) << "Should not have been resolved yet";
|
||||
|
||||
FooMR->notifyResolved({{Foo, FooSym}});
|
||||
cantFail(FooMR->notifyResolved({{Foo, FooSym}}));
|
||||
|
||||
EXPECT_FALSE(OnCompletionRun) << "Should not be ready yet";
|
||||
|
||||
FooMR->notifyEmitted();
|
||||
cantFail(FooMR->notifyEmitted());
|
||||
|
||||
EXPECT_TRUE(OnCompletionRun) << "Should have been marked ready";
|
||||
}
|
||||
|
@ -109,8 +109,8 @@ TEST_F(CoreAPIsStandardTest, RemoveSymbolsTest) {
|
|||
SymbolFlagsMap({{Bar, BarSym.getFlags()}}),
|
||||
[this](MaterializationResponsibility R) {
|
||||
ADD_FAILURE() << "Unexpected materialization of \"Bar\"";
|
||||
R.notifyResolved({{Bar, BarSym}});
|
||||
R.notifyEmitted();
|
||||
cantFail(R.notifyResolved({{Bar, BarSym}}));
|
||||
cantFail(R.notifyEmitted());
|
||||
},
|
||||
[&](const JITDylib &JD, const SymbolStringPtr &Name) {
|
||||
EXPECT_EQ(Name, Bar) << "Expected \"Bar\" to be discarded";
|
||||
|
@ -156,8 +156,8 @@ TEST_F(CoreAPIsStandardTest, RemoveSymbolsTest) {
|
|||
consumeError(std::move(Err));
|
||||
}
|
||||
|
||||
BazR->notifyResolved({{Baz, BazSym}});
|
||||
BazR->notifyEmitted();
|
||||
cantFail(BazR->notifyResolved({{Baz, BazSym}}));
|
||||
cantFail(BazR->notifyEmitted());
|
||||
{
|
||||
// Attempt 3: Search now that all symbols are fully materialized
|
||||
// (Foo, Baz), or not yet materialized (Bar).
|
||||
|
@ -318,8 +318,8 @@ TEST_F(CoreAPIsStandardTest, TestThatReExportsDontUnnecessarilyMaterialize) {
|
|||
SymbolFlagsMap({{Bar, BarSym.getFlags()}}),
|
||||
[&](MaterializationResponsibility R) {
|
||||
BarMaterialized = true;
|
||||
R.notifyResolved({{Bar, BarSym}});
|
||||
R.notifyEmitted();
|
||||
cantFail(R.notifyResolved({{Bar, BarSym}}));
|
||||
cantFail(R.notifyEmitted());
|
||||
});
|
||||
|
||||
cantFail(JD.define(BarMU));
|
||||
|
@ -374,8 +374,10 @@ TEST_F(CoreAPIsStandardTest, TestTrivialCircularDependency) {
|
|||
OnCompletion, NoDependenciesToRegister);
|
||||
|
||||
FooR->addDependenciesForAll({{&JD, SymbolNameSet({Foo})}});
|
||||
FooR->notifyResolved({{Foo, FooSym}});
|
||||
FooR->notifyEmitted();
|
||||
EXPECT_THAT_ERROR(FooR->notifyResolved({{Foo, FooSym}}), Succeeded())
|
||||
<< "No symbols marked failed, but Foo failed to resolve";
|
||||
EXPECT_THAT_ERROR(FooR->notifyEmitted(), Succeeded())
|
||||
<< "No symbols marked failed, but Foo failed to emit";
|
||||
|
||||
EXPECT_TRUE(FooReady)
|
||||
<< "Self-dependency prevented symbol from being marked ready";
|
||||
|
@ -488,9 +490,12 @@ TEST_F(CoreAPIsStandardTest, TestCircularDependenceInOneJITDylib) {
|
|||
EXPECT_FALSE(BazResolved) << "\"Baz\" should not be resolved yet";
|
||||
|
||||
// Resolve the symbols (but do not emit them).
|
||||
FooR->notifyResolved({{Foo, FooSym}});
|
||||
BarR->notifyResolved({{Bar, BarSym}});
|
||||
BazR->notifyResolved({{Baz, BazSym}});
|
||||
EXPECT_THAT_ERROR(FooR->notifyResolved({{Foo, FooSym}}), Succeeded())
|
||||
<< "No symbols failed, but Foo failed to resolve";
|
||||
EXPECT_THAT_ERROR(BarR->notifyResolved({{Bar, BarSym}}), Succeeded())
|
||||
<< "No symbols failed, but Bar failed to resolve";
|
||||
EXPECT_THAT_ERROR(BazR->notifyResolved({{Baz, BazSym}}), Succeeded())
|
||||
<< "No symbols failed, but Baz failed to resolve";
|
||||
|
||||
// Verify that the symbols have been resolved, but are not ready yet.
|
||||
EXPECT_TRUE(FooResolved) << "\"Foo\" should be resolved now";
|
||||
|
@ -502,8 +507,10 @@ TEST_F(CoreAPIsStandardTest, TestCircularDependenceInOneJITDylib) {
|
|||
EXPECT_FALSE(BazReady) << "\"Baz\" should not be ready yet";
|
||||
|
||||
// Emit two of the symbols.
|
||||
FooR->notifyEmitted();
|
||||
BarR->notifyEmitted();
|
||||
EXPECT_THAT_ERROR(FooR->notifyEmitted(), Succeeded())
|
||||
<< "No symbols failed, but Foo failed to emit";
|
||||
EXPECT_THAT_ERROR(BarR->notifyEmitted(), Succeeded())
|
||||
<< "No symbols failed, but Bar failed to emit";
|
||||
|
||||
// Verify that nothing is ready until the circular dependence is resolved.
|
||||
EXPECT_FALSE(FooReady) << "\"Foo\" still should not be ready";
|
||||
|
@ -511,7 +518,8 @@ TEST_F(CoreAPIsStandardTest, TestCircularDependenceInOneJITDylib) {
|
|||
EXPECT_FALSE(BazReady) << "\"Baz\" still should not be ready";
|
||||
|
||||
// Emit the last symbol.
|
||||
BazR->notifyEmitted();
|
||||
EXPECT_THAT_ERROR(BazR->notifyEmitted(), Succeeded())
|
||||
<< "No symbols failed, but Baz failed to emit";
|
||||
|
||||
// Verify that everything becomes ready once the circular dependence resolved.
|
||||
EXPECT_TRUE(FooReady) << "\"Foo\" should be ready now";
|
||||
|
@ -519,6 +527,197 @@ TEST_F(CoreAPIsStandardTest, TestCircularDependenceInOneJITDylib) {
|
|||
EXPECT_TRUE(BazReady) << "\"Baz\" should be ready now";
|
||||
}
|
||||
|
||||
TEST_F(CoreAPIsStandardTest, FailureInDependency) {
|
||||
Optional<MaterializationResponsibility> FooR;
|
||||
Optional<MaterializationResponsibility> BarR;
|
||||
|
||||
// Create a MaterializationUnit for each symbol that moves the
|
||||
// MaterializationResponsibility into one of the locals above.
|
||||
auto FooMU = std::make_unique<SimpleMaterializationUnit>(
|
||||
SymbolFlagsMap({{Foo, FooSym.getFlags()}}),
|
||||
[&](MaterializationResponsibility R) { FooR.emplace(std::move(R)); });
|
||||
|
||||
auto BarMU = std::make_unique<SimpleMaterializationUnit>(
|
||||
SymbolFlagsMap({{Bar, BarSym.getFlags()}}),
|
||||
[&](MaterializationResponsibility R) { BarR.emplace(std::move(R)); });
|
||||
|
||||
// Define the symbols.
|
||||
cantFail(JD.define(FooMU));
|
||||
cantFail(JD.define(BarMU));
|
||||
|
||||
bool OnFooReadyRun = false;
|
||||
auto OnFooReady = [&](Expected<SymbolMap> Result) {
|
||||
EXPECT_THAT_EXPECTED(std::move(Result), Failed());
|
||||
OnFooReadyRun = true;
|
||||
};
|
||||
|
||||
ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo}, SymbolState::Ready,
|
||||
std::move(OnFooReady), NoDependenciesToRegister);
|
||||
|
||||
bool OnBarReadyRun = false;
|
||||
auto OnBarReady = [&](Expected<SymbolMap> Result) {
|
||||
EXPECT_THAT_EXPECTED(std::move(Result), Failed());
|
||||
OnBarReadyRun = true;
|
||||
};
|
||||
|
||||
ES.lookup(JITDylibSearchList({{&JD, false}}), {Bar}, SymbolState::Ready,
|
||||
std::move(OnBarReady), NoDependenciesToRegister);
|
||||
|
||||
// Add a dependency by Foo on Bar.
|
||||
FooR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}});
|
||||
|
||||
// Fail bar.
|
||||
BarR->failMaterialization();
|
||||
|
||||
// Verify that queries on Bar failed, but queries on Foo have not yet.
|
||||
EXPECT_TRUE(OnBarReadyRun) << "Query for \"Bar\" was not run";
|
||||
EXPECT_FALSE(OnFooReadyRun) << "Query for \"Foo\" was run unexpectedly";
|
||||
|
||||
// Check that we can still resolve Foo (even though it has been failed).
|
||||
EXPECT_THAT_ERROR(FooR->notifyResolved({{Foo, FooSym}}), Failed())
|
||||
<< "Expected resolution for \"Foo\" to fail.";
|
||||
|
||||
FooR->failMaterialization();
|
||||
|
||||
// Verify that queries on Foo have now failed.
|
||||
EXPECT_TRUE(OnFooReadyRun) << "Query for \"Foo\" was not run";
|
||||
|
||||
// Verify that subsequent lookups on Bar and Foo fail.
|
||||
EXPECT_THAT_EXPECTED(ES.lookup({&JD}, {Bar}), Failed())
|
||||
<< "Lookup on failed symbol should fail";
|
||||
|
||||
EXPECT_THAT_EXPECTED(ES.lookup({&JD}, {Foo}), Failed())
|
||||
<< "Lookup on failed symbol should fail";
|
||||
}
|
||||
|
||||
TEST_F(CoreAPIsStandardTest, FailureInCircularDependency) {
|
||||
Optional<MaterializationResponsibility> FooR;
|
||||
Optional<MaterializationResponsibility> BarR;
|
||||
|
||||
// Create a MaterializationUnit for each symbol that moves the
|
||||
// MaterializationResponsibility into one of the locals above.
|
||||
auto FooMU = std::make_unique<SimpleMaterializationUnit>(
|
||||
SymbolFlagsMap({{Foo, FooSym.getFlags()}}),
|
||||
[&](MaterializationResponsibility R) { FooR.emplace(std::move(R)); });
|
||||
|
||||
auto BarMU = std::make_unique<SimpleMaterializationUnit>(
|
||||
SymbolFlagsMap({{Bar, BarSym.getFlags()}}),
|
||||
[&](MaterializationResponsibility R) { BarR.emplace(std::move(R)); });
|
||||
|
||||
// Define the symbols.
|
||||
cantFail(JD.define(FooMU));
|
||||
cantFail(JD.define(BarMU));
|
||||
|
||||
bool OnFooReadyRun = false;
|
||||
auto OnFooReady = [&](Expected<SymbolMap> Result) {
|
||||
EXPECT_THAT_EXPECTED(std::move(Result), Failed());
|
||||
OnFooReadyRun = true;
|
||||
};
|
||||
|
||||
ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo}, SymbolState::Ready,
|
||||
std::move(OnFooReady), NoDependenciesToRegister);
|
||||
|
||||
bool OnBarReadyRun = false;
|
||||
auto OnBarReady = [&](Expected<SymbolMap> Result) {
|
||||
EXPECT_THAT_EXPECTED(std::move(Result), Failed());
|
||||
OnBarReadyRun = true;
|
||||
};
|
||||
|
||||
ES.lookup(JITDylibSearchList({{&JD, false}}), {Bar}, SymbolState::Ready,
|
||||
std::move(OnBarReady), NoDependenciesToRegister);
|
||||
|
||||
// Add a dependency by Foo on Bar and vice-versa.
|
||||
FooR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}});
|
||||
BarR->addDependenciesForAll({{&JD, SymbolNameSet({Foo})}});
|
||||
|
||||
// Fail bar.
|
||||
BarR->failMaterialization();
|
||||
|
||||
// Verify that queries on Bar failed, but queries on Foo have not yet.
|
||||
EXPECT_TRUE(OnBarReadyRun) << "Query for \"Bar\" was not run";
|
||||
EXPECT_FALSE(OnFooReadyRun) << "Query for \"Foo\" was run unexpectedly";
|
||||
|
||||
// Verify that trying to resolve Foo fails.
|
||||
EXPECT_THAT_ERROR(FooR->notifyResolved({{Foo, FooSym}}), Failed())
|
||||
<< "Expected resolution for \"Foo\" to fail.";
|
||||
|
||||
FooR->failMaterialization();
|
||||
|
||||
// Verify that queries on Foo have now failed.
|
||||
EXPECT_TRUE(OnFooReadyRun) << "Query for \"Foo\" was not run";
|
||||
|
||||
// Verify that subsequent lookups on Bar and Foo fail.
|
||||
EXPECT_THAT_EXPECTED(ES.lookup({&JD}, {Bar}), Failed())
|
||||
<< "Lookup on failed symbol should fail";
|
||||
|
||||
EXPECT_THAT_EXPECTED(ES.lookup({&JD}, {Foo}), Failed())
|
||||
<< "Lookup on failed symbol should fail";
|
||||
}
|
||||
|
||||
TEST_F(CoreAPIsStandardTest, AddDependencyOnFailedSymbol) {
|
||||
Optional<MaterializationResponsibility> FooR;
|
||||
Optional<MaterializationResponsibility> BarR;
|
||||
|
||||
// Create a MaterializationUnit for each symbol that moves the
|
||||
// MaterializationResponsibility into one of the locals above.
|
||||
auto FooMU = std::make_unique<SimpleMaterializationUnit>(
|
||||
SymbolFlagsMap({{Foo, FooSym.getFlags()}}),
|
||||
[&](MaterializationResponsibility R) { FooR.emplace(std::move(R)); });
|
||||
|
||||
auto BarMU = std::make_unique<SimpleMaterializationUnit>(
|
||||
SymbolFlagsMap({{Bar, BarSym.getFlags()}}),
|
||||
[&](MaterializationResponsibility R) { BarR.emplace(std::move(R)); });
|
||||
|
||||
// Define the symbols.
|
||||
cantFail(JD.define(FooMU));
|
||||
cantFail(JD.define(BarMU));
|
||||
|
||||
bool OnFooReadyRun = false;
|
||||
auto OnFooReady = [&](Expected<SymbolMap> Result) {
|
||||
EXPECT_THAT_EXPECTED(std::move(Result), Failed());
|
||||
OnFooReadyRun = true;
|
||||
};
|
||||
|
||||
ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo}, SymbolState::Ready,
|
||||
std::move(OnFooReady), NoDependenciesToRegister);
|
||||
|
||||
bool OnBarReadyRun = false;
|
||||
auto OnBarReady = [&](Expected<SymbolMap> Result) {
|
||||
EXPECT_THAT_EXPECTED(std::move(Result), Failed());
|
||||
OnBarReadyRun = true;
|
||||
};
|
||||
|
||||
ES.lookup(JITDylibSearchList({{&JD, false}}), {Bar}, SymbolState::Ready,
|
||||
std::move(OnBarReady), NoDependenciesToRegister);
|
||||
|
||||
// Fail bar.
|
||||
BarR->failMaterialization();
|
||||
|
||||
// We expect Bar's query to fail immediately, but Foo's query not to have run
|
||||
// yet.
|
||||
EXPECT_TRUE(OnBarReadyRun) << "Query for \"Bar\" was not run";
|
||||
EXPECT_FALSE(OnFooReadyRun) << "Query for \"Foo\" should not have run yet";
|
||||
|
||||
// Add dependency of Foo on Bar.
|
||||
FooR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}});
|
||||
|
||||
// Check that we can still resolve Foo (even though it has been failed).
|
||||
EXPECT_THAT_ERROR(FooR->notifyResolved({{Foo, FooSym}}), Failed())
|
||||
<< "Expected resolution for \"Foo\" to fail.";
|
||||
|
||||
FooR->failMaterialization();
|
||||
|
||||
// Foo's query should have failed before we return from addDependencies.
|
||||
EXPECT_TRUE(OnFooReadyRun) << "Query for \"Foo\" was not run";
|
||||
|
||||
// Verify that subsequent lookups on Bar and Foo fail.
|
||||
EXPECT_THAT_EXPECTED(ES.lookup({&JD}, {Bar}), Failed())
|
||||
<< "Lookup on failed symbol should fail";
|
||||
|
||||
EXPECT_THAT_EXPECTED(ES.lookup({&JD}, {Foo}), Failed())
|
||||
<< "Lookup on failed symbol should fail";
|
||||
}
|
||||
|
||||
TEST_F(CoreAPIsStandardTest, DropMaterializerWhenEmpty) {
|
||||
bool DestructorRun = false;
|
||||
|
||||
|
@ -560,8 +759,8 @@ TEST_F(CoreAPIsStandardTest, AddAndMaterializeLazySymbol) {
|
|||
SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}, {Bar, WeakExported}}),
|
||||
[&](MaterializationResponsibility R) {
|
||||
assert(BarDiscarded && "Bar should have been discarded by this point");
|
||||
R.notifyResolved(SymbolMap({{Foo, FooSym}}));
|
||||
R.notifyEmitted();
|
||||
cantFail(R.notifyResolved(SymbolMap({{Foo, FooSym}})));
|
||||
cantFail(R.notifyEmitted());
|
||||
FooMaterialized = true;
|
||||
},
|
||||
[&](const JITDylib &JD, SymbolStringPtr Name) {
|
||||
|
@ -601,7 +800,8 @@ TEST_F(CoreAPIsStandardTest, TestBasicWeakSymbolMaterialization) {
|
|||
auto MU1 = std::make_unique<SimpleMaterializationUnit>(
|
||||
SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}),
|
||||
[&](MaterializationResponsibility R) {
|
||||
R.notifyResolved(SymbolMap({{Foo, FooSym}, {Bar, BarSym}})), R.notifyEmitted();
|
||||
cantFail(R.notifyResolved(SymbolMap({{Foo, FooSym}, {Bar, BarSym}})));
|
||||
cantFail(R.notifyEmitted());
|
||||
BarMaterialized = true;
|
||||
});
|
||||
|
||||
|
@ -650,8 +850,8 @@ TEST_F(CoreAPIsStandardTest, DefineMaterializingSymbol) {
|
|||
[&](MaterializationResponsibility R) {
|
||||
cantFail(
|
||||
R.defineMaterializing(SymbolFlagsMap({{Bar, BarSym.getFlags()}})));
|
||||
R.notifyResolved(SymbolMap({{Foo, FooSym}, {Bar, BarSym}}));
|
||||
R.notifyEmitted();
|
||||
cantFail(R.notifyResolved(SymbolMap({{Foo, FooSym}, {Bar, BarSym}})));
|
||||
cantFail(R.notifyEmitted());
|
||||
});
|
||||
|
||||
cantFail(JD.define(MU));
|
||||
|
@ -716,21 +916,22 @@ TEST_F(CoreAPIsStandardTest, FailResolution) {
|
|||
|
||||
EXPECT_FALSE(!!Result) << "Expected failure";
|
||||
if (!Result) {
|
||||
handleAllErrors(Result.takeError(),
|
||||
[&](FailedToMaterialize &F) {
|
||||
EXPECT_EQ(F.getSymbols(), Names)
|
||||
<< "Expected to fail on symbols in Names";
|
||||
},
|
||||
[](ErrorInfoBase &EIB) {
|
||||
std::string ErrMsg;
|
||||
{
|
||||
raw_string_ostream ErrOut(ErrMsg);
|
||||
EIB.log(ErrOut);
|
||||
}
|
||||
ADD_FAILURE()
|
||||
<< "Expected a FailedToResolve error. Got:\n"
|
||||
<< ErrMsg;
|
||||
});
|
||||
handleAllErrors(
|
||||
Result.takeError(),
|
||||
[&](FailedToMaterialize &F) {
|
||||
EXPECT_TRUE(F.getSymbols().count(&JD))
|
||||
<< "Expected to fail on JITDylib JD";
|
||||
EXPECT_EQ(F.getSymbols().find(&JD)->second, Names)
|
||||
<< "Expected to fail on symbols in Names";
|
||||
},
|
||||
[](ErrorInfoBase &EIB) {
|
||||
std::string ErrMsg;
|
||||
{
|
||||
raw_string_ostream ErrOut(ErrMsg);
|
||||
EIB.log(ErrOut);
|
||||
}
|
||||
ADD_FAILURE() << "Expected a FailedToResolve error. Got:\n" << ErrMsg;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -741,7 +942,7 @@ TEST_F(CoreAPIsStandardTest, FailEmissionEarly) {
|
|||
auto MU = std::make_unique<SimpleMaterializationUnit>(
|
||||
SymbolFlagsMap({{Foo, FooSym.getFlags()}, {Bar, BarSym.getFlags()}}),
|
||||
[&](MaterializationResponsibility R) {
|
||||
R.notifyResolved(SymbolMap({{Foo, FooSym}, {Bar, BarSym}}));
|
||||
cantFail(R.notifyResolved(SymbolMap({{Foo, FooSym}, {Bar, BarSym}})));
|
||||
|
||||
ES.lookup(
|
||||
JITDylibSearchList({{&JD, false}}), SymbolNameSet({Baz}),
|
||||
|
@ -773,8 +974,8 @@ TEST_F(CoreAPIsStandardTest, TestLookupWithUnthreadedMaterialization) {
|
|||
auto MU = std::make_unique<SimpleMaterializationUnit>(
|
||||
SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}}),
|
||||
[&](MaterializationResponsibility R) {
|
||||
R.notifyResolved({{Foo, FooSym}});
|
||||
R.notifyEmitted();
|
||||
cantFail(R.notifyResolved({{Foo, FooSym}}));
|
||||
cantFail(R.notifyEmitted());
|
||||
});
|
||||
|
||||
cantFail(JD.define(MU));
|
||||
|
@ -832,15 +1033,15 @@ TEST_F(CoreAPIsStandardTest, TestGetRequestedSymbolsAndReplace) {
|
|||
auto NewMU = std::make_unique<SimpleMaterializationUnit>(
|
||||
SymbolFlagsMap({{Bar, BarSym.getFlags()}}),
|
||||
[&](MaterializationResponsibility R2) {
|
||||
R2.notifyResolved(SymbolMap({{Bar, BarSym}}));
|
||||
R2.notifyEmitted();
|
||||
cantFail(R2.notifyResolved(SymbolMap({{Bar, BarSym}})));
|
||||
cantFail(R2.notifyEmitted());
|
||||
BarMaterialized = true;
|
||||
});
|
||||
|
||||
R.replace(std::move(NewMU));
|
||||
|
||||
R.notifyResolved(SymbolMap({{Foo, FooSym}}));
|
||||
R.notifyEmitted();
|
||||
cantFail(R.notifyResolved(SymbolMap({{Foo, FooSym}})));
|
||||
cantFail(R.notifyEmitted());
|
||||
|
||||
FooMaterialized = true;
|
||||
});
|
||||
|
@ -871,10 +1072,10 @@ TEST_F(CoreAPIsStandardTest, TestMaterializationResponsibilityDelegation) {
|
|||
[&](MaterializationResponsibility R) {
|
||||
auto R2 = R.delegate({Bar});
|
||||
|
||||
R.notifyResolved({{Foo, FooSym}});
|
||||
R.notifyEmitted();
|
||||
R2.notifyResolved({{Bar, BarSym}});
|
||||
R2.notifyEmitted();
|
||||
cantFail(R.notifyResolved({{Foo, FooSym}}));
|
||||
cantFail(R.notifyEmitted());
|
||||
cantFail(R2.notifyResolved({{Bar, BarSym}}));
|
||||
cantFail(R2.notifyEmitted());
|
||||
});
|
||||
|
||||
cantFail(JD.define(MU));
|
||||
|
@ -924,8 +1125,9 @@ TEST_F(CoreAPIsStandardTest, TestMaterializeWeakSymbol) {
|
|||
<< "Expected a duplicate definition error";
|
||||
consumeError(std::move(Err));
|
||||
|
||||
FooResponsibility->notifyResolved(SymbolMap({{Foo, FooSym}}));
|
||||
FooResponsibility->notifyEmitted();
|
||||
// No dependencies registered, can't fail:
|
||||
cantFail(FooResponsibility->notifyResolved(SymbolMap({{Foo, FooSym}})));
|
||||
cantFail(FooResponsibility->notifyEmitted());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -41,12 +41,13 @@ TEST_F(LazyReexportsTest, BasicLocalCallThroughManagerOperation) {
|
|||
SymbolFlagsMap({{DummyTarget, JITSymbolFlags::Exported}}),
|
||||
[&](MaterializationResponsibility R) {
|
||||
DummyTargetMaterialized = true;
|
||||
R.notifyResolved(
|
||||
// No dependencies registered, can't fail.
|
||||
cantFail(R.notifyResolved(
|
||||
{{DummyTarget,
|
||||
JITEvaluatedSymbol(static_cast<JITTargetAddress>(
|
||||
reinterpret_cast<uintptr_t>(&dummyTarget)),
|
||||
JITSymbolFlags::Exported)}});
|
||||
R.notifyEmitted();
|
||||
JITSymbolFlags::Exported)}}));
|
||||
cantFail(R.notifyEmitted());
|
||||
})));
|
||||
|
||||
unsigned NotifyResolvedCount = 0;
|
||||
|
|
Loading…
Reference in New Issue