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
--- /dev/null
+//===- 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
/// 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;
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.
/// 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
}
};
+/// 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.
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
}
auto SendSerializedResult = [SDR = std::move(SendDeserializedResult)](
- WrapperFunctionResult R) {
+ WrapperFunctionResult R) mutable {
RetT RetVal = detail::ResultDeserializer<SPSRetTagT, RetT>::makeValue();
detail::ResultDeserializer<SPSRetTagT, RetT>::makeSafe(RetVal);
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...);
}
EPCDynamicLibrarySearchGenerator.cpp
EPCDebugObjectRegistrar.cpp
EPCEHFrameRegistrar.cpp
+ EPCGenericMemoryAccess.cpp
EPCIndirectionUtils.cpp
ExecutionUtils.cpp
IndirectionUtils.cpp
--- /dev/null
+//===----- 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
add_llvm_unittest(OrcJITTests
CoreAPIsTest.cpp
ExecutionSessionWrapperFunctionCallsTest.cpp
+ EPCGenericMemoryAccessTest.cpp
IndirectionUtilsTest.cpp
JITTargetMachineBuilderTest.cpp
LazyCallThroughAndReexportsTest.cpp
--- /dev/null
+//===- 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