Lang Hames 507d7742b9 [ORC] Rename SymbolSource to MaterializationUnit, and make the materialization
operation all-or-nothing, rather than allowing materialization on a per-symbol
basis.

This addresses a shortcoming of per-symbol materialization: If a
MaterializationUnit (/SymbolSource) wants to materialize more symbols than
requested (which is likely: most materializers will want to materialize whole
modules) then it needs a way to notify the symbol table about the extra symbols
being materialized. This process (checking what has been requested against what
is being provided and notifying the symbol table about the difference) has to
be repeated at every level of the JIT stack. Making materialization
all-or-nothing eliminates this issue, simplifying both materializer
implementations and the symbol table (VSO class) API. The cost is that
per-symbol materialization (e.g. for individual symbols in a module) now
requires multiple MaterializationUnits.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@327946 91177308-0d34-0410-b5e6-96231b3b80d8
2018-03-20 03:49:29 +00:00

545 lines
18 KiB
C++

//===----- Core.cpp - Core ORC APIs (MaterializationUnit, VSO, etc.) ------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "llvm/ExecutionEngine/Orc/Core.h"
#include "llvm/ExecutionEngine/Orc/OrcError.h"
#if LLVM_ENABLE_THREADS
#include <future>
#endif
namespace llvm {
namespace orc {
void MaterializationUnit::anchor() {}
void SymbolResolver::anchor() {}
AsynchronousSymbolQuery::AsynchronousSymbolQuery(
const SymbolNameSet &Symbols, SymbolsResolvedCallback NotifySymbolsResolved,
SymbolsReadyCallback NotifySymbolsReady)
: NotifySymbolsResolved(std::move(NotifySymbolsResolved)),
NotifySymbolsReady(std::move(NotifySymbolsReady)) {
assert(this->NotifySymbolsResolved &&
"Symbols resolved callback must be set");
assert(this->NotifySymbolsReady && "Symbols ready callback must be set");
OutstandingResolutions = OutstandingFinalizations = Symbols.size();
}
void AsynchronousSymbolQuery::setFailed(Error Err) {
OutstandingResolutions = OutstandingFinalizations = 0;
if (NotifySymbolsResolved)
NotifySymbolsResolved(std::move(Err));
else
NotifySymbolsReady(std::move(Err));
}
void AsynchronousSymbolQuery::setDefinition(SymbolStringPtr Name,
JITEvaluatedSymbol Sym) {
// If OutstandingResolutions is zero we must have errored out already. Just
// ignore this.
if (OutstandingResolutions == 0)
return;
assert(!Symbols.count(Name) && "Symbol has already been assigned an address");
Symbols.insert(std::make_pair(std::move(Name), std::move(Sym)));
--OutstandingResolutions;
if (OutstandingResolutions == 0) {
NotifySymbolsResolved(std::move(Symbols));
// Null out NotifySymbolsResolved to indicate that we've already called it.
NotifySymbolsResolved = {};
}
}
void AsynchronousSymbolQuery::notifySymbolFinalized() {
// If OutstandingFinalizations is zero we must have errored out already. Just
// ignore this.
if (OutstandingFinalizations == 0)
return;
assert(OutstandingFinalizations > 0 && "All symbols already finalized");
--OutstandingFinalizations;
if (OutstandingFinalizations == 0)
NotifySymbolsReady(Error::success());
}
VSO::MaterializationInfo::MaterializationInfo(
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(JITEvaluatedSymbol Sym)
: Flags(Sym.getFlags()), Address(Sym.getAddress()) {
// FIXME: Assert flag sanity.
}
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() { destroy(); }
VSO::SymbolTableEntry &VSO::SymbolTableEntry::
operator=(JITEvaluatedSymbol Sym) {
destroy();
Flags = Sym.getFlags();
Address = Sym.getAddress();
return *this;
}
void VSO::SymbolTableEntry::destroy() {
if (!Flags.isMaterialized())
MaterializationInfoItr
.MaterializationInfoIterator::~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);
}
MaterializationInfoItr = std::move(NewMaterializationInfoItr);
}
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;
}
}
}
}
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::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.
MaterializationInfoItr
.MaterializationInfoIterator::~MaterializationInfoIterator();
Flags = Sym.getFlags();
Address = Sym.getAddress();
}
assert(Flags.isMaterialized() && "Trying to finalize not-emitted symbol");
}
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;
}
VSO::RelativeLinkageStrength VSO::compareLinkage(Optional<JITSymbolFlags> Old,
JITSymbolFlags New) {
if (Old == None)
return llvm::orc::VSO::NewDefinitionIsStronger;
if (Old->isStrong()) {
if (New.isStrong())
return llvm::orc::VSO::DuplicateDefinition;
else
return llvm::orc::VSO::ExistingDefinitionIsStronger;
} else {
if (New.isStrong())
return llvm::orc::VSO::NewDefinitionIsStronger;
else
return llvm::orc::VSO::ExistingDefinitionIsStronger;
}
}
VSO::RelativeLinkageStrength
VSO::compareLinkage(SymbolStringPtr Name, JITSymbolFlags NewFlags) const {
auto I = Symbols.find(Name);
return compareLinkage(I == Symbols.end()
? None
: Optional<JITSymbolFlags>(I->second.getFlags()),
NewFlags);
}
Error VSO::define(SymbolMap NewSymbols) {
Error Err = Error::success();
for (auto &KV : NewSymbols) {
auto I = Symbols.find(KV.first);
auto LinkageResult = compareLinkage(
I == Symbols.end() ? None
: Optional<JITSymbolFlags>(I->second.getFlags()),
KV.second.getFlags());
// Silently discard weaker definitions.
if (LinkageResult == ExistingDefinitionIsStronger)
continue;
// Report duplicate definition errors.
if (LinkageResult == DuplicateDefinition) {
Err = joinErrors(std::move(Err),
make_error<orc::DuplicateDefinition>(*KV.first));
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
Symbols.insert(std::make_pair(KV.first, std::move(KV.second)));
}
return Err;
}
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;
Error Err = Error::success();
for (auto &KV : NewSymbols) {
auto I = Symbols.find(KV.first);
auto LinkageResult = compareLinkage(
I == Symbols.end() ? None
: Optional<JITSymbolFlags>(I->second.getFlags()),
KV.second);
// Discard weaker definitions.
if (LinkageResult == ExistingDefinitionIsStronger) {
(*MaterializationInfoItr)->MU->discard(*this, KV.first);
assert((*MaterializationInfoItr)->SymbolsRemaining > 0 &&
"Discarding non-existant symbols?");
--(*MaterializationInfoItr)->SymbolsRemaining;
continue;
}
// Report duplicate definition errors.
if (LinkageResult == DuplicateDefinition) {
Err = joinErrors(std::move(Err),
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;
continue;
}
if (I != Symbols.end())
I->second.replaceWith(*this, KV.first, KV.second, MaterializationInfoItr);
else
Symbols.emplace(std::make_pair(
KV.first, SymbolTableEntry(KV.second, MaterializationInfoItr)));
}
// 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) {
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));
}
}
void VSO::finalize(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);
}
}
SymbolNameSet VSO::lookupFlags(SymbolFlagsMap &Flags, SymbolNameSet Names) {
for (SymbolNameSet::iterator I = Names.begin(), E = Names.end(); I != E;) {
auto Tmp = I++;
auto SymI = Symbols.find(*Tmp);
// If the symbol isn't in this dylib then just continue.
if (SymI == Symbols.end())
continue;
Names.erase(Tmp);
Flags[SymI->first] =
JITSymbolFlags::stripTransientFlags(SymI->second.getFlags());
}
return Names;
}
VSO::LookupResult VSO::lookup(std::shared_ptr<AsynchronousSymbolQuery> Query,
SymbolNameSet Names) {
MaterializationUnitList MaterializationUnits;
for (SymbolNameSet::iterator I = Names.begin(), E = Names.end(); I != E;) {
auto Tmp = I++;
auto SymI = Symbols.find(*Tmp);
// If the symbol isn't in this dylib then just continue.
if (SymI == Symbols.end())
continue;
// The symbol is in the dylib. 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));
}
return {std::move(MaterializationUnits), std::move(Names)};
}
Expected<SymbolMap> lookup(const std::vector<VSO *> &VSOs, SymbolNameSet Names,
MaterializationDispatcher DispatchMaterialization) {
#if LLVM_ENABLE_THREADS
// In the threaded case we use promises to return the results.
std::promise<SymbolMap> PromisedResult;
std::mutex ErrMutex;
Error ResolutionError = Error::success();
std::promise<void> PromisedReady;
Error ReadyError = Error::success();
auto OnResolve = [&](Expected<SymbolMap> Result) {
if (Result)
PromisedResult.set_value(std::move(*Result));
else {
{
ErrorAsOutParameter _(&ResolutionError);
std::lock_guard<std::mutex> Lock(ErrMutex);
ResolutionError = Result.takeError();
}
PromisedResult.set_value(SymbolMap());
}
};
auto OnReady = [&](Error Err) {
if (Err) {
ErrorAsOutParameter _(&ReadyError);
std::lock_guard<std::mutex> Lock(ErrMutex);
ReadyError = std::move(Err);
}
PromisedReady.set_value();
};
#else
SymbolMap Result;
Error ResolutionError = Error::success();
Error ReadyError = Error::success();
auto OnResolve = [&](Expected<SymbolMap> R) {
ErrorAsOutParameter _(&ResolutionError);
if (R)
Result = std::move(*R);
else
ResolutionError = R.takeError();
};
auto OnReady = [&](Error Err) {
ErrorAsOutParameter _(&ReadyError);
if (Err)
ReadyError = std::move(Err);
};
#endif
auto Query = std::make_shared<AsynchronousSymbolQuery>(
Names, std::move(OnResolve), std::move(OnReady));
SymbolNameSet UnresolvedSymbols(std::move(Names));
for (auto *V : VSOs) {
if (UnresolvedSymbols.empty())
break;
assert(V && "VSO pointers in VSOs list should be non-null");
auto LR = V->lookup(Query, UnresolvedSymbols);
UnresolvedSymbols = std::move(LR.UnresolvedSymbols);
for (auto &MU : LR.MaterializationUnits)
DispatchMaterialization(*V, std::move(MU));
}
#if LLVM_ENABLE_THREADS
auto ResultFuture = PromisedResult.get_future();
auto Result = ResultFuture.get();
{
std::lock_guard<std::mutex> Lock(ErrMutex);
if (ResolutionError) {
// ReadyError will never be assigned. Consume the success value.
cantFail(std::move(ReadyError));
return std::move(ResolutionError);
}
}
auto ReadyFuture = PromisedReady.get_future();
ReadyFuture.get();
{
std::lock_guard<std::mutex> Lock(ErrMutex);
if (ReadyError)
return std::move(ReadyError);
}
return std::move(Result);
#else
if (ResolutionError) {
// ReadyError will never be assigned. Consume the success value.
cantFail(std::move(ReadyError));
return std::move(ResolutionError);
}
if (ReadyError)
return std::move(ReadyError);
return Result;
#endif
}
/// @brief Look up a symbol by searching a list of VSOs.
Expected<JITEvaluatedSymbol>
lookup(const std::vector<VSO *> VSOs, SymbolStringPtr Name,
MaterializationDispatcher DispatchMaterialization) {
SymbolNameSet Names({Name});
if (auto ResultMap =
lookup(VSOs, std::move(Names), std::move(DispatchMaterialization))) {
assert(ResultMap->size() == 1 && "Unexpected number of results");
assert(ResultMap->count(Name) && "Missing result for symbol");
return ResultMap->begin()->second;
} else
return ResultMap.takeError();
}
ExecutionSession::ExecutionSession(SymbolStringPool &SSP) : SSP(SSP) {}
VModuleKey ExecutionSession::allocateVModule() { return ++LastKey; }
void ExecutionSession::releaseVModule(VModuleKey VMod) {
// FIXME: Recycle keys.
}
void ExecutionSession::logErrorsToStdErr(Error Err) {
logAllUnhandledErrors(std::move(Err), errs(), "JIT session error: ");
}
} // End namespace orc.
} // End namespace llvm.