[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