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.