llvm-project/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp
Lang Hames 78b083dbb7 [ORC] Add finalization & deallocation actions, SimpleExecutorMemoryManager class
Finalization and deallocation actions are a key part of the upcoming
JITLinkMemoryManager redesign: They generalize the existing finalization and
deallocate concepts (basically "copy-and-mprotect", and "munmap") to include
support for arbitrary registration and deregistration of parts of JIT linked
code. This allows us to register and deregister eh-frames, TLV sections,
language metadata, etc. using regular memory management calls with no additional
IPC/RPC overhead, which should both improve JIT performance and simplify
interactions between ORC and the ORC runtime.

The SimpleExecutorMemoryManager class provides executor-side support for memory
management operations, including finalization and deallocation actions.

This support is being added in advance of the rest of the memory manager
redesign as it will simplify the introduction of an EPC based
RuntimeDyld::MemoryManager (since eh-frame registration/deregistration will be
expressible as actions). The new RuntimeDyld::MemoryManager will in turn allow
us to remove older remote allocators that are blocking the rest of the memory
manager changes.
2021-09-17 09:55:45 +10:00

318 lines
11 KiB
C++

//===------- SimpleRemoteEPC.cpp -- Simple remote executor control --------===//
//
// 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/SimpleRemoteEPC.h"
#include "llvm/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.h"
#include "llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h"
#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
#include "llvm/Support/FormatVariadic.h"
#define DEBUG_TYPE "orc"
namespace llvm {
namespace orc {
namespace shared {
template <>
class SPSSerializationTraits<SPSRemoteSymbolLookupSetElement,
SymbolLookupSet::value_type> {
public:
static size_t size(const SymbolLookupSet::value_type &V) {
return SPSArgList<SPSString, bool>::size(
*V.first, V.second == SymbolLookupFlags::RequiredSymbol);
}
static bool serialize(SPSOutputBuffer &OB,
const SymbolLookupSet::value_type &V) {
return SPSArgList<SPSString, bool>::serialize(
OB, *V.first, V.second == SymbolLookupFlags::RequiredSymbol);
}
};
template <>
class TrivialSPSSequenceSerialization<SPSRemoteSymbolLookupSetElement,
SymbolLookupSet> {
public:
static constexpr bool available = true;
};
template <>
class SPSSerializationTraits<SPSRemoteSymbolLookup,
ExecutorProcessControl::LookupRequest> {
using MemberSerialization =
SPSArgList<SPSExecutorAddress, SPSRemoteSymbolLookupSet>;
public:
static size_t size(const ExecutorProcessControl::LookupRequest &LR) {
return MemberSerialization::size(ExecutorAddress(LR.Handle), LR.Symbols);
}
static bool serialize(SPSOutputBuffer &OB,
const ExecutorProcessControl::LookupRequest &LR) {
return MemberSerialization::serialize(OB, ExecutorAddress(LR.Handle),
LR.Symbols);
}
};
} // end namespace shared
SimpleRemoteEPC::~SimpleRemoteEPC() {
assert(Disconnected && "Destroyed without disconnection");
}
Expected<tpctypes::DylibHandle>
SimpleRemoteEPC::loadDylib(const char *DylibPath) {
Expected<tpctypes::DylibHandle> H((tpctypes::DylibHandle()));
if (auto Err = callSPSWrapper<shared::SPSLoadDylibSignature>(
LoadDylibAddr.getValue(), H, JDI.JITDispatchContextAddress,
StringRef(DylibPath), (uint64_t)0))
return std::move(Err);
return H;
}
Expected<std::vector<tpctypes::LookupResult>>
SimpleRemoteEPC::lookupSymbols(ArrayRef<LookupRequest> Request) {
Expected<std::vector<tpctypes::LookupResult>> R(
(std::vector<tpctypes::LookupResult>()));
if (auto Err = callSPSWrapper<shared::SPSLookupSymbolsSignature>(
LookupSymbolsAddr.getValue(), R, JDI.JITDispatchContextAddress,
Request))
return std::move(Err);
return R;
}
Expected<int32_t> SimpleRemoteEPC::runAsMain(JITTargetAddress MainFnAddr,
ArrayRef<std::string> Args) {
int64_t Result = 0;
if (auto Err = callSPSWrapper<rt::SPSRunAsMainSignature>(
RunAsMainAddr.getValue(), Result, ExecutorAddress(MainFnAddr), Args))
return std::move(Err);
return Result;
}
void SimpleRemoteEPC::callWrapperAsync(SendResultFunction OnComplete,
JITTargetAddress WrapperFnAddr,
ArrayRef<char> ArgBuffer) {
uint64_t SeqNo;
{
std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
SeqNo = getNextSeqNo();
assert(!PendingCallWrapperResults.count(SeqNo) && "SeqNo already in use");
PendingCallWrapperResults[SeqNo] = std::move(OnComplete);
}
if (auto Err = T->sendMessage(SimpleRemoteEPCOpcode::CallWrapper, SeqNo,
ExecutorAddress(WrapperFnAddr), ArgBuffer)) {
getExecutionSession().reportError(std::move(Err));
}
}
Error SimpleRemoteEPC::disconnect() {
Disconnected = true;
T->disconnect();
return Error::success();
}
Expected<SimpleRemoteEPCTransportClient::HandleMessageAction>
SimpleRemoteEPC::handleMessage(SimpleRemoteEPCOpcode OpC, uint64_t SeqNo,
ExecutorAddress TagAddr,
SimpleRemoteEPCArgBytesVector ArgBytes) {
using UT = std::underlying_type_t<SimpleRemoteEPCOpcode>;
if (static_cast<UT>(OpC) > static_cast<UT>(SimpleRemoteEPCOpcode::LastOpC))
return make_error<StringError>("Unexpected opcode",
inconvertibleErrorCode());
switch (OpC) {
case SimpleRemoteEPCOpcode::Setup:
if (auto Err = handleSetup(SeqNo, TagAddr, std::move(ArgBytes)))
return std::move(Err);
break;
case SimpleRemoteEPCOpcode::Hangup:
// FIXME: Put EPC into 'detached' state.
return SimpleRemoteEPCTransportClient::EndSession;
case SimpleRemoteEPCOpcode::Result:
if (auto Err = handleResult(SeqNo, TagAddr, std::move(ArgBytes)))
return std::move(Err);
break;
case SimpleRemoteEPCOpcode::CallWrapper:
handleCallWrapper(SeqNo, TagAddr, std::move(ArgBytes));
break;
}
return ContinueSession;
}
void SimpleRemoteEPC::handleDisconnect(Error Err) {
PendingCallWrapperResultsMap TmpPending;
{
std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
std::swap(TmpPending, PendingCallWrapperResults);
}
for (auto &KV : TmpPending)
KV.second(
shared::WrapperFunctionResult::createOutOfBandError("disconnecting"));
if (Err) {
// FIXME: Move ReportError to EPC.
if (ES)
ES->reportError(std::move(Err));
else
logAllUnhandledErrors(std::move(Err), errs(), "SimpleRemoteEPC: ");
}
}
Expected<std::unique_ptr<jitlink::JITLinkMemoryManager>>
SimpleRemoteEPC::createMemoryManager() {
EPCGenericJITLinkMemoryManager::SymbolAddrs SAs;
if (auto Err = getBootstrapSymbols(
{{SAs.Allocator, rt::SimpleExecutorMemoryManagerInstanceName},
{SAs.Reserve, rt::SimpleExecutorMemoryManagerReserveWrapperName},
{SAs.Finalize, rt::SimpleExecutorMemoryManagerFinalizeWrapperName},
{SAs.Deallocate,
rt::SimpleExecutorMemoryManagerDeallocateWrapperName}}))
return std::move(Err);
return std::make_unique<EPCGenericJITLinkMemoryManager>(*this, SAs);
}
Expected<std::unique_ptr<ExecutorProcessControl::MemoryAccess>>
SimpleRemoteEPC::createMemoryAccess() {
return nullptr;
}
Error SimpleRemoteEPC::handleSetup(uint64_t SeqNo, ExecutorAddress TagAddr,
SimpleRemoteEPCArgBytesVector ArgBytes) {
if (SeqNo != 0)
return make_error<StringError>("Setup packet SeqNo not zero",
inconvertibleErrorCode());
if (TagAddr)
return make_error<StringError>("Setup packet TagAddr not zero",
inconvertibleErrorCode());
std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
auto I = PendingCallWrapperResults.find(0);
assert(PendingCallWrapperResults.size() == 1 &&
I != PendingCallWrapperResults.end() &&
"Setup message handler not connectly set up");
auto SetupMsgHandler = std::move(I->second);
PendingCallWrapperResults.erase(I);
auto WFR =
shared::WrapperFunctionResult::copyFrom(ArgBytes.data(), ArgBytes.size());
SetupMsgHandler(std::move(WFR));
return Error::success();
}
void SimpleRemoteEPC::prepareToReceiveSetupMessage(
std::promise<MSVCPExpected<SimpleRemoteEPCExecutorInfo>> &ExecInfoP) {
PendingCallWrapperResults[0] =
[&](shared::WrapperFunctionResult SetupMsgBytes) {
if (const char *ErrMsg = SetupMsgBytes.getOutOfBandError()) {
ExecInfoP.set_value(
make_error<StringError>(ErrMsg, inconvertibleErrorCode()));
return;
}
using SPSSerialize =
shared::SPSArgList<shared::SPSSimpleRemoteEPCExecutorInfo>;
shared::SPSInputBuffer IB(SetupMsgBytes.data(), SetupMsgBytes.size());
SimpleRemoteEPCExecutorInfo EI;
if (SPSSerialize::deserialize(IB, EI))
ExecInfoP.set_value(EI);
else
ExecInfoP.set_value(make_error<StringError>(
"Could not deserialize setup message", inconvertibleErrorCode()));
};
}
Error SimpleRemoteEPC::setup(std::unique_ptr<SimpleRemoteEPCTransport> T,
SimpleRemoteEPCExecutorInfo EI) {
using namespace SimpleRemoteEPCDefaultBootstrapSymbolNames;
LLVM_DEBUG({
dbgs() << "SimpleRemoteEPC received setup message:\n"
<< " Triple: " << EI.TargetTriple << "\n"
<< " Page size: " << EI.PageSize << "\n"
<< " Bootstrap symbols:\n";
for (const auto &KV : EI.BootstrapSymbols)
dbgs() << " " << KV.first() << ": "
<< formatv("{0:x16}", KV.second.getValue()) << "\n";
});
this->T = std::move(T);
TargetTriple = Triple(EI.TargetTriple);
PageSize = EI.PageSize;
BootstrapSymbols = std::move(EI.BootstrapSymbols);
if (auto Err = getBootstrapSymbols(
{{JDI.JITDispatchContextAddress, ExecutorSessionObjectName},
{JDI.JITDispatchFunctionAddress, DispatchFnName},
{LoadDylibAddr, "__llvm_orc_load_dylib"},
{LookupSymbolsAddr, "__llvm_orc_lookup_symbols"},
{RunAsMainAddr, rt::RunAsMainWrapperName}}))
return Err;
if (auto MemMgr = createMemoryManager()) {
OwnedMemMgr = std::move(*MemMgr);
this->MemMgr = OwnedMemMgr.get();
} else
return MemMgr.takeError();
if (auto MemAccess = createMemoryAccess()) {
OwnedMemAccess = std::move(*MemAccess);
this->MemAccess = OwnedMemAccess.get();
} else
return MemAccess.takeError();
return Error::success();
}
Error SimpleRemoteEPC::handleResult(uint64_t SeqNo, ExecutorAddress TagAddr,
SimpleRemoteEPCArgBytesVector ArgBytes) {
SendResultFunction SendResult;
if (TagAddr)
return make_error<StringError>("Unexpected TagAddr in result message",
inconvertibleErrorCode());
{
std::lock_guard<std::mutex> Lock(SimpleRemoteEPCMutex);
auto I = PendingCallWrapperResults.find(SeqNo);
if (I == PendingCallWrapperResults.end())
return make_error<StringError>("No call for sequence number " +
Twine(SeqNo),
inconvertibleErrorCode());
SendResult = std::move(I->second);
PendingCallWrapperResults.erase(I);
releaseSeqNo(SeqNo);
}
auto WFR =
shared::WrapperFunctionResult::copyFrom(ArgBytes.data(), ArgBytes.size());
SendResult(std::move(WFR));
return Error::success();
}
void SimpleRemoteEPC::handleCallWrapper(
uint64_t RemoteSeqNo, ExecutorAddress TagAddr,
SimpleRemoteEPCArgBytesVector ArgBytes) {
assert(ES && "No ExecutionSession attached");
ES->runJITDispatchHandler(
[this, RemoteSeqNo](shared::WrapperFunctionResult WFR) {
if (auto Err =
T->sendMessage(SimpleRemoteEPCOpcode::Result, RemoteSeqNo,
ExecutorAddress(), {WFR.data(), WFR.size()}))
getExecutionSession().reportError(std::move(Err));
},
TagAddr.getValue(), ArgBytes);
}
} // end namespace orc
} // end namespace llvm