Implement non-void indirect Reactor Call() support
Previously the return value of the function called through the
Pointer<Byte> argument was not returned from the rr::Call(). This
change implements proper support for both non-void and void returning
function signatures.
This change also adds a unit test which demonstrates the use of a
'trampoline', which makes use of the new non-void indirect call.
Bug: b/175830790
Change-Id: I100588c6319d0c45797c48a068e1e46fa9f441ba
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/51308
Presubmit-Ready: Nicolas Capens <nicolascapens@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Tested-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
diff --git a/src/Reactor/Reactor.hpp b/src/Reactor/Reactor.hpp
index d3e1d5a..40a596a 100644
--- a/src/Reactor/Reactor.hpp
+++ b/src/Reactor/Reactor.hpp
@@ -3392,10 +3392,52 @@
CastToReactor(std::forward<RArgs>(args))...);
}
-// Calls the Reactor function pointer fptr with the signature
-// FUNCTION_SIGNATURE and arguments.
+// NonVoidFunction<F> and VoidFunction<F> are helper classes which define ReturnType
+// when F matches a non-void fuction signature or void function signature, respectively,
+// as the function's return type.
+template<typename F>
+struct NonVoidFunction
+{};
+
+template<typename Return, typename... Arguments>
+struct NonVoidFunction<Return(Arguments...)>
+{
+ using ReturnType = Return;
+};
+
+template<typename... Arguments>
+struct NonVoidFunction<void(Arguments...)>
+{
+};
+
+template<typename F>
+using NonVoidFunctionReturnType = typename NonVoidFunction<F>::ReturnType;
+
+template<typename F>
+struct VoidFunction
+{};
+
+template<typename... Arguments>
+struct VoidFunction<void(Arguments...)>
+{
+ using ReturnType = void;
+};
+
+template<typename F>
+using VoidFunctionReturnType = typename VoidFunction<F>::ReturnType;
+
+// Calls the Reactor function pointer fptr with the signature FUNCTION_SIGNATURE and arguments.
+// Overload for calling functions with non-void return type.
template<typename FUNCTION_SIGNATURE, typename... RArgs>
-inline void Call(Pointer<Byte> fptr, RArgs &&... args)
+inline CToReactorT<NonVoidFunctionReturnType<FUNCTION_SIGNATURE>> Call(Pointer<Byte> fptr, RArgs &&... args)
+{
+ return CallHelper<FUNCTION_SIGNATURE>::Call(fptr, CastToReactor(std::forward<RArgs>(args))...);
+}
+
+// Calls the Reactor function pointer fptr with the signature FUNCTION_SIGNATURE and arguments.
+// Overload for calling functions with void return type.
+template<typename FUNCTION_SIGNATURE, typename... RArgs>
+inline VoidFunctionReturnType<FUNCTION_SIGNATURE> Call(Pointer<Byte> fptr, RArgs &&... args)
{
CallHelper<FUNCTION_SIGNATURE>::Call(fptr, CastToReactor(std::forward<RArgs>(args))...);
}
diff --git a/tests/ReactorUnitTests/ReactorUnitTests.cpp b/tests/ReactorUnitTests/ReactorUnitTests.cpp
index 1f01e7c..fb7da84 100644
--- a/tests/ReactorUnitTests/ReactorUnitTests.cpp
+++ b/tests/ReactorUnitTests/ReactorUnitTests.cpp
@@ -27,7 +27,7 @@
using namespace rr;
-std::string testName()
+static std::string testName()
{
auto info = ::testing::UnitTest::GetInstance()->current_test_info();
return std::string{ info->test_suite_name() } + "_" + info->name();
@@ -78,6 +78,66 @@
EXPECT_EQ(result, reference(&one[1], 2));
}
+// This test demonstrates the use of a 'trampoline', where a routine calls
+// a static function which then generates another routine during the execution
+// of the first routine. Also note the code generated for the second routine
+// depends on a parameter passed to the first routine.
+TEST(ReactorUnitTests, Trampoline)
+{
+ using SecondaryFunc = int(int, int);
+
+ static auto generateSecondary = [](int upDown) {
+ FunctionT<SecondaryFunc> secondary;
+ {
+ Int x = secondary.Arg<0>();
+ Int y = secondary.Arg<1>();
+ Int r;
+
+ if(upDown > 0)
+ {
+ r = x + y;
+ }
+ else if(upDown < 0)
+ {
+ r = x - y;
+ }
+ else
+ {
+ r = 0;
+ }
+
+ Return(r);
+ }
+
+ static auto routine = secondary((testName() + "_secondary").c_str());
+ return routine.getEntry();
+ };
+
+ using SecondaryGeneratorFunc = SecondaryFunc *(*)(int);
+ SecondaryGeneratorFunc secondaryGenerator = (SecondaryGeneratorFunc)generateSecondary;
+
+ using PrimaryFunc = int(int, int, int);
+ RoutineT<PrimaryFunc> routine;
+ {
+ FunctionT<PrimaryFunc> primary;
+ {
+ Int x = primary.Arg<0>();
+ Int y = primary.Arg<1>();
+ Int z = primary.Arg<2>();
+
+ Pointer<Byte> secondary = Call(secondaryGenerator, z);
+ Int r = Call<SecondaryFunc>(secondary, x, y);
+
+ Return(r);
+ }
+
+ routine = primary((testName() + "_primary").c_str());
+ }
+
+ int result = routine(100, 20, -3);
+ EXPECT_EQ(result, 80);
+}
+
TEST(ReactorUnitTests, Uninitialized)
{
FunctionT<int()> function;