[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.
This commit is contained in:
Lang Hames 2021-09-14 11:47:08 +10:00
parent fe950cba8f
commit 78b083dbb7
17 changed files with 730 additions and 149 deletions

View File

@ -27,7 +27,8 @@ namespace orc {
class EPCGenericJITLinkMemoryManager : public jitlink::JITLinkMemoryManager {
public:
/// Function addresses for memory access.
struct FuncAddrs {
struct SymbolAddrs {
ExecutorAddress Allocator;
ExecutorAddress Reserve;
ExecutorAddress Finalize;
ExecutorAddress Deallocate;
@ -35,8 +36,8 @@ public:
/// Create an EPCGenericJITLinkMemoryManager instance from a given set of
/// function addrs.
EPCGenericJITLinkMemoryManager(ExecutorProcessControl &EPC, FuncAddrs FAs)
: EPC(EPC), FAs(FAs) {}
EPCGenericJITLinkMemoryManager(ExecutorProcessControl &EPC, SymbolAddrs SAs)
: EPC(EPC), SAs(SAs) {}
Expected<std::unique_ptr<Allocation>>
allocate(const jitlink::JITLinkDylib *JD,
@ -46,7 +47,7 @@ private:
class Alloc;
ExecutorProcessControl &EPC;
FuncAddrs FAs;
SymbolAddrs SAs;
};
} // end namespace orc

View File

@ -21,9 +21,11 @@ namespace llvm {
namespace orc {
namespace rt {
extern const char *MemoryReserveWrapperName;
extern const char *MemoryFinalizeWrapperName;
extern const char *MemoryDeallocateWrapperName;
extern const char *SimpleExecutorMemoryManagerInstanceName;
extern const char *SimpleExecutorMemoryManagerReserveWrapperName;
extern const char *SimpleExecutorMemoryManagerFinalizeWrapperName;
extern const char *SimpleExecutorMemoryManagerDeallocateWrapperName;
extern const char *MemoryWriteUInt8sWrapperName;
extern const char *MemoryWriteUInt16sWrapperName;
extern const char *MemoryWriteUInt32sWrapperName;
@ -31,11 +33,15 @@ extern const char *MemoryWriteUInt64sWrapperName;
extern const char *MemoryWriteBuffersWrapperName;
extern const char *RunAsMainWrapperName;
using SPSMemoryReserveSignature =
shared::SPSExpected<shared::SPSExecutorAddress>(uint64_t);
using SPSMemoryFinalizeSignature = shared::SPSError(shared::SPSFinalizeRequest);
using SPSMemoryDeallocateSignature =
shared::SPSError(shared::SPSExecutorAddress, uint64_t);
using SPSSimpleExecutorMemoryManagerReserveSignature =
shared::SPSExpected<shared::SPSExecutorAddress>(shared::SPSExecutorAddress,
uint64_t);
using SPSSimpleExecutorMemoryManagerFinalizeSignature =
shared::SPSError(shared::SPSExecutorAddress, shared::SPSFinalizeRequest);
using SPSSimpleExecutorMemoryManagerDeallocateSignature =
shared::SPSError(shared::SPSExecutorAddress,
shared::SPSSequence<shared::SPSExecutorAddress>);
using SPSRunAsMainSignature = int64_t(shared::SPSExecutorAddress,
shared::SPSSequence<shared::SPSString>);

View File

@ -19,6 +19,7 @@
#include "llvm/ExecutionEngine/JITSymbol.h"
#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
#include "llvm/ExecutionEngine/Orc/Shared/SimplePackedSerialization.h"
#include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h"
#include "llvm/Support/Memory.h"
#include <vector>
@ -68,6 +69,31 @@ inline std::string getWireProtectionFlagsStr(WireProtectionFlags WPF) {
return Result;
}
struct SupportFunctionCall {
using FnTy = shared::detail::CWrapperFunctionResult(const char *ArgData,
size_t ArgSize);
ExecutorAddress Func;
ExecutorAddress ArgData;
uint64_t ArgSize;
Error run() {
shared::WrapperFunctionResult WFR(
Func.toPtr<FnTy *>()(ArgData.toPtr<const char *>(), ArgSize));
if (const char *ErrMsg = WFR.getOutOfBandError())
return make_error<StringError>(ErrMsg, inconvertibleErrorCode());
if (!WFR.empty())
return make_error<StringError>("Unexpected result bytes from "
"support function call",
inconvertibleErrorCode());
return Error::success();
}
};
struct AllocationActionsPair {
SupportFunctionCall Finalize;
SupportFunctionCall Deallocate;
};
struct SegFinalizeRequest {
WireProtectionFlags Prot;
ExecutorAddress Addr;
@ -75,7 +101,10 @@ struct SegFinalizeRequest {
ArrayRef<char> Content;
};
using FinalizeRequest = std::vector<SegFinalizeRequest>;
struct FinalizeRequest {
std::vector<SegFinalizeRequest> Segments;
std::vector<AllocationActionsPair> Actions;
};
template <typename T> struct UIntWrite {
UIntWrite() = default;
@ -120,11 +149,18 @@ namespace shared {
class SPSMemoryProtectionFlags {};
using SPSSupportFunctionCall =
SPSTuple<SPSExecutorAddress, SPSExecutorAddress, uint64_t>;
using SPSSegFinalizeRequest =
SPSTuple<SPSMemoryProtectionFlags, SPSExecutorAddress, uint64_t,
SPSSequence<char>>;
using SPSFinalizeRequest = SPSSequence<SPSSegFinalizeRequest>;
using SPSAllocationActionsPair =
SPSTuple<SPSSupportFunctionCall, SPSSupportFunctionCall>;
using SPSFinalizeRequest = SPSTuple<SPSSequence<SPSSegFinalizeRequest>,
SPSSequence<SPSAllocationActionsPair>>;
template <typename T>
using SPSMemoryAccessUIntWrite = SPSTuple<SPSExecutorAddress, T>;
@ -160,6 +196,48 @@ public:
}
};
template <>
class SPSSerializationTraits<SPSSupportFunctionCall,
tpctypes::SupportFunctionCall> {
using AL = SPSSupportFunctionCall::AsArgList;
public:
static size_t size(const tpctypes::SupportFunctionCall &SFC) {
return AL::size(SFC.Func, SFC.ArgData, SFC.ArgSize);
}
static bool serialize(SPSOutputBuffer &OB,
const tpctypes::SupportFunctionCall &SFC) {
return AL::serialize(OB, SFC.Func, SFC.ArgData, SFC.ArgSize);
}
static bool deserialize(SPSInputBuffer &IB,
tpctypes::SupportFunctionCall &SFC) {
return AL::deserialize(IB, SFC.Func, SFC.ArgData, SFC.ArgSize);
}
};
template <>
class SPSSerializationTraits<SPSAllocationActionsPair,
tpctypes::AllocationActionsPair> {
using AL = SPSAllocationActionsPair::AsArgList;
public:
static size_t size(const tpctypes::AllocationActionsPair &AAP) {
return AL::size(AAP.Finalize, AAP.Deallocate);
}
static bool serialize(SPSOutputBuffer &OB,
const tpctypes::AllocationActionsPair &AAP) {
return AL::serialize(OB, AAP.Finalize, AAP.Deallocate);
}
static bool deserialize(SPSInputBuffer &IB,
tpctypes::AllocationActionsPair &AAP) {
return AL::deserialize(IB, AAP.Finalize, AAP.Deallocate);
}
};
template <>
class SPSSerializationTraits<SPSSegFinalizeRequest,
tpctypes::SegFinalizeRequest> {
@ -181,6 +259,25 @@ public:
}
};
template <>
class SPSSerializationTraits<SPSFinalizeRequest, tpctypes::FinalizeRequest> {
using FRAL = SPSFinalizeRequest::AsArgList;
public:
static size_t size(const tpctypes::FinalizeRequest &FR) {
return FRAL::size(FR.Segments, FR.Actions);
}
static bool serialize(SPSOutputBuffer &OB,
const tpctypes::FinalizeRequest &FR) {
return FRAL::serialize(OB, FR.Segments, FR.Actions);
}
static bool deserialize(SPSInputBuffer &IB, tpctypes::FinalizeRequest &FR) {
return FRAL::deserialize(IB, FR.Segments, FR.Actions);
}
};
template <typename T>
class SPSSerializationTraits<SPSMemoryAccessUIntWrite<T>,
tpctypes::UIntWrite<T>> {

View File

@ -0,0 +1,36 @@
//===- ExecutorService.h - Provide bootstrap symbols to session -*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Provides a service by supplying some set of bootstrap symbols.
//
// FIXME: The functionality in this file should be moved to the ORC runtime.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_EXECUTORBOOTSTRAPSERVICE_H
#define LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_EXECUTORBOOTSTRAPSERVICE_H
#include "llvm/ADT/StringMap.h"
#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
namespace llvm {
namespace orc {
class ExecutorBootstrapService {
public:
virtual ~ExecutorBootstrapService();
virtual void
addBootstrapSymbols(StringMap<ExecutorAddress> &BootstrapSymbols) = 0;
virtual Error shutdown() = 0;
};
} // end namespace orc
} // end namespace llvm
#endif // LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_EXECUTORBOOTSTRAPSERVICE_H

View File

@ -0,0 +1,70 @@
//===---------------- SimpleExecutorMemoryManager.h -------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// A simple allocator class suitable for basic remote-JIT use.
//
// FIXME: The functionality in this file should be moved to the ORC runtime.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_SIMPLEEXECUTORMEMORYMANAGER_H
#define LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_SIMPLEEXECUTORMEMORYMANAGER_H
#include "llvm/ADT/DenseMap.h"
#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h"
#include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/ExecutorBootstrapService.h"
#include "llvm/Support/Error.h"
#include <mutex>
namespace llvm {
namespace orc {
namespace rt_bootstrap {
/// Simple page-based allocator.
class SimpleExecutorMemoryManager : public ExecutorBootstrapService {
public:
virtual ~SimpleExecutorMemoryManager();
Expected<ExecutorAddress> allocate(uint64_t Size);
Error finalize(tpctypes::FinalizeRequest &FR);
Error deallocate(const std::vector<ExecutorAddress> &Bases);
Error shutdown() override;
void addBootstrapSymbols(StringMap<ExecutorAddress> &M) override;
private:
struct Allocation {
size_t Size = 0;
std::vector<tpctypes::SupportFunctionCall> DeallocationActions;
};
using AllocationsMap = DenseMap<void *, Allocation>;
Error deallocateImpl(void *Base, Allocation &A);
static llvm::orc::shared::detail::CWrapperFunctionResult
reserveWrapper(const char *ArgData, size_t ArgSize);
static llvm::orc::shared::detail::CWrapperFunctionResult
finalizeWrapper(const char *ArgData, size_t ArgSize);
static llvm::orc::shared::detail::CWrapperFunctionResult
deallocateWrapper(const char *ArgData, size_t ArgSize);
std::mutex M;
AllocationsMap Allocations;
};
} // end namespace rt_bootstrap
} // end namespace orc
} // end namespace llvm
#endif // LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_SIMPLEEXECUTORMEMORYMANAGER_H

View File

@ -19,6 +19,7 @@
#include "llvm/ExecutionEngine/Orc/Shared/SimpleRemoteEPCUtils.h"
#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h"
#include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/ExecutorBootstrapService.h"
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/Error.h"
@ -35,6 +36,7 @@ class SimpleRemoteEPCServer : public SimpleRemoteEPCTransportClient {
public:
using ReportErrorFunction = unique_function<void(Error)>;
/// Dispatches calls to runWrapper.
class Dispatcher {
public:
virtual ~Dispatcher();
@ -56,26 +58,60 @@ public:
};
#endif
class Setup {
friend class SimpleRemoteEPCServer;
public:
SimpleRemoteEPCServer &server();
StringMap<ExecutorAddress> &bootstrapSymbols() { return BootstrapSymbols; }
std::vector<std::unique_ptr<ExecutorBootstrapService>> &services() {
return Services;
}
void setDispatcher(std::unique_ptr<Dispatcher> D) { S.D = std::move(D); }
void setErrorReporter(unique_function<void(Error)> ReportError) {
S.ReportError = std::move(ReportError);
}
private:
Setup(SimpleRemoteEPCServer &S) : S(S) {}
SimpleRemoteEPCServer &S;
StringMap<ExecutorAddress> BootstrapSymbols;
std::vector<std::unique_ptr<ExecutorBootstrapService>> Services;
};
static StringMap<ExecutorAddress> defaultBootstrapSymbols();
template <typename TransportT, typename... TransportTCtorArgTs>
static Expected<std::unique_ptr<SimpleRemoteEPCServer>>
Create(std::unique_ptr<Dispatcher> D,
StringMap<ExecutorAddress> BootstrapSymbols,
Create(unique_function<Error(Setup &S)> SetupFunction,
TransportTCtorArgTs &&...TransportTCtorArgs) {
auto SREPCServer = std::make_unique<SimpleRemoteEPCServer>();
SREPCServer->D = std::move(D);
SREPCServer->ReportError = [](Error Err) {
logAllUnhandledErrors(std::move(Err), errs(), "SimpleRemoteEPCServer ");
};
auto Server = std::make_unique<SimpleRemoteEPCServer>();
Setup S(*Server);
if (auto Err = SetupFunction(S))
return std::move(Err);
// Set ReportError up-front so that it can be used if construction
// process fails.
if (!Server->ReportError)
Server->ReportError = [](Error Err) {
logAllUnhandledErrors(std::move(Err), errs(), "SimpleRemoteEPCServer ");
};
// Attempt to create transport.
auto T = TransportT::Create(
*SREPCServer, std::forward<TransportTCtorArgTs>(TransportTCtorArgs)...);
*Server, std::forward<TransportTCtorArgTs>(TransportTCtorArgs)...);
if (!T)
return T.takeError();
SREPCServer->T = std::move(*T);
if (auto Err = SREPCServer->sendSetupMessage(std::move(BootstrapSymbols)))
Server->T = std::move(*T);
// If transport creation succeeds then start up services.
Server->Services = std::move(S.services());
for (auto &Service : Server->Services)
Service->addBootstrapSymbols(S.bootstrapSymbols());
if (auto Err = Server->sendSetupMessage(std::move(S.BootstrapSymbols)))
return std::move(Err);
return std::move(SREPCServer);
return std::move(Server);
}
/// Set an error reporter for this server.
@ -136,6 +172,7 @@ private:
Error ShutdownErr = Error::success();
std::unique_ptr<SimpleRemoteEPCTransport> T;
std::unique_ptr<Dispatcher> D;
std::vector<std::unique_ptr<ExecutorBootstrapService>> Services;
ReportErrorFunction ReportError;
uint64_t NextSeqNo = 0;

View File

@ -27,9 +27,8 @@ public:
using SegInfoMap = DenseMap<unsigned, SegInfo>;
Alloc(EPCGenericJITLinkMemoryManager &Parent, ExecutorAddress TargetAddr,
uint64_t TargetSize, std::unique_ptr<char[]> WorkingBuffer,
SegInfoMap Segs)
: Parent(Parent), TargetAddr(TargetAddr), TargetSize(TargetSize),
std::unique_ptr<char[]> WorkingBuffer, SegInfoMap Segs)
: Parent(Parent), TargetAddr(TargetAddr),
WorkingBuffer(std::move(WorkingBuffer)), Segs(std::move(Segs)) {}
MutableArrayRef<char> getWorkingMemory(ProtectionFlags Seg) override {
@ -50,7 +49,7 @@ public:
tpctypes::FinalizeRequest FR;
for (auto &KV : Segs) {
assert(KV.second.ContentSize <= std::numeric_limits<size_t>::max());
FR.push_back(tpctypes::SegFinalizeRequest{
FR.Segments.push_back(tpctypes::SegFinalizeRequest{
tpctypes::toWireProtectionFlags(
static_cast<sys::Memory::ProtectionFlags>(KV.first)),
KV.second.TargetAddr,
@ -59,7 +58,8 @@ public:
{WorkingMem, static_cast<size_t>(KV.second.ContentSize)}});
WorkingMem += KV.second.ContentSize;
}
Parent.EPC.callSPSWrapperAsync<rt::SPSMemoryFinalizeSignature>(
Parent.EPC.callSPSWrapperAsync<
rt::SPSSimpleExecutorMemoryManagerFinalizeSignature>(
[OnFinalize = std::move(OnFinalize)](Error SerializationErr,
Error FinalizeErr) {
if (SerializationErr)
@ -67,13 +67,15 @@ public:
else
OnFinalize(std::move(FinalizeErr));
},
Parent.FAs.Finalize.getValue(), std::move(FR));
Parent.SAs.Finalize.getValue(), Parent.SAs.Allocator, std::move(FR));
}
Error deallocate() override {
Error Err = Error::success();
if (auto E2 = Parent.EPC.callSPSWrapper<rt::SPSMemoryDeallocateSignature>(
Parent.FAs.Deallocate.getValue(), Err, TargetAddr, TargetSize))
if (auto E2 = Parent.EPC.callSPSWrapper<
rt::SPSSimpleExecutorMemoryManagerDeallocateSignature>(
Parent.SAs.Deallocate.getValue(), Err, Parent.SAs.Allocator,
ArrayRef<ExecutorAddress>(TargetAddr)))
return E2;
return Err;
}
@ -81,7 +83,6 @@ public:
private:
EPCGenericJITLinkMemoryManager &Parent;
ExecutorAddress TargetAddr;
uint64_t TargetSize;
std::unique_ptr<char[]> WorkingBuffer;
SegInfoMap Segs;
};
@ -111,8 +112,9 @@ EPCGenericJITLinkMemoryManager::allocate(const jitlink::JITLinkDylib *JD,
if (WorkingSize > 0)
WorkingBuffer = std::make_unique<char[]>(WorkingSize);
Expected<ExecutorAddress> TargetAllocAddr((ExecutorAddress()));
if (auto Err = EPC.callSPSWrapper<rt::SPSMemoryReserveSignature>(
FAs.Reserve.getValue(), TargetAllocAddr, AllocSize))
if (auto Err = EPC.callSPSWrapper<
rt::SPSSimpleExecutorMemoryManagerReserveSignature>(
SAs.Reserve.getValue(), TargetAllocAddr, SAs.Allocator, AllocSize))
return std::move(Err);
if (!TargetAllocAddr)
return TargetAllocAddr.takeError();
@ -127,7 +129,7 @@ EPCGenericJITLinkMemoryManager::allocate(const jitlink::JITLinkDylib *JD,
WorkingMem += Seg.ContentSize;
}
return std::make_unique<Alloc>(*this, *TargetAllocAddr, AllocSize,
return std::make_unique<Alloc>(*this, *TargetAllocAddr,
std::move(WorkingBuffer), std::move(Segs));
}

View File

@ -12,12 +12,14 @@ namespace llvm {
namespace orc {
namespace rt {
const char *MemoryReserveWrapperName =
"__llvm_orc_bootstrap_mem_reserve_wrapper";
const char *MemoryFinalizeWrapperName =
"__llvm_orc_bootstrap_mem_finalize_wrapper";
const char *MemoryDeallocateWrapperName =
"__llvm_orc_bootstrap_mem_deallocate_wrapper";
const char *SimpleExecutorMemoryManagerInstanceName =
"__llvm_orc_SimpleExecutorMemoryManager_Instance";
const char *SimpleExecutorMemoryManagerReserveWrapperName =
"__llvm_orc_SimpleExecutorMemoryManager_reserve_wrapper";
const char *SimpleExecutorMemoryManagerFinalizeWrapperName =
"__llvm_orc_SimpleExecutorMemoryManager_finalize_wrapper";
const char *SimpleExecutorMemoryManagerDeallocateWrapperName =
"__llvm_orc_SimpleExecutorMemoryManager_deallocate_wrapper";
const char *MemoryWriteUInt8sWrapperName =
"__llvm_orc_bootstrap_mem_write_uint8s_wrapper";
const char *MemoryWriteUInt16sWrapperName =

View File

@ -170,14 +170,16 @@ void SimpleRemoteEPC::handleDisconnect(Error Err) {
Expected<std::unique_ptr<jitlink::JITLinkMemoryManager>>
SimpleRemoteEPC::createMemoryManager() {
EPCGenericJITLinkMemoryManager::FuncAddrs FAs;
EPCGenericJITLinkMemoryManager::SymbolAddrs SAs;
if (auto Err = getBootstrapSymbols(
{{FAs.Reserve, rt::MemoryReserveWrapperName},
{FAs.Finalize, rt::MemoryFinalizeWrapperName},
{FAs.Deallocate, rt::MemoryDeallocateWrapperName}}))
{{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, FAs);
return std::make_unique<EPCGenericJITLinkMemoryManager>(*this, SAs);
}
Expected<std::unique_ptr<ExecutorProcessControl::MemoryAccess>>

View File

@ -2,6 +2,7 @@ add_llvm_component_library(LLVMOrcTargetProcess
JITLoaderGDB.cpp
OrcRTBootstrap.cpp
RegisterEHFrames.cpp
SimpleExecutorMemoryManager.cpp
SimpleRemoteEPCServer.cpp
TargetExecutionUtils.cpp

View File

@ -20,57 +20,6 @@ namespace llvm {
namespace orc {
namespace rt_bootstrap {
static llvm::orc::shared::detail::CWrapperFunctionResult
reserveWrapper(const char *ArgData, size_t ArgSize) {
return WrapperFunction<rt::SPSMemoryReserveSignature>::handle(
ArgData, ArgSize,
[](uint64_t Size) -> Expected<ExecutorAddress> {
std::error_code EC;
auto MB = sys::Memory::allocateMappedMemory(
Size, 0, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC);
if (EC)
return errorCodeToError(EC);
return ExecutorAddress::fromPtr(MB.base());
})
.release();
}
static llvm::orc::shared::detail::CWrapperFunctionResult
finalizeWrapper(const char *ArgData, size_t ArgSize) {
return WrapperFunction<rt::SPSMemoryFinalizeSignature>::handle(
ArgData, ArgSize,
[](const tpctypes::FinalizeRequest &FR) -> Error {
for (auto &Seg : FR) {
char *Mem = Seg.Addr.toPtr<char *>();
memcpy(Mem, Seg.Content.data(), Seg.Content.size());
memset(Mem + Seg.Content.size(), 0,
Seg.Size - Seg.Content.size());
assert(Seg.Size <= std::numeric_limits<size_t>::max());
if (auto EC = sys::Memory::protectMappedMemory(
{Mem, static_cast<size_t>(Seg.Size)},
tpctypes::fromWireProtectionFlags(Seg.Prot)))
return errorCodeToError(EC);
if (Seg.Prot & tpctypes::WPF_Exec)
sys::Memory::InvalidateInstructionCache(Mem, Seg.Size);
}
return Error::success();
})
.release();
}
static llvm::orc::shared::detail::CWrapperFunctionResult
deallocateWrapper(const char *ArgData, size_t ArgSize) {
return WrapperFunction<rt::SPSMemoryDeallocateSignature>::handle(
ArgData, ArgSize,
[](ExecutorAddress Base, uint64_t Size) -> Error {
sys::MemoryBlock MB(Base.toPtr<void *>(), Size);
if (auto EC = sys::Memory::releaseMappedMemory(MB))
return errorCodeToError(EC);
return Error::success();
})
.release();
}
template <typename WriteT, typename SPSWriteT>
static llvm::orc::shared::detail::CWrapperFunctionResult
writeUIntsWrapper(const char *ArgData, size_t ArgSize) {
@ -108,10 +57,6 @@ runAsMainWrapper(const char *ArgData, size_t ArgSize) {
}
void addTo(StringMap<ExecutorAddress> &M) {
M[rt::MemoryReserveWrapperName] = ExecutorAddress::fromPtr(&reserveWrapper);
M[rt::MemoryFinalizeWrapperName] = ExecutorAddress::fromPtr(&finalizeWrapper);
M[rt::MemoryDeallocateWrapperName] =
ExecutorAddress::fromPtr(&deallocateWrapper);
M[rt::MemoryWriteUInt8sWrapperName] = ExecutorAddress::fromPtr(
&writeUIntsWrapper<tpctypes::UInt8Write,
shared::SPSMemoryAccessUInt8Write>);

View File

@ -0,0 +1,251 @@
//===------- ResourceManager.cpp - Abstract executor resource mgmt --------===//
//
// 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/TargetProcess/SimpleExecutorMemoryManager.h"
#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
#include "llvm/Support/FormatVariadic.h"
#define DEBUG_TYPE "orc"
namespace llvm {
namespace orc {
namespace rt_bootstrap {
SimpleExecutorMemoryManager::~SimpleExecutorMemoryManager() {
assert(Allocations.empty() && "shutdown not called?");
}
Expected<ExecutorAddress> SimpleExecutorMemoryManager::allocate(uint64_t Size) {
std::error_code EC;
auto MB = sys::Memory::allocateMappedMemory(
Size, 0, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC);
if (EC)
return errorCodeToError(EC);
std::lock_guard<std::mutex> Lock(M);
assert(!Allocations.count(MB.base()) && "Duplicate allocation addr");
Allocations[MB.base()].Size = Size;
return ExecutorAddress::fromPtr(MB.base());
}
Error SimpleExecutorMemoryManager::finalize(tpctypes::FinalizeRequest &FR) {
ExecutorAddress Base(~0ULL);
std::vector<tpctypes::SupportFunctionCall> DeallocationActions;
size_t SuccessfulFinalizationActions = 0;
for (auto &Seg : FR.Segments)
Base = std::min(Base, Seg.Addr);
for (auto &ActPair : FR.Actions)
if (ActPair.Deallocate.Func)
DeallocationActions.push_back(ActPair.Deallocate);
// Get the Allocation for this finalization.
size_t AllocSize = 0;
{
std::lock_guard<std::mutex> Lock(M);
auto I = Allocations.find(Base.toPtr<void *>());
if (I == Allocations.end())
return make_error<StringError>("Attempt to finalize unrecognized "
"allocation " +
formatv("{0:x}", Base.getValue()),
inconvertibleErrorCode());
AllocSize = I->second.Size;
I->second.DeallocationActions = std::move(DeallocationActions);
}
ExecutorAddress AllocEnd = Base + ExecutorAddrDiff(AllocSize);
// Bail-out function: this will run deallocation actions corresponding to any
// completed finalization actions, then deallocate memory.
auto BailOut = [&](Error Err) {
std::pair<void *, Allocation> AllocToDestroy;
// Get allocation to destory.
{
std::lock_guard<std::mutex> Lock(M);
auto I = Allocations.find(Base.toPtr<void *>());
// Check for missing allocation (effective a double free).
if (I == Allocations.end())
return joinErrors(
std::move(Err),
make_error<StringError>("No allocation entry found "
"for " +
formatv("{0:x}", Base.getValue()),
inconvertibleErrorCode()));
AllocToDestroy = std::move(*I);
Allocations.erase(I);
}
// Run deallocation actions for all completed finalization actions.
while (SuccessfulFinalizationActions)
Err = joinErrors(
std::move(Err),
FR.Actions[--SuccessfulFinalizationActions].Deallocate.run());
// Deallocate memory.
sys::MemoryBlock MB(AllocToDestroy.first, AllocToDestroy.second.Size);
if (auto EC = sys::Memory::releaseMappedMemory(MB))
Err = joinErrors(std::move(Err), errorCodeToError(EC));
return Err;
};
// Copy content and apply permissions.
for (auto &Seg : FR.Segments) {
// Check segment ranges.
if (LLVM_UNLIKELY(Seg.Size < Seg.Content.size()))
return BailOut(make_error<StringError>(
formatv("Segment {0:x} content size ({1:x} bytes) "
"exceeds segment size ({2:x} bytes)",
Seg.Addr.getValue(), Seg.Content.size(), Seg.Size),
inconvertibleErrorCode()));
ExecutorAddress SegEnd = Seg.Addr + ExecutorAddrDiff(Seg.Size);
if (LLVM_UNLIKELY(Seg.Addr < Base || SegEnd > AllocEnd))
return BailOut(make_error<StringError>(
formatv("Segment {0:x} -- {1:x} crosses boundary of "
"allocation {2:x} -- {3:x}",
Seg.Addr.getValue(), SegEnd.getValue(), Base.getValue(),
AllocEnd.getValue()),
inconvertibleErrorCode()));
char *Mem = Seg.Addr.toPtr<char *>();
memcpy(Mem, Seg.Content.data(), Seg.Content.size());
memset(Mem + Seg.Content.size(), 0, Seg.Size - Seg.Content.size());
assert(Seg.Size <= std::numeric_limits<size_t>::max());
if (auto EC = sys::Memory::protectMappedMemory(
{Mem, static_cast<size_t>(Seg.Size)},
tpctypes::fromWireProtectionFlags(Seg.Prot)))
return BailOut(errorCodeToError(EC));
if (Seg.Prot & tpctypes::WPF_Exec)
sys::Memory::InvalidateInstructionCache(Mem, Seg.Size);
}
// Run finalization actions.
for (auto &ActPair : FR.Actions) {
if (auto Err = ActPair.Finalize.run())
return BailOut(std::move(Err));
++SuccessfulFinalizationActions;
}
return Error::success();
}
Error SimpleExecutorMemoryManager::deallocate(
const std::vector<ExecutorAddress> &Bases) {
std::vector<std::pair<void *, Allocation>> AllocPairs;
AllocPairs.reserve(Bases.size());
// Get allocation to destory.
Error Err = Error::success();
{
std::lock_guard<std::mutex> Lock(M);
for (auto &Base : Bases) {
auto I = Allocations.find(Base.toPtr<void *>());
// Check for missing allocation (effective a double free).
if (I != Allocations.end()) {
AllocPairs.push_back(std::move(*I));
Allocations.erase(I);
} else
Err = joinErrors(
std::move(Err),
make_error<StringError>("No allocation entry found "
"for " +
formatv("{0:x}", Base.getValue()),
inconvertibleErrorCode()));
}
}
while (!AllocPairs.empty()) {
auto &P = AllocPairs.back();
Err = joinErrors(std::move(Err), deallocateImpl(P.first, P.second));
AllocPairs.pop_back();
}
return Err;
}
Error SimpleExecutorMemoryManager::shutdown() {
AllocationsMap AM;
{
std::lock_guard<std::mutex> Lock(M);
AM = std::move(Allocations);
}
Error Err = Error::success();
for (auto &KV : AM)
Err = joinErrors(std::move(Err), deallocateImpl(KV.first, KV.second));
return Err;
}
void SimpleExecutorMemoryManager::addBootstrapSymbols(
StringMap<ExecutorAddress> &M) {
M[rt::SimpleExecutorMemoryManagerInstanceName] =
ExecutorAddress::fromPtr(this);
M[rt::SimpleExecutorMemoryManagerReserveWrapperName] =
ExecutorAddress::fromPtr(&reserveWrapper);
M[rt::SimpleExecutorMemoryManagerFinalizeWrapperName] =
ExecutorAddress::fromPtr(&finalizeWrapper);
M[rt::SimpleExecutorMemoryManagerDeallocateWrapperName] =
ExecutorAddress::fromPtr(&deallocateWrapper);
}
Error SimpleExecutorMemoryManager::deallocateImpl(void *Base, Allocation &A) {
Error Err = Error::success();
while (!A.DeallocationActions.empty()) {
Err = joinErrors(std::move(Err), A.DeallocationActions.back().run());
A.DeallocationActions.pop_back();
}
sys::MemoryBlock MB(Base, A.Size);
if (auto EC = sys::Memory::releaseMappedMemory(MB))
Err = joinErrors(std::move(Err), errorCodeToError(EC));
return Err;
}
llvm::orc::shared::detail::CWrapperFunctionResult
SimpleExecutorMemoryManager::reserveWrapper(const char *ArgData,
size_t ArgSize) {
return shared::WrapperFunction<
rt::SPSSimpleExecutorMemoryManagerReserveSignature>::
handle(ArgData, ArgSize,
shared::makeMethodWrapperHandler(
&SimpleExecutorMemoryManager::allocate))
.release();
}
llvm::orc::shared::detail::CWrapperFunctionResult
SimpleExecutorMemoryManager::finalizeWrapper(const char *ArgData,
size_t ArgSize) {
return shared::WrapperFunction<
rt::SPSSimpleExecutorMemoryManagerFinalizeSignature>::
handle(ArgData, ArgSize,
shared::makeMethodWrapperHandler(
&SimpleExecutorMemoryManager::finalize))
.release();
}
llvm::orc::shared::detail::CWrapperFunctionResult
SimpleExecutorMemoryManager::deallocateWrapper(const char *ArgData,
size_t ArgSize) {
return shared::WrapperFunction<
rt::SPSSimpleExecutorMemoryManagerDeallocateSignature>::
handle(ArgData, ArgSize,
shared::makeMethodWrapperHandler(
&SimpleExecutorMemoryManager::deallocate))
.release();
}
} // namespace rt_bootstrap
} // end namespace orc
} // end namespace llvm

View File

@ -22,6 +22,8 @@ using namespace llvm::orc::shared;
namespace llvm {
namespace orc {
ExecutorBootstrapService::~ExecutorBootstrapService() {}
SimpleRemoteEPCServer::Dispatcher::~Dispatcher() {}
#if LLVM_ENABLE_THREADS

View File

@ -14,6 +14,7 @@
#include "llvm/ExecutionEngine/Orc/Shared/FDRawByteChannel.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/SimpleExecutorMemoryManager.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/SimpleRemoteEPCServer.h"
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/Error.h"
@ -138,8 +139,16 @@ int main(int argc, char *argv[]) {
auto Server =
ExitOnErr(SimpleRemoteEPCServer::Create<FDSimpleRemoteEPCTransport>(
std::make_unique<SimpleRemoteEPCServer::ThreadDispatcher>(),
SimpleRemoteEPCServer::defaultBootstrapSymbols(), InFD, OutFD));
[](SimpleRemoteEPCServer::Setup &S) -> Error {
S.setDispatcher(
std::make_unique<SimpleRemoteEPCServer::ThreadDispatcher>());
S.bootstrapSymbols() =
SimpleRemoteEPCServer::defaultBootstrapSymbols();
S.services().push_back(
std::make_unique<rt_bootstrap::SimpleExecutorMemoryManager>());
return Error::success();
},
InFD, OutFD));
ExitOnErr(Server->waitForDisconnect());
return 0;

View File

@ -30,6 +30,7 @@ add_llvm_unittest(OrcJITTests
ResourceTrackerTest.cpp
RPCUtilsTest.cpp
RTDyldObjectLinkingLayerTest.cpp
SimpleExecutorMemoryManagerTest.cpp
SimplePackedSerializationTest.cpp
SymbolStringPoolTest.cpp
ThreadSafeModuleTest.cpp

View File

@ -9,12 +9,16 @@
#include "OrcTestCommon.h"
#include "llvm/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/Memory.h"
#include "llvm/Testing/Support/Error.h"
#include <limits>
#include <vector>
using namespace llvm;
using namespace llvm::orc;
@ -22,66 +26,95 @@ using namespace llvm::orc::shared;
namespace {
class SimpleAllocator {
public:
Expected<ExecutorAddress> reserve(uint64_t Size) {
std::error_code EC;
auto MB = sys::Memory::allocateMappedMemory(
Size, 0, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC);
if (EC)
return errorCodeToError(EC);
Blocks[MB.base()] = sys::OwningMemoryBlock(std::move(MB));
return ExecutorAddress::fromPtr(MB.base());
}
Error finalize(tpctypes::FinalizeRequest FR) {
for (auto &Seg : FR.Segments) {
char *Mem = Seg.Addr.toPtr<char *>();
memcpy(Mem, Seg.Content.data(), Seg.Content.size());
memset(Mem + Seg.Content.size(), 0, Seg.Size - Seg.Content.size());
assert(Seg.Size <= std::numeric_limits<size_t>::max());
if (auto EC = sys::Memory::protectMappedMemory(
{Mem, static_cast<size_t>(Seg.Size)},
tpctypes::fromWireProtectionFlags(Seg.Prot)))
return errorCodeToError(EC);
if (Seg.Prot & tpctypes::WPF_Exec)
sys::Memory::InvalidateInstructionCache(Mem, Seg.Size);
}
return Error::success();
}
Error deallocate(std::vector<ExecutorAddress> &Bases) {
Error Err = Error::success();
for (auto &Base : Bases) {
auto I = Blocks.find(Base.toPtr<void *>());
if (I == Blocks.end()) {
Err = joinErrors(
std::move(Err),
make_error<StringError>("No allocation for " +
formatv("{0:x}", Base.getValue()),
inconvertibleErrorCode()));
continue;
}
auto MB = std::move(I->second);
Blocks.erase(I);
auto MBToRelease = MB.getMemoryBlock();
if (auto EC = sys::Memory::releaseMappedMemory(MBToRelease))
Err = joinErrors(std::move(Err), errorCodeToError(EC));
}
return Err;
}
private:
DenseMap<void *, sys::OwningMemoryBlock> Blocks;
};
llvm::orc::shared::detail::CWrapperFunctionResult
testReserve(const char *ArgData, size_t ArgSize) {
return WrapperFunction<rt::SPSMemoryReserveSignature>::handle(
ArgData, ArgSize,
[](uint64_t Size) -> Expected<ExecutorAddress> {
std::error_code EC;
auto MB = sys::Memory::allocateMappedMemory(
Size, 0, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC);
if (EC)
return errorCodeToError(EC);
return ExecutorAddress::fromPtr(MB.base());
})
.release();
return WrapperFunction<rt::SPSSimpleExecutorMemoryManagerReserveSignature>::
handle(ArgData, ArgSize,
makeMethodWrapperHandler(&SimpleAllocator::reserve))
.release();
}
llvm::orc::shared::detail::CWrapperFunctionResult
testFinalize(const char *ArgData, size_t ArgSize) {
return WrapperFunction<rt::SPSMemoryFinalizeSignature>::handle(
ArgData, ArgSize,
[](const tpctypes::FinalizeRequest &FR) -> Error {
for (auto &Seg : FR) {
char *Mem = Seg.Addr.toPtr<char *>();
memcpy(Mem, Seg.Content.data(), Seg.Content.size());
memset(Mem + Seg.Content.size(), 0,
Seg.Size - Seg.Content.size());
assert(Seg.Size <= std::numeric_limits<size_t>::max());
if (auto EC = sys::Memory::protectMappedMemory(
{Mem, static_cast<size_t>(Seg.Size)},
tpctypes::fromWireProtectionFlags(Seg.Prot)))
return errorCodeToError(EC);
if (Seg.Prot & tpctypes::WPF_Exec)
sys::Memory::InvalidateInstructionCache(Mem, Seg.Size);
}
return Error::success();
})
.release();
return WrapperFunction<rt::SPSSimpleExecutorMemoryManagerFinalizeSignature>::
handle(ArgData, ArgSize,
makeMethodWrapperHandler(&SimpleAllocator::finalize))
.release();
}
llvm::orc::shared::detail::CWrapperFunctionResult
testDeallocate(const char *ArgData, size_t ArgSize) {
return WrapperFunction<rt::SPSMemoryDeallocateSignature>::handle(
ArgData, ArgSize,
[](ExecutorAddress Base, uint64_t Size) -> Error {
sys::MemoryBlock MB(Base.toPtr<void *>(), Size);
if (auto EC = sys::Memory::releaseMappedMemory(MB))
return errorCodeToError(EC);
return Error::success();
})
.release();
return WrapperFunction<
rt::SPSSimpleExecutorMemoryManagerDeallocateSignature>::
handle(ArgData, ArgSize,
makeMethodWrapperHandler(&SimpleAllocator::deallocate))
.release();
}
TEST(EPCGenericJITLinkMemoryManagerTest, AllocFinalizeFree) {
auto SelfEPC = cantFail(SelfExecutorProcessControl::Create());
SimpleAllocator SA;
EPCGenericJITLinkMemoryManager::FuncAddrs FAs;
FAs.Reserve = ExecutorAddress::fromPtr(&testReserve);
FAs.Finalize = ExecutorAddress::fromPtr(&testFinalize);
FAs.Deallocate = ExecutorAddress::fromPtr(&testDeallocate);
EPCGenericJITLinkMemoryManager::SymbolAddrs SAs;
SAs.Allocator = ExecutorAddress::fromPtr(&SA);
SAs.Reserve = ExecutorAddress::fromPtr(&testReserve);
SAs.Finalize = ExecutorAddress::fromPtr(&testFinalize);
SAs.Deallocate = ExecutorAddress::fromPtr(&testDeallocate);
auto MemMgr = std::make_unique<EPCGenericJITLinkMemoryManager>(*SelfEPC, FAs);
auto MemMgr = std::make_unique<EPCGenericJITLinkMemoryManager>(*SelfEPC, SAs);
jitlink::JITLinkMemoryManager::SegmentsRequestMap SRM;
StringRef Hello = "hello";

View File

@ -0,0 +1,86 @@
//===---------------- SimpleExecutorMemoryManagerTest.cpp -----------------===//
//
// 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/TargetProcess/SimpleExecutorMemoryManager.h"
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
#include <limits>
#include <vector>
using namespace llvm;
using namespace llvm::orc;
using namespace llvm::orc::shared;
using namespace llvm::orc::rt_bootstrap;
namespace {
orc::shared::detail::CWrapperFunctionResult
incrementWrapper(const char *ArgData, size_t ArgSize) {
return WrapperFunction<void(SPSExecutorAddress)>::handle(
ArgData, ArgSize,
[](ExecutorAddress A) { *A.toPtr<int *>() += 1; })
.release();
}
TEST(SimpleExecutorMemoryManagerTest, AllocFinalizeFree) {
SimpleExecutorMemoryManager MemMgr;
constexpr unsigned AllocSize = 16384;
auto Mem = MemMgr.allocate(AllocSize);
EXPECT_THAT_ERROR(Mem.takeError(), Succeeded());
std::string HW = "Hello, world!";
int FinalizeCounter = 0;
auto FinalizeCounterAddrArgBuffer =
orc::shared::detail::serializeViaSPSToWrapperFunctionResult<
SPSArgList<SPSExecutorAddress>>(
ExecutorAddress::fromPtr(&FinalizeCounter));
int DeallocateCounter = 0;
auto DeallocateCounterAddrArgBuffer =
orc::shared::detail::serializeViaSPSToWrapperFunctionResult<
SPSArgList<SPSExecutorAddress>>(
ExecutorAddress::fromPtr(&DeallocateCounter));
tpctypes::FinalizeRequest FR;
FR.Segments.push_back(
tpctypes::SegFinalizeRequest{tpctypes::WPF_Read | tpctypes::WPF_Write,
*Mem,
AllocSize,
{HW.data(), HW.size() + 1}});
FR.Actions.push_back(
{/* Finalize: */
{ExecutorAddress::fromPtr(incrementWrapper),
ExecutorAddress::fromPtr(FinalizeCounterAddrArgBuffer.data()),
FinalizeCounterAddrArgBuffer.size()},
/* Deallocate: */
{ExecutorAddress::fromPtr(incrementWrapper),
ExecutorAddress::fromPtr(DeallocateCounterAddrArgBuffer.data()),
DeallocateCounterAddrArgBuffer.size()}});
EXPECT_EQ(FinalizeCounter, 0);
EXPECT_EQ(DeallocateCounter, 0);
auto FinalizeErr = MemMgr.finalize(FR);
EXPECT_THAT_ERROR(std::move(FinalizeErr), Succeeded());
EXPECT_EQ(FinalizeCounter, 1);
EXPECT_EQ(DeallocateCounter, 0);
EXPECT_EQ(HW, std::string(Mem->toPtr<const char *>()));
auto DeallocateErr = MemMgr.deallocate({*Mem});
EXPECT_THAT_ERROR(std::move(DeallocateErr), Succeeded());
EXPECT_EQ(FinalizeCounter, 1);
EXPECT_EQ(DeallocateCounter, 1);
}
} // namespace