diff options
author | Lang Hames <lhames@gmail.com> | 2019-08-23 20:37:31 +0000 |
---|---|---|
committer | Lang Hames <lhames@gmail.com> | 2019-08-23 20:37:31 +0000 |
commit | e00585c77ca63928794e730ac38194c659819db4 (patch) | |
tree | 79d9c53bbd87cc8138f598a24f369ecb0f158dbf | |
parent | a47d622240b199d6ab8736d83e1e3de44556f4a1 (diff) | |
download | bcm5719-llvm-e00585c77ca63928794e730ac38194c659819db4.tar.gz bcm5719-llvm-e00585c77ca63928794e730ac38194c659819db4.zip |
[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
-rw-r--r-- | llvm/include/llvm/ExecutionEngine/Orc/Core.h | 47 | ||||
-rw-r--r-- | llvm/lib/ExecutionEngine/Orc/Core.cpp | 406 | ||||
-rw-r--r-- | llvm/lib/ExecutionEngine/Orc/IndirectionUtils.cpp | 5 | ||||
-rw-r--r-- | llvm/lib/ExecutionEngine/Orc/LazyReexports.cpp | 5 | ||||
-rw-r--r-- | llvm/lib/ExecutionEngine/Orc/ObjectLinkingLayer.cpp | 12 | ||||
-rw-r--r-- | llvm/lib/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.cpp | 11 | ||||
-rw-r--r-- | llvm/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp | 300 | ||||
-rw-r--r-- | llvm/unittests/ExecutionEngine/Orc/LazyCallThroughAndReexportsTest.cpp | 7 |
8 files changed, 629 insertions, 164 deletions
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Core.h b/llvm/include/llvm/ExecutionEngine/Orc/Core.h index 599aa35db5c..67f7b013194 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/Core.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/Core.h @@ -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; diff --git a/llvm/lib/ExecutionEngine/Orc/Core.cpp b/llvm/lib/ExecutionEngine/Orc/Core.cpp index 5d1e574ea96..ecfa116a646 100644 --- a/llvm/lib/ExecutionEngine/Orc/Core.cpp +++ b/llvm/lib/ExecutionEngine/Orc/Core.cpp @@ -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"); + + 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"); + + 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(); - assert((Sym.getFlags() & ~JITSymbolFlags::Weak) == - (I->second.getFlags() & ~JITSymbolFlags::Weak) && - "Resolved flags should match the declared flags"); + auto &Name = SymI->first; - // Once resolved, symbols can never be weak. - JITSymbolFlags ResolvedFlags = Sym.getFlags(); + // 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); + } + } + + // 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); } } - // 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. + // 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> diff --git a/llvm/lib/ExecutionEngine/Orc/IndirectionUtils.cpp b/llvm/lib/ExecutionEngine/Orc/IndirectionUtils.cpp index 382a6a2951c..d59a7a6e29f 100644 --- a/llvm/lib/ExecutionEngine/Orc/IndirectionUtils.cpp +++ b/llvm/lib/ExecutionEngine/Orc/IndirectionUtils.cpp @@ -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 { diff --git a/llvm/lib/ExecutionEngine/Orc/LazyReexports.cpp b/llvm/lib/ExecutionEngine/Orc/LazyReexports.cpp index 11ba2e55deb..863698eb725 100644 --- a/llvm/lib/ExecutionEngine/Orc/LazyReexports.cpp +++ b/llvm/lib/ExecutionEngine/Orc/LazyReexports.cpp @@ -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, diff --git a/llvm/lib/ExecutionEngine/Orc/ObjectLinkingLayer.cpp b/llvm/lib/ExecutionEngine/Orc/ObjectLinkingLayer.cpp index ff9b55dbbcb..0d0b8a9c227 100644 --- a/llvm/lib/ExecutionEngine/Orc/ObjectLinkingLayer.cpp +++ b/llvm/lib/ExecutionEngine/Orc/ObjectLinkingLayer.cpp @@ -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 { diff --git a/llvm/lib/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.cpp b/llvm/lib/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.cpp index b22ecd5f80a..7ea1351af8f 100644 --- a/llvm/lib/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.cpp +++ b/llvm/lib/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.cpp @@ -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)); diff --git a/llvm/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp b/llvm/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp index 0212f53468c..729dad9c29a 100644 --- a/llvm/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp +++ b/llvm/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp @@ -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 diff --git a/llvm/unittests/ExecutionEngine/Orc/LazyCallThroughAndReexportsTest.cpp b/llvm/unittests/ExecutionEngine/Orc/LazyCallThroughAndReexportsTest.cpp index d4757e0f86e..6a3c5dde737 100644 --- a/llvm/unittests/ExecutionEngine/Orc/LazyCallThroughAndReexportsTest.cpp +++ b/llvm/unittests/ExecutionEngine/Orc/LazyCallThroughAndReexportsTest.cpp @@ -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; |