From d261e1258c2d72911f278aa85298b4d6b5393731 Mon Sep 17 00:00:00 2001 From: Lang Hames Date: Wed, 16 May 2018 22:24:30 +0000 Subject: [PATCH] [ORC] Rewrite the VSO symbol table yet again. Update related utilities. VSOs now track dependencies for materializing symbols. Each symbol must have its dependencies registered with the VSO prior to finalization. Usually this will involve registering the dependencies returned in AsynchronousSymbolQuery::ResolutionResults for queries made while linking the symbols being materialized. Queries against symbols are notified that a symbol is ready once it and all of its transitive dependencies are finalized, allowing compilation work to be broken up and moved between threads without queries returning until their symbols fully safe to access / execute. Related utilities (VSO, MaterializationUnit, MaterializationResponsibility) are updated to support dependence tracking and more explicitly track responsibility for symbols from the point of definition until they are finalized. llvm-svn: 332541 --- .../BuildingAJIT/Chapter1/KaleidoscopeJIT.h | 1 + .../BuildingAJIT/Chapter2/KaleidoscopeJIT.h | 1 + .../BuildingAJIT/Chapter3/KaleidoscopeJIT.h | 1 + .../BuildingAJIT/Chapter4/KaleidoscopeJIT.h | 1 + .../BuildingAJIT/Chapter5/KaleidoscopeJIT.h | 1 + .../Kaleidoscope/include/KaleidoscopeJIT.h | 1 + llvm/include/llvm/ExecutionEngine/JITSymbol.h | 3 + .../Orc/CompileOnDemandLayer.h | 13 +- llvm/include/llvm/ExecutionEngine/Orc/Core.h | 688 ++++++----- .../include/llvm/ExecutionEngine/Orc/Legacy.h | 36 +- .../Orc/RTDyldObjectLinkingLayer.h | 3 +- llvm/lib/ExecutionEngine/Orc/Core.cpp | 1093 ++++++++++------- llvm/lib/ExecutionEngine/Orc/Legacy.cpp | 38 +- .../ExecutionEngine/Orc/OrcCBindingsStack.h | 7 +- .../ExecutionEngine/Orc/OrcMCJITReplacement.h | 22 +- llvm/tools/lli/OrcLazyJIT.h | 5 +- .../ExecutionEngine/Orc/CoreAPIsTest.cpp | 543 +++++--- .../Orc/LegacyAPIInteropTest.cpp | 50 +- .../Orc/RTDyldObjectLinkingLayerTest.cpp | 2 +- 19 files changed, 1549 insertions(+), 960 deletions(-) diff --git a/llvm/examples/Kaleidoscope/BuildingAJIT/Chapter1/KaleidoscopeJIT.h b/llvm/examples/Kaleidoscope/BuildingAJIT/Chapter1/KaleidoscopeJIT.h index 902f5f0403a5..0b8bb381d08a 100644 --- a/llvm/examples/Kaleidoscope/BuildingAJIT/Chapter1/KaleidoscopeJIT.h +++ b/llvm/examples/Kaleidoscope/BuildingAJIT/Chapter1/KaleidoscopeJIT.h @@ -48,6 +48,7 @@ private: public: KaleidoscopeJIT() : Resolver(createLegacyLookupResolver( + ES, [this](const std::string &Name) -> JITSymbol { if (auto Sym = CompileLayer.findSymbol(Name, false)) return Sym; diff --git a/llvm/examples/Kaleidoscope/BuildingAJIT/Chapter2/KaleidoscopeJIT.h b/llvm/examples/Kaleidoscope/BuildingAJIT/Chapter2/KaleidoscopeJIT.h index 4b7fd7f1c220..9ea84d1a8581 100644 --- a/llvm/examples/Kaleidoscope/BuildingAJIT/Chapter2/KaleidoscopeJIT.h +++ b/llvm/examples/Kaleidoscope/BuildingAJIT/Chapter2/KaleidoscopeJIT.h @@ -58,6 +58,7 @@ private: public: KaleidoscopeJIT() : Resolver(createLegacyLookupResolver( + ES, [this](const std::string &Name) -> JITSymbol { if (auto Sym = OptimizeLayer.findSymbol(Name, false)) return Sym; diff --git a/llvm/examples/Kaleidoscope/BuildingAJIT/Chapter3/KaleidoscopeJIT.h b/llvm/examples/Kaleidoscope/BuildingAJIT/Chapter3/KaleidoscopeJIT.h index 0dc1faef185d..212dc1c42ffb 100644 --- a/llvm/examples/Kaleidoscope/BuildingAJIT/Chapter3/KaleidoscopeJIT.h +++ b/llvm/examples/Kaleidoscope/BuildingAJIT/Chapter3/KaleidoscopeJIT.h @@ -98,6 +98,7 @@ public: // Build a resolver and associate it with the new key. Resolvers[K] = createLegacyLookupResolver( + ES, [this](const std::string &Name) -> JITSymbol { if (auto Sym = CompileLayer.findSymbol(Name, false)) return Sym; diff --git a/llvm/examples/Kaleidoscope/BuildingAJIT/Chapter4/KaleidoscopeJIT.h b/llvm/examples/Kaleidoscope/BuildingAJIT/Chapter4/KaleidoscopeJIT.h index f0f016c8be18..dc8e936afd23 100644 --- a/llvm/examples/Kaleidoscope/BuildingAJIT/Chapter4/KaleidoscopeJIT.h +++ b/llvm/examples/Kaleidoscope/BuildingAJIT/Chapter4/KaleidoscopeJIT.h @@ -91,6 +91,7 @@ private: public: KaleidoscopeJIT() : Resolver(createLegacyLookupResolver( + ES, [this](const std::string &Name) -> JITSymbol { if (auto Sym = IndirectStubsMgr->findStub(Name, false)) return Sym; diff --git a/llvm/examples/Kaleidoscope/BuildingAJIT/Chapter5/KaleidoscopeJIT.h b/llvm/examples/Kaleidoscope/BuildingAJIT/Chapter5/KaleidoscopeJIT.h index 1b503e40fa6f..e86f3c3c633f 100644 --- a/llvm/examples/Kaleidoscope/BuildingAJIT/Chapter5/KaleidoscopeJIT.h +++ b/llvm/examples/Kaleidoscope/BuildingAJIT/Chapter5/KaleidoscopeJIT.h @@ -97,6 +97,7 @@ private: public: KaleidoscopeJIT(MyRemote &Remote) : Resolver(createLegacyLookupResolver( + ES, [this](const std::string &Name) -> JITSymbol { if (auto Sym = IndirectStubsMgr->findStub(Name, false)) return Sym; diff --git a/llvm/examples/Kaleidoscope/include/KaleidoscopeJIT.h b/llvm/examples/Kaleidoscope/include/KaleidoscopeJIT.h index d2154887852b..7239aea7ba1b 100644 --- a/llvm/examples/Kaleidoscope/include/KaleidoscopeJIT.h +++ b/llvm/examples/Kaleidoscope/include/KaleidoscopeJIT.h @@ -45,6 +45,7 @@ public: KaleidoscopeJIT() : Resolver(createLegacyLookupResolver( + ES, [this](const std::string &Name) { return ObjectLayer.findSymbol(Name, true); }, diff --git a/llvm/include/llvm/ExecutionEngine/JITSymbol.h b/llvm/include/llvm/ExecutionEngine/JITSymbol.h index e2e7f72444a3..53037c3dbc72 100644 --- a/llvm/include/llvm/ExecutionEngine/JITSymbol.h +++ b/llvm/include/llvm/ExecutionEngine/JITSymbol.h @@ -174,6 +174,9 @@ public: /// Return the flags for this symbol. JITSymbolFlags getFlags() const { return Flags; } + /// Set the flags for this symbol. + void setFlags(JITSymbolFlags Flags) { this->Flags = std::move(Flags); } + private: JITTargetAddress Address = 0; JITSymbolFlags Flags; diff --git a/llvm/include/llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h b/llvm/include/llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h index 1239f7e0c2e2..f99dedc97fa5 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h @@ -490,10 +490,11 @@ private: return LD.BackingResolver->lookupFlags(SymbolFlags, *NotFoundViaLegacyLookup); }, - [&LD, LegacyLookup](std::shared_ptr Query, - SymbolNameSet Symbols) { + [this, &LD, + LegacyLookup](std::shared_ptr Query, + SymbolNameSet Symbols) { auto NotFoundViaLegacyLookup = - lookupWithLegacyFn(*Query, Symbols, LegacyLookup); + lookupWithLegacyFn(ES, *Query, Symbols, LegacyLookup); return LD.BackingResolver->lookup(Query, NotFoundViaLegacyLookup); }); @@ -647,10 +648,10 @@ private: return LD.BackingResolver->lookupFlags(SymbolFlags, *NotFoundViaLegacyLookup); }, - [&LD, LegacyLookup](std::shared_ptr Q, - SymbolNameSet Symbols) { + [this, &LD, LegacyLookup](std::shared_ptr Q, + SymbolNameSet Symbols) { auto NotFoundViaLegacyLookup = - lookupWithLegacyFn(*Q, Symbols, LegacyLookup); + lookupWithLegacyFn(ES, *Q, Symbols, LegacyLookup); return LD.BackingResolver->lookup(Q, std::move(NotFoundViaLegacyLookup)); }); diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Core.h b/llvm/include/llvm/ExecutionEngine/Orc/Core.h index 518de3fc6f98..c9a4d8300065 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/Core.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/Core.h @@ -27,6 +27,10 @@ namespace llvm { namespace orc { // Forward declare some classes. +class AsynchronousSymbolQuery; +class ExecutionSession; +class MaterializationUnit; +class MaterializationResponsibility; class VSO; /// VModuleKey provides a unique identifier (allocated and managed by @@ -55,6 +59,13 @@ raw_ostream &operator<<(raw_ostream &OS, const SymbolFlagsMap &Symbols); /// A base class for materialization failures that allows the failing /// symbols to be obtained for logging. +using SymbolDependenceMap = std::map; + +/// Render a SymbolDependendeMap. +raw_ostream &operator<<(raw_ostream &OS, const SymbolDependenceMap &Deps); + +/// A base class for materialization failures that allows the failing +/// symbols to be obtained for logging. class FailedToMaterialize : public ErrorInfo { public: static char ID; @@ -91,14 +102,252 @@ private: SymbolNameSet Symbols; }; +/// Tracks responsibility for materialization, and mediates interactions between +/// MaterializationUnits and VSOs. +/// +/// An instance of this class is passed to MaterializationUnits when their +/// materialize method is called. It allows MaterializationUnits to resolve and +/// finalize symbols, or abandon materialization by notifying any unmaterialized +/// symbols of an error. +class MaterializationResponsibility { +public: + /// Create a MaterializationResponsibility for the given VSO and + /// initial symbols. + MaterializationResponsibility(VSO &V, SymbolFlagsMap SymbolFlags); + + MaterializationResponsibility(MaterializationResponsibility &&) = default; + MaterializationResponsibility & + operator=(MaterializationResponsibility &&) = default; + + /// Destruct a MaterializationResponsibility instance. In debug mode + /// this asserts that all symbols being tracked have been either + /// finalized or notified of an error. + ~MaterializationResponsibility(); + + /// Returns the target VSO that these symbols are being materialized + /// into. + const VSO &getTargetVSO() const { return V; } + + /// Resolves the given symbols. Individual calls to this method may + /// resolve a subset of the symbols, but all symbols must have been + /// resolved prior to calling finalize. + void resolve(const SymbolMap &Symbols); + + /// Finalizes all symbols tracked by this instance. + void finalize(); + + /// Adds new symbols to the VSO and this responsibility instance. + /// VSO entries start out in the materializing state. + /// + /// This method can be used by materialization units that want to add + /// additional symbols at materialization time (e.g. stubs, compile + /// callbacks, metadata). + Error defineMaterializing(const SymbolFlagsMap &SymbolFlags); + + /// Notify all unfinalized symbols that an error has occurred. + /// This will remove all symbols covered by this MaterializationResponsibilty + /// from V, and send an error to any queries waiting on these symbols. + void failMaterialization(std::function GenerateError); + + /// Transfers responsibility to the given MaterializationUnit for all + /// symbols defined by that MaterializationUnit. This allows + /// materializers to break up work based on run-time information (e.g. + /// by introspecting which symbols have actually been looked up and + /// materializing only those). + void delegate(std::unique_ptr MU); + + /// Add dependencies for the symbols in this dylib. + void addDependencies(const SymbolDependenceMap &Dependencies); + +private: + VSO &V; + SymbolFlagsMap SymbolFlags; +}; + +/// A MaterializationUnit represents a set of symbol definitions that can +/// be materialized as a group, or individually discarded (when +/// overriding definitions are encountered). +/// +/// MaterializationUnits are used when providing lazy definitions of symbols to +/// VSOs. The VSO will call materialize when the address of a symbol is +/// requested via the lookup method. The VSO will call discard if a stronger +/// definition is added or already present. +class MaterializationUnit { +public: + MaterializationUnit(SymbolFlagsMap SymbolFlags) + : SymbolFlags(std::move(SymbolFlags)) {} + + virtual ~MaterializationUnit() {} + + /// Return the set of symbols that this source provides. + const SymbolFlagsMap &getSymbols() const { return SymbolFlags; } + + /// Called by materialization dispatchers (see + /// ExecutionSession::DispatchMaterializationFunction) to trigger + /// materialization of this MaterializationUnit. + void doMaterialize(VSO &V) { + materialize(MaterializationResponsibility(V, std::move(SymbolFlags))); + } + + /// Called by VSOs to notify MaterializationUnits that the given symbol has + /// been overridden. + void doDiscard(const VSO &V, SymbolStringPtr Name) { + SymbolFlags.erase(Name); + discard(V, std::move(Name)); + } + +private: + virtual void anchor(); + + /// Implementations of this method should materialize all symbols + /// in the materialzation unit, except for those that have been + /// previously discarded. + virtual void materialize(MaterializationResponsibility R) = 0; + + /// Implementations of this method should discard the given symbol + /// from the source (e.g. if the source is an LLVM IR Module and the + /// symbol is a function, delete the function body or mark it available + /// externally). + virtual void discard(const VSO &V, SymbolStringPtr Name) = 0; + + SymbolFlagsMap SymbolFlags; +}; + +/// A MaterializationUnit implementation for pre-existing absolute symbols. +/// +/// All symbols will be resolved and marked ready as soon as the unit is +/// materialized. +class AbsoluteSymbolsMaterializationUnit : public MaterializationUnit { +public: + AbsoluteSymbolsMaterializationUnit(SymbolMap Symbols); + +private: + void materialize(MaterializationResponsibility R) override; + void discard(const VSO &V, SymbolStringPtr Name) override; + static SymbolFlagsMap extractFlags(const SymbolMap &Symbols); + + SymbolMap Symbols; +}; + +/// Create an AbsoluteSymbolsMaterializationUnit with the given symbols. +/// Useful for inserting absolute symbols into a VSO. E.g.: +/// \code{.cpp} +/// VSO &V = ...; +/// SymbolStringPtr Foo = ...; +/// JITEvaluatedSymbol FooSym = ...; +/// if (auto Err = V.define(absoluteSymbols({{Foo, FooSym}}))) +/// return Err; +/// \endcode +/// +inline std::unique_ptr +absoluteSymbols(SymbolMap Symbols) { + return llvm::make_unique( + std::move(Symbols)); +} + +/// Base utilities for ExecutionSession. +class ExecutionSessionBase { +public: + /// For reporting errors. + using ErrorReporter = std::function; + + /// For dispatching MaterializationUnit::materialize calls. + using DispatchMaterializationFunction = + std::function MU)>; + + /// Construct an ExecutionSessionBase. + /// + /// SymbolStringPools may be shared between ExecutionSessions. + ExecutionSessionBase(std::shared_ptr SSP = nullptr) + : SSP(SSP ? std::move(SSP) : std::make_shared()) {} + + /// Returns the SymbolStringPool for this ExecutionSession. + SymbolStringPool &getSymbolStringPool() const { return *SSP; } + + /// Run the given lambda with the session mutex locked. + template auto runSessionLocked(Func &&F) -> decltype(F()) { + std::lock_guard Lock(SessionMutex); + return F(); + } + + /// Set the error reporter function. + ExecutionSessionBase &setErrorReporter(ErrorReporter ReportError) { + this->ReportError = std::move(ReportError); + return *this; + } + + /// Set the materialization dispatch function. + ExecutionSessionBase &setDispatchMaterialization( + DispatchMaterializationFunction DispatchMaterialization) { + this->DispatchMaterialization = std::move(DispatchMaterialization); + return *this; + } + + /// Report a error for this execution session. + /// + /// Unhandled errors can be sent here to log them. + void reportError(Error Err) { ReportError(std::move(Err)); } + + /// Allocate a module key for a new module to add to the JIT. + VModuleKey allocateVModule() { return ++LastKey; } + + /// Return a module key to the ExecutionSession so that it can be + /// re-used. This should only be done once all resources associated + /// with the original key have been released. + void releaseVModule(VModuleKey Key) { /* FIXME: Recycle keys */ + } + + /// Cause the given query to fail with the given Error. + /// + /// This should only be used by legacy APIs and will be deprecated in the + /// future. + void failQuery(AsynchronousSymbolQuery &Q, Error Err); + + /// Materialize the given unit. + void dispatchMaterialization(VSO &V, + std::unique_ptr MU) { + DispatchMaterialization(V, std::move(MU)); + } + +private: + static void logErrorsToStdErr(Error Err) { + logAllUnhandledErrors(std::move(Err), errs(), "JIT session error: "); + } + + static void + materializeOnCurrentThread(VSO &V, std::unique_ptr MU) { + MU->doMaterialize(V); + } + + mutable std::recursive_mutex SessionMutex; + std::shared_ptr SSP; + VModuleKey LastKey = 0; + ErrorReporter ReportError = logErrorsToStdErr; + DispatchMaterializationFunction DispatchMaterialization = + materializeOnCurrentThread; +}; + /// A symbol query that returns results via a callback when results are /// ready. /// /// makes a callback when all symbols are available. class AsynchronousSymbolQuery { + friend class ExecutionSessionBase; + friend class VSO; + public: + class ResolutionResult { + public: + ResolutionResult(SymbolMap Symbols, const SymbolDependenceMap &Dependencies) + : Symbols(std::move(Symbols)), Dependencies(Dependencies) {} + + SymbolMap Symbols; + const SymbolDependenceMap &Dependencies; + }; + /// Callback to notify client that symbols have been resolved. - using SymbolsResolvedCallback = std::function)>; + using SymbolsResolvedCallback = + std::function)>; /// Callback to notify client that symbols are ready for execution. using SymbolsReadyCallback = std::function; @@ -109,36 +358,45 @@ public: SymbolsResolvedCallback NotifySymbolsResolved, SymbolsReadyCallback NotifySymbolsReady); - /// Notify client that the query failed. - /// - /// If the notify-resolved callback has not been made yet, then it is called - /// with the given error, and the notify-finalized callback is never made. - /// - /// If the notify-resolved callback has already been made then then the - /// notify-finalized callback is called with the given error. - /// - /// It is illegal to call setFailed after both callbacks have been made. - void notifyMaterializationFailed(Error Err); - /// Set the resolved symbol information for the given symbol name. + void resolve(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. /// - /// If this symbol was the last one not resolved, this will trigger a call to - /// the notify-finalized callback passing the completed sybol map. - void resolve(SymbolStringPtr Name, JITEvaluatedSymbol Sym); + /// 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(); + + /// Returns true if all symbols covered by this query are ready. + bool isFullyReady() const { return NotYetReadyCount == 0; } + + /// Calls the NotifySymbolsReady callback. /// - /// This decrements the query's internal count of not-yet-ready symbols. If - /// this call to notifySymbolFinalized sets the counter to zero, it will call - /// the notify-finalized callback with Error::success as the value. - void finalizeSymbol(); + /// This should only be called if all symbols covered by this query are ready. + void handleFullyReady(); private: - SymbolMap Symbols; - size_t OutstandingResolutions = 0; - size_t OutstandingFinalizations = 0; + void addQueryDependence(VSO &V, SymbolStringPtr Name); + + void removeQueryDependence(VSO &V, const SymbolStringPtr &Name); + + void handleFailed(Error Err); + + void detach(); + SymbolsResolvedCallback NotifySymbolsResolved; SymbolsReadyCallback NotifySymbolsReady; + SymbolDependenceMap QueryRegistrations; + SymbolMap ResolvedSymbols; + size_t NotYetResolvedCount; + size_t NotYetReadyCount; }; /// SymbolResolver is a composable interface for looking up symbol flags @@ -206,311 +464,191 @@ createSymbolResolver(LookupFlagsFn &&LookupFlags, LookupFn &&Lookup) { std::forward(LookupFlags), std::forward(Lookup)); } -/// Tracks responsibility for materialization. +/// A symbol table that supports asynchoronous symbol queries. /// -/// An instance of this class is passed to MaterializationUnits when their -/// materialize method is called. It allows MaterializationUnits to resolve and -/// finalize symbols, or abandon materialization by notifying any unmaterialized -/// symbols of an error. -class MaterializationResponsibility { -public: - /// Create a MaterializationResponsibility for the given VSO and - /// initial symbols. - MaterializationResponsibility(VSO &V, SymbolFlagsMap SymbolFlags); - - MaterializationResponsibility(MaterializationResponsibility &&) = default; - MaterializationResponsibility & - operator=(MaterializationResponsibility &&) = default; - - /// Destruct a MaterializationResponsibility instance. In debug mode - /// this asserts that all symbols being tracked have been either - /// finalized or notified of an error. - ~MaterializationResponsibility(); - - /// Returns the target VSO that these symbols are being materialized - /// into. - const VSO &getTargetVSO() const { return V; } - - /// Resolves the given symbols. Individual calls to this method may - /// resolve a subset of the symbols, but all symbols must have been - /// resolved prior to calling finalize. - void resolve(const SymbolMap &Symbols); - - /// Finalizes all symbols tracked by this instance. - void finalize(); - - /// Notify all unfinalized symbols that an error has occurred. - /// This method should be called if materialization of any symbol is - /// abandoned. - void notifyMaterializationFailed(); - - /// Transfers responsibility for the given symbols to a new - /// MaterializationResponsibility class. This is useful if a - /// MaterializationUnit wants to transfer responsibility for a subset - /// of symbols to another MaterializationUnit or utility. - MaterializationResponsibility delegate(SymbolNameSet Symbols); - -private: - VSO &V; - SymbolFlagsMap SymbolFlags; -}; - -/// A MaterializationUnit represents a set of symbol definitions that can -/// be materialized as a group, or individually discarded (when -/// overriding definitions are encountered). -/// -/// MaterializationUnits are used when providing lazy definitions of symbols to -/// VSOs. The VSO will call materialize when the address of a symbol is -/// requested via the lookup method. The VSO will call discard if a stronger -/// definition is added or already present. -class MaterializationUnit { -public: - virtual ~MaterializationUnit() {} - - /// Return the set of symbols that this source provides. - virtual SymbolFlagsMap getSymbols() = 0; - - /// Implementations of this method should materialize all symbols - /// in the materialzation unit, except for those that have been - /// previously discarded. - virtual void materialize(MaterializationResponsibility R) = 0; - - /// Implementations of this method should discard the given symbol - /// from the source (e.g. if the source is an LLVM IR Module and the - /// symbol is a function, delete the function body or mark it available - /// externally). - virtual void discard(const VSO &V, SymbolStringPtr Name) = 0; - -private: - virtual void anchor(); -}; - -/// Represents a dynamic linkage unit in a JIT process. -/// -/// VSO acts as a symbol table (symbol definitions can be set and the dylib -/// queried to find symbol addresses) and as a key for tracking resources -/// (since a VSO's address is fixed). +/// Represents a virtual shared object. Instances can not be copied or moved, so +/// their addresses may be used as keys for resource management. +/// VSO state changes must be made via an ExecutionSession to guarantee that +/// they are synchronized with respect to other VSO operations. class VSO { + friend class AsynchronousSymbolQuery; friend class ExecutionSession; - friend class MaterializationResponsibility; - public: - enum RelativeLinkageStrength { - NewDefinitionIsStronger, - DuplicateDefinition, - ExistingDefinitionIsStronger - }; + using AsynchronousSymbolQuerySet = + std::set>; - using SetDefinitionsResult = - std::map; + using MaterializationUnitList = + std::vector>; - struct Materializer { - public: - Materializer(std::unique_ptr MU, - MaterializationResponsibility R); - void operator()(); - - private: - std::unique_ptr MU; - MaterializationResponsibility R; - }; - - using MaterializerList = std::vector; - - struct LookupResult { - MaterializerList Materializers; - SymbolNameSet UnresolvedSymbols; - }; - - VSO() = default; + using VSOList = std::vector; VSO(const VSO &) = delete; VSO &operator=(const VSO &) = delete; VSO(VSO &&) = delete; VSO &operator=(VSO &&) = delete; - /// Compare new linkage with existing linkage. - static RelativeLinkageStrength - compareLinkage(Optional OldFlags, JITSymbolFlags NewFlags); + /// Get the name for this VSO. + const std::string &getName() const { return VSOName; } - /// Compare new linkage with an existing symbol's linkage. - RelativeLinkageStrength compareLinkage(SymbolStringPtr Name, - JITSymbolFlags NewFlags) const; + /// Get a reference to the ExecutionSession for this VSO. + ExecutionSessionBase &getExecutionSession() const { return ES; } - /// Adds the given symbols to the mapping as resolved, finalized - /// symbols. + /// Define all symbols provided by the materialization unit to be part + /// of the given VSO. + template + typename std::enable_if< + std::is_convertible< + typename std::decay::type, + std::unique_ptr>::value, + Error>::type + define(UniquePtrToMaterializationUnit &&MU) { + return ES.runSessionLocked([&, this]() -> Error { + assert(MU && "Can't define with a null MU"); + + if (auto Err = defineImpl(*MU)) + return Err; + + /// defineImpl succeeded. + auto UMI = std::make_shared(std::move(MU)); + for (auto &KV : UMI->MU->getSymbols()) + UnmaterializedInfos[KV.first] = UMI; + + return Error::success(); + }); + } + + /// Define a set of symbols already in the materializing state. + Error defineMaterializing(const SymbolFlagsMap &SymbolFlags); + + /// Replace the definition of a set of materializing symbols with a new + /// MaterializationUnit. /// - /// FIXME: We can take this by const-ref once symbol-based laziness is - /// removed. - Error define(SymbolMap NewSymbols); + /// All symbols being replaced must be in the materializing state. If any + /// symbol being replaced has pending queries then the MU will be returned + /// for materialization. Otherwise it will be stored in the VSO and all + /// symbols covered by MU moved back to the lazy state. + void replace(std::unique_ptr MU); - /// Adds the given symbols to the mapping as lazy symbols. - Error defineLazy(std::unique_ptr Source); + /// Record dependencies between symbols in this VSO and symbols in + /// other VSOs. + void addDependencies(const SymbolFlagsMap &Dependents, + const SymbolDependenceMap &Dependencies); - /// Look up the flags for the given symbols. + /// Resolve the given symbols. /// - /// Returns the flags for the give symbols, together with the set of symbols - /// not found. - SymbolNameSet lookupFlags(SymbolFlagsMap &Flags, SymbolNameSet Symbols); - - /// Apply the given query to the given symbols in this VSO. - /// - /// For symbols in this VSO that have already been materialized, their address - /// will be set in the query immediately. - /// - /// For symbols in this VSO that have not been materialized, the query will be - /// recorded and the source for those symbols (plus the set of symbols to be - /// materialized by that source) will be returned as the MaterializationWork - /// field of the LookupResult. - /// - /// Any symbols not found in this VSO will be returned in the - /// UnresolvedSymbols field of the LookupResult. - LookupResult lookup(std::shared_ptr Query, - SymbolNameSet Symbols); - -private: - /// Add the given symbol/address mappings to the dylib, but do not - /// mark the symbols as finalized yet. - void resolve(const SymbolMap &SymbolValues); + /// Returns the list of queries that become fully resolved as a consequence of + /// this operation. + void resolve(const SymbolMap &Resolved); /// Finalize the given symbols. - void finalize(const SymbolNameSet &SymbolsToFinalize); + /// + /// Returns the list of queries that become fully ready as a consequence of + /// this operation. + void finalize(const SymbolFlagsMap &Finalized); - /// Notify the VSO that the given symbols failed to materialized. - void notifyMaterializationFailed(const SymbolNameSet &Names); + /// Fail to materialize the given symbols. + /// + /// Returns the list of queries that fail as a consequence. + void notifyFailed(const SymbolFlagsMap &Failed, + std::function GenerateError); - class UnmaterializedInfo { - public: - UnmaterializedInfo(std::unique_ptr MU); - void discard(VSO &V, SymbolStringPtr Name); + /// Search the given VSO for the symbols in Symbols. If found, store + /// the flags for each symbol in Flags. Returns any unresolved symbols. + SymbolNameSet lookupFlags(SymbolFlagsMap &Flags, const SymbolNameSet &Names); + + /// Search the given VSOs in order for the symbols in Symbols. Results + /// (once they become available) will be returned via the given Query. + /// + /// If any symbol is not found then the unresolved symbols will be returned, + /// and the query will not be applied. The Query is not failed and can be + /// re-used in a subsequent lookup once the symbols have been added, or + /// manually failed. + SymbolNameSet lookup(std::shared_ptr Q, + SymbolNameSet Names); + + /// Dump current VSO state to OS. + void dump(raw_ostream &OS); + +private: + using AsynchronousSymbolQueryList = + std::vector>; + + struct UnmaterializedInfo { + UnmaterializedInfo(std::unique_ptr MU) + : MU(std::move(MU)) {} std::unique_ptr MU; - SymbolFlagsMap Symbols; }; - using UnmaterializedInfoList = std::list; + using UnmaterializedInfosMap = + std::map>; - using UnmaterializedInfoIterator = UnmaterializedInfoList::iterator; - - class MaterializingInfo { - public: - using QueryList = std::vector>; - - QueryList PendingResolution; - QueryList PendingFinalization; + struct MaterializingInfo { + AsynchronousSymbolQueryList PendingQueries; + SymbolDependenceMap Dependants; + SymbolDependenceMap UnfinalizedDependencies; + bool IsFinalized = false; }; - using MaterializingInfoMap = std::map; + using MaterializingInfosMap = std::map; - using MaterializingInfoIterator = MaterializingInfoMap::iterator; + VSO(ExecutionSessionBase &ES, std::string Name) + : ES(ES), VSOName(std::move(Name)) {} - class SymbolTableEntry { - public: - SymbolTableEntry(JITSymbolFlags SymbolFlags, - UnmaterializedInfoIterator UnmaterializedInfoItr); - SymbolTableEntry(JITSymbolFlags SymbolFlags); - SymbolTableEntry(JITEvaluatedSymbol Sym); - SymbolTableEntry(SymbolTableEntry &&Other); - SymbolTableEntry &operator=(SymbolTableEntry &&Other); - ~SymbolTableEntry(); + ExecutionSessionBase &ES; + std::string VSOName; + SymbolMap Symbols; + UnmaterializedInfosMap UnmaterializedInfos; + MaterializingInfosMap MaterializingInfos; - // Change definition due to override. Only usable prior to materialization. - void replaceWith(VSO &V, SymbolStringPtr Name, JITEvaluatedSymbol Sym); + Error defineImpl(MaterializationUnit &MU); - // Change definition due to override. Only usable prior to materialization. - void replaceWith(VSO &V, SymbolStringPtr Name, JITSymbolFlags Flags, - UnmaterializedInfoIterator NewUMII); + void detachQueryHelper(AsynchronousSymbolQuery &Q, + const SymbolNameSet &QuerySymbols); - // Abandon old definition and move to materializing state. - // There is no need to call notifyMaterializing after this. - void replaceMaterializing(VSO &V, SymbolStringPtr Name, - JITSymbolFlags NewFlags); - - // Notify this entry that it is being materialized. - void notifyMaterializing(); - - // Move entry to resolved state. - void resolve(VSO &V, JITEvaluatedSymbol Sym); - - // Move entry to finalized state. - void finalize(); - - JITSymbolFlags Flags; - - union { - JITTargetAddress Address; - UnmaterializedInfoIterator UMII; - }; - - private: - void destroy(); - }; - - std::map Symbols; - UnmaterializedInfoList UnmaterializedInfos; - MaterializingInfoMap MaterializingInfos; + void transferFinalizedNodeDependencies(MaterializingInfo &DependantMI, + const SymbolStringPtr &DependantName, + MaterializingInfo &FinalizedMI); }; /// An ExecutionSession represents a running JIT program. -class ExecutionSession { +class ExecutionSession : public ExecutionSessionBase { public: using ErrorReporter = std::function; + using DispatchMaterializationFunction = + std::function MU)>; + /// Construct an ExecutionEngine. /// /// SymbolStringPools may be shared between ExecutionSessions. ExecutionSession(std::shared_ptr SSP = nullptr) - : SSP(SSP ? std::move(SSP) : std::make_shared()) {} + : ExecutionSessionBase(std::move(SSP)) {} - /// Returns the SymbolStringPool for this ExecutionSession. - SymbolStringPool &getSymbolStringPool() const { return *SSP; } + /// Add a new VSO to this ExecutionSession. + VSO &createVSO(std::string Name); - /// Set the error reporter function. - void setErrorReporter(ErrorReporter ReportError) { - this->ReportError = std::move(ReportError); - } - - /// Report a error for this execution session. - /// - /// Unhandled errors can be sent here to log them. - void reportError(Error Err) { ReportError(std::move(Err)); } - - /// Allocate a module key for a new module to add to the JIT. - VModuleKey allocateVModule() { return ++LastKey; } - - /// Return a module key to the ExecutionSession so that it can be - /// re-used. This should only be done once all resources associated - //// with the original key have been released. - void releaseVModule(VModuleKey Key) { /* FIXME: Recycle keys */ } - -public: - static void logErrorsToStdErr(Error Err); - - std::shared_ptr SSP; - VModuleKey LastKey = 0; - ErrorReporter ReportError = logErrorsToStdErr; +private: + std::vector> VSOs; }; -/// Runs Materializers on the current thread and reports errors to the given -/// ExecutionSession. -class MaterializeOnCurrentThread { -public: - void operator()(VSO::Materializer M) { M(); } -}; - -/// Materialization function object wrapper for the lookup method. -using MaterializationDispatcher = std::function; - -/// Look up a set of symbols by searching a list of VSOs. +/// Look up the given names in the given VSOs. +/// VSOs will be searched in order and no VSO pointer may be null. +/// All symbols must be found within the given VSOs or an error +/// will be returned. /// -/// All VSOs in the list should be non-null. +/// If this lookup is being performed on behalf of a +/// MaterializationResponsibility then it must be passed in as R +/// (in order to record the symbol dependencies). +/// If this lookup is not being performed on behalf of a +/// MaterializationResponsibility then R should be left null. Expected lookup(const std::vector &VSOs, SymbolNameSet Names, - MaterializationDispatcher DispatchMaterialization); + MaterializationResponsibility *R); /// Look up a symbol by searching a list of VSOs. -Expected -lookup(const std::vector VSOs, SymbolStringPtr Name, - MaterializationDispatcher DispatchMaterialization); +Expected lookup(const std::vector VSOs, + SymbolStringPtr Name, + MaterializationResponsibility *R); } // End namespace orc } // End namespace llvm diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Legacy.h b/llvm/include/llvm/ExecutionEngine/Orc/Legacy.h index 4115f49a9cb3..343daa484322 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/Legacy.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/Legacy.h @@ -22,7 +22,8 @@ namespace orc { class JITSymbolResolverAdapter : public JITSymbolResolver { public: - JITSymbolResolverAdapter(ExecutionSession &ES, SymbolResolver &R); + JITSymbolResolverAdapter(ExecutionSession &ES, SymbolResolver &R, + MaterializationResponsibility *MR); Expected lookupFlags(const LookupSet &Symbols) override; Expected lookup(const LookupSet &Symbols) override; @@ -30,6 +31,7 @@ private: ExecutionSession &ES; std::set ResolvedStrings; SymbolResolver &R; + MaterializationResponsibility *MR; }; /// Use the given legacy-style FindSymbol function (i.e. a function that @@ -68,27 +70,35 @@ Expected lookupFlagsWithLegacyFn(SymbolFlagsMap &SymbolFlags, /// /// Useful for implementing lookup bodies that query legacy resolvers. template -SymbolNameSet lookupWithLegacyFn(AsynchronousSymbolQuery &Query, - const SymbolNameSet &Symbols, - FindSymbolFn FindSymbol) { +SymbolNameSet +lookupWithLegacyFn(ExecutionSession &ES, AsynchronousSymbolQuery &Query, + const SymbolNameSet &Symbols, FindSymbolFn FindSymbol) { SymbolNameSet SymbolsNotFound; + bool NewSymbolsResolved = false; for (auto &S : Symbols) { if (JITSymbol Sym = FindSymbol(*S)) { if (auto Addr = Sym.getAddress()) { Query.resolve(S, JITEvaluatedSymbol(*Addr, Sym.getFlags())); - Query.finalizeSymbol(); + Query.notifySymbolReady(); + NewSymbolsResolved = true; } else { - Query.notifyMaterializationFailed(Addr.takeError()); + ES.failQuery(Query, Addr.takeError()); return SymbolNameSet(); } } else if (auto Err = Sym.takeError()) { - Query.notifyMaterializationFailed(std::move(Err)); + ES.failQuery(Query, std::move(Err)); return SymbolNameSet(); } else SymbolsNotFound.insert(S); } + if (NewSymbolsResolved && Query.isFullyResolved()) + Query.handleFullyResolved(); + + if (NewSymbolsResolved && Query.isFullyReady()) + Query.handleFullyReady(); + return SymbolsNotFound; } @@ -99,8 +109,9 @@ class LegacyLookupFnResolver final : public SymbolResolver { public: using ErrorReporter = std::function; - LegacyLookupFnResolver(LegacyLookupFn LegacyLookup, ErrorReporter ReportError) - : LegacyLookup(std::move(LegacyLookup)), + LegacyLookupFnResolver(ExecutionSession &ES, LegacyLookupFn LegacyLookup, + ErrorReporter ReportError) + : ES(ES), LegacyLookup(std::move(LegacyLookup)), ReportError(std::move(ReportError)) {} SymbolNameSet lookupFlags(SymbolFlagsMap &Flags, @@ -116,20 +127,21 @@ public: SymbolNameSet lookup(std::shared_ptr Query, SymbolNameSet Symbols) final { - return lookupWithLegacyFn(*Query, Symbols, LegacyLookup); + return lookupWithLegacyFn(ES, *Query, Symbols, LegacyLookup); } private: + ExecutionSession &ES; LegacyLookupFn LegacyLookup; ErrorReporter ReportError; }; template std::shared_ptr> -createLegacyLookupResolver(LegacyLookupFn LegacyLookup, +createLegacyLookupResolver(ExecutionSession &ES, LegacyLookupFn LegacyLookup, std::function ErrorReporter) { return std::make_shared>( - std::move(LegacyLookup), std::move(ErrorReporter)); + ES, std::move(LegacyLookup), std::move(ErrorReporter)); } } // End namespace orc diff --git a/llvm/include/llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h b/llvm/include/llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h index 59f2a07d31e2..6f2cf50c36b4 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h @@ -124,7 +124,8 @@ private: Error finalize() override { assert(PFC && "mapSectionAddress called on finalized LinkedObject"); - JITSymbolResolverAdapter ResolverAdapter(PFC->Parent.ES, *PFC->Resolver); + JITSymbolResolverAdapter ResolverAdapter(PFC->Parent.ES, *PFC->Resolver, + nullptr); PFC->RTDyld = llvm::make_unique(*MemMgr, ResolverAdapter); PFC->RTDyld->setProcessAllSections(PFC->ProcessAllSections); diff --git a/llvm/lib/ExecutionEngine/Orc/Core.cpp b/llvm/lib/ExecutionEngine/Orc/Core.cpp index b2b53471ee11..94016c2b4ca3 100644 --- a/llvm/lib/ExecutionEngine/Orc/Core.cpp +++ b/llvm/lib/ExecutionEngine/Orc/Core.cpp @@ -76,7 +76,7 @@ raw_ostream &operator<<(raw_ostream &OS, const SymbolMap &Symbols) { raw_ostream &operator<<(raw_ostream &OS, const SymbolFlagsMap &SymbolFlags) { OS << "{"; - if (SymbolFlags.empty()) { + if (!SymbolFlags.empty()) { OS << " {\"" << *SymbolFlags.begin()->first << "\": " << SymbolFlags.begin()->second << "}"; for (auto &KV : @@ -87,6 +87,18 @@ raw_ostream &operator<<(raw_ostream &OS, const SymbolFlagsMap &SymbolFlags) { 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"); @@ -113,57 +125,101 @@ 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)) { - assert(this->NotifySymbolsResolved && - "Symbols resolved callback must be set"); - assert(this->NotifySymbolsReady && "Symbols ready callback must be set"); - OutstandingResolutions = OutstandingFinalizations = Symbols.size(); + NotYetResolvedCount = NotYetReadyCount = Symbols.size(); + + for (auto &S : Symbols) + ResolvedSymbols[S] = nullptr; } -void AsynchronousSymbolQuery::notifyMaterializationFailed(Error Err) { - if (OutstandingResolutions != 0) - NotifySymbolsResolved(std::move(Err)); - else if (OutstandingFinalizations != 0) - NotifySymbolsReady(std::move(Err)); - else - consumeError(std::move(Err)); - OutstandingResolutions = OutstandingFinalizations = 0; -} - -void AsynchronousSymbolQuery::resolve(SymbolStringPtr Name, +void AsynchronousSymbolQuery::resolve(const SymbolStringPtr &Name, JITEvaluatedSymbol Sym) { - // If OutstandingResolutions is zero we must have errored out already. Just - // ignore this. - if (OutstandingResolutions == 0) - return; - - assert(!Symbols.count(Name) && "Symbol has already been assigned an address"); - Symbols.insert(std::make_pair(std::move(Name), std::move(Sym))); - --OutstandingResolutions; - if (OutstandingResolutions == 0) - NotifySymbolsResolved(std::move(Symbols)); + 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::finalizeSymbol() { - // If OutstandingFinalizations is zero we must have errored out already. Just - // ignore this. - if (OutstandingFinalizations == 0) - return; +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(); +} - assert(OutstandingFinalizations > 0 && "All symbols already finalized"); - --OutstandingFinalizations; - if (OutstandingFinalizations == 0) - NotifySymbolsReady(Error::success()); +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() { @@ -172,441 +228,634 @@ MaterializationResponsibility::~MaterializationResponsibility() { } void MaterializationResponsibility::resolve(const SymbolMap &Symbols) { -#ifndef NDEBUG 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"); } -#endif + V.resolve(Symbols); } void MaterializationResponsibility::finalize() { - SymbolNameSet SymbolNames; +#ifndef NDEBUG for (auto &KV : SymbolFlags) - SymbolNames.insert(KV.first); + assert(!KV.second.isMaterializing() && + "Failed to resolve symbol before finalization"); +#endif // NDEBUG + + V.finalize(SymbolFlags); SymbolFlags.clear(); - V.finalize(SymbolNames); } -void MaterializationResponsibility::notifyMaterializationFailed() { - SymbolNameSet SymbolNames; - for (auto &KV : SymbolFlags) - SymbolNames.insert(KV.first); +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(); - V.notifyMaterializationFailed(SymbolNames); } -MaterializationResponsibility -MaterializationResponsibility::delegate(SymbolNameSet Symbols) { - SymbolFlagsMap ExtractedFlags; +void MaterializationResponsibility::delegate( + std::unique_ptr MU) { + for (auto &KV : MU->getSymbols()) + SymbolFlags.erase(KV.first); - for (auto &S : Symbols) { - auto I = SymbolFlags.find(S); - ExtractedFlags.insert(*I); - SymbolFlags.erase(I); - } - - return MaterializationResponsibility(V, std::move(ExtractedFlags)); + V.replace(std::move(MU)); } -VSO::Materializer::Materializer(std::unique_ptr MU, - MaterializationResponsibility R) - : MU(std::move(MU)), R(std::move(R)) {} - -void VSO::Materializer::operator()() { MU->materialize(std::move(R)); } - -VSO::UnmaterializedInfo::UnmaterializedInfo( - std::unique_ptr MU) - : MU(std::move(MU)), Symbols(this->MU->getSymbols()) {} - -void VSO::UnmaterializedInfo::discard(VSO &V, SymbolStringPtr Name) { - assert(MU && "No materializer attached"); - MU->discard(V, Name); - auto I = Symbols.find(Name); - assert(I != Symbols.end() && "Symbol not found in this MU"); - Symbols.erase(I); +void MaterializationResponsibility::addDependencies( + const SymbolDependenceMap &Dependencies) { + V.addDependencies(SymbolFlags, Dependencies); } -VSO::SymbolTableEntry::SymbolTableEntry(JITSymbolFlags Flags, - UnmaterializedInfoIterator UMII) - : Flags(Flags), UMII(std::move(UMII)) { - // We *don't* expect isLazy to be set here. That's for the VSO to do. - assert(!Flags.isLazy() && "Initial flags include lazy?"); - assert(!Flags.isMaterializing() && "Initial flags include materializing"); - this->Flags |= JITSymbolFlags::Lazy; +AbsoluteSymbolsMaterializationUnit::AbsoluteSymbolsMaterializationUnit( + SymbolMap Symbols) + : MaterializationUnit(extractFlags(Symbols)), Symbols(std::move(Symbols)) {} + +void AbsoluteSymbolsMaterializationUnit::materialize( + MaterializationResponsibility R) { + R.resolve(Symbols); + R.finalize(); } -VSO::SymbolTableEntry::SymbolTableEntry(JITSymbolFlags Flags) - : Flags(Flags), Address(0) { - // We *don't* expect isMaterializing to be set here. That's for the VSO to do. - assert(!Flags.isLazy() && "Initial flags include lazy?"); - assert(!Flags.isMaterializing() && "Initial flags include materializing"); - this->Flags |= JITSymbolFlags::Materializing; +void AbsoluteSymbolsMaterializationUnit::discard(const VSO &V, + SymbolStringPtr Name) { + assert(Symbols.count(Name) && "Symbol is not part of this MU"); + Symbols.erase(Name); } -VSO::SymbolTableEntry::SymbolTableEntry(JITEvaluatedSymbol Sym) - : Flags(Sym.getFlags()), Address(Sym.getAddress()) { - assert(!Flags.isLazy() && !Flags.isMaterializing() && - "This constructor is for final symbols only"); +SymbolFlagsMap +AbsoluteSymbolsMaterializationUnit::extractFlags(const SymbolMap &Symbols) { + SymbolFlagsMap Flags; + for (const auto &KV : Symbols) + Flags[KV.first] = KV.second.getFlags(); + return Flags; } -VSO::SymbolTableEntry::SymbolTableEntry(SymbolTableEntry &&Other) - : Flags(Other.Flags), Address(0) { - if (this->Flags.isLazy()) - UMII = std::move(Other.UMII); - else - Address = Other.Address; -} +Error VSO::defineMaterializing(const SymbolFlagsMap &SymbolFlags) { + return ES.runSessionLocked([&]() -> Error { + std::vector AddedSyms; -VSO::SymbolTableEntry &VSO::SymbolTableEntry:: -operator=(SymbolTableEntry &&Other) { - destroy(); - Flags = std::move(Other.Flags); - if (Other.Flags.isLazy()) { - UMII = std::move(Other.UMII); - } else - Address = Other.Address; - return *this; -} + for (auto &KV : SymbolFlags) { + SymbolMap::iterator EntryItr; + bool Added; -VSO::SymbolTableEntry::~SymbolTableEntry() { destroy(); } + auto NewFlags = KV.second; + NewFlags |= JITSymbolFlags::Materializing; -void VSO::SymbolTableEntry::replaceWith(VSO &V, SymbolStringPtr Name, - JITEvaluatedSymbol Sym) { - assert(!Flags.isMaterializing() && - "Attempting to replace definition during materialization?"); - if (Flags.isLazy()) { - UMII->discard(V, Name); - if (UMII->Symbols.empty()) - V.UnmaterializedInfos.erase(UMII); - } - destroy(); - Flags = Sym.getFlags(); - Address = Sym.getAddress(); -} + std::tie(EntryItr, Added) = Symbols.insert( + std::make_pair(KV.first, JITEvaluatedSymbol(0, NewFlags))); -void VSO::SymbolTableEntry::replaceWith(VSO &V, SymbolStringPtr Name, - JITSymbolFlags NewFlags, - UnmaterializedInfoIterator NewUMII) { - assert(!Flags.isMaterializing() && - "Attempting to replace definition during materialization?"); - if (Flags.isLazy()) { - UMII->discard(V, Name); - if (UMII->Symbols.empty()) - V.UnmaterializedInfos.erase(UMII); - } - destroy(); - Flags = NewFlags; - UMII = std::move(NewUMII); -} + if (Added) + AddedSyms.push_back(EntryItr); + else { + // Remove any symbols already added. + for (auto &SI : AddedSyms) + Symbols.erase(SI); -void VSO::SymbolTableEntry::replaceMaterializing(VSO &V, SymbolStringPtr Name, - JITSymbolFlags NewFlags) { - assert(!NewFlags.isWeak() && - "Can't define a lazy symbol in materializing mode"); - assert(!NewFlags.isLazy() && !NewFlags.isMaterializing() && - "Flags should not be in lazy or materializing state"); - if (Flags.isLazy()) { - UMII->discard(V, Name); - if (UMII->Symbols.empty()) - V.UnmaterializedInfos.erase(UMII); - } - destroy(); - Flags = NewFlags; - Flags |= JITSymbolFlags::Materializing; - Address = 0; -} - -void VSO::SymbolTableEntry::notifyMaterializing() { - assert(Flags.isLazy() && "Can only start materializing from lazy state"); - UMII.~UnmaterializedInfoIterator(); - Flags &= ~JITSymbolFlags::Lazy; - Flags |= JITSymbolFlags::Materializing; - Address = 0; -} - -void VSO::SymbolTableEntry::resolve(VSO &V, JITEvaluatedSymbol Sym) { - assert(!Flags.isLazy() && Flags.isMaterializing() && - "Can only resolve in materializing state"); - Flags = Sym.getFlags(); - Flags |= JITSymbolFlags::Materializing; - Address = Sym.getAddress(); - assert(Address != 0 && "Can not resolve to null"); -} - -void VSO::SymbolTableEntry::finalize() { - assert(Address != 0 && "Cannot finalize with null address"); - assert(Flags.isMaterializing() && !Flags.isLazy() && - "Symbol should be in materializing state"); - Flags &= ~JITSymbolFlags::Materializing; -} - -void VSO::SymbolTableEntry::destroy() { - if (Flags.isLazy()) - UMII.~UnmaterializedInfoIterator(); -} - -VSO::RelativeLinkageStrength VSO::compareLinkage(Optional Old, - JITSymbolFlags New) { - if (Old == None) - return llvm::orc::VSO::NewDefinitionIsStronger; - - if (Old->isStrong()) { - if (New.isStrong()) - return llvm::orc::VSO::DuplicateDefinition; - else - return llvm::orc::VSO::ExistingDefinitionIsStronger; - } else { - if (New.isStrong()) - return llvm::orc::VSO::NewDefinitionIsStronger; - else - return llvm::orc::VSO::ExistingDefinitionIsStronger; - } -} - -VSO::RelativeLinkageStrength -VSO::compareLinkage(SymbolStringPtr Name, JITSymbolFlags NewFlags) const { - auto I = Symbols.find(Name); - return compareLinkage( - I == Symbols.end() ? None : Optional(I->second.Flags), - NewFlags); -} - -Error VSO::define(SymbolMap NewSymbols) { - Error Err = Error::success(); - for (auto &KV : NewSymbols) { - auto I = Symbols.find(KV.first); - auto LinkageResult = compareLinkage( - I == Symbols.end() ? None : Optional(I->second.Flags), - KV.second.getFlags()); - - // Silently discard weaker definitions. - if (LinkageResult == ExistingDefinitionIsStronger) - continue; - - // Report duplicate definition errors. - if (LinkageResult == DuplicateDefinition) { - Err = joinErrors(std::move(Err), - make_error(*KV.first)); - continue; + // FIXME: Return all duplicates. + return make_error(*KV.first); + } } - if (I != Symbols.end()) - I->second.replaceWith(*this, I->first, KV.second); - else - Symbols.insert(std::make_pair(KV.first, std::move(KV.second))); - } - return Err; + return Error::success(); + }); } -Error VSO::defineLazy(std::unique_ptr MU) { - auto UMII = UnmaterializedInfos.insert(UnmaterializedInfos.end(), - UnmaterializedInfo(std::move(MU))); +void VSO::replace(std::unique_ptr MU) { + assert(MU != nullptr && "Can not replace with a null MaterializationUnit"); - Error Err = Error::success(); - for (auto &KV : UMII->Symbols) { - auto I = Symbols.find(KV.first); + auto MustRunMU = + ES.runSessionLocked([&, this]() -> std::unique_ptr { - assert((I == Symbols.end() || - !I->second.Flags.isMaterializing()) && - "Attempt to replace materializing symbol definition"); +#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 - auto LinkageResult = compareLinkage( - I == Symbols.end() ? None : Optional(I->second.Flags), - KV.second); + // 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); - // Discard weaker definitions. - if (LinkageResult == ExistingDefinitionIsStronger) { - UMII->discard(*this, KV.first); - continue; + // 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); + } } - // Report duplicate definition errors. - if (LinkageResult == DuplicateDefinition) { - Err = joinErrors(std::move(Err), - make_error(*KV.first)); - // Duplicate definitions are discarded, so remove the duplicates from - // materializer. - UMII->discard(*this, KV.first); - continue; + 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); + } } - // Existing definition was weaker. Replace it. - if (I != Symbols.end()) - I->second.replaceWith(*this, KV.first, KV.second, UMII); - else - Symbols.insert( - std::make_pair(KV.first, SymbolTableEntry(KV.second, UMII))); + return ReadyQueries; + }); + + for (auto &Q : FullyReadyQueries) { + assert(Q->isFullyReady() && "Q is not fully ready"); + Q->handleFullyReady(); } - - if (UMII->Symbols.empty()) - UnmaterializedInfos.erase(UMII); - - return Err; } -SymbolNameSet VSO::lookupFlags(SymbolFlagsMap &Flags, SymbolNameSet Names) { +void VSO::notifyFailed(const SymbolFlagsMap &Failed, + std::function GenerateError) { + auto FailedQueriesToNotify = ES.runSessionLocked([&, this]() { + AsynchronousSymbolQuerySet FailedQueries; - for (SymbolNameSet::iterator I = Names.begin(), E = Names.end(); I != E;) { - auto Tmp = I++; - auto SymI = Symbols.find(*Tmp); + for (auto &KV : Failed) { + const auto &Name = KV.first; - // If the symbol isn't in this dylib then just continue. - if (SymI == Symbols.end()) - continue; + auto I = Symbols.find(Name); + assert(I != Symbols.end() && "Symbol not present in this VSO"); + Symbols.erase(I); - Names.erase(Tmp); + auto MII = MaterializingInfos.find(Name); - Flags[SymI->first] = - JITSymbolFlags::stripTransientFlags(SymI->second.Flags); - } + // If we have not created a MaterializingInfo for this symbol yet then + // there is nobody to notify. + if (MII == MaterializingInfos.end()) + continue; - return Names; + // 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()); } -VSO::LookupResult VSO::lookup(std::shared_ptr Query, - SymbolNameSet Names) { - MaterializerList Materializers; +SymbolNameSet VSO::lookupFlags(SymbolFlagsMap &Flags, + const SymbolNameSet &Names) { + return ES.runSessionLocked([&, this]() { + SymbolNameSet Unresolved; - for (SymbolNameSet::iterator I = Names.begin(), E = Names.end(); I != E;) { - auto Tmp = I++; - auto SymI = Symbols.find(*Tmp); - - // If the symbol isn't in this dylib then just continue. - if (SymI == Symbols.end()) - continue; - - // The symbol is in the VSO. Erase it from Names and proceed. - Names.erase(Tmp); - - // If this symbol has not been materialized yet grab its materializer, - // move all of its sibling symbols to the materializing state, and - // delete its unmaterialized info. - if (SymI->second.Flags.isLazy()) { - assert(SymI->second.UMII->MU && - "Lazy symbol has no materializer attached"); - auto MU = std::move(SymI->second.UMII->MU); - auto SymbolFlags = std::move(SymI->second.UMII->Symbols); - UnmaterializedInfos.erase(SymI->second.UMII); - - for (auto &KV : SymbolFlags) { - auto SiblingI = Symbols.find(KV.first); - MaterializingInfos.insert( - std::make_pair(SiblingI->first, MaterializingInfo())); - SiblingI->second.notifyMaterializing(); + for (auto &Name : Names) { + auto I = Symbols.find(Name); + if (I == Symbols.end()) { + Unresolved.insert(Name); + continue; } - Materializers.push_back(Materializer( - std::move(MU), - MaterializationResponsibility(*this, std::move(SymbolFlags)))); + assert(!Flags.count(Name) && "Symbol already present in Flags map"); + Flags[Name] = JITSymbolFlags::stripTransientFlags(I->second.getFlags()); } - // If this symbol already has a fully materialized value, just use it. - if (!SymI->second.Flags.isMaterializing()) { - Query->resolve(SymI->first, JITEvaluatedSymbol(SymI->second.Address, - SymI->second.Flags)); - Query->finalizeSymbol(); - continue; - } - - // If this symbol is materializing, then get (or create) its - // MaterializingInfo struct and appaend the query. - auto J = MaterializingInfos.find(SymI->first); - assert(J != MaterializingInfos.end() && "Missing MaterializingInfo"); - - if (SymI->second.Address) { - auto Sym = JITEvaluatedSymbol(SymI->second.Address, SymI->second.Flags); - Query->resolve(SymI->first, Sym); - assert(J->second.PendingResolution.empty() && - "Queries still pending resolution on resolved symbol?"); - J->second.PendingFinalization.push_back(Query); - } else { - assert(J->second.PendingFinalization.empty() && - "Queries pendiing finalization on unresolved symbol?"); - J->second.PendingResolution.push_back(Query); - } - } - - return {std::move(Materializers), std::move(Names)}; + return Unresolved; + }); } -void VSO::resolve(const SymbolMap &SymbolValues) { - for (auto &KV : SymbolValues) { - auto I = Symbols.find(KV.first); - assert(I != Symbols.end() && "Resolving symbol not present in this dylib"); - I->second.resolve(*this, KV.second); +SymbolNameSet VSO::lookup(std::shared_ptr Q, + SymbolNameSet Names) { + SymbolNameSet Unresolved = std::move(Names); + std::vector> MUs; - auto J = MaterializingInfos.find(KV.first); - if (J == MaterializingInfos.end()) - continue; + ES.runSessionLocked([&, this]() { + for (auto I = Unresolved.begin(), E = Unresolved.end(); I != E;) { + auto TmpI = I++; + auto Name = *TmpI; - assert(J->second.PendingFinalization.empty() && - "Queries already pending finalization?"); - for (auto &Q : J->second.PendingResolution) - Q->resolve(KV.first, KV.second); - J->second.PendingFinalization = std::move(J->second.PendingResolution); - J->second.PendingResolution = MaterializingInfo::QueryList(); - } -} + // Search for the name in Symbols. Skip it if not found. + auto SymI = Symbols.find(Name); + if (SymI == Symbols.end()) + continue; -void VSO::notifyMaterializationFailed(const SymbolNameSet &Names) { - assert(!Names.empty() && "Failed to materialize empty set?"); + // If we found Name in V, remove it frome the Unresolved set and add it + // to the dependencies set. + Unresolved.erase(TmpI); - std::map, SymbolNameSet> - ResolutionFailures; - std::map, SymbolNameSet> - FinalizationFailures; + // If the symbol has an address then resolve it. + if (SymI->second.getAddress() != 0) + Q->resolve(Name, SymI->second); - for (auto &S : Names) { - auto I = Symbols.find(S); - assert(I != Symbols.end() && "Symbol not present in this VSO"); + // 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"); - auto J = MaterializingInfos.find(S); - if (J != MaterializingInfos.end()) { - if (J->second.PendingFinalization.empty()) { - for (auto &Q : J->second.PendingResolution) - ResolutionFailures[Q].insert(S); - } else { - for (auto &Q : J->second.PendingFinalization) - FinalizationFailures[Q].insert(S); + // 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; } - MaterializingInfos.erase(J); + + // 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); } - Symbols.erase(I); - } + }); - for (auto &KV : ResolutionFailures) - KV.first->notifyMaterializationFailed( - make_error(std::move(KV.second))); + if (Q->isFullyResolved()) + Q->handleFullyResolved(); - for (auto &KV : FinalizationFailures) - KV.first->notifyMaterializationFailed( - make_error(std::move(KV.second))); + 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::finalize(const SymbolNameSet &SymbolsToFinalize) { - for (auto &S : SymbolsToFinalize) { - auto I = Symbols.find(S); - assert(I != Symbols.end() && "Finalizing symbol not present in this dylib"); +void VSO::dump(raw_ostream &OS) { + ES.runSessionLocked([&, this]() { + OS << "VSO \"" << VSOName + << "\" (ES: " << format("0x%016x", reinterpret_cast(&ES)) + << "):\n" + << "Symbol table:\n"; - auto J = MaterializingInfos.find(S); - if (J != MaterializingInfos.end()) { - assert(J->second.PendingResolution.empty() && - "Queries still pending resolution?"); - for (auto &Q : J->second.PendingFinalization) - Q->finalizeSymbol(); - MaterializingInfos.erase(J); + 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); } - I->second.finalize(); } + + 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, - MaterializationDispatcher DispatchMaterialization) { + MaterializationResponsibility *R) { #if LLVM_ENABLE_THREADS // In the threaded case we use promises to return the results. std::promise PromisedResult; @@ -614,18 +863,21 @@ Expected lookup(const std::vector &VSOs, SymbolNameSet Names, Error ResolutionError = Error::success(); std::promise PromisedReady; Error ReadyError = Error::success(); - auto OnResolve = [&](Expected Result) { - if (Result) - PromisedResult.set_value(std::move(*Result)); - else { - { - ErrorAsOutParameter _(&ResolutionError); - std::lock_guard Lock(ErrMutex); - ResolutionError = Result.takeError(); - } - PromisedResult.set_value(SymbolMap()); - } - }; + 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); @@ -639,12 +891,14 @@ Expected lookup(const std::vector &VSOs, SymbolNameSet Names, Error ResolutionError = Error::success(); Error ReadyError = Error::success(); - auto OnResolve = [&](Expected R) { + auto OnResolve = [&](Expected RR) { ErrorAsOutParameter _(&ResolutionError); - if (R) - Result = std::move(*R); - else - ResolutionError = R.takeError(); + if (RR) { + if (R) + R->addDependencies(RR->Dependencies); + Result = std::move(RR->Symbols); + } else + ResolutionError = RR.takeError(); }; auto OnReady = [&](Error Err) { ErrorAsOutParameter _(&ReadyError); @@ -658,18 +912,14 @@ Expected lookup(const std::vector &VSOs, SymbolNameSet Names, SymbolNameSet UnresolvedSymbols(std::move(Names)); for (auto *V : VSOs) { - + assert(V && "VSO pointers in VSOs list should be non-null"); if (UnresolvedSymbols.empty()) break; - - assert(V && "VSO pointers in VSOs list should be non-null"); - auto LR = V->lookup(Query, UnresolvedSymbols); - UnresolvedSymbols = std::move(LR.UnresolvedSymbols); - - for (auto &M : LR.Materializers) - DispatchMaterialization(std::move(M)); + 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(); @@ -709,22 +959,17 @@ Expected lookup(const std::vector &VSOs, SymbolNameSet Names, } /// Look up a symbol by searching a list of VSOs. -Expected -lookup(const std::vector VSOs, SymbolStringPtr Name, - MaterializationDispatcher DispatchMaterialization) { +Expected lookup(const std::vector VSOs, + SymbolStringPtr Name, + MaterializationResponsibility *R) { SymbolNameSet Names({Name}); - if (auto ResultMap = - lookup(VSOs, std::move(Names), std::move(DispatchMaterialization))) { + 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 ResultMap->begin()->second; + return std::move(ResultMap->begin()->second); } else return ResultMap.takeError(); } -void ExecutionSession::logErrorsToStdErr(Error Err) { - logAllUnhandledErrors(std::move(Err), errs(), "JIT session error: "); -} - } // End namespace orc. } // End namespace llvm. diff --git a/llvm/lib/ExecutionEngine/Orc/Legacy.cpp b/llvm/lib/ExecutionEngine/Orc/Legacy.cpp index a2413703f130..f7487b3a7370 100644 --- a/llvm/lib/ExecutionEngine/Orc/Legacy.cpp +++ b/llvm/lib/ExecutionEngine/Orc/Legacy.cpp @@ -12,9 +12,9 @@ namespace llvm { namespace orc { -JITSymbolResolverAdapter::JITSymbolResolverAdapter(ExecutionSession &ES, - SymbolResolver &R) - : ES(ES), R(R) {} +JITSymbolResolverAdapter::JITSymbolResolverAdapter( + ExecutionSession &ES, SymbolResolver &R, MaterializationResponsibility *MR) + : ES(ES), R(R), MR(MR) {} Expected JITSymbolResolverAdapter::lookup(const LookupSet &Symbols) { @@ -25,26 +25,28 @@ JITSymbolResolverAdapter::lookup(const LookupSet &Symbols) { for (auto &S : Symbols) InternedSymbols.insert(ES.getSymbolStringPool().intern(S)); - auto OnResolve = [&](Expected R) { - if (R) { - for (auto &KV : *R) { - ResolvedStrings.insert(KV.first); - Result[*KV.first] = KV.second; - } - } else - Err = joinErrors(std::move(Err), R.takeError()); - }; + auto OnResolve = + [&, this](Expected RR) { + if (RR) { + // If this lookup was attached to a MaterializationResponsibility then + // record the dependencies. + if (MR) + MR->addDependencies(RR->Dependencies); - auto OnReady = [](Error Err) { - // FIXME: Report error to ExecutionSession. - logAllUnhandledErrors(std::move(Err), errs(), - "legacy resolver received on-ready error:\n"); - }; + for (auto &KV : RR->Symbols) { + ResolvedStrings.insert(KV.first); + Result[*KV.first] = KV.second; + } + } else + Err = joinErrors(std::move(Err), RR.takeError()); + }; + + auto OnReady = [this](Error Err) { ES.reportError(std::move(Err)); }; auto Query = std::make_shared(InternedSymbols, OnResolve, OnReady); - auto UnresolvedSymbols = R.lookup(std::move(Query), InternedSymbols); + auto UnresolvedSymbols = R.lookup(Query, InternedSymbols); if (!UnresolvedSymbols.empty()) { std::string ErrorMsg = "Unresolved symbols: "; diff --git a/llvm/lib/ExecutionEngine/Orc/OrcCBindingsStack.h b/llvm/lib/ExecutionEngine/Orc/OrcCBindingsStack.h index 268597cec66d..5af5e6a3a491 100644 --- a/llvm/lib/ExecutionEngine/Orc/OrcCBindingsStack.h +++ b/llvm/lib/ExecutionEngine/Orc/OrcCBindingsStack.h @@ -155,16 +155,19 @@ private: if (auto Addr = Sym.getAddress()) Query->resolve(S, JITEvaluatedSymbol(*Addr, Sym.getFlags())); else { - Query->notifyMaterializationFailed(Addr.takeError()); + Stack.ES.failQuery(*Query, Addr.takeError()); return orc::SymbolNameSet(); } } else if (auto Err = Sym.takeError()) { - Query->notifyMaterializationFailed(std::move(Err)); + Stack.ES.failQuery(*Query, std::move(Err)); return orc::SymbolNameSet(); } else UnresolvedSymbols.insert(S); } + if (Query->isFullyResolved()) + Query->handleFullyResolved(); + return UnresolvedSymbols; } diff --git a/llvm/lib/ExecutionEngine/Orc/OrcMCJITReplacement.h b/llvm/lib/ExecutionEngine/Orc/OrcMCJITReplacement.h index 67a270e6c45b..3b1eabe5e862 100644 --- a/llvm/lib/ExecutionEngine/Orc/OrcMCJITReplacement.h +++ b/llvm/lib/ExecutionEngine/Orc/OrcMCJITReplacement.h @@ -171,34 +171,40 @@ class OrcMCJITReplacement : public ExecutionEngine { SymbolNameSet lookup(std::shared_ptr Query, SymbolNameSet Symbols) override { SymbolNameSet UnresolvedSymbols; + bool NewSymbolsResolved = false; for (auto &S : Symbols) { if (auto Sym = M.findMangledSymbol(*S)) { - if (auto Addr = Sym.getAddress()) + if (auto Addr = Sym.getAddress()) { Query->resolve(S, JITEvaluatedSymbol(*Addr, Sym.getFlags())); - else { - Query->notifyMaterializationFailed(Addr.takeError()); + NewSymbolsResolved = true; + } else { + M.ES.failQuery(*Query, Addr.takeError()); return SymbolNameSet(); } } else if (auto Err = Sym.takeError()) { - Query->notifyMaterializationFailed(std::move(Err)); + M.ES.failQuery(*Query, std::move(Err)); return SymbolNameSet(); } else { if (auto Sym2 = M.ClientResolver->findSymbol(*S)) { - if (auto Addr = Sym2.getAddress()) + if (auto Addr = Sym2.getAddress()) { Query->resolve(S, JITEvaluatedSymbol(*Addr, Sym2.getFlags())); - else { - Query->notifyMaterializationFailed(Addr.takeError()); + NewSymbolsResolved = true; + } else { + M.ES.failQuery(*Query, Addr.takeError()); return SymbolNameSet(); } } else if (auto Err = Sym2.takeError()) { - Query->notifyMaterializationFailed(std::move(Err)); + M.ES.failQuery(*Query, std::move(Err)); return SymbolNameSet(); } else UnresolvedSymbols.insert(S); } } + if (NewSymbolsResolved && Query->isFullyResolved()) + Query->handleFullyResolved(); + return UnresolvedSymbols; } diff --git a/llvm/tools/lli/OrcLazyJIT.h b/llvm/tools/lli/OrcLazyJIT.h index c2b10ffa5950..a421dc23e7d0 100644 --- a/llvm/tools/lli/OrcLazyJIT.h +++ b/llvm/tools/lli/OrcLazyJIT.h @@ -174,9 +174,10 @@ public: } return std::move(*NotFoundViaLegacyLookup); }, - [LegacyLookup](std::shared_ptr Query, + [this, + LegacyLookup](std::shared_ptr Query, orc::SymbolNameSet Symbols) { - return lookupWithLegacyFn(*Query, Symbols, LegacyLookup); + return lookupWithLegacyFn(ES, *Query, Symbols, LegacyLookup); }); // Add the module to the JIT. diff --git a/llvm/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp b/llvm/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp index ad17671f1b12..c36b347d670a 100644 --- a/llvm/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp +++ b/llvm/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp @@ -22,36 +22,36 @@ namespace { class SimpleMaterializationUnit : public MaterializationUnit { public: - using GetSymbolsFunction = std::function; using MaterializeFunction = std::function; using DiscardFunction = std::function; using DestructorFunction = std::function; SimpleMaterializationUnit( - GetSymbolsFunction GetSymbols, MaterializeFunction Materialize, - DiscardFunction Discard, + SymbolFlagsMap SymbolFlags, MaterializeFunction Materialize, + DiscardFunction Discard = DiscardFunction(), DestructorFunction Destructor = DestructorFunction()) - : GetSymbols(std::move(GetSymbols)), Materialize(std::move(Materialize)), - Discard(std::move(Discard)), Destructor(std::move(Destructor)) {} + : MaterializationUnit(std::move(SymbolFlags)), + Materialize(std::move(Materialize)), Discard(std::move(Discard)), + Destructor(std::move(Destructor)) {} ~SimpleMaterializationUnit() override { if (Destructor) Destructor(); } - SymbolFlagsMap getSymbols() override { return GetSymbols(); } - void materialize(MaterializationResponsibility R) override { Materialize(std::move(R)); } void discard(const VSO &V, SymbolStringPtr Name) override { - Discard(V, std::move(Name)); + if (Discard) + Discard(V, std::move(Name)); + else + llvm_unreachable("Discard not supported"); } private: - GetSymbolsFunction GetSymbols; MaterializeFunction Materialize; DiscardFunction Discard; DestructorFunction Destructor; @@ -65,14 +65,16 @@ TEST(CoreAPIsTest, AsynchronousSymbolQuerySuccessfulResolutionOnly) { bool OnResolutionRun = false; bool OnReadyRun = false; - auto OnResolution = [&](Expected 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(), FakeAddr) - << "Resolution returned incorrect result"; - OnResolutionRun = true; - }; + auto OnResolution = + [&](Expected Result) { + EXPECT_TRUE(!!Result) << "Resolution unexpectedly returned error"; + auto &Resolved = Result->Symbols; + auto I = Resolved.find(Foo); + EXPECT_NE(I, Resolved.end()) << "Could not find symbol definition"; + EXPECT_EQ(I->second.getAddress(), FakeAddr) + << "Resolution returned incorrect result"; + OnResolutionRun = true; + }; auto OnReady = [&](Error Err) { cantFail(std::move(Err)); OnReadyRun = true; @@ -82,24 +84,32 @@ TEST(CoreAPIsTest, AsynchronousSymbolQuerySuccessfulResolutionOnly) { Q.resolve(Foo, JITEvaluatedSymbol(FakeAddr, JITSymbolFlags::Exported)); + EXPECT_TRUE(Q.isFullyResolved()) << "Expected query to be fully resolved"; + + if (!Q.isFullyResolved()) + return; + + Q.handleFullyResolved(); + EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run"; EXPECT_FALSE(OnReadyRun) << "OnReady unexpectedly run"; } -TEST(CoreAPIsTest, AsynchronousSymbolQueryResolutionErrorOnly) { - SymbolStringPool SP; - auto Foo = SP.intern("foo"); +TEST(CoreAPIsTest, ExecutionSessionFailQuery) { + ExecutionSession ES; + auto Foo = ES.getSymbolStringPool().intern("foo"); SymbolNameSet Names({Foo}); bool OnResolutionRun = false; bool OnReadyRun = false; - auto OnResolution = [&](Expected Result) { - EXPECT_FALSE(!!Result) << "Resolution unexpectedly returned success"; - auto Msg = toString(Result.takeError()); - EXPECT_EQ(Msg, "xyz") << "Resolution returned incorrect result"; - OnResolutionRun = true; - }; + auto OnResolution = + [&](Expected 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; @@ -107,30 +117,31 @@ TEST(CoreAPIsTest, AsynchronousSymbolQueryResolutionErrorOnly) { AsynchronousSymbolQuery Q(Names, OnResolution, OnReady); - Q.notifyMaterializationFailed( - make_error("xyz", inconvertibleErrorCode())); + ES.failQuery(Q, make_error("xyz", inconvertibleErrorCode())); EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run"; EXPECT_FALSE(OnReadyRun) << "OnReady unexpectedly run"; } TEST(CoreAPIsTest, SimpleAsynchronousSymbolQueryAgainstVSO) { - SymbolStringPool SP; - auto Foo = SP.intern("foo"); + ExecutionSession ES; + auto Foo = ES.getSymbolStringPool().intern("foo"); constexpr JITTargetAddress FakeAddr = 0xdeadbeef; SymbolNameSet Names({Foo}); bool OnResolutionRun = false; bool OnReadyRun = false; - auto OnResolution = [&](Expected Result) { - EXPECT_TRUE(!!Result) << "Query unexpectedly returned error"; - auto I = Result->find(Foo); - EXPECT_NE(I, Result->end()) << "Could not find symbol definition"; - EXPECT_EQ(I->second.getAddress(), FakeAddr) - << "Resolution returned incorrect result"; - OnResolutionRun = true; - }; + auto OnResolution = + [&](Expected Result) { + EXPECT_TRUE(!!Result) << "Query unexpectedly returned error"; + auto &Resolved = Result->Symbols; + auto I = Resolved.find(Foo); + EXPECT_NE(I, Resolved.end()) << "Could not find symbol definition"; + EXPECT_EQ(I->second.getAddress(), FakeAddr) + << "Resolution returned incorrect result"; + OnResolutionRun = true; + }; auto OnReady = [&](Error Err) { cantFail(std::move(Err)); @@ -139,11 +150,12 @@ TEST(CoreAPIsTest, SimpleAsynchronousSymbolQueryAgainstVSO) { auto Q = std::make_shared(Names, OnResolution, OnReady); - VSO V; + auto &V = ES.createVSO("V"); - SymbolMap Defs; - Defs[Foo] = JITEvaluatedSymbol(FakeAddr, JITSymbolFlags::Exported); - cantFail(V.define(std::move(Defs))); + auto Defs = absoluteSymbols( + {{Foo, JITEvaluatedSymbol(FakeAddr, JITSymbolFlags::Exported)}}); + cantFail(V.define(Defs)); + assert(Defs == nullptr && "Defs should have been accepted"); V.lookup(Q, Names); EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run"; @@ -155,33 +167,26 @@ TEST(CoreAPIsTest, LookupFlagsTest) { // Test that lookupFlags works on a predefined symbol, and does not trigger // materialization of a lazy symbol. - SymbolStringPool SP; - auto Foo = SP.intern("foo"); - auto Bar = SP.intern("bar"); - auto Baz = SP.intern("baz"); + ExecutionSession ES; + auto Foo = ES.getSymbolStringPool().intern("foo"); + auto Bar = ES.getSymbolStringPool().intern("bar"); + auto Baz = ES.getSymbolStringPool().intern("baz"); JITSymbolFlags FooFlags = JITSymbolFlags::Exported; JITSymbolFlags BarFlags = static_cast( JITSymbolFlags::Exported | JITSymbolFlags::Weak); - VSO V; + VSO &V = ES.createVSO("V"); auto MU = llvm::make_unique( - [=]() { - return SymbolFlagsMap({{Bar, BarFlags}}); - }, + SymbolFlagsMap({{Bar, BarFlags}}), [](MaterializationResponsibility R) { llvm_unreachable("Symbol materialized on flags lookup"); - }, - [](const VSO &V, SymbolStringPtr Name) { - llvm_unreachable("Symbol finalized on flags lookup"); }); - SymbolMap InitialDefs; - InitialDefs[Foo] = JITEvaluatedSymbol(0xdeadbeef, FooFlags); - cantFail(V.define(std::move(InitialDefs))); - - cantFail(V.defineLazy(std::move(MU))); + cantFail(V.define( + absoluteSymbols({{Foo, JITEvaluatedSymbol(0xdeadbeef, FooFlags)}}))); + cantFail(V.define(std::move(MU))); SymbolNameSet Names({Foo, Bar, Baz}); @@ -199,18 +204,150 @@ TEST(CoreAPIsTest, LookupFlagsTest) { EXPECT_EQ(SymbolFlags[Bar], BarFlags) << "Incorrect flags returned for Bar"; } +TEST(CoreAPIsTest, TestCircularDependenceInOneVSO) { + + ExecutionSession ES; + + auto &V = ES.createVSO("V"); + + // Create three symbols: Foo, Bar and Baz. + auto Foo = ES.getSymbolStringPool().intern("foo"); + auto FooFlags = JITSymbolFlags::Exported; + auto FooSym = JITEvaluatedSymbol(1U, FooFlags); + + auto Bar = ES.getSymbolStringPool().intern("bar"); + auto BarFlags = JITSymbolFlags::Exported; + auto BarSym = JITEvaluatedSymbol(2U, BarFlags); + + auto Baz = ES.getSymbolStringPool().intern("baz"); + auto BazFlags = JITSymbolFlags::Exported; + auto BazSym = JITEvaluatedSymbol(3U, BazFlags); + + // Create three MaterializationResponsibility objects: one for each symbol + // (these are optional because MaterializationResponsibility does not have + // a default constructor). + Optional FooR; + Optional BarR; + Optional BazR; + + // Create a MaterializationUnit for each symbol that moves the + // MaterializationResponsibility into one of the locals above. + auto FooMU = llvm::make_unique( + SymbolFlagsMap({{Foo, FooFlags}}), + [&](MaterializationResponsibility R) { FooR.emplace(std::move(R)); }); + + auto BarMU = llvm::make_unique( + SymbolFlagsMap({{Bar, BarFlags}}), + [&](MaterializationResponsibility R) { BarR.emplace(std::move(R)); }); + + auto BazMU = llvm::make_unique( + SymbolFlagsMap({{Baz, BazFlags}}), + [&](MaterializationResponsibility R) { BazR.emplace(std::move(R)); }); + + // Define the symbols. + cantFail(V.define(FooMU)); + cantFail(V.define(BarMU)); + cantFail(V.define(BazMU)); + + // Query each of the symbols to trigger materialization. + bool FooResolved = false; + bool FooReady = false; + auto FooQ = std::make_shared( + SymbolNameSet({Foo}), + [&](Expected RR) { + cantFail(std::move(RR)); + FooResolved = true; + }, + [&](Error Err) { + cantFail(std::move(Err)); + FooReady = true; + }); + { + auto Unresolved = V.lookup(FooQ, {Foo}); + EXPECT_TRUE(Unresolved.empty()) << "Failed to resolve \"Foo\""; + } + + bool BarResolved = false; + bool BarReady = false; + auto BarQ = std::make_shared( + SymbolNameSet({Bar}), + [&](Expected RR) { + cantFail(std::move(RR)); + BarResolved = true; + }, + [&](Error Err) { + cantFail(std::move(Err)); + BarReady = true; + }); + { + auto Unresolved = V.lookup(BarQ, {Bar}); + EXPECT_TRUE(Unresolved.empty()) << "Failed to resolve \"Bar\""; + } + + bool BazResolved = false; + bool BazReady = false; + auto BazQ = std::make_shared( + SymbolNameSet({Baz}), + [&](Expected RR) { + cantFail(std::move(RR)); + BazResolved = true; + }, + [&](Error Err) { + cantFail(std::move(Err)); + BazReady = true; + }); + { + auto Unresolved = V.lookup(BazQ, {Baz}); + EXPECT_TRUE(Unresolved.empty()) << "Failed to resolve \"Baz\""; + } + + FooR->addDependencies({{&V, SymbolNameSet({Bar})}}); + BarR->addDependencies({{&V, SymbolNameSet({Baz})}}); + BazR->addDependencies({{&V, SymbolNameSet({Foo})}}); + + EXPECT_FALSE(FooResolved) << "\"Foo\" should not be resolved yet"; + EXPECT_FALSE(BarResolved) << "\"Bar\" should not be resolved yet"; + EXPECT_FALSE(BazResolved) << "\"Baz\" should not be resolved yet"; + + FooR->resolve({{Foo, FooSym}}); + BarR->resolve({{Bar, BarSym}}); + BazR->resolve({{Baz, BazSym}}); + + EXPECT_TRUE(FooResolved) << "\"Foo\" should be resolved now"; + EXPECT_TRUE(BarResolved) << "\"Bar\" should be resolved now"; + EXPECT_TRUE(BazResolved) << "\"Baz\" should be resolved now"; + + EXPECT_FALSE(FooReady) << "\"Foo\" should not be ready yet"; + EXPECT_FALSE(BarReady) << "\"Bar\" should not be ready yet"; + EXPECT_FALSE(BazReady) << "\"Baz\" should not be ready yet"; + + FooR->finalize(); + BarR->finalize(); + + // Verify that nothing is ready until the circular dependence is resolved. + + EXPECT_FALSE(FooReady) << "\"Foo\" still should not be ready"; + EXPECT_FALSE(BarReady) << "\"Bar\" still should not be ready"; + EXPECT_FALSE(BazReady) << "\"Baz\" still should not be ready"; + + BazR->finalize(); + + // Verify that everything becomes ready once the circular dependence resolved. + EXPECT_TRUE(FooReady) << "\"Foo\" should be ready now"; + EXPECT_TRUE(BarReady) << "\"Bar\" should be ready now"; + EXPECT_TRUE(BazReady) << "\"Baz\" should be ready now"; +} + TEST(CoreAPIsTest, DropMaterializerWhenEmpty) { - SymbolStringPool SP; - auto Foo = SP.intern("foo"); - auto Bar = SP.intern("bar"); + ExecutionSession ES; + auto Foo = ES.getSymbolStringPool().intern("foo"); + auto Bar = ES.getSymbolStringPool().intern("bar"); bool DestructorRun = false; auto MU = llvm::make_unique( - [=]() { - return SymbolFlagsMap( - {{Foo, JITSymbolFlags::Weak}, {Bar, JITSymbolFlags::Weak}}); - }, + SymbolFlagsMap( + {{Foo, JITSymbolFlags::Weak}, {Bar, JITSymbolFlags::Weak}}), [](MaterializationResponsibility R) { llvm_unreachable("Unexpected call to materialize"); }, @@ -220,18 +357,18 @@ TEST(CoreAPIsTest, DropMaterializerWhenEmpty) { }, [&]() { DestructorRun = true; }); - VSO V; + auto &V = ES.createVSO("V"); - cantFail(V.defineLazy(std::move(MU))); + cantFail(V.define(MU)); auto FooSym = JITEvaluatedSymbol(1, JITSymbolFlags::Exported); auto BarSym = JITEvaluatedSymbol(2, JITSymbolFlags::Exported); - cantFail(V.define(SymbolMap({{Foo, FooSym}}))); + cantFail(V.define(absoluteSymbols({{Foo, FooSym}}))); EXPECT_FALSE(DestructorRun) << "MaterializationUnit should not have been destroyed yet"; - cantFail(V.define(SymbolMap({{Bar, BarSym}}))); + cantFail(V.define(absoluteSymbols({{Bar, BarSym}}))); EXPECT_TRUE(DestructorRun) << "MaterializationUnit should have been destroyed"; @@ -242,22 +379,20 @@ TEST(CoreAPIsTest, AddAndMaterializeLazySymbol) { constexpr JITTargetAddress FakeFooAddr = 0xdeadbeef; constexpr JITTargetAddress FakeBarAddr = 0xcafef00d; - SymbolStringPool SP; - auto Foo = SP.intern("foo"); - auto Bar = SP.intern("bar"); + ExecutionSession ES; + auto Foo = ES.getSymbolStringPool().intern("foo"); + auto Bar = ES.getSymbolStringPool().intern("bar"); bool FooMaterialized = false; bool BarDiscarded = false; - VSO V; + auto &V = ES.createVSO("V"); auto MU = llvm::make_unique( - [=]() { - return SymbolFlagsMap( - {{Foo, JITSymbolFlags::Exported}, - {Bar, static_cast( - JITSymbolFlags::Exported | JITSymbolFlags::Weak)}}); - }, + SymbolFlagsMap( + {{Foo, JITSymbolFlags::Exported}, + {Bar, static_cast( + JITSymbolFlags::Exported | JITSymbolFlags::Weak)}}), [&](MaterializationResponsibility R) { assert(BarDiscarded && "Bar should have been discarded by this point"); SymbolMap SymbolsToResolve; @@ -272,25 +407,27 @@ TEST(CoreAPIsTest, AddAndMaterializeLazySymbol) { BarDiscarded = true; }); - cantFail(V.defineLazy(std::move(MU))); + cantFail(V.define(MU)); - SymbolMap BarOverride; - BarOverride[Bar] = JITEvaluatedSymbol(FakeBarAddr, JITSymbolFlags::Exported); - cantFail(V.define(std::move(BarOverride))); + ; + cantFail(V.define(absoluteSymbols( + {{Bar, JITEvaluatedSymbol(FakeBarAddr, JITSymbolFlags::Exported)}}))); SymbolNameSet Names({Foo}); bool OnResolutionRun = false; bool OnReadyRun = false; - auto OnResolution = [&](Expected 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(), FakeFooAddr) - << "Resolution returned incorrect result"; - OnResolutionRun = true; - }; + auto OnResolution = + [&](Expected Result) { + EXPECT_TRUE(!!Result) << "Resolution unexpectedly returned error"; + auto I = Result->Symbols.find(Foo); + EXPECT_NE(I, Result->Symbols.end()) + << "Could not find symbol definition"; + EXPECT_EQ(I->second.getAddress(), FakeFooAddr) + << "Resolution returned incorrect result"; + OnResolutionRun = true; + }; auto OnReady = [&](Error Err) { cantFail(std::move(Err)); @@ -300,40 +437,98 @@ TEST(CoreAPIsTest, AddAndMaterializeLazySymbol) { auto Q = std::make_shared(Names, OnResolution, OnReady); - auto LR = V.lookup(std::move(Q), Names); + auto Unresolved = V.lookup(std::move(Q), Names); - for (auto &M : LR.Materializers) - M(); - - EXPECT_TRUE(LR.UnresolvedSymbols.empty()) << "Could not find Foo in dylib"; + EXPECT_TRUE(Unresolved.empty()) << "Could not find Foo in dylib"; 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"; } +TEST(CoreAPIsTest, DefineMaterializingSymbol) { + ExecutionSession ES; + auto Foo = ES.getSymbolStringPool().intern("foo"); + auto Bar = ES.getSymbolStringPool().intern("bar"); + + auto FooSym = JITEvaluatedSymbol(1, JITSymbolFlags::Exported); + auto BarSym = JITEvaluatedSymbol(2, JITSymbolFlags::Exported); + + bool ExpectNoMoreMaterialization = false; + ES.setDispatchMaterialization( + [&](VSO &V, std::unique_ptr MU) { + if (ExpectNoMoreMaterialization) + ADD_FAILURE() << "Unexpected materialization"; + MU->doMaterialize(V); + }); + + auto MU = llvm::make_unique( + SymbolFlagsMap({{Foo, FooSym.getFlags()}}), + [&](MaterializationResponsibility R) { + cantFail( + R.defineMaterializing(SymbolFlagsMap({{Bar, BarSym.getFlags()}}))); + R.resolve(SymbolMap({{Foo, FooSym}, {Bar, BarSym}})); + R.finalize(); + }); + + auto &V = ES.createVSO("V"); + cantFail(V.define(MU)); + + auto OnResolution1 = + [&](Expected Result) { + cantFail(std::move(Result)); + }; + + auto OnReady1 = [](Error Err) { cantFail(std::move(Err)); }; + + auto Q1 = std::make_shared(SymbolNameSet({Foo}), + OnResolution1, OnReady1); + + V.lookup(std::move(Q1), {Foo}); + + bool BarResolved = false; + auto OnResolution2 = + [&](Expected Result) { + auto R = cantFail(std::move(Result)); + EXPECT_EQ(R.Symbols.size(), 1U) << "Expected to resolve one symbol"; + EXPECT_EQ(R.Symbols.count(Bar), 1U) << "Expected to resolve 'Bar'"; + EXPECT_EQ(R.Symbols[Bar].getAddress(), BarSym.getAddress()) + << "Expected Bar == BarSym"; + BarResolved = true; + }; + + auto OnReady2 = [](Error Err) { cantFail(std::move(Err)); }; + + auto Q2 = std::make_shared(SymbolNameSet({Bar}), + OnResolution2, OnReady2); + + ExpectNoMoreMaterialization = true; + V.lookup(std::move(Q2), {Bar}); + + EXPECT_TRUE(BarResolved) << "Bar should have been resolved"; +} + TEST(CoreAPIsTest, FailResolution) { - SymbolStringPool SP; - auto Foo = SP.intern("foo"); - auto Bar = SP.intern("bar"); + ExecutionSession ES; + auto Foo = ES.getSymbolStringPool().intern("foo"); + auto Bar = ES.getSymbolStringPool().intern("bar"); SymbolNameSet Names({Foo, Bar}); auto MU = llvm::make_unique( - [=]() { - return SymbolFlagsMap( - {{Foo, JITSymbolFlags::Weak}, {Bar, JITSymbolFlags::Weak}}); - }, - [&](MaterializationResponsibility R) { R.notifyMaterializationFailed(); }, - [&](const VSO &V, SymbolStringPtr Name) { - llvm_unreachable("Unexpected call to discard"); + SymbolFlagsMap( + {{Foo, JITSymbolFlags::Weak}, {Bar, JITSymbolFlags::Weak}}), + [&](MaterializationResponsibility R) { + R.failMaterialization( + [&]() { return make_error(Names); }); }); - VSO V; + auto &V = ES.createVSO("V"); - cantFail(V.defineLazy(std::move(MU))); + cantFail(V.define(MU)); - auto OnResolution = [&](Expected Result) { + auto OnResolution = [&](Expected + Result) { handleAllErrors(Result.takeError(), [&](FailedToResolve &F) { EXPECT_EQ(F.getSymbols(), Names) @@ -359,23 +554,19 @@ TEST(CoreAPIsTest, FailResolution) { auto Q = std::make_shared(Names, OnResolution, OnReady); - auto LR = V.lookup(std::move(Q), Names); - for (auto &M : LR.Materializers) - M(); + V.lookup(std::move(Q), Names); } TEST(CoreAPIsTest, FailFinalization) { - SymbolStringPool SP; - auto Foo = SP.intern("foo"); - auto Bar = SP.intern("bar"); + ExecutionSession ES; + auto Foo = ES.getSymbolStringPool().intern("foo"); + auto Bar = ES.getSymbolStringPool().intern("bar"); SymbolNameSet Names({Foo, Bar}); auto MU = llvm::make_unique( - [=]() { - return SymbolFlagsMap( - {{Foo, JITSymbolFlags::Exported}, {Bar, JITSymbolFlags::Exported}}); - }, + SymbolFlagsMap( + {{Foo, JITSymbolFlags::Exported}, {Bar, JITSymbolFlags::Exported}}), [&](MaterializationResponsibility R) { constexpr JITTargetAddress FakeFooAddr = 0xdeadbeef; constexpr JITTargetAddress FakeBarAddr = 0xcafef00d; @@ -383,19 +574,18 @@ TEST(CoreAPIsTest, FailFinalization) { auto FooSym = JITEvaluatedSymbol(FakeFooAddr, JITSymbolFlags::Exported); auto BarSym = JITEvaluatedSymbol(FakeBarAddr, JITSymbolFlags::Exported); R.resolve(SymbolMap({{Foo, FooSym}, {Bar, BarSym}})); - R.notifyMaterializationFailed(); - }, - [&](const VSO &V, SymbolStringPtr Name) { - llvm_unreachable("Unexpected call to discard"); + R.failMaterialization( + [&]() { return make_error(Names); }); }); - VSO V; + auto &V = ES.createVSO("V"); - cantFail(V.defineLazy(std::move(MU))); + cantFail(V.define(MU)); - auto OnResolution = [](Expected Result) { - cantFail(std::move(Result)); - }; + auto OnResolution = + [](Expected Result) { + cantFail(std::move(Result)); + }; auto OnReady = [&](Error Err) { handleAllErrors(std::move(Err), @@ -418,32 +608,28 @@ TEST(CoreAPIsTest, FailFinalization) { auto Q = std::make_shared(Names, OnResolution, OnReady); - auto LR = V.lookup(std::move(Q), Names); - for (auto &M : LR.Materializers) - M(); + V.lookup(std::move(Q), Names); } TEST(CoreAPIsTest, TestLambdaSymbolResolver) { JITEvaluatedSymbol FooSym(0xdeadbeef, JITSymbolFlags::Exported); JITEvaluatedSymbol BarSym(0xcafef00d, JITSymbolFlags::Exported); - SymbolStringPool SP; - auto Foo = SP.intern("foo"); - auto Bar = SP.intern("bar"); - auto Baz = SP.intern("baz"); + ExecutionSession ES; - VSO V; - cantFail(V.define({{Foo, FooSym}, {Bar, BarSym}})); + auto Foo = ES.getSymbolStringPool().intern("foo"); + auto Bar = ES.getSymbolStringPool().intern("bar"); + auto Baz = ES.getSymbolStringPool().intern("baz"); + + auto &V = ES.createVSO("V"); + cantFail(V.define(absoluteSymbols({{Foo, FooSym}, {Bar, BarSym}}))); auto Resolver = createSymbolResolver( [&](SymbolFlagsMap &SymbolFlags, const SymbolNameSet &Symbols) { return V.lookupFlags(SymbolFlags, Symbols); }, [&](std::shared_ptr Q, SymbolNameSet Symbols) { - auto LR = V.lookup(std::move(Q), Symbols); - assert(LR.Materializers.empty() && - "Test generated unexpected materialization work?"); - return std::move(LR.UnresolvedSymbols); + return V.lookup(std::move(Q), Symbols); }); SymbolNameSet Symbols({Foo, Bar, Baz}); @@ -466,17 +652,21 @@ TEST(CoreAPIsTest, TestLambdaSymbolResolver) { bool OnResolvedRun = false; - auto OnResolved = [&](Expected Result) { - OnResolvedRun = 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->count(Bar), 1U) << "Missing lookup result for bar"; - EXPECT_EQ((*Result)[Foo].getAddress(), FooSym.getAddress()) - << "Incorrect address for foo"; - EXPECT_EQ((*Result)[Bar].getAddress(), BarSym.getAddress()) - << "Incorrect address for bar"; - }; + auto OnResolved = + [&](Expected Result) { + OnResolvedRun = true; + EXPECT_TRUE(!!Result) << "Unexpected error"; + EXPECT_EQ(Result->Symbols.size(), 2U) + << "Unexpected number of resolved symbols"; + EXPECT_EQ(Result->Symbols.count(Foo), 1U) + << "Missing lookup result for foo"; + EXPECT_EQ(Result->Symbols.count(Bar), 1U) + << "Missing lookup result for bar"; + EXPECT_EQ(Result->Symbols[Foo].getAddress(), FooSym.getAddress()) + << "Incorrect address for foo"; + EXPECT_EQ(Result->Symbols[Bar].getAddress(), BarSym.getAddress()) + << "Incorrect address for bar"; + }; auto OnReady = [&](Error Err) { EXPECT_FALSE(!!Err) << "Finalization should never fail in this test"; }; @@ -498,23 +688,17 @@ TEST(CoreAPIsTest, TestLookupWithUnthreadedMaterialization) { auto Foo = ES.getSymbolStringPool().intern("foo"); auto MU = llvm::make_unique( - [=]() { - return SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}}); - }, + SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}}), [&](MaterializationResponsibility R) { R.resolve({{Foo, FooSym}}); R.finalize(); - }, - [](const VSO &V, SymbolStringPtr Name) { - llvm_unreachable("Not expecting finalization"); }); - VSO V; + auto &V = ES.createVSO("V"); - cantFail(V.defineLazy(std::move(MU))); + cantFail(V.define(MU)); - auto FooLookupResult = - cantFail(lookup({&V}, Foo, MaterializeOnCurrentThread())); + auto FooLookupResult = cantFail(lookup({&V}, Foo, nullptr)); EXPECT_EQ(FooLookupResult.getAddress(), FooSym.getAddress()) << "lookup returned an incorrect address"; @@ -528,33 +712,20 @@ TEST(CoreAPIsTest, TestLookupWithThreadedMaterialization) { JITEvaluatedSymbol FooSym(FakeFooAddr, JITSymbolFlags::Exported); ExecutionSession ES(std::make_shared()); - auto Foo = ES.getSymbolStringPool().intern("foo"); - - auto MU = llvm::make_unique( - [=]() { - return SymbolFlagsMap({{Foo, JITSymbolFlags::Exported}}); - }, - [&](MaterializationResponsibility R) { - R.resolve({{Foo, FooSym}}); - R.finalize(); - }, - [](const VSO &V, SymbolStringPtr Name) { - llvm_unreachable("Not expecting finalization"); - }); - - VSO V; - - cantFail(V.defineLazy(std::move(MU))); std::thread MaterializationThread; - auto MaterializeOnNewThread = [&](VSO::Materializer M) { - // FIXME: Use move capture once we move to C++14. - auto SharedM = std::make_shared(std::move(M)); - MaterializationThread = std::thread([SharedM]() { (*SharedM)(); }); - }; + ES.setDispatchMaterialization( + [&](VSO &V, std::unique_ptr MU) { + auto SharedMU = std::shared_ptr(std::move(MU)); + MaterializationThread = + std::thread([SharedMU, &V]() { SharedMU->doMaterialize(V); }); + }); + auto Foo = ES.getSymbolStringPool().intern("foo"); - auto FooLookupResult = - cantFail(lookup({&V}, Foo, MaterializeOnNewThread)); + auto &V = ES.createVSO("V"); + cantFail(V.define(absoluteSymbols({{Foo, FooSym}}))); + + auto FooLookupResult = cantFail(lookup({&V}, Foo, nullptr)); EXPECT_EQ(FooLookupResult.getAddress(), FooSym.getAddress()) << "lookup returned an incorrect address"; diff --git a/llvm/unittests/ExecutionEngine/Orc/LegacyAPIInteropTest.cpp b/llvm/unittests/ExecutionEngine/Orc/LegacyAPIInteropTest.cpp index 514aafaff437..ade5aa26470f 100644 --- a/llvm/unittests/ExecutionEngine/Orc/LegacyAPIInteropTest.cpp +++ b/llvm/unittests/ExecutionEngine/Orc/LegacyAPIInteropTest.cpp @@ -21,11 +21,9 @@ TEST(LegacyAPIInteropTest, QueryAgainstVSO) { ExecutionSession ES(std::make_shared()); auto Foo = ES.getSymbolStringPool().intern("foo"); - VSO V; - SymbolMap Defs; + auto &V = ES.createVSO("V"); JITEvaluatedSymbol FooSym(0xdeadbeef, JITSymbolFlags::Exported); - Defs[Foo] = FooSym; - cantFail(V.define(std::move(Defs))); + cantFail(V.define(absoluteSymbols({{Foo, FooSym}}))); auto LookupFlags = [&](SymbolFlagsMap &SymbolFlags, const SymbolNameSet &Names) { @@ -34,15 +32,12 @@ TEST(LegacyAPIInteropTest, QueryAgainstVSO) { auto Lookup = [&](std::shared_ptr Query, SymbolNameSet Symbols) { - auto R = V.lookup(std::move(Query), Symbols); - EXPECT_TRUE(R.Materializers.empty()) - << "Query resulted in unexpected materialization work"; - return std::move(R.UnresolvedSymbols); + return V.lookup(std::move(Query), Symbols); }; auto UnderlyingResolver = createSymbolResolver(std::move(LookupFlags), std::move(Lookup)); - JITSymbolResolverAdapter Resolver(ES, *UnderlyingResolver); + JITSymbolResolverAdapter Resolver(ES, *UnderlyingResolver, nullptr); JITSymbolResolver::LookupSet Names{StringRef("foo")}; @@ -90,10 +85,10 @@ TEST(LegacyAPIInteropTset, LegacyLookupHelpersFn) { return nullptr; }; - SymbolStringPool SP; - auto Foo = SP.intern("foo"); - auto Bar = SP.intern("bar"); - auto Baz = SP.intern("baz"); + ExecutionSession ES; + auto Foo = ES.getSymbolStringPool().intern("foo"); + auto Bar = ES.getSymbolStringPool().intern("bar"); + auto Baz = ES.getSymbolStringPool().intern("baz"); SymbolNameSet Symbols({Foo, Bar, Baz}); @@ -115,24 +110,29 @@ TEST(LegacyAPIInteropTset, LegacyLookupHelpersFn) { bool OnResolvedRun = false; bool OnReadyRun = false; - auto OnResolved = [&](Expected Result) { - OnResolvedRun = true; - EXPECT_TRUE(!!Result) << "lookuWithLegacy failed to resolve"; - EXPECT_EQ(Result->size(), 2U) << "Wrong number of symbols resolved"; - EXPECT_EQ(Result->count(Foo), 1U) << "Result for foo missing"; - EXPECT_EQ(Result->count(Bar), 1U) << "Result for bar missing"; - EXPECT_EQ((*Result)[Foo].getAddress(), FooAddr) << "Wrong address for foo"; - EXPECT_EQ((*Result)[Foo].getFlags(), FooFlags) << "Wrong flags for foo"; - EXPECT_EQ((*Result)[Bar].getAddress(), BarAddr) << "Wrong address for bar"; - EXPECT_EQ((*Result)[Bar].getFlags(), BarFlags) << "Wrong flags for bar"; - }; + auto OnResolved = + [&](Expected Result) { + OnResolvedRun = true; + EXPECT_TRUE(!!Result) << "lookuWithLegacy failed to resolve"; + + auto &Resolved = Result->Symbols; + EXPECT_EQ(Resolved.size(), 2U) << "Wrong number of symbols resolved"; + EXPECT_EQ(Resolved.count(Foo), 1U) << "Result for foo missing"; + EXPECT_EQ(Resolved.count(Bar), 1U) << "Result for bar missing"; + EXPECT_EQ(Resolved[Foo].getAddress(), FooAddr) + << "Wrong address for foo"; + EXPECT_EQ(Resolved[Foo].getFlags(), FooFlags) << "Wrong flags for foo"; + EXPECT_EQ(Resolved[Bar].getAddress(), BarAddr) + << "Wrong address for bar"; + EXPECT_EQ(Resolved[Bar].getFlags(), BarFlags) << "Wrong flags for bar"; + }; auto OnReady = [&](Error Err) { EXPECT_FALSE(!!Err) << "Finalization unexpectedly failed"; OnReadyRun = true; }; AsynchronousSymbolQuery Q({Foo, Bar}, OnResolved, OnReady); - auto Unresolved = lookupWithLegacyFn(Q, Symbols, LegacyLookup); + auto Unresolved = lookupWithLegacyFn(ES, Q, Symbols, LegacyLookup); EXPECT_TRUE(OnResolvedRun) << "OnResolved was not run"; EXPECT_TRUE(OnReadyRun) << "OnReady was not run"; diff --git a/llvm/unittests/ExecutionEngine/Orc/RTDyldObjectLinkingLayerTest.cpp b/llvm/unittests/ExecutionEngine/Orc/RTDyldObjectLinkingLayerTest.cpp index 650fd5248125..17f733c99d20 100644 --- a/llvm/unittests/ExecutionEngine/Orc/RTDyldObjectLinkingLayerTest.cpp +++ b/llvm/unittests/ExecutionEngine/Orc/RTDyldObjectLinkingLayerTest.cpp @@ -190,7 +190,7 @@ TEST_F(RTDyldObjectLinkingLayerExecutionTest, NoDuplicateFinalization) { }, [&](std::shared_ptr Query, const SymbolNameSet &Symbols) { - return lookupWithLegacyFn(*Query, Symbols, LegacyLookup); + return lookupWithLegacyFn(ES, *Query, Symbols, LegacyLookup); }); cantFail(ObjLayer.addObject(K2, std::move(Obj2)));