forked from OSchip/llvm-project
[ORC] Add an asynchronous jit-link function, jitLinkForORC, to RuntimeDyld and
switch RTDyldObjectLinkingLayer2 to use it. RuntimeDyld::loadObject is currently a blocking operation. This means that any JIT'd code whose call-graph contains an embedded complete K graph will require at least K threads to link, which precludes the use of a fixed sized thread pool for concurrent JITing of arbitrary code (whatever K the thread-pool is set at, any code with a K+1 complete subgraph will deadlock at JIT-link time). To address this issue, this commmit introduces a function called jitLinkForORC that uses continuation-passing style to pass the fix-up and finalization steps to the asynchronous symbol resolver interface so that linking can be performed without blocking. llvm-svn: 343043
This commit is contained in:
parent
b794aec290
commit
abeedf1812
|
@ -103,6 +103,14 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
Error onObjLoad(VModuleKey K, MaterializationResponsibility &R,
|
||||
object::ObjectFile &Obj,
|
||||
std::unique_ptr<RuntimeDyld::LoadedObjectInfo> LoadedObjInfo,
|
||||
std::map<StringRef, JITEvaluatedSymbol> Resolved,
|
||||
std::set<StringRef> &InternalSymbols);
|
||||
|
||||
void onObjEmit(VModuleKey K, MaterializationResponsibility &R, Error Err);
|
||||
|
||||
mutable std::mutex RTDyldLayerMutex;
|
||||
GetMemoryManagerFunction GetMemoryManager;
|
||||
NotifyLoadedFunction NotifyLoaded;
|
||||
|
|
|
@ -250,6 +250,16 @@ public:
|
|||
void finalizeWithMemoryManagerLocking();
|
||||
|
||||
private:
|
||||
friend void
|
||||
jitLinkForORC(object::ObjectFile &Obj,
|
||||
std::unique_ptr<MemoryBuffer> UnderlyingBuffer,
|
||||
RuntimeDyld::MemoryManager &MemMgr, JITSymbolResolver &Resolver,
|
||||
bool ProcessAllSections,
|
||||
std::function<Error(std::unique_ptr<LoadedObjectInfo>,
|
||||
std::map<StringRef, JITEvaluatedSymbol>)>
|
||||
OnLoaded,
|
||||
std::function<void(Error)> OnEmitted);
|
||||
|
||||
// RuntimeDyldImpl is the actual class. RuntimeDyld is just the public
|
||||
// interface.
|
||||
std::unique_ptr<RuntimeDyldImpl> Dyld;
|
||||
|
@ -259,6 +269,21 @@ private:
|
|||
RuntimeDyldCheckerImpl *Checker;
|
||||
};
|
||||
|
||||
// Asynchronous JIT link for ORC.
|
||||
//
|
||||
// Warning: This API is experimental and probably should not be used by anyone
|
||||
// but ORC's RTDyldObjectLinkingLayer2. Internally it constructs a RuntimeDyld
|
||||
// instance and uses continuation passing to perform the fix-up and finalize
|
||||
// steps asynchronously.
|
||||
void jitLinkForORC(object::ObjectFile &Obj,
|
||||
std::unique_ptr<MemoryBuffer> UnderlyingBuffer,
|
||||
RuntimeDyld::MemoryManager &MemMgr,
|
||||
JITSymbolResolver &Resolver, bool ProcessAllSections,
|
||||
std::function<Error(std::unique_ptr<LoadedObjectInfo>,
|
||||
std::map<StringRef, JITEvaluatedSymbol>)>
|
||||
OnLoaded,
|
||||
std::function<void(Error)> OnEmitted);
|
||||
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_EXECUTIONENGINE_RUNTIMEDYLD_H
|
||||
|
|
|
@ -88,20 +88,41 @@ void RTDyldObjectLinkingLayer2::emit(MaterializationResponsibility R,
|
|||
std::unique_ptr<MemoryBuffer> O) {
|
||||
assert(O && "Object must not be null");
|
||||
|
||||
// This method launches an asynchronous link step that will fulfill our
|
||||
// materialization responsibility. We need to switch R to be heap
|
||||
// allocated before that happens so it can live as long as the asynchronous
|
||||
// link needs it to (i.e. it must be able to outlive this method).
|
||||
auto SharedR = std::make_shared<MaterializationResponsibility>(std::move(R));
|
||||
|
||||
auto &ES = getExecutionSession();
|
||||
|
||||
auto ObjFile = object::ObjectFile::createObjectFile(*O);
|
||||
if (!ObjFile) {
|
||||
getExecutionSession().reportError(ObjFile.takeError());
|
||||
R.failMaterialization();
|
||||
auto Obj = object::ObjectFile::createObjectFile(*O);
|
||||
|
||||
if (!Obj) {
|
||||
getExecutionSession().reportError(Obj.takeError());
|
||||
SharedR->failMaterialization();
|
||||
return;
|
||||
}
|
||||
|
||||
// Collect the internal symbols from the object file: We will need to
|
||||
// filter these later.
|
||||
auto InternalSymbols = std::make_shared<std::set<StringRef>>();
|
||||
{
|
||||
for (auto &Sym : (*Obj)->symbols()) {
|
||||
if (!(Sym.getFlags() & object::BasicSymbolRef::SF_Global)) {
|
||||
if (auto SymName = Sym.getName())
|
||||
InternalSymbols->insert(*SymName);
|
||||
else {
|
||||
ES.reportError(SymName.takeError());
|
||||
R.failMaterialization();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto MemoryManager = GetMemoryManager(K);
|
||||
|
||||
JITDylibSearchOrderResolver Resolver(R);
|
||||
auto RTDyld = llvm::make_unique<RuntimeDyld>(*MemoryManager, Resolver);
|
||||
RTDyld->setProcessAllSections(ProcessAllSections);
|
||||
|
||||
auto &MemMgr = *MemoryManager;
|
||||
{
|
||||
std::lock_guard<std::mutex> Lock(RTDyldLayerMutex);
|
||||
|
||||
|
@ -110,66 +131,77 @@ void RTDyldObjectLinkingLayer2::emit(MaterializationResponsibility R,
|
|||
MemMgrs[K] = std::move(MemoryManager);
|
||||
}
|
||||
|
||||
auto Info = RTDyld->loadObject(**ObjFile);
|
||||
JITDylibSearchOrderResolver Resolver(*SharedR);
|
||||
|
||||
{
|
||||
std::set<StringRef> InternalSymbols;
|
||||
for (auto &Sym : (*ObjFile)->symbols()) {
|
||||
if (!(Sym.getFlags() & object::BasicSymbolRef::SF_Global)) {
|
||||
if (auto SymName = Sym.getName())
|
||||
InternalSymbols.insert(*SymName);
|
||||
else {
|
||||
ES.reportError(SymName.takeError());
|
||||
R.failMaterialization();
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Thoughts on proper cross-dylib weak symbol handling:
|
||||
*
|
||||
* Change selection of canonical defs to be a manually triggered process, and
|
||||
* add a 'canonical' bit to symbol definitions. When canonical def selection
|
||||
* is triggered, sweep the JITDylibs to mark defs as canonical, discard
|
||||
* duplicate defs.
|
||||
*/
|
||||
jitLinkForORC(
|
||||
**Obj, std::move(O), MemMgr, Resolver, ProcessAllSections,
|
||||
[this, K, SharedR, &Obj, InternalSymbols](
|
||||
std::unique_ptr<RuntimeDyld::LoadedObjectInfo> LoadedObjInfo,
|
||||
std::map<StringRef, JITEvaluatedSymbol> ResolvedSymbols) {
|
||||
return onObjLoad(K, *SharedR, **Obj, std::move(LoadedObjInfo),
|
||||
ResolvedSymbols, *InternalSymbols);
|
||||
},
|
||||
[this, K, SharedR](Error Err) {
|
||||
onObjEmit(K, *SharedR, std::move(Err));
|
||||
});
|
||||
}
|
||||
|
||||
Error RTDyldObjectLinkingLayer2::onObjLoad(
|
||||
VModuleKey K, MaterializationResponsibility &R, object::ObjectFile &Obj,
|
||||
std::unique_ptr<RuntimeDyld::LoadedObjectInfo> LoadedObjInfo,
|
||||
std::map<StringRef, JITEvaluatedSymbol> Resolved,
|
||||
std::set<StringRef> &InternalSymbols) {
|
||||
SymbolFlagsMap ExtraSymbolsToClaim;
|
||||
SymbolMap Symbols;
|
||||
for (auto &KV : Resolved) {
|
||||
// Scan the symbols and add them to the Symbols map for resolution.
|
||||
|
||||
// We never claim internal symbols.
|
||||
if (InternalSymbols.count(KV.first))
|
||||
continue;
|
||||
|
||||
auto InternedName =
|
||||
getExecutionSession().getSymbolStringPool().intern(KV.first);
|
||||
auto Flags = KV.second.getFlags();
|
||||
|
||||
// Override object flags and claim responsibility for symbols if
|
||||
// requested.
|
||||
if (OverrideObjectFlags || AutoClaimObjectSymbols) {
|
||||
auto I = R.getSymbols().find(InternedName);
|
||||
|
||||
if (OverrideObjectFlags && I != R.getSymbols().end())
|
||||
Flags = JITSymbolFlags::stripTransientFlags(I->second);
|
||||
else if (AutoClaimObjectSymbols && I == R.getSymbols().end())
|
||||
ExtraSymbolsToClaim[InternedName] = Flags;
|
||||
}
|
||||
|
||||
SymbolFlagsMap ExtraSymbolsToClaim;
|
||||
SymbolMap Symbols;
|
||||
for (auto &KV : RTDyld->getSymbolTable()) {
|
||||
// Scan the symbols and add them to the Symbols map for resolution.
|
||||
|
||||
// We never claim internal symbols.
|
||||
if (InternalSymbols.count(KV.first))
|
||||
continue;
|
||||
|
||||
auto InternedName = ES.getSymbolStringPool().intern(KV.first);
|
||||
auto Flags = KV.second.getFlags();
|
||||
|
||||
// Override object flags and claim responsibility for symbols if
|
||||
// requested.
|
||||
if (OverrideObjectFlags || AutoClaimObjectSymbols) {
|
||||
auto I = R.getSymbols().find(InternedName);
|
||||
|
||||
if (OverrideObjectFlags && I != R.getSymbols().end())
|
||||
Flags = JITSymbolFlags::stripTransientFlags(I->second);
|
||||
else if (AutoClaimObjectSymbols && I == R.getSymbols().end())
|
||||
ExtraSymbolsToClaim[InternedName] = Flags;
|
||||
}
|
||||
|
||||
Symbols[InternedName] = JITEvaluatedSymbol(KV.second.getAddress(), Flags);
|
||||
}
|
||||
|
||||
if (!ExtraSymbolsToClaim.empty())
|
||||
if (auto Err = R.defineMaterializing(ExtraSymbolsToClaim)) {
|
||||
ES.reportError(std::move(Err));
|
||||
R.failMaterialization();
|
||||
return;
|
||||
}
|
||||
|
||||
R.resolve(Symbols);
|
||||
Symbols[InternedName] = JITEvaluatedSymbol(KV.second.getAddress(), Flags);
|
||||
}
|
||||
|
||||
if (!ExtraSymbolsToClaim.empty())
|
||||
if (auto Err = R.defineMaterializing(ExtraSymbolsToClaim))
|
||||
return Err;
|
||||
|
||||
R.resolve(Symbols);
|
||||
|
||||
if (NotifyLoaded)
|
||||
NotifyLoaded(K, **ObjFile, *Info);
|
||||
NotifyLoaded(K, Obj, *LoadedObjInfo);
|
||||
|
||||
RTDyld->finalizeWithMemoryManagerLocking();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
if (RTDyld->hasError()) {
|
||||
ES.reportError(make_error<StringError>(RTDyld->getErrorString(),
|
||||
inconvertibleErrorCode()));
|
||||
void RTDyldObjectLinkingLayer2::onObjEmit(VModuleKey K,
|
||||
MaterializationResponsibility &R,
|
||||
Error Err) {
|
||||
if (Err) {
|
||||
getExecutionSession().reportError(std::move(Err));
|
||||
R.failMaterialization();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -134,6 +134,14 @@ void RuntimeDyldImpl::resolveRelocations() {
|
|||
ErrorStr = toString(std::move(Err));
|
||||
}
|
||||
|
||||
resolveLocalRelocations();
|
||||
|
||||
// Print out sections after relocation.
|
||||
LLVM_DEBUG(for (int i = 0, e = Sections.size(); i != e; ++i)
|
||||
dumpSectionMemory(Sections[i], "after relocations"););
|
||||
}
|
||||
|
||||
void RuntimeDyldImpl::resolveLocalRelocations() {
|
||||
// Iterate over all outstanding relocations
|
||||
for (auto it = Relocations.begin(), e = Relocations.end(); it != e; ++it) {
|
||||
// The Section here (Sections[i]) refers to the section in which the
|
||||
|
@ -146,10 +154,6 @@ void RuntimeDyldImpl::resolveRelocations() {
|
|||
resolveRelocationList(it->second, Addr);
|
||||
}
|
||||
Relocations.clear();
|
||||
|
||||
// Print out sections after relocation.
|
||||
LLVM_DEBUG(for (int i = 0, e = Sections.size(); i != e; ++i)
|
||||
dumpSectionMemory(Sections[i], "after relocations"););
|
||||
}
|
||||
|
||||
void RuntimeDyldImpl::mapSectionAddress(const void *LocalAddress,
|
||||
|
@ -1120,6 +1124,56 @@ Error RuntimeDyldImpl::resolveExternalSymbols() {
|
|||
return Error::success();
|
||||
}
|
||||
|
||||
void RuntimeDyldImpl::finalizeAsync(
|
||||
std::unique_ptr<RuntimeDyldImpl> This, std::function<void(Error)> OnEmitted,
|
||||
std::unique_ptr<MemoryBuffer> UnderlyingBuffer) {
|
||||
|
||||
// FIXME: Move-capture OnRelocsApplied and UnderlyingBuffer once we have
|
||||
// c++14.
|
||||
auto SharedUnderlyingBuffer =
|
||||
std::shared_ptr<MemoryBuffer>(std::move(UnderlyingBuffer));
|
||||
auto SharedThis = std::shared_ptr<RuntimeDyldImpl>(std::move(This));
|
||||
auto PostResolveContinuation =
|
||||
[SharedThis, OnEmitted, SharedUnderlyingBuffer](
|
||||
Expected<JITSymbolResolver::LookupResult> Result) {
|
||||
if (!Result) {
|
||||
OnEmitted(Result.takeError());
|
||||
return;
|
||||
}
|
||||
|
||||
/// Copy the result into a StringMap, where the keys are held by value.
|
||||
StringMap<JITEvaluatedSymbol> Resolved;
|
||||
for (auto &KV : *Result)
|
||||
Resolved[KV.first] = KV.second;
|
||||
|
||||
SharedThis->applyExternalSymbolRelocations(Resolved);
|
||||
SharedThis->resolveLocalRelocations();
|
||||
SharedThis->registerEHFrames();
|
||||
std::string ErrMsg;
|
||||
if (SharedThis->MemMgr.finalizeMemory(&ErrMsg))
|
||||
OnEmitted(make_error<StringError>(std::move(ErrMsg),
|
||||
inconvertibleErrorCode()));
|
||||
else
|
||||
OnEmitted(Error::success());
|
||||
};
|
||||
|
||||
JITSymbolResolver::LookupSet Symbols;
|
||||
|
||||
for (auto &RelocKV : SharedThis->ExternalSymbolRelocations) {
|
||||
StringRef Name = RelocKV.first();
|
||||
assert(!Name.empty() && "Symbol has no name?");
|
||||
assert(!SharedThis->GlobalSymbolTable.count(Name) &&
|
||||
"Name already processed. RuntimeDyld instances can not be re-used "
|
||||
"when finalizing with finalizeAsync.");
|
||||
Symbols.insert(Name);
|
||||
}
|
||||
|
||||
if (!Symbols.empty()) {
|
||||
SharedThis->Resolver.lookup(Symbols, PostResolveContinuation);
|
||||
} else
|
||||
PostResolveContinuation(std::map<StringRef, JITEvaluatedSymbol>());
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// RuntimeDyld class implementation
|
||||
|
||||
|
@ -1267,5 +1321,35 @@ void RuntimeDyld::deregisterEHFrames() {
|
|||
if (Dyld)
|
||||
Dyld->deregisterEHFrames();
|
||||
}
|
||||
// FIXME: Kill this with fire once we have a new JIT linker: this is only here
|
||||
// so that we can re-use RuntimeDyld's implementation without twisting the
|
||||
// interface any further for ORC's purposes.
|
||||
void jitLinkForORC(object::ObjectFile &Obj,
|
||||
std::unique_ptr<MemoryBuffer> UnderlyingBuffer,
|
||||
RuntimeDyld::MemoryManager &MemMgr,
|
||||
JITSymbolResolver &Resolver, bool ProcessAllSections,
|
||||
std::function<Error(
|
||||
std::unique_ptr<RuntimeDyld::LoadedObjectInfo> LoadedObj,
|
||||
std::map<StringRef, JITEvaluatedSymbol>)>
|
||||
OnLoaded,
|
||||
std::function<void(Error)> OnEmitted) {
|
||||
|
||||
RuntimeDyld RTDyld(MemMgr, Resolver);
|
||||
RTDyld.setProcessAllSections(ProcessAllSections);
|
||||
|
||||
auto Info = RTDyld.loadObject(Obj);
|
||||
|
||||
if (RTDyld.hasError()) {
|
||||
OnEmitted(make_error<StringError>(RTDyld.getErrorString(),
|
||||
inconvertibleErrorCode()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto Err = OnLoaded(std::move(Info), RTDyld.getSymbolTable()))
|
||||
OnEmitted(std::move(Err));
|
||||
|
||||
RuntimeDyldImpl::finalizeAsync(std::move(RTDyld.Dyld), std::move(OnEmitted),
|
||||
std::move(UnderlyingBuffer));
|
||||
}
|
||||
|
||||
} // end namespace llvm
|
||||
|
|
|
@ -539,6 +539,12 @@ public:
|
|||
|
||||
void resolveRelocations();
|
||||
|
||||
void resolveLocalRelocations();
|
||||
|
||||
static void finalizeAsync(std::unique_ptr<RuntimeDyldImpl> This,
|
||||
std::function<void(Error)> OnEmitted,
|
||||
std::unique_ptr<MemoryBuffer> UnderlyingBuffer);
|
||||
|
||||
void reassignSectionAddress(unsigned SectionID, uint64_t Addr);
|
||||
|
||||
void mapSectionAddress(const void *LocalAddress, uint64_t TargetAddress);
|
||||
|
|
Loading…
Reference in New Issue