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,
Absolute = 1U << 3,
Exported = 1U << 4,
NotMaterialized = 1U << 5
Lazy = 1U << 5,
Materializing = 1U << 6
};
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.
@ -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 {

View File

@ -17,6 +17,7 @@
#include "llvm/ExecutionEngine/JITSymbol.h"
#include "llvm/ExecutionEngine/Orc/SymbolStringPool.h"
#include <list>
#include <map>
#include <memory>
#include <set>
@ -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<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
/// (address/flags pairs).
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.
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
/// 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<std::shared_ptr<AsynchronousSymbolQuery>>;
MaterializationInfo(size_t SymbolsRemaining,
std::unique_ptr<MaterializationUnit> MU);
UnmaterializedInfo(size_t SymbolsRemaining,
std::unique_ptr<MaterializationUnit> MU);
uint64_t SymbolsRemaining;
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 {
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<MaterializationUnit>
query(SymbolStringPtr Name, std::shared_ptr<AsynchronousSymbolQuery> Query);
void resolve(VSO &V, SymbolStringPtr Name, JITEvaluatedSymbol Sym);
void finalize(VSO &V, SymbolStringPtr Name);
void discard(VSO &V, SymbolStringPtr Name);
UnmaterializedInfoIterator NewUMII);
// Move entry to materializing state, detach from UMII.
std::unique_ptr<MaterializationUnit> initMaterialize(VSO &V);
// Move entry to resolved state.
void resolve(VSO &V, JITEvaluatedSymbol Sym);
// Move entry to finalized state.
void finalize();
JITSymbolFlags Flags;
union {
JITTargetAddress Address;
UnmaterializedInfoIterator UMII;
};
private:
void destroy();
JITSymbolFlags Flags;
MaterializationInfoIterator MII;
union {
JITTargetAddress Address;
MaterializationInfoIterator MaterializationInfoItr;
};
};
void detach(UnmaterializedInfoIterator UMII);
std::map<SymbolStringPtr, SymbolTableEntry> Symbols;
MaterializationInfoSet MaterializationInfos;
UnmaterializedInfoList UnmaterializedInfos;
MaterializingInfoMap MaterializingInfos;
};
/// @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
/// 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);

View File

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

View File

@ -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 <future>
@ -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<MaterializationUnit> 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<MaterializationUnit>
VSO::SymbolTableEntry::query(SymbolStringPtr Name,
std::shared_ptr<AsynchronousSymbolQuery> 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);
// 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::finalize() {
assert(Flags.isMaterializing() && !Flags.isLazy() &&
"Symbol should be in materializing state");
Flags &= ~JITSymbolFlags::Materializing;
}
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::SymbolTableEntry::destroy() {
if (Flags.isLazy())
UMII.~UnmaterializedInfoIterator();
}
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,
@ -258,10 +291,9 @@ VSO::RelativeLinkageStrength VSO::compareLinkage(Optional<JITSymbolFlags> Old,
VSO::RelativeLinkageStrength
VSO::compareLinkage(SymbolStringPtr Name, JITSymbolFlags NewFlags) const {
auto I = Symbols.find(Name);
return compareLinkage(I == Symbols.end()
? None
: Optional<JITSymbolFlags>(I->second.getFlags()),
NewFlags);
return compareLinkage(
I == Symbols.end() ? None : Optional<JITSymbolFlags>(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<JITSymbolFlags>(I->second.getFlags()),
I == Symbols.end() ? None : Optional<JITSymbolFlags>(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<MaterializationUnit> MU) {
auto NewSymbols = MU->getSymbols();
auto MaterializationInfoItr =
MaterializationInfos
.insert(llvm::make_unique<MaterializationInfo>(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<JITSymbolFlags>(I->second.getFlags()),
I == Symbols.end() ? None : Optional<JITSymbolFlags>(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,43 +356,109 @@ Error VSO::defineLazy(std::unique_ptr<MaterializationUnit> MU) {
make_error<orc::DuplicateDefinition>(*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<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) {
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<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) {
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);
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<AsynchronousSymbolQuery> 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)};

View File

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

View File

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

View File

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

View File

@ -24,12 +24,19 @@ public:
using GetSymbolsFunction = std::function<SymbolFlagsMap()>;
using MaterializeFunction = std::function<Error(VSO &)>;
using DiscardFunction = std::function<void(VSO &, SymbolStringPtr)>;
using DestructorFunction = std::function<void()>;
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<StringError>("xyz", inconvertibleErrorCode()));
Q.notifyFailed(make_error<StringError>("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<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) {
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<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) {
JITEvaluatedSymbol FooSym(0xdeadbeef, JITSymbolFlags::Exported);
JITEvaluatedSymbol BarSym(0xcafef00d, JITSymbolFlags::Exported);