[ORC] Add EPCGenericMemoryAccess: generic executor memory access via EPC calls.
All ExecutorProcessControl subclasses must provide an ExecutorProcessControl::MemoryAccess object that can be used to access executor memory from the JIT process. The EPCGenericMemoryAccess class provides an off-the-shelf MemoryAccess implementation for JITs that do not need (or cannot provide) a specialized MemoryAccess implementation. This simplifies the process of creating new ExecutorProcessControl implementations.
This commit is contained in:
@@ -1489,14 +1489,8 @@ public:
|
||||
void callSPSWrapperAsync(SendResultT &&SendResult,
|
||||
JITTargetAddress WrapperFnAddr,
|
||||
const ArgTs &...Args) {
|
||||
shared::WrapperFunction<SPSSignature>::callAsync(
|
||||
[this,
|
||||
WrapperFnAddr](ExecutorProcessControl::SendResultFunction SendResult,
|
||||
const char *ArgData, size_t ArgSize) {
|
||||
callWrapperAsync(std::move(SendResult), WrapperFnAddr,
|
||||
ArrayRef<char>(ArgData, ArgSize));
|
||||
},
|
||||
std::move(SendResult), Args...);
|
||||
EPC->callSPSWrapperAsync<SPSSignature, SendResultT, ArgTs...>(
|
||||
std::forward<SendResultT>(SendResult), WrapperFnAddr, Args...);
|
||||
}
|
||||
|
||||
/// Run a wrapper function using SPS to serialize the arguments and
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
//===- EPCGenericMemoryAccess.h - Generic EPC MemoryAccess impl -*- 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 ExecutorProcessControl::MemoryAccess by making calls to
|
||||
// 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_EPCGENERICMEMORYACCESS_H
|
||||
#define LLVM_EXECUTIONENGINE_ORC_EPCGENERICMEMORYACCESS_H
|
||||
|
||||
#include "llvm/ExecutionEngine/Orc/Core.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace orc {
|
||||
|
||||
class EPCGenericMemoryAccess : public ExecutorProcessControl::MemoryAccess {
|
||||
public:
|
||||
/// Function addresses for memory access.
|
||||
struct FuncAddrs {
|
||||
ExecutorAddress WriteUInt8s;
|
||||
ExecutorAddress WriteUInt16s;
|
||||
ExecutorAddress WriteUInt32s;
|
||||
ExecutorAddress WriteUInt64s;
|
||||
ExecutorAddress WriteBuffers;
|
||||
};
|
||||
|
||||
/// Create an EPCGenericMemoryAccess instance from a given set of
|
||||
/// function addrs.
|
||||
EPCGenericMemoryAccess(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<EPCGenericMemoryAccess>>
|
||||
CreateUsingOrcRTFuncs(ExecutionSession &ES, JITDylib &OrcRuntimeJD);
|
||||
|
||||
void writeUInt8sAsync(ArrayRef<tpctypes::UInt8Write> Ws,
|
||||
WriteResultFn OnWriteComplete) override {
|
||||
using namespace shared;
|
||||
EPC.callSPSWrapperAsync<void(SPSSequence<SPSMemoryAccessUInt8Write>)>(
|
||||
std::move(OnWriteComplete), FAs.WriteUInt8s.getValue(), Ws);
|
||||
}
|
||||
|
||||
void writeUInt16sAsync(ArrayRef<tpctypes::UInt16Write> Ws,
|
||||
WriteResultFn OnWriteComplete) override {
|
||||
using namespace shared;
|
||||
EPC.callSPSWrapperAsync<void(SPSSequence<SPSMemoryAccessUInt16Write>)>(
|
||||
std::move(OnWriteComplete), FAs.WriteUInt16s.getValue(), Ws);
|
||||
}
|
||||
|
||||
void writeUInt32sAsync(ArrayRef<tpctypes::UInt32Write> Ws,
|
||||
WriteResultFn OnWriteComplete) override {
|
||||
using namespace shared;
|
||||
EPC.callSPSWrapperAsync<void(SPSSequence<SPSMemoryAccessUInt32Write>)>(
|
||||
std::move(OnWriteComplete), FAs.WriteUInt32s.getValue(), Ws);
|
||||
}
|
||||
|
||||
void writeUInt64sAsync(ArrayRef<tpctypes::UInt64Write> Ws,
|
||||
WriteResultFn OnWriteComplete) override {
|
||||
using namespace shared;
|
||||
EPC.callSPSWrapperAsync<void(SPSSequence<SPSMemoryAccessUInt64Write>)>(
|
||||
std::move(OnWriteComplete), FAs.WriteUInt64s.getValue(), Ws);
|
||||
}
|
||||
|
||||
void writeBuffersAsync(ArrayRef<tpctypes::BufferWrite> Ws,
|
||||
WriteResultFn OnWriteComplete) override {
|
||||
using namespace shared;
|
||||
EPC.callSPSWrapperAsync<void(SPSSequence<SPSMemoryAccessBufferWrite>)>(
|
||||
std::move(OnWriteComplete), FAs.WriteBuffers.getValue(), Ws);
|
||||
}
|
||||
|
||||
private:
|
||||
ExecutorProcessControl &EPC;
|
||||
FuncAddrs FAs;
|
||||
};
|
||||
|
||||
} // end namespace orc
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_EXECUTIONENGINE_ORC_EPCGENERICMEMORYACCESS_H
|
||||
@@ -168,8 +168,7 @@ public:
|
||||
/// The result of the lookup is a 2-dimentional array of target addresses
|
||||
/// that correspond to the lookup order. If a required symbol is not
|
||||
/// found then this method will return an error. If a weakly referenced
|
||||
/// symbol is not found then it be assigned a '0' value in the result.
|
||||
/// that correspond to the lookup order.
|
||||
/// symbol is not found then it be assigned a '0' value.
|
||||
virtual Expected<std::vector<tpctypes::LookupResult>>
|
||||
lookupSymbols(ArrayRef<LookupRequest> Request) = 0;
|
||||
|
||||
@@ -190,6 +189,22 @@ public:
|
||||
JITTargetAddress WrapperFnAddr,
|
||||
ArrayRef<char> ArgBuffer) = 0;
|
||||
|
||||
/// Run a wrapper function using SPS to serialize the arguments and
|
||||
/// deserialize the results.
|
||||
template <typename SPSSignature, typename SendResultT, typename... ArgTs>
|
||||
void callSPSWrapperAsync(SendResultT &&SendResult,
|
||||
JITTargetAddress WrapperFnAddr,
|
||||
const ArgTs &...Args) {
|
||||
shared::WrapperFunction<SPSSignature>::callAsync(
|
||||
[this,
|
||||
WrapperFnAddr](ExecutorProcessControl::SendResultFunction SendResult,
|
||||
const char *ArgData, size_t ArgSize) {
|
||||
callWrapperAsync(std::move(SendResult), WrapperFnAddr,
|
||||
ArrayRef<char>(ArgData, ArgSize));
|
||||
},
|
||||
std::move(SendResult), Args...);
|
||||
}
|
||||
|
||||
/// Disconnect from the target process.
|
||||
///
|
||||
/// This should be called after the JIT session is shut down.
|
||||
|
||||
@@ -61,7 +61,8 @@ Error lookupAndRecordAddrs(
|
||||
/// weak.
|
||||
Error lookupAndRecordAddrs(
|
||||
ExecutorProcessControl &EPC, tpctypes::DylibHandle H,
|
||||
std::vector<std::pair<SymbolStringPtr, ExecutorAddress *>> Pairs);
|
||||
std::vector<std::pair<SymbolStringPtr, ExecutorAddress *>> Pairs,
|
||||
SymbolLookupFlags LookupFlags = SymbolLookupFlags::RequiredSymbol);
|
||||
|
||||
} // End namespace orc
|
||||
} // End namespace llvm
|
||||
|
||||
@@ -289,6 +289,13 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/// Trivial ArrayRef<T> -> SPSSequence<SPSElementTagT> serialization.
|
||||
template <typename SPSElementTagT, typename T>
|
||||
class TrivialSPSSequenceSerialization<SPSElementTagT, ArrayRef<T>> {
|
||||
public:
|
||||
static constexpr bool available = true;
|
||||
};
|
||||
|
||||
/// 'Trivial' sequence serialization: Sequence is serialized as a uint64_t size
|
||||
/// followed by a for-earch loop over the elements of the sequence to serialize
|
||||
/// each of them.
|
||||
|
||||
@@ -62,6 +62,62 @@ using DylibHandle = JITTargetAddress;
|
||||
using LookupResult = std::vector<JITTargetAddress>;
|
||||
|
||||
} // end namespace tpctypes
|
||||
|
||||
namespace shared {
|
||||
|
||||
template <typename T>
|
||||
using SPSMemoryAccessUIntWrite = SPSTuple<SPSExecutorAddress, T>;
|
||||
|
||||
using SPSMemoryAccessUInt8Write = SPSMemoryAccessUIntWrite<uint8_t>;
|
||||
using SPSMemoryAccessUInt16Write = SPSMemoryAccessUIntWrite<uint16_t>;
|
||||
using SPSMemoryAccessUInt32Write = SPSMemoryAccessUIntWrite<uint32_t>;
|
||||
using SPSMemoryAccessUInt64Write = SPSMemoryAccessUIntWrite<uint64_t>;
|
||||
|
||||
using SPSMemoryAccessBufferWrite =
|
||||
SPSTuple<SPSExecutorAddress, SPSSequence<char>>;
|
||||
|
||||
template <typename T>
|
||||
class SPSSerializationTraits<SPSMemoryAccessUIntWrite<T>,
|
||||
tpctypes::UIntWrite<T>> {
|
||||
public:
|
||||
static size_t size(const tpctypes::UIntWrite<T> &W) {
|
||||
return SPSTuple<SPSExecutorAddress, T>::AsArgList::size(W.Address, W.Value);
|
||||
}
|
||||
|
||||
static bool serialize(SPSOutputBuffer &OB, const tpctypes::UIntWrite<T> &W) {
|
||||
return SPSTuple<SPSExecutorAddress, T>::AsArgList::serialize(OB, W.Address,
|
||||
W.Value);
|
||||
}
|
||||
|
||||
static bool deserialize(SPSInputBuffer &IB, tpctypes::UIntWrite<T> &W) {
|
||||
return SPSTuple<SPSExecutorAddress, T>::AsArgList::deserialize(
|
||||
IB, W.Address, W.Value);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class SPSSerializationTraits<SPSMemoryAccessBufferWrite,
|
||||
tpctypes::BufferWrite> {
|
||||
public:
|
||||
static size_t size(const tpctypes::BufferWrite &W) {
|
||||
return SPSTuple<SPSExecutorAddress, SPSSequence<char>>::AsArgList::size(
|
||||
W.Address, W.Buffer);
|
||||
}
|
||||
|
||||
static bool serialize(SPSOutputBuffer &OB, const tpctypes::BufferWrite &W) {
|
||||
return SPSTuple<SPSExecutorAddress,
|
||||
SPSSequence<char>>::AsArgList ::serialize(OB, W.Address,
|
||||
W.Buffer);
|
||||
}
|
||||
|
||||
static bool deserialize(SPSInputBuffer &IB, tpctypes::BufferWrite &W) {
|
||||
return SPSTuple<SPSExecutorAddress,
|
||||
SPSSequence<char>>::AsArgList ::deserialize(IB, W.Address,
|
||||
W.Buffer);
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace shared
|
||||
} // end namespace orc
|
||||
} // end namespace llvm
|
||||
|
||||
|
||||
@@ -486,7 +486,7 @@ public:
|
||||
}
|
||||
|
||||
auto SendSerializedResult = [SDR = std::move(SendDeserializedResult)](
|
||||
WrapperFunctionResult R) {
|
||||
WrapperFunctionResult R) mutable {
|
||||
RetT RetVal = detail::ResultDeserializer<SPSRetTagT, RetT>::makeValue();
|
||||
detail::ResultDeserializer<SPSRetTagT, RetT>::makeSafe(RetVal);
|
||||
|
||||
@@ -554,8 +554,10 @@ public:
|
||||
const ArgTs &...Args) {
|
||||
WrapperFunction<SPSEmpty(SPSTagTs...)>::callAsync(
|
||||
Caller,
|
||||
[SDR = std::move(SendDeserializedResult)](
|
||||
Error SerializeErr, SPSEmpty E) { SDR(std::move(SerializeErr)); },
|
||||
[SDR = std::move(SendDeserializedResult)](Error SerializeErr,
|
||||
SPSEmpty E) mutable {
|
||||
SDR(std::move(SerializeErr));
|
||||
},
|
||||
Args...);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ add_llvm_component_library(LLVMOrcJIT
|
||||
EPCDynamicLibrarySearchGenerator.cpp
|
||||
EPCDebugObjectRegistrar.cpp
|
||||
EPCEHFrameRegistrar.cpp
|
||||
EPCGenericMemoryAccess.cpp
|
||||
EPCIndirectionUtils.cpp
|
||||
ExecutionUtils.cpp
|
||||
IndirectionUtils.cpp
|
||||
|
||||
44
llvm/lib/ExecutionEngine/Orc/EPCGenericMemoryAccess.cpp
Normal file
44
llvm/lib/ExecutionEngine/Orc/EPCGenericMemoryAccess.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
//===----- EPCGenericMemoryAccess.cpp - Generic EPC MemoryAccess impl -----===//
|
||||
//
|
||||
// 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/EPCGenericMemoryAccess.h"
|
||||
#include "llvm/ExecutionEngine/Orc/LookupAndRecordAddrs.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace orc {
|
||||
|
||||
/// Create from a ExecutorProcessControl instance.
|
||||
Expected<std::unique_ptr<EPCGenericMemoryAccess>>
|
||||
EPCGenericMemoryAccess::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_write_uint8s_wrapper").str()),
|
||||
&FAs.WriteUInt8s},
|
||||
{ES.intern((GlobalPrefix + "__orc_rt_write_uint16s_wrapper").str()),
|
||||
&FAs.WriteUInt16s},
|
||||
{ES.intern((GlobalPrefix + "__orc_rt_write_uint32s_wrapper").str()),
|
||||
&FAs.WriteUInt32s},
|
||||
{ES.intern((GlobalPrefix + "__orc_rt_write_uint64s_wrapper").str()),
|
||||
&FAs.WriteUInt64s},
|
||||
{ES.intern((GlobalPrefix + "__orc_rt_write_buffers_wrapper").str()),
|
||||
&FAs.WriteBuffers}}))
|
||||
return std::move(Err);
|
||||
|
||||
return std::make_unique<EPCGenericMemoryAccess>(
|
||||
ES.getExecutorProcessControl(), FAs);
|
||||
}
|
||||
|
||||
} // end namespace orc
|
||||
} // end namespace llvm
|
||||
@@ -17,6 +17,7 @@ set(LLVM_LINK_COMPONENTS
|
||||
add_llvm_unittest(OrcJITTests
|
||||
CoreAPIsTest.cpp
|
||||
ExecutionSessionWrapperFunctionCallsTest.cpp
|
||||
EPCGenericMemoryAccessTest.cpp
|
||||
IndirectionUtilsTest.cpp
|
||||
JITTargetMachineBuilderTest.cpp
|
||||
LazyCallThroughAndReexportsTest.cpp
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
//===- EPCGenericMemoryAccessTest.cpp -- Tests for EPCGenericMemoryAccess -===//
|
||||
//
|
||||
// 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/EPCGenericMemoryAccess.h"
|
||||
#include "llvm/Testing/Support/Error.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::orc;
|
||||
using namespace llvm::orc::shared;
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename WriteT, typename SPSWriteT>
|
||||
llvm::orc::shared::detail::CWrapperFunctionResult
|
||||
testWriteUInts(const char *ArgData, size_t ArgSize) {
|
||||
return WrapperFunction<void(SPSSequence<SPSWriteT>)>::handle(
|
||||
ArgData, ArgSize,
|
||||
[](std::vector<WriteT> Ws) {
|
||||
for (auto &W : Ws)
|
||||
*jitTargetAddressToPointer<decltype(W.Value) *>(W.Address) =
|
||||
W.Value;
|
||||
})
|
||||
.release();
|
||||
}
|
||||
|
||||
llvm::orc::shared::detail::CWrapperFunctionResult
|
||||
testWriteBuffers(const char *ArgData, size_t ArgSize) {
|
||||
return WrapperFunction<void(SPSSequence<SPSMemoryAccessBufferWrite>)>::handle(
|
||||
ArgData, ArgSize,
|
||||
[](std::vector<tpctypes::BufferWrite> Ws) {
|
||||
for (auto &W : Ws)
|
||||
memcpy(jitTargetAddressToPointer<char *>(W.Address),
|
||||
W.Buffer.data(), W.Buffer.size());
|
||||
})
|
||||
.release();
|
||||
}
|
||||
|
||||
TEST(EPCGenericMemoryAccessTest, MemWrites) {
|
||||
auto SelfEPC = cantFail(SelfExecutorProcessControl::Create());
|
||||
|
||||
EPCGenericMemoryAccess::FuncAddrs FAs;
|
||||
FAs.WriteUInt8s = ExecutorAddress::fromPtr(
|
||||
&testWriteUInts<tpctypes::UInt8Write, SPSMemoryAccessUInt8Write>);
|
||||
FAs.WriteUInt16s = ExecutorAddress::fromPtr(
|
||||
&testWriteUInts<tpctypes::UInt16Write, SPSMemoryAccessUInt16Write>);
|
||||
FAs.WriteUInt32s = ExecutorAddress::fromPtr(
|
||||
&testWriteUInts<tpctypes::UInt32Write, SPSMemoryAccessUInt32Write>);
|
||||
FAs.WriteUInt64s = ExecutorAddress::fromPtr(
|
||||
&testWriteUInts<tpctypes::UInt64Write, SPSMemoryAccessUInt64Write>);
|
||||
FAs.WriteBuffers = ExecutorAddress::fromPtr(&testWriteBuffers);
|
||||
|
||||
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;
|
||||
char Test_Buffer[21];
|
||||
|
||||
auto Err1 = MemAccess->writeUInt8s(
|
||||
{{pointerToJITTargetAddress(&Test_UInt8_1), 1},
|
||||
{pointerToJITTargetAddress(&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({{pointerToJITTargetAddress(&Test_UInt16), 1}});
|
||||
EXPECT_THAT_ERROR(std::move(Err2), Succeeded());
|
||||
EXPECT_EQ(Test_UInt16, 1U);
|
||||
|
||||
auto Err3 =
|
||||
MemAccess->writeUInt32s({{pointerToJITTargetAddress(&Test_UInt32), 1}});
|
||||
EXPECT_THAT_ERROR(std::move(Err3), Succeeded());
|
||||
EXPECT_EQ(Test_UInt32, 1U);
|
||||
|
||||
auto Err4 =
|
||||
MemAccess->writeUInt64s({{pointerToJITTargetAddress(&Test_UInt64), 1}});
|
||||
EXPECT_THAT_ERROR(std::move(Err4), Succeeded());
|
||||
EXPECT_EQ(Test_UInt64, 1U);
|
||||
|
||||
StringRef TestMsg("test-message");
|
||||
auto Err5 = MemAccess->writeBuffers(
|
||||
{{pointerToJITTargetAddress(&Test_Buffer), TestMsg}});
|
||||
EXPECT_THAT_ERROR(std::move(Err5), Succeeded());
|
||||
EXPECT_EQ(StringRef(Test_Buffer, TestMsg.size()), TestMsg);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user