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)));