[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:
Lang Hames
2021-08-20 15:52:42 +10:00
parent 4fc98ca617
commit 7f99337f9b
11 changed files with 323 additions and 14 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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...);
}

View File

@@ -7,6 +7,7 @@ add_llvm_component_library(LLVMOrcJIT
EPCDynamicLibrarySearchGenerator.cpp
EPCDebugObjectRegistrar.cpp
EPCEHFrameRegistrar.cpp
EPCGenericMemoryAccess.cpp
EPCIndirectionUtils.cpp
ExecutionUtils.cpp
IndirectionUtils.cpp

View 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

View File

@@ -17,6 +17,7 @@ set(LLVM_LINK_COMPONENTS
add_llvm_unittest(OrcJITTests
CoreAPIsTest.cpp
ExecutionSessionWrapperFunctionCallsTest.cpp
EPCGenericMemoryAccessTest.cpp
IndirectionUtilsTest.cpp
JITTargetMachineBuilderTest.cpp
LazyCallThroughAndReexportsTest.cpp

View File

@@ -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