Skip to content

Commit 1a09ac5

Browse files
authored
[ORC] Add CallViaEPC and CallSPSViaEPC utilities. (#170464)
These utilities simplify making typed async calls via ExecutorProcessControl::callWrapperAsync. CallViaEPC.h provides utilities for making typed async calls using a given Serializer to serialize arguments and deserialize results. callViaEPC takes a result handler function object (accepting Expected<T>), an EPC reference, a Serializer, a target function address, and arguments. The return type T is inferred from the handler's argument type using CallableTraitsHelper. EPCCaller wraps an ExecutorProcessControl& and Serializer, simplifying repeated calls with the same serialization. EPCCall wraps an EPCCaller and target function address, simplifying repeated calls to a specific wrapper function. CallSPSViaEPC.h provides utilities for using CallViaEPC with SPS serialization. SPSCallSerializer is a CallViaEPC-compatible serializer using SPS. SPSEPCCaller takes an SPS function signature as its template argument and applies SPS serialization to arguments and return values. SPSEPCCall wraps an SPSCaller and a target function address, simplifying repeated calls to a specific SPS wrapper function.
1 parent d7fb086 commit 1a09ac5

File tree

4 files changed

+272
-0
lines changed

4 files changed

+272
-0
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//===---- CallSPSViaEPC.h - EPCCalls using SPS serialization ----*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// EPCCalls using SimplePackedSerialization.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_EXECUTIONENGINE_ORC_CALLSPSVIAEPC_H
14+
#define LLVM_EXECUTIONENGINE_ORC_CALLSPSVIAEPC_H
15+
16+
#include "llvm/ExecutionEngine/Orc/CallViaEPC.h"
17+
#include "llvm/ExecutionEngine/Orc/CallableTraitsHelper.h"
18+
#include "llvm/ExecutionEngine/Orc/Shared/SimplePackedSerialization.h"
19+
20+
namespace llvm::orc {
21+
22+
namespace detail {
23+
template <typename RetT, typename... ArgTs> struct SPSCallSerializationImpl {
24+
using RetSerialization = shared::SPSArgList<RetT>;
25+
using ArgSerialization = shared::SPSArgList<ArgTs...>;
26+
};
27+
} // namespace detail
28+
29+
template <typename SPSSig>
30+
struct SPSCallSerialization
31+
: public CallableTraitsHelper<detail::SPSCallSerializationImpl, SPSSig> {};
32+
33+
template <typename SPSSig> class SPSCallSerializer {
34+
public:
35+
template <typename... ArgTs>
36+
Expected<shared::WrapperFunctionResult> serialize(ArgTs &&...Args) {
37+
using ArgSerialization =
38+
typename SPSCallSerialization<SPSSig>::ArgSerialization;
39+
auto Buffer = shared::WrapperFunctionResult::allocate(
40+
ArgSerialization::size(Args...));
41+
shared::SPSOutputBuffer OB(Buffer.data(), Buffer.size());
42+
if (!ArgSerialization::serialize(OB, Args...))
43+
return make_error<StringError>("Could not serialize arguments",
44+
inconvertibleErrorCode());
45+
return std::move(Buffer);
46+
}
47+
48+
template <typename RetT>
49+
Expected<RetT> deserialize(shared::WrapperFunctionResult ResultBytes) {
50+
using RetDeserialization =
51+
typename SPSCallSerialization<SPSSig>::RetSerialization;
52+
shared::SPSInputBuffer IB(ResultBytes.data(), ResultBytes.size());
53+
RetT ReturnValue;
54+
if (!RetDeserialization::deserialize(IB, ReturnValue))
55+
return make_error<StringError>("Could not deserialize return value",
56+
inconvertibleErrorCode());
57+
return ReturnValue;
58+
}
59+
};
60+
61+
template <typename SPSSig>
62+
class SPSEPCCaller : public EPCCaller<SPSCallSerializer<SPSSig>> {
63+
public:
64+
SPSEPCCaller(ExecutorProcessControl &EPC)
65+
: EPCCaller<SPSCallSerializer<SPSSig>>(EPC, SPSCallSerializer<SPSSig>()) {
66+
}
67+
};
68+
69+
template <typename SPSSig>
70+
class SPSEPCCall : public EPCCall<SPSCallSerializer<SPSSig>> {
71+
public:
72+
SPSEPCCall(ExecutorProcessControl &EPC, ExecutorSymbolDef Fn)
73+
: EPCCall<SPSCallSerializer<SPSSig>>(EPC, SPSCallSerializer<SPSSig>(),
74+
std::move(Fn)) {}
75+
};
76+
77+
} // namespace llvm::orc
78+
79+
#endif // LLVM_EXECUTIONENGINE_ORC_CALLSPSVIAEPC_H
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
//===------ CallViaEPC.h - Call wrapper functions via EPC -------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Call executor functions with common signatures via
10+
// ExecutorProcessControl::callWrapperAsync.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#ifndef LLVM_EXECUTIONENGINE_ORC_CALLVIAEPC_H
15+
#define LLVM_EXECUTIONENGINE_ORC_CALLVIAEPC_H
16+
17+
#include "llvm/ExecutionEngine/Orc/CallableTraitsHelper.h"
18+
#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
19+
20+
namespace llvm::orc {
21+
22+
namespace detail {
23+
24+
// Helper to extract the Expected<T> argument type from a handler callable.
25+
template <typename HandlerT> struct HandlerTraits {
26+
using ArgInfo = CallableArgInfo<HandlerT>;
27+
using ArgsTuple = typename ArgInfo::ArgsTupleType;
28+
static_assert(std::tuple_size_v<ArgsTuple> == 1,
29+
"Handler must take exactly one argument");
30+
using ExpectedArgType = std::tuple_element_t<0, ArgsTuple>;
31+
using RetT = typename std::remove_cv_t<
32+
std::remove_reference_t<ExpectedArgType>>::value_type;
33+
};
34+
35+
} // namespace detail
36+
37+
/// Call a wrapper function via EPC.
38+
template <typename HandlerT, typename Serializer, typename... ArgTs>
39+
void callViaEPC(HandlerT &&H, ExecutorProcessControl &EPC, Serializer S,
40+
ExecutorSymbolDef Fn, ArgTs &&...Args) {
41+
using RetT = typename detail::HandlerTraits<HandlerT>::RetT;
42+
43+
if (auto ArgBytes = S.serialize(std::forward<ArgTs>(Args)...))
44+
EPC.callWrapperAsync(
45+
Fn.getAddress(),
46+
[S = std::move(S), H = std::forward<HandlerT>(H)](
47+
shared::WrapperFunctionResult R) mutable {
48+
if (const char *ErrMsg = R.getOutOfBandError())
49+
H(make_error<StringError>(ErrMsg, inconvertibleErrorCode()));
50+
else
51+
H(S.template deserialize<RetT>(std::move(R)));
52+
},
53+
{ArgBytes->data(), ArgBytes->size()});
54+
else
55+
H(ArgBytes.takeError());
56+
}
57+
58+
/// Encapsulates calls via EPC to any function that's compatible with the given
59+
/// serialization scheme.
60+
template <typename Serializer> class EPCCaller {
61+
public:
62+
EPCCaller(ExecutorProcessControl &EPC, Serializer &&S)
63+
: EPC(EPC), S(std::move(S)) {}
64+
65+
// TODO: Add an ExecutionSession constructor once ExecutionSession has been
66+
// moved to its own header.
67+
68+
// Async call version.
69+
template <typename HandlerT, typename... ArgTs>
70+
void operator()(HandlerT &&H, ExecutorSymbolDef Fn, ArgTs &&...Args) {
71+
callViaEPC(std::forward<HandlerT>(H), EPC, S, Fn,
72+
std::forward<ArgTs>(Args)...);
73+
}
74+
75+
private:
76+
ExecutorProcessControl &EPC;
77+
Serializer S;
78+
};
79+
80+
/// Encapsulates calls via EPC to a specific function, using the given
81+
/// serialization scheme.
82+
template <typename Serializer> class EPCCall {
83+
public:
84+
EPCCall(ExecutorProcessControl &EPC, Serializer &&S, ExecutorSymbolDef Fn)
85+
: Caller(EPC, std::move(S)), Fn(std::move(Fn)) {}
86+
87+
// TODO: Add an ExecutionSession constructor once ExecutionSession has been
88+
// moved to its own header.
89+
90+
template <typename HandlerT, typename... ArgTs>
91+
void operator()(HandlerT &&H, ArgTs &&...Args) {
92+
Caller(std::forward<HandlerT>(H), Fn, std::forward<ArgTs>(Args)...);
93+
}
94+
95+
private:
96+
EPCCaller<Serializer> Caller;
97+
ExecutorSymbolDef Fn;
98+
};
99+
100+
} // namespace llvm::orc
101+
102+
#endif // LLVM_EXECUTIONENGINE_ORC_CALLVIAEPC_H

llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ set(LLVM_LINK_COMPONENTS
1919

2020
add_llvm_unittest(OrcJITTests
2121
CallableTraitsHelperTest.cpp
22+
CallSPSViaEPCTest.cpp
2223
CoreAPIsTest.cpp
2324
ExecutorAddressTest.cpp
2425
ExecutionSessionWrapperFunctionCallsTest.cpp
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//===----------- CallSPSViaEPC.cpp - Test CallSPSViaEPC.h APIs ------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "llvm/ExecutionEngine/Orc/CallSPSViaEPC.h"
10+
#include "llvm/ExecutionEngine/Orc/SelfExecutorProcessControl.h"
11+
12+
#include "llvm/Testing/Support/Error.h"
13+
14+
#include "gtest/gtest.h"
15+
16+
using namespace llvm;
17+
using namespace llvm::orc;
18+
using namespace llvm::orc::shared;
19+
20+
static CWrapperFunctionResult mainWrapper(const char *ArgData, size_t ArgSize) {
21+
return WrapperFunction<int32_t(SPSSequence<SPSString>)>::handle(
22+
ArgData, ArgSize,
23+
[](std::vector<std::string> Args) -> int32_t {
24+
return Args.size();
25+
})
26+
.release();
27+
}
28+
29+
TEST(CallSPSViaEPCTest, CallMainViaCaller) {
30+
auto EPC = cantFail(SelfExecutorProcessControl::Create());
31+
SPSEPCCaller<int32_t(SPSSequence<SPSString>)> C(*EPC);
32+
std::vector<std::string> Args;
33+
34+
std::optional<Expected<int32_t>> R1;
35+
C([&](Expected<int32_t> R) { R1 = std::move(R); },
36+
ExecutorSymbolDef::fromPtr(mainWrapper), Args);
37+
ASSERT_THAT_EXPECTED(*R1, Succeeded());
38+
EXPECT_EQ(**R1, 0);
39+
40+
Args.push_back("foo");
41+
std::optional<Expected<int32_t>> R2;
42+
C([&](Expected<int32_t> R) { R2 = std::move(R); },
43+
ExecutorSymbolDef::fromPtr(mainWrapper), Args);
44+
ASSERT_THAT_EXPECTED(*R2, Succeeded());
45+
EXPECT_EQ(**R2, 1);
46+
47+
Args.push_back("foo");
48+
std::optional<Expected<int32_t>> R3;
49+
C([&](Expected<int32_t> R) { R3 = std::move(R); },
50+
ExecutorSymbolDef::fromPtr(mainWrapper), Args);
51+
ASSERT_THAT_EXPECTED(*R3, Succeeded());
52+
EXPECT_EQ(**R3, 2);
53+
54+
Args.clear();
55+
std::optional<Expected<int32_t>> R4;
56+
C([&](Expected<int32_t> R) { R4 = std::move(R); },
57+
ExecutorSymbolDef::fromPtr(mainWrapper), Args);
58+
ASSERT_THAT_EXPECTED(*R4, Succeeded());
59+
EXPECT_EQ(**R4, 0);
60+
}
61+
62+
TEST(CallSPSViaEPCTest, CallMainViaGenericCall) {
63+
auto EPC = cantFail(SelfExecutorProcessControl::Create());
64+
SPSEPCCall<int32_t(SPSSequence<SPSString>)> C(
65+
*EPC, ExecutorSymbolDef::fromPtr(mainWrapper));
66+
std::vector<std::string> Args;
67+
68+
std::optional<Expected<int32_t>> R1;
69+
C([&](Expected<int32_t> R) { R1 = std::move(R); }, Args);
70+
ASSERT_THAT_EXPECTED(*R1, Succeeded());
71+
EXPECT_EQ(**R1, 0);
72+
73+
Args.push_back("foo");
74+
std::optional<Expected<int32_t>> R2;
75+
C([&](Expected<int32_t> R) { R2 = std::move(R); }, Args);
76+
ASSERT_THAT_EXPECTED(*R2, Succeeded());
77+
EXPECT_EQ(**R2, 1);
78+
79+
Args.push_back("foo");
80+
std::optional<Expected<int32_t>> R3;
81+
C([&](Expected<int32_t> R) { R3 = std::move(R); }, Args);
82+
ASSERT_THAT_EXPECTED(*R3, Succeeded());
83+
EXPECT_EQ(**R3, 2);
84+
85+
Args.clear();
86+
std::optional<Expected<int32_t>> R4;
87+
C([&](Expected<int32_t> R) { R4 = std::move(R); }, Args);
88+
ASSERT_THAT_EXPECTED(*R4, Succeeded());
89+
EXPECT_EQ(**R4, 0);
90+
}

0 commit comments

Comments
 (0)