[llvm] [ORC] Add read operations to orc::MemoryAccess. (PR #145834)

Lang Hames via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 26 02:15:49 PDT 2025


https://github.com/lhames updated https://github.com/llvm/llvm-project/pull/145834

>From ab81b22af899c7c5e0c0be5a3512f543f59df708 Mon Sep 17 00:00:00 2001
From: Lang Hames <lhames at gmail.com>
Date: Thu, 26 Jun 2025 11:07:11 +1000
Subject: [PATCH] [ORC] Add read operations to orc::MemoryAccess.

This commit adds operations to orc::MemoryAccess for reading basic types
(uint8_t, uint16_t, uint32_t, uint64_t, pointers, buffers, and strings) from
executor memory.

The InProcessMemoryAccess and EPCGenericMemoryAccess implementations are
updated to support the new operations.
---
 .../Orc/EPCGenericMemoryAccess.h              | 129 ++++++-
 .../Orc/InProcessMemoryAccess.h               |  25 +-
 .../llvm/ExecutionEngine/Orc/MemoryAccess.h   | 110 +++++-
 .../ExecutionEngine/Orc/Shared/OrcRTBridge.h  |  10 +-
 .../Orc/Shared/TargetProcessControlTypes.h    |   4 +-
 .../Orc/InProcessMemoryAccess.cpp             |  84 +++-
 .../Orc/Shared/OrcRTBridge.cpp                |  19 +-
 .../ExecutionEngine/Orc/SimpleRemoteEPC.cpp   |   8 +-
 .../Orc/TargetProcess/OrcRTBootstrap.cpp      | 100 ++++-
 .../Orc/EPCGenericMemoryAccessTest.cpp        | 364 ++++++++++++++----
 10 files changed, 748 insertions(+), 105 deletions(-)

diff --git a/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h b/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h
index e1815e4d303ad..c69b6f736651e 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/EPCGenericMemoryAccess.h
@@ -31,8 +31,15 @@ class EPCGenericMemoryAccess : public MemoryAccess {
     ExecutorAddr WriteUInt16s;
     ExecutorAddr WriteUInt32s;
     ExecutorAddr WriteUInt64s;
-    ExecutorAddr WriteBuffers;
     ExecutorAddr WritePointers;
+    ExecutorAddr WriteBuffers;
+    ExecutorAddr ReadUInt8s;
+    ExecutorAddr ReadUInt16s;
+    ExecutorAddr ReadUInt32s;
+    ExecutorAddr ReadUInt64s;
+    ExecutorAddr ReadPointers;
+    ExecutorAddr ReadBuffers;
+    ExecutorAddr ReadStrings;
   };
 
   /// Create an EPCGenericMemoryAccess instance from a given set of
@@ -68,6 +75,13 @@ class EPCGenericMemoryAccess : public MemoryAccess {
         FAs.WriteUInt64s, std::move(OnWriteComplete), Ws);
   }
 
+  void writePointersAsync(ArrayRef<tpctypes::PointerWrite> Ws,
+                          WriteResultFn OnWriteComplete) override {
+    using namespace shared;
+    EPC.callSPSWrapperAsync<void(SPSSequence<SPSMemoryAccessPointerWrite>)>(
+        FAs.WritePointers, std::move(OnWriteComplete), Ws);
+  }
+
   void writeBuffersAsync(ArrayRef<tpctypes::BufferWrite> Ws,
                          WriteResultFn OnWriteComplete) override {
     using namespace shared;
@@ -75,11 +89,116 @@ class EPCGenericMemoryAccess : public MemoryAccess {
         FAs.WriteBuffers, std::move(OnWriteComplete), Ws);
   }
 
-  void writePointersAsync(ArrayRef<tpctypes::PointerWrite> Ws,
-                          WriteResultFn OnWriteComplete) override {
+  void readUInt8sAsync(ArrayRef<ExecutorAddr> Rs,
+                       OnReadUIntsCompleteFn<uint8_t> OnComplete) override {
     using namespace shared;
-    EPC.callSPSWrapperAsync<void(SPSSequence<SPSMemoryAccessPointerWrite>)>(
-        FAs.WritePointers, std::move(OnWriteComplete), Ws);
+    EPC.callSPSWrapperAsync<SPSSequence<uint8_t>(SPSSequence<SPSExecutorAddr>)>(
+        FAs.ReadUInt8s,
+        [OnComplete = std::move(OnComplete)](
+            Error Err, ReadUIntsResult<uint8_t> Result) mutable {
+          if (Err)
+            OnComplete(std::move(Err));
+          else
+            OnComplete(std::move(Result));
+        },
+        Rs);
+  }
+
+  void readUInt16sAsync(ArrayRef<ExecutorAddr> Rs,
+                        OnReadUIntsCompleteFn<uint16_t> OnComplete) override {
+    using namespace shared;
+    EPC.callSPSWrapperAsync<SPSSequence<uint16_t>(
+        SPSSequence<SPSExecutorAddr>)>(
+        FAs.ReadUInt16s,
+        [OnComplete = std::move(OnComplete)](
+            Error Err, ReadUIntsResult<uint16_t> Result) mutable {
+          if (Err)
+            OnComplete(std::move(Err));
+          else
+            OnComplete(std::move(Result));
+        },
+        Rs);
+  }
+
+  void readUInt32sAsync(ArrayRef<ExecutorAddr> Rs,
+                        OnReadUIntsCompleteFn<uint32_t> OnComplete) override {
+    using namespace shared;
+    EPC.callSPSWrapperAsync<SPSSequence<uint32_t>(
+        SPSSequence<SPSExecutorAddr>)>(
+        FAs.ReadUInt32s,
+        [OnComplete = std::move(OnComplete)](
+            Error Err, ReadUIntsResult<uint32_t> Result) mutable {
+          if (Err)
+            OnComplete(std::move(Err));
+          else
+            OnComplete(std::move(Result));
+        },
+        Rs);
+  }
+
+  void readUInt64sAsync(ArrayRef<ExecutorAddr> Rs,
+                        OnReadUIntsCompleteFn<uint64_t> OnComplete) override {
+    using namespace shared;
+    EPC.callSPSWrapperAsync<SPSSequence<uint64_t>(
+        SPSSequence<SPSExecutorAddr>)>(
+        FAs.ReadUInt64s,
+        [OnComplete = std::move(OnComplete)](
+            Error Err, ReadUIntsResult<uint64_t> Result) mutable {
+          if (Err)
+            OnComplete(std::move(Err));
+          else
+            OnComplete(std::move(Result));
+        },
+        Rs);
+  }
+
+  void readPointersAsync(ArrayRef<ExecutorAddr> Rs,
+                         OnReadPointersCompleteFn OnComplete) override {
+    using namespace shared;
+    using SPSSig = SPSSequence<SPSExecutorAddr>(SPSSequence<SPSExecutorAddr>);
+    EPC.callSPSWrapperAsync<SPSSig>(
+        FAs.ReadPointers,
+        [OnComplete = std::move(OnComplete)](
+            Error Err, ReadPointersResult Result) mutable {
+          if (Err)
+            OnComplete(std::move(Err));
+          else
+            OnComplete(std::move(Result));
+        },
+        Rs);
+  }
+
+  void readBuffersAsync(ArrayRef<ExecutorAddrRange> Rs,
+                        OnReadBuffersCompleteFn OnComplete) override {
+    using namespace shared;
+    using SPSSig =
+        SPSSequence<SPSSequence<uint8_t>>(SPSSequence<SPSExecutorAddrRange>);
+    EPC.callSPSWrapperAsync<SPSSig>(
+        FAs.ReadBuffers,
+        [OnComplete = std::move(OnComplete)](Error Err,
+                                             ReadBuffersResult Result) mutable {
+          if (Err)
+            OnComplete(std::move(Err));
+          else
+            OnComplete(std::move(Result));
+        },
+        Rs);
+  }
+
+  void readStringsAsync(ArrayRef<ExecutorAddr> Rs,
+                        OnReadStringsCompleteFn OnComplete) override {
+    using namespace shared;
+    using SPSSig = SPSSequence<SPSString>(SPSSequence<SPSExecutorAddr>);
+    EPC.callSPSWrapperAsync<SPSSig>(
+        FAs.ReadStrings,
+        [OnComplete = std::move(OnComplete)](Error Err,
+                                             ReadStringsResult Result) mutable {
+          if (Err)
+            OnComplete(std::move(Err));
+          else
+            OnComplete(std::move(Result));
+        },
+        Rs);
   }
 
 private:
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/InProcessMemoryAccess.h b/llvm/include/llvm/ExecutionEngine/Orc/InProcessMemoryAccess.h
index 934f6f62ffab0..eb68495469ec0 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/InProcessMemoryAccess.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/InProcessMemoryAccess.h
@@ -32,11 +32,32 @@ class LLVM_ABI InProcessMemoryAccess : public MemoryAccess {
   void writeUInt64sAsync(ArrayRef<tpctypes::UInt64Write> Ws,
                          WriteResultFn OnWriteComplete) override;
 
+  void writePointersAsync(ArrayRef<tpctypes::PointerWrite> Ws,
+                          WriteResultFn OnWriteComplete) override;
+
   void writeBuffersAsync(ArrayRef<tpctypes::BufferWrite> Ws,
                          WriteResultFn OnWriteComplete) override;
 
-  void writePointersAsync(ArrayRef<tpctypes::PointerWrite> Ws,
-                          WriteResultFn OnWriteComplete) override;
+  void readUInt8sAsync(ArrayRef<ExecutorAddr> Rs,
+                       OnReadUIntsCompleteFn<uint8_t> OnComplete) override;
+
+  void readUInt16sAsync(ArrayRef<ExecutorAddr> Rs,
+                        OnReadUIntsCompleteFn<uint16_t> OnComplete) override;
+
+  void readUInt32sAsync(ArrayRef<ExecutorAddr> Rs,
+                        OnReadUIntsCompleteFn<uint32_t> OnComplete) override;
+
+  void readUInt64sAsync(ArrayRef<ExecutorAddr> Rs,
+                        OnReadUIntsCompleteFn<uint64_t> OnComplete) override;
+
+  void readPointersAsync(ArrayRef<ExecutorAddr> Rs,
+                         OnReadPointersCompleteFn OnComplete) override;
+
+  void readBuffersAsync(ArrayRef<ExecutorAddrRange> Rs,
+                        OnReadBuffersCompleteFn OnComplete) override;
+
+  void readStringsAsync(ArrayRef<ExecutorAddr> Rs,
+                        OnReadStringsCompleteFn OnComplete) override;
 
 private:
   bool IsArch64Bit;
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/MemoryAccess.h b/llvm/include/llvm/ExecutionEngine/Orc/MemoryAccess.h
index f4b34978f247c..1935f3c564439 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/MemoryAccess.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/MemoryAccess.h
@@ -28,6 +28,23 @@ class LLVM_ABI MemoryAccess {
   /// Callback function for asynchronous writes.
   using WriteResultFn = unique_function<void(Error)>;
 
+  template <typename T> using ReadUIntsResult = std::vector<T>;
+  template <typename T>
+  using OnReadUIntsCompleteFn =
+      unique_function<void(Expected<ReadUIntsResult<T>>)>;
+
+  using ReadPointersResult = std::vector<ExecutorAddr>;
+  using OnReadPointersCompleteFn =
+      unique_function<void(Expected<ReadPointersResult>)>;
+
+  using ReadBuffersResult = std::vector<std::vector<uint8_t>>;
+  using OnReadBuffersCompleteFn =
+      unique_function<void(Expected<ReadBuffersResult>)>;
+
+  using ReadStringsResult = std::vector<std::string>;
+  using OnReadStringsCompleteFn =
+      unique_function<void(Expected<ReadStringsResult>)>;
+
   virtual ~MemoryAccess();
 
   virtual void writeUInt8sAsync(ArrayRef<tpctypes::UInt8Write> Ws,
@@ -42,11 +59,32 @@ class LLVM_ABI MemoryAccess {
   virtual void writeUInt64sAsync(ArrayRef<tpctypes::UInt64Write> Ws,
                                  WriteResultFn OnWriteComplete) = 0;
 
+  virtual void writePointersAsync(ArrayRef<tpctypes::PointerWrite> Ws,
+                                  WriteResultFn OnWriteComplete) = 0;
+
   virtual void writeBuffersAsync(ArrayRef<tpctypes::BufferWrite> Ws,
                                  WriteResultFn OnWriteComplete) = 0;
 
-  virtual void writePointersAsync(ArrayRef<tpctypes::PointerWrite> Ws,
-                                  WriteResultFn OnWriteComplete) = 0;
+  virtual void readUInt8sAsync(ArrayRef<ExecutorAddr> Rs,
+                               OnReadUIntsCompleteFn<uint8_t> OnComplete) = 0;
+
+  virtual void readUInt16sAsync(ArrayRef<ExecutorAddr> Rs,
+                                OnReadUIntsCompleteFn<uint16_t> OnComplete) = 0;
+
+  virtual void readUInt32sAsync(ArrayRef<ExecutorAddr> Rs,
+                                OnReadUIntsCompleteFn<uint32_t> OnComplete) = 0;
+
+  virtual void readUInt64sAsync(ArrayRef<ExecutorAddr> Rs,
+                                OnReadUIntsCompleteFn<uint64_t> OnComplete) = 0;
+
+  virtual void readPointersAsync(ArrayRef<ExecutorAddr> Rs,
+                                 OnReadPointersCompleteFn OnComplete) = 0;
+
+  virtual void readBuffersAsync(ArrayRef<ExecutorAddrRange> Rs,
+                                OnReadBuffersCompleteFn OnComplete) = 0;
+
+  virtual void readStringsAsync(ArrayRef<ExecutorAddr> Rs,
+                                OnReadStringsCompleteFn OnComplete) = 0;
 
   Error writeUInt8s(ArrayRef<tpctypes::UInt8Write> Ws) {
     std::promise<MSVCPError> ResultP;
@@ -79,21 +117,77 @@ class LLVM_ABI MemoryAccess {
     return ResultF.get();
   }
 
-  Error writeBuffers(ArrayRef<tpctypes::BufferWrite> Ws) {
+  Error writePointers(ArrayRef<tpctypes::PointerWrite> Ws) {
     std::promise<MSVCPError> ResultP;
     auto ResultF = ResultP.get_future();
-    writeBuffersAsync(Ws,
-                      [&](Error Err) { ResultP.set_value(std::move(Err)); });
+    writePointersAsync(Ws,
+                       [&](Error Err) { ResultP.set_value(std::move(Err)); });
     return ResultF.get();
   }
 
-  Error writePointers(ArrayRef<tpctypes::PointerWrite> Ws) {
+  Error writeBuffers(ArrayRef<tpctypes::BufferWrite> Ws) {
     std::promise<MSVCPError> ResultP;
     auto ResultF = ResultP.get_future();
-    writePointersAsync(Ws,
-                       [&](Error Err) { ResultP.set_value(std::move(Err)); });
+    writeBuffersAsync(Ws,
+                      [&](Error Err) { ResultP.set_value(std::move(Err)); });
     return ResultF.get();
   }
+
+  Expected<ReadUIntsResult<uint8_t>> readUInt8s(ArrayRef<ExecutorAddr> Rs) {
+    std::promise<MSVCPExpected<ReadUIntsResult<uint8_t>>> P;
+    readUInt8sAsync(Rs, [&](Expected<ReadUIntsResult<uint8_t>> Result) {
+      P.set_value(std::move(Result));
+    });
+    return P.get_future().get();
+  }
+
+  Expected<ReadUIntsResult<uint16_t>> readUInt16s(ArrayRef<ExecutorAddr> Rs) {
+    std::promise<MSVCPExpected<ReadUIntsResult<uint16_t>>> P;
+    readUInt16sAsync(Rs, [&](Expected<ReadUIntsResult<uint16_t>> Result) {
+      P.set_value(std::move(Result));
+    });
+    return P.get_future().get();
+  }
+
+  Expected<ReadUIntsResult<uint32_t>> readUInt32s(ArrayRef<ExecutorAddr> Rs) {
+    std::promise<MSVCPExpected<ReadUIntsResult<uint32_t>>> P;
+    readUInt32sAsync(Rs, [&](Expected<ReadUIntsResult<uint32_t>> Result) {
+      P.set_value(std::move(Result));
+    });
+    return P.get_future().get();
+  }
+
+  Expected<ReadUIntsResult<uint64_t>> readUInt64s(ArrayRef<ExecutorAddr> Rs) {
+    std::promise<MSVCPExpected<ReadUIntsResult<uint64_t>>> P;
+    readUInt64sAsync(Rs, [&](Expected<ReadUIntsResult<uint64_t>> Result) {
+      P.set_value(std::move(Result));
+    });
+    return P.get_future().get();
+  }
+
+  Expected<ReadPointersResult> readPointers(ArrayRef<ExecutorAddr> Rs) {
+    std::promise<MSVCPExpected<ReadPointersResult>> P;
+    readPointersAsync(Rs, [&](Expected<ReadPointersResult> Result) {
+      P.set_value(std::move(Result));
+    });
+    return P.get_future().get();
+  }
+
+  Expected<ReadBuffersResult> readBuffers(ArrayRef<ExecutorAddrRange> Rs) {
+    std::promise<MSVCPExpected<ReadBuffersResult>> P;
+    readBuffersAsync(Rs, [&](Expected<ReadBuffersResult> Result) {
+      P.set_value(std::move(Result));
+    });
+    return P.get_future().get();
+  }
+
+  Expected<ReadStringsResult> readStrings(ArrayRef<ExecutorAddr> Rs) {
+    std::promise<MSVCPExpected<ReadStringsResult>> P;
+    readStringsAsync(Rs, [&](Expected<ReadStringsResult> Result) {
+      P.set_value(std::move(Result));
+    });
+    return P.get_future().get();
+  }
 };
 
 } // namespace llvm::orc
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h
index 017ef70469e29..2bc6c1218ae72 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h
@@ -44,8 +44,16 @@ LLVM_ABI extern const char *MemoryWriteUInt8sWrapperName;
 LLVM_ABI extern const char *MemoryWriteUInt16sWrapperName;
 LLVM_ABI extern const char *MemoryWriteUInt32sWrapperName;
 LLVM_ABI extern const char *MemoryWriteUInt64sWrapperName;
-LLVM_ABI extern const char *MemoryWriteBuffersWrapperName;
 LLVM_ABI extern const char *MemoryWritePointersWrapperName;
+LLVM_ABI extern const char *MemoryWriteBuffersWrapperName;
+
+LLVM_ABI extern const char *MemoryReadUInt8sWrapperName;
+LLVM_ABI extern const char *MemoryReadUInt16sWrapperName;
+LLVM_ABI extern const char *MemoryReadUInt32sWrapperName;
+LLVM_ABI extern const char *MemoryReadUInt64sWrapperName;
+LLVM_ABI extern const char *MemoryReadPointersWrapperName;
+LLVM_ABI extern const char *MemoryReadBuffersWrapperName;
+LLVM_ABI extern const char *MemoryReadStringsWrapperName;
 
 LLVM_ABI extern const char *RegisterEHFrameSectionAllocActionName;
 LLVM_ABI extern const char *DeregisterEHFrameSectionAllocActionName;
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h
index e91d8d926d88c..adb07ba29b92a 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h
@@ -93,11 +93,11 @@ using UInt64Write = UIntWrite<uint64_t>;
 /// For use with TargetProcessControl::MemoryAccess objects.
 struct BufferWrite {
   BufferWrite() = default;
-  BufferWrite(ExecutorAddr Addr, StringRef Buffer)
+  BufferWrite(ExecutorAddr Addr, ArrayRef<char> Buffer)
       : Addr(Addr), Buffer(Buffer) {}
 
   ExecutorAddr Addr;
-  StringRef Buffer;
+  ArrayRef<char> Buffer;
 };
 
 /// Describes a write to a pointer.
diff --git a/llvm/lib/ExecutionEngine/Orc/InProcessMemoryAccess.cpp b/llvm/lib/ExecutionEngine/Orc/InProcessMemoryAccess.cpp
index c8b2f00bc199d..261eedc5461e3 100644
--- a/llvm/lib/ExecutionEngine/Orc/InProcessMemoryAccess.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/InProcessMemoryAccess.cpp
@@ -42,13 +42,6 @@ void InProcessMemoryAccess::writeUInt64sAsync(
   OnWriteComplete(Error::success());
 }
 
-void InProcessMemoryAccess::writeBuffersAsync(
-    ArrayRef<tpctypes::BufferWrite> Ws, WriteResultFn OnWriteComplete) {
-  for (auto &W : Ws)
-    memcpy(W.Addr.toPtr<char *>(), W.Buffer.data(), W.Buffer.size());
-  OnWriteComplete(Error::success());
-}
-
 void InProcessMemoryAccess::writePointersAsync(
     ArrayRef<tpctypes::PointerWrite> Ws, WriteResultFn OnWriteComplete) {
   if (IsArch64Bit) {
@@ -61,4 +54,81 @@ void InProcessMemoryAccess::writePointersAsync(
 
   OnWriteComplete(Error::success());
 }
+
+void InProcessMemoryAccess::writeBuffersAsync(
+    ArrayRef<tpctypes::BufferWrite> Ws, WriteResultFn OnWriteComplete) {
+  for (auto &W : Ws)
+    memcpy(W.Addr.toPtr<char *>(), W.Buffer.data(), W.Buffer.size());
+  OnWriteComplete(Error::success());
+}
+
+void InProcessMemoryAccess::readUInt8sAsync(
+    ArrayRef<ExecutorAddr> Rs, OnReadUIntsCompleteFn<uint8_t> OnComplete) {
+  ReadUIntsResult<uint8_t> Result;
+  Result.reserve(Rs.size());
+  for (auto &R : Rs)
+    Result.push_back(*R.toPtr<uint8_t *>());
+  OnComplete(std::move(Result));
+}
+
+void InProcessMemoryAccess::readUInt16sAsync(
+    ArrayRef<ExecutorAddr> Rs, OnReadUIntsCompleteFn<uint16_t> OnComplete) {
+  ReadUIntsResult<uint16_t> Result;
+  Result.reserve(Rs.size());
+  for (auto &R : Rs)
+    Result.push_back(*R.toPtr<uint16_t *>());
+  OnComplete(std::move(Result));
+}
+
+void InProcessMemoryAccess::readUInt32sAsync(
+    ArrayRef<ExecutorAddr> Rs, OnReadUIntsCompleteFn<uint32_t> OnComplete) {
+  ReadUIntsResult<uint32_t> Result;
+  Result.reserve(Rs.size());
+  for (auto &R : Rs)
+    Result.push_back(*R.toPtr<uint32_t *>());
+  OnComplete(std::move(Result));
+}
+
+void InProcessMemoryAccess::readUInt64sAsync(
+    ArrayRef<ExecutorAddr> Rs, OnReadUIntsCompleteFn<uint64_t> OnComplete) {
+  ReadUIntsResult<uint64_t> Result;
+  Result.reserve(Rs.size());
+  for (auto &R : Rs)
+    Result.push_back(*R.toPtr<uint64_t *>());
+  OnComplete(std::move(Result));
+}
+
+void InProcessMemoryAccess::readPointersAsync(
+    ArrayRef<ExecutorAddr> Rs, OnReadPointersCompleteFn OnComplete) {
+  ReadPointersResult Result;
+  Result.reserve(Rs.size());
+  for (auto &R : Rs)
+    Result.push_back(ExecutorAddr::fromPtr(*R.toPtr<void **>()));
+  OnComplete(std::move(Result));
+}
+
+void InProcessMemoryAccess::readBuffersAsync(
+    ArrayRef<ExecutorAddrRange> Rs, OnReadBuffersCompleteFn OnComplete) {
+  ReadBuffersResult Result;
+  Result.reserve(Rs.size());
+  for (auto &R : Rs) {
+    Result.push_back({});
+    Result.back().resize(R.size());
+    memcpy(Result.back().data(), R.Start.toPtr<char *>(), R.size());
+  }
+  OnComplete(std::move(Result));
+}
+
+void InProcessMemoryAccess::readStringsAsync(
+    ArrayRef<ExecutorAddr> Rs, OnReadStringsCompleteFn OnComplete) {
+  ReadStringsResult Result;
+  Result.reserve(Rs.size());
+  for (auto &R : Rs) {
+    Result.push_back({});
+    for (auto *P = R.toPtr<char *>(); *P; ++P)
+      Result.back().push_back(*P);
+  }
+  OnComplete(std::move(Result));
+}
+
 } // end namespace llvm::orc
diff --git a/llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp b/llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp
index 4e5b800100b9c..123651fc623e2 100644
--- a/llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp
@@ -47,10 +47,25 @@ const char *MemoryWriteUInt32sWrapperName =
     "__llvm_orc_bootstrap_mem_write_uint32s_wrapper";
 const char *MemoryWriteUInt64sWrapperName =
     "__llvm_orc_bootstrap_mem_write_uint64s_wrapper";
-const char *MemoryWriteBuffersWrapperName =
-    "__llvm_orc_bootstrap_mem_write_buffers_wrapper";
 const char *MemoryWritePointersWrapperName =
     "__llvm_orc_bootstrap_mem_write_pointers_wrapper";
+const char *MemoryWriteBuffersWrapperName =
+    "__llvm_orc_bootstrap_mem_write_buffers_wrapper";
+
+const char *MemoryReadUInt8sWrapperName =
+    "__llvm_orc_bootstrap_mem_read_uint8s_wrapper";
+const char *MemoryReadUInt16sWrapperName =
+    "__llvm_orc_bootstrap_mem_read_uint16s_wrapper";
+const char *MemoryReadUInt32sWrapperName =
+    "__llvm_orc_bootstrap_mem_read_uint32s_wrapper";
+const char *MemoryReadUInt64sWrapperName =
+    "__llvm_orc_bootstrap_mem_read_uint64s_wrapper";
+const char *MemoryReadPointersWrapperName =
+    "__llvm_orc_bootstrap_mem_read_pointers_wrapper";
+const char *MemoryReadBuffersWrapperName =
+    "__llvm_orc_bootstrap_mem_read_buffers_wrapper";
+const char *MemoryReadStringsWrapperName =
+    "__llvm_orc_bootstrap_mem_read_strings_wrapper";
 
 const char *RegisterEHFrameSectionAllocActionName =
     "llvm_orc_registerEHFrameAllocAction";
diff --git a/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp b/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp
index 746b843499101..87d757805a64c 100644
--- a/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp
@@ -233,7 +233,13 @@ SimpleRemoteEPC::createDefaultMemoryAccess(SimpleRemoteEPC &SREPC) {
            {FAs.WriteUInt32s, rt::MemoryWriteUInt32sWrapperName},
            {FAs.WriteUInt64s, rt::MemoryWriteUInt64sWrapperName},
            {FAs.WriteBuffers, rt::MemoryWriteBuffersWrapperName},
-           {FAs.WritePointers, rt::MemoryWritePointersWrapperName}}))
+           {FAs.WritePointers, rt::MemoryWritePointersWrapperName},
+           {FAs.ReadUInt8s, rt::MemoryReadUInt8sWrapperName},
+           {FAs.ReadUInt16s, rt::MemoryReadUInt16sWrapperName},
+           {FAs.ReadUInt32s, rt::MemoryReadUInt32sWrapperName},
+           {FAs.ReadUInt64s, rt::MemoryReadUInt64sWrapperName},
+           {FAs.ReadBuffers, rt::MemoryReadBuffersWrapperName},
+           {FAs.ReadStrings, rt::MemoryReadStringsWrapperName}}))
     return std::move(Err);
 
   return std::make_unique<EPCGenericMemoryAccess>(SREPC, FAs);
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/OrcRTBootstrap.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/OrcRTBootstrap.cpp
index c4f201b353d27..06cb717750e35 100644
--- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/OrcRTBootstrap.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/OrcRTBootstrap.cpp
@@ -33,6 +33,18 @@ writeUIntsWrapper(const char *ArgData, size_t ArgSize) {
       .release();
 }
 
+static llvm::orc::shared::CWrapperFunctionResult
+writePointersWrapper(const char *ArgData, size_t ArgSize) {
+  return WrapperFunction<void(SPSSequence<SPSMemoryAccessPointerWrite>)>::
+      handle(ArgData, ArgSize,
+             [](std::vector<tpctypes::PointerWrite> Ws) {
+               for (auto &W : Ws)
+                 *W.Addr.template toPtr<void **>() =
+                     W.Value.template toPtr<void *>();
+             })
+          .release();
+}
+
 static llvm::orc::shared::CWrapperFunctionResult
 writeBuffersWrapper(const char *ArgData, size_t ArgSize) {
   return WrapperFunction<void(SPSSequence<SPSMemoryAccessBufferWrite>)>::handle(
@@ -45,16 +57,70 @@ writeBuffersWrapper(const char *ArgData, size_t ArgSize) {
       .release();
 }
 
+template <typename ReadT>
 static llvm::orc::shared::CWrapperFunctionResult
-writePointersWrapper(const char *ArgData, size_t ArgSize) {
-  return WrapperFunction<void(SPSSequence<SPSMemoryAccessPointerWrite>)>::
-      handle(ArgData, ArgSize,
-             [](std::vector<tpctypes::PointerWrite> Ws) {
-               for (auto &W : Ws)
-                 *W.Addr.template toPtr<void **>() =
-                     W.Value.template toPtr<void *>();
+readUIntsWrapper(const char *ArgData, size_t ArgSize) {
+  using SPSSig = SPSSequence<ReadT>(SPSSequence<SPSExecutorAddr>);
+  return WrapperFunction<SPSSig>::handle(ArgData, ArgSize,
+                                         [](std::vector<ExecutorAddr> Rs) {
+                                           std::vector<ReadT> Result;
+                                           Result.reserve(Rs.size());
+                                           for (auto &R : Rs)
+                                             Result.push_back(
+                                                 *R.toPtr<ReadT *>());
+                                           return Result;
+                                         })
+      .release();
+}
+
+static llvm::orc::shared::CWrapperFunctionResult
+readPointersWrapper(const char *ArgData, size_t ArgSize) {
+  using SPSSig = SPSSequence<SPSExecutorAddr>(SPSSequence<SPSExecutorAddr>);
+  return WrapperFunction<SPSSig>::handle(
+             ArgData, ArgSize,
+             [](std::vector<ExecutorAddr> Rs) {
+               std::vector<ExecutorAddr> Result;
+               Result.reserve(Rs.size());
+               for (auto &R : Rs)
+                 Result.push_back(ExecutorAddr::fromPtr(*R.toPtr<void **>()));
+               return Result;
              })
-          .release();
+      .release();
+}
+
+static llvm::orc::shared::CWrapperFunctionResult
+readBuffersWrapper(const char *ArgData, size_t ArgSize) {
+  using SPSSig =
+      SPSSequence<SPSSequence<uint8_t>>(SPSSequence<SPSExecutorAddrRange>);
+  return WrapperFunction<SPSSig>::handle(
+             ArgData, ArgSize,
+             [](std::vector<ExecutorAddrRange> Rs) {
+               std::vector<std::vector<uint8_t>> Result;
+               Result.reserve(Rs.size());
+               for (auto &R : Rs) {
+                 Result.push_back({});
+                 Result.back().resize(R.size());
+                 memcpy(reinterpret_cast<char *>(Result.back().data()),
+                        R.Start.toPtr<char *>(), R.size());
+               }
+               return Result;
+             })
+      .release();
+}
+
+static llvm::orc::shared::CWrapperFunctionResult
+readStringsWrapper(const char *ArgData, size_t ArgSize) {
+  using SPSSig = SPSSequence<SPSString>(SPSSequence<SPSExecutorAddr>);
+  return WrapperFunction<SPSSig>::handle(ArgData, ArgSize,
+                                         [](std::vector<ExecutorAddr> Rs) {
+                                           std::vector<std::string> Result;
+                                           Result.reserve(Rs.size());
+                                           for (auto &R : Rs)
+                                             Result.push_back(
+                                                 R.toPtr<char *>());
+                                           return Result;
+                                         })
+      .release();
 }
 
 static llvm::orc::shared::CWrapperFunctionResult
@@ -102,10 +168,24 @@ void addTo(StringMap<ExecutorAddr> &M) {
   M[rt::MemoryWriteUInt64sWrapperName] = ExecutorAddr::fromPtr(
       &writeUIntsWrapper<tpctypes::UInt64Write,
                          shared::SPSMemoryAccessUInt64Write>);
-  M[rt::MemoryWriteBuffersWrapperName] =
-      ExecutorAddr::fromPtr(&writeBuffersWrapper);
   M[rt::MemoryWritePointersWrapperName] =
       ExecutorAddr::fromPtr(&writePointersWrapper);
+  M[rt::MemoryWriteBuffersWrapperName] =
+      ExecutorAddr::fromPtr(&writeBuffersWrapper);
+  M[rt::MemoryReadUInt8sWrapperName] =
+      ExecutorAddr::fromPtr(&readUIntsWrapper<uint8_t>);
+  M[rt::MemoryReadUInt16sWrapperName] =
+      ExecutorAddr::fromPtr(&readUIntsWrapper<uint16_t>);
+  M[rt::MemoryReadUInt32sWrapperName] =
+      ExecutorAddr::fromPtr(&readUIntsWrapper<uint32_t>);
+  M[rt::MemoryReadUInt64sWrapperName] =
+      ExecutorAddr::fromPtr(&readUIntsWrapper<uint64_t>);
+  M[rt::MemoryReadPointersWrapperName] =
+      ExecutorAddr::fromPtr(&readPointersWrapper);
+  M[rt::MemoryReadBuffersWrapperName] =
+      ExecutorAddr::fromPtr(&readBuffersWrapper);
+  M[rt::MemoryReadStringsWrapperName] =
+      ExecutorAddr::fromPtr(&readStringsWrapper);
   M[rt::RunAsMainWrapperName] = ExecutorAddr::fromPtr(&runAsMainWrapper);
   M[rt::RunAsVoidFunctionWrapperName] =
       ExecutorAddr::fromPtr(&runAsVoidFunctionWrapper);
diff --git a/llvm/unittests/ExecutionEngine/Orc/EPCGenericMemoryAccessTest.cpp b/llvm/unittests/ExecutionEngine/Orc/EPCGenericMemoryAccessTest.cpp
index d54bcd4fd5218..9358ea8149e95 100644
--- a/llvm/unittests/ExecutionEngine/Orc/EPCGenericMemoryAccessTest.cpp
+++ b/llvm/unittests/ExecutionEngine/Orc/EPCGenericMemoryAccessTest.cpp
@@ -29,6 +29,16 @@ CWrapperFunctionResult testWriteUInts(const char *ArgData, size_t ArgSize) {
       .release();
 }
 
+CWrapperFunctionResult testWritePointers(const char *ArgData, size_t ArgSize) {
+  return WrapperFunction<void(SPSSequence<SPSMemoryAccessPointerWrite>)>::
+      handle(ArgData, ArgSize,
+             [](std::vector<tpctypes::PointerWrite> Ws) {
+               for (auto &W : Ws)
+                 *W.Addr.template toPtr<uint64_t *>() = W.Value.getValue();
+             })
+          .release();
+}
+
 CWrapperFunctionResult testWriteBuffers(const char *ArgData, size_t ArgSize) {
   return WrapperFunction<void(SPSSequence<SPSMemoryAccessBufferWrite>)>::handle(
              ArgData, ArgSize,
@@ -40,76 +50,296 @@ CWrapperFunctionResult testWriteBuffers(const char *ArgData, size_t ArgSize) {
       .release();
 }
 
-CWrapperFunctionResult testWritePointers(const char *ArgData, size_t ArgSize) {
-  return WrapperFunction<void(SPSSequence<SPSMemoryAccessPointerWrite>)>::
-      handle(ArgData, ArgSize,
-             [](std::vector<tpctypes::PointerWrite> Ws) {
-               for (auto &W : Ws)
-                 *W.Addr.template toPtr<uint64_t *>() = W.Value.getValue();
+template <typename ReadT>
+CWrapperFunctionResult testReadUInts(const char *ArgData, size_t ArgSize) {
+  using SPSSig = SPSSequence<ReadT>(SPSSequence<SPSExecutorAddr>);
+  return WrapperFunction<SPSSig>::handle(ArgData, ArgSize,
+                                         [](std::vector<ExecutorAddr> Rs) {
+                                           std::vector<ReadT> Result;
+                                           Result.reserve(Rs.size());
+                                           for (auto &R : Rs)
+                                             Result.push_back(
+                                                 *R.template toPtr<ReadT *>());
+                                           return Result;
+                                         })
+      .release();
+}
+
+CWrapperFunctionResult testReadPointers(const char *ArgData, size_t ArgSize) {
+  using SPSSig = SPSSequence<SPSExecutorAddr>(SPSSequence<SPSExecutorAddr>);
+  return WrapperFunction<SPSSig>::handle(
+             ArgData, ArgSize,
+             [](std::vector<ExecutorAddr> Rs) {
+               std::vector<ExecutorAddr> Result;
+               Result.reserve(Rs.size());
+               for (auto &R : Rs)
+                 Result.push_back(
+                     ExecutorAddr::fromPtr(*R.template toPtr<void **>()));
+               return Result;
              })
-          .release();
+      .release();
+}
+
+CWrapperFunctionResult testReadBuffers(const char *ArgData, size_t ArgSize) {
+  using SPSSig =
+      SPSSequence<SPSSequence<uint8_t>>(SPSSequence<SPSExecutorAddrRange>);
+  return WrapperFunction<SPSSig>::handle(
+             ArgData, ArgSize,
+             [](std::vector<ExecutorAddrRange> Rs) {
+               std::vector<std::vector<uint8_t>> Result;
+               Result.reserve(Rs.size());
+               for (auto &R : Rs) {
+                 Result.push_back({});
+                 Result.back().resize(R.size());
+                 memcpy(reinterpret_cast<char *>(Result.back().data()),
+                        R.Start.toPtr<char *>(), R.size());
+               }
+               return Result;
+             })
+      .release();
+}
+
+CWrapperFunctionResult testReadStrings(const char *ArgData, size_t ArgSize) {
+  using SPSSig = SPSSequence<SPSString>(SPSSequence<SPSExecutorAddr>);
+  return WrapperFunction<SPSSig>::handle(
+             ArgData, ArgSize,
+             [](std::vector<ExecutorAddr> Rs) {
+               std::vector<std::string> Result;
+               Result.reserve(Rs.size());
+               for (auto &R : Rs)
+                 Result.push_back(std::string(R.toPtr<char *>()));
+               return Result;
+             })
+      .release();
+}
+
+class EPCGenericMemoryAccessTest : public testing::Test {
+public:
+  EPCGenericMemoryAccessTest() {
+    EPC = cantFail(SelfExecutorProcessControl::Create());
+
+    EPCGenericMemoryAccess::FuncAddrs FAs;
+    FAs.WriteUInt8s = ExecutorAddr::fromPtr(
+        &testWriteUInts<tpctypes::UInt8Write, SPSMemoryAccessUInt8Write>);
+    FAs.WriteUInt16s = ExecutorAddr::fromPtr(
+        &testWriteUInts<tpctypes::UInt16Write, SPSMemoryAccessUInt16Write>);
+    FAs.WriteUInt32s = ExecutorAddr::fromPtr(
+        &testWriteUInts<tpctypes::UInt32Write, SPSMemoryAccessUInt32Write>);
+    FAs.WriteUInt64s = ExecutorAddr::fromPtr(
+        &testWriteUInts<tpctypes::UInt64Write, SPSMemoryAccessUInt64Write>);
+    FAs.WritePointers = ExecutorAddr::fromPtr(&testWritePointers);
+    FAs.WriteBuffers = ExecutorAddr::fromPtr(&testWriteBuffers);
+    FAs.ReadUInt8s = ExecutorAddr::fromPtr(&testReadUInts<uint8_t>);
+    FAs.ReadUInt16s = ExecutorAddr::fromPtr(&testReadUInts<uint16_t>);
+    FAs.ReadUInt32s = ExecutorAddr::fromPtr(&testReadUInts<uint32_t>);
+    FAs.ReadUInt64s = ExecutorAddr::fromPtr(&testReadUInts<uint64_t>);
+    FAs.ReadPointers = ExecutorAddr::fromPtr(&testReadPointers);
+    FAs.ReadBuffers = ExecutorAddr::fromPtr(&testReadBuffers);
+    FAs.ReadStrings = ExecutorAddr::fromPtr(&testReadStrings);
+
+    MemAccess = std::make_unique<EPCGenericMemoryAccess>(*EPC, FAs);
+  }
+
+  ~EPCGenericMemoryAccessTest() { cantFail(EPC->disconnect()); }
+
+protected:
+  std::shared_ptr<SelfExecutorProcessControl> EPC;
+  std::unique_ptr<MemoryAccess> MemAccess;
+
+  static const uint8_t UInt8_1_TestValue;
+  static const uint8_t UInt8_2_TestValue;
+  static const uint16_t UInt16_TestValue;
+  static const uint32_t UInt32_TestValue;
+  static const uint64_t UInt64_TestValue;
+  static const void *Pointer_TestValue;
+
+  static const char Buffer_TestValue[21];
+  static const char *String_TestValue;
+};
+
+const uint8_t EPCGenericMemoryAccessTest::UInt8_1_TestValue = 1;
+const uint8_t EPCGenericMemoryAccessTest::UInt8_2_TestValue = 2;
+const uint16_t EPCGenericMemoryAccessTest::UInt16_TestValue = 3;
+const uint32_t EPCGenericMemoryAccessTest::UInt32_TestValue = 4;
+const uint64_t EPCGenericMemoryAccessTest::UInt64_TestValue = 5;
+
+const void *EPCGenericMemoryAccessTest::Pointer_TestValue =
+    reinterpret_cast<void *>(uintptr_t(0x6));
+
+const char EPCGenericMemoryAccessTest::Buffer_TestValue[21] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
+    0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14};
+const char *EPCGenericMemoryAccessTest::String_TestValue = "hello, world!";
+
+TEST_F(EPCGenericMemoryAccessTest, WriteUInt8s) {
+  uint8_t UInt8_1_Storage = 0;
+  uint8_t UInt8_2_Storage = 0;
+
+  auto Err = MemAccess->writeUInt8s(
+      {{ExecutorAddr::fromPtr(&UInt8_1_Storage), UInt8_1_TestValue},
+       {ExecutorAddr::fromPtr(&UInt8_2_Storage), UInt8_2_TestValue}});
+  EXPECT_THAT_ERROR(std::move(Err), Succeeded());
+  EXPECT_EQ(UInt8_1_Storage, UInt8_1_TestValue);
+  EXPECT_EQ(UInt8_2_Storage, UInt8_2_TestValue);
+}
+
+TEST_F(EPCGenericMemoryAccessTest, ReadUInt8s) {
+  uint8_t UInt8_1_Storage = UInt8_1_TestValue;
+  uint8_t UInt8_2_Storage = UInt8_2_TestValue;
+
+  auto Vals =
+      MemAccess->readUInt8s({{ExecutorAddr::fromPtr(&UInt8_1_Storage),
+                              ExecutorAddr::fromPtr(&UInt8_2_Storage)}});
+  static_assert(
+      std::is_same_v<decltype(Vals)::value_type::value_type, uint8_t>);
+  if (!Vals)
+    return ADD_FAILURE() << toString(Vals.takeError());
+
+  EXPECT_EQ(Vals->size(), 2U);
+  if (Vals->size() >= 1)
+    EXPECT_EQ((*Vals)[0], UInt8_1_TestValue);
+  if (Vals->size() >= 2)
+    EXPECT_EQ((*Vals)[1], UInt8_2_TestValue);
+}
+
+TEST_F(EPCGenericMemoryAccessTest, WriteUInt16s) {
+  uint16_t UInt16_Storage = 0;
+
+  auto Err = MemAccess->writeUInt16s(
+      {{ExecutorAddr::fromPtr(&UInt16_Storage), UInt16_TestValue}});
+  EXPECT_THAT_ERROR(std::move(Err), Succeeded());
+  EXPECT_EQ(UInt16_Storage, UInt16_TestValue);
+}
+
+TEST_F(EPCGenericMemoryAccessTest, ReadUInt16s) {
+  uint16_t UInt16_Storage = UInt16_TestValue;
+
+  auto Vals =
+      MemAccess->readUInt16s({{ExecutorAddr::fromPtr(&UInt16_Storage)}});
+  static_assert(
+      std::is_same_v<decltype(Vals)::value_type::value_type, uint16_t>);
+  if (Vals) {
+    EXPECT_EQ(Vals->size(), 1U);
+    if (Vals->size() == 1)
+      EXPECT_EQ((*Vals)[0], UInt16_TestValue);
+  } else
+    EXPECT_THAT_ERROR(Vals.takeError(), Succeeded());
+}
+
+TEST_F(EPCGenericMemoryAccessTest, WriteUInt32s) {
+  uint32_t UInt32_Storage = 0;
+
+  auto Err = MemAccess->writeUInt32s(
+      {{ExecutorAddr::fromPtr(&UInt32_Storage), UInt32_TestValue}});
+  EXPECT_THAT_ERROR(std::move(Err), Succeeded());
+  EXPECT_EQ(UInt32_Storage, UInt32_TestValue);
+}
+
+TEST_F(EPCGenericMemoryAccessTest, ReadUInt32s) {
+  uint32_t UInt32_Storage = UInt32_TestValue;
+
+  auto Vals =
+      MemAccess->readUInt32s({{ExecutorAddr::fromPtr(&UInt32_Storage)}});
+  static_assert(
+      std::is_same_v<decltype(Vals)::value_type::value_type, uint32_t>);
+  if (Vals) {
+    EXPECT_EQ(Vals->size(), 1U);
+    if (Vals->size() == 1)
+      EXPECT_EQ((*Vals)[0], UInt32_TestValue);
+  } else
+    EXPECT_THAT_ERROR(Vals.takeError(), Succeeded());
+}
+
+TEST_F(EPCGenericMemoryAccessTest, WriteUInt64s) {
+  uint64_t UInt64_Storage = 0;
+
+  auto Err = MemAccess->writeUInt64s(
+      {{ExecutorAddr::fromPtr(&UInt64_Storage), UInt64_TestValue}});
+  EXPECT_THAT_ERROR(std::move(Err), Succeeded());
+  EXPECT_EQ(UInt64_Storage, UInt64_TestValue);
+}
+
+TEST_F(EPCGenericMemoryAccessTest, ReadUInt64s) {
+  uint64_t UInt64_Storage = UInt64_TestValue;
+
+  auto Vals =
+      MemAccess->readUInt64s({{ExecutorAddr::fromPtr(&UInt64_Storage)}});
+  static_assert(
+      std::is_same_v<decltype(Vals)::value_type::value_type, uint64_t>);
+  if (Vals) {
+    EXPECT_EQ(Vals->size(), 1U);
+    if (Vals->size() == 1)
+      EXPECT_EQ((*Vals)[0], UInt64_TestValue);
+  } else
+    EXPECT_THAT_ERROR(Vals.takeError(), Succeeded());
+}
+
+TEST_F(EPCGenericMemoryAccessTest, WritePointers) {
+  void *Pointer_Storage = nullptr;
+
+  auto Err =
+      MemAccess->writePointers({{ExecutorAddr::fromPtr(&Pointer_Storage),
+                                 ExecutorAddr::fromPtr(Pointer_TestValue)}});
+  EXPECT_THAT_ERROR(std::move(Err), Succeeded());
+  EXPECT_EQ(Pointer_Storage, Pointer_TestValue);
+}
+
+TEST_F(EPCGenericMemoryAccessTest, ReadPointers) {
+  auto Vals =
+      MemAccess->readPointers({{ExecutorAddr::fromPtr(&Pointer_TestValue)}});
+  static_assert(
+      std::is_same_v<decltype(Vals)::value_type::value_type, ExecutorAddr>);
+  if (Vals) {
+    EXPECT_EQ(Vals->size(), 1U);
+    if (Vals->size() == 1)
+      EXPECT_EQ((*Vals)[0], ExecutorAddr::fromPtr(Pointer_TestValue));
+  } else
+    EXPECT_THAT_ERROR(Vals.takeError(), Succeeded());
+}
+
+TEST_F(EPCGenericMemoryAccessTest, WriteBuffers) {
+  char Buffer_Storage[sizeof(Buffer_TestValue)];
+  memset(Buffer_Storage, 0, sizeof(Buffer_TestValue));
+
+  auto Err = MemAccess->writeBuffers(
+      {{ExecutorAddr::fromPtr(&Buffer_Storage),
+        ArrayRef(Buffer_TestValue, sizeof(Buffer_TestValue))}});
+  EXPECT_THAT_ERROR(std::move(Err), Succeeded());
+  EXPECT_EQ(ArrayRef(Buffer_Storage, sizeof(Buffer_TestValue)),
+            ArrayRef(Buffer_TestValue, sizeof(Buffer_TestValue)));
+}
+
+TEST_F(EPCGenericMemoryAccessTest, ReadBuffers) {
+  char Buffer_Storage[sizeof(Buffer_TestValue)];
+  memcpy(Buffer_Storage, Buffer_TestValue, sizeof(Buffer_TestValue));
+
+  auto Vals = MemAccess->readBuffers({{ExecutorAddrRange(
+      ExecutorAddr::fromPtr(&Buffer_Storage), sizeof(Buffer_Storage))}});
+  static_assert(std::is_same_v<decltype(Vals)::value_type::value_type,
+                               std::vector<uint8_t>>);
+  if (Vals) {
+    EXPECT_EQ(Vals->size(), 1U);
+    if (Vals->size() == 1) {
+      EXPECT_EQ((*Vals)[0].size(), sizeof(Buffer_Storage));
+      EXPECT_EQ(
+          memcmp((*Vals)[0].data(), Buffer_TestValue, sizeof(Buffer_Storage)),
+          0);
+    }
+  } else
+    EXPECT_THAT_ERROR(Vals.takeError(), Succeeded());
 }
 
-TEST(EPCGenericMemoryAccessTest, MemWrites) {
-  auto SelfEPC = cantFail(SelfExecutorProcessControl::Create());
-
-  EPCGenericMemoryAccess::FuncAddrs FAs;
-  FAs.WriteUInt8s = ExecutorAddr::fromPtr(
-      &testWriteUInts<tpctypes::UInt8Write, SPSMemoryAccessUInt8Write>);
-  FAs.WriteUInt16s = ExecutorAddr::fromPtr(
-      &testWriteUInts<tpctypes::UInt16Write, SPSMemoryAccessUInt16Write>);
-  FAs.WriteUInt32s = ExecutorAddr::fromPtr(
-      &testWriteUInts<tpctypes::UInt32Write, SPSMemoryAccessUInt32Write>);
-  FAs.WriteUInt64s = ExecutorAddr::fromPtr(
-      &testWriteUInts<tpctypes::UInt64Write, SPSMemoryAccessUInt64Write>);
-  FAs.WriteBuffers = ExecutorAddr::fromPtr(&testWriteBuffers);
-  FAs.WritePointers = ExecutorAddr::fromPtr(&testWritePointers);
-
-  auto MemAccess = std::make_unique<EPCGenericMemoryAccess>(*SelfEPC, FAs);
-
-  uint8_t Test_UInt8_1 = 0;
-  uint8_t Test_UInt8_2 = 0;
-  uint16_t Test_UInt16 = 0;
-  uint32_t Test_UInt32 = 0;
-  uint64_t Test_UInt64 = 0;
-  uint64_t Test_Pointer = 0;
-  char Test_Buffer[21];
-
-  auto Err1 =
-      MemAccess->writeUInt8s({{ExecutorAddr::fromPtr(&Test_UInt8_1), 1},
-                              {ExecutorAddr::fromPtr(&Test_UInt8_2), 0xFE}});
-
-  EXPECT_THAT_ERROR(std::move(Err1), Succeeded());
-  EXPECT_EQ(Test_UInt8_1, 1U);
-  EXPECT_EQ(Test_UInt8_2, 0xFE);
-
-  auto Err2 =
-      MemAccess->writeUInt16s({{ExecutorAddr::fromPtr(&Test_UInt16), 1}});
-  EXPECT_THAT_ERROR(std::move(Err2), Succeeded());
-  EXPECT_EQ(Test_UInt16, 1U);
-
-  auto Err3 =
-      MemAccess->writeUInt32s({{ExecutorAddr::fromPtr(&Test_UInt32), 1}});
-  EXPECT_THAT_ERROR(std::move(Err3), Succeeded());
-  EXPECT_EQ(Test_UInt32, 1U);
-
-  auto Err4 =
-      MemAccess->writeUInt64s({{ExecutorAddr::fromPtr(&Test_UInt64), 1}});
-  EXPECT_THAT_ERROR(std::move(Err4), Succeeded());
-  EXPECT_EQ(Test_UInt64, 1U);
-
-  StringRef TestMsg("test-message");
-  auto Err5 =
-      MemAccess->writeBuffers({{ExecutorAddr::fromPtr(&Test_Buffer), TestMsg}});
-  EXPECT_THAT_ERROR(std::move(Err5), Succeeded());
-  EXPECT_EQ(StringRef(Test_Buffer, TestMsg.size()), TestMsg);
-
-  auto Err6 = MemAccess->writePointers(
-      {{ExecutorAddr::fromPtr(&Test_Pointer), ExecutorAddr(1U)}});
-  EXPECT_THAT_ERROR(std::move(Err6), Succeeded());
-  EXPECT_EQ(Test_Pointer, 1U);
-
-  cantFail(SelfEPC->disconnect());
+TEST_F(EPCGenericMemoryAccessTest, ReadStrings) {
+  auto Vals =
+      MemAccess->readStrings({{ExecutorAddr::fromPtr(String_TestValue)}});
+  static_assert(
+      std::is_same_v<decltype(Vals)::value_type, std::vector<std::string>>);
+  if (Vals) {
+    EXPECT_EQ(Vals->size(), 1U);
+    if (Vals->size() == 1)
+      EXPECT_EQ((*Vals)[0], std::string(String_TestValue));
+  } else
+    EXPECT_THAT_ERROR(Vals.takeError(), Succeeded());
 }
 
 } // namespace



More information about the llvm-commits mailing list