[llvm] dad60f8 - [ORC] Add EPCGenericJITLinkMemoryManager: memory management via EPC calls.
Lang Hames via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 2 15:29:19 PDT 2021
Author: Lang Hames
Date: 2021-09-03T08:28:29+10:00
New Revision: dad60f8071d56c4c8244910d3d5af3b895b7a4ca
URL: https://github.com/llvm/llvm-project/commit/dad60f8071d56c4c8244910d3d5af3b895b7a4ca
DIFF: https://github.com/llvm/llvm-project/commit/dad60f8071d56c4c8244910d3d5af3b895b7a4ca.diff
LOG: [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.
Added:
llvm/include/llvm/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.h
llvm/lib/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.cpp
llvm/unittests/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManagerTest.cpp
Modified:
llvm/include/llvm/ExecutionEngine/Orc/OrcRPCExecutorProcessControl.h
llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h
llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/OrcRPCTPCServer.h
llvm/lib/ExecutionEngine/Orc/CMakeLists.txt
llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
Removed:
################################################################################
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.h b/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.h
new file mode 100644
index 0000000000000..6ad2351fb449c
--- /dev/null
+++ b/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.h
@@ -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
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/OrcRPCExecutorProcessControl.h b/llvm/include/llvm/ExecutionEngine/Orc/OrcRPCExecutorProcessControl.h
index eac64a2cfaa20..c701d9e4d1ca0 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/OrcRPCExecutorProcessControl.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/OrcRPCExecutorProcessControl.h
@@ -80,7 +80,7 @@ class OrcRPCEPCJITLinkMemoryManager : public jitlink::JITLinkMemoryManager {
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 @@ class OrcRPCEPCJITLinkMemoryManager : public jitlink::JITLinkMemoryManager {
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 @@ class OrcRPCEPCJITLinkMemoryManager : public jitlink::JITLinkMemoryManager {
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 @@ class OrcRPCEPCJITLinkMemoryManager : public jitlink::JITLinkMemoryManager {
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 @@ class OrcRPCEPCJITLinkMemoryManager : public jitlink::JITLinkMemoryManager {
TargetAllocMap TargetAllocs;
for (auto &E : *TmpTargetAllocs)
- TargetAllocs[orcrpctpc::fromWireProtectionFlags(E.Prot)] = {
+ TargetAllocs[tpctypes::fromWireProtectionFlags(E.Prot)] = {
E.Address, E.AllocatedSize};
DEBUG_WITH_TYPE("orc", {
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h
index d05a1a158db70..815dd73ff2877 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h
@@ -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 @@ class SPSSerializationTraits<SPSMemoryAccessBufferWrite,
}
};
+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
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/OrcRPCTPCServer.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/OrcRPCTPCServer.h
index 0b11371905aff..df35eb2458206 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/OrcRPCTPCServer.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/OrcRPCTPCServer.h
@@ -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 @@ template <typename RPCEndpointT> class OrcRPCTPCServer {
*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 @@ template <typename RPCEndpointT> class OrcRPCTPCServer {
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: " +
diff --git a/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt b/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt
index 59f6946b596c0..982a93cc60c0b 100644
--- a/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt
+++ b/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt
@@ -7,6 +7,7 @@ add_llvm_component_library(LLVMOrcJIT
EPCDynamicLibrarySearchGenerator.cpp
EPCDebugObjectRegistrar.cpp
EPCEHFrameRegistrar.cpp
+ EPCGenericJITLinkMemoryManager.cpp
EPCGenericMemoryAccess.cpp
EPCIndirectionUtils.cpp
ExecutionUtils.cpp
diff --git a/llvm/lib/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.cpp b/llvm/lib/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.cpp
new file mode 100644
index 0000000000000..2925660207b39
--- /dev/null
+++ b/llvm/lib/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManager.cpp
@@ -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
diff --git a/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt b/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
index 79c16cbe0597e..25a0e1f7d81af 100644
--- a/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
+++ b/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
@@ -17,6 +17,7 @@ set(LLVM_LINK_COMPONENTS
add_llvm_unittest(OrcJITTests
CoreAPIsTest.cpp
ExecutionSessionWrapperFunctionCallsTest.cpp
+ EPCGenericJITLinkMemoryManagerTest.cpp
EPCGenericMemoryAccessTest.cpp
IndirectionUtilsTest.cpp
JITTargetMachineBuilderTest.cpp
diff --git a/llvm/unittests/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManagerTest.cpp b/llvm/unittests/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManagerTest.cpp
new file mode 100644
index 0000000000000..4d9b01b0c2d6d
--- /dev/null
+++ b/llvm/unittests/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManagerTest.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
More information about the llvm-commits
mailing list