1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-19 11:02:59 +02:00

[ORC] Plumb error notifications through the VSO interface.

This allows materializers to notify the VSO that they were unable to
resolve or finalize symbols.

llvm-svn: 329934
This commit is contained in:
Lang Hames 2018-04-12 18:35:08 +00:00
parent 42742ad477
commit 7a54926cb9
9 changed files with 623 additions and 256 deletions

View File

@ -52,11 +52,12 @@ public:
Common = 1U << 2, Common = 1U << 2,
Absolute = 1U << 3, Absolute = 1U << 3,
Exported = 1U << 4, Exported = 1U << 4,
NotMaterialized = 1U << 5 Lazy = 1U << 5,
Materializing = 1U << 6
}; };
static JITSymbolFlags stripTransientFlags(JITSymbolFlags Orig) { static JITSymbolFlags stripTransientFlags(JITSymbolFlags Orig) {
return static_cast<FlagNames>(Orig.Flags & ~NotMaterialized); return static_cast<FlagNames>(Orig.Flags & ~Lazy & ~Materializing);
} }
/// @brief Default-construct a JITSymbolFlags instance. /// @brief Default-construct a JITSymbolFlags instance.
@ -75,9 +76,18 @@ public:
return (Flags & HasError) == HasError; return (Flags & HasError) == HasError;
} }
/// @brief Returns true if this symbol has been fully materialized (i.e. is /// @brief Returns true if this is a lazy symbol.
/// callable). /// This flag is used internally by the JIT APIs to track
bool isMaterialized() const { return !(Flags & NotMaterialized); } /// 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. /// @brief Returns true if the Weak flag is set.
bool isWeak() const { bool isWeak() const {

View File

@ -17,6 +17,7 @@
#include "llvm/ExecutionEngine/JITSymbol.h" #include "llvm/ExecutionEngine/JITSymbol.h"
#include "llvm/ExecutionEngine/Orc/SymbolStringPool.h" #include "llvm/ExecutionEngine/Orc/SymbolStringPool.h"
#include <list>
#include <map> #include <map>
#include <memory> #include <memory>
#include <set> #include <set>
@ -25,23 +26,71 @@
namespace llvm { namespace llvm {
namespace orc { namespace orc {
// Forward declare some classes.
class VSO;
/// VModuleKey provides a unique identifier (allocated and managed by /// VModuleKey provides a unique identifier (allocated and managed by
/// ExecutionSessions) for a module added to the JIT. /// ExecutionSessions) for a module added to the JIT.
using VModuleKey = uint64_t; using VModuleKey = uint64_t;
class VSO;
/// @brief A set of symbol names (represented by SymbolStringPtrs for /// @brief A set of symbol names (represented by SymbolStringPtrs for
// efficiency). // efficiency).
using SymbolNameSet = std::set<SymbolStringPtr>; using SymbolNameSet = std::set<SymbolStringPtr>;
/// @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 /// @brief A map from symbol names (as SymbolStringPtrs) to JITSymbols
/// (address/flags pairs). /// (address/flags pairs).
using SymbolMap = std::map<SymbolStringPtr, JITEvaluatedSymbol>; using SymbolMap = std::map<SymbolStringPtr, JITEvaluatedSymbol>;
/// @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. /// @brief A map from symbol names (as SymbolStringPtrs) to JITSymbolFlags.
using SymbolFlagsMap = std::map<SymbolStringPtr, JITSymbolFlags>; using SymbolFlagsMap = std::map<SymbolStringPtr, JITSymbolFlags>;
/// @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<FailedToMaterialize> {
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<FailedToResolve, FailedToMaterialize> {
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<FailedToFinalize, FailedToMaterialize> {
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 /// @brief A symbol query that returns results via a callback when results are
/// ready. /// ready.
/// ///
@ -69,20 +118,20 @@ public:
/// notify-finalized callback is called with the given error. /// notify-finalized callback is called with the given error.
/// ///
/// It is illegal to call setFailed after both callbacks have been made. /// 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. /// @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 /// If this symbol was the last one not resolved, this will trigger a call to
/// the notify-finalized callback passing the completed sybol map. /// 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. /// @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 decrements the query's internal count of not-yet-ready symbols. If
/// this call to notifySymbolFinalized sets the counter to zero, it will call /// this call to notifySymbolFinalized sets the counter to zero, it will call
/// the notify-finalized callback with Error::success as the value. /// the notify-finalized callback with Error::success as the value.
void notifySymbolFinalized(); void finalizeSymbol();
private: private:
SymbolMap Symbols; SymbolMap Symbols;
@ -240,10 +289,16 @@ public:
/// @brief Add the given symbol/address mappings to the dylib, but do not /// @brief Add the given symbol/address mappings to the dylib, but do not
/// mark the symbols as finalized yet. /// 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. /// @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. /// @brief Look up the flags for the given symbols.
/// ///
@ -267,56 +322,72 @@ public:
SymbolNameSet Symbols); SymbolNameSet Symbols);
private: private:
class MaterializationInfo { class UnmaterializedInfo {
public: public:
using QueryList = std::vector<std::shared_ptr<AsynchronousSymbolQuery>>; UnmaterializedInfo(size_t SymbolsRemaining,
std::unique_ptr<MaterializationUnit> MU);
MaterializationInfo(size_t SymbolsRemaining,
std::unique_ptr<MaterializationUnit> MU);
uint64_t SymbolsRemaining; uint64_t SymbolsRemaining;
std::unique_ptr<MaterializationUnit> MU; std::unique_ptr<MaterializationUnit> MU;
SymbolMap Symbols;
std::map<SymbolStringPtr, QueryList> PendingResolution;
std::map<SymbolStringPtr, QueryList> PendingFinalization;
}; };
using MaterializationInfoSet = std::set<std::unique_ptr<MaterializationInfo>>; using UnmaterializedInfoList = std::list<UnmaterializedInfo>;
using MaterializationInfoIterator = MaterializationInfoSet::iterator; using UnmaterializedInfoIterator = UnmaterializedInfoList::iterator;
class MaterializingInfo {
public:
using QueryList = std::vector<std::shared_ptr<AsynchronousSymbolQuery>>;
QueryList PendingResolution;
QueryList PendingFinalization;
};
using MaterializingInfoMap = std::map<SymbolStringPtr, MaterializingInfo>;
using MaterializingInfoIterator = MaterializingInfoMap::iterator;
class SymbolTableEntry { class SymbolTableEntry {
public: public:
SymbolTableEntry(JITSymbolFlags SymbolFlags, SymbolTableEntry(JITSymbolFlags SymbolFlags,
MaterializationInfoIterator MaterializationInfoItr); UnmaterializedInfoIterator UnmaterializedInfoItr);
SymbolTableEntry(JITEvaluatedSymbol Sym); SymbolTableEntry(JITEvaluatedSymbol Sym);
SymbolTableEntry(SymbolTableEntry &&Other); // SymbolTableEntry(SymbolTableEntry &&Other);
// SymbolTableEntry &operator=(SymbolTableEntry &&Other);
~SymbolTableEntry(); ~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, void replaceWith(VSO &V, SymbolStringPtr Name, JITSymbolFlags Flags,
MaterializationInfoIterator NewMaterializationInfoItr); UnmaterializedInfoIterator NewUMII);
std::unique_ptr<MaterializationUnit>
query(SymbolStringPtr Name, std::shared_ptr<AsynchronousSymbolQuery> Query); // Move entry to materializing state, detach from UMII.
void resolve(VSO &V, SymbolStringPtr Name, JITEvaluatedSymbol Sym); std::unique_ptr<MaterializationUnit> initMaterialize(VSO &V);
void finalize(VSO &V, SymbolStringPtr Name);
void discard(VSO &V, SymbolStringPtr Name); // 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: private:
void destroy(); void destroy();
JITSymbolFlags Flags;
MaterializationInfoIterator MII;
union {
JITTargetAddress Address;
MaterializationInfoIterator MaterializationInfoItr;
};
}; };
void detach(UnmaterializedInfoIterator UMII);
std::map<SymbolStringPtr, SymbolTableEntry> Symbols; std::map<SymbolStringPtr, SymbolTableEntry> Symbols;
MaterializationInfoSet MaterializationInfos; UnmaterializedInfoList UnmaterializedInfos;
MaterializingInfoMap MaterializingInfos;
}; };
/// @brief An ExecutionSession represents a running JIT program. /// @brief An ExecutionSession represents a running JIT program.

View File

@ -62,7 +62,7 @@ Expected<SymbolNameSet> lookupFlagsWithLegacyFn(SymbolFlagsMap &SymbolFlags,
/// takes a const std::string& or StringRef and returns a JITSymbol) to /// 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 /// 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 /// 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 /// function returns immediately. On success, returns the set of symbols
/// not found. /// not found.
/// ///
@ -76,14 +76,14 @@ SymbolNameSet lookupWithLegacyFn(AsynchronousSymbolQuery &Query,
for (auto &S : Symbols) { for (auto &S : Symbols) {
if (JITSymbol Sym = FindSymbol(*S)) { if (JITSymbol Sym = FindSymbol(*S)) {
if (auto Addr = Sym.getAddress()) { if (auto Addr = Sym.getAddress()) {
Query.setDefinition(S, JITEvaluatedSymbol(*Addr, Sym.getFlags())); Query.resolve(S, JITEvaluatedSymbol(*Addr, Sym.getFlags()));
Query.notifySymbolFinalized(); Query.finalizeSymbol();
} else { } else {
Query.setFailed(Addr.takeError()); Query.notifyFailed(Addr.takeError());
return SymbolNameSet(); return SymbolNameSet();
} }
} else if (auto Err = Sym.takeError()) { } else if (auto Err = Sym.takeError()) {
Query.setFailed(std::move(Err)); Query.notifyFailed(std::move(Err));
return SymbolNameSet(); return SymbolNameSet();
} else } else
SymbolsNotFound.insert(S); SymbolsNotFound.insert(S);

View File

@ -22,7 +22,8 @@ namespace orc {
enum class OrcErrorCode : int { enum class OrcErrorCode : int {
// RPC Errors // RPC Errors
DuplicateDefinition = 1, UnknownORCError = 1,
DuplicateDefinition,
JITSymbolNotFound, JITSymbolNotFound,
RemoteAllocatorDoesNotExist, RemoteAllocatorDoesNotExist,
RemoteAllocatorIdAlreadyInUse, RemoteAllocatorIdAlreadyInUse,

View File

@ -9,6 +9,7 @@
#include "llvm/ExecutionEngine/Orc/Core.h" #include "llvm/ExecutionEngine/Orc/Core.h"
#include "llvm/ExecutionEngine/Orc/OrcError.h" #include "llvm/ExecutionEngine/Orc/OrcError.h"
#include "llvm/Support/Format.h"
#if LLVM_ENABLE_THREADS #if LLVM_ENABLE_THREADS
#include <future> #include <future>
@ -17,9 +18,100 @@
namespace llvm { namespace llvm {
namespace orc { namespace orc {
char FailedToMaterialize::ID = 0;
char FailedToResolve::ID = 0;
char FailedToFinalize::ID = 0;
void MaterializationUnit::anchor() {} void MaterializationUnit::anchor() {}
void SymbolResolver::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( AsynchronousSymbolQuery::AsynchronousSymbolQuery(
const SymbolNameSet &Symbols, SymbolsResolvedCallback NotifySymbolsResolved, const SymbolNameSet &Symbols, SymbolsResolvedCallback NotifySymbolsResolved,
SymbolsReadyCallback NotifySymbolsReady) SymbolsReadyCallback NotifySymbolsReady)
@ -31,16 +123,18 @@ AsynchronousSymbolQuery::AsynchronousSymbolQuery(
OutstandingResolutions = OutstandingFinalizations = Symbols.size(); OutstandingResolutions = OutstandingFinalizations = Symbols.size();
} }
void AsynchronousSymbolQuery::setFailed(Error Err) { void AsynchronousSymbolQuery::notifyFailed(Error Err) {
OutstandingResolutions = OutstandingFinalizations = 0; if (OutstandingResolutions != 0)
if (NotifySymbolsResolved)
NotifySymbolsResolved(std::move(Err)); NotifySymbolsResolved(std::move(Err));
else else if (OutstandingFinalizations != 0)
NotifySymbolsReady(std::move(Err)); NotifySymbolsReady(std::move(Err));
else
consumeError(std::move(Err));
OutstandingResolutions = OutstandingFinalizations = 0;
} }
void AsynchronousSymbolQuery::setDefinition(SymbolStringPtr Name, void AsynchronousSymbolQuery::resolve(SymbolStringPtr Name,
JITEvaluatedSymbol Sym) { JITEvaluatedSymbol Sym) {
// If OutstandingResolutions is zero we must have errored out already. Just // If OutstandingResolutions is zero we must have errored out already. Just
// ignore this. // ignore this.
if (OutstandingResolutions == 0) if (OutstandingResolutions == 0)
@ -49,14 +143,11 @@ void AsynchronousSymbolQuery::setDefinition(SymbolStringPtr Name,
assert(!Symbols.count(Name) && "Symbol has already been assigned an address"); assert(!Symbols.count(Name) && "Symbol has already been assigned an address");
Symbols.insert(std::make_pair(std::move(Name), std::move(Sym))); Symbols.insert(std::make_pair(std::move(Name), std::move(Sym)));
--OutstandingResolutions; --OutstandingResolutions;
if (OutstandingResolutions == 0) { if (OutstandingResolutions == 0)
NotifySymbolsResolved(std::move(Symbols)); 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 // If OutstandingFinalizations is zero we must have errored out already. Just
// ignore this. // ignore this.
if (OutstandingFinalizations == 0) if (OutstandingFinalizations == 0)
@ -68,173 +159,115 @@ void AsynchronousSymbolQuery::notifySymbolFinalized() {
NotifySymbolsReady(Error::success()); NotifySymbolsReady(Error::success());
} }
VSO::MaterializationInfo::MaterializationInfo( VSO::UnmaterializedInfo::UnmaterializedInfo(
size_t SymbolsRemaining, std::unique_ptr<MaterializationUnit> MU) size_t SymbolsRemaining, std::unique_ptr<MaterializationUnit> MU)
: SymbolsRemaining(SymbolsRemaining), MU(std::move(MU)) {} : SymbolsRemaining(SymbolsRemaining), MU(std::move(MU)) {}
VSO::SymbolTableEntry::SymbolTableEntry( VSO::SymbolTableEntry::SymbolTableEntry(JITSymbolFlags Flags,
JITSymbolFlags Flags, MaterializationInfoIterator MaterializationInfoItr) UnmaterializedInfoIterator UMII)
: Flags(JITSymbolFlags::FlagNames(Flags | JITSymbolFlags::NotMaterialized)), : Flags(Flags), UMII(std::move(UMII)) {
MaterializationInfoItr(std::move(MaterializationInfoItr)) { // We *don't* expect isLazy to be set here. That's for the VSO to do.
// FIXME: Assert flag sanity. assert(!Flags.isLazy() && "Initial flags include lazy?");
assert(!Flags.isMaterializing() && "Initial flags include materializing");
this->Flags |= JITSymbolFlags::Lazy;
} }
VSO::SymbolTableEntry::SymbolTableEntry(JITEvaluatedSymbol Sym) VSO::SymbolTableEntry::SymbolTableEntry(JITEvaluatedSymbol Sym)
: Flags(Sym.getFlags()), Address(Sym.getAddress()) { : 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) // VSO::SymbolTableEntry::SymbolTableEntry(SymbolTableEntry &&Other)
: Flags(Other.Flags), Address(0) { // : Flags(Other.Flags), Address(0) {
if (Flags.isMaterialized()) // if (this->Flags.isLazy())
Address = Other.Address; // UMII = std::move(Other.UMII);
else // else
MaterializationInfoItr = std::move(Other.MaterializationInfoItr); // 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::~SymbolTableEntry() { destroy(); }
VSO::SymbolTableEntry &VSO::SymbolTableEntry:: void VSO::SymbolTableEntry::replaceWith(VSO &V, SymbolStringPtr Name,
operator=(JITEvaluatedSymbol Sym) { 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(); destroy();
Flags = Sym.getFlags(); Flags = Sym.getFlags();
Address = Sym.getAddress(); Address = Sym.getAddress();
return *this;
} }
void VSO::SymbolTableEntry::destroy() { void VSO::SymbolTableEntry::replaceWith(VSO &V, SymbolStringPtr Name,
if (!Flags.isMaterialized()) JITSymbolFlags NewFlags,
MaterializationInfoItr.~MaterializationInfoIterator(); UnmaterializedInfoIterator NewUMII) {
} assert(!Flags.isMaterializing() &&
"Attempting to replace definition during materialization?");
JITSymbolFlags VSO::SymbolTableEntry::getFlags() const { return Flags; } if (Flags.isLazy()) {
if (UMII->MU)
void VSO::SymbolTableEntry::replaceWith( UMII->MU->discard(V, Name);
VSO &V, SymbolStringPtr Name, JITSymbolFlags NewFlags, V.detach(UMII);
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);
} }
MaterializationInfoItr = std::move(NewMaterializationInfoItr); destroy();
Flags = NewFlags;
UMII = std::move(NewUMII);
} }
std::unique_ptr<MaterializationUnit> std::unique_ptr<MaterializationUnit>
VSO::SymbolTableEntry::query(SymbolStringPtr Name, VSO::SymbolTableEntry::initMaterialize(VSO &V) {
std::shared_ptr<AsynchronousSymbolQuery> Query) { assert(Flags.isLazy() && "Can't materialize non-lazy symbol");
if (Flags.isMaterialized()) { auto TmpMU = std::move(UMII->MU);
Query->setDefinition(std::move(Name), JITEvaluatedSymbol(Address, Flags)); V.detach(UMII);
Query->notifySymbolFinalized(); destroy();
return nullptr; Flags &= ~JITSymbolFlags::Lazy;
} else { Flags |= JITSymbolFlags::Materializing;
if ((*MaterializationInfoItr)->MU) { Address = 0;
assert((*MaterializationInfoItr)->PendingResolution.count(Name) == 0 && return TmpMU;
(*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;
}
}
}
} }
void VSO::SymbolTableEntry::resolve(VSO &V, SymbolStringPtr Name, void VSO::SymbolTableEntry::resolve(VSO &V, JITEvaluatedSymbol Sym) {
JITEvaluatedSymbol Sym) { if (Flags.isLazy()) {
if (Flags.isMaterialized()) { assert(!UMII->MU && "Resolving with MaterializationUnit still attached?");
// FIXME: Should we assert flag state here (flags must match except for V.detach(UMII);
// 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);
}
} }
destroy();
Flags = Sym.getFlags();
Flags |= JITSymbolFlags::Materializing;
Address = Sym.getAddress();
} }
void VSO::SymbolTableEntry::finalize(VSO &V, SymbolStringPtr Name) { void VSO::SymbolTableEntry::finalize() {
if (!Flags.isMaterialized()) { assert(Flags.isMaterializing() && !Flags.isLazy() &&
auto SymI = (*MaterializationInfoItr)->Symbols.find(Name); "Symbol should be in materializing state");
assert(SymI != (*MaterializationInfoItr)->Symbols.end() && Flags &= ~JITSymbolFlags::Materializing;
"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);
// 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::discard(VSO &V, SymbolStringPtr Name) { void VSO::SymbolTableEntry::destroy() {
assert((*MaterializationInfoItr)->MU != nullptr && if (Flags.isLazy())
"Can not override a symbol after it has been materialized"); UMII.~UnmaterializedInfoIterator();
(*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<JITSymbolFlags> Old, VSO::RelativeLinkageStrength VSO::compareLinkage(Optional<JITSymbolFlags> Old,
@ -258,10 +291,9 @@ VSO::RelativeLinkageStrength VSO::compareLinkage(Optional<JITSymbolFlags> Old,
VSO::RelativeLinkageStrength VSO::RelativeLinkageStrength
VSO::compareLinkage(SymbolStringPtr Name, JITSymbolFlags NewFlags) const { VSO::compareLinkage(SymbolStringPtr Name, JITSymbolFlags NewFlags) const {
auto I = Symbols.find(Name); auto I = Symbols.find(Name);
return compareLinkage(I == Symbols.end() return compareLinkage(
? None I == Symbols.end() ? None : Optional<JITSymbolFlags>(I->second.Flags),
: Optional<JITSymbolFlags>(I->second.getFlags()), NewFlags);
NewFlags);
} }
Error VSO::define(SymbolMap NewSymbols) { Error VSO::define(SymbolMap NewSymbols) {
@ -269,8 +301,7 @@ Error VSO::define(SymbolMap NewSymbols) {
for (auto &KV : NewSymbols) { for (auto &KV : NewSymbols) {
auto I = Symbols.find(KV.first); auto I = Symbols.find(KV.first);
auto LinkageResult = compareLinkage( auto LinkageResult = compareLinkage(
I == Symbols.end() ? None I == Symbols.end() ? None : Optional<JITSymbolFlags>(I->second.Flags),
: Optional<JITSymbolFlags>(I->second.getFlags()),
KV.second.getFlags()); KV.second.getFlags());
// Silently discard weaker definitions. // Silently discard weaker definitions.
@ -284,11 +315,9 @@ Error VSO::define(SymbolMap NewSymbols) {
continue; continue;
} }
if (I != Symbols.end()) { if (I != Symbols.end())
// This is an override -- discard the overridden definition and overwrite. I->second.replaceWith(*this, I->first, KV.second);
I->second.discard(*this, KV.first); else
I->second = std::move(KV.second);
} else
Symbols.insert(std::make_pair(KV.first, std::move(KV.second))); Symbols.insert(std::make_pair(KV.first, std::move(KV.second)));
} }
return Err; return Err;
@ -298,27 +327,26 @@ Error VSO::defineLazy(std::unique_ptr<MaterializationUnit> MU) {
auto NewSymbols = MU->getSymbols(); auto NewSymbols = MU->getSymbols();
auto MaterializationInfoItr = auto UMII = UnmaterializedInfos.insert(
MaterializationInfos UnmaterializedInfos.end(),
.insert(llvm::make_unique<MaterializationInfo>(NewSymbols.size(), UnmaterializedInfo(NewSymbols.size(), std::move(MU)));
std::move(MU)))
.first;
Error Err = Error::success(); Error Err = Error::success();
for (auto &KV : NewSymbols) { for (auto &KV : NewSymbols) {
auto I = Symbols.find(KV.first); auto I = Symbols.find(KV.first);
assert(I == Symbols.end() ||
!I->second.Flags.isMaterializing() &&
"Attempt to replace materializing symbol definition");
auto LinkageResult = compareLinkage( auto LinkageResult = compareLinkage(
I == Symbols.end() ? None I == Symbols.end() ? None : Optional<JITSymbolFlags>(I->second.Flags),
: Optional<JITSymbolFlags>(I->second.getFlags()),
KV.second); KV.second);
// Discard weaker definitions. // Discard weaker definitions.
if (LinkageResult == ExistingDefinitionIsStronger) { if (LinkageResult == ExistingDefinitionIsStronger) {
(*MaterializationInfoItr)->MU->discard(*this, KV.first); UMII->MU->discard(*this, KV.first);
assert((*MaterializationInfoItr)->SymbolsRemaining > 0 && detach(UMII);
"Discarding non-existant symbols?");
--(*MaterializationInfoItr)->SymbolsRemaining;
continue; continue;
} }
@ -328,43 +356,109 @@ Error VSO::defineLazy(std::unique_ptr<MaterializationUnit> MU) {
make_error<orc::DuplicateDefinition>(*KV.first)); make_error<orc::DuplicateDefinition>(*KV.first));
// Duplicate definitions are discarded, so remove the duplicates from // Duplicate definitions are discarded, so remove the duplicates from
// materializer. // materializer.
assert((*MaterializationInfoItr)->SymbolsRemaining > 0 && detach(UMII);
"Discarding non-existant symbols?");
--(*MaterializationInfoItr)->SymbolsRemaining;
continue; continue;
} }
// Existing definition was weaker. Replace it.
if (I != Symbols.end()) if (I != Symbols.end())
I->second.replaceWith(*this, KV.first, KV.second, MaterializationInfoItr); I->second.replaceWith(*this, KV.first, KV.second, UMII);
else else
Symbols.emplace(std::make_pair( Symbols.emplace(
KV.first, SymbolTableEntry(KV.second, MaterializationInfoItr))); 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; return Err;
} }
void VSO::resolve(SymbolMap SymbolValues) { void VSO::resolve(const SymbolMap &SymbolValues) {
for (auto &KV : SymbolValues) { for (auto &KV : SymbolValues) {
auto I = Symbols.find(KV.first); auto I = Symbols.find(KV.first);
assert(I != Symbols.end() && "Resolving symbol not present in this dylib"); 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<std::shared_ptr<AsynchronousSymbolQuery>, 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<FailedToResolve>(std::move(KV.second)));
}
void VSO::finalize(const SymbolNameSet &SymbolsToFinalize) {
for (auto &S : SymbolsToFinalize) { for (auto &S : SymbolsToFinalize) {
auto I = Symbols.find(S); auto I = Symbols.find(S);
assert(I != Symbols.end() && "Finalizing symbol not present in this dylib"); 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<std::shared_ptr<AsynchronousSymbolQuery>, 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<FailedToFinalize>(std::move(KV.second)));
}
SymbolNameSet VSO::lookupFlags(SymbolFlagsMap &Flags, SymbolNameSet Names) { SymbolNameSet VSO::lookupFlags(SymbolFlagsMap &Flags, SymbolNameSet Names) {
for (SymbolNameSet::iterator I = Names.begin(), E = Names.end(); I != E;) { for (SymbolNameSet::iterator I = Names.begin(), E = Names.end(); I != E;) {
@ -378,7 +472,7 @@ SymbolNameSet VSO::lookupFlags(SymbolFlagsMap &Flags, SymbolNameSet Names) {
Names.erase(Tmp); Names.erase(Tmp);
Flags[SymI->first] = Flags[SymI->first] =
JITSymbolFlags::stripTransientFlags(SymI->second.getFlags()); JITSymbolFlags::stripTransientFlags(SymI->second.Flags);
} }
return Names; return Names;
@ -396,14 +490,43 @@ VSO::LookupResult VSO::lookup(std::shared_ptr<AsynchronousSymbolQuery> Query,
if (SymI == Symbols.end()) if (SymI == Symbols.end())
continue; 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); Names.erase(Tmp);
// Forward the query to the given SymbolTableEntry, and if it return a // If this symbol has not been materialized yet, move it to materializing,
// layer to perform materialization with, add that to the // then fall through to the materializing case below.
// MaterializationWork map. if (SymI->second.Flags.isLazy()) {
if (auto MU = SymI->second.query(SymI->first, Query)) if (auto MU = SymI->second.initMaterialize(*this))
MaterializationUnits.push_back(std::move(MU)); 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)}; return {std::move(MaterializationUnits), std::move(Names)};

View File

@ -153,13 +153,13 @@ private:
for (auto &S : Symbols) { for (auto &S : Symbols) {
if (auto Sym = findSymbol(*S)) { if (auto Sym = findSymbol(*S)) {
if (auto Addr = Sym.getAddress()) if (auto Addr = Sym.getAddress())
Query->setDefinition(S, JITEvaluatedSymbol(*Addr, Sym.getFlags())); Query->resolve(S, JITEvaluatedSymbol(*Addr, Sym.getFlags()));
else { else {
Query->setFailed(Addr.takeError()); Query->notifyFailed(Addr.takeError());
return orc::SymbolNameSet(); return orc::SymbolNameSet();
} }
} else if (auto Err = Sym.takeError()) { } else if (auto Err = Sym.takeError()) {
Query->setFailed(std::move(Err)); Query->notifyFailed(std::move(Err));
return orc::SymbolNameSet(); return orc::SymbolNameSet();
} else } else
UnresolvedSymbols.insert(S); UnresolvedSymbols.insert(S);

View File

@ -29,6 +29,8 @@ public:
std::string message(int condition) const override { std::string message(int condition) const override {
switch (static_cast<OrcErrorCode>(condition)) { switch (static_cast<OrcErrorCode>(condition)) {
case OrcErrorCode::UnknownORCError:
return "Unknown ORC error";
case OrcErrorCode::DuplicateDefinition: case OrcErrorCode::DuplicateDefinition:
return "Duplicate symbol definition"; return "Duplicate symbol definition";
case OrcErrorCode::JITSymbolNotFound: case OrcErrorCode::JITSymbolNotFound:

View File

@ -175,25 +175,24 @@ class OrcMCJITReplacement : public ExecutionEngine {
for (auto &S : Symbols) { for (auto &S : Symbols) {
if (auto Sym = M.findMangledSymbol(*S)) { if (auto Sym = M.findMangledSymbol(*S)) {
if (auto Addr = Sym.getAddress()) if (auto Addr = Sym.getAddress())
Query->setDefinition(S, JITEvaluatedSymbol(*Addr, Sym.getFlags())); Query->resolve(S, JITEvaluatedSymbol(*Addr, Sym.getFlags()));
else { else {
Query->setFailed(Addr.takeError()); Query->notifyFailed(Addr.takeError());
return SymbolNameSet(); return SymbolNameSet();
} }
} else if (auto Err = Sym.takeError()) { } else if (auto Err = Sym.takeError()) {
Query->setFailed(std::move(Err)); Query->notifyFailed(std::move(Err));
return SymbolNameSet(); return SymbolNameSet();
} else { } else {
if (auto Sym2 = M.ClientResolver->findSymbol(*S)) { if (auto Sym2 = M.ClientResolver->findSymbol(*S)) {
if (auto Addr = Sym2.getAddress()) if (auto Addr = Sym2.getAddress())
Query->setDefinition(S, Query->resolve(S, JITEvaluatedSymbol(*Addr, Sym2.getFlags()));
JITEvaluatedSymbol(*Addr, Sym2.getFlags()));
else { else {
Query->setFailed(Addr.takeError()); Query->notifyFailed(Addr.takeError());
return SymbolNameSet(); return SymbolNameSet();
} }
} else if (auto Err = Sym2.takeError()) { } else if (auto Err = Sym2.takeError()) {
Query->setFailed(std::move(Err)); Query->notifyFailed(std::move(Err));
return SymbolNameSet(); return SymbolNameSet();
} else } else
UnresolvedSymbols.insert(S); UnresolvedSymbols.insert(S);

View File

@ -24,12 +24,19 @@ public:
using GetSymbolsFunction = std::function<SymbolFlagsMap()>; using GetSymbolsFunction = std::function<SymbolFlagsMap()>;
using MaterializeFunction = std::function<Error(VSO &)>; using MaterializeFunction = std::function<Error(VSO &)>;
using DiscardFunction = std::function<void(VSO &, SymbolStringPtr)>; using DiscardFunction = std::function<void(VSO &, SymbolStringPtr)>;
using DestructorFunction = std::function<void()>;
SimpleMaterializationUnit(GetSymbolsFunction GetSymbols, SimpleMaterializationUnit(
MaterializeFunction Materialize, GetSymbolsFunction GetSymbols, MaterializeFunction Materialize,
DiscardFunction Discard) DiscardFunction Discard,
DestructorFunction Destructor = DestructorFunction())
: GetSymbols(std::move(GetSymbols)), Materialize(std::move(Materialize)), : 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(); } SymbolFlagsMap getSymbols() override { return GetSymbols(); }
@ -43,6 +50,7 @@ private:
GetSymbolsFunction GetSymbols; GetSymbolsFunction GetSymbols;
MaterializeFunction Materialize; MaterializeFunction Materialize;
DiscardFunction Discard; DiscardFunction Discard;
DestructorFunction Destructor;
}; };
TEST(CoreAPIsTest, AsynchronousSymbolQuerySuccessfulResolutionOnly) { TEST(CoreAPIsTest, AsynchronousSymbolQuerySuccessfulResolutionOnly) {
@ -68,7 +76,7 @@ TEST(CoreAPIsTest, AsynchronousSymbolQuerySuccessfulResolutionOnly) {
AsynchronousSymbolQuery Q(Names, OnResolution, OnReady); 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_TRUE(OnResolutionRun) << "OnResolutionCallback was not run";
EXPECT_FALSE(OnReadyRun) << "OnReady unexpectedly run"; EXPECT_FALSE(OnReadyRun) << "OnReady unexpectedly run";
@ -95,7 +103,7 @@ TEST(CoreAPIsTest, AsynchronousSymbolQueryResolutionErrorOnly) {
AsynchronousSymbolQuery Q(Names, OnResolution, OnReady); AsynchronousSymbolQuery Q(Names, OnResolution, OnReady);
Q.setFailed(make_error<StringError>("xyz", inconvertibleErrorCode())); Q.notifyFailed(make_error<StringError>("xyz", inconvertibleErrorCode()));
EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run"; EXPECT_TRUE(OnResolutionRun) << "OnResolutionCallback was not run";
EXPECT_FALSE(OnReadyRun) << "OnReady unexpectedly run"; EXPECT_FALSE(OnReadyRun) << "OnReady unexpectedly run";
@ -186,6 +194,44 @@ TEST(CoreAPIsTest, LookupFlagsTest) {
EXPECT_EQ(SymbolFlags[Bar], BarFlags) << "Incorrect flags returned for Bar"; 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<SimpleMaterializationUnit>(
[=]() {
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) { TEST(CoreAPIsTest, AddAndMaterializeLazySymbol) {
constexpr JITTargetAddress FakeFooAddr = 0xdeadbeef; constexpr JITTargetAddress FakeFooAddr = 0xdeadbeef;
@ -264,6 +310,121 @@ TEST(CoreAPIsTest, AddAndMaterializeLazySymbol) {
EXPECT_TRUE(OnReadyRun) << "OnReady was not run"; 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<SimpleMaterializationUnit>(
[=]() {
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<SymbolMap> 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<AsynchronousSymbolQuery>(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<SimpleMaterializationUnit>(
[=]() {
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<SymbolMap> 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<AsynchronousSymbolQuery>(Names, OnResolution, OnReady);
auto LR = V.lookup(std::move(Q), Names);
for (auto &SWKV : LR.MaterializationUnits)
cantFail(SWKV->materialize(V));
}
TEST(CoreAPIsTest, TestLambdaSymbolResolver) { TEST(CoreAPIsTest, TestLambdaSymbolResolver) {
JITEvaluatedSymbol FooSym(0xdeadbeef, JITSymbolFlags::Exported); JITEvaluatedSymbol FooSym(0xdeadbeef, JITSymbolFlags::Exported);
JITEvaluatedSymbol BarSym(0xcafef00d, JITSymbolFlags::Exported); JITEvaluatedSymbol BarSym(0xcafef00d, JITSymbolFlags::Exported);