[ORC] Add EPCGenericJITLinkMemoryManager: memory management via EPC calls.

All ExecutorProcessControl subclasses must provide a JITLinkMemoryManager object
that can be used to allocate memory in the executor process. The
EPCGenericJITLinkMemoryManager class provides an off-the-shelf
JITLinkMemoryManager implementation for JITs that do not need (or cannot
provide) a specialized JITLinkMemoryManager implementation. This simplifies the
process of creating new ExecutorProcessControl implementations.
This commit is contained in:
Lang Hames
2021-09-02 20:48:58 +10:00
parent f38cfdabd1
commit dad60f8071
8 changed files with 439 additions and 52 deletions

View File

@@ -0,0 +1,60 @@
//===- EPCGenericJITLinkMemoryManager.h - EPC-based mem manager -*- 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
//
//===----------------------------------------------------------------------===//
//
// Implements JITLinkMemoryManager by making remove calls via
// ExecutorProcessControl::callWrapperAsync.
//
// This simplifies the implementaton of new ExecutorProcessControl instances,
// as this implementation will always work (at the cost of some performance
// overhead for the calls).
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_EXECUTIONENGINE_ORC_EPCGENERICJITLINKMEMORYMANAGER_H
#define LLVM_EXECUTIONENGINE_ORC_EPCGENERICJITLINKMEMORYMANAGER_H
#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
#include "llvm/ExecutionEngine/Orc/Core.h"
namespace llvm {
namespace orc {
class EPCGenericJITLinkMemoryManager : public jitlink::JITLinkMemoryManager {
public:
/// Function addresses for memory access.
struct FuncAddrs {
ExecutorAddress Reserve;
ExecutorAddress Finalize;
ExecutorAddress Deallocate;
};
/// Create an EPCGenericJITLinkMemoryManager instance from a given set of
/// function addrs.
EPCGenericJITLinkMemoryManager(ExecutorProcessControl &EPC, FuncAddrs FAs)
: EPC(EPC), FAs(FAs) {}
/// Create using the standard memory access function names from the ORC
/// runtime.
static Expected<std::unique_ptr<EPCGenericJITLinkMemoryManager>>
CreateUsingOrcRTFuncs(ExecutionSession &ES, JITDylib &OrcRuntimeJD);
Expected<std::unique_ptr<Allocation>>
allocate(const jitlink::JITLinkDylib *JD,
const SegmentsRequestMap &Request) override;
private:
class Alloc;
ExecutorProcessControl &EPC;
FuncAddrs FAs;
};
} // end namespace orc
} // end namespace llvm
#endif // LLVM_EXECUTIONENGINE_ORC_EPCGENERICJITLINKMEMORYMANAGER_H

View File

@@ -80,7 +80,7 @@ public:
auto &HA = KV.second;
auto &TA = TargetAllocs[KV.first];
BufferWrites.push_back({TA.Address, StringRef(HA.Mem.get(), HA.Size)});
FMR.push_back({orcrpctpc::toWireProtectionFlags(
FMR.push_back({tpctypes::toWireProtectionFlags(
static_cast<sys::Memory::ProtectionFlags>(KV.first)),
TA.Address, TA.AllocatedSize});
}
@@ -92,9 +92,7 @@ public:
auto Prot = FMRI->Prot;
++FMRI;
dbgs() << " Writing " << formatv("{0:x16}", B.Buffer.size())
<< " bytes to " << ((Prot & orcrpctpc::WPF_Read) ? 'R' : '-')
<< ((Prot & orcrpctpc::WPF_Write) ? 'W' : '-')
<< ((Prot & orcrpctpc::WPF_Exec) ? 'X' : '-')
<< " bytes to " << tpctypes::getWireProtectionFlagsStr(Prot)
<< " segment: local " << (const void *)B.Buffer.data()
<< " -> target " << formatv("{0:x16}", B.Address) << "\n";
}
@@ -132,7 +130,7 @@ public:
Error deallocate() override {
orcrpctpc::ReleaseOrFinalizeMemRequest RMR;
for (auto &KV : TargetAllocs)
RMR.push_back({orcrpctpc::toWireProtectionFlags(
RMR.push_back({tpctypes::toWireProtectionFlags(
static_cast<sys::Memory::ProtectionFlags>(KV.first)),
KV.second.Address, KV.second.AllocatedSize});
TargetAllocs.clear();
@@ -158,7 +156,7 @@ public:
assert(KV.second.getContentSize() <= std::numeric_limits<size_t>::max() &&
"Content size is out-of-range for host");
RMR.push_back({orcrpctpc::toWireProtectionFlags(
RMR.push_back({tpctypes::toWireProtectionFlags(
static_cast<sys::Memory::ProtectionFlags>(KV.first)),
KV.second.getContentSize() + KV.second.getZeroFillSize(),
KV.second.getAlignment()});
@@ -196,7 +194,7 @@ public:
TargetAllocMap TargetAllocs;
for (auto &E : *TmpTargetAllocs)
TargetAllocs[orcrpctpc::fromWireProtectionFlags(E.Prot)] = {
TargetAllocs[tpctypes::fromWireProtectionFlags(E.Prot)] = {
E.Address, E.AllocatedSize};
DEBUG_WITH_TYPE("orc", {

View File

@@ -17,7 +17,9 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ExecutionEngine/JITSymbol.h"
#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
#include "llvm/ExecutionEngine/Orc/Shared/SimplePackedSerialization.h"
#include "llvm/Support/Memory.h"
#include <vector>
@@ -25,6 +27,56 @@ namespace llvm {
namespace orc {
namespace tpctypes {
enum WireProtectionFlags : uint8_t {
WPF_None = 0,
WPF_Read = 1U << 0,
WPF_Write = 1U << 1,
WPF_Exec = 1U << 2,
LLVM_MARK_AS_BITMASK_ENUM(WPF_Exec)
};
/// Convert from sys::Memory::ProtectionFlags
inline WireProtectionFlags
toWireProtectionFlags(sys::Memory::ProtectionFlags PF) {
WireProtectionFlags WPF = WPF_None;
if (PF & sys::Memory::MF_READ)
WPF |= WPF_Read;
if (PF & sys::Memory::MF_WRITE)
WPF |= WPF_Write;
if (PF & sys::Memory::MF_EXEC)
WPF |= WPF_Exec;
return WPF;
}
inline sys::Memory::ProtectionFlags
fromWireProtectionFlags(WireProtectionFlags WPF) {
int PF = 0;
if (WPF & WPF_Read)
PF |= sys::Memory::MF_READ;
if (WPF & WPF_Write)
PF |= sys::Memory::MF_WRITE;
if (WPF & WPF_Exec)
PF |= sys::Memory::MF_EXEC;
return static_cast<sys::Memory::ProtectionFlags>(PF);
}
inline std::string getWireProtectionFlagsStr(WireProtectionFlags WPF) {
std::string Result;
Result += (WPF & WPF_Read) ? 'R' : '-';
Result += (WPF & WPF_Write) ? 'W' : '-';
Result += (WPF & WPF_Exec) ? 'X' : '-';
return Result;
}
struct SegFinalizeRequest {
WireProtectionFlags Prot;
ExecutorAddress Addr;
uint64_t Size;
ArrayRef<char> Content;
};
using FinalizeRequest = std::vector<SegFinalizeRequest>;
template <typename T> struct UIntWrite {
UIntWrite() = default;
UIntWrite(JITTargetAddress Address, T Value)
@@ -66,6 +118,14 @@ using LookupResult = std::vector<JITTargetAddress>;
namespace shared {
class SPSMemoryProtectionFlags {};
using SPSSegFinalizeRequest =
SPSTuple<SPSMemoryProtectionFlags, SPSExecutorAddress, uint64_t,
SPSSequence<char>>;
using SPSFinalizeRequest = SPSSequence<SPSSegFinalizeRequest>;
template <typename T>
using SPSMemoryAccessUIntWrite = SPSTuple<SPSExecutorAddress, T>;
@@ -77,6 +137,50 @@ using SPSMemoryAccessUInt64Write = SPSMemoryAccessUIntWrite<uint64_t>;
using SPSMemoryAccessBufferWrite =
SPSTuple<SPSExecutorAddress, SPSSequence<char>>;
template <>
class SPSSerializationTraits<SPSMemoryProtectionFlags,
tpctypes::WireProtectionFlags> {
public:
static size_t size(const tpctypes::WireProtectionFlags &WPF) {
return SPSArgList<uint8_t>::size(static_cast<uint8_t>(WPF));
}
static bool serialize(SPSOutputBuffer &OB,
const tpctypes::WireProtectionFlags &WPF) {
return SPSArgList<uint8_t>::serialize(OB, static_cast<uint8_t>(WPF));
}
static bool deserialize(SPSInputBuffer &IB,
tpctypes::WireProtectionFlags &WPF) {
uint8_t Val;
if (!SPSArgList<uint8_t>::deserialize(IB, Val))
return false;
WPF = static_cast<tpctypes::WireProtectionFlags>(Val);
return true;
}
};
template <>
class SPSSerializationTraits<SPSSegFinalizeRequest,
tpctypes::SegFinalizeRequest> {
using SFRAL = SPSSegFinalizeRequest::AsArgList;
public:
static size_t size(const tpctypes::SegFinalizeRequest &SFR) {
return SFRAL::size(SFR.Prot, SFR.Addr, SFR.Size, SFR.Content);
}
static bool serialize(SPSOutputBuffer &OB,
const tpctypes::SegFinalizeRequest &SFR) {
return SFRAL::serialize(OB, SFR.Prot, SFR.Addr, SFR.Size, SFR.Content);
}
static bool deserialize(SPSInputBuffer &IB,
tpctypes::SegFinalizeRequest &SFR) {
return SFRAL::deserialize(IB, SFR.Prot, SFR.Addr, SFR.Size, SFR.Content);
}
};
template <typename T>
class SPSSerializationTraits<SPSMemoryAccessUIntWrite<T>,
tpctypes::UIntWrite<T>> {
@@ -118,6 +222,10 @@ public:
}
};
using SPSOrcTargetProcessAllocate = SPSExpected<SPSExecutorAddress>(uint64_t);
using SPSOrcTargetProcessFinalize = SPSError(SPSFinalizeRequest);
using SPSOrcTargetProcessDeallocate = SPSError(SPSExecutorAddress, uint64_t);
} // end namespace shared
} // end namespace orc
} // end namespace llvm

View File

@@ -33,14 +33,6 @@ namespace orc {
namespace orcrpctpc {
enum WireProtectionFlags : uint8_t {
WPF_None = 0,
WPF_Read = 1U << 0,
WPF_Write = 1U << 1,
WPF_Exec = 1U << 2,
LLVM_MARK_AS_BITMASK_ENUM(WPF_Exec)
};
struct ExecutorProcessInfo {
std::string Triple;
unsigned PageSize;
@@ -48,33 +40,8 @@ struct ExecutorProcessInfo {
JITTargetAddress DispatchCtxAddr;
};
/// Convert from sys::Memory::ProtectionFlags
inline WireProtectionFlags
toWireProtectionFlags(sys::Memory::ProtectionFlags PF) {
WireProtectionFlags WPF = WPF_None;
if (PF & sys::Memory::MF_READ)
WPF |= WPF_Read;
if (PF & sys::Memory::MF_WRITE)
WPF |= WPF_Write;
if (PF & sys::Memory::MF_EXEC)
WPF |= WPF_Exec;
return WPF;
}
inline sys::Memory::ProtectionFlags
fromWireProtectionFlags(WireProtectionFlags WPF) {
int PF = 0;
if (WPF & WPF_Read)
PF |= sys::Memory::MF_READ;
if (WPF & WPF_Write)
PF |= sys::Memory::MF_WRITE;
if (WPF & WPF_Exec)
PF |= sys::Memory::MF_EXEC;
return static_cast<sys::Memory::ProtectionFlags>(PF);
}
struct ReserveMemRequestElement {
WireProtectionFlags Prot = WPF_None;
tpctypes::WireProtectionFlags Prot = tpctypes::WPF_None;
uint64_t Size = 0;
uint64_t Alignment = 0;
};
@@ -82,7 +49,7 @@ struct ReserveMemRequestElement {
using ReserveMemRequest = std::vector<ReserveMemRequestElement>;
struct ReserveMemResultElement {
WireProtectionFlags Prot = WPF_None;
tpctypes::WireProtectionFlags Prot = tpctypes::WPF_None;
JITTargetAddress Address = 0;
uint64_t AllocatedSize = 0;
};
@@ -90,7 +57,7 @@ struct ReserveMemResultElement {
using ReserveMemResult = std::vector<ReserveMemResultElement>;
struct ReleaseOrFinalizeMemRequestElement {
WireProtectionFlags Prot = WPF_None;
tpctypes::WireProtectionFlags Prot = tpctypes::WPF_None;
JITTargetAddress Address = 0;
uint64_t Size = 0;
};
@@ -492,14 +459,6 @@ private:
*jitTargetAddressToPointer<ValueT *>(W.Address) = W.Value;
}
std::string getProtStr(orcrpctpc::WireProtectionFlags WPF) {
std::string Result;
Result += (WPF & orcrpctpc::WPF_Read) ? 'R' : '-';
Result += (WPF & orcrpctpc::WPF_Write) ? 'W' : '-';
Result += (WPF & orcrpctpc::WPF_Exec) ? 'X' : '-';
return Result;
}
static void handleWriteBuffer(const std::vector<tpctypes::BufferWrite> &Ws) {
for (auto &W : Ws) {
memcpy(jitTargetAddressToPointer<char *>(W.Address), W.Buffer.data(),
@@ -554,7 +513,7 @@ private:
for (const auto &E : FMR) {
sys::MemoryBlock MB(jitTargetAddressToPointer<void *>(E.Address), E.Size);
auto PF = orcrpctpc::fromWireProtectionFlags(E.Prot);
auto PF = tpctypes::fromWireProtectionFlags(E.Prot);
if (auto EC =
sys::Memory::protectMappedMemory(MB, static_cast<unsigned>(PF)))
return make_error<StringError>("error protecting memory: " +

View File

@@ -7,6 +7,7 @@ add_llvm_component_library(LLVMOrcJIT
EPCDynamicLibrarySearchGenerator.cpp
EPCDebugObjectRegistrar.cpp
EPCEHFrameRegistrar.cpp
EPCGenericJITLinkMemoryManager.cpp
EPCGenericMemoryAccess.cpp
EPCIndirectionUtils.cpp
ExecutionUtils.cpp

View File

@@ -0,0 +1,154 @@
//===---- EPCGenericJITLinkMemoryManager.cpp -- Mem management via EPC ----===//
//
// 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/EPCGenericJITLinkMemoryManager.h"
#include "llvm/ExecutionEngine/Orc/LookupAndRecordAddrs.h"
namespace llvm {
namespace orc {
class EPCGenericJITLinkMemoryManager::Alloc
: public jitlink::JITLinkMemoryManager::Allocation {
public:
struct SegInfo {
char *WorkingMem = nullptr;
ExecutorAddress TargetAddr;
uint64_t ContentSize = 0;
uint64_t ZeroFillSize = 0;
};
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),
WorkingBuffer(std::move(WorkingBuffer)), Segs(std::move(Segs)) {}
MutableArrayRef<char> getWorkingMemory(ProtectionFlags Seg) override {
auto I = Segs.find(Seg);
assert(I != Segs.end() && "No allocation for seg");
return {I->second.WorkingMem, I->second.ContentSize};
}
JITTargetAddress getTargetMemory(ProtectionFlags Seg) override {
auto I = Segs.find(Seg);
assert(I != Segs.end() && "No allocation for seg");
return I->second.TargetAddr.getValue();
}
void finalizeAsync(FinalizeContinuation OnFinalize) override {
char *WorkingMem = WorkingBuffer.get();
tpctypes::FinalizeRequest FR;
for (auto &KV : Segs) {
FR.push_back(tpctypes::SegFinalizeRequest{
tpctypes::toWireProtectionFlags(
static_cast<sys::Memory::ProtectionFlags>(KV.first)),
KV.second.TargetAddr,
alignTo(KV.second.ContentSize + KV.second.ZeroFillSize,
Parent.EPC.getPageSize()),
{WorkingMem, KV.second.ContentSize}});
WorkingMem += KV.second.ContentSize;
}
Parent.EPC.callSPSWrapperAsync<shared::SPSOrcTargetProcessFinalize>(
[OnFinalize = std::move(OnFinalize)](Error SerializationErr,
Error FinalizeErr) {
if (SerializationErr)
OnFinalize(std::move(SerializationErr));
else
OnFinalize(std::move(FinalizeErr));
},
Parent.FAs.Finalize.getValue(), std::move(FR));
}
Error deallocate() override {
Error Err = Error::success();
if (auto E2 =
Parent.EPC.callSPSWrapper<shared::SPSOrcTargetProcessDeallocate>(
Parent.FAs.Deallocate.getValue(), Err, TargetAddr, TargetSize))
return E2;
return Err;
}
private:
EPCGenericJITLinkMemoryManager &Parent;
ExecutorAddress TargetAddr;
uint64_t TargetSize;
std::unique_ptr<char[]> WorkingBuffer;
SegInfoMap Segs;
};
/// Create from a ExecutorProcessControl instance.
Expected<std::unique_ptr<EPCGenericJITLinkMemoryManager>>
EPCGenericJITLinkMemoryManager::CreateUsingOrcRTFuncs(ExecutionSession &ES,
JITDylib &OrcRuntimeJD) {
StringRef GlobalPrefix = "";
if (ES.getExecutorProcessControl().getTargetTriple().isOSBinFormatMachO())
GlobalPrefix = "_";
FuncAddrs FAs;
if (auto Err = lookupAndRecordAddrs(
ES, LookupKind::Static, makeJITDylibSearchOrder(&OrcRuntimeJD),
{{ES.intern((GlobalPrefix + "__orc_rt_reserve").str()), &FAs.Reserve},
{ES.intern((GlobalPrefix + "__orc_rt_finalize").str()),
&FAs.Finalize},
{ES.intern((GlobalPrefix + "__orc_rt_deallocate").str()),
&FAs.Deallocate}}))
return std::move(Err);
return std::make_unique<EPCGenericJITLinkMemoryManager>(
ES.getExecutorProcessControl(), FAs);
}
Expected<std::unique_ptr<jitlink::JITLinkMemoryManager::Allocation>>
EPCGenericJITLinkMemoryManager::allocate(const jitlink::JITLinkDylib *JD,
const SegmentsRequestMap &Request) {
Alloc::SegInfoMap Segs;
uint64_t AllocSize = 0;
size_t WorkingSize = 0;
for (auto &KV : Request) {
if (!isPowerOf2_64(KV.second.getAlignment()))
return make_error<StringError>("Alignment is not a power of two",
inconvertibleErrorCode());
if (KV.second.getAlignment() > EPC.getPageSize())
return make_error<StringError>("Alignment exceeds page size",
inconvertibleErrorCode());
auto &Seg = Segs[KV.first];
Seg.ContentSize = KV.second.getContentSize();
Seg.ZeroFillSize = KV.second.getZeroFillSize();
AllocSize += alignTo(Seg.ContentSize + Seg.ZeroFillSize, EPC.getPageSize());
WorkingSize += Seg.ContentSize;
}
std::unique_ptr<char[]> WorkingBuffer;
if (WorkingSize > 0)
WorkingBuffer = std::make_unique<char[]>(WorkingSize);
Expected<ExecutorAddress> TargetAllocAddr((ExecutorAddress()));
if (auto Err = EPC.callSPSWrapper<shared::SPSOrcTargetProcessAllocate>(
FAs.Reserve.getValue(), TargetAllocAddr, AllocSize))
return std::move(Err);
if (!TargetAllocAddr)
return TargetAllocAddr.takeError();
char *WorkingMem = WorkingBuffer.get();
JITTargetAddress SegAddr = TargetAllocAddr->getValue();
for (auto &KV : Segs) {
auto &Seg = KV.second;
Seg.TargetAddr.setValue(SegAddr);
SegAddr += alignTo(Seg.ContentSize + Seg.ZeroFillSize, EPC.getPageSize());
Seg.WorkingMem = WorkingMem;
WorkingMem += Seg.ContentSize;
}
return std::make_unique<Alloc>(*this, *TargetAllocAddr, AllocSize,
std::move(WorkingBuffer), std::move(Segs));
}
} // end namespace orc
} // end namespace llvm

View File

@@ -17,6 +17,7 @@ set(LLVM_LINK_COMPONENTS
add_llvm_unittest(OrcJITTests
CoreAPIsTest.cpp
ExecutionSessionWrapperFunctionCallsTest.cpp
EPCGenericJITLinkMemoryManagerTest.cpp
EPCGenericMemoryAccessTest.cpp
IndirectionUtilsTest.cpp
JITTargetMachineBuilderTest.cpp

View File

@@ -0,0 +1,106 @@
//===-------------- EPCGenericJITLinkMemoryManagerTest.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 "OrcTestCommon.h"
#include "llvm/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.h"
#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h"
#include "llvm/Support/Memory.h"
#include "llvm/Testing/Support/Error.h"
using namespace llvm;
using namespace llvm::orc;
using namespace llvm::orc::shared;
namespace {
llvm::orc::shared::detail::CWrapperFunctionResult
testReserve(const char *ArgData, size_t ArgSize) {
return WrapperFunction<SPSOrcTargetProcessAllocate>::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();
}
llvm::orc::shared::detail::CWrapperFunctionResult
testFinalize(const char *ArgData, size_t ArgSize) {
return WrapperFunction<SPSOrcTargetProcessFinalize>::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());
if (auto EC = sys::Memory::protectMappedMemory(
{Mem, 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();
}
llvm::orc::shared::detail::CWrapperFunctionResult
testDeallocate(const char *ArgData, size_t ArgSize) {
return WrapperFunction<SPSOrcTargetProcessDeallocate>::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();
}
TEST(EPCGenericJITLinkMemoryManagerTest, AllocFinalizeFree) {
auto SelfEPC = cantFail(SelfExecutorProcessControl::Create());
EPCGenericJITLinkMemoryManager::FuncAddrs FAs;
FAs.Reserve = ExecutorAddress::fromPtr(&testReserve);
FAs.Finalize = ExecutorAddress::fromPtr(&testFinalize);
FAs.Deallocate = ExecutorAddress::fromPtr(&testDeallocate);
auto MemMgr = std::make_unique<EPCGenericJITLinkMemoryManager>(*SelfEPC, FAs);
jitlink::JITLinkMemoryManager::SegmentsRequestMap SRM;
StringRef Hello = "hello";
SRM[sys::Memory::MF_READ] = {8, Hello.size(), 8};
auto Alloc = MemMgr->allocate(nullptr, SRM);
EXPECT_THAT_EXPECTED(Alloc, Succeeded());
MutableArrayRef<char> WorkingMem =
(*Alloc)->getWorkingMemory(sys::Memory::MF_READ);
memcpy(WorkingMem.data(), Hello.data(), Hello.size());
auto Err = (*Alloc)->finalize();
EXPECT_THAT_ERROR(std::move(Err), Succeeded());
ExecutorAddress TargetAddr((*Alloc)->getTargetMemory(sys::Memory::MF_READ));
const char *TargetMem = TargetAddr.toPtr<const char *>();
EXPECT_NE(TargetMem, WorkingMem.data());
StringRef TargetHello(TargetMem, Hello.size());
EXPECT_EQ(Hello, TargetHello);
auto Err2 = (*Alloc)->deallocate();
EXPECT_THAT_ERROR(std::move(Err2), Succeeded());
}
} // namespace