Finalize Reactor Function creation at Routine acquisition
This enables generating multiple functions within the same scope, as
excercised by the Trampoline unit test.
Also adds a unit test for functions which are never finalized (useful
for when a code path is not supported and we want to fall back to a C++
implementation).
Also adds a unit test for the common pattern of deriving from Function<>
and using Reactor variables as members.
Bug: b/177999324
Change-Id: I269946e9c8a791eab84ff756c9c588ca8ce26337
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/51988
Tested-by: Nicolas Capens <nicolascapens@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
diff --git a/src/Reactor/Coroutine.hpp b/src/Reactor/Coroutine.hpp
index 3375787..a28f765 100644
--- a/src/Reactor/Coroutine.hpp
+++ b/src/Reactor/Coroutine.hpp
@@ -154,9 +154,8 @@
template<typename Return, typename... Arguments>
Coroutine<Return(Arguments...)>::Coroutine()
+ : core(new Nucleus())
{
- core.reset(new Nucleus());
-
std::vector<Type *> types = { CToReactorT<Arguments>::type()... };
for(auto type : types)
{
diff --git a/src/Reactor/Reactor.cpp b/src/Reactor/Reactor.cpp
index 0853519..3c4a146 100644
--- a/src/Reactor/Reactor.cpp
+++ b/src/Reactor/Reactor.cpp
@@ -124,7 +124,13 @@
Variable::~Variable()
{
- unmaterializedVariables->remove(this);
+ // `unmaterializedVariables` can be null at this point due to the function
+ // already having been finalized, while classes derived from `Function<>`
+ // can have member `Variable` fields which are destructed afterwards.
+ if(unmaterializedVariables)
+ {
+ unmaterializedVariables->remove(this);
+ }
}
void Variable::materialize() const
diff --git a/src/Reactor/Reactor.hpp b/src/Reactor/Reactor.hpp
index 40a596a..4871c6e 100644
--- a/src/Reactor/Reactor.hpp
+++ b/src/Reactor/Reactor.hpp
@@ -2610,8 +2610,6 @@
public:
Function();
- virtual ~Function();
-
template<int index>
Argument<typename std::tuple_element<index, std::tuple<Arguments...>>::type> Arg() const
{
@@ -2623,7 +2621,7 @@
std::shared_ptr<Routine> operator()(const Config::Edit &cfg, const char *name, ...);
protected:
- Nucleus *core;
+ std::unique_ptr<Nucleus> core;
std::vector<Type *> arguments;
};
@@ -3175,9 +3173,8 @@
template<typename Return, typename... Arguments>
Function<Return(Arguments...)>::Function()
+ : core(new Nucleus())
{
- core = new Nucleus();
-
Type *types[] = { Arguments::type()... };
for(Type *type : types)
{
@@ -3191,12 +3188,6 @@
}
template<typename Return, typename... Arguments>
-Function<Return(Arguments...)>::~Function()
-{
- delete core;
-}
-
-template<typename Return, typename... Arguments>
std::shared_ptr<Routine> Function<Return(Arguments...)>::operator()(const char *name, ...)
{
char fullName[1024 + 1];
@@ -3206,7 +3197,10 @@
vsnprintf(fullName, 1024, name, vararg);
va_end(vararg);
- return core->acquireRoutine(fullName, Config::Edit::None);
+ auto routine = core->acquireRoutine(fullName, Config::Edit::None);
+ core.reset(nullptr);
+
+ return routine;
}
template<typename Return, typename... Arguments>
@@ -3219,7 +3213,10 @@
vsnprintf(fullName, 1024, name, vararg);
va_end(vararg);
- return core->acquireRoutine(fullName, cfg);
+ auto routine = core->acquireRoutine(fullName, cfg);
+ core.reset(nullptr);
+
+ return routine;
}
template<class T, class S>
diff --git a/tests/ReactorUnitTests/ReactorUnitTests.cpp b/tests/ReactorUnitTests/ReactorUnitTests.cpp
index fb7da84..b615756 100644
--- a/tests/ReactorUnitTests/ReactorUnitTests.cpp
+++ b/tests/ReactorUnitTests/ReactorUnitTests.cpp
@@ -117,23 +117,21 @@
SecondaryGeneratorFunc secondaryGenerator = (SecondaryGeneratorFunc)generateSecondary;
using PrimaryFunc = int(int, int, int);
- RoutineT<PrimaryFunc> routine;
+
+ FunctionT<PrimaryFunc> primary;
{
- FunctionT<PrimaryFunc> primary;
- {
- Int x = primary.Arg<0>();
- Int y = primary.Arg<1>();
- Int z = primary.Arg<2>();
+ 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);
+ Pointer<Byte> secondary = Call(secondaryGenerator, z);
+ Int r = Call<SecondaryFunc>(secondary, x, y);
- Return(r);
- }
-
- routine = primary((testName() + "_primary").c_str());
+ Return(r);
}
+ auto routine = primary((testName() + "_primary").c_str());
+
int result = routine(100, 20, -3);
EXPECT_EQ(result, 80);
}
@@ -202,6 +200,72 @@
EXPECT_EQ(result, 20);
}
+// Stopping in the middle of a `Function<>` is supported and should not affect
+// subsequent complete ones.
+TEST(ReactorUnitTests, UnfinishedFunction)
+{
+ do
+ {
+ FunctionT<int(int)> function;
+ {
+ Int a = function.Arg<0>();
+ Int z = 4;
+
+ break; // Terminate do-while early.
+
+ Return(a + z);
+ }
+ } while(true);
+
+ FunctionT<int(int)> function;
+ {
+ Int a = function.Arg<0>();
+ Int z = 4;
+
+ Return(a - z);
+ }
+
+ auto routine = function(testName().c_str());
+
+ int result = routine(16);
+ EXPECT_EQ(result, 12);
+}
+
+// Deriving from `Function<>` and using Reactor variables as members can be a
+// convenient way to 'name' function arguments and compose complex functions
+// with helper methods. This test checks the interactions between the lifetime
+// of the `Function<>` and the variables belonging to the derived class.
+struct FunctionMembers : FunctionT<int(int)>
+{
+ FunctionMembers()
+ : level(Arg<0>())
+ {
+ For(Int i = 0, i < 3, i++)
+ {
+ pourSomeMore();
+ }
+
+ Return(level);
+ }
+
+ void pourSomeMore()
+ {
+ level += 2;
+ }
+
+ Int level;
+};
+
+TEST(ReactorUnitTests, FunctionMembers)
+{
+ FunctionMembers function;
+
+ auto routine = function(testName().c_str());
+
+ int result = routine(3);
+ EXPECT_EQ(result, 9);
+}
+
TEST(ReactorUnitTests, VariableAddress)
{
FunctionT<int(int)> function;