[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:
@@ -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
|
||||
@@ -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", {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: " +
|
||||
|
||||
@@ -7,6 +7,7 @@ add_llvm_component_library(LLVMOrcJIT
|
||||
EPCDynamicLibrarySearchGenerator.cpp
|
||||
EPCDebugObjectRegistrar.cpp
|
||||
EPCEHFrameRegistrar.cpp
|
||||
EPCGenericJITLinkMemoryManager.cpp
|
||||
EPCGenericMemoryAccess.cpp
|
||||
EPCIndirectionUtils.cpp
|
||||
ExecutionUtils.cpp
|
||||
|
||||
154
llvm/lib/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.cpp
Normal file
154
llvm/lib/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.cpp
Normal 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
|
||||
@@ -17,6 +17,7 @@ set(LLVM_LINK_COMPONENTS
|
||||
add_llvm_unittest(OrcJITTests
|
||||
CoreAPIsTest.cpp
|
||||
ExecutionSessionWrapperFunctionCallsTest.cpp
|
||||
EPCGenericJITLinkMemoryManagerTest.cpp
|
||||
EPCGenericMemoryAccessTest.cpp
|
||||
IndirectionUtilsTest.cpp
|
||||
JITTargetMachineBuilderTest.cpp
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user