class MaterializationUnit;
class MaterializationResponsibility;
class JITDylib;
+enum class SymbolState : uint8_t;
/// VModuleKey provides a unique identifier (allocated and managed by
/// ExecutionSessions) for a module added to the JIT.
/// A list of (JITDylib*, bool) pairs.
using JITDylibSearchList = std::vector<std::pair<JITDylib *, bool>>;
+struct SymbolAliasMapEntry {
+ SymbolAliasMapEntry() = default;
+ SymbolAliasMapEntry(SymbolStringPtr Aliasee, JITSymbolFlags AliasFlags)
+ : Aliasee(std::move(Aliasee)), AliasFlags(AliasFlags) {}
+
+ SymbolStringPtr Aliasee;
+ JITSymbolFlags AliasFlags;
+};
+
+/// A map of Symbols to (Symbol, Flags) pairs.
+using SymbolAliasMap = DenseMap<SymbolStringPtr, SymbolAliasMapEntry>;
+
/// Render a SymbolStringPtr.
raw_ostream &operator<<(raw_ostream &OS, const SymbolStringPtr &Sym);
/// Render a JITDylibSearchList.
raw_ostream &operator<<(raw_ostream &OS, const JITDylibSearchList &JDs);
+/// Render a SymbolAliasMap.
+raw_ostream &operator<<(raw_ostream &OS, const SymbolAliasMap &Aliases);
+
+/// Render a SymbolState.
+raw_ostream &operator<<(raw_ostream &OS, const SymbolState &S);
+
/// Callback to notify client that symbols have been resolved.
using SymbolsResolvedCallback = std::function<void(Expected<SymbolMap>)>;
-/// Callback to notify client that symbols are ready for execution.
-using SymbolsReadyCallback = std::function<void(Error)>;
-
/// Callback to register the dependencies for a given query.
using RegisterDependenciesFunction =
std::function<void(const SymbolDependenceMap &)>;
std::move(Symbols), std::move(K));
}
-struct SymbolAliasMapEntry {
- SymbolAliasMapEntry() = default;
- SymbolAliasMapEntry(SymbolStringPtr Aliasee, JITSymbolFlags AliasFlags)
- : Aliasee(std::move(Aliasee)), AliasFlags(AliasFlags) {}
-
- SymbolStringPtr Aliasee;
- JITSymbolFlags AliasFlags;
-};
-
-/// A map of Symbols to (Symbol, Flags) pairs.
-using SymbolAliasMap = DenseMap<SymbolStringPtr, SymbolAliasMapEntry>;
-
/// A materialization unit for symbol aliases. Allows existing symbols to be
/// aliased with alternate flags.
class ReExportsMaterializationUnit : public MaterializationUnit {
SymbolPredicate Allow;
};
+/// Represents the state that a symbol has reached during materialization.
+enum class SymbolState : uint8_t {
+ Invalid, /// No symbol should be in this state.
+ NeverSearched, /// Added to the symbol table, never queried.
+ Materializing, /// Queried, materialization begun.
+ Resolved, /// Assigned address, still materializing.
+ Ready = 0x3f /// Ready and safe for clients to access.
+};
+
/// A symbol query that returns results via a callback when results are
/// ready.
///
friend class JITSymbolResolverAdapter;
public:
-
- /// Create a query for the given symbols, notify-resolved and
- /// notify-ready callbacks.
+ /// Create a query for the given symbols. The NotifyComplete
+ /// callback will be called once all queried symbols reach the given
+ /// minimum state.
AsynchronousSymbolQuery(const SymbolNameSet &Symbols,
- SymbolsResolvedCallback NotifySymbolsResolved,
- SymbolsReadyCallback NotifySymbolsReady);
+ SymbolState RequiredState,
+ SymbolsResolvedCallback NotifyComplete);
- /// Set the resolved symbol information for the given symbol name.
- void resolve(const SymbolStringPtr &Name, JITEvaluatedSymbol Sym);
+ /// Notify the query that a requested symbol has reached the required state.
+ void notifySymbolMetRequiredState(const SymbolStringPtr &Name,
+ JITEvaluatedSymbol Sym);
/// Returns true if all symbols covered by this query have been
/// resolved.
- bool isFullyResolved() const { return NotYetResolvedCount == 0; }
-
- /// Call the NotifySymbolsResolved callback.
- ///
- /// This should only be called if all symbols covered by the query have been
- /// resolved.
- void handleFullyResolved();
-
- /// Notify the query that a requested symbol is ready for execution.
- void notifySymbolReady();
+ bool isComplete() const { return OutstandingSymbolsCount == 0; }
- /// Returns true if all symbols covered by this query are ready.
- bool isFullyReady() const { return NotYetReadyCount == 0; }
-
- /// Calls the NotifySymbolsReady callback.
+ /// Call the NotifyComplete callback.
///
- /// This should only be called if all symbols covered by this query are ready.
- void handleFullyReady();
+ /// This should only be called if all symbols covered by the query have
+ /// reached the specified state.
+ void handleComplete();
private:
+ SymbolState getRequiredState() { return RequiredState; }
+
void addQueryDependence(JITDylib &JD, SymbolStringPtr Name);
void removeQueryDependence(JITDylib &JD, const SymbolStringPtr &Name);
void detach();
- SymbolsResolvedCallback NotifySymbolsResolved;
- SymbolsReadyCallback NotifySymbolsReady;
+ SymbolsResolvedCallback NotifyComplete;
SymbolDependenceMap QueryRegistrations;
SymbolMap ResolvedSymbols;
- size_t NotYetResolvedCount;
- size_t NotYetReadyCount;
+ size_t OutstandingSymbolsCount;
+ SymbolState RequiredState;
};
/// A symbol table that supports asynchoronous symbol queries.
DenseMap<SymbolStringPtr, std::shared_ptr<UnmaterializedInfo>>;
struct MaterializingInfo {
- AsynchronousSymbolQueryList PendingQueries;
SymbolDependenceMap Dependants;
SymbolDependenceMap UnemittedDependencies;
bool IsEmitted = false;
- };
- using MaterializingInfosMap = DenseMap<SymbolStringPtr, MaterializingInfo>;
+ void addQuery(std::shared_ptr<AsynchronousSymbolQuery> Q);
+ void removeQuery(const AsynchronousSymbolQuery &Q);
+ AsynchronousSymbolQueryList takeQueriesMeeting(SymbolState RequiredState);
+ AsynchronousSymbolQueryList takeAllQueries();
+ bool hasQueriesPending() const { return !PendingQueries.empty(); }
+ const AsynchronousSymbolQueryList &pendingQueries() const {
+ return PendingQueries;
+ }
- using LookupImplActionFlags = enum {
- None = 0,
- NotifyFullyResolved = 1 << 0U,
- NotifyFullyReady = 1 << 1U,
- LLVM_MARK_AS_BITMASK_ENUM(NotifyFullyReady)
+ private:
+ AsynchronousSymbolQueryList PendingQueries;
};
- enum class SymbolState : uint8_t {
- Invalid, // No symbol should be in this state.
- NeverSearched, // Added to the symbol table, never queried.
- Materializing, // Queried, materialization begun.
- Resolved, // Assigned address, still materializing.
- Ready = 0x3f // Ready and safe for clients to access.
- };
+ using MaterializingInfosMap = DenseMap<SymbolStringPtr, MaterializingInfo>;
class SymbolTableEntry {
public:
SymbolNameSet &Unresolved, bool MatchNonExported,
MaterializationUnitList &MUs);
- LookupImplActionFlags
- lookupImpl(std::shared_ptr<AsynchronousSymbolQuery> &Q,
- std::vector<std::unique_ptr<MaterializationUnit>> &MUs,
- SymbolNameSet &Unresolved);
+ bool lookupImpl(std::shared_ptr<AsynchronousSymbolQuery> &Q,
+ std::vector<std::unique_ptr<MaterializationUnit>> &MUs,
+ SymbolNameSet &Unresolved);
void detachQueryHelper(AsynchronousSymbolQuery &Q,
const SymbolNameSet &QuerySymbols);
/// Do not use -- this will be removed soon.
Expected<SymbolMap>
legacyLookup(LegacyAsyncLookupFunction AsyncLookup, SymbolNameSet Names,
- bool WaiUntilReady,
+ SymbolState RequiredState,
RegisterDependenciesFunction RegisterDependencies);
/// Search the given JITDylib list for the given symbols.
/// (hidden visibility) symbols in that dylib (true means match against
/// non-exported symbols, false means do not match).
///
- /// The OnResolve callback will be called once all requested symbols are
- /// resolved, or if an error occurs prior to resolution.
- ///
- /// The OnReady callback will be called once all requested symbols are ready,
- /// or if an error occurs after resolution but before all symbols are ready.
+ /// The NotifyComplete callback will be called once all requested symbols
+ /// reach the required state.
///
/// If all symbols are found, the RegisterDependencies function will be called
/// while the session lock is held. This gives clients a chance to register
/// client to get an address to call) then the value NoDependenciesToRegister
/// can be used.
void lookup(const JITDylibSearchList &SearchOrder, SymbolNameSet Symbols,
- SymbolsResolvedCallback OnResolve, SymbolsReadyCallback OnReady,
+ SymbolState RequiredState, SymbolsResolvedCallback NotifyComplete,
RegisterDependenciesFunction RegisterDependencies);
/// Blocking version of lookup above. Returns the resolved symbol map.
/// error will be reported via reportErrors.
Expected<SymbolMap> lookup(const JITDylibSearchList &SearchOrder,
const SymbolNameSet &Symbols,
+ SymbolState RequiredState = SymbolState::Ready,
RegisterDependenciesFunction RegisterDependencies =
- NoDependenciesToRegister,
- bool WaitUntilReady = true);
+ NoDependenciesToRegister);
/// Convenience version of blocking lookup.
/// Searches each of the JITDylibs in the search order in turn for the given
/// Materialize the given unit.
void dispatchMaterialization(JITDylib &JD,
std::unique_ptr<MaterializationUnit> MU) {
- LLVM_DEBUG(runSessionLocked([&]() {
- dbgs() << "Compiling, for " << JD.getName() << ", " << *MU
- << "\n";
- }););
+ LLVM_DEBUG({
+ runSessionLocked([&]() {
+ dbgs() << "Dispatching " << *MU << " for " << JD.getName() << "\n";
+ });
+ });
DispatchMaterialization(JD, std::move(MU));
}
for (auto &S : Symbols) {
if (JITSymbol Sym = FindSymbol(*S)) {
if (auto Addr = Sym.getAddress()) {
- Query.resolve(S, JITEvaluatedSymbol(*Addr, Sym.getFlags()));
- Query.notifySymbolReady();
+ Query.notifySymbolMetRequiredState(
+ S, JITEvaluatedSymbol(*Addr, Sym.getFlags()));
NewSymbolsResolved = true;
} else {
ES.legacyFailQuery(Query, Addr.takeError());
SymbolsNotFound.insert(S);
}
- if (NewSymbolsResolved && Query.isFullyResolved())
- Query.handleFullyResolved();
-
- if (NewSymbolsResolved && Query.isFullyReady())
- Query.handleFullyReady();
+ if (NewSymbolsResolved && Query.isComplete())
+ Query.handleComplete();
return SymbolsNotFound;
}
return OS;
}
+raw_ostream &operator<<(raw_ostream &OS, const SymbolAliasMap &Aliases) {
+ OS << "{";
+ for (auto &KV : Aliases)
+ OS << " " << *KV.first << ": " << KV.second.Aliasee << " "
+ << KV.second.AliasFlags;
+ OS << " }\n";
+ return OS;
+}
+
+raw_ostream &operator<<(raw_ostream &OS, const SymbolState &S) {
+ switch (S) {
+ case SymbolState::Invalid:
+ return OS << "Invalid";
+ case SymbolState::NeverSearched:
+ return OS << "Never-Searched";
+ case SymbolState::Materializing:
+ return OS << "Materializing";
+ case SymbolState::Resolved:
+ return OS << "Resolved";
+ case SymbolState::Ready:
+ return OS << "Ready";
+ }
+ llvm_unreachable("Invalid state");
+}
+
FailedToMaterialize::FailedToMaterialize(SymbolNameSet Symbols)
: Symbols(std::move(Symbols)) {
assert(!this->Symbols.empty() && "Can not fail to resolve an empty set");
}
AsynchronousSymbolQuery::AsynchronousSymbolQuery(
- const SymbolNameSet &Symbols, SymbolsResolvedCallback NotifySymbolsResolved,
- SymbolsReadyCallback NotifySymbolsReady)
- : NotifySymbolsResolved(std::move(NotifySymbolsResolved)),
- NotifySymbolsReady(std::move(NotifySymbolsReady)) {
- NotYetResolvedCount = NotYetReadyCount = Symbols.size();
+ const SymbolNameSet &Symbols, SymbolState RequiredState,
+ SymbolsResolvedCallback NotifyComplete)
+ : NotifyComplete(std::move(NotifyComplete)), RequiredState(RequiredState) {
+ assert(RequiredState >= SymbolState::Resolved &&
+ "Cannot query for a symbols that have not reached the resolve state "
+ "yet");
+
+ OutstandingSymbolsCount = Symbols.size();
for (auto &S : Symbols)
ResolvedSymbols[S] = nullptr;
}
-void AsynchronousSymbolQuery::resolve(const SymbolStringPtr &Name,
- JITEvaluatedSymbol Sym) {
+void AsynchronousSymbolQuery::notifySymbolMetRequiredState(
+ 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;
+ --OutstandingSymbolsCount;
}
-void AsynchronousSymbolQuery::handleFullyResolved() {
- assert(NotYetResolvedCount == 0 && "Not fully resolved?");
+void AsynchronousSymbolQuery::handleComplete() {
+ assert(OutstandingSymbolsCount == 0 &&
+ "Symbols remain, handleComplete called prematurely");
- if (!NotifySymbolsResolved) {
- // handleFullyResolved may be called by handleFullyReady (see comments in
- // that method), in which case this is a no-op, so bail out.
- assert(!NotifySymbolsReady &&
- "NotifySymbolsResolved already called or an error occurred");
- return;
- }
-
- auto TmpNotifySymbolsResolved = std::move(NotifySymbolsResolved);
- NotifySymbolsResolved = SymbolsResolvedCallback();
- TmpNotifySymbolsResolved(std::move(ResolvedSymbols));
+ auto TmpNotifyComplete = std::move(NotifyComplete);
+ NotifyComplete = SymbolsResolvedCallback();
+ TmpNotifyComplete(std::move(ResolvedSymbols));
}
-void AsynchronousSymbolQuery::notifySymbolReady() {
- assert(NotYetReadyCount != 0 && "All symbols already emitted");
- --NotYetReadyCount;
-}
-
-void AsynchronousSymbolQuery::handleFullyReady() {
- assert(NotifySymbolsReady &&
- "NotifySymbolsReady already called or an error occurred");
-
- auto TmpNotifySymbolsReady = std::move(NotifySymbolsReady);
- NotifySymbolsReady = SymbolsReadyCallback();
-
- if (NotYetResolvedCount == 0 && NotifySymbolsResolved) {
- // The NotifyResolved callback of one query must have caused this query to
- // become ready (i.e. there is still a handleFullyResolved callback waiting
- // to be made back up the stack). Fold the handleFullyResolved call into
- // this one before proceeding. This will cause the call further up the
- // stack to become a no-op.
- handleFullyResolved();
- }
-
- assert(QueryRegistrations.empty() &&
- "Query is still registered with some symbols");
- assert(!NotifySymbolsResolved && "Resolution not applied yet");
- TmpNotifySymbolsReady(Error::success());
-}
-
-bool AsynchronousSymbolQuery::canStillFail() {
- return (NotifySymbolsResolved || NotifySymbolsReady);
-}
+bool AsynchronousSymbolQuery::canStillFail() { return !!NotifyComplete; }
void AsynchronousSymbolQuery::handleFailed(Error Err) {
assert(QueryRegistrations.empty() && ResolvedSymbols.empty() &&
- NotYetResolvedCount == 0 && NotYetReadyCount == 0 &&
+ OutstandingSymbolsCount == 0 &&
"Query should already have been abandoned");
- if (NotifySymbolsResolved) {
- NotifySymbolsResolved(std::move(Err));
- NotifySymbolsResolved = SymbolsResolvedCallback();
- } else {
- assert(NotifySymbolsReady && "Failed after both callbacks issued?");
- NotifySymbolsReady(std::move(Err));
- }
- NotifySymbolsReady = SymbolsReadyCallback();
+ NotifyComplete(std::move(Err));
+ NotifyComplete = SymbolsResolvedCallback();
}
void AsynchronousSymbolQuery::addQueryDependence(JITDylib &JD,
void AsynchronousSymbolQuery::detach() {
ResolvedSymbols.clear();
- NotYetResolvedCount = 0;
- NotYetReadyCount = 0;
+ OutstandingSymbolsCount = 0;
for (auto &KV : QueryRegistrations)
KV.first->detachQueryHelper(*this, KV.second);
QueryRegistrations.clear();
Aliases.erase(I);
}
+ LLVM_DEBUG({
+ ES.runSessionLocked([&]() {
+ dbgs() << "materializing reexports: target = " << TgtJD.getName()
+ << ", source = " << SrcJD.getName() << " " << RequestedAliases
+ << "\n";
+ });
+ });
+
if (!Aliases.empty()) {
if (SourceJD)
R.replace(reexports(*SourceJD, std::move(Aliases), MatchNonExported));
}
};
- auto OnResolve = [QueryInfo](Expected<SymbolMap> Result) {
+ auto OnComplete = [QueryInfo](Expected<SymbolMap> Result) {
if (Result) {
SymbolMap ResolutionMap;
for (auto &KV : QueryInfo->Aliases) {
}
};
- auto OnReady = [&ES](Error Err) { ES.reportError(std::move(Err)); };
-
ES.lookup(JITDylibSearchList({{&SrcJD, MatchNonExported}}), QuerySymbols,
- std::move(OnResolve), std::move(OnReady),
+ SymbolState::Resolved, std::move(OnComplete),
std::move(RegisterDependencies));
}
}
for (auto &KV : MU->getSymbols()) {
auto MII = MaterializingInfos.find(KV.first);
if (MII != MaterializingInfos.end()) {
- if (!MII->second.PendingQueries.empty())
+ if (MII->second.hasQueriesPending())
return std::move(MU);
}
}
if (I == MaterializingInfos.end())
continue;
- if (!I->second.PendingQueries.empty())
+ if (I->second.hasQueriesPending())
RequestedSymbols.insert(KV.first);
}
}
void JITDylib::resolve(const SymbolMap &Resolved) {
- auto FullyResolvedQueries = ES.runSessionLocked([&, this]() {
- AsynchronousSymbolQuerySet FullyResolvedQueries;
+ auto CompletedQueries = ES.runSessionLocked([&, this]() {
+ AsynchronousSymbolQuerySet CompletedQueries;
for (const auto &KV : Resolved) {
auto &Name = KV.first;
auto Sym = KV.second;
I->second.setState(SymbolState::Resolved);
auto &MI = MaterializingInfos[Name];
- for (auto &Q : MI.PendingQueries) {
- Q->resolve(Name, Sym);
- if (Q->isFullyResolved())
- FullyResolvedQueries.insert(Q);
+ for (auto &Q : MI.takeQueriesMeeting(SymbolState::Resolved)) {
+ Q->notifySymbolMetRequiredState(Name, Sym);
+ if (Q->isComplete())
+ CompletedQueries.insert(std::move(Q));
}
}
- return FullyResolvedQueries;
+ return CompletedQueries;
});
- for (auto &Q : FullyResolvedQueries) {
- assert(Q->isFullyResolved() && "Q not fully resolved");
- Q->handleFullyResolved();
+ for (auto &Q : CompletedQueries) {
+ assert(Q->isComplete() && "Q not completed");
+ Q->handleComplete();
}
}
void JITDylib::emit(const SymbolFlagsMap &Emitted) {
- auto FullyReadyQueries = ES.runSessionLocked([&, this]() {
- AsynchronousSymbolQuerySet ReadyQueries;
+ auto CompletedQueries = ES.runSessionLocked([&, this]() {
+ AsynchronousSymbolQuerySet CompletedQueries;
for (const auto &KV : Emitted) {
const auto &Name = KV.first;
DependantMI.UnemittedDependencies.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(DependantJD, DependantName);
- }
// Since this dependant is now ready, we erase its MaterializingInfo
// and update its materializing state.
- assert(DependantJD.Symbols.count(DependantName) &&
+ auto DependantSymI = DependantJD.Symbols.find(DependantName);
+ assert(DependantSymI != DependantJD.Symbols.end() &&
"Dependant has no entry in the Symbols table");
- DependantJD.Symbols[DependantName].setState(SymbolState::Ready);
+ DependantSymI->second.setState(SymbolState::Ready);
+
+ for (auto &Q : DependantMI.takeQueriesMeeting(SymbolState::Ready)) {
+ Q->notifySymbolMetRequiredState(
+ DependantName, DependantSymI->second.getSymbol());
+ if (Q->isComplete())
+ CompletedQueries.insert(Q);
+ Q->removeQueryDependence(DependantJD, DependantName);
+ }
+
DependantJD.MaterializingInfos.erase(DependantMII);
}
}
MI.IsEmitted = true;
if (MI.UnemittedDependencies.empty()) {
- for (auto &Q : MI.PendingQueries) {
- Q->notifySymbolReady();
- if (Q->isFullyReady())
- ReadyQueries.insert(Q);
+ 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());
+ if (Q->isComplete())
+ CompletedQueries.insert(Q);
Q->removeQueryDependence(*this, Name);
}
- assert(Symbols.count(Name) &&
- "Symbol has no entry in the Symbols table");
- Symbols[Name].setState(SymbolState::Ready);
MaterializingInfos.erase(MII);
}
}
- return ReadyQueries;
+ return CompletedQueries;
});
- for (auto &Q : FullyReadyQueries) {
- assert(Q->isFullyReady() && "Q is not fully ready");
- Q->handleFullyReady();
+ for (auto &Q : CompletedQueries) {
+ assert(Q->isComplete() && "Q is not complete");
+ Q->handleComplete();
}
}
auto FailedQueriesToNotify = ES.runSessionLocked([&, this]() {
AsynchronousSymbolQuerySet FailedQueries;
+ std::vector<MaterializingInfosMap::iterator> MIIsToRemove;
for (auto &Name : FailedSymbols) {
auto I = Symbols.find(Name);
// 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)
+ for (auto &Q : MII->second.pendingQueries())
FailedQueries.insert(Q);
- for (auto &Q : FailedQueries)
- Q->detach();
+ MIIsToRemove.push_back(std::move(MII));
+ }
+
+ // Detach failed queries.
+ for (auto &Q : FailedQueries)
+ Q->detach();
- assert(MII->second.PendingQueries.empty() &&
+ // Remove the MaterializingInfos.
+ for (auto &MII : MIIsToRemove) {
+ assert(!MII->second.hasQueriesPending() &&
"Queries remain after symbol was failed");
MaterializingInfos.erase(MII);
if (!SymI->second.getFlags().isExported() && !MatchNonExported)
continue;
- // If we matched against Name in JD, mark it to be removed from the Unresolved
- // set.
+ // If we matched against Name in JD, mark it to be removed from the
+ // Unresolved set.
ToRemove.push_back(Name);
- if (SymI->second.getState() >= SymbolState::Resolved) {
- assert(!SymI->second.hasMaterializerAttached() &&
- "Resolved symbols should not have materializers attached");
- Q->resolve(Name, SymI->second.getSymbol());
- if (SymI->second.getState() == SymbolState::Ready) {
- Q->notifySymbolReady();
- continue;
- }
- } else if (SymI->second.hasMaterializerAttached()) {
+ // If this symbol already meets the required state for then notify the
+ // query and continue.
+ if (SymI->second.getState() >= Q->getRequiredState()) {
+ Q->notifySymbolMetRequiredState(Name, SymI->second.getSymbol());
+ continue;
+ }
+
+ // Otherwise this symbol does not yet meet the required state. Check whether
+ // it has a materializer attached, and if so prepare to run it.
+ if (SymI->second.hasMaterializerAttached()) {
assert(SymI->second.getAddress() == 0 &&
"Symbol not resolved but already has address?");
auto UMII = UnmaterializedInfos.find(Name);
assert(SymI->second.isInMaterializationPhase() &&
"By this line the symbol should be materializing");
auto &MI = MaterializingInfos[Name];
- MI.PendingQueries.push_back(Q);
+ MI.addQuery(Q);
Q->addQueryDependence(*this, Name);
}
ES.runOutstandingMUs();
- LookupImplActionFlags ActionFlags = None;
+ bool QueryComplete = false;
std::vector<std::unique_ptr<MaterializationUnit>> MUs;
SymbolNameSet Unresolved = std::move(Names);
auto Err = ES.runSessionLocked([&, this]() -> Error {
- ActionFlags = lookupImpl(Q, MUs, Unresolved);
+ QueryComplete = lookupImpl(Q, MUs, Unresolved);
if (DefGenerator && !Unresolved.empty()) {
- assert(ActionFlags == None &&
- "ActionFlags set but unresolved symbols remain?");
+ assert(!QueryComplete && "query complete but unresolved symbols remain?");
auto NewDefs = DefGenerator(*this, Unresolved);
if (!NewDefs)
return NewDefs.takeError();
if (!NewDefs->empty()) {
for (auto &D : *NewDefs)
Unresolved.erase(D);
- ActionFlags = lookupImpl(Q, MUs, *NewDefs);
+ QueryComplete = lookupImpl(Q, MUs, *NewDefs);
assert(NewDefs->empty() &&
"All fallback defs should have been found by lookupImpl");
}
if (Err)
return std::move(Err);
- assert((MUs.empty() || ActionFlags == None) &&
+ assert((MUs.empty() || !QueryComplete) &&
"If action flags are set, there should be no work to do (so no MUs)");
- if (ActionFlags & NotifyFullyResolved)
- Q->handleFullyResolved();
-
- if (ActionFlags & NotifyFullyReady)
- Q->handleFullyReady();
+ if (QueryComplete)
+ Q->handleComplete();
// FIXME: Swap back to the old code below once RuntimeDyld works with
// callbacks from asynchronous queries.
return Unresolved;
}
-JITDylib::LookupImplActionFlags
-JITDylib::lookupImpl(std::shared_ptr<AsynchronousSymbolQuery> &Q,
- std::vector<std::unique_ptr<MaterializationUnit>> &MUs,
- SymbolNameSet &Unresolved) {
- LookupImplActionFlags ActionFlags = None;
- std::vector<SymbolStringPtr> ToRemove;
+bool JITDylib::lookupImpl(
+ std::shared_ptr<AsynchronousSymbolQuery> &Q,
+ std::vector<std::unique_ptr<MaterializationUnit>> &MUs,
+ SymbolNameSet &Unresolved) {
+ bool QueryComplete = false;
+ std::vector<SymbolStringPtr> ToRemove;
for (auto Name : Unresolved) {
// Search for the name in Symbols. Skip it if not found.
// If we found Name, mark it to be removed from the Unresolved set.
ToRemove.push_back(Name);
- // If the symbol has an address then resolve it.
- if (SymI->second.getAddress() != 0) {
- Q->resolve(Name, SymI->second.getSymbol());
- if (Q->isFullyResolved())
- ActionFlags |= NotifyFullyResolved;
+ if (SymI->second.getState() >= Q->getRequiredState()) {
+ Q->notifySymbolMetRequiredState(Name, SymI->second.getSymbol());
+ if (Q->isComplete())
+ QueryComplete = true;
+ continue;
}
// If the symbol is lazy, get the MaterialiaztionUnit for it.
// Add MU to the list of MaterializationUnits to be materialized.
MUs.push_back(std::move(MU));
- } else if (SymI->second.getState() == SymbolState::Ready) {
- Q->notifySymbolReady();
- if (Q->isFullyReady())
- ActionFlags |= NotifyFullyReady;
- continue;
}
// Add the query to the PendingQueries list.
assert(SymI->second.isInMaterializationPhase() &&
"By this line the symbol should be materializing");
auto &MI = MaterializingInfos[Name];
- MI.PendingQueries.push_back(Q);
+ MI.addQuery(Q);
Q->addQueryDependence(*this, Name);
}
for (auto &Name : ToRemove)
Unresolved.erase(Name);
- return ActionFlags;
+ return QueryComplete;
}
void JITDylib::dump(raw_ostream &OS) {
else
OS << "<not resolved> ";
- switch (KV.second.getState()) {
- case SymbolState::Invalid:
- OS << "Invalid";
- break;
- case SymbolState::NeverSearched:
- OS << "Never-Searched";
- break;
- case SymbolState::Materializing:
- OS << "Materializing";
- break;
- case SymbolState::Resolved:
- OS << "Resolved";
- break;
- case SymbolState::Ready:
- OS << "Ready";
- break;
- // default: llvm_unreachable("Invalid state"); break;
- }
+ OS << KV.second.getState();
if (KV.second.hasMaterializerAttached()) {
OS << " (Materializer ";
OS << " \"" << *KV.first << "\":\n"
<< " IsEmitted = " << (KV.second.IsEmitted ? "true" : "false")
<< "\n"
- << " " << KV.second.PendingQueries.size()
+ << " " << KV.second.pendingQueries().size()
<< " pending queries: { ";
- for (auto &Q : KV.second.PendingQueries)
- OS << Q.get() << " ";
+ for (const auto &Q : KV.second.pendingQueries())
+ OS << Q.get() << " (" << Q->getRequiredState() << ") ";
OS << "}\n Dependants:\n";
for (auto &KV2 : KV.second.Dependants)
OS << " " << KV2.first->getName() << ": " << KV2.second << "\n";
});
}
+void JITDylib::MaterializingInfo::addQuery(
+ std::shared_ptr<AsynchronousSymbolQuery> Q) {
+
+ auto I = std::lower_bound(
+ PendingQueries.rbegin(), PendingQueries.rend(), Q->getRequiredState(),
+ [](const std::shared_ptr<AsynchronousSymbolQuery> &V, SymbolState S) {
+ return V->getRequiredState() <= S;
+ });
+ PendingQueries.insert(I.base(), std::move(Q));
+}
+
+void JITDylib::MaterializingInfo::removeQuery(
+ const AsynchronousSymbolQuery &Q) {
+ // FIXME: Implement 'find_as' for shared_ptr<T>/T*.
+ auto I =
+ std::find_if(PendingQueries.begin(), PendingQueries.end(),
+ [&Q](const std::shared_ptr<AsynchronousSymbolQuery> &V) {
+ return V.get() == &Q;
+ });
+ assert(I != PendingQueries.end() &&
+ "Query is not attached to this MaterializingInfo");
+ PendingQueries.erase(I);
+}
+
+JITDylib::AsynchronousSymbolQueryList
+JITDylib::MaterializingInfo::takeQueriesMeeting(SymbolState RequiredState) {
+ AsynchronousSymbolQueryList Result;
+ while (!PendingQueries.empty()) {
+ if (PendingQueries.back()->getRequiredState() > RequiredState)
+ break;
+
+ Result.push_back(std::move(PendingQueries.back()));
+ PendingQueries.pop_back();
+ }
+
+ return Result;
+}
+
+JITDylib::AsynchronousSymbolQueryList
+JITDylib::MaterializingInfo::takeAllQueries() {
+ AsynchronousSymbolQueryList Result;
+ std::swap(Result, PendingQueries);
+ return Result;
+}
+
JITDylib::JITDylib(ExecutionSession &ES, std::string Name)
: ES(ES), JITDylibName(std::move(Name)) {
SearchOrder.push_back({this, true});
assert(MaterializingInfos.count(QuerySymbol) &&
"QuerySymbol does not have MaterializingInfo");
auto &MI = MaterializingInfos[QuerySymbol];
-
- auto IdenticalQuery =
- [&](const std::shared_ptr<AsynchronousSymbolQuery> &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);
+ MI.removeQuery(Q);
}
}
Expected<SymbolMap> ExecutionSession::legacyLookup(
LegacyAsyncLookupFunction AsyncLookup, SymbolNameSet Names,
- bool WaitUntilReady, RegisterDependenciesFunction RegisterDependencies) {
+ SymbolState RequiredState,
+ RegisterDependenciesFunction RegisterDependencies) {
#if LLVM_ENABLE_THREADS
// In the threaded case we use promises to return the results.
std::promise<SymbolMap> PromisedResult;
- std::mutex ErrMutex;
Error ResolutionError = Error::success();
- std::promise<void> PromisedReady;
- Error ReadyError = Error::success();
- auto OnResolve = [&](Expected<SymbolMap> R) {
+ auto NotifyComplete = [&](Expected<SymbolMap> R) {
if (R)
PromisedResult.set_value(std::move(*R));
else {
- {
- ErrorAsOutParameter _(&ResolutionError);
- std::lock_guard<std::mutex> Lock(ErrMutex);
- ResolutionError = R.takeError();
- }
+ ErrorAsOutParameter _(&ResolutionError);
+ ResolutionError = R.takeError();
PromisedResult.set_value(SymbolMap());
}
};
-
- std::function<void(Error)> OnReady;
- if (WaitUntilReady) {
- OnReady = [&](Error Err) {
- if (Err) {
- ErrorAsOutParameter _(&ReadyError);
- std::lock_guard<std::mutex> Lock(ErrMutex);
- ReadyError = std::move(Err);
- }
- PromisedReady.set_value();
- };
- } else {
- OnReady = [&](Error Err) {
- if (Err)
- reportError(std::move(Err));
- };
- }
-
#else
SymbolMap Result;
Error ResolutionError = Error::success();
- Error ReadyError = Error::success();
- auto OnResolve = [&](Expected<SymbolMap> R) {
+ auto NotifyComplete = [&](Expected<SymbolMap> R) {
ErrorAsOutParameter _(&ResolutionError);
if (R)
Result = std::move(*R);
else
ResolutionError = R.takeError();
};
-
- std::function<void(Error)> OnReady;
- if (WaitUntilReady) {
- OnReady = [&](Error Err) {
- ErrorAsOutParameter _(&ReadyError);
- if (Err)
- ReadyError = std::move(Err);
- };
- } else {
- OnReady = [&](Error Err) {
- if (Err)
- reportError(std::move(Err));
- };
- }
#endif
auto Query = std::make_shared<AsynchronousSymbolQuery>(
- Names, std::move(OnResolve), std::move(OnReady));
+ Names, RequiredState, std::move(NotifyComplete));
// FIXME: This should be run session locked along with the registration code
// and error reporting below.
SymbolNameSet UnresolvedSymbols = AsyncLookup(Query, std::move(Names));
#if LLVM_ENABLE_THREADS
auto ResultFuture = PromisedResult.get_future();
auto Result = ResultFuture.get();
-
- {
- std::lock_guard<std::mutex> Lock(ErrMutex);
- if (ResolutionError) {
- // ReadyError will never be assigned. Consume the success value.
- cantFail(std::move(ReadyError));
- return std::move(ResolutionError);
- }
- }
-
- if (WaitUntilReady) {
- auto ReadyFuture = PromisedReady.get_future();
- ReadyFuture.get();
-
- {
- std::lock_guard<std::mutex> Lock(ErrMutex);
- if (ReadyError)
- return std::move(ReadyError);
- }
- } else
- cantFail(std::move(ReadyError));
-
+ if (ResolutionError)
+ return std::move(ResolutionError);
return std::move(Result);
#else
- if (ResolutionError) {
- // ReadyError will never be assigned. Consume the success value.
- cantFail(std::move(ReadyError));
+ if (ResolutionError)
return std::move(ResolutionError);
- }
-
- if (ReadyError)
- return std::move(ReadyError);
return Result;
#endif
void ExecutionSession::lookup(
const JITDylibSearchList &SearchOrder, SymbolNameSet Symbols,
- SymbolsResolvedCallback OnResolve, SymbolsReadyCallback OnReady,
+ SymbolState RequiredState, SymbolsResolvedCallback NotifyComplete,
RegisterDependenciesFunction RegisterDependencies) {
+ LLVM_DEBUG({
+ runSessionLocked([&]() {
+ dbgs() << "Looking up " << Symbols << " in " << SearchOrder
+ << " (required state: " << RequiredState << ")\n";
+ });
+ });
+
// lookup can be re-entered recursively if running on a single thread. Run any
// outstanding MUs in case this query depends on them, otherwise this lookup
// will starve waiting for a result from an MU that is stuck in the queue.
auto Unresolved = std::move(Symbols);
std::map<JITDylib *, MaterializationUnitList> CollectedMUsMap;
- auto Q = std::make_shared<AsynchronousSymbolQuery>(
- Unresolved, std::move(OnResolve), std::move(OnReady));
- bool QueryIsFullyResolved = false;
- bool QueryIsFullyReady = false;
+ auto Q = std::make_shared<AsynchronousSymbolQuery>(Unresolved, RequiredState,
+ std::move(NotifyComplete));
+ bool QueryComplete = false;
auto LodgingErr = runSessionLocked([&]() -> Error {
auto LodgeQuery = [&]() -> Error {
// Record whether this query is fully ready / resolved. We will use
// this to call handleFullyResolved/handleFullyReady outside the session
// lock.
- QueryIsFullyResolved = Q->isFullyResolved();
- QueryIsFullyReady = Q->isFullyReady();
+ QueryComplete = Q->isComplete();
// Call the register dependencies function.
if (RegisterDependencies && !Q->QueryRegistrations.empty())
if (LodgingErr) {
Q->handleFailed(std::move(LodgingErr));
return;
- } else {
- if (QueryIsFullyResolved)
- Q->handleFullyResolved();
- if (QueryIsFullyReady)
- Q->handleFullyReady();
}
+ if (QueryComplete)
+ Q->handleComplete();
+
// Move the MUs to the OutstandingMUs list, then materialize.
{
std::lock_guard<std::recursive_mutex> Lock(OutstandingMUsMutex);
runOutstandingMUs();
}
-Expected<SymbolMap> ExecutionSession::lookup(
- const JITDylibSearchList &SearchOrder, const SymbolNameSet &Symbols,
- RegisterDependenciesFunction RegisterDependencies, bool WaitUntilReady) {
+Expected<SymbolMap>
+ExecutionSession::lookup(const JITDylibSearchList &SearchOrder,
+ const SymbolNameSet &Symbols,
+ SymbolState RequiredState,
+ RegisterDependenciesFunction RegisterDependencies) {
#if LLVM_ENABLE_THREADS
// In the threaded case we use promises to return the results.
std::promise<SymbolMap> PromisedResult;
- std::mutex ErrMutex;
Error ResolutionError = Error::success();
- std::promise<void> PromisedReady;
- Error ReadyError = Error::success();
- auto OnResolve = [&](Expected<SymbolMap> R) {
+
+ auto NotifyComplete = [&](Expected<SymbolMap> R) {
if (R)
PromisedResult.set_value(std::move(*R));
else {
- {
- ErrorAsOutParameter _(&ResolutionError);
- std::lock_guard<std::mutex> Lock(ErrMutex);
- ResolutionError = R.takeError();
- }
+ ErrorAsOutParameter _(&ResolutionError);
+ ResolutionError = R.takeError();
PromisedResult.set_value(SymbolMap());
}
};
- std::function<void(Error)> OnReady;
- if (WaitUntilReady) {
- OnReady = [&](Error Err) {
- if (Err) {
- ErrorAsOutParameter _(&ReadyError);
- std::lock_guard<std::mutex> Lock(ErrMutex);
- ReadyError = std::move(Err);
- }
- PromisedReady.set_value();
- };
- } else {
- OnReady = [&](Error Err) {
- if (Err)
- reportError(std::move(Err));
- };
- }
-
#else
SymbolMap Result;
Error ResolutionError = Error::success();
- Error ReadyError = Error::success();
- auto OnResolve = [&](Expected<SymbolMap> R) {
+ auto NotifyComplete = [&](Expected<SymbolMap> R) {
ErrorAsOutParameter _(&ResolutionError);
if (R)
Result = std::move(*R);
else
ResolutionError = R.takeError();
};
-
- std::function<void(Error)> OnReady;
- if (WaitUntilReady) {
- OnReady = [&](Error Err) {
- ErrorAsOutParameter _(&ReadyError);
- if (Err)
- ReadyError = std::move(Err);
- };
- } else {
- OnReady = [&](Error Err) {
- if (Err)
- reportError(std::move(Err));
- };
- }
#endif
// Perform the asynchronous lookup.
- lookup(SearchOrder, Symbols, OnResolve, OnReady, RegisterDependencies);
+ lookup(SearchOrder, Symbols, RequiredState, NotifyComplete,
+ RegisterDependencies);
#if LLVM_ENABLE_THREADS
auto ResultFuture = PromisedResult.get_future();
auto Result = ResultFuture.get();
- {
- std::lock_guard<std::mutex> Lock(ErrMutex);
- if (ResolutionError) {
- // ReadyError will never be assigned. Consume the success value.
- cantFail(std::move(ReadyError));
- return std::move(ResolutionError);
- }
- }
-
- if (WaitUntilReady) {
- auto ReadyFuture = PromisedReady.get_future();
- ReadyFuture.get();
-
- {
- std::lock_guard<std::mutex> Lock(ErrMutex);
- if (ReadyError)
- return std::move(ReadyError);
- }
- } else
- cantFail(std::move(ReadyError));
+ if (ResolutionError)
+ return std::move(ResolutionError);
return std::move(Result);
#else
- if (ResolutionError) {
- // ReadyError will never be assigned. Consume the success value.
- cantFail(std::move(ReadyError));
+ if (ResolutionError)
return std::move(ResolutionError);
- }
-
- if (ReadyError)
- return std::move(ReadyError);
return Result;
#endif
SymbolStringPtr Name) {
SymbolNameSet Names({Name});
- if (auto ResultMap = lookup(SearchOrder, std::move(Names),
- NoDependenciesToRegister, true)) {
+ if (auto ResultMap = lookup(SearchOrder, std::move(Names), SymbolState::Ready,
+ NoDependenciesToRegister)) {
assert(ResultMap->size() == 1 && "Unexpected number of results");
assert(ResultMap->count(Name) && "Missing result for symbol");
return std::move(ResultMap->begin()->second);
auto &ES = JD.getExecutionSession();
if (auto CtorDtorMap =
- ES.lookup(JITDylibSearchList({{&JD, true}}), std::move(Names),
- NoDependenciesToRegister, true)) {
+ ES.lookup(JITDylibSearchList({{&JD, true}}), std::move(Names))) {
for (auto &KV : CtorDtorsByPriority) {
for (auto &Name : KV.second) {
assert(CtorDtorMap->count(Name) && "No entry for Name");
SymbolName = I->second.second;
}
- auto LookupResult = ES.lookup(JITDylibSearchList({{SourceJD, true}}),
- {SymbolName}, NoDependenciesToRegister, true);
+ auto LookupResult =
+ ES.lookup(JITDylibSearchList({{SourceJD, true}}), SymbolName);
if (!LookupResult) {
ES.reportError(LookupResult.takeError());
return ErrorHandlerAddr;
}
- assert(LookupResult->size() == 1 && "Unexpected number of results");
- assert(LookupResult->count(SymbolName) && "Unexpected result");
-
- auto ResolvedAddr = LookupResult->begin()->second.getAddress();
+ auto ResolvedAddr = LookupResult->getAddress();
std::shared_ptr<NotifyResolvedFunction> NotifyResolved = nullptr;
{
};
auto Q = std::make_shared<AsynchronousSymbolQuery>(
- InternedSymbols, OnResolvedWithUnwrap,
- [this](Error Err) { ES.reportError(std::move(Err)); });
+ InternedSymbols, SymbolState::Resolved, OnResolvedWithUnwrap);
auto Unresolved = R.lookup(Q, InternedSymbols);
if (Unresolved.empty()) {
}
};
- ES.lookup(
- SearchOrder, std::move(InternedSymbols), std::move(OnResolve),
- // OnReady:
- [&ES](Error Err) { ES.reportError(std::move(Err)); },
- // RegisterDependencies:
- [this](const SymbolDependenceMap &Deps) {
- registerDependencies(Deps);
- });
+ ES.lookup(SearchOrder, std::move(InternedSymbols), SymbolState::Resolved,
+ std::move(OnResolve), [this](const SymbolDependenceMap &Deps) {
+ registerDependencies(Deps);
+ });
}
void notifyResolved(AtomGraph &G) override {
for (auto &S : Symbols) {
if (auto Sym = findSymbol(*S)) {
if (auto Addr = Sym.getAddress()) {
- Query->resolve(S, JITEvaluatedSymbol(*Addr, Sym.getFlags()));
- Query->notifySymbolReady();
+ Query->notifySymbolMetRequiredState(
+ S, JITEvaluatedSymbol(*Addr, Sym.getFlags()));
} else {
Stack.ES.legacyFailQuery(*Query, Addr.takeError());
return orc::SymbolNameSet();
UnresolvedSymbols.insert(S);
}
- if (Query->isFullyResolved())
- Query->handleFullyResolved();
-
- if (Query->isFullyReady())
- Query->handleFullyReady();
+ if (Query->isComplete())
+ Query->handleComplete();
return UnresolvedSymbols;
}
for (auto &S : Symbols) {
if (auto Sym = M.findMangledSymbol(*S)) {
if (auto Addr = Sym.getAddress()) {
- Query->resolve(S, JITEvaluatedSymbol(*Addr, Sym.getFlags()));
- Query->notifySymbolReady();
+ Query->notifySymbolMetRequiredState(
+ S, JITEvaluatedSymbol(*Addr, Sym.getFlags()));
NewSymbolsResolved = true;
} else {
M.ES.legacyFailQuery(*Query, Addr.takeError());
} else {
if (auto Sym2 = M.ClientResolver->findSymbol(*S)) {
if (auto Addr = Sym2.getAddress()) {
- Query->resolve(S, JITEvaluatedSymbol(*Addr, Sym2.getFlags()));
- Query->notifySymbolReady();
+ Query->notifySymbolMetRequiredState(
+ S, JITEvaluatedSymbol(*Addr, Sym2.getFlags()));
NewSymbolsResolved = true;
} else {
M.ES.legacyFailQuery(*Query, Addr.takeError());
}
}
- if (NewSymbolsResolved && Query->isFullyResolved())
- Query->handleFullyResolved();
-
- if (NewSymbolsResolved && Query->isFullyReady())
- Query->handleFullyReady();
+ if (NewSymbolsResolved && Query->isComplete())
+ Query->handleComplete();
return UnresolvedSymbols;
}
OnResolved(Result);
};
- // We're not waiting for symbols to be ready. Just log any errors.
- auto OnReady = [&ES](Error Err) { ES.reportError(std::move(Err)); };
-
// Register dependencies for all symbols contained in this set.
auto RegisterDependencies = [&](const SymbolDependenceMap &Deps) {
MR.addDependenciesForAll(Deps);
JITDylibSearchList SearchOrder;
MR.getTargetJITDylib().withSearchOrderDo(
[&](const JITDylibSearchList &JDs) { SearchOrder = JDs; });
- ES.lookup(SearchOrder, InternedSymbols, OnResolvedWithUnwrap, OnReady,
- RegisterDependencies);
+ ES.lookup(SearchOrder, InternedSymbols, SymbolState::Resolved,
+ OnResolvedWithUnwrap, RegisterDependencies);
}
Expected<LookupSet> getResponsibilitySet(const LookupSet &Symbols) {
namespace {
TEST_F(CoreAPIsStandardTest, BasicSuccessfulLookup) {
- bool OnResolutionRun = false;
- bool OnReadyRun = false;
+ bool OnCompletionRun = false;
- auto OnResolution = [&](Expected<SymbolMap> Result) {
+ auto OnCompletion = [&](Expected<SymbolMap> Result) {
EXPECT_TRUE(!!Result) << "Resolution unexpectedly returned error";
auto &Resolved = *Result;
auto I = Resolved.find(Foo);
EXPECT_NE(I, Resolved.end()) << "Could not find symbol definition";
EXPECT_EQ(I->second.getAddress(), FooAddr)
<< "Resolution returned incorrect result";
- OnResolutionRun = true;
- };
- auto OnReady = [&](Error Err) {
- cantFail(std::move(Err));
- OnReadyRun = true;
+ OnCompletionRun = true;
};
std::shared_ptr<MaterializationResponsibility> FooMR;
FooMR = std::make_shared<MaterializationResponsibility>(std::move(R));
})));
- ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo}, OnResolution, OnReady,
- NoDependenciesToRegister);
+ ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo}, SymbolState::Ready,
+ OnCompletion, NoDependenciesToRegister);
- EXPECT_FALSE(OnResolutionRun) << "Should not have been resolved yet";
- EXPECT_FALSE(OnReadyRun) << "Should not have been marked ready yet";
+ EXPECT_FALSE(OnCompletionRun) << "Should not have been resolved yet";
FooMR->resolve({{Foo, FooSym}});
- EXPECT_TRUE(OnResolutionRun) << "Should have been resolved";
- EXPECT_FALSE(OnReadyRun) << "Should not have been marked ready yet";
+ EXPECT_FALSE(OnCompletionRun) << "Should not be ready yet";
FooMR->emit();
- EXPECT_TRUE(OnReadyRun) << "Should have been marked ready";
+ EXPECT_TRUE(OnCompletionRun) << "Should have been marked ready";
}
TEST_F(CoreAPIsStandardTest, ExecutionSessionFailQuery) {
- bool OnResolutionRun = false;
- bool OnReadyRun = false;
+ bool OnCompletionRun = false;
- auto OnResolution = [&](Expected<SymbolMap> Result) {
+ auto OnCompletion = [&](Expected<SymbolMap> Result) {
EXPECT_FALSE(!!Result) << "Resolution unexpectedly returned success";
auto Msg = toString(Result.takeError());
EXPECT_EQ(Msg, "xyz") << "Resolution returned incorrect result";
- OnResolutionRun = true;
- };
- auto OnReady = [&](Error Err) {
- cantFail(std::move(Err));
- OnReadyRun = true;
+ OnCompletionRun = true;
};
- AsynchronousSymbolQuery Q(SymbolNameSet({Foo}), OnResolution, OnReady);
+ AsynchronousSymbolQuery Q(SymbolNameSet({Foo}), SymbolState::Ready,
+ OnCompletion);
ES.legacyFailQuery(Q,
make_error<StringError>("xyz", inconvertibleErrorCode()));
- EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run";
- EXPECT_FALSE(OnReadyRun) << "OnReady unexpectedly run";
+ EXPECT_TRUE(OnCompletionRun) << "OnCompletionCallback was not run";
}
TEST_F(CoreAPIsStandardTest, EmptyLookup) {
- bool OnResolvedRun = false;
- bool OnReadyRun = false;
+ bool OnCompletionRun = false;
- auto OnResolution = [&](Expected<SymbolMap> Result) {
+ auto OnCompletion = [&](Expected<SymbolMap> Result) {
cantFail(std::move(Result));
- OnResolvedRun = true;
- };
-
- auto OnReady = [&](Error Err) {
- cantFail(std::move(Err));
- OnReadyRun = true;
+ OnCompletionRun = true;
};
- ES.lookup(JITDylibSearchList({{&JD, false}}), {}, OnResolution, OnReady,
- NoDependenciesToRegister);
+ ES.lookup(JITDylibSearchList({{&JD, false}}), {}, SymbolState::Ready,
+ OnCompletion, NoDependenciesToRegister);
- EXPECT_TRUE(OnResolvedRun) << "OnResolved was not run for empty query";
- EXPECT_TRUE(OnReadyRun) << "OnReady was not run for empty query";
+ EXPECT_TRUE(OnCompletionRun) << "OnCompletion was not run for empty query";
}
TEST_F(CoreAPIsStandardTest, RemoveSymbolsTest) {
ADD_FAILURE() << "\"Baz\" discarded unexpectedly";
})));
- bool OnResolvedRun = false;
- bool OnReadyRun = false;
- ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo, Baz},
- [&](Expected<SymbolMap> Result) {
- EXPECT_TRUE(!!Result) << "OnResolved failed unexpectedly";
- consumeError(Result.takeError());
- OnResolvedRun = true;
- },
- [&](Error Err) {
- EXPECT_FALSE(!!Err) << "OnReady failed unexpectedly";
- consumeError(std::move(Err));
- OnReadyRun = true;
- },
- NoDependenciesToRegister);
+ bool OnCompletionRun = false;
+ ES.lookup(
+ JITDylibSearchList({{&JD, false}}), {Foo, Baz}, SymbolState::Ready,
+ [&](Expected<SymbolMap> Result) {
+ cantFail(Result.takeError());
+ OnCompletionRun = true;
+ },
+ NoDependenciesToRegister);
{
// Attempt 1: Search for a missing symbol, Qux.
EXPECT_TRUE(BarDiscarded) << "\"Bar\" should have been discarded";
EXPECT_TRUE(BarMaterializerDestructed)
<< "\"Bar\"'s materializer should have been destructed";
- EXPECT_TRUE(OnResolvedRun) << "OnResolved should have been run";
- EXPECT_TRUE(OnReadyRun) << "OnReady should have been run";
+ EXPECT_TRUE(OnCompletionRun) << "OnCompletion should have been run";
}
TEST_F(CoreAPIsStandardTest, ChainedJITDylibLookup) {
auto &JD2 = ES.createJITDylib("JD2");
- bool OnResolvedRun = false;
- bool OnReadyRun = false;
+ bool OnCompletionRun = false;
auto Q = std::make_shared<AsynchronousSymbolQuery>(
- SymbolNameSet({Foo}),
+ SymbolNameSet({Foo}), SymbolState::Ready,
[&](Expected<SymbolMap> Result) {
cantFail(std::move(Result));
- OnResolvedRun = true;
- },
- [&](Error Err) {
- cantFail(std::move(Err));
- OnReadyRun = true;
+ OnCompletionRun = true;
});
cantFail(JD2.legacyLookup(Q, cantFail(JD.legacyLookup(Q, {Foo}))));
- EXPECT_TRUE(OnResolvedRun) << "OnResolved was not run for empty query";
- EXPECT_TRUE(OnReadyRun) << "OnReady was not run for empty query";
+ EXPECT_TRUE(OnCompletionRun) << "OnCompletion was not run for empty query";
}
TEST_F(CoreAPIsStandardTest, LookupWithHiddenSymbols) {
cantFail(JD.define(FooMU));
bool FooReady = false;
- auto OnResolution = [](Expected<SymbolMap> R) { cantFail(std::move(R)); };
- auto OnReady = [&](Error Err) {
- cantFail(std::move(Err));
+ auto OnCompletion = [&](Expected<SymbolMap> Result) {
+ cantFail(std::move(Result));
FooReady = true;
};
- ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo}, std::move(OnResolution),
- std::move(OnReady), NoDependenciesToRegister);
+ ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo}, SymbolState::Ready,
+ OnCompletion, NoDependenciesToRegister);
FooR->resolve({{Foo, FooSym}});
FooR->emit();
FooResolved = true;
};
- auto OnFooReady = [&](Error Err) {
- cantFail(std::move(Err));
+ auto OnFooReady = [&](Expected<SymbolMap> Result) {
+ cantFail(std::move(Result));
FooReady = true;
};
- // Issue a lookup for Foo. Use NoDependenciesToRegister: We're going to add
+ // Issue lookups for Foo. Use NoDependenciesToRegister: We're going to add
// the dependencies manually below.
- ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo},
- std::move(OnFooResolution), std::move(OnFooReady),
- NoDependenciesToRegister);
+ ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo}, SymbolState::Resolved,
+ std::move(OnFooResolution), NoDependenciesToRegister);
+
+ ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo}, SymbolState::Ready,
+ std::move(OnFooReady), NoDependenciesToRegister);
bool BarResolved = false;
bool BarReady = false;
BarResolved = true;
};
- auto OnBarReady = [&](Error Err) {
- cantFail(std::move(Err));
+ auto OnBarReady = [&](Expected<SymbolMap> Result) {
+ cantFail(std::move(Result));
BarReady = true;
};
- ES.lookup(JITDylibSearchList({{&JD, false}}), {Bar},
- std::move(OnBarResolution), std::move(OnBarReady),
- NoDependenciesToRegister);
+ ES.lookup(JITDylibSearchList({{&JD, false}}), {Bar}, SymbolState::Resolved,
+ std::move(OnBarResolution), NoDependenciesToRegister);
+
+ ES.lookup(JITDylibSearchList({{&JD, false}}), {Bar}, SymbolState::Ready,
+ std::move(OnBarReady), NoDependenciesToRegister);
bool BazResolved = false;
bool BazReady = false;
BazResolved = true;
};
- auto OnBazReady = [&](Error Err) {
- cantFail(std::move(Err));
+ auto OnBazReady = [&](Expected<SymbolMap> Result) {
+ cantFail(std::move(Result));
BazReady = true;
};
- ES.lookup(JITDylibSearchList({{&JD, false}}), {Baz},
- std::move(OnBazResolution), std::move(OnBazReady),
- NoDependenciesToRegister);
+ ES.lookup(JITDylibSearchList({{&JD, false}}), {Baz}, SymbolState::Resolved,
+ std::move(OnBazResolution), NoDependenciesToRegister);
+
+ ES.lookup(JITDylibSearchList({{&JD, false}}), {Baz}, SymbolState::Ready,
+ std::move(OnBazReady), NoDependenciesToRegister);
// Add a circular dependency: Foo -> Bar, Bar -> Baz, Baz -> Foo.
FooR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}});
SymbolNameSet Names({Foo});
- bool OnResolutionRun = false;
- bool OnReadyRun = false;
+ bool OnCompletionRun = false;
- auto OnResolution = [&](Expected<SymbolMap> Result) {
+ auto OnCompletion = [&](Expected<SymbolMap> Result) {
EXPECT_TRUE(!!Result) << "Resolution unexpectedly returned error";
auto I = Result->find(Foo);
EXPECT_NE(I, Result->end()) << "Could not find symbol definition";
EXPECT_EQ(I->second.getAddress(), FooSym.getAddress())
<< "Resolution returned incorrect result";
- OnResolutionRun = true;
- };
-
- auto OnReady = [&](Error Err) {
- cantFail(std::move(Err));
- OnReadyRun = true;
+ OnCompletionRun = true;
};
- ES.lookup(JITDylibSearchList({{&JD, false}}), Names, std::move(OnResolution),
- std::move(OnReady), NoDependenciesToRegister);
+ ES.lookup(JITDylibSearchList({{&JD, false}}), Names, SymbolState::Ready,
+ std::move(OnCompletion), NoDependenciesToRegister);
EXPECT_TRUE(FooMaterialized) << "Foo was not materialized";
EXPECT_TRUE(BarDiscarded) << "Bar was not discarded";
- EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run";
- EXPECT_TRUE(OnReadyRun) << "OnReady was not run";
+ EXPECT_TRUE(OnCompletionRun) << "OnResolutionCallback was not run";
}
TEST_F(CoreAPIsStandardTest, TestBasicWeakSymbolMaterialization) {
cantFail(JD.define(MU1));
cantFail(JD.define(MU2));
- bool OnResolvedRun = false;
- bool OnReadyRun = false;
+ bool OnCompletionRun = false;
- auto OnResolution = [&](Expected<SymbolMap> Result) {
+ auto OnCompletion = [&](Expected<SymbolMap> Result) {
cantFail(std::move(Result));
- OnResolvedRun = true;
+ OnCompletionRun = true;
};
- auto OnReady = [&](Error Err) {
- cantFail(std::move(Err));
- OnReadyRun = true;
- };
-
- ES.lookup(JITDylibSearchList({{&JD, false}}), {Bar}, std::move(OnResolution),
- std::move(OnReady), NoDependenciesToRegister);
+ ES.lookup(JITDylibSearchList({{&JD, false}}), {Bar}, SymbolState::Ready,
+ std::move(OnCompletion), NoDependenciesToRegister);
- EXPECT_TRUE(OnResolvedRun) << "OnResolved not run";
- EXPECT_TRUE(OnReadyRun) << "OnReady not run";
+ EXPECT_TRUE(OnCompletionRun) << "OnCompletion not run";
EXPECT_TRUE(BarMaterialized) << "Bar was not materialized at all";
EXPECT_TRUE(DuplicateBarDiscarded)
<< "Duplicate bar definition not discarded";
auto MU = llvm::make_unique<SimpleMaterializationUnit>(
SymbolFlagsMap({{Foo, JITSymbolFlags::Exported | JITSymbolFlags::Weak},
{Bar, JITSymbolFlags::Exported | JITSymbolFlags::Weak}}),
- [&](MaterializationResponsibility R) { R.failMaterialization(); });
+ [&](MaterializationResponsibility R) {
+ dbgs() << "Before failMat:\n";
+ ES.dump(dbgs());
+ R.failMaterialization();
+ });
cantFail(JD.define(MU));
ES.lookup(
JITDylibSearchList({{&JD, false}}), SymbolNameSet({Baz}),
+ SymbolState::Resolved,
[&R](Expected<SymbolMap> Result) {
// Called when "baz" is resolved. We don't actually depend
// on or care about baz, but use it to trigger failure of
cantFail(std::move(Result));
R.failMaterialization();
},
- [](Error Err) { cantFail(std::move(Err)); },
[&](const SymbolDependenceMap &Deps) {
R.addDependenciesForAll(Deps);
});
});
cantFail(JD.define(MU));
- auto OnResolution = [](Expected<SymbolMap> Result) {
+ auto OnCompletion = [](Expected<SymbolMap> Result) {
cantFail(std::move(Result));
};
- auto OnReady = [](Error Err) { cantFail(std::move(Err)); };
-
- ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo}, std::move(OnResolution),
- std::move(OnReady), NoDependenciesToRegister);
+ ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo}, SymbolState::Ready,
+ std::move(OnCompletion), NoDependenciesToRegister);
auto MU2 = llvm::make_unique<SimpleMaterializationUnit>(
SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}}),
EXPECT_EQ(RS.count(Bar), 1U)
<< "getResponsibilitySet result incorrect. Should be {'bar'}";
- bool OnResolvedRun = false;
+ bool OnCompletionRun = false;
- auto OnResolved = [&](Expected<SymbolMap> Result) {
- OnResolvedRun = true;
+ auto OnCompletion = [&](Expected<SymbolMap> Result) {
+ OnCompletionRun = true;
EXPECT_TRUE(!!Result) << "Unexpected error";
EXPECT_EQ(Result->size(), 2U) << "Unexpected number of resolved symbols";
EXPECT_EQ(Result->count(Foo), 1U) << "Missing lookup result for foo";
EXPECT_EQ((*Result)[Bar].getAddress(), BarSym.getAddress())
<< "Incorrect address for bar";
};
- auto OnReady = [&](Error Err) {
- EXPECT_FALSE(!!Err) << "Finalization should never fail in this test";
- };
- auto Q = std::make_shared<AsynchronousSymbolQuery>(SymbolNameSet({Foo, Bar}),
- OnResolved, OnReady);
+ auto Q = std::make_shared<AsynchronousSymbolQuery>(
+ SymbolNameSet({Foo, Bar}), SymbolState::Resolved, OnCompletion);
auto Unresolved =
Resolver->lookup(std::move(Q), SymbolNameSet({Foo, Bar, Baz}));
EXPECT_EQ(Unresolved.size(), 1U) << "Expected one unresolved symbol";
EXPECT_EQ(Unresolved.count(Baz), 1U) << "Expected baz to not be resolved";
- EXPECT_TRUE(OnResolvedRun) << "OnResolved was never run";
+ EXPECT_TRUE(OnCompletionRun) << "OnCompletion was never run";
}
TEST_F(LegacyAPIsStandardTest, LegacyLookupHelpersFn) {
EXPECT_FALSE(BarMaterialized)
<< "lookupFlags should not have materialized bar";
- bool OnResolvedRun = false;
- bool OnReadyRun = false;
- auto OnResolved = [&](Expected<SymbolMap> Result) {
- OnResolvedRun = true;
+ bool OnCompletionRun = false;
+ auto OnCompletion = [&](Expected<SymbolMap> Result) {
+ OnCompletionRun = true;
EXPECT_TRUE(!!Result) << "lookuWithLegacy failed to resolve";
EXPECT_EQ(Result->size(), 2U) << "Wrong number of symbols resolved";
EXPECT_EQ((*Result)[Bar].getFlags(), BarSym.getFlags())
<< "Wrong flags for bar";
};
- auto OnReady = [&](Error Err) {
- EXPECT_FALSE(!!Err) << "Finalization unexpectedly failed";
- OnReadyRun = true;
- };
- AsynchronousSymbolQuery Q({Foo, Bar}, OnResolved, OnReady);
+ AsynchronousSymbolQuery Q({Foo, Bar}, SymbolState::Resolved, OnCompletion);
auto Unresolved =
lookupWithLegacyFn(ES, Q, SymbolNameSet({Foo, Bar, Baz}), LegacyLookup);
- EXPECT_TRUE(OnResolvedRun) << "OnResolved was not run";
- EXPECT_TRUE(OnReadyRun) << "OnReady was not run";
+ EXPECT_TRUE(OnCompletionRun) << "OnCompletion was not run";
EXPECT_EQ(Unresolved.size(), 1U) << "Expected one unresolved symbol";
EXPECT_EQ(Unresolved.count(Baz), 1U) << "Expected baz to be unresolved";
}
cantFail(std::move(R));
};
- auto OnReadyDoNothing = [](Error Err) { cantFail(std::move(Err)); };
-
ObjLayer.setProcessAllSections(ProcessAllSections);
cantFail(ObjLayer.add(JD, std::move(Obj), ES.allocateVModule()));
- ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo}, OnResolveDoNothing,
- OnReadyDoNothing, NoDependenciesToRegister);
+ ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo}, SymbolState::Resolved,
+ OnResolveDoNothing, NoDependenciesToRegister);
+
return DebugSectionSeen;
}
ObjLayer.setOverrideObjectFlagsWithResponsibilityFlags(true);
cantFail(CompileLayer.add(JD, std::move(M), ES.allocateVModule()));
- ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo},
- [](Expected<SymbolMap> R) { cantFail(std::move(R)); },
- [](Error Err) { cantFail(std::move(Err)); },
- NoDependenciesToRegister);
+ ES.lookup(
+ JITDylibSearchList({{&JD, false}}), {Foo}, SymbolState::Resolved,
+ [](Expected<SymbolMap> R) { cantFail(std::move(R)); },
+ NoDependenciesToRegister);
}
TEST(RTDyldObjectLinkingLayerTest, TestAutoClaimResponsibilityForSymbols) {
ObjLayer.setAutoClaimResponsibilityForObjectSymbols(true);
cantFail(CompileLayer.add(JD, std::move(M), ES.allocateVModule()));
- ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo},
- [](Expected<SymbolMap> R) { cantFail(std::move(R)); },
- [](Error Err) { cantFail(std::move(Err)); },
- NoDependenciesToRegister);
+ ES.lookup(
+ JITDylibSearchList({{&JD, false}}), {Foo}, SymbolState::Resolved,
+ [](Expected<SymbolMap> R) { cantFail(std::move(R)); },
+ NoDependenciesToRegister);
}
} // end anonymous namespace