diff --git a/include/llvm/ExecutionEngine/Orc/Core.h b/include/llvm/ExecutionEngine/Orc/Core.h index e9eb1727596..f7a92ae0fdd 100644 --- a/include/llvm/ExecutionEngine/Orc/Core.h +++ b/include/llvm/ExecutionEngine/Orc/Core.h @@ -366,146 +366,12 @@ private: SymbolPredicate Allow; }; -/// Base utilities for ExecutionSession. -class ExecutionSessionBase { - // FIXME: Remove this when we remove the old ORC layers. - friend class JITDylib; - -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 */ - } - - void legacyFailQuery(AsynchronousSymbolQuery &Q, Error Err); - - using LegacyAsyncLookupFunction = std::function Q, SymbolNameSet Names)>; - - /// A legacy lookup function for JITSymbolResolverAdapter. - /// Do not use -- this will be removed soon. - Expected - legacyLookup(ExecutionSessionBase &ES, LegacyAsyncLookupFunction AsyncLookup, - SymbolNameSet Names, bool WaiUntilReady, - RegisterDependenciesFunction RegisterDependencies); - - /// Search the given JITDylib list for the given symbols. - /// - /// - /// The OnResolve callback will be called once all requested symbols are - /// resolved, or if an error occurs prior to resolution. - /// - /// The OnReady callback will be called once all requested symbols are ready, - /// or if an error occurs after resolution but before all symbols are ready. - /// - /// If all symbols are found, the RegisterDependencies function will be called - /// while the session lock is held. This gives clients a chance to register - /// dependencies for on the queried symbols for any symbols they are - /// materializing (if a MaterializationResponsibility instance is present, - /// this can be implemented by calling - /// MaterializationResponsibility::addDependencies). If there are no - /// dependenant symbols for this query (e.g. it is being made by a top level - /// client to get an address to call) then the value NoDependenciesToRegister - /// can be used. - void lookup(const JITDylibList &JDs, const SymbolNameSet &Symbols, - SymbolsResolvedCallback OnResolve, SymbolsReadyCallback OnReady, - RegisterDependenciesFunction RegisterDependencies); - - /// Blocking version of lookup above. Returns the resolved symbol map. - /// If WaitUntilReady is true (the default), will not return until all - /// requested symbols are ready (or an error occurs). If WaitUntilReady is - /// false, will return as soon as all requested symbols are resolved, - /// or an error occurs. If WaitUntilReady is false and an error occurs - /// after resolution, the function will return a success value, but the - /// error will be reported via reportErrors. - Expected lookup(const JITDylibList &JDs, - const SymbolNameSet &Symbols, - RegisterDependenciesFunction RegisterDependencies, - bool WaitUntilReady = true); - - /// Materialize the given unit. - void dispatchMaterialization(JITDylib &JD, - std::unique_ptr MU) { - DispatchMaterialization(JD, std::move(MU)); - } - -private: - static void logErrorsToStdErr(Error Err) { - logAllUnhandledErrors(std::move(Err), errs(), "JIT session error: "); - } - - static void - materializeOnCurrentThread(JITDylib &JD, - std::unique_ptr MU) { - MU->doMaterialize(JD); - } - - void runOutstandingMUs(); - - mutable std::recursive_mutex SessionMutex; - std::shared_ptr SSP; - VModuleKey LastKey = 0; - ErrorReporter ReportError = logErrorsToStdErr; - DispatchMaterializationFunction DispatchMaterialization = - materializeOnCurrentThread; - - // FIXME: Remove this (and runOutstandingMUs) once the linking layer works - // with callbacks from asynchronous queries. - mutable std::recursive_mutex OutstandingMUsMutex; - std::vector>> - OutstandingMUs; -}; - /// 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 ExecutionSession; friend class JITDylib; public: @@ -568,7 +434,6 @@ private: class JITDylib { friend class AsynchronousSymbolQuery; friend class ExecutionSession; - friend class ExecutionSessionBase; friend class MaterializationResponsibility; public: using FallbackDefinitionGeneratorFunction = std::function auto withSearchOrderDo(Func &&F) - -> decltype(F(std::declval())) { - return ES.runSessionLocked([&]() { return F(SearchOrder); }); - } + -> decltype(F(std::declval())); - /// Define all symbols provided by the materialization unit to be part - /// of the given JITDylib. - 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"); + /// Define all symbols provided by the materialization unit to be part of this + /// JITDylib. + /// + /// This overload always takes ownership of the MaterializationUnit. If any + /// errors occur, the MaterializationUnit consumed. + template + Error define(std::unique_ptr &&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 all symbols provided by the materialization unit to be part of this + /// JITDylib. + /// + /// This overload only takes ownership of the MaterializationUnit no error is + /// generated. If an error occurs, ownership remains with the caller. This + /// may allow the caller to modify the MaterializationUnit to correct the + /// issue, then re-call define. + template + Error define(std::unique_ptr &MU); /// Search the given JITDylib for the symbols in Symbols. If found, store /// the flags for each symbol in Flags. Returns any unresolved symbols. @@ -709,7 +566,7 @@ private: LLVM_MARK_AS_BITMASK_ENUM(NotifyFullyReady) }; - JITDylib(ExecutionSessionBase &ES, std::string Name); + JITDylib(ExecutionSession &ES, std::string Name); Error defineImpl(MaterializationUnit &MU); @@ -749,7 +606,7 @@ private: void notifyFailed(const SymbolNameSet &FailedSymbols); - ExecutionSessionBase &ES; + ExecutionSession &ES; std::string JITDylibName; SymbolMap Symbols; UnmaterializedInfosMap UnmaterializedInfos; @@ -759,15 +616,32 @@ private: }; /// An ExecutionSession represents a running JIT program. -class ExecutionSession : public ExecutionSessionBase { -public: - using ExecutionSessionBase::lookup; +class ExecutionSession { + // FIXME: Remove this when we remove the old ORC layers. + friend class JITDylib; - /// Construct an ExecutionEngine. +public: + /// For reporting errors. + using ErrorReporter = std::function; + + /// For dispatching MaterializationUnit::materialize calls. + using DispatchMaterializationFunction = std::function MU)>; + + /// Construct an ExecutionSession. /// /// SymbolStringPools may be shared between ExecutionSessions. ExecutionSession(std::shared_ptr SSP = nullptr); + /// 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(); + } + /// Get the "main" JITDylib, which is created automatically on construction of /// the ExecutionSession. JITDylib &getMainJITDylib(); @@ -776,21 +650,163 @@ public: JITDylib &createJITDylib(std::string Name, bool AddToMainDylibSearchOrder = true); - /// Convenience version of the blocking version of lookup in - /// ExecutionSessionBase. Uses the main JITDylib's search order as the lookup - /// order, and registers no dependencies. + /// 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 */ + } + + /// Set the error reporter function. + ExecutionSession &setErrorReporter(ErrorReporter ReportError) { + this->ReportError = std::move(ReportError); + 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)); } + + /// Set the materialization dispatch function. + ExecutionSession &setDispatchMaterialization( + DispatchMaterializationFunction DispatchMaterialization) { + this->DispatchMaterialization = std::move(DispatchMaterialization); + return *this; + } + + void legacyFailQuery(AsynchronousSymbolQuery &Q, Error Err); + + using LegacyAsyncLookupFunction = std::function Q, SymbolNameSet Names)>; + + /// A legacy lookup function for JITSymbolResolverAdapter. + /// Do not use -- this will be removed soon. + Expected + legacyLookup(LegacyAsyncLookupFunction AsyncLookup, SymbolNameSet Names, + bool WaiUntilReady, + RegisterDependenciesFunction RegisterDependencies); + + /// Search the given JITDylib list for the given symbols. + /// + /// + /// The OnResolve callback will be called once all requested symbols are + /// resolved, or if an error occurs prior to resolution. + /// + /// The OnReady callback will be called once all requested symbols are ready, + /// or if an error occurs after resolution but before all symbols are ready. + /// + /// If all symbols are found, the RegisterDependencies function will be called + /// while the session lock is held. This gives clients a chance to register + /// dependencies for on the queried symbols for any symbols they are + /// materializing (if a MaterializationResponsibility instance is present, + /// this can be implemented by calling + /// MaterializationResponsibility::addDependencies). If there are no + /// dependenant symbols for this query (e.g. it is being made by a top level + /// client to get an address to call) then the value NoDependenciesToRegister + /// can be used. + void lookup(const JITDylibList &JDs, const SymbolNameSet &Symbols, + SymbolsResolvedCallback OnResolve, SymbolsReadyCallback OnReady, + RegisterDependenciesFunction RegisterDependencies); + + /// Blocking version of lookup above. Returns the resolved symbol map. + /// If WaitUntilReady is true (the default), will not return until all + /// requested symbols are ready (or an error occurs). If WaitUntilReady is + /// false, will return as soon as all requested symbols are resolved, + /// or an error occurs. If WaitUntilReady is false and an error occurs + /// after resolution, the function will return a success value, but the + /// error will be reported via reportErrors. + Expected lookup(const JITDylibList &JDs, + const SymbolNameSet &Symbols, + RegisterDependenciesFunction RegisterDependencies, + bool WaitUntilReady = true); + + /// Convenience version of the blocking version of lookup above. Uses the main + /// JITDylib's search order as the lookup order, and registers no + /// dependencies. Expected lookup(const SymbolNameSet &Symbols) { return getMainJITDylib().withSearchOrderDo( [&](const JITDylibList &SearchOrder) { - return ExecutionSessionBase::lookup(SearchOrder, Symbols, - NoDependenciesToRegister, true); + return lookup(SearchOrder, Symbols, NoDependenciesToRegister, true); }); } + /// Materialize the given unit. + void dispatchMaterialization(JITDylib &JD, + std::unique_ptr MU) { + DispatchMaterialization(JD, std::move(MU)); + } + private: + static void logErrorsToStdErr(Error Err) { + logAllUnhandledErrors(std::move(Err), errs(), "JIT session error: "); + } + + static void + materializeOnCurrentThread(JITDylib &JD, + std::unique_ptr MU) { + MU->doMaterialize(JD); + } + + void runOutstandingMUs(); + + mutable std::recursive_mutex SessionMutex; + std::shared_ptr SSP; + VModuleKey LastKey = 0; + ErrorReporter ReportError = logErrorsToStdErr; + DispatchMaterializationFunction DispatchMaterialization = + materializeOnCurrentThread; + std::vector> JDs; + + // FIXME: Remove this (and runOutstandingMUs) once the linking layer works + // with callbacks from asynchronous queries. + mutable std::recursive_mutex OutstandingMUsMutex; + std::vector>> + OutstandingMUs; }; +template +auto JITDylib::withSearchOrderDo(Func &&F) + -> decltype(F(std::declval())) { + return ES.runSessionLocked([&]() { return F(SearchOrder); }); +} + +template +Error JITDylib::define(std::unique_ptr &&MU) { + assert(MU && "Can not define with a null MU"); + return ES.runSessionLocked([&, this]() -> Error { + 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(); + }); +} + +template +Error JITDylib::define(std::unique_ptr &MU) { + assert(MU && "Can not define with a null MU"); + + return ES.runSessionLocked([&, this]() -> Error { + 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(); + }); +} + /// Look up the given names in the given JITDylibs. /// JDs will be searched in order and no JITDylib pointer may be null. /// All symbols must be found within the given JITDylibs or an error @@ -805,11 +821,11 @@ Expected lookup(const JITDylibList &JDs, /// ExecutionSession. class MangleAndInterner { public: - MangleAndInterner(ExecutionSessionBase &ES, const DataLayout &DL); + MangleAndInterner(ExecutionSession &ES, const DataLayout &DL); SymbolStringPtr operator()(StringRef Name); private: - ExecutionSessionBase &ES; + ExecutionSession &ES; const DataLayout &DL; }; diff --git a/lib/ExecutionEngine/Orc/Core.cpp b/lib/ExecutionEngine/Orc/Core.cpp index 469f849b1c2..780e2fb9802 100644 --- a/lib/ExecutionEngine/Orc/Core.cpp +++ b/lib/ExecutionEngine/Orc/Core.cpp @@ -142,361 +142,6 @@ void SymbolsNotFound::log(raw_ostream &OS) const { OS << "Symbols not found: " << Symbols; } -void ExecutionSessionBase::legacyFailQuery(AsynchronousSymbolQuery &Q, - Error Err) { - assert(!!Err && "Error should be in failure state"); - - bool SendErrorToQuery; - runSessionLocked([&]() { - Q.detach(); - SendErrorToQuery = Q.canStillFail(); - }); - - if (SendErrorToQuery) - Q.handleFailed(std::move(Err)); - else - reportError(std::move(Err)); -} - -Expected ExecutionSessionBase::legacyLookup( - ExecutionSessionBase &ES, LegacyAsyncLookupFunction AsyncLookup, - SymbolNameSet Names, bool WaitUntilReady, - RegisterDependenciesFunction RegisterDependencies) { -#if LLVM_ENABLE_THREADS - // In the threaded case we use promises to return the results. - std::promise PromisedResult; - std::mutex ErrMutex; - Error ResolutionError = Error::success(); - std::promise PromisedReady; - Error ReadyError = Error::success(); - auto OnResolve = [&](Expected R) { - if (R) - PromisedResult.set_value(std::move(*R)); - else { - { - ErrorAsOutParameter _(&ResolutionError); - std::lock_guard Lock(ErrMutex); - ResolutionError = R.takeError(); - } - PromisedResult.set_value(SymbolMap()); - } - }; - - std::function OnReady; - if (WaitUntilReady) { - OnReady = [&](Error Err) { - if (Err) { - ErrorAsOutParameter _(&ReadyError); - std::lock_guard Lock(ErrMutex); - ReadyError = std::move(Err); - } - PromisedReady.set_value(); - }; - } else { - OnReady = [&](Error Err) { - if (Err) - ES.reportError(std::move(Err)); - }; - } - -#else - SymbolMap Result; - Error ResolutionError = Error::success(); - Error ReadyError = Error::success(); - - auto OnResolve = [&](Expected R) { - ErrorAsOutParameter _(&ResolutionError); - if (R) - Result = std::move(*R); - else - ResolutionError = R.takeError(); - }; - - std::function OnReady; - if (WaitUntilReady) { - OnReady = [&](Error Err) { - ErrorAsOutParameter _(&ReadyError); - if (Err) - ReadyError = std::move(Err); - }; - } else { - OnReady = [&](Error Err) { - if (Err) - ES.reportError(std::move(Err)); - }; - } -#endif - - auto Query = std::make_shared( - Names, std::move(OnResolve), std::move(OnReady)); - // FIXME: This should be run session locked along with the registration code - // and error reporting below. - SymbolNameSet UnresolvedSymbols = AsyncLookup(Query, std::move(Names)); - - // If the query was lodged successfully then register the dependencies, - // otherwise fail it with an error. - if (UnresolvedSymbols.empty()) - RegisterDependencies(Query->QueryRegistrations); - else { - bool DeliverError = runSessionLocked([&]() { - Query->detach(); - return Query->canStillFail(); - }); - auto Err = make_error(std::move(UnresolvedSymbols)); - if (DeliverError) - Query->handleFailed(std::move(Err)); - else - ES.reportError(std::move(Err)); - } - -#if LLVM_ENABLE_THREADS - auto ResultFuture = PromisedResult.get_future(); - auto Result = ResultFuture.get(); - - { - std::lock_guard Lock(ErrMutex); - if (ResolutionError) { - // ReadyError will never be assigned. Consume the success value. - cantFail(std::move(ReadyError)); - return std::move(ResolutionError); - } - } - - if (WaitUntilReady) { - auto ReadyFuture = PromisedReady.get_future(); - ReadyFuture.get(); - - { - std::lock_guard Lock(ErrMutex); - if (ReadyError) - return std::move(ReadyError); - } - } else - cantFail(std::move(ReadyError)); - - return std::move(Result); - -#else - if (ResolutionError) { - // ReadyError will never be assigned. Consume the success value. - cantFail(std::move(ReadyError)); - return std::move(ResolutionError); - } - - if (ReadyError) - return std::move(ReadyError); - - return Result; -#endif -} - -void ExecutionSessionBase::lookup( - const JITDylibList &JDs, const SymbolNameSet &Symbols, - SymbolsResolvedCallback OnResolve, SymbolsReadyCallback OnReady, - RegisterDependenciesFunction RegisterDependencies) { - - // lookup can be re-entered recursively if running on a single thread. Run any - // outstanding MUs in case this query depends on them, otherwise the main - // thread will starve waiting for a result from an MU that it failed to run. - runOutstandingMUs(); - - auto Unresolved = std::move(Symbols); - std::map MUsMap; - auto Q = std::make_shared( - Symbols, std::move(OnResolve), std::move(OnReady)); - bool QueryIsFullyResolved = false; - bool QueryIsFullyReady = false; - bool QueryFailed = false; - - runSessionLocked([&]() { - for (auto *JD : JDs) { - assert(JD && "JITDylibList entries must not be null"); - assert(!MUsMap.count(JD) && - "JITDylibList should not contain duplicate entries"); - JD->lodgeQuery(Q, Unresolved, MUsMap[JD]); - } - - if (Unresolved.empty()) { - // Query lodged successfully. - - // Record whether this query is fully ready / resolved. We will use - // this to call handleFullyResolved/handleFullyReady outside the session - // lock. - QueryIsFullyResolved = Q->isFullyResolved(); - QueryIsFullyReady = Q->isFullyReady(); - - // Call the register dependencies function. - if (RegisterDependencies && !Q->QueryRegistrations.empty()) - RegisterDependencies(Q->QueryRegistrations); - } else { - // Query failed due to unresolved symbols. - QueryFailed = true; - - // Disconnect the query from its dependencies. - Q->detach(); - - // Replace the MUs. - for (auto &KV : MUsMap) - for (auto &MU : KV.second) - KV.first->replace(std::move(MU)); - } - }); - - if (QueryFailed) { - Q->handleFailed(make_error(std::move(Unresolved))); - return; - } else { - if (QueryIsFullyResolved) - Q->handleFullyResolved(); - if (QueryIsFullyReady) - Q->handleFullyReady(); - } - - // Move the MUs to the OutstandingMUs list, then materialize. - { - std::lock_guard Lock(OutstandingMUsMutex); - - for (auto &KV : MUsMap) - for (auto &MU : KV.second) - OutstandingMUs.push_back(std::make_pair(KV.first, std::move(MU))); - } - - runOutstandingMUs(); -} - -Expected ExecutionSessionBase::lookup( - const JITDylibList &JDs, const SymbolNameSet &Symbols, - RegisterDependenciesFunction RegisterDependencies, bool WaitUntilReady) { -#if LLVM_ENABLE_THREADS - // In the threaded case we use promises to return the results. - std::promise PromisedResult; - std::mutex ErrMutex; - Error ResolutionError = Error::success(); - std::promise PromisedReady; - Error ReadyError = Error::success(); - auto OnResolve = [&](Expected R) { - if (R) - PromisedResult.set_value(std::move(*R)); - else { - { - ErrorAsOutParameter _(&ResolutionError); - std::lock_guard Lock(ErrMutex); - ResolutionError = R.takeError(); - } - PromisedResult.set_value(SymbolMap()); - } - }; - - std::function OnReady; - if (WaitUntilReady) { - OnReady = [&](Error Err) { - if (Err) { - ErrorAsOutParameter _(&ReadyError); - std::lock_guard Lock(ErrMutex); - ReadyError = std::move(Err); - } - PromisedReady.set_value(); - }; - } else { - OnReady = [&](Error Err) { - if (Err) - reportError(std::move(Err)); - }; - } - -#else - SymbolMap Result; - Error ResolutionError = Error::success(); - Error ReadyError = Error::success(); - - auto OnResolve = [&](Expected R) { - ErrorAsOutParameter _(&ResolutionError); - if (R) - Result = std::move(*R); - else - ResolutionError = R.takeError(); - }; - - std::function OnReady; - if (WaitUntilReady) { - OnReady = [&](Error Err) { - ErrorAsOutParameter _(&ReadyError); - if (Err) - ReadyError = std::move(Err); - }; - } else { - OnReady = [&](Error Err) { - if (Err) - reportError(std::move(Err)); - }; - } -#endif - - // Perform the asynchronous lookup. - lookup(JDs, Symbols, OnResolve, OnReady, RegisterDependencies); - -#if LLVM_ENABLE_THREADS - auto ResultFuture = PromisedResult.get_future(); - auto Result = ResultFuture.get(); - - { - std::lock_guard Lock(ErrMutex); - if (ResolutionError) { - // ReadyError will never be assigned. Consume the success value. - cantFail(std::move(ReadyError)); - return std::move(ResolutionError); - } - } - - if (WaitUntilReady) { - auto ReadyFuture = PromisedReady.get_future(); - ReadyFuture.get(); - - { - std::lock_guard Lock(ErrMutex); - if (ReadyError) - return std::move(ReadyError); - } - } else - cantFail(std::move(ReadyError)); - - return std::move(Result); - -#else - if (ResolutionError) { - // ReadyError will never be assigned. Consume the success value. - cantFail(std::move(ReadyError)); - return std::move(ResolutionError); - } - - if (ReadyError) - return std::move(ReadyError); - - return Result; -#endif -} - -void ExecutionSessionBase::runOutstandingMUs() { - while (1) { - std::pair> JITDylibAndMU; - - { - std::lock_guard Lock(OutstandingMUsMutex); - if (!OutstandingMUs.empty()) { - JITDylibAndMU = std::move(OutstandingMUs.back()); - OutstandingMUs.pop_back(); - } - } - - if (JITDylibAndMU.first) { - assert(JITDylibAndMU.second && "JITDylib, but no MU?"); - dispatchMaterialization(*JITDylibAndMU.first, - std::move(JITDylibAndMU.second)); - } else - break; - } -} - AsynchronousSymbolQuery::AsynchronousSymbolQuery( const SymbolNameSet &Symbols, SymbolsResolvedCallback NotifySymbolsResolved, SymbolsReadyCallback NotifySymbolsReady) @@ -520,10 +165,18 @@ void AsynchronousSymbolQuery::resolve(const SymbolStringPtr &Name, void AsynchronousSymbolQuery::handleFullyResolved() { assert(NotYetResolvedCount == 0 && "Not fully resolved?"); - assert(NotifySymbolsResolved && - "NotifySymbolsResolved already called or error occurred"); - NotifySymbolsResolved(std::move(ResolvedSymbols)); + + if (!NotifySymbolsResolved) { + // handleFullyResolved may be called by handleFullyReady (see comments in + // that method), in which case this is a no-op, so bail out. + assert(!NotifySymbolsReady && + "NotifySymbolsResolved already called or an error occurred"); + return; + } + + auto TmpNotifySymbolsResolved = std::move(NotifySymbolsResolved); NotifySymbolsResolved = SymbolsResolvedCallback(); + TmpNotifySymbolsResolved(std::move(ResolvedSymbols)); } void AsynchronousSymbolQuery::notifySymbolReady() { @@ -532,11 +185,25 @@ void AsynchronousSymbolQuery::notifySymbolReady() { } void AsynchronousSymbolQuery::handleFullyReady() { + assert(NotifySymbolsReady && + "NotifySymbolsReady already called or an error occurred"); + + auto TmpNotifySymbolsReady = std::move(NotifySymbolsReady); + NotifySymbolsReady = SymbolsReadyCallback(); + + if (NotYetResolvedCount == 0 && NotifySymbolsResolved) { + // The NotifyResolved callback of one query must have caused this query to + // become ready (i.e. there is still a handleFullyResolved callback waiting + // to be made back up the stack). Fold the handleFullyResolved call into + // this one before proceeding. This will cause the call further up the + // stack to become a no-op. + handleFullyResolved(); + } + assert(QueryRegistrations.empty() && "Query is still registered with some symbols"); assert(!NotifySymbolsResolved && "Resolution not applied yet"); - NotifySymbolsReady(Error::success()); - NotifySymbolsReady = SymbolsReadyCallback(); + TmpNotifySymbolsReady(Error::success()); } bool AsynchronousSymbolQuery::canStillFail() { @@ -1549,7 +1216,7 @@ void JITDylib::dump(raw_ostream &OS) { }); } -JITDylib::JITDylib(ExecutionSessionBase &ES, std::string Name) +JITDylib::JITDylib(ExecutionSession &ES, std::string Name) : ES(ES), JITDylibName(std::move(Name)) { SearchOrder.push_back(this); } @@ -1678,7 +1345,7 @@ void JITDylib::transferEmittedNodeDependencies( } ExecutionSession::ExecutionSession(std::shared_ptr SSP) - : ExecutionSessionBase(std::move(SSP)) { + : SSP(SSP ? std::move(SSP) : std::make_shared()) { // Construct the main dylib. JDs.push_back(std::unique_ptr(new JITDylib(*this, "
"))); } @@ -1698,6 +1365,360 @@ JITDylib &ExecutionSession::createJITDylib(std::string Name, }); } +void ExecutionSession::legacyFailQuery(AsynchronousSymbolQuery &Q, Error Err) { + assert(!!Err && "Error should be in failure state"); + + bool SendErrorToQuery; + runSessionLocked([&]() { + Q.detach(); + SendErrorToQuery = Q.canStillFail(); + }); + + if (SendErrorToQuery) + Q.handleFailed(std::move(Err)); + else + reportError(std::move(Err)); +} + +Expected ExecutionSession::legacyLookup( + LegacyAsyncLookupFunction AsyncLookup, SymbolNameSet Names, + bool WaitUntilReady, RegisterDependenciesFunction RegisterDependencies) { +#if LLVM_ENABLE_THREADS + // In the threaded case we use promises to return the results. + std::promise PromisedResult; + std::mutex ErrMutex; + Error ResolutionError = Error::success(); + std::promise PromisedReady; + Error ReadyError = Error::success(); + auto OnResolve = [&](Expected R) { + if (R) + PromisedResult.set_value(std::move(*R)); + else { + { + ErrorAsOutParameter _(&ResolutionError); + std::lock_guard Lock(ErrMutex); + ResolutionError = R.takeError(); + } + PromisedResult.set_value(SymbolMap()); + } + }; + + std::function OnReady; + if (WaitUntilReady) { + OnReady = [&](Error Err) { + if (Err) { + ErrorAsOutParameter _(&ReadyError); + std::lock_guard Lock(ErrMutex); + ReadyError = std::move(Err); + } + PromisedReady.set_value(); + }; + } else { + OnReady = [&](Error Err) { + if (Err) + reportError(std::move(Err)); + }; + } + +#else + SymbolMap Result; + Error ResolutionError = Error::success(); + Error ReadyError = Error::success(); + + auto OnResolve = [&](Expected R) { + ErrorAsOutParameter _(&ResolutionError); + if (R) + Result = std::move(*R); + else + ResolutionError = R.takeError(); + }; + + std::function OnReady; + if (WaitUntilReady) { + OnReady = [&](Error Err) { + ErrorAsOutParameter _(&ReadyError); + if (Err) + ReadyError = std::move(Err); + }; + } else { + OnReady = [&](Error Err) { + if (Err) + reportError(std::move(Err)); + }; + } +#endif + + auto Query = std::make_shared( + Names, std::move(OnResolve), std::move(OnReady)); + // FIXME: This should be run session locked along with the registration code + // and error reporting below. + SymbolNameSet UnresolvedSymbols = AsyncLookup(Query, std::move(Names)); + + // If the query was lodged successfully then register the dependencies, + // otherwise fail it with an error. + if (UnresolvedSymbols.empty()) + RegisterDependencies(Query->QueryRegistrations); + else { + bool DeliverError = runSessionLocked([&]() { + Query->detach(); + return Query->canStillFail(); + }); + auto Err = make_error(std::move(UnresolvedSymbols)); + if (DeliverError) + Query->handleFailed(std::move(Err)); + else + reportError(std::move(Err)); + } + +#if LLVM_ENABLE_THREADS + auto ResultFuture = PromisedResult.get_future(); + auto Result = ResultFuture.get(); + + { + std::lock_guard Lock(ErrMutex); + if (ResolutionError) { + // ReadyError will never be assigned. Consume the success value. + cantFail(std::move(ReadyError)); + return std::move(ResolutionError); + } + } + + if (WaitUntilReady) { + auto ReadyFuture = PromisedReady.get_future(); + ReadyFuture.get(); + + { + std::lock_guard Lock(ErrMutex); + if (ReadyError) + return std::move(ReadyError); + } + } else + cantFail(std::move(ReadyError)); + + return std::move(Result); + +#else + if (ResolutionError) { + // ReadyError will never be assigned. Consume the success value. + cantFail(std::move(ReadyError)); + return std::move(ResolutionError); + } + + if (ReadyError) + return std::move(ReadyError); + + return Result; +#endif +} + +void ExecutionSession::lookup( + const JITDylibList &JDs, const SymbolNameSet &Symbols, + SymbolsResolvedCallback OnResolve, SymbolsReadyCallback OnReady, + RegisterDependenciesFunction RegisterDependencies) { + + // lookup can be re-entered recursively if running on a single thread. Run any + // outstanding MUs in case this query depends on them, otherwise the main + // thread will starve waiting for a result from an MU that it failed to run. + runOutstandingMUs(); + + auto Unresolved = std::move(Symbols); + std::map MUsMap; + auto Q = std::make_shared( + Symbols, std::move(OnResolve), std::move(OnReady)); + bool QueryIsFullyResolved = false; + bool QueryIsFullyReady = false; + bool QueryFailed = false; + + runSessionLocked([&]() { + for (auto *JD : JDs) { + assert(JD && "JITDylibList entries must not be null"); + assert(!MUsMap.count(JD) && + "JITDylibList should not contain duplicate entries"); + JD->lodgeQuery(Q, Unresolved, MUsMap[JD]); + } + + if (Unresolved.empty()) { + // Query lodged successfully. + + // Record whether this query is fully ready / resolved. We will use + // this to call handleFullyResolved/handleFullyReady outside the session + // lock. + QueryIsFullyResolved = Q->isFullyResolved(); + QueryIsFullyReady = Q->isFullyReady(); + + // Call the register dependencies function. + if (RegisterDependencies && !Q->QueryRegistrations.empty()) + RegisterDependencies(Q->QueryRegistrations); + } else { + // Query failed due to unresolved symbols. + QueryFailed = true; + + // Disconnect the query from its dependencies. + Q->detach(); + + // Replace the MUs. + for (auto &KV : MUsMap) + for (auto &MU : KV.second) + KV.first->replace(std::move(MU)); + } + }); + + if (QueryFailed) { + Q->handleFailed(make_error(std::move(Unresolved))); + return; + } else { + if (QueryIsFullyResolved) + Q->handleFullyResolved(); + if (QueryIsFullyReady) + Q->handleFullyReady(); + } + + // Move the MUs to the OutstandingMUs list, then materialize. + { + std::lock_guard Lock(OutstandingMUsMutex); + + for (auto &KV : MUsMap) + for (auto &MU : KV.second) + OutstandingMUs.push_back(std::make_pair(KV.first, std::move(MU))); + } + + runOutstandingMUs(); +} + +Expected +ExecutionSession::lookup(const JITDylibList &JDs, const SymbolNameSet &Symbols, + RegisterDependenciesFunction RegisterDependencies, + bool WaitUntilReady) { +#if LLVM_ENABLE_THREADS + // In the threaded case we use promises to return the results. + std::promise PromisedResult; + std::mutex ErrMutex; + Error ResolutionError = Error::success(); + std::promise PromisedReady; + Error ReadyError = Error::success(); + auto OnResolve = [&](Expected R) { + if (R) + PromisedResult.set_value(std::move(*R)); + else { + { + ErrorAsOutParameter _(&ResolutionError); + std::lock_guard Lock(ErrMutex); + ResolutionError = R.takeError(); + } + PromisedResult.set_value(SymbolMap()); + } + }; + + std::function OnReady; + if (WaitUntilReady) { + OnReady = [&](Error Err) { + if (Err) { + ErrorAsOutParameter _(&ReadyError); + std::lock_guard Lock(ErrMutex); + ReadyError = std::move(Err); + } + PromisedReady.set_value(); + }; + } else { + OnReady = [&](Error Err) { + if (Err) + reportError(std::move(Err)); + }; + } + +#else + SymbolMap Result; + Error ResolutionError = Error::success(); + Error ReadyError = Error::success(); + + auto OnResolve = [&](Expected R) { + ErrorAsOutParameter _(&ResolutionError); + if (R) + Result = std::move(*R); + else + ResolutionError = R.takeError(); + }; + + std::function OnReady; + if (WaitUntilReady) { + OnReady = [&](Error Err) { + ErrorAsOutParameter _(&ReadyError); + if (Err) + ReadyError = std::move(Err); + }; + } else { + OnReady = [&](Error Err) { + if (Err) + reportError(std::move(Err)); + }; + } +#endif + + // Perform the asynchronous lookup. + lookup(JDs, Symbols, OnResolve, OnReady, RegisterDependencies); + +#if LLVM_ENABLE_THREADS + auto ResultFuture = PromisedResult.get_future(); + auto Result = ResultFuture.get(); + + { + std::lock_guard Lock(ErrMutex); + if (ResolutionError) { + // ReadyError will never be assigned. Consume the success value. + cantFail(std::move(ReadyError)); + return std::move(ResolutionError); + } + } + + if (WaitUntilReady) { + auto ReadyFuture = PromisedReady.get_future(); + ReadyFuture.get(); + + { + std::lock_guard Lock(ErrMutex); + if (ReadyError) + return std::move(ReadyError); + } + } else + cantFail(std::move(ReadyError)); + + return std::move(Result); + +#else + if (ResolutionError) { + // ReadyError will never be assigned. Consume the success value. + cantFail(std::move(ReadyError)); + return std::move(ResolutionError); + } + + if (ReadyError) + return std::move(ReadyError); + + return Result; +#endif +} + +void ExecutionSession::runOutstandingMUs() { + while (1) { + std::pair> JITDylibAndMU; + + { + std::lock_guard Lock(OutstandingMUsMutex); + if (!OutstandingMUs.empty()) { + JITDylibAndMU = std::move(OutstandingMUs.back()); + OutstandingMUs.pop_back(); + } + } + + if (JITDylibAndMU.first) { + assert(JITDylibAndMU.second && "JITDylib, but no MU?"); + dispatchMaterialization(*JITDylibAndMU.first, + std::move(JITDylibAndMU.second)); + } else + break; + } +} + Expected lookup(const JITDylibList &JDs, SymbolNameSet Names) { if (JDs.empty()) @@ -1720,8 +1741,7 @@ Expected lookup(const JITDylibList &JDs, return ResultMap.takeError(); } -MangleAndInterner::MangleAndInterner(ExecutionSessionBase &ES, - const DataLayout &DL) +MangleAndInterner::MangleAndInterner(ExecutionSession &ES, const DataLayout &DL) : ES(ES), DL(DL) {} SymbolStringPtr MangleAndInterner::operator()(StringRef Name) { diff --git a/lib/ExecutionEngine/Orc/Legacy.cpp b/lib/ExecutionEngine/Orc/Legacy.cpp index 517176e5f42..925729e0eee 100644 --- a/lib/ExecutionEngine/Orc/Legacy.cpp +++ b/lib/ExecutionEngine/Orc/Legacy.cpp @@ -35,7 +35,7 @@ JITSymbolResolverAdapter::lookup(const LookupSet &Symbols) { }; auto InternedResult = - ES.legacyLookup(ES, std::move(LookupFn), std::move(InternedSymbols), + ES.legacyLookup(std::move(LookupFn), std::move(InternedSymbols), false, RegisterDependencies); if (!InternedResult)