Reactor: Add ConstantData() and member function calling

rr::ConstantData() returns a Pointer<Byte> to an immutable copy of the provided data.

Added two new variants of rr::Call() for calling member function pointers.

Added tests.

Bug: b/143479561
Change-Id: I5846fb313fbd81821bf4e9bb655414a5e0eaf133
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/38408
Tested-by: Ben Clayton <bclayton@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
diff --git a/src/Reactor/LLVMReactor.cpp b/src/Reactor/LLVMReactor.cpp
index 57c9d5c..559df5b 100644
--- a/src/Reactor/LLVMReactor.cpp
+++ b/src/Reactor/LLVMReactor.cpp
@@ -3981,6 +3981,13 @@
 		return RValue<Pointer<Byte>>(V(jit->builder->CreateIntToPtr(ptrAsInt, T(Pointer<Byte>::getType()))));
 	}
 
+	RValue<Pointer<Byte>> ConstantData(void const * data, size_t size)
+	{
+		auto str = ::llvm::StringRef(reinterpret_cast<const char*>(data), size);
+		auto ptr = jit->builder->CreateGlobalStringPtr(str);
+		return RValue<Pointer<Byte>>(V(ptr));
+	}
+
 	Value* Call(RValue<Pointer<Byte>> fptr, Type* retTy, std::initializer_list<Value*> args, std::initializer_list<Type*> argTys)
 	{
 		::llvm::SmallVector<::llvm::Type*, 8> paramTys;
diff --git a/src/Reactor/Reactor.hpp b/src/Reactor/Reactor.hpp
index 9f93adf..b958b2d 100644
--- a/src/Reactor/Reactor.hpp
+++ b/src/Reactor/Reactor.hpp
@@ -2880,6 +2880,12 @@
 		return *this = rhs.operator RValue<Float4>();
 	}
 
+	// Returns a reactor pointer to the fixed-address ptr.
+	RValue<Pointer<Byte>> ConstantPointer(void const * ptr);
+
+	// Returns a reactor pointer to an immutable copy of the data of size bytes.
+	RValue<Pointer<Byte>> ConstantData(void const * data, size_t size);
+
 	template<class T>
 	Pointer<T>::Pointer(Argument<Pointer<T>> argument) : alignment(1)
 	{
@@ -3194,9 +3200,6 @@
 		return ReinterpretCast<T>(val);
 	}
 
-	// Returns a reactor pointer to the fixed-address ptr.
-	RValue<Pointer<Byte>> ConstantPointer(void const * ptr);
-
 	// Calls the function pointer fptr with the given arguments, return type
 	// and parameter types. Returns the call's return value if the function has
 	// a non-void return type.
@@ -3254,14 +3257,14 @@
 	template <typename T>
 	inline ReactorTypeT<T> CastToReactor(const T& v) { return ReactorType<T>::cast(v); }
 
-	// Calls the function pointer fptr with the given arguments args.
+	// Calls the static function pointer fptr with the given arguments args.
 	template<typename Return, typename ... CArgs, typename ... RArgs>
 	inline CToReactorT<Return> Call(Return(fptr)(CArgs...), RArgs&&... args)
 	{
 		return CallHelper<Return(CArgs...)>::Call(fptr, CastToReactor(std::forward<RArgs>(args))...);
 	}
 
-	// Calls the function pointer fptr with the given arguments args.
+	// Calls the static function pointer fptr with the given arguments args.
 	// Overload for calling functions with void return type.
 	template<typename ... CArgs, typename ... RArgs>
 	inline void Call(void(fptr)(CArgs...), RArgs&&... args)
@@ -3269,8 +3272,49 @@
 		CallHelper<void(CArgs...)>::Call(fptr, CastToReactor(std::forward<RArgs>(args))...);
 	}
 
-	// Calls the function pointer fptr with the signature FUNCTION_SIGNATURE and
-	// arguments.
+	// Calls the member function pointer fptr with the given arguments args.
+	// object can be a Class*, or a Pointer<Byte>.
+	template<typename Return, typename Class, typename C, typename ... CArgs, typename ... RArgs>
+	inline CToReactorT<Return> Call(Return(Class::* fptr)(CArgs...), C&& object, RArgs&&... args)
+	{
+		using Helper = CallHelper<Return(Class*, void*, CArgs...)>;
+		using fptrTy = decltype(fptr);
+		struct Static {
+			static inline Return Call(Class* object, void* fptrptr, CArgs... args)
+			{
+				auto fptr = *reinterpret_cast<fptrTy*>(fptrptr);
+				return (object->*fptr)(std::forward<CArgs>(args)...);
+			}
+		};
+		return Helper::Call(&Static::Call,
+		                    CastToReactor(object),
+		                    ConstantData(&fptr, sizeof(fptr)),
+		                    CastToReactor(std::forward<RArgs>(args))...);
+	}
+
+	// Calls the member function pointer fptr with the given arguments args.
+	// Overload for calling functions with void return type.
+	// object can be a Class*, or a Pointer<Byte>.
+	template<typename Class, typename C, typename ... CArgs, typename ... RArgs>
+	inline void Call(void(Class::* fptr)(CArgs...), C&& object, RArgs&&... args)
+	{
+		using Helper = CallHelper<void(Class*, void*, CArgs...)>;
+		using fptrTy = decltype(fptr);
+		struct Static {
+			static inline void Call(Class* object, void* fptrptr, CArgs... args)
+			{
+				auto fptr = *reinterpret_cast<fptrTy*>(fptrptr);
+				(object->*fptr)(std::forward<CArgs>(args)...);
+			}
+		};
+		Helper::Call(&Static::Call,
+		             CastToReactor(object),
+		             ConstantData(&fptr, sizeof(fptr)),
+		             CastToReactor(std::forward<RArgs>(args))...);
+	}
+
+	// Calls the Reactor function pointer fptr with the signature
+	// FUNCTION_SIGNATURE and arguments.
 	template<typename FUNCTION_SIGNATURE, typename ... RArgs>
 	inline void Call(Pointer<Byte> fptr, RArgs&& ... args)
 	{
diff --git a/src/Reactor/ReactorUnitTests.cpp b/src/Reactor/ReactorUnitTests.cpp
index 7677c23..55bbff1 100644
--- a/src/Reactor/ReactorUnitTests.cpp
+++ b/src/Reactor/ReactorUnitTests.cpp
@@ -1171,6 +1171,80 @@
 	EXPECT_EQ(c.f, 20.0f);
 }
 
+TEST(ReactorUnitTests, CallMemberFunction)
+{
+	if (!rr::Caps.CallSupported)
+	{
+		SUCCEED() << "rr::Call() not supported";
+		return;
+	}
+
+	struct Class
+	{
+		int Callback(int argI, float argF)
+		{
+			i = argI;
+			f = argF;
+			return i + int(f);
+		}
+
+		int i = 0;
+		float f = 0.0f;
+	};
+
+	Class c;
+
+	FunctionT<int()> function;
+	{
+		auto res = Call(&Class::Callback, &c, 10, 20.0f);
+		Return(res);
+	}
+
+	auto routine = function("one");
+
+	int res = routine();
+	EXPECT_EQ(res, 30);
+	EXPECT_EQ(c.i, 10);
+	EXPECT_EQ(c.f, 20.0f);
+}
+
+TEST(ReactorUnitTests, CallMemberFunctionIndirect)
+{
+	if (!rr::Caps.CallSupported)
+	{
+		SUCCEED() << "rr::Call() not supported";
+		return;
+	}
+
+	struct Class
+	{
+		int Callback(int argI, float argF)
+		{
+			i = argI;
+			f = argF;
+			return i + int(f);
+		}
+
+		int i = 0;
+		float f = 0.0f;
+	};
+
+	FunctionT<int(void*)> function;
+	{
+		Pointer<Byte> c = function.Arg<0>();
+		auto res = Call(&Class::Callback, c, 10, 20.0f);
+		Return(res);
+	}
+
+	auto routine = function("one");
+
+	Class c;
+	int res = routine(&c);
+	EXPECT_EQ(res, 30);
+	EXPECT_EQ(c.i, 10);
+	EXPECT_EQ(c.f, 20.0f);
+}
+
 TEST(ReactorUnitTests, CallImplicitCast)
 {
 	if (!rr::Caps.CallSupported)
diff --git a/src/Reactor/SubzeroReactor.cpp b/src/Reactor/SubzeroReactor.cpp
index e070dd3..17740a4 100644
--- a/src/Reactor/SubzeroReactor.cpp
+++ b/src/Reactor/SubzeroReactor.cpp
@@ -53,6 +53,11 @@
 #include <limits>
 #include <iostream>
 
+namespace rr
+{
+	class ELFMemoryStreamer;
+}
+
 namespace
 {
 	// Default configuration settings. Must be accessed under mutex lock.
@@ -71,7 +76,7 @@
 	Ice::Cfg *function = nullptr;
 	Ice::CfgNode *basicBlock = nullptr;
 	Ice::CfgLocalAllocatorScope *allocator = nullptr;
-	rr::Routine *routine = nullptr;
+	rr::ELFMemoryStreamer *routine = nullptr;
 
 	std::mutex codegenMutex;
 
@@ -543,10 +548,20 @@
 			return entry;
 		}
 
+		const void* addConstantData(const void* data, size_t size)
+		{
+			auto buf = std::unique_ptr<uint8_t[]>(new uint8_t[size]);
+			memcpy(buf.get(), data, size);
+			auto ptr = buf.get();
+			constantData.emplace_back(std::move(buf));
+			return ptr;
+		}
+
 	private:
 		void *entry;
 		std::vector<uint8_t, ExecutableAllocator<uint8_t>> buffer;
 		std::size_t position;
+		std::vector<std::unique_ptr<uint8_t[]>> constantData;
 
 		#if defined(_WIN32)
 		DWORD oldProtection;
@@ -3497,6 +3512,13 @@
 		}
 	}
 
+	RValue<Pointer<Byte>> ConstantData(void const * data, size_t size)
+	{
+		// TODO: Try to use Ice::VariableDeclaration::DataInitializer and
+		// getConstantSym instead of tagging data on the routine.
+		return ConstantPointer(::routine->addConstantData(data, size));
+	}
+
 	Value* Call(RValue<Pointer<Byte>> fptr, Type* retTy, std::initializer_list<Value*> args, std::initializer_list<Type*> argTys)
 	{
 		// FIXME: This does not currently work on Windows.