From 741d7afdf12851d714b5ecb80e213290d2ba9708 Mon Sep 17 00:00:00 2001 From: Lang Hames Date: Thu, 12 Apr 2018 18:35:08 +0000 Subject: [PATCH] [ORC] Plumb error notifications through the VSO interface. This allows materializers to notify the VSO that they were unable to resolve or finalize symbols. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@329934 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/ExecutionEngine/JITSymbol.h | 20 +- include/llvm/ExecutionEngine/Orc/Core.h | 135 +++++-- include/llvm/ExecutionEngine/Orc/Legacy.h | 10 +- include/llvm/ExecutionEngine/Orc/OrcError.h | 3 +- lib/ExecutionEngine/Orc/Core.cpp | 509 +++++++++++++++---------- lib/ExecutionEngine/Orc/OrcCBindingsStack.h | 6 +- lib/ExecutionEngine/Orc/OrcError.cpp | 2 + lib/ExecutionEngine/Orc/OrcMCJITReplacement.h | 13 +- unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp | 173 ++++++++- 9 files changed, 619 insertions(+), 252 deletions(-) diff --git a/include/llvm/ExecutionEngine/JITSymbol.h b/include/llvm/ExecutionEngine/JITSymbol.h index 86ab17363e1..bb906f86029 100644 --- a/include/llvm/ExecutionEngine/JITSymbol.h +++ b/include/llvm/ExecutionEngine/JITSymbol.h @@ -52,11 +52,12 @@ public: Common = 1U << 2, Absolute = 1U << 3, Exported = 1U << 4, - NotMaterialized = 1U << 5 + Lazy = 1U << 5, + Materializing = 1U << 6 }; static JITSymbolFlags stripTransientFlags(JITSymbolFlags Orig) { - return static_cast(Orig.Flags & ~NotMaterialized); + return static_cast(Orig.Flags & ~Lazy & ~Materializing); } /// @brief Default-construct a JITSymbolFlags instance. @@ -75,9 +76,18 @@ public: return (Flags & HasError) == HasError; } - /// @brief Returns true if this symbol has been fully materialized (i.e. is - /// callable). - bool isMaterialized() const { return !(Flags & NotMaterialized); } + /// @brief Returns true if this is a lazy symbol. + /// This flag is used internally by the JIT APIs to track + /// materialization states. + bool isLazy() const { return Flags & Lazy; } + + /// @brief Returns true if this symbol is in the process of being + /// materialized. + bool isMaterializing() const { return Flags & Materializing; } + + /// @brief Returns true if this symbol is fully materialized. + /// (i.e. neither lazy, nor materializing). + bool isMaterialized() const { return !(Flags & (Lazy | Materializing)); } /// @brief Returns true if the Weak flag is set. bool isWeak() const { diff --git a/include/llvm/ExecutionEngine/Orc/Core.h b/include/llvm/ExecutionEngine/Orc/Core.h index 475ef9c7142..0000a9a2ba1 100644 --- a/include/llvm/ExecutionEngine/Orc/Core.h +++ b/include/llvm/ExecutionEngine/Orc/Core.h @@ -17,6 +17,7 @@ #include "llvm/ExecutionEngine/JITSymbol.h" #include "llvm/ExecutionEngine/Orc/SymbolStringPool.h" +#include #include #include #include @@ -25,23 +26,71 @@ namespace llvm { namespace orc { +// Forward declare some classes. +class VSO; + /// VModuleKey provides a unique identifier (allocated and managed by /// ExecutionSessions) for a module added to the JIT. using VModuleKey = uint64_t; -class VSO; - /// @brief A set of symbol names (represented by SymbolStringPtrs for // efficiency). using SymbolNameSet = std::set; +/// @brief Render a SymbolNameSet to an ostream. +raw_ostream &operator<<(raw_ostream &OS, const SymbolNameSet &Symbols); + /// @brief A map from symbol names (as SymbolStringPtrs) to JITSymbols /// (address/flags pairs). using SymbolMap = std::map; +/// @brief Render a SymbolMap to an ostream. +raw_ostream &operator<<(raw_ostream &OS, const SymbolMap &Symbols); + /// @brief A map from symbol names (as SymbolStringPtrs) to JITSymbolFlags. using SymbolFlagsMap = std::map; +/// @brief Render a SymbolMap to an ostream. +raw_ostream &operator<<(raw_ostream &OS, const SymbolFlagsMap &Symbols); + +/// @brief A base class for materialization failures that allows the failing +/// symbols to be obtained for logging. +class FailedToMaterialize : public ErrorInfo { +public: + static char ID; + virtual const SymbolNameSet &getSymbols() const = 0; +}; + +/// @brief Used to notify a VSO that the given set of symbols failed to resolve. +class FailedToResolve : public ErrorInfo { +public: + static char ID; + + FailedToResolve(SymbolNameSet Symbols); + std::error_code convertToErrorCode() const override; + void log(raw_ostream &OS) const override; + const SymbolNameSet &getSymbols() const override { return Symbols; } + +private: + SymbolNameSet Symbols; +}; + +/// @brief Used to notify a VSO that the given set of symbols failed to +/// finalize. +class FailedToFinalize + : public ErrorInfo { +public: + static char ID; + + FailedToFinalize(SymbolNameSet Symbols); + std::error_code convertToErrorCode() const override; + void log(raw_ostream &OS) const override; + const SymbolNameSet &getSymbols() const override { return Symbols; } + +private: + SymbolNameSet Symbols; +}; + /// @brief A symbol query that returns results via a callback when results are /// ready. /// @@ -69,20 +118,20 @@ public: /// notify-finalized callback is called with the given error. /// /// It is illegal to call setFailed after both callbacks have been made. - void setFailed(Error Err); + void notifyFailed(Error Err); /// @brief Set the resolved symbol information for the given symbol name. /// /// 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 setDefinition(SymbolStringPtr Name, JITEvaluatedSymbol Sym); + void resolve(SymbolStringPtr Name, JITEvaluatedSymbol Sym); /// @brief Notify the query that a requested symbol is ready for execution. /// /// 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 notifySymbolFinalized(); + void finalizeSymbol(); private: SymbolMap Symbols; @@ -240,10 +289,16 @@ public: /// @brief Add the given symbol/address mappings to the dylib, but do not /// mark the symbols as finalized yet. - void resolve(SymbolMap SymbolValues); + void resolve(const SymbolMap &SymbolValues); + + /// @brief Notify the VSO that the given symbols failed to finalize. + void notifyResolutionFailed(const SymbolNameSet &Names); /// @brief Finalize the given symbols. - void finalize(SymbolNameSet SymbolsToFinalize); + void finalize(const SymbolNameSet &SymbolsToFinalize); + + /// @brief Notify the VSO that the given symbols failed to finalize. + void notifyFinalizationFailed(const SymbolNameSet &Names); /// @brief Look up the flags for the given symbols. /// @@ -267,56 +322,72 @@ public: SymbolNameSet Symbols); private: - class MaterializationInfo { + class UnmaterializedInfo { public: - using QueryList = std::vector>; - - MaterializationInfo(size_t SymbolsRemaining, - std::unique_ptr MU); + UnmaterializedInfo(size_t SymbolsRemaining, + std::unique_ptr MU); uint64_t SymbolsRemaining; std::unique_ptr MU; - SymbolMap Symbols; - std::map PendingResolution; - std::map PendingFinalization; }; - using MaterializationInfoSet = std::set>; + using UnmaterializedInfoList = std::list; + + using UnmaterializedInfoIterator = UnmaterializedInfoList::iterator; + + class MaterializingInfo { + public: + using QueryList = std::vector>; + + QueryList PendingResolution; + QueryList PendingFinalization; + }; - using MaterializationInfoIterator = MaterializationInfoSet::iterator; + using MaterializingInfoMap = std::map; + + using MaterializingInfoIterator = MaterializingInfoMap::iterator; class SymbolTableEntry { public: SymbolTableEntry(JITSymbolFlags SymbolFlags, - MaterializationInfoIterator MaterializationInfoItr); + UnmaterializedInfoIterator UnmaterializedInfoItr); SymbolTableEntry(JITEvaluatedSymbol Sym); - SymbolTableEntry(SymbolTableEntry &&Other); + // SymbolTableEntry(SymbolTableEntry &&Other); + // SymbolTableEntry &operator=(SymbolTableEntry &&Other); ~SymbolTableEntry(); - SymbolTableEntry &operator=(JITEvaluatedSymbol Sym); + // Change definition due to override. Only usable prior to materialization. + void replaceWith(VSO &V, SymbolStringPtr Name, JITEvaluatedSymbol Sym); - JITSymbolFlags getFlags() const; + // Change definition due to override. Only usable prior to materialization. void replaceWith(VSO &V, SymbolStringPtr Name, JITSymbolFlags Flags, - MaterializationInfoIterator NewMaterializationInfoItr); - std::unique_ptr - query(SymbolStringPtr Name, std::shared_ptr Query); - void resolve(VSO &V, SymbolStringPtr Name, JITEvaluatedSymbol Sym); - void finalize(VSO &V, SymbolStringPtr Name); - void discard(VSO &V, SymbolStringPtr Name); + UnmaterializedInfoIterator NewUMII); - private: - void destroy(); + // Move entry to materializing state, detach from UMII. + std::unique_ptr initMaterialize(VSO &V); + + // Move entry to resolved state. + void resolve(VSO &V, JITEvaluatedSymbol Sym); + + // Move entry to finalized state. + void finalize(); JITSymbolFlags Flags; - MaterializationInfoIterator MII; + union { JITTargetAddress Address; - MaterializationInfoIterator MaterializationInfoItr; + UnmaterializedInfoIterator UMII; }; + + private: + void destroy(); }; + void detach(UnmaterializedInfoIterator UMII); + std::map Symbols; - MaterializationInfoSet MaterializationInfos; + UnmaterializedInfoList UnmaterializedInfos; + MaterializingInfoMap MaterializingInfos; }; /// @brief An ExecutionSession represents a running JIT program. diff --git a/include/llvm/ExecutionEngine/Orc/Legacy.h b/include/llvm/ExecutionEngine/Orc/Legacy.h index b2b389ad339..73debf3ad19 100644 --- a/include/llvm/ExecutionEngine/Orc/Legacy.h +++ b/include/llvm/ExecutionEngine/Orc/Legacy.h @@ -62,7 +62,7 @@ Expected lookupFlagsWithLegacyFn(SymbolFlagsMap &SymbolFlags, /// takes a const std::string& or StringRef and returns a JITSymbol) to /// find the address and flags for each symbol in Symbols and store the /// result in Query. If any JITSymbol returned by FindSymbol is in an -/// error then Query.setFailed(...) is called with that error and the +/// error then Query.notifyFailed(...) is called with that error and the /// function returns immediately. On success, returns the set of symbols /// not found. /// @@ -76,14 +76,14 @@ SymbolNameSet lookupWithLegacyFn(AsynchronousSymbolQuery &Query, for (auto &S : Symbols) { if (JITSymbol Sym = FindSymbol(*S)) { if (auto Addr = Sym.getAddress()) { - Query.setDefinition(S, JITEvaluatedSymbol(*Addr, Sym.getFlags())); - Query.notifySymbolFinalized(); + Query.resolve(S, JITEvaluatedSymbol(*Addr, Sym.getFlags())); + Query.finalizeSymbol(); } else { - Query.setFailed(Addr.takeError()); + Query.notifyFailed(Addr.takeError()); return SymbolNameSet(); } } else if (auto Err = Sym.takeError()) { - Query.setFailed(std::move(Err)); + Query.notifyFailed(std::move(Err)); return SymbolNameSet(); } else SymbolsNotFound.insert(S); diff --git a/include/llvm/ExecutionEngine/Orc/OrcError.h b/include/llvm/ExecutionEngine/Orc/OrcError.h index c2ff41e421e..dc60e8d74e9 100644 --- a/include/llvm/ExecutionEngine/Orc/OrcError.h +++ b/include/llvm/ExecutionEngine/Orc/OrcError.h @@ -22,7 +22,8 @@ namespace orc { enum class OrcErrorCode : int { // RPC Errors - DuplicateDefinition = 1, + UnknownORCError = 1, + DuplicateDefinition, JITSymbolNotFound, RemoteAllocatorDoesNotExist, RemoteAllocatorIdAlreadyInUse, diff --git a/lib/ExecutionEngine/Orc/Core.cpp b/lib/ExecutionEngine/Orc/Core.cpp index 4c1126b23bf..bff83f5bf5c 100644 --- a/lib/ExecutionEngine/Orc/Core.cpp +++ b/lib/ExecutionEngine/Orc/Core.cpp @@ -9,6 +9,7 @@ #include "llvm/ExecutionEngine/Orc/Core.h" #include "llvm/ExecutionEngine/Orc/OrcError.h" +#include "llvm/Support/Format.h" #if LLVM_ENABLE_THREADS #include @@ -17,9 +18,100 @@ namespace llvm { namespace orc { +char FailedToMaterialize::ID = 0; +char FailedToResolve::ID = 0; +char FailedToFinalize::ID = 0; + void MaterializationUnit::anchor() {} void SymbolResolver::anchor() {} +raw_ostream &operator<<(raw_ostream &OS, const JITSymbolFlags &Flags) { + if (Flags.isWeak()) + OS << 'W'; + else if (Flags.isCommon()) + OS << 'C'; + else + OS << 'S'; + + if (Flags.isExported()) + OS << 'E'; + else + OS << 'H'; + + return OS; +} + +raw_ostream &operator<<(raw_ostream &OS, const JITEvaluatedSymbol &Sym) { + OS << format("0x%016x", Sym.getAddress()) << " " << Sym.getFlags(); + return OS; +} + +raw_ostream &operator<<(raw_ostream &OS, const SymbolMap::value_type &KV) { + OS << "\"" << *KV.first << "\": " << KV.second; + return OS; +} + +raw_ostream &operator<<(raw_ostream &OS, const SymbolNameSet &Symbols) { + OS << "{"; + if (!Symbols.empty()) { + OS << " \"" << **Symbols.begin() << "\""; + for (auto &Sym : make_range(std::next(Symbols.begin()), Symbols.end())) + OS << ", \"" << *Sym << "\""; + } + OS << " }"; + return OS; +} + +raw_ostream &operator<<(raw_ostream &OS, const SymbolMap &Symbols) { + OS << "{"; + if (!Symbols.empty()) { + OS << " {" << *Symbols.begin() << "}"; + for (auto &Sym : make_range(std::next(Symbols.begin()), Symbols.end())) + OS << ", {" << Sym << "}"; + } + OS << " }"; + return OS; +} + +raw_ostream &operator<<(raw_ostream &OS, const SymbolFlagsMap &SymbolFlags) { + OS << "{"; + if (SymbolFlags.empty()) { + OS << " {\"" << *SymbolFlags.begin()->first + << "\": " << SymbolFlags.begin()->second << "}"; + for (auto &KV : + make_range(std::next(SymbolFlags.begin()), SymbolFlags.end())) + OS << ", {\"" << *KV.first << "\": " << KV.second << "}"; + } + OS << " }"; + return OS; +} + +FailedToResolve::FailedToResolve(SymbolNameSet Symbols) + : Symbols(std::move(Symbols)) { + assert(!this->Symbols.empty() && "Can not fail to resolve an empty set"); +} + +std::error_code FailedToResolve::convertToErrorCode() const { + return orcError(OrcErrorCode::UnknownORCError); +} + +void FailedToResolve::log(raw_ostream &OS) const { + OS << "Failed to resolve symbols: " << Symbols; +} + +FailedToFinalize::FailedToFinalize(SymbolNameSet Symbols) + : Symbols(std::move(Symbols)) { + assert(!this->Symbols.empty() && "Can not fail to finalize an empty set"); +} + +std::error_code FailedToFinalize::convertToErrorCode() const { + return orcError(OrcErrorCode::UnknownORCError); +} + +void FailedToFinalize::log(raw_ostream &OS) const { + OS << "Failed to finalize symbols: " << Symbols; +} + AsynchronousSymbolQuery::AsynchronousSymbolQuery( const SymbolNameSet &Symbols, SymbolsResolvedCallback NotifySymbolsResolved, SymbolsReadyCallback NotifySymbolsReady) @@ -31,16 +123,18 @@ AsynchronousSymbolQuery::AsynchronousSymbolQuery( OutstandingResolutions = OutstandingFinalizations = Symbols.size(); } -void AsynchronousSymbolQuery::setFailed(Error Err) { - OutstandingResolutions = OutstandingFinalizations = 0; - if (NotifySymbolsResolved) +void AsynchronousSymbolQuery::notifyFailed(Error Err) { + if (OutstandingResolutions != 0) NotifySymbolsResolved(std::move(Err)); - else + else if (OutstandingFinalizations != 0) NotifySymbolsReady(std::move(Err)); + else + consumeError(std::move(Err)); + OutstandingResolutions = OutstandingFinalizations = 0; } -void AsynchronousSymbolQuery::setDefinition(SymbolStringPtr Name, - JITEvaluatedSymbol Sym) { +void AsynchronousSymbolQuery::resolve(SymbolStringPtr Name, + JITEvaluatedSymbol Sym) { // If OutstandingResolutions is zero we must have errored out already. Just // ignore this. if (OutstandingResolutions == 0) @@ -49,14 +143,11 @@ void AsynchronousSymbolQuery::setDefinition(SymbolStringPtr Name, 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) { + if (OutstandingResolutions == 0) NotifySymbolsResolved(std::move(Symbols)); - // Null out NotifySymbolsResolved to indicate that we've already called it. - NotifySymbolsResolved = {}; - } } -void AsynchronousSymbolQuery::notifySymbolFinalized() { +void AsynchronousSymbolQuery::finalizeSymbol() { // If OutstandingFinalizations is zero we must have errored out already. Just // ignore this. if (OutstandingFinalizations == 0) @@ -68,173 +159,115 @@ void AsynchronousSymbolQuery::notifySymbolFinalized() { NotifySymbolsReady(Error::success()); } -VSO::MaterializationInfo::MaterializationInfo( +VSO::UnmaterializedInfo::UnmaterializedInfo( size_t SymbolsRemaining, std::unique_ptr MU) : SymbolsRemaining(SymbolsRemaining), MU(std::move(MU)) {} -VSO::SymbolTableEntry::SymbolTableEntry( - JITSymbolFlags Flags, MaterializationInfoIterator MaterializationInfoItr) - : Flags(JITSymbolFlags::FlagNames(Flags | JITSymbolFlags::NotMaterialized)), - MaterializationInfoItr(std::move(MaterializationInfoItr)) { - // FIXME: Assert flag sanity. +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; } VSO::SymbolTableEntry::SymbolTableEntry(JITEvaluatedSymbol Sym) : Flags(Sym.getFlags()), Address(Sym.getAddress()) { - // FIXME: Assert flag sanity. + assert(!Flags.isLazy() && !Flags.isMaterializing() && + "This constructor is for final symbols only"); } -VSO::SymbolTableEntry::SymbolTableEntry(SymbolTableEntry &&Other) - : Flags(Other.Flags), Address(0) { - if (Flags.isMaterialized()) - Address = Other.Address; - else - MaterializationInfoItr = std::move(Other.MaterializationInfoItr); -} +// VSO::SymbolTableEntry::SymbolTableEntry(SymbolTableEntry &&Other) +// : Flags(Other.Flags), Address(0) { +// if (this->Flags.isLazy()) +// UMII = std::move(Other.UMII); +// else +// Address = Other.Address; +// } + +// 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; +// } VSO::SymbolTableEntry::~SymbolTableEntry() { destroy(); } -VSO::SymbolTableEntry &VSO::SymbolTableEntry:: -operator=(JITEvaluatedSymbol Sym) { +void VSO::SymbolTableEntry::replaceWith(VSO &V, SymbolStringPtr Name, + JITEvaluatedSymbol Sym) { + assert(!Flags.isMaterializing() && + "Attempting to replace definition during materialization?"); + if (Flags.isLazy()) { + if (UMII->MU) + UMII->MU->discard(V, Name); + V.detach(UMII); + } destroy(); Flags = Sym.getFlags(); Address = Sym.getAddress(); - return *this; -} - -void VSO::SymbolTableEntry::destroy() { - if (!Flags.isMaterialized()) - MaterializationInfoItr.~MaterializationInfoIterator(); } -JITSymbolFlags VSO::SymbolTableEntry::getFlags() const { return Flags; } - -void VSO::SymbolTableEntry::replaceWith( - VSO &V, SymbolStringPtr Name, JITSymbolFlags NewFlags, - MaterializationInfoIterator NewMaterializationInfoItr) { - bool ReplaceExistingLazyDefinition = !Flags.isMaterialized(); - Flags = NewFlags; - if (ReplaceExistingLazyDefinition) { - // If we are replacing an existing lazy definition with a stronger one, - // we need to notify the old lazy definition to discard its definition. - assert((*MaterializationInfoItr)->MU != nullptr && - (*MaterializationInfoItr)->Symbols.count(Name) == 0 && - (*MaterializationInfoItr)->PendingResolution.count(Name) == 0 && - (*MaterializationInfoItr)->PendingFinalization.count(Name) == 0 && - "Attempt to replace materializer during materialization"); - - if (--(*MaterializationInfoItr)->SymbolsRemaining == 0) - V.MaterializationInfos.erase(MaterializationInfoItr); +void VSO::SymbolTableEntry::replaceWith(VSO &V, SymbolStringPtr Name, + JITSymbolFlags NewFlags, + UnmaterializedInfoIterator NewUMII) { + assert(!Flags.isMaterializing() && + "Attempting to replace definition during materialization?"); + if (Flags.isLazy()) { + if (UMII->MU) + UMII->MU->discard(V, Name); + V.detach(UMII); } - MaterializationInfoItr = std::move(NewMaterializationInfoItr); + destroy(); + Flags = NewFlags; + UMII = std::move(NewUMII); } std::unique_ptr -VSO::SymbolTableEntry::query(SymbolStringPtr Name, - std::shared_ptr Query) { - if (Flags.isMaterialized()) { - Query->setDefinition(std::move(Name), JITEvaluatedSymbol(Address, Flags)); - Query->notifySymbolFinalized(); - return nullptr; - } else { - if ((*MaterializationInfoItr)->MU) { - assert((*MaterializationInfoItr)->PendingResolution.count(Name) == 0 && - (*MaterializationInfoItr)->PendingFinalization.count(Name) == 0 && - "Materializer should have been activated on first query"); - (*MaterializationInfoItr) - ->PendingResolution[Name] - .push_back(std::move(Query)); - return std::move((*MaterializationInfoItr)->MU); - } else { - assert((*MaterializationInfoItr)->MU == nullptr && - "Materializer should have been activated on first query"); - auto SymValueItr = (*MaterializationInfoItr)->Symbols.find(Name); - if (SymValueItr == (*MaterializationInfoItr)->Symbols.end()) { - // Symbol has not been resolved yet. - (*MaterializationInfoItr) - ->PendingResolution[Name] - .push_back(std::move(Query)); - return nullptr; - } else { - // Symbol has already resolved, is just waiting on finalization. - Query->setDefinition(Name, SymValueItr->second); - (*MaterializationInfoItr) - ->PendingFinalization[Name] - .push_back(std::move(Query)); - return nullptr; - } - } - } +VSO::SymbolTableEntry::initMaterialize(VSO &V) { + assert(Flags.isLazy() && "Can't materialize non-lazy symbol"); + auto TmpMU = std::move(UMII->MU); + V.detach(UMII); + destroy(); + Flags &= ~JITSymbolFlags::Lazy; + Flags |= JITSymbolFlags::Materializing; + Address = 0; + return TmpMU; } -void VSO::SymbolTableEntry::resolve(VSO &V, SymbolStringPtr Name, - JITEvaluatedSymbol Sym) { - if (Flags.isMaterialized()) { - // FIXME: Should we assert flag state here (flags must match except for - // materialization state, overrides must be legal) or in the caller - // in VSO? - Flags = Sym.getFlags(); - Address = Sym.getAddress(); - } else { - assert((*MaterializationInfoItr)->MU == nullptr && - "Can not resolve a symbol that has not been materialized"); - assert((*MaterializationInfoItr)->Symbols.count(Name) == 0 && - "Symbol resolved more than once"); - - // Add the symbol to the MaterializationInfo Symbols table. - (*MaterializationInfoItr)->Symbols[Name] = Sym; - - // If there are any queries waiting on this symbol then notify them that it - // has been resolved, then move them to the PendingFinalization list. - auto I = (*MaterializationInfoItr)->PendingResolution.find(Name); - if (I != (*MaterializationInfoItr)->PendingResolution.end()) { - assert((*MaterializationInfoItr)->PendingFinalization.count(Name) == 0 && - "Queries already pending finalization on newly resolved symbol"); - auto &PendingFinalization = - (*MaterializationInfoItr)->PendingFinalization[Name]; - - for (auto &Query : I->second) { - Query->setDefinition(Name, Sym); - PendingFinalization.push_back(Query); - } - - // Clear the PendingResolution list for this symbol. - (*MaterializationInfoItr)->PendingResolution.erase(I); - } +void VSO::SymbolTableEntry::resolve(VSO &V, JITEvaluatedSymbol Sym) { + if (Flags.isLazy()) { + assert(!UMII->MU && "Resolving with MaterializationUnit still attached?"); + V.detach(UMII); } + destroy(); + Flags = Sym.getFlags(); + Flags |= JITSymbolFlags::Materializing; + Address = Sym.getAddress(); } -void VSO::SymbolTableEntry::finalize(VSO &V, SymbolStringPtr Name) { - if (!Flags.isMaterialized()) { - auto SymI = (*MaterializationInfoItr)->Symbols.find(Name); - assert(SymI != (*MaterializationInfoItr)->Symbols.end() && - "Finalizing an unresolved symbol"); - auto Sym = SymI->second; - (*MaterializationInfoItr)->Symbols.erase(SymI); - auto I = (*MaterializationInfoItr)->PendingFinalization.find(Name); - if (I != (*MaterializationInfoItr)->PendingFinalization.end()) { - for (auto &Query : I->second) - Query->notifySymbolFinalized(); - (*MaterializationInfoItr)->PendingFinalization.erase(I); - } - - if (--(*MaterializationInfoItr)->SymbolsRemaining == 0) - V.MaterializationInfos.erase(MaterializationInfoItr); +void VSO::SymbolTableEntry::finalize() { + assert(Flags.isMaterializing() && !Flags.isLazy() && + "Symbol should be in materializing state"); + Flags &= ~JITSymbolFlags::Materializing; +} - // Destruct the iterator and re-define this entry using the final symbol - // value. - destroy(); - Flags = Sym.getFlags(); - Address = Sym.getAddress(); - } - assert(Flags.isMaterialized() && "Trying to finalize not-emitted symbol"); +void VSO::SymbolTableEntry::destroy() { + if (Flags.isLazy()) + UMII.~UnmaterializedInfoIterator(); } -void VSO::SymbolTableEntry::discard(VSO &V, SymbolStringPtr Name) { - assert((*MaterializationInfoItr)->MU != nullptr && - "Can not override a symbol after it has been materialized"); - (*MaterializationInfoItr)->MU->discard(V, Name); - --(*MaterializationInfoItr)->SymbolsRemaining; +void VSO::detach(UnmaterializedInfoIterator UMII) { + assert(UMII->SymbolsRemaining > 0 && + "Detaching from empty UnmaterializedInfo?"); + --UMII->SymbolsRemaining; + if (UMII->SymbolsRemaining == 0) + UnmaterializedInfos.erase(UMII); } VSO::RelativeLinkageStrength VSO::compareLinkage(Optional Old, @@ -258,10 +291,9 @@ VSO::RelativeLinkageStrength VSO::compareLinkage(Optional Old, VSO::RelativeLinkageStrength VSO::compareLinkage(SymbolStringPtr Name, JITSymbolFlags NewFlags) const { auto I = Symbols.find(Name); - return compareLinkage(I == Symbols.end() - ? None - : Optional(I->second.getFlags()), - NewFlags); + return compareLinkage( + I == Symbols.end() ? None : Optional(I->second.Flags), + NewFlags); } Error VSO::define(SymbolMap NewSymbols) { @@ -269,8 +301,7 @@ Error VSO::define(SymbolMap NewSymbols) { for (auto &KV : NewSymbols) { auto I = Symbols.find(KV.first); auto LinkageResult = compareLinkage( - I == Symbols.end() ? None - : Optional(I->second.getFlags()), + I == Symbols.end() ? None : Optional(I->second.Flags), KV.second.getFlags()); // Silently discard weaker definitions. @@ -284,11 +315,9 @@ Error VSO::define(SymbolMap NewSymbols) { continue; } - if (I != Symbols.end()) { - // This is an override -- discard the overridden definition and overwrite. - I->second.discard(*this, KV.first); - I->second = std::move(KV.second); - } else + 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; @@ -298,27 +327,26 @@ Error VSO::defineLazy(std::unique_ptr MU) { auto NewSymbols = MU->getSymbols(); - auto MaterializationInfoItr = - MaterializationInfos - .insert(llvm::make_unique(NewSymbols.size(), - std::move(MU))) - .first; + auto UMII = UnmaterializedInfos.insert( + UnmaterializedInfos.end(), + UnmaterializedInfo(NewSymbols.size(), std::move(MU))); Error Err = Error::success(); for (auto &KV : NewSymbols) { auto I = Symbols.find(KV.first); + assert(I == Symbols.end() || + !I->second.Flags.isMaterializing() && + "Attempt to replace materializing symbol definition"); + auto LinkageResult = compareLinkage( - I == Symbols.end() ? None - : Optional(I->second.getFlags()), + I == Symbols.end() ? None : Optional(I->second.Flags), KV.second); // Discard weaker definitions. if (LinkageResult == ExistingDefinitionIsStronger) { - (*MaterializationInfoItr)->MU->discard(*this, KV.first); - assert((*MaterializationInfoItr)->SymbolsRemaining > 0 && - "Discarding non-existant symbols?"); - --(*MaterializationInfoItr)->SymbolsRemaining; + UMII->MU->discard(*this, KV.first); + detach(UMII); continue; } @@ -328,41 +356,107 @@ Error VSO::defineLazy(std::unique_ptr MU) { make_error(*KV.first)); // Duplicate definitions are discarded, so remove the duplicates from // materializer. - assert((*MaterializationInfoItr)->SymbolsRemaining > 0 && - "Discarding non-existant symbols?"); - --(*MaterializationInfoItr)->SymbolsRemaining; + detach(UMII); continue; } + // Existing definition was weaker. Replace it. if (I != Symbols.end()) - I->second.replaceWith(*this, KV.first, KV.second, MaterializationInfoItr); + I->second.replaceWith(*this, KV.first, KV.second, UMII); else - Symbols.emplace(std::make_pair( - KV.first, SymbolTableEntry(KV.second, MaterializationInfoItr))); + Symbols.emplace( + std::make_pair(KV.first, SymbolTableEntry(KV.second, UMII))); } - // If we ended up overriding all definitions in this materializer then delete - // it. - if ((*MaterializationInfoItr)->SymbolsRemaining == 0) - MaterializationInfos.erase(MaterializationInfoItr); - return Err; } -void VSO::resolve(SymbolMap SymbolValues) { +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.first, std::move(KV.second)); + I->second.resolve(*this, KV.second); + + auto J = MaterializingInfos.find(KV.first); + if (J == MaterializingInfos.end()) + continue; + + 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(); } } -void VSO::finalize(SymbolNameSet SymbolsToFinalize) { +void VSO::notifyResolutionFailed(const SymbolNameSet &Names) { + assert(!Names.empty() && "Failed to resolve empty set?"); + + std::map, SymbolNameSet> + QueriesToFail; + + for (auto &S : Names) { + auto I = Symbols.find(S); + assert(I != Symbols.end() && "Symbol not present in this VSO"); + + auto J = MaterializingInfos.find(S); + if (J != MaterializingInfos.end()) { + assert(J->second.PendingFinalization.empty() && + "Failed during resolution, but queries pending finalization?"); + for (auto &Q : J->second.PendingResolution) + QueriesToFail[Q].insert(S); + MaterializingInfos.erase(J); + } + Symbols.erase(I); + } + + for (auto &KV : QueriesToFail) + KV.first->notifyFailed(make_error(std::move(KV.second))); +} + +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"); - I->second.finalize(*this, S); + + 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); + } + I->second.finalize(); + } +} + +void VSO::notifyFinalizationFailed(const SymbolNameSet &Names) { + assert(!Names.empty() && "Failed to finalize empty set?"); + + std::map, SymbolNameSet> + QueriesToFail; + + for (auto &S : Names) { + auto I = Symbols.find(S); + assert(I != Symbols.end() && "Symbol not present in this VSO"); + assert((I->second.Flags & JITSymbolFlags::Materializing) && + "Failed to finalize symbol that was not materializing"); + + auto J = MaterializingInfos.find(S); + if (J != MaterializingInfos.end()) { + assert(J->second.PendingResolution.empty() && + "Failed during finalization, but queries pending resolution?"); + for (auto &Q : J->second.PendingFinalization) + QueriesToFail[Q].insert(S); + MaterializingInfos.erase(J); + } + Symbols.erase(I); } + + for (auto &KV : QueriesToFail) + KV.first->notifyFailed(make_error(std::move(KV.second))); } SymbolNameSet VSO::lookupFlags(SymbolFlagsMap &Flags, SymbolNameSet Names) { @@ -378,7 +472,7 @@ SymbolNameSet VSO::lookupFlags(SymbolFlagsMap &Flags, SymbolNameSet Names) { Names.erase(Tmp); Flags[SymI->first] = - JITSymbolFlags::stripTransientFlags(SymI->second.getFlags()); + JITSymbolFlags::stripTransientFlags(SymI->second.Flags); } return Names; @@ -396,14 +490,43 @@ VSO::LookupResult VSO::lookup(std::shared_ptr Query, if (SymI == Symbols.end()) continue; - // The symbol is in the dylib. Erase it from Names and proceed. + // The symbol is in the VSO. Erase it from Names and proceed. Names.erase(Tmp); - // Forward the query to the given SymbolTableEntry, and if it return a - // layer to perform materialization with, add that to the - // MaterializationWork map. - if (auto MU = SymI->second.query(SymI->first, Query)) - MaterializationUnits.push_back(std::move(MU)); + // If this symbol has not been materialized yet, move it to materializing, + // then fall through to the materializing case below. + if (SymI->second.Flags.isLazy()) { + if (auto MU = SymI->second.initMaterialize(*this)) + MaterializationUnits.push_back(std::move(MU)); + } + + // 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); + if (J == MaterializingInfos.end()) + J = MaterializingInfos + .insert(std::make_pair(SymI->first, MaterializingInfo())) + .first; + + 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(MaterializationUnits), std::move(Names)}; diff --git a/lib/ExecutionEngine/Orc/OrcCBindingsStack.h b/lib/ExecutionEngine/Orc/OrcCBindingsStack.h index a14859ae98f..ab0c9f07a14 100644 --- a/lib/ExecutionEngine/Orc/OrcCBindingsStack.h +++ b/lib/ExecutionEngine/Orc/OrcCBindingsStack.h @@ -153,13 +153,13 @@ private: for (auto &S : Symbols) { if (auto Sym = findSymbol(*S)) { if (auto Addr = Sym.getAddress()) - Query->setDefinition(S, JITEvaluatedSymbol(*Addr, Sym.getFlags())); + Query->resolve(S, JITEvaluatedSymbol(*Addr, Sym.getFlags())); else { - Query->setFailed(Addr.takeError()); + Query->notifyFailed(Addr.takeError()); return orc::SymbolNameSet(); } } else if (auto Err = Sym.takeError()) { - Query->setFailed(std::move(Err)); + Query->notifyFailed(std::move(Err)); return orc::SymbolNameSet(); } else UnresolvedSymbols.insert(S); diff --git a/lib/ExecutionEngine/Orc/OrcError.cpp b/lib/ExecutionEngine/Orc/OrcError.cpp index f0bfed8ddb8..f4102b359a6 100644 --- a/lib/ExecutionEngine/Orc/OrcError.cpp +++ b/lib/ExecutionEngine/Orc/OrcError.cpp @@ -29,6 +29,8 @@ public: std::string message(int condition) const override { switch (static_cast(condition)) { + case OrcErrorCode::UnknownORCError: + return "Unknown ORC error"; case OrcErrorCode::DuplicateDefinition: return "Duplicate symbol definition"; case OrcErrorCode::JITSymbolNotFound: diff --git a/lib/ExecutionEngine/Orc/OrcMCJITReplacement.h b/lib/ExecutionEngine/Orc/OrcMCJITReplacement.h index af87df9ac34..73af52055e8 100644 --- a/lib/ExecutionEngine/Orc/OrcMCJITReplacement.h +++ b/lib/ExecutionEngine/Orc/OrcMCJITReplacement.h @@ -175,25 +175,24 @@ class OrcMCJITReplacement : public ExecutionEngine { for (auto &S : Symbols) { if (auto Sym = M.findMangledSymbol(*S)) { if (auto Addr = Sym.getAddress()) - Query->setDefinition(S, JITEvaluatedSymbol(*Addr, Sym.getFlags())); + Query->resolve(S, JITEvaluatedSymbol(*Addr, Sym.getFlags())); else { - Query->setFailed(Addr.takeError()); + Query->notifyFailed(Addr.takeError()); return SymbolNameSet(); } } else if (auto Err = Sym.takeError()) { - Query->setFailed(std::move(Err)); + Query->notifyFailed(std::move(Err)); return SymbolNameSet(); } else { if (auto Sym2 = M.ClientResolver->findSymbol(*S)) { if (auto Addr = Sym2.getAddress()) - Query->setDefinition(S, - JITEvaluatedSymbol(*Addr, Sym2.getFlags())); + Query->resolve(S, JITEvaluatedSymbol(*Addr, Sym2.getFlags())); else { - Query->setFailed(Addr.takeError()); + Query->notifyFailed(Addr.takeError()); return SymbolNameSet(); } } else if (auto Err = Sym2.takeError()) { - Query->setFailed(std::move(Err)); + Query->notifyFailed(std::move(Err)); return SymbolNameSet(); } else UnresolvedSymbols.insert(S); diff --git a/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp b/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp index 878f0a532b8..0a87ed08edf 100644 --- a/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp +++ b/unittests/ExecutionEngine/Orc/CoreAPIsTest.cpp @@ -24,12 +24,19 @@ public: using GetSymbolsFunction = std::function; using MaterializeFunction = std::function; using DiscardFunction = std::function; + using DestructorFunction = std::function; - SimpleMaterializationUnit(GetSymbolsFunction GetSymbols, - MaterializeFunction Materialize, - DiscardFunction Discard) + SimpleMaterializationUnit( + GetSymbolsFunction GetSymbols, MaterializeFunction Materialize, + DiscardFunction Discard, + DestructorFunction Destructor = DestructorFunction()) : GetSymbols(std::move(GetSymbols)), Materialize(std::move(Materialize)), - Discard(std::move(Discard)) {} + Discard(std::move(Discard)), Destructor(std::move(Destructor)) {} + + ~SimpleMaterializationUnit() override { + if (Destructor) + Destructor(); + } SymbolFlagsMap getSymbols() override { return GetSymbols(); } @@ -43,6 +50,7 @@ private: GetSymbolsFunction GetSymbols; MaterializeFunction Materialize; DiscardFunction Discard; + DestructorFunction Destructor; }; TEST(CoreAPIsTest, AsynchronousSymbolQuerySuccessfulResolutionOnly) { @@ -68,7 +76,7 @@ TEST(CoreAPIsTest, AsynchronousSymbolQuerySuccessfulResolutionOnly) { AsynchronousSymbolQuery Q(Names, OnResolution, OnReady); - Q.setDefinition(Foo, JITEvaluatedSymbol(FakeAddr, JITSymbolFlags::Exported)); + Q.resolve(Foo, JITEvaluatedSymbol(FakeAddr, JITSymbolFlags::Exported)); EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run"; EXPECT_FALSE(OnReadyRun) << "OnReady unexpectedly run"; @@ -95,7 +103,7 @@ TEST(CoreAPIsTest, AsynchronousSymbolQueryResolutionErrorOnly) { AsynchronousSymbolQuery Q(Names, OnResolution, OnReady); - Q.setFailed(make_error("xyz", inconvertibleErrorCode())); + Q.notifyFailed(make_error("xyz", inconvertibleErrorCode())); EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run"; EXPECT_FALSE(OnReadyRun) << "OnReady unexpectedly run"; @@ -186,6 +194,44 @@ TEST(CoreAPIsTest, LookupFlagsTest) { EXPECT_EQ(SymbolFlags[Bar], BarFlags) << "Incorrect flags returned for Bar"; } +TEST(CoreAPIsTest, DropMaterializerWhenEmpty) { + SymbolStringPool SP; + auto Foo = SP.intern("foo"); + auto Bar = SP.intern("bar"); + + bool DestructorRun = false; + + auto MU = llvm::make_unique( + [=]() { + return SymbolFlagsMap( + {{Foo, JITSymbolFlags::Weak}, {Bar, JITSymbolFlags::Weak}}); + }, + [](VSO &V) -> Error { + llvm_unreachable("Unexpected call to materialize"); + }, + [&](VSO &V, SymbolStringPtr Name) { + EXPECT_TRUE(Name == Foo || Name == Bar) + << "Discard of unexpected symbol?"; + }, + [&]() { DestructorRun = true; }); + + VSO V; + + cantFail(V.defineLazy(std::move(MU))); + + auto FooSym = JITEvaluatedSymbol(1, JITSymbolFlags::Exported); + auto BarSym = JITEvaluatedSymbol(2, JITSymbolFlags::Exported); + cantFail(V.define(SymbolMap({{Foo, FooSym}}))); + + EXPECT_FALSE(DestructorRun) + << "MaterializationUnit should not have been destroyed yet"; + + cantFail(V.define(SymbolMap({{Bar, BarSym}}))); + + EXPECT_TRUE(DestructorRun) + << "MaterializationUnit should have been destroyed"; +} + TEST(CoreAPIsTest, AddAndMaterializeLazySymbol) { constexpr JITTargetAddress FakeFooAddr = 0xdeadbeef; @@ -264,6 +310,121 @@ TEST(CoreAPIsTest, AddAndMaterializeLazySymbol) { EXPECT_TRUE(OnReadyRun) << "OnReady was not run"; } +TEST(CoreAPIsTest, FailResolution) { + SymbolStringPool SP; + auto Foo = SP.intern("foo"); + auto Bar = SP.intern("bar"); + + SymbolNameSet Names({Foo, Bar}); + + auto MU = llvm::make_unique( + [=]() { + return SymbolFlagsMap( + {{Foo, JITSymbolFlags::Weak}, {Bar, JITSymbolFlags::Weak}}); + }, + [&](VSO &V) -> Error { + V.notifyResolutionFailed(Names); + return Error::success(); + }, + [&](VSO &V, SymbolStringPtr Name) { + llvm_unreachable("Unexpected call to discard"); + }); + + VSO V; + + cantFail(V.defineLazy(std::move(MU))); + + auto OnResolution = [&](Expected Result) { + handleAllErrors(Result.takeError(), + [&](FailedToResolve &F) { + EXPECT_EQ(F.getSymbols(), Names) + << "Expected to fail on symbols in Names"; + }, + [](ErrorInfoBase &EIB) { + std::string ErrMsg; + { + raw_string_ostream ErrOut(ErrMsg); + EIB.log(ErrOut); + } + ADD_FAILURE() + << "Expected a FailedToResolve error. Got:\n" + << ErrMsg; + }); + }; + + auto OnReady = [](Error Err) { + cantFail(std::move(Err)); + ADD_FAILURE() << "OnReady should never be called"; + }; + + auto Q = + std::make_shared(Names, OnResolution, OnReady); + + auto LR = V.lookup(std::move(Q), Names); + for (auto &SWKV : LR.MaterializationUnits) + cantFail(SWKV->materialize(V)); +} + +TEST(CoreAPIsTest, FailFinalization) { + SymbolStringPool SP; + auto Foo = SP.intern("foo"); + auto Bar = SP.intern("bar"); + + SymbolNameSet Names({Foo, Bar}); + + auto MU = llvm::make_unique( + [=]() { + return SymbolFlagsMap( + {{Foo, JITSymbolFlags::Weak}, {Bar, JITSymbolFlags::Weak}}); + }, + [&](VSO &V) -> Error { + constexpr JITTargetAddress FakeFooAddr = 0xdeadbeef; + constexpr JITTargetAddress FakeBarAddr = 0xcafef00d; + + auto FooSym = JITEvaluatedSymbol(FakeFooAddr, JITSymbolFlags::Exported); + auto BarSym = JITEvaluatedSymbol(FakeBarAddr, JITSymbolFlags::Exported); + V.resolve(SymbolMap({{Foo, FooSym}, {Bar, BarSym}})); + V.notifyFinalizationFailed(Names); + return Error::success(); + }, + [&](VSO &V, SymbolStringPtr Name) { + llvm_unreachable("Unexpected call to discard"); + }); + + VSO V; + + cantFail(V.defineLazy(std::move(MU))); + + auto OnResolution = [](Expected Result) { + cantFail(std::move(Result)); + }; + + auto OnReady = [&](Error Err) { + handleAllErrors(std::move(Err), + [&](FailedToFinalize &F) { + EXPECT_EQ(F.getSymbols(), Names) + << "Expected to fail on symbols in Names"; + }, + [](ErrorInfoBase &EIB) { + std::string ErrMsg; + { + raw_string_ostream ErrOut(ErrMsg); + EIB.log(ErrOut); + } + ADD_FAILURE() + << "Expected a FailedToFinalize error. Got:\n" + << ErrMsg; + }); + }; + + auto Q = + std::make_shared(Names, OnResolution, OnReady); + + auto LR = V.lookup(std::move(Q), Names); + for (auto &SWKV : LR.MaterializationUnits) + cantFail(SWKV->materialize(V)); +} + TEST(CoreAPIsTest, TestLambdaSymbolResolver) { JITEvaluatedSymbol FooSym(0xdeadbeef, JITSymbolFlags::Exported); JITEvaluatedSymbol BarSym(0xcafef00d, JITSymbolFlags::Exported); -- 2.11.0