llvm-project/llvm/lib/ExecutionEngine/Orc/ObjectLinkingLayer.cpp
Lang Hames e50aea58d5 [JITLink][ORC] Major JITLinkMemoryManager refactor.
This commit substantially refactors the JITLinkMemoryManager API to: (1) add
asynchronous versions of key operations, (2) give memory manager implementations
full control over link graph address layout, (3) enable more efficient tracking
of allocated memory, and (4) support "allocation actions" and finalize-lifetime
memory.

Together these changes provide a more usable API, and enable more powerful and
efficient memory manager implementations.

To support these changes the JITLinkMemoryManager::Allocation inner class has
been split into two new classes: InFlightAllocation, and FinalizedAllocation.
The allocate method returns an InFlightAllocation that tracks memory (both
working and executor memory) prior to finalization. The finalize method returns
a FinalizedAllocation object, and the InFlightAllocation is discarded. Breaking
Allocation into InFlightAllocation and FinalizedAllocation allows
InFlightAllocation subclassses to be written more naturally, and FinalizedAlloc
to be implemented and used efficiently (see (3) below).

In addition to the memory manager changes this commit also introduces a new
MemProt type to represent memory protections (MemProt replaces use of
sys::Memory::ProtectionFlags in JITLink), and a new MemDeallocPolicy type that
can be used to indicate when a section should be deallocated (see (4) below).

Plugin/pass writers who were using sys::Memory::ProtectionFlags will have to
switch to MemProt -- this should be straightworward. Clients with out-of-tree
memory managers will need to update their implementations. Clients using
in-tree memory managers should mostly be able to ignore it.

Major features:

(1) More asynchrony:

The allocate and deallocate methods are now asynchronous by default, with
synchronous convenience wrappers supplied. The asynchronous versions allow
clients (including JITLink) to request and deallocate memory without blocking.

(2) Improved control over graph address layout:

Instead of a SegmentRequestMap, JITLinkMemoryManager::allocate now takes a
reference to the LinkGraph to be allocated. The memory manager is responsible
for calculating the memory requirements for the graph, and laying out the graph
(setting working and executor memory addresses) within the allocated memory.
This gives memory managers full control over JIT'd memory layout. For clients
that don't need or want this degree of control the new "BasicLayout" utility can
be used to get a segment-based view of the graph, similar to the one provided by
SegmentRequestMap. Once segment addresses are assigned the BasicLayout::apply
method can be used to automatically lay out the graph.

(3) Efficient tracking of allocated memory.

The FinalizedAlloc type is a wrapper for an ExecutorAddr and requires only
64-bits to store in the controller. The meaning of the address held by the
FinalizedAlloc is left up to the memory manager implementation, but the
FinalizedAlloc type enforces a requirement that deallocate be called on any
non-default values prior to destruction. The deallocate method takes a
vector<FinalizedAlloc>, allowing for bulk deallocation of many allocations in a
single call.

Memory manager implementations will typically store the address of some
allocation metadata in the executor in the FinalizedAlloc, as holding this
metadata in the executor is often cheaper and may allow for clean deallocation
even in failure cases where the connection with the controller is lost.

(4) Support for "allocation actions" and finalize-lifetime memory.

Allocation actions are pairs (finalize_act, deallocate_act) of JITTargetAddress
triples (fn, arg_buffer_addr, arg_buffer_size), that can be attached to a
finalize request. At finalization time, after memory protections have been
applied, each of the "finalize_act" elements will be called in order (skipping
any elements whose fn value is zero) as

((char*(*)(const char *, size_t))fn)((const char *)arg_buffer_addr,
                                     (size_t)arg_buffer_size);

At deallocation time the deallocate elements will be run in reverse order (again
skipping any elements where fn is zero).

The returned char * should be null to indicate success, or a non-null
heap-allocated string error message to indicate failure.

These actions allow finalization and deallocation to be extended to include
operations like registering and deregistering eh-frames, TLS sections,
initializer and deinitializers, and language metadata sections. Previously these
operations required separate callWrapper invocations. Compared to callWrapper
invocations, actions require no extra IPC/RPC, reducing costs and eliminating
a potential source of errors.

Finalize lifetime memory can be used to support finalize actions: Sections with
finalize lifetime should be destroyed by memory managers immediately after
finalization actions have been run. Finalize memory can be used to support
finalize actions (e.g. with extra-metadata, or synthesized finalize actions)
without incurring permanent memory overhead.
2021-10-11 19:12:42 -07:00

833 lines
28 KiB
C++

//===------- ObjectLinkingLayer.cpp - JITLink backed ORC ObjectLayer ------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ExecutionEngine/JITLink/EHFrameSupport.h"
#include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
#include "llvm/Support/MemoryBuffer.h"
#include <string>
#include <vector>
#define DEBUG_TYPE "orc"
using namespace llvm;
using namespace llvm::jitlink;
using namespace llvm::orc;
namespace {
class LinkGraphMaterializationUnit : public MaterializationUnit {
private:
struct LinkGraphInterface {
SymbolFlagsMap SymbolFlags;
SymbolStringPtr InitSymbol;
};
public:
static std::unique_ptr<LinkGraphMaterializationUnit>
Create(ObjectLinkingLayer &ObjLinkingLayer, std::unique_ptr<LinkGraph> G) {
auto LGI = scanLinkGraph(ObjLinkingLayer.getExecutionSession(), *G);
return std::unique_ptr<LinkGraphMaterializationUnit>(
new LinkGraphMaterializationUnit(ObjLinkingLayer, std::move(G),
std::move(LGI)));
}
StringRef getName() const override { return G->getName(); }
void materialize(std::unique_ptr<MaterializationResponsibility> MR) override {
ObjLinkingLayer.emit(std::move(MR), std::move(G));
}
private:
static LinkGraphInterface scanLinkGraph(ExecutionSession &ES, LinkGraph &G) {
LinkGraphInterface LGI;
for (auto *Sym : G.defined_symbols()) {
// Skip local symbols.
if (Sym->getScope() == Scope::Local)
continue;
assert(Sym->hasName() && "Anonymous non-local symbol?");
JITSymbolFlags Flags;
if (Sym->getScope() == Scope::Default)
Flags |= JITSymbolFlags::Exported;
if (Sym->isCallable())
Flags |= JITSymbolFlags::Callable;
LGI.SymbolFlags[ES.intern(Sym->getName())] = Flags;
}
if ((G.getTargetTriple().isOSBinFormatMachO() && hasMachOInitSection(G)) ||
(G.getTargetTriple().isOSBinFormatELF() && hasELFInitSection(G)))
LGI.InitSymbol = makeInitSymbol(ES, G);
return LGI;
}
static bool hasMachOInitSection(LinkGraph &G) {
for (auto &Sec : G.sections())
if (Sec.getName() == "__DATA,__obj_selrefs" ||
Sec.getName() == "__DATA,__objc_classlist" ||
Sec.getName() == "__TEXT,__swift5_protos" ||
Sec.getName() == "__TEXT,__swift5_proto" ||
Sec.getName() == "__DATA,__mod_init_func")
return true;
return false;
}
static bool hasELFInitSection(LinkGraph &G) {
for (auto &Sec : G.sections())
if (Sec.getName() == ".init_array")
return true;
return false;
}
static SymbolStringPtr makeInitSymbol(ExecutionSession &ES, LinkGraph &G) {
std::string InitSymString;
raw_string_ostream(InitSymString)
<< "$." << G.getName() << ".__inits" << Counter++;
return ES.intern(InitSymString);
}
LinkGraphMaterializationUnit(ObjectLinkingLayer &ObjLinkingLayer,
std::unique_ptr<LinkGraph> G,
LinkGraphInterface LGI)
: MaterializationUnit(std::move(LGI.SymbolFlags),
std::move(LGI.InitSymbol)),
ObjLinkingLayer(ObjLinkingLayer), G(std::move(G)) {}
void discard(const JITDylib &JD, const SymbolStringPtr &Name) override {
for (auto *Sym : G->defined_symbols())
if (Sym->getName() == *Name) {
assert(Sym->getLinkage() == Linkage::Weak &&
"Discarding non-weak definition");
G->makeExternal(*Sym);
break;
}
}
ObjectLinkingLayer &ObjLinkingLayer;
std::unique_ptr<LinkGraph> G;
static std::atomic<uint64_t> Counter;
};
std::atomic<uint64_t> LinkGraphMaterializationUnit::Counter{0};
} // end anonymous namespace
namespace llvm {
namespace orc {
class ObjectLinkingLayerJITLinkContext final : public JITLinkContext {
public:
ObjectLinkingLayerJITLinkContext(
ObjectLinkingLayer &Layer,
std::unique_ptr<MaterializationResponsibility> MR,
std::unique_ptr<MemoryBuffer> ObjBuffer)
: JITLinkContext(&MR->getTargetJITDylib()), Layer(Layer),
MR(std::move(MR)), ObjBuffer(std::move(ObjBuffer)) {}
~ObjectLinkingLayerJITLinkContext() {
// If there is an object buffer return function then use it to
// return ownership of the buffer.
if (Layer.ReturnObjectBuffer && ObjBuffer)
Layer.ReturnObjectBuffer(std::move(ObjBuffer));
}
JITLinkMemoryManager &getMemoryManager() override { return Layer.MemMgr; }
void notifyMaterializing(LinkGraph &G) {
for (auto &P : Layer.Plugins)
P->notifyMaterializing(*MR, G, *this,
ObjBuffer ? ObjBuffer->getMemBufferRef()
: MemoryBufferRef());
}
void notifyFailed(Error Err) override {
for (auto &P : Layer.Plugins)
Err = joinErrors(std::move(Err), P->notifyFailed(*MR));
Layer.getExecutionSession().reportError(std::move(Err));
MR->failMaterialization();
}
void lookup(const LookupMap &Symbols,
std::unique_ptr<JITLinkAsyncLookupContinuation> LC) override {
JITDylibSearchOrder LinkOrder;
MR->getTargetJITDylib().withLinkOrderDo(
[&](const JITDylibSearchOrder &LO) { LinkOrder = LO; });
auto &ES = Layer.getExecutionSession();
SymbolLookupSet LookupSet;
for (auto &KV : Symbols) {
orc::SymbolLookupFlags LookupFlags;
switch (KV.second) {
case jitlink::SymbolLookupFlags::RequiredSymbol:
LookupFlags = orc::SymbolLookupFlags::RequiredSymbol;
break;
case jitlink::SymbolLookupFlags::WeaklyReferencedSymbol:
LookupFlags = orc::SymbolLookupFlags::WeaklyReferencedSymbol;
break;
}
LookupSet.add(ES.intern(KV.first), LookupFlags);
}
// OnResolve -- De-intern the symbols and pass the result to the linker.
auto OnResolve = [LookupContinuation =
std::move(LC)](Expected<SymbolMap> Result) mutable {
if (!Result)
LookupContinuation->run(Result.takeError());
else {
AsyncLookupResult LR;
for (auto &KV : *Result)
LR[*KV.first] = KV.second;
LookupContinuation->run(std::move(LR));
}
};
for (auto &KV : InternalNamedSymbolDeps) {
SymbolDependenceMap InternalDeps;
InternalDeps[&MR->getTargetJITDylib()] = std::move(KV.second);
MR->addDependencies(KV.first, InternalDeps);
}
ES.lookup(LookupKind::Static, LinkOrder, std::move(LookupSet),
SymbolState::Resolved, std::move(OnResolve),
[this](const SymbolDependenceMap &Deps) {
registerDependencies(Deps);
});
}
Error notifyResolved(LinkGraph &G) override {
auto &ES = Layer.getExecutionSession();
SymbolFlagsMap ExtraSymbolsToClaim;
bool AutoClaim = Layer.AutoClaimObjectSymbols;
SymbolMap InternedResult;
for (auto *Sym : G.defined_symbols())
if (Sym->hasName() && Sym->getScope() != Scope::Local) {
auto InternedName = ES.intern(Sym->getName());
JITSymbolFlags Flags;
if (Sym->isCallable())
Flags |= JITSymbolFlags::Callable;
if (Sym->getScope() == Scope::Default)
Flags |= JITSymbolFlags::Exported;
InternedResult[InternedName] =
JITEvaluatedSymbol(Sym->getAddress(), Flags);
if (AutoClaim && !MR->getSymbols().count(InternedName)) {
assert(!ExtraSymbolsToClaim.count(InternedName) &&
"Duplicate symbol to claim?");
ExtraSymbolsToClaim[InternedName] = Flags;
}
}
for (auto *Sym : G.absolute_symbols())
if (Sym->hasName()) {
auto InternedName = ES.intern(Sym->getName());
JITSymbolFlags Flags;
Flags |= JITSymbolFlags::Absolute;
if (Sym->isCallable())
Flags |= JITSymbolFlags::Callable;
if (Sym->getLinkage() == Linkage::Weak)
Flags |= JITSymbolFlags::Weak;
InternedResult[InternedName] =
JITEvaluatedSymbol(Sym->getAddress(), Flags);
if (AutoClaim && !MR->getSymbols().count(InternedName)) {
assert(!ExtraSymbolsToClaim.count(InternedName) &&
"Duplicate symbol to claim?");
ExtraSymbolsToClaim[InternedName] = Flags;
}
}
if (!ExtraSymbolsToClaim.empty())
if (auto Err = MR->defineMaterializing(ExtraSymbolsToClaim))
return Err;
{
// Check that InternedResult matches up with MR->getSymbols().
// This guards against faulty transformations / compilers / object caches.
// First check that there aren't any missing symbols.
size_t NumMaterializationSideEffectsOnlySymbols = 0;
SymbolNameVector ExtraSymbols;
SymbolNameVector MissingSymbols;
for (auto &KV : MR->getSymbols()) {
// If this is a materialization-side-effects only symbol then bump
// the counter and make sure it's *not* defined, otherwise make
// sure that it is defined.
if (KV.second.hasMaterializationSideEffectsOnly()) {
++NumMaterializationSideEffectsOnlySymbols;
if (InternedResult.count(KV.first))
ExtraSymbols.push_back(KV.first);
continue;
} else if (!InternedResult.count(KV.first))
MissingSymbols.push_back(KV.first);
}
// If there were missing symbols then report the error.
if (!MissingSymbols.empty())
return make_error<MissingSymbolDefinitions>(
Layer.getExecutionSession().getSymbolStringPool(), G.getName(),
std::move(MissingSymbols));
// If there are more definitions than expected, add them to the
// ExtraSymbols vector.
if (InternedResult.size() >
MR->getSymbols().size() - NumMaterializationSideEffectsOnlySymbols) {
for (auto &KV : InternedResult)
if (!MR->getSymbols().count(KV.first))
ExtraSymbols.push_back(KV.first);
}
// If there were extra definitions then report the error.
if (!ExtraSymbols.empty())
return make_error<UnexpectedSymbolDefinitions>(
Layer.getExecutionSession().getSymbolStringPool(), G.getName(),
std::move(ExtraSymbols));
}
if (auto Err = MR->notifyResolved(InternedResult))
return Err;
Layer.notifyLoaded(*MR);
return Error::success();
}
void notifyFinalized(JITLinkMemoryManager::FinalizedAlloc A) override {
if (auto Err = Layer.notifyEmitted(*MR, std::move(A))) {
Layer.getExecutionSession().reportError(std::move(Err));
MR->failMaterialization();
return;
}
if (auto Err = MR->notifyEmitted()) {
Layer.getExecutionSession().reportError(std::move(Err));
MR->failMaterialization();
}
}
LinkGraphPassFunction getMarkLivePass(const Triple &TT) const override {
return [this](LinkGraph &G) { return markResponsibilitySymbolsLive(G); };
}
Error modifyPassConfig(LinkGraph &LG, PassConfiguration &Config) override {
// Add passes to mark duplicate defs as should-discard, and to walk the
// link graph to build the symbol dependence graph.
Config.PrePrunePasses.push_back([this](LinkGraph &G) {
return claimOrExternalizeWeakAndCommonSymbols(G);
});
Layer.modifyPassConfig(*MR, LG, Config);
Config.PostPrunePasses.push_back(
[this](LinkGraph &G) { return computeNamedSymbolDependencies(G); });
return Error::success();
}
private:
// Symbol name dependencies:
// Internal: Defined in this graph.
// External: Defined externally.
struct BlockSymbolDependencies {
SymbolNameSet Internal, External;
};
// Lazily populated map of blocks to BlockSymbolDependencies values.
class BlockDependenciesMap {
public:
BlockDependenciesMap(ExecutionSession &ES,
DenseMap<const Block *, DenseSet<Block *>> BlockDeps)
: ES(ES), BlockDeps(std::move(BlockDeps)) {}
const BlockSymbolDependencies &operator[](const Block &B) {
// Check the cache first.
auto I = BlockTransitiveDepsCache.find(&B);
if (I != BlockTransitiveDepsCache.end())
return I->second;
// No value. Populate the cache.
BlockSymbolDependencies BTDCacheVal;
auto BDI = BlockDeps.find(&B);
assert(BDI != BlockDeps.end() && "No block dependencies");
for (auto *BDep : BDI->second) {
auto &BID = getBlockImmediateDeps(*BDep);
for (auto &ExternalDep : BID.External)
BTDCacheVal.External.insert(ExternalDep);
for (auto &InternalDep : BID.Internal)
BTDCacheVal.Internal.insert(InternalDep);
}
return BlockTransitiveDepsCache
.insert(std::make_pair(&B, std::move(BTDCacheVal)))
.first->second;
}
SymbolStringPtr &getInternedName(Symbol &Sym) {
auto I = NameCache.find(&Sym);
if (I != NameCache.end())
return I->second;
return NameCache.insert(std::make_pair(&Sym, ES.intern(Sym.getName())))
.first->second;
}
private:
BlockSymbolDependencies &getBlockImmediateDeps(Block &B) {
// Check the cache first.
auto I = BlockImmediateDepsCache.find(&B);
if (I != BlockImmediateDepsCache.end())
return I->second;
BlockSymbolDependencies BIDCacheVal;
for (auto &E : B.edges()) {
auto &Tgt = E.getTarget();
if (Tgt.getScope() != Scope::Local) {
if (Tgt.isExternal())
BIDCacheVal.External.insert(getInternedName(Tgt));
else
BIDCacheVal.Internal.insert(getInternedName(Tgt));
}
}
return BlockImmediateDepsCache
.insert(std::make_pair(&B, std::move(BIDCacheVal)))
.first->second;
}
ExecutionSession &ES;
DenseMap<const Block *, DenseSet<Block *>> BlockDeps;
DenseMap<const Symbol *, SymbolStringPtr> NameCache;
DenseMap<const Block *, BlockSymbolDependencies> BlockImmediateDepsCache;
DenseMap<const Block *, BlockSymbolDependencies> BlockTransitiveDepsCache;
};
Error claimOrExternalizeWeakAndCommonSymbols(LinkGraph &G) {
auto &ES = Layer.getExecutionSession();
SymbolFlagsMap NewSymbolsToClaim;
std::vector<std::pair<SymbolStringPtr, Symbol *>> NameToSym;
auto ProcessSymbol = [&](Symbol *Sym) {
if (Sym->hasName() && Sym->getLinkage() == Linkage::Weak) {
auto Name = ES.intern(Sym->getName());
if (!MR->getSymbols().count(ES.intern(Sym->getName()))) {
JITSymbolFlags SF = JITSymbolFlags::Weak;
if (Sym->getScope() == Scope::Default)
SF |= JITSymbolFlags::Exported;
NewSymbolsToClaim[Name] = SF;
NameToSym.push_back(std::make_pair(std::move(Name), Sym));
}
}
};
for (auto *Sym : G.defined_symbols())
ProcessSymbol(Sym);
for (auto *Sym : G.absolute_symbols())
ProcessSymbol(Sym);
// Attempt to claim all weak defs that we're not already responsible for.
// This cannot fail -- any clashes will just result in rejection of our
// claim, at which point we'll externalize that symbol.
cantFail(MR->defineMaterializing(std::move(NewSymbolsToClaim)));
for (auto &KV : NameToSym)
if (!MR->getSymbols().count(KV.first))
G.makeExternal(*KV.second);
return Error::success();
}
Error markResponsibilitySymbolsLive(LinkGraph &G) const {
auto &ES = Layer.getExecutionSession();
for (auto *Sym : G.defined_symbols())
if (Sym->hasName() && MR->getSymbols().count(ES.intern(Sym->getName())))
Sym->setLive(true);
return Error::success();
}
Error computeNamedSymbolDependencies(LinkGraph &G) {
auto &ES = MR->getTargetJITDylib().getExecutionSession();
auto BlockDeps = computeBlockNonLocalDeps(G);
// Compute dependencies for symbols defined in the JITLink graph.
for (auto *Sym : G.defined_symbols()) {
// Skip local symbols: we do not track dependencies for these.
if (Sym->getScope() == Scope::Local)
continue;
assert(Sym->hasName() &&
"Defined non-local jitlink::Symbol should have a name");
auto &SymDeps = BlockDeps[Sym->getBlock()];
if (SymDeps.External.empty() && SymDeps.Internal.empty())
continue;
auto SymName = ES.intern(Sym->getName());
if (!SymDeps.External.empty())
ExternalNamedSymbolDeps[SymName] = SymDeps.External;
if (!SymDeps.Internal.empty())
InternalNamedSymbolDeps[SymName] = SymDeps.Internal;
}
for (auto &P : Layer.Plugins) {
auto SynthDeps = P->getSyntheticSymbolDependencies(*MR);
if (SynthDeps.empty())
continue;
DenseSet<Block *> BlockVisited;
for (auto &KV : SynthDeps) {
auto &Name = KV.first;
auto &DepsForName = KV.second;
for (auto *Sym : DepsForName) {
if (Sym->getScope() == Scope::Local) {
auto &BDeps = BlockDeps[Sym->getBlock()];
for (auto &S : BDeps.Internal)
InternalNamedSymbolDeps[Name].insert(S);
for (auto &S : BDeps.External)
ExternalNamedSymbolDeps[Name].insert(S);
} else {
if (Sym->isExternal())
ExternalNamedSymbolDeps[Name].insert(
BlockDeps.getInternedName(*Sym));
else
InternalNamedSymbolDeps[Name].insert(
BlockDeps.getInternedName(*Sym));
}
}
}
}
return Error::success();
}
BlockDependenciesMap computeBlockNonLocalDeps(LinkGraph &G) {
// First calculate the reachable-via-non-local-symbol blocks for each block.
struct BlockInfo {
DenseSet<Block *> Dependencies;
DenseSet<Block *> Dependants;
bool DependenciesChanged = true;
};
DenseMap<Block *, BlockInfo> BlockInfos;
SmallVector<Block *> WorkList;
// Pre-allocate map entries. This prevents any iterator/reference
// invalidation in the next loop.
for (auto *B : G.blocks())
(void)BlockInfos[B];
// Build initial worklist, record block dependencies/dependants and
// non-local symbol dependencies.
for (auto *B : G.blocks()) {
auto &BI = BlockInfos[B];
for (auto &E : B->edges()) {
if (E.getTarget().getScope() == Scope::Local) {
auto &TgtB = E.getTarget().getBlock();
if (&TgtB != B) {
BI.Dependencies.insert(&TgtB);
BlockInfos[&TgtB].Dependants.insert(B);
}
}
}
// If this node has both dependants and dependencies then add it to the
// worklist to propagate the dependencies to the dependants.
if (!BI.Dependants.empty() && !BI.Dependencies.empty())
WorkList.push_back(B);
}
// Propagate block-level dependencies through the block-dependence graph.
while (!WorkList.empty()) {
auto *B = WorkList.pop_back_val();
auto &BI = BlockInfos[B];
assert(BI.DependenciesChanged &&
"Block in worklist has unchanged dependencies");
BI.DependenciesChanged = false;
for (auto *Dependant : BI.Dependants) {
auto &DependantBI = BlockInfos[Dependant];
for (auto *Dependency : BI.Dependencies) {
if (Dependant != Dependency &&
DependantBI.Dependencies.insert(Dependency).second)
if (!DependantBI.DependenciesChanged) {
DependantBI.DependenciesChanged = true;
WorkList.push_back(Dependant);
}
}
}
}
DenseMap<const Block *, DenseSet<Block *>> BlockDeps;
for (auto &KV : BlockInfos)
BlockDeps[KV.first] = std::move(KV.second.Dependencies);
return BlockDependenciesMap(Layer.getExecutionSession(),
std::move(BlockDeps));
}
void registerDependencies(const SymbolDependenceMap &QueryDeps) {
for (auto &NamedDepsEntry : ExternalNamedSymbolDeps) {
auto &Name = NamedDepsEntry.first;
auto &NameDeps = NamedDepsEntry.second;
SymbolDependenceMap SymbolDeps;
for (const auto &QueryDepsEntry : QueryDeps) {
JITDylib &SourceJD = *QueryDepsEntry.first;
const SymbolNameSet &Symbols = QueryDepsEntry.second;
auto &DepsForJD = SymbolDeps[&SourceJD];
for (const auto &S : Symbols)
if (NameDeps.count(S))
DepsForJD.insert(S);
if (DepsForJD.empty())
SymbolDeps.erase(&SourceJD);
}
MR->addDependencies(Name, SymbolDeps);
}
}
ObjectLinkingLayer &Layer;
std::unique_ptr<MaterializationResponsibility> MR;
std::unique_ptr<MemoryBuffer> ObjBuffer;
DenseMap<SymbolStringPtr, SymbolNameSet> ExternalNamedSymbolDeps;
DenseMap<SymbolStringPtr, SymbolNameSet> InternalNamedSymbolDeps;
};
ObjectLinkingLayer::Plugin::~Plugin() {}
char ObjectLinkingLayer::ID;
using BaseT = RTTIExtends<ObjectLinkingLayer, ObjectLayer>;
ObjectLinkingLayer::ObjectLinkingLayer(ExecutionSession &ES)
: BaseT(ES), MemMgr(ES.getExecutorProcessControl().getMemMgr()) {
ES.registerResourceManager(*this);
}
ObjectLinkingLayer::ObjectLinkingLayer(ExecutionSession &ES,
JITLinkMemoryManager &MemMgr)
: BaseT(ES), MemMgr(MemMgr) {
ES.registerResourceManager(*this);
}
ObjectLinkingLayer::ObjectLinkingLayer(
ExecutionSession &ES, std::unique_ptr<JITLinkMemoryManager> MemMgr)
: BaseT(ES), MemMgr(*MemMgr), MemMgrOwnership(std::move(MemMgr)) {
ES.registerResourceManager(*this);
}
ObjectLinkingLayer::~ObjectLinkingLayer() {
assert(Allocs.empty() && "Layer destroyed with resources still attached");
getExecutionSession().deregisterResourceManager(*this);
}
Error ObjectLinkingLayer::add(ResourceTrackerSP RT,
std::unique_ptr<LinkGraph> G) {
auto &JD = RT->getJITDylib();
return JD.define(LinkGraphMaterializationUnit::Create(*this, std::move(G)),
std::move(RT));
}
void ObjectLinkingLayer::emit(std::unique_ptr<MaterializationResponsibility> R,
std::unique_ptr<MemoryBuffer> O) {
assert(O && "Object must not be null");
MemoryBufferRef ObjBuffer = O->getMemBufferRef();
auto Ctx = std::make_unique<ObjectLinkingLayerJITLinkContext>(
*this, std::move(R), std::move(O));
if (auto G = createLinkGraphFromObject(ObjBuffer)) {
Ctx->notifyMaterializing(**G);
link(std::move(*G), std::move(Ctx));
} else {
Ctx->notifyFailed(G.takeError());
}
}
void ObjectLinkingLayer::emit(std::unique_ptr<MaterializationResponsibility> R,
std::unique_ptr<LinkGraph> G) {
auto Ctx = std::make_unique<ObjectLinkingLayerJITLinkContext>(
*this, std::move(R), nullptr);
Ctx->notifyMaterializing(*G);
link(std::move(G), std::move(Ctx));
}
void ObjectLinkingLayer::modifyPassConfig(MaterializationResponsibility &MR,
LinkGraph &G,
PassConfiguration &PassConfig) {
for (auto &P : Plugins)
P->modifyPassConfig(MR, G, PassConfig);
}
void ObjectLinkingLayer::notifyLoaded(MaterializationResponsibility &MR) {
for (auto &P : Plugins)
P->notifyLoaded(MR);
}
Error ObjectLinkingLayer::notifyEmitted(MaterializationResponsibility &MR,
FinalizedAlloc FA) {
Error Err = Error::success();
for (auto &P : Plugins)
Err = joinErrors(std::move(Err), P->notifyEmitted(MR));
if (Err)
return Err;
return MR.withResourceKeyDo(
[&](ResourceKey K) { Allocs[K].push_back(std::move(FA)); });
}
Error ObjectLinkingLayer::handleRemoveResources(ResourceKey K) {
{
Error Err = Error::success();
for (auto &P : Plugins)
Err = joinErrors(std::move(Err), P->notifyRemovingResources(K));
if (Err)
return Err;
}
std::vector<FinalizedAlloc> AllocsToRemove;
getExecutionSession().runSessionLocked([&] {
auto I = Allocs.find(K);
if (I != Allocs.end()) {
std::swap(AllocsToRemove, I->second);
Allocs.erase(I);
}
});
return MemMgr.deallocate(std::move(AllocsToRemove));
}
void ObjectLinkingLayer::handleTransferResources(ResourceKey DstKey,
ResourceKey SrcKey) {
auto I = Allocs.find(SrcKey);
if (I != Allocs.end()) {
auto &SrcAllocs = I->second;
auto &DstAllocs = Allocs[DstKey];
DstAllocs.reserve(DstAllocs.size() + SrcAllocs.size());
for (auto &Alloc : SrcAllocs)
DstAllocs.push_back(std::move(Alloc));
// Erase SrcKey entry using value rather than iterator I: I may have been
// invalidated when we looked up DstKey.
Allocs.erase(SrcKey);
}
for (auto &P : Plugins)
P->notifyTransferringResources(DstKey, SrcKey);
}
EHFrameRegistrationPlugin::EHFrameRegistrationPlugin(
ExecutionSession &ES, std::unique_ptr<EHFrameRegistrar> Registrar)
: ES(ES), Registrar(std::move(Registrar)) {}
void EHFrameRegistrationPlugin::modifyPassConfig(
MaterializationResponsibility &MR, LinkGraph &G,
PassConfiguration &PassConfig) {
PassConfig.PostFixupPasses.push_back(createEHFrameRecorderPass(
G.getTargetTriple(), [this, &MR](JITTargetAddress Addr, size_t Size) {
if (Addr) {
std::lock_guard<std::mutex> Lock(EHFramePluginMutex);
assert(!InProcessLinks.count(&MR) &&
"Link for MR already being tracked?");
InProcessLinks[&MR] = {Addr, Size};
}
}));
}
Error EHFrameRegistrationPlugin::notifyEmitted(
MaterializationResponsibility &MR) {
EHFrameRange EmittedRange;
{
std::lock_guard<std::mutex> Lock(EHFramePluginMutex);
auto EHFrameRangeItr = InProcessLinks.find(&MR);
if (EHFrameRangeItr == InProcessLinks.end())
return Error::success();
EmittedRange = EHFrameRangeItr->second;
assert(EmittedRange.Addr && "eh-frame addr to register can not be null");
InProcessLinks.erase(EHFrameRangeItr);
}
if (auto Err = MR.withResourceKeyDo(
[&](ResourceKey K) { EHFrameRanges[K].push_back(EmittedRange); }))
return Err;
return Registrar->registerEHFrames(EmittedRange.Addr, EmittedRange.Size);
}
Error EHFrameRegistrationPlugin::notifyFailed(
MaterializationResponsibility &MR) {
std::lock_guard<std::mutex> Lock(EHFramePluginMutex);
InProcessLinks.erase(&MR);
return Error::success();
}
Error EHFrameRegistrationPlugin::notifyRemovingResources(ResourceKey K) {
std::vector<EHFrameRange> RangesToRemove;
ES.runSessionLocked([&] {
auto I = EHFrameRanges.find(K);
if (I != EHFrameRanges.end()) {
RangesToRemove = std::move(I->second);
EHFrameRanges.erase(I);
}
});
Error Err = Error::success();
while (!RangesToRemove.empty()) {
auto RangeToRemove = RangesToRemove.back();
RangesToRemove.pop_back();
assert(RangeToRemove.Addr && "Untracked eh-frame range must not be null");
Err = joinErrors(
std::move(Err),
Registrar->deregisterEHFrames(RangeToRemove.Addr, RangeToRemove.Size));
}
return Err;
}
void EHFrameRegistrationPlugin::notifyTransferringResources(
ResourceKey DstKey, ResourceKey SrcKey) {
auto SI = EHFrameRanges.find(SrcKey);
if (SI == EHFrameRanges.end())
return;
auto DI = EHFrameRanges.find(DstKey);
if (DI != EHFrameRanges.end()) {
auto &SrcRanges = SI->second;
auto &DstRanges = DI->second;
DstRanges.reserve(DstRanges.size() + SrcRanges.size());
for (auto &SrcRange : SrcRanges)
DstRanges.push_back(std::move(SrcRange));
EHFrameRanges.erase(SI);
} else {
// We need to move SrcKey's ranges over without invalidating the SI
// iterator.
auto Tmp = std::move(SI->second);
EHFrameRanges.erase(SI);
EHFrameRanges[DstKey] = std::move(Tmp);
}
}
} // End namespace orc.
} // End namespace llvm.