//===----- Core.cpp - Core ORC APIs (MaterializationUnit, VSO, etc.) ------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "llvm/ExecutionEngine/Orc/Core.h" #include "llvm/Config/llvm-config.h" #include "llvm/ExecutionEngine/Orc/OrcError.h" #include "llvm/Support/Format.h" #if LLVM_ENABLE_THREADS #include #endif namespace llvm { namespace orc { char FailedToMaterialize::ID = 0; char FailedToResolve::ID = 0; char FailedToFinalize::ID = 0; void MaterializationUnit::anchor() {} void SymbolResolver::anchor() {} raw_ostream &operator<<(raw_ostream &OS, const JITSymbolFlags &Flags) { if (Flags.isWeak()) OS << 'W'; else if (Flags.isCommon()) OS << 'C'; else OS << 'S'; if (Flags.isExported()) OS << 'E'; else OS << 'H'; return OS; } raw_ostream &operator<<(raw_ostream &OS, const JITEvaluatedSymbol &Sym) { OS << format("0x%016x", Sym.getAddress()) << " " << Sym.getFlags(); return OS; } raw_ostream &operator<<(raw_ostream &OS, const SymbolMap::value_type &KV) { OS << "\"" << *KV.first << "\": " << KV.second; return OS; } raw_ostream &operator<<(raw_ostream &OS, const SymbolNameSet &Symbols) { OS << "{"; if (!Symbols.empty()) { OS << " \"" << **Symbols.begin() << "\""; for (auto &Sym : make_range(std::next(Symbols.begin()), Symbols.end())) OS << ", \"" << *Sym << "\""; } OS << " }"; return OS; } raw_ostream &operator<<(raw_ostream &OS, const SymbolMap &Symbols) { OS << "{"; if (!Symbols.empty()) { OS << " {" << *Symbols.begin() << "}"; for (auto &Sym : make_range(std::next(Symbols.begin()), Symbols.end())) OS << ", {" << Sym << "}"; } OS << " }"; return OS; } raw_ostream &operator<<(raw_ostream &OS, const SymbolFlagsMap &SymbolFlags) { OS << "{"; if (!SymbolFlags.empty()) { OS << " {\"" << *SymbolFlags.begin()->first << "\": " << SymbolFlags.begin()->second << "}"; for (auto &KV : make_range(std::next(SymbolFlags.begin()), SymbolFlags.end())) OS << ", {\"" << *KV.first << "\": " << KV.second << "}"; } OS << " }"; return OS; } raw_ostream &operator<<(raw_ostream &OS, const SymbolDependenceMap &Deps) { OS << "{"; if (!Deps.empty()) { OS << " { " << Deps.begin()->first->getName() << ": " << Deps.begin()->second << " }"; for (auto &KV : make_range(std::next(Deps.begin()), Deps.end())) OS << ", { " << KV.first->getName() << ": " << KV.second << " }"; } OS << " }"; return OS; } FailedToResolve::FailedToResolve(SymbolNameSet Symbols) : Symbols(std::move(Symbols)) { assert(!this->Symbols.empty() && "Can not fail to resolve an empty set"); } std::error_code FailedToResolve::convertToErrorCode() const { return orcError(OrcErrorCode::UnknownORCError); } void FailedToResolve::log(raw_ostream &OS) const { OS << "Failed to resolve symbols: " << Symbols; } FailedToFinalize::FailedToFinalize(SymbolNameSet Symbols) : Symbols(std::move(Symbols)) { assert(!this->Symbols.empty() && "Can not fail to finalize an empty set"); } std::error_code FailedToFinalize::convertToErrorCode() const { return orcError(OrcErrorCode::UnknownORCError); } void FailedToFinalize::log(raw_ostream &OS) const { OS << "Failed to finalize symbols: " << Symbols; } void ExecutionSessionBase::failQuery(AsynchronousSymbolQuery &Q, Error Err) { runSessionLocked([&]() -> void { Q.detach(); Q.handleFailed(std::move(Err)); }); } AsynchronousSymbolQuery::AsynchronousSymbolQuery( const SymbolNameSet &Symbols, SymbolsResolvedCallback NotifySymbolsResolved, SymbolsReadyCallback NotifySymbolsReady) : NotifySymbolsResolved(std::move(NotifySymbolsResolved)), NotifySymbolsReady(std::move(NotifySymbolsReady)) { NotYetResolvedCount = NotYetReadyCount = Symbols.size(); for (auto &S : Symbols) ResolvedSymbols[S] = nullptr; } void AsynchronousSymbolQuery::resolve(const SymbolStringPtr &Name, JITEvaluatedSymbol Sym) { auto I = ResolvedSymbols.find(Name); assert(I != ResolvedSymbols.end() && "Resolving symbol outside the requested set"); assert(I->second.getAddress() == 0 && "Redundantly resolving symbol Name"); I->second = std::move(Sym); --NotYetResolvedCount; } void AsynchronousSymbolQuery::handleFullyResolved() { assert(NotYetResolvedCount == 0 && "Not fully resolved?"); assert(NotifySymbolsResolved && "NotifySymbolsResolved already called or error occurred"); NotifySymbolsResolved( ResolutionResult(std::move(ResolvedSymbols), QueryRegistrations)); NotifySymbolsResolved = SymbolsResolvedCallback(); } void AsynchronousSymbolQuery::notifySymbolReady() { assert(NotYetReadyCount != 0 && "All symbols already finalized"); --NotYetReadyCount; } void AsynchronousSymbolQuery::handleFullyReady() { assert(QueryRegistrations.empty() && "Query is still registered with some symbols"); assert(!NotifySymbolsResolved && "Resolution not applied yet"); NotifySymbolsReady(Error::success()); NotifySymbolsReady = SymbolsReadyCallback(); } void AsynchronousSymbolQuery::handleFailed(Error Err) { assert(QueryRegistrations.empty() && ResolvedSymbols.empty() && NotYetResolvedCount == 0 && NotYetReadyCount == 0 && "Query should already have been abandoned"); if (NotifySymbolsResolved) NotifySymbolsResolved(std::move(Err)); else { assert(NotifySymbolsReady && "Failed after both callbacks issued?"); NotifySymbolsReady(std::move(Err)); NotifySymbolsReady = SymbolsReadyCallback(); } } void AsynchronousSymbolQuery::addQueryDependence(VSO &V, SymbolStringPtr Name) { bool Added = QueryRegistrations[&V].insert(std::move(Name)).second; (void)Added; assert(Added && "Duplicate dependence notification?"); } void AsynchronousSymbolQuery::removeQueryDependence( VSO &V, const SymbolStringPtr &Name) { auto QRI = QueryRegistrations.find(&V); assert(QRI != QueryRegistrations.end() && "No dependencies registered for V"); assert(QRI->second.count(Name) && "No dependency on Name in V"); QRI->second.erase(Name); if (QRI->second.empty()) QueryRegistrations.erase(QRI); } void AsynchronousSymbolQuery::detach() { ResolvedSymbols.clear(); NotYetResolvedCount = 0; NotYetReadyCount = 0; for (auto &KV : QueryRegistrations) KV.first->detachQueryHelper(*this, KV.second); QueryRegistrations.clear(); } MaterializationResponsibility::MaterializationResponsibility( VSO &V, SymbolFlagsMap SymbolFlags) : V(V), SymbolFlags(std::move(SymbolFlags)) { assert(!this->SymbolFlags.empty() && "Materializing nothing?"); for (auto &KV : this->SymbolFlags) KV.second |= JITSymbolFlags::Materializing; } MaterializationResponsibility::~MaterializationResponsibility() { assert(SymbolFlags.empty() && "All symbols should have been explicitly materialized or failed"); } void MaterializationResponsibility::resolve(const SymbolMap &Symbols) { for (auto &KV : Symbols) { auto I = SymbolFlags.find(KV.first); assert(I != SymbolFlags.end() && "Resolving symbol outside this responsibility set"); assert(I->second.isMaterializing() && "Duplicate resolution"); I->second &= ~JITSymbolFlags::Materializing; assert(KV.second.getFlags() == I->second && "Resolving symbol with incorrect flags"); } V.resolve(Symbols); } void MaterializationResponsibility::finalize() { #ifndef NDEBUG for (auto &KV : SymbolFlags) assert(!KV.second.isMaterializing() && "Failed to resolve symbol before finalization"); #endif // NDEBUG V.finalize(SymbolFlags); SymbolFlags.clear(); } Error MaterializationResponsibility::defineMaterializing( const SymbolFlagsMap &NewSymbolFlags) { // Add the given symbols to this responsibility object. // It's ok if we hit a duplicate here: In that case the new version will be // discarded, and the VSO::defineMaterializing method will return a duplicate // symbol error. for (auto &KV : NewSymbolFlags) { auto I = SymbolFlags.insert(KV).first; I->second |= JITSymbolFlags::Materializing; } return V.defineMaterializing(NewSymbolFlags); } void MaterializationResponsibility::failMaterialization( std::function GenerateError) { V.notifyFailed(SymbolFlags, std::move(GenerateError)); SymbolFlags.clear(); } void MaterializationResponsibility::delegate( std::unique_ptr MU) { for (auto &KV : MU->getSymbols()) SymbolFlags.erase(KV.first); V.replace(std::move(MU)); } void MaterializationResponsibility::addDependencies( const SymbolDependenceMap &Dependencies) { V.addDependencies(SymbolFlags, Dependencies); } AbsoluteSymbolsMaterializationUnit::AbsoluteSymbolsMaterializationUnit( SymbolMap Symbols) : MaterializationUnit(extractFlags(Symbols)), Symbols(std::move(Symbols)) {} void AbsoluteSymbolsMaterializationUnit::materialize( MaterializationResponsibility R) { R.resolve(Symbols); R.finalize(); } void AbsoluteSymbolsMaterializationUnit::discard(const VSO &V, SymbolStringPtr Name) { assert(Symbols.count(Name) && "Symbol is not part of this MU"); Symbols.erase(Name); } SymbolFlagsMap AbsoluteSymbolsMaterializationUnit::extractFlags(const SymbolMap &Symbols) { SymbolFlagsMap Flags; for (const auto &KV : Symbols) Flags[KV.first] = KV.second.getFlags(); return Flags; } Error VSO::defineMaterializing(const SymbolFlagsMap &SymbolFlags) { return ES.runSessionLocked([&]() -> Error { std::vector AddedSyms; for (auto &KV : SymbolFlags) { SymbolMap::iterator EntryItr; bool Added; auto NewFlags = KV.second; NewFlags |= JITSymbolFlags::Materializing; std::tie(EntryItr, Added) = Symbols.insert( std::make_pair(KV.first, JITEvaluatedSymbol(0, NewFlags))); if (Added) AddedSyms.push_back(EntryItr); else { // Remove any symbols already added. for (auto &SI : AddedSyms) Symbols.erase(SI); // FIXME: Return all duplicates. return make_error(*KV.first); } } return Error::success(); }); } void VSO::replace(std::unique_ptr MU) { assert(MU != nullptr && "Can not replace with a null MaterializationUnit"); auto MustRunMU = ES.runSessionLocked([&, this]() -> std::unique_ptr { #ifndef NDEBUG for (auto &KV : MU->getSymbols()) { auto SymI = Symbols.find(KV.first); assert(SymI != Symbols.end() && "Replacing unknown symbol"); assert(!SymI->second.getFlags().isLazy() && SymI->second.getFlags().isMaterializing() && "Can not replace symbol that is not materializing"); assert(UnmaterializedInfos.count(KV.first) == 0 && "Symbol being replaced should have no UnmaterializedInfo"); assert(MaterializingInfos.count(KV.first) && "Symbol being replaced should have a MaterializingInfo"); } #endif // NDEBUG // If any symbol has pending queries against it then we need to // materialize MU immediately. for (auto &KV : MU->getSymbols()) if (!MaterializingInfos[KV.first].PendingQueries.empty()) return std::move(MU); // Otherwise, make MU responsible for all the symbols. auto UMI = std::make_shared(std::move(MU)); for (auto &KV : UMI->MU->getSymbols()) { assert(!KV.second.isLazy() && "Lazy flag should be managed internally."); assert(!KV.second.isMaterializing() && "Materializing flags should be managed internally."); auto SymI = Symbols.find(KV.first); SymI->second.getFlags() = KV.second; SymI->second.getFlags() |= JITSymbolFlags::Lazy; UnmaterializedInfos[KV.first] = UMI; } return nullptr; }); if (MustRunMU) ES.dispatchMaterialization(*this, std::move(MustRunMU)); } void VSO::addDependencies(const SymbolFlagsMap &Dependants, const SymbolDependenceMap &Dependencies) { ES.runSessionLocked([&, this]() { for (auto &KV : Dependants) { const auto &Name = KV.first; assert(Symbols.count(Name) && "Name not in symbol table"); assert((Symbols[Name].getFlags().isLazy() || Symbols[Name].getFlags().isMaterializing()) && "Symbol is not lazy or materializing"); auto &MI = MaterializingInfos[Name]; assert(!MI.IsFinalized && "Can not add dependencies to finalized symbol"); for (auto &KV : Dependencies) { assert(KV.first && "Null VSO in dependency?"); auto &OtherVSO = *KV.first; auto &DepsOnOtherVSO = MI.UnfinalizedDependencies[&OtherVSO]; for (auto &OtherSymbol : KV.second) { auto &OtherMI = OtherVSO.MaterializingInfos[OtherSymbol]; if (OtherMI.IsFinalized) transferFinalizedNodeDependencies(MI, Name, OtherMI); else { OtherMI.Dependants[this].insert(Name); DepsOnOtherVSO.insert(OtherSymbol); } } } } }); } void VSO::resolve(const SymbolMap &Resolved) { auto FullyResolvedQueries = ES.runSessionLocked([&, this]() { AsynchronousSymbolQuerySet FullyResolvedQueries; for (const auto &KV : Resolved) { auto &Name = KV.first; auto Sym = KV.second; assert(!Sym.getFlags().isLazy() && !Sym.getFlags().isMaterializing() && "Materializing flags should be managed internally"); auto I = Symbols.find(Name); assert(I != Symbols.end() && "Symbol not found"); assert(!I->second.getFlags().isLazy() && I->second.getFlags().isMaterializing() && "Symbol should be materializing"); assert(I->second.getAddress() == 0 && "Symbol has already been resolved"); assert(Sym.getFlags() == JITSymbolFlags::stripTransientFlags(I->second.getFlags()) && "Resolved flags should match the declared flags"); // Once resolved, symbols can never be weak. Sym.getFlags() = static_cast( Sym.getFlags() & ~JITSymbolFlags::Weak); I->second = Sym; auto &MI = MaterializingInfos[Name]; for (auto &Q : MI.PendingQueries) { Q->resolve(Name, Sym); if (Q->isFullyResolved()) FullyResolvedQueries.insert(Q); } } return FullyResolvedQueries; }); for (auto &Q : FullyResolvedQueries) { assert(Q->isFullyResolved() && "Q not fully resolved"); Q->handleFullyResolved(); } } void VSO::finalize(const SymbolFlagsMap &Finalized) { auto FullyReadyQueries = ES.runSessionLocked([&, this]() { AsynchronousSymbolQuerySet ReadyQueries; for (const auto &KV : Finalized) { const auto &Name = KV.first; auto MII = MaterializingInfos.find(Name); assert(MII != MaterializingInfos.end() && "Missing MaterializingInfo entry"); auto &MI = MII->second; // For each dependant, transfer this node's unfinalized dependencies to // it. If the dependant node is fully finalized then notify any pending // queries. for (auto &KV : MI.Dependants) { auto &DependantVSO = *KV.first; for (auto &DependantName : KV.second) { auto DependantMII = DependantVSO.MaterializingInfos.find(DependantName); assert(DependantMII != DependantVSO.MaterializingInfos.end() && "Dependant should have MaterializingInfo"); auto &DependantMI = DependantMII->second; // Remove the dependant's dependency on this node. assert(DependantMI.UnfinalizedDependencies[this].count(Name) && "Dependant does not count this symbol as a dependency?"); DependantMI.UnfinalizedDependencies[this].erase(Name); if (DependantMI.UnfinalizedDependencies[this].empty()) DependantMI.UnfinalizedDependencies.erase(this); // Transfer unfinalized dependencies from this node to the dependant. DependantVSO.transferFinalizedNodeDependencies(DependantMI, DependantName, MI); // If the dependant is finalized and this node was the last of its // unfinalized dependencies then notify any pending queries on the // dependant node. if (DependantMI.IsFinalized && DependantMI.UnfinalizedDependencies.empty()) { assert(DependantMI.Dependants.empty() && "Dependants should be empty by now"); for (auto &Q : DependantMI.PendingQueries) { Q->notifySymbolReady(); if (Q->isFullyReady()) ReadyQueries.insert(Q); Q->removeQueryDependence(DependantVSO, DependantName); } // If this dependant node was fully finalized we can erase its // MaterializingInfo and update its materializing state. assert(DependantVSO.Symbols.count(DependantName) && "Dependant has no entry in the Symbols table"); DependantVSO.Symbols[DependantName].getFlags() &= JITSymbolFlags::Materializing; DependantVSO.MaterializingInfos.erase(DependantMII); } } } MI.Dependants.clear(); MI.IsFinalized = true; if (MI.UnfinalizedDependencies.empty()) { for (auto &Q : MI.PendingQueries) { Q->notifySymbolReady(); if (Q->isFullyReady()) ReadyQueries.insert(Q); Q->removeQueryDependence(*this, Name); } assert(Symbols.count(Name) && "Symbol has no entry in the Symbols table"); Symbols[Name].getFlags() &= ~JITSymbolFlags::Materializing; MaterializingInfos.erase(MII); } } return ReadyQueries; }); for (auto &Q : FullyReadyQueries) { assert(Q->isFullyReady() && "Q is not fully ready"); Q->handleFullyReady(); } } void VSO::notifyFailed(const SymbolFlagsMap &Failed, std::function GenerateError) { auto FailedQueriesToNotify = ES.runSessionLocked([&, this]() { AsynchronousSymbolQuerySet FailedQueries; for (auto &KV : Failed) { const auto &Name = KV.first; auto I = Symbols.find(Name); assert(I != Symbols.end() && "Symbol not present in this VSO"); Symbols.erase(I); 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; // 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. for (auto &Q : MII->second.PendingQueries) FailedQueries.insert(Q); for (auto &Q : FailedQueries) Q->detach(); assert(MII->second.PendingQueries.empty() && "Queries remain after symbol was failed"); MaterializingInfos.erase(MII); } return FailedQueries; }); for (auto &Q : FailedQueriesToNotify) Q->handleFailed(GenerateError()); } SymbolNameSet VSO::lookupFlags(SymbolFlagsMap &Flags, const SymbolNameSet &Names) { return ES.runSessionLocked([&, this]() { SymbolNameSet Unresolved; for (auto &Name : Names) { auto I = Symbols.find(Name); if (I == Symbols.end()) { Unresolved.insert(Name); continue; } assert(!Flags.count(Name) && "Symbol already present in Flags map"); Flags[Name] = JITSymbolFlags::stripTransientFlags(I->second.getFlags()); } return Unresolved; }); } SymbolNameSet VSO::lookup(std::shared_ptr Q, SymbolNameSet Names) { SymbolNameSet Unresolved = std::move(Names); std::vector> MUs; ES.runSessionLocked([&, this]() { for (auto I = Unresolved.begin(), E = Unresolved.end(); I != E;) { auto TmpI = I++; auto Name = *TmpI; // Search for the name in Symbols. Skip it if not found. auto SymI = Symbols.find(Name); if (SymI == Symbols.end()) continue; // If we found Name in V, remove it frome the Unresolved set and add it // to the dependencies set. Unresolved.erase(TmpI); // If the symbol has an address then resolve it. if (SymI->second.getAddress() != 0) Q->resolve(Name, SymI->second); // If the symbol is lazy, get the MaterialiaztionUnit for it. if (SymI->second.getFlags().isLazy()) { assert(SymI->second.getAddress() == 0 && "Lazy symbol should not have a resolved address"); assert(!SymI->second.getFlags().isMaterializing() && "Materializing and lazy should not both be set"); auto UMII = UnmaterializedInfos.find(Name); assert(UMII != UnmaterializedInfos.end() && "Lazy symbol should have UnmaterializedInfo"); auto MU = std::move(UMII->second->MU); assert(MU != nullptr && "Materializer should not be null"); // Kick all symbols associated with this MaterializationUnit into // materializing state. for (auto &KV : MU->getSymbols()) { auto SymK = Symbols.find(KV.first); auto Flags = SymK->second.getFlags(); Flags &= ~JITSymbolFlags::Lazy; Flags |= JITSymbolFlags::Materializing; SymK->second.setFlags(Flags); UnmaterializedInfos.erase(KV.first); } // Add MU to the list of MaterializationUnits to be materialized. MUs.push_back(std::move(MU)); } else if (!SymI->second.getFlags().isMaterializing()) { // The symbol is neither lazy nor materializing. Finalize it and // continue. Q->notifySymbolReady(); continue; } // Add the query to the PendingQueries list. assert(SymI->second.getFlags().isMaterializing() && "By this line the symbol should be materializing"); auto &MI = MaterializingInfos[Name]; MI.PendingQueries.push_back(Q); Q->addQueryDependence(*this, Name); } }); if (Q->isFullyResolved()) Q->handleFullyResolved(); if (Q->isFullyReady()) Q->handleFullyReady(); // Dispatch any required MaterializationUnits for materialization. for (auto &MU : MUs) ES.dispatchMaterialization(*this, std::move(MU)); return Unresolved; } void VSO::dump(raw_ostream &OS) { ES.runSessionLocked([&, this]() { OS << "VSO \"" << VSOName << "\" (ES: " << format("0x%016x", reinterpret_cast(&ES)) << "):\n" << "Symbol table:\n"; for (auto &KV : Symbols) { OS << " \"" << *KV.first << "\": " << KV.second.getAddress(); if (KV.second.getFlags().isLazy() || KV.second.getFlags().isMaterializing()) { OS << " ("; if (KV.second.getFlags().isLazy()) { auto I = UnmaterializedInfos.find(KV.first); assert(I != UnmaterializedInfos.end() && "Lazy symbol should have UnmaterializedInfo"); OS << " Lazy (MU=" << I->second->MU.get() << ")"; } if (KV.second.getFlags().isMaterializing()) OS << " Materializing"; OS << " )\n"; } else OS << "\n"; } if (!MaterializingInfos.empty()) OS << " MaterializingInfos entries:\n"; for (auto &KV : MaterializingInfos) { OS << " \"" << *KV.first << "\":\n" << " IsFinalized = " << (KV.second.IsFinalized ? "true" : "false") << "\n" << " " << KV.second.PendingQueries.size() << " pending queries.\n" << " Dependants:\n"; for (auto &KV2 : KV.second.Dependants) OS << " " << KV2.first->getName() << ": " << KV2.second << "\n"; OS << " Unfinalized Dependencies:\n"; for (auto &KV2 : KV.second.UnfinalizedDependencies) OS << " " << KV2.first->getName() << ": " << KV2.second << "\n"; } }); } Error VSO::defineImpl(MaterializationUnit &MU) { SymbolNameSet Duplicates; SymbolNameSet MUDefsOverridden; std::vector ExistingDefsOverridden; for (auto &KV : MU.getSymbols()) { assert(!KV.second.isLazy() && "Lazy flag should be managed internally."); assert(!KV.second.isMaterializing() && "Materializing flags should be managed internally."); SymbolMap::iterator EntryItr; bool Added; auto NewFlags = KV.second; NewFlags |= JITSymbolFlags::Lazy; std::tie(EntryItr, Added) = Symbols.insert( std::make_pair(KV.first, JITEvaluatedSymbol(0, NewFlags))); if (!Added) { if (KV.second.isStrong()) { if (EntryItr->second.getFlags().isStrong()) Duplicates.insert(KV.first); else ExistingDefsOverridden.push_back(EntryItr); } else MUDefsOverridden.insert(KV.first); } } if (!Duplicates.empty()) { // We need to remove the symbols we added. for (auto &KV : MU.getSymbols()) { if (Duplicates.count(KV.first) || Duplicates.count(KV.first)) continue; bool Found = false; for (const auto &I : ExistingDefsOverridden) if (I->first == KV.first) Found = true; if (!Found) Symbols.erase(KV.first); } // FIXME: Return all duplicates. return make_error(**Duplicates.begin()); } // Update flags on existing defs and call discard on their materializers. for (auto &ExistingDefItr : ExistingDefsOverridden) { assert(ExistingDefItr->second.getFlags().isLazy() && !ExistingDefItr->second.getFlags().isMaterializing() && "Overridden existing def should be in the Lazy state"); ExistingDefItr->second.getFlags() &= ~JITSymbolFlags::Weak; auto UMII = UnmaterializedInfos.find(ExistingDefItr->first); assert(UMII != UnmaterializedInfos.end() && "Overridden existing def should have an UnmaterializedInfo"); UMII->second->MU->doDiscard(*this, ExistingDefItr->first); } // Discard overridden symbols povided by MU. for (auto &Sym : MUDefsOverridden) MU.doDiscard(*this, Sym); return Error::success(); } void VSO::detachQueryHelper(AsynchronousSymbolQuery &Q, const SymbolNameSet &QuerySymbols) { for (auto &QuerySymbol : QuerySymbols) { assert(MaterializingInfos.count(QuerySymbol) && "QuerySymbol does not have MaterializingInfo"); auto &MI = MaterializingInfos[QuerySymbol]; auto IdenticalQuery = [&](const std::shared_ptr &R) { return R.get() == &Q; }; auto I = std::find_if(MI.PendingQueries.begin(), MI.PendingQueries.end(), IdenticalQuery); assert(I != MI.PendingQueries.end() && "Query Q should be in the PendingQueries list for QuerySymbol"); MI.PendingQueries.erase(I); } } void VSO::transferFinalizedNodeDependencies( MaterializingInfo &DependantMI, const SymbolStringPtr &DependantName, MaterializingInfo &FinalizedMI) { for (auto &KV : FinalizedMI.UnfinalizedDependencies) { auto &DependencyVSO = *KV.first; SymbolNameSet *UnfinalizedDependenciesOnDependencyVSO = nullptr; for (auto &DependencyName : KV.second) { auto &DependencyMI = DependencyVSO.MaterializingInfos[DependencyName]; // Do not add self dependencies. if (&DependencyMI == &DependantMI) continue; // If we haven't looked up the dependencies for DependencyVSO yet, do it // now and cache the result. if (!UnfinalizedDependenciesOnDependencyVSO) UnfinalizedDependenciesOnDependencyVSO = &DependantMI.UnfinalizedDependencies[&DependencyVSO]; DependencyMI.Dependants[this].insert(DependantName); UnfinalizedDependenciesOnDependencyVSO->insert(DependencyName); } } } VSO &ExecutionSession::createVSO(std::string Name) { return runSessionLocked([&, this]() -> VSO & { VSOs.push_back(std::unique_ptr(new VSO(*this, std::move(Name)))); return *VSOs.back(); }); } Expected lookup(const std::vector &VSOs, SymbolNameSet Names, MaterializationResponsibility *R) { #if LLVM_ENABLE_THREADS // In the threaded case we use promises to return the results. std::promise PromisedResult; std::mutex ErrMutex; Error ResolutionError = Error::success(); std::promise PromisedReady; Error ReadyError = Error::success(); auto OnResolve = [&](Expected Result) { if (Result) { if (R) R->addDependencies(Result->Dependencies); PromisedResult.set_value(std::move(Result->Symbols)); } else { { ErrorAsOutParameter _(&ResolutionError); std::lock_guard Lock(ErrMutex); ResolutionError = Result.takeError(); } PromisedResult.set_value(SymbolMap()); } }; auto OnReady = [&](Error Err) { if (Err) { ErrorAsOutParameter _(&ReadyError); std::lock_guard Lock(ErrMutex); ReadyError = std::move(Err); } PromisedReady.set_value(); }; #else SymbolMap Result; Error ResolutionError = Error::success(); Error ReadyError = Error::success(); auto OnResolve = [&](Expected RR) { ErrorAsOutParameter _(&ResolutionError); if (RR) { if (R) R->addDependencies(RR->Dependencies); Result = std::move(RR->Symbols); } else ResolutionError = RR.takeError(); }; auto OnReady = [&](Error Err) { ErrorAsOutParameter _(&ReadyError); if (Err) ReadyError = std::move(Err); }; #endif auto Query = std::make_shared( Names, std::move(OnResolve), std::move(OnReady)); SymbolNameSet UnresolvedSymbols(std::move(Names)); for (auto *V : VSOs) { assert(V && "VSO pointers in VSOs list should be non-null"); if (UnresolvedSymbols.empty()) break; UnresolvedSymbols = V->lookup(Query, UnresolvedSymbols); } // FIXME: Error out if there are remaining unresolved symbols. #if LLVM_ENABLE_THREADS auto ResultFuture = PromisedResult.get_future(); auto Result = ResultFuture.get(); { std::lock_guard Lock(ErrMutex); if (ResolutionError) { // ReadyError will never be assigned. Consume the success value. cantFail(std::move(ReadyError)); return std::move(ResolutionError); } } auto ReadyFuture = PromisedReady.get_future(); ReadyFuture.get(); { std::lock_guard Lock(ErrMutex); if (ReadyError) return std::move(ReadyError); } return std::move(Result); #else if (ResolutionError) { // ReadyError will never be assigned. Consume the success value. cantFail(std::move(ReadyError)); return std::move(ResolutionError); } if (ReadyError) return std::move(ReadyError); return Result; #endif } /// Look up a symbol by searching a list of VSOs. Expected lookup(const std::vector VSOs, SymbolStringPtr Name, MaterializationResponsibility *R) { SymbolNameSet Names({Name}); if (auto ResultMap = lookup(VSOs, std::move(Names), R)) { assert(ResultMap->size() == 1 && "Unexpected number of results"); assert(ResultMap->count(Name) && "Missing result for symbol"); return std::move(ResultMap->begin()->second); } else return ResultMap.takeError(); } } // End namespace orc. } // End namespace llvm.