Add unit tests for arithmetic ops with constant args

Bug: b/120208871
Change-Id: If777ca4d4df189b1b7fd67ffe572044c8d0508c0
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/49008
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Tested-by: Antonio Maiorano <amaiorano@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Reactor/ReactorUnitTests.cpp b/src/Reactor/ReactorUnitTests.cpp
index d6e57c0..cc6f0c9 100644
--- a/src/Reactor/ReactorUnitTests.cpp
+++ b/src/Reactor/ReactorUnitTests.cpp
@@ -3061,6 +3061,92 @@
 #endif
 }
 
+// Test constant <op> variable
+template<typename T, typename Func>
+T Arithmetic_LhsConstArg(T arg1, T arg2, Func f)
+{
+	using ReactorT = CToReactorT<T>;
+
+	FunctionT<T(T)> function;
+	{
+		ReactorT lhs = arg1;
+		ReactorT rhs = function.template Arg<0>();
+		ReactorT result = f(lhs, rhs);
+		Return(result);
+	}
+
+	auto routine = function("one");
+	return routine(arg2);
+}
+
+// Test variable <op> constant
+template<typename T, typename Func>
+T Arithmetic_RhsConstArg(T arg1, T arg2, Func f)
+{
+	using ReactorT = CToReactorT<T>;
+
+	FunctionT<T(T)> function;
+	{
+		ReactorT lhs = function.template Arg<0>();
+		ReactorT rhs = arg2;
+		ReactorT result = f(lhs, rhs);
+		Return(result);
+	}
+
+	auto routine = function("one");
+	return routine(arg1);
+}
+
+// Test constant <op> constant
+template<typename T, typename Func>
+T Arithmetic_TwoConstArgs(T arg1, T arg2, Func f)
+{
+	using ReactorT = CToReactorT<T>;
+
+	FunctionT<T()> function;
+	{
+		ReactorT lhs = arg1;
+		ReactorT rhs = arg2;
+		ReactorT result = f(lhs, rhs);
+		Return(result);
+	}
+
+	auto routine = function("one");
+	return routine();
+}
+
+template<typename T, typename Func>
+void Arithmetic_ConstArgs(T arg1, T arg2, T expected, Func f)
+{
+	SCOPED_TRACE(std::to_string(arg1) + " <op> " + std::to_string(arg2) + " = " + std::to_string(expected));
+	T result{};
+	result = Arithmetic_LhsConstArg(arg1, arg2, std::forward<Func>(f));
+	EXPECT_EQ(result, expected);
+	result = Arithmetic_RhsConstArg(arg1, arg2, std::forward<Func>(f));
+	EXPECT_EQ(result, expected);
+	result = Arithmetic_TwoConstArgs(arg1, arg2, std::forward<Func>(f));
+	EXPECT_EQ(result, expected);
+}
+
+// Test that we generate valid code for when one or both args to arithmetic operations
+// are constant. In particular, we want to validate the case for two const args, as
+// often lowered instructions do not support this case.
+TEST(ReactorUnitTests, Arithmetic_ConstantArgs)
+{
+	Arithmetic_ConstArgs(2, 3, 5, [](auto c1, auto c2) { return c1 + c2; });
+	Arithmetic_ConstArgs(5, 3, 2, [](auto c1, auto c2) { return c1 - c2; });
+	Arithmetic_ConstArgs(2, 3, 6, [](auto c1, auto c2) { return c1 * c2; });
+	Arithmetic_ConstArgs(6, 3, 2, [](auto c1, auto c2) { return c1 / c2; });
+	Arithmetic_ConstArgs(0xF0F0, 0xAAAA, 0xA0A0, [](auto c1, auto c2) { return c1 & c2; });
+	Arithmetic_ConstArgs(0xF0F0, 0xAAAA, 0xFAFA, [](auto c1, auto c2) { return c1 | c2; });
+	Arithmetic_ConstArgs(0xF0F0, 0xAAAA, 0x5A5A, [](auto c1, auto c2) { return c1 ^ c2; });
+
+	Arithmetic_ConstArgs(2.f, 3.f, 5.f, [](auto c1, auto c2) { return c1 + c2; });
+	Arithmetic_ConstArgs(5.f, 3.f, 2.f, [](auto c1, auto c2) { return c1 - c2; });
+	Arithmetic_ConstArgs(2.f, 3.f, 6.f, [](auto c1, auto c2) { return c1 * c2; });
+	Arithmetic_ConstArgs(6.f, 3.f, 2.f, [](auto c1, auto c2) { return c1 / c2; });
+}
+
 int main(int argc, char **argv)
 {
 	::testing::InitGoogleTest(&argc, argv);