Commit 51f08312 by Ben Clayton

Reactor: Handle more implicit casting for Call() arguments

Add cast() static method to the CToReactor trait specializations. Use this in CallHelper to automatically convert C to Reactor types. This was implicitly handled by (most) reactor types having a contructor that took the C type, but this does not work for more complex conversions, such as 'const char*' -> rr::Pointer<Byte>. This particular conversion is now automatically handled when using Call() to invoke a function with a 'const char*' parameter. This also allows developers to define custom cast functions by specializing rr::CToReactor for their own types. This change also swaps the naming of CToReactor and CToReactorT. CToReactor now has contains 'type' and 'cast', and the 'T' suffix is more in keeping with the C++ traits naming convention for aliasing the ::type. Bug: b/143479561 Change-Id: I81faae365b5cbe0e45055160cc0f5470cd4fb2de Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/38028Reviewed-by: 's avatarAntonio Maiorano <amaiorano@google.com> Tested-by: 's avatarBen Clayton <bclayton@google.com> Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
parent 8df0aaf2
......@@ -118,7 +118,7 @@ private:
using CArgumentType = typename std::tuple_element<index, std::tuple<Arguments...>>::type;
template<int index>
using RArgumentType = CToReactor<CArgumentType<index>>;
using RArgumentType = CToReactorT<CArgumentType<index>>;
// Return the argument value with the given index.
template<int index>
......@@ -151,7 +151,7 @@ private:
{
core.reset(new Nucleus());
std::vector<Type*> types = {CToReactor<Arguments>::getType()...};
std::vector<Type*> types = {CToReactorT<Arguments>::getType()...};
for(auto type : types)
{
if(type != Void::getType())
......@@ -160,7 +160,7 @@ private:
}
}
Nucleus::createCoroutine(CToReactor<Return>::getType(), arguments);
Nucleus::createCoroutine(CToReactorT<Return>::getType(), arguments);
}
template<typename Return, typename... Arguments>
......
......@@ -4357,4 +4357,17 @@ namespace rr
Nucleus::createFence(memoryOrder);
}
Bool CToReactor<bool>::cast(bool v) { return type(v); }
Byte CToReactor<uint8_t>::cast(uint8_t v) { return type(v); }
SByte CToReactor<int8_t>::cast(int8_t v) { return type(v); }
Short CToReactor<int16_t>::cast(int16_t v) { return type(v); }
UShort CToReactor<uint16_t>::cast(uint16_t v) { return type(v); }
Int CToReactor<int32_t>::cast(int32_t v) { return type(v); }
UInt CToReactor<uint32_t>::cast(uint32_t v) { return type(v); }
Float CToReactor<float>::cast(float v) { return type(v); }
Pointer<Byte> CToReactor<void*>::cast(void* v) { return ConstantPointer(v); }
Pointer<Byte> CToReactor<const char*>::cast(const char* v) { return ConstantPointer(v); }
// TODO: Long has no constructor that takes a uint64_t
// Long CToReactor<uint64_t>::cast(uint64_t v) { return type(v); }
}
......@@ -2472,7 +2472,7 @@ namespace rr
template <typename T>
inline Value* ValueOf(const T &v)
{
return ReactorType<T>(v).loadValue();
return ReactorType<T>::cast(v).loadValue();
}
void Return();
......@@ -2480,7 +2480,7 @@ namespace rr
template<class T>
void Return(const T &ret)
{
static_assert(CanBeUsedAsReturn< ReactorType<T> >::value, "Unsupported type for Return()");
static_assert(CanBeUsedAsReturn< ReactorTypeT<T> >::value, "Unsupported type for Return()");
Nucleus::createRet(ValueOf<T>(ret));
// Place any unreachable instructions in an unreferenced block.
Nucleus::setInsertBlock(Nucleus::createBasicBlock());
......@@ -2527,17 +2527,17 @@ namespace rr
class FunctionT;
template<typename Return, typename... Arguments>
class FunctionT<Return(Arguments...)> : public Function<CToReactor<Return>(CToReactor<Arguments>...)>
class FunctionT<Return(Arguments...)> : public Function<CToReactorT<Return>(CToReactorT<Arguments>...)>
{
public:
// Type of base class
using BaseType = Function<CToReactor<Return>(CToReactor<Arguments>...)>;
using BaseType = Function<CToReactorT<Return>(CToReactorT<Arguments>...)>;
// Function type, e.g. void(int,float)
using CFunctionType = Return(Arguments...);
// Reactor function type, e.g. Void(Int, Float)
using ReactorFunctionType = CToReactor<Return>(CToReactor<Arguments>...);
using ReactorFunctionType = CToReactorT<Return>(CToReactorT<Arguments>...);
// Returned RoutineT type
using RoutineType = RoutineT<CFunctionType>;
......@@ -3209,24 +3209,24 @@ namespace rr
class CallHelper<Return(Arguments...)>
{
public:
using RReturn = CToReactor<Return>;
using RReturn = CToReactorT<Return>;
static inline RReturn Call(Return(fptr)(Arguments...), CToReactor<Arguments>... args)
static inline RReturn Call(Return(fptr)(Arguments...), CToReactorT<Arguments>... args)
{
return RValue<RReturn>(rr::Call(
ConstantPointer(reinterpret_cast<void*>(fptr)),
RReturn::getType(),
{ ValueOf(args) ... },
{ CToReactor<Arguments>::getType() ... }));
{ CToReactorT<Arguments>::getType() ... }));
}
static inline RReturn Call(Pointer<Byte> fptr, CToReactor<Arguments>... args)
static inline RReturn Call(Pointer<Byte> fptr, CToReactorT<Arguments>... args)
{
return RValue<RReturn>(rr::Call(
fptr,
RReturn::getType(),
{ ValueOf(args) ... },
{ CToReactor<Arguments>::getType() ... }));
{ CToReactorT<Arguments>::getType() ... }));
}
};
......@@ -3234,44 +3234,47 @@ namespace rr
class CallHelper<void(Arguments...)>
{
public:
static inline void Call(void(fptr)(Arguments...), CToReactor<Arguments>... args)
static inline void Call(void(fptr)(Arguments...), CToReactorT<Arguments>... args)
{
rr::Call(ConstantPointer(reinterpret_cast<void*>(fptr)),
Void::getType(),
{ ValueOf(args) ... },
{ CToReactor<Arguments>::getType() ... });
{ CToReactorT<Arguments>::getType() ... });
}
static inline void Call(Pointer<Byte> fptr, CToReactor<Arguments>... args)
static inline void Call(Pointer<Byte> fptr, CToReactorT<Arguments>... args)
{
rr::Call(fptr,
Void::getType(),
{ ValueOf(args) ... },
{ CToReactor<Arguments>::getType() ... });
{ CToReactorT<Arguments>::getType() ... });
}
};
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.
template<typename Return, typename ... Arguments>
inline CToReactor<Return> Call(Return(fptr)(Arguments...), CToReactor<Arguments>... args)
template<typename Return, typename ... CArgs, typename ... RArgs>
inline CToReactorT<Return> Call(Return(fptr)(CArgs...), RArgs&&... args)
{
return CallHelper<Return(Arguments...)>::Call(fptr, args...);
return CallHelper<Return(CArgs...)>::Call(fptr, CastToReactor(std::forward<RArgs>(args))...);
}
// Calls the function pointer fptr with the given arguments args.
// Overload for calling functions with void return type.
template<typename ... Arguments>
inline void Call(void(fptr)(Arguments...), CToReactor<Arguments>... args)
template<typename ... CArgs, typename ... RArgs>
inline void Call(void(fptr)(CArgs...), RArgs&&... args)
{
CallHelper<void(Arguments...)>::Call(fptr, args...);
CallHelper<void(CArgs...)>::Call(fptr, CastToReactor(std::forward<RArgs>(args))...);
}
// Calls the function pointer fptr with the signature FUNCTION_SIGNATURE and
// arguments.
template<typename FUNCTION_SIGNATURE, typename ... Arguments>
inline void Call(Pointer<Byte> fptr, Arguments ... args)
template<typename FUNCTION_SIGNATURE, typename ... RArgs>
inline void Call(Pointer<Byte> fptr, RArgs&& ... args)
{
CallHelper<FUNCTION_SIGNATURE>::Call(fptr, args...);
CallHelper<FUNCTION_SIGNATURE>::Call(fptr, CastToReactor(std::forward<RArgs>(args))...);
}
// Breakpoint emits an instruction that will cause the application to trap.
......
......@@ -1144,11 +1144,10 @@ TEST(ReactorUnitTests, Call)
struct Class
{
static int Callback(uint8_t *p, int i, float f)
static int Callback(Class *p, int i, float f)
{
auto c = reinterpret_cast<Class*>(p);
c->i = i;
c->f = f;
p->i = i;
p->f = f;
return i + int(f);
}
......@@ -1156,27 +1155,51 @@ TEST(ReactorUnitTests, Call)
float f = 0.0f;
};
FunctionT<int(void*)> function;
{
FunctionT<int(void*)> function;
{
Pointer<Byte> c = function.Arg<0>();
auto res = Call(Class::Callback, c, 10, 20.0f);
Return(res);
}
Pointer<Byte> c = function.Arg<0>();
auto res = Call(Class::Callback, c, 10, 20.0f);
Return(res);
}
auto routine = function("one");
auto routine = function("one");
if(routine)
{
Class c;
Class c;
int res = routine(&c);
EXPECT_EQ(res, 30);
EXPECT_EQ(c.i, 10);
EXPECT_EQ(c.f, 20.0f);
}
int res = routine(&c);
TEST(ReactorUnitTests, CallImplicitCast)
{
if (!rr::Caps.CallSupported)
{
SUCCEED() << "rr::Call() not supported";
return;
}
EXPECT_EQ(res, 30);
EXPECT_EQ(c.i, 10);
EXPECT_EQ(c.f, 20.0f);
struct Class
{
static void Callback(Class *c, const char* s)
{
c->str = s;
}
std::string str;
};
FunctionT<void(Class *c, const char *s)> function;
{
Pointer<Byte> c = function.Arg<0>();
Pointer<Byte> s = function.Arg<1>();
Call(Class::Callback, c, s);
}
auto routine = function("one");
Class c;
routine(&c, "hello world");
EXPECT_EQ(c.str, "hello world");
}
TEST(ReactorUnitTests, CallExternalCallRoutine)
......@@ -1309,14 +1332,14 @@ TEST(ReactorUnitTests, PreserveXMMRegisters)
}
template <typename T>
class CToReactorCastTest : public ::testing::Test
class CToReactorTCastTest : public ::testing::Test
{
public:
using CType = typename std::tuple_element<0, T>::type;
using ReactorType = typename std::tuple_element<1, T>::type;
};
using CToReactorCastTestTypes = ::testing::Types
using CToReactorTCastTestTypes = ::testing::Types
< // Subset of types that can be used as arguments.
// std::pair<bool, Bool>, FIXME(capn): Not supported as argument type by Subzero.
// std::pair<uint8_t, Byte>, FIXME(capn): Not supported as argument type by Subzero.
......@@ -1328,9 +1351,9 @@ using CToReactorCastTestTypes = ::testing::Types
std::pair<float, Float>
>;
TYPED_TEST_SUITE(CToReactorCastTest, CToReactorCastTestTypes);
TYPED_TEST_SUITE(CToReactorTCastTest, CToReactorTCastTestTypes);
TYPED_TEST(CToReactorCastTest, Casts)
TYPED_TEST(CToReactorTCastTest, Casts)
{
using CType = typename TestFixture::CType;
using ReactorType = typename TestFixture::ReactorType;
......@@ -1534,37 +1557,37 @@ int main(int argc, char **argv)
// Trait compile time checks. //
////////////////////////////////
// Assert CToReactor resolves to expected types.
static_assert(std::is_same<CToReactor<void>, Void>::value, "");
static_assert(std::is_same<CToReactor<bool>, Bool>::value, "");
static_assert(std::is_same<CToReactor<uint8_t>, Byte>::value, "");
static_assert(std::is_same<CToReactor<int8_t>, SByte>::value, "");
static_assert(std::is_same<CToReactor<int16_t>, Short>::value, "");
static_assert(std::is_same<CToReactor<uint16_t>, UShort>::value, "");
static_assert(std::is_same<CToReactor<int32_t>, Int>::value, "");
static_assert(std::is_same<CToReactor<uint64_t>, Long>::value, "");
static_assert(std::is_same<CToReactor<uint32_t>, UInt>::value, "");
static_assert(std::is_same<CToReactor<float>, Float>::value, "");
// Assert CToReactor for known pointer types resolves to expected types.
static_assert(std::is_same<CToReactor<void*>, Pointer<Byte>>::value, "");
static_assert(std::is_same<CToReactor<bool*>, Pointer<Bool>>::value, "");
static_assert(std::is_same<CToReactor<uint8_t*>, Pointer<Byte>>::value, "");
static_assert(std::is_same<CToReactor<int8_t*>, Pointer<SByte>>::value, "");
static_assert(std::is_same<CToReactor<int16_t*>, Pointer<Short>>::value, "");
static_assert(std::is_same<CToReactor<uint16_t*>, Pointer<UShort>>::value, "");
static_assert(std::is_same<CToReactor<int32_t*>, Pointer<Int>>::value, "");
static_assert(std::is_same<CToReactor<uint64_t*>, Pointer<Long>>::value, "");
static_assert(std::is_same<CToReactor<uint32_t*>, Pointer<UInt>>::value, "");
static_assert(std::is_same<CToReactor<float*>, Pointer<Float>>::value, "");
static_assert(std::is_same<CToReactor<uint16_t**>, Pointer<Pointer<UShort>>>::value, "");
static_assert(std::is_same<CToReactor<uint16_t***>, Pointer<Pointer<Pointer<UShort>>>>::value, "");
// Assert CToReactor for unknown pointer types resolves to Pointer<Byte>.
// Assert CToReactorT resolves to expected types.
static_assert(std::is_same<CToReactorT<void>, Void>::value, "");
static_assert(std::is_same<CToReactorT<bool>, Bool>::value, "");
static_assert(std::is_same<CToReactorT<uint8_t>, Byte>::value, "");
static_assert(std::is_same<CToReactorT<int8_t>, SByte>::value, "");
static_assert(std::is_same<CToReactorT<int16_t>, Short>::value, "");
static_assert(std::is_same<CToReactorT<uint16_t>, UShort>::value, "");
static_assert(std::is_same<CToReactorT<int32_t>, Int>::value, "");
static_assert(std::is_same<CToReactorT<uint64_t>, Long>::value, "");
static_assert(std::is_same<CToReactorT<uint32_t>, UInt>::value, "");
static_assert(std::is_same<CToReactorT<float>, Float>::value, "");
// Assert CToReactorT for known pointer types resolves to expected types.
static_assert(std::is_same<CToReactorT<void*>, Pointer<Byte>>::value, "");
static_assert(std::is_same<CToReactorT<bool*>, Pointer<Bool>>::value, "");
static_assert(std::is_same<CToReactorT<uint8_t*>, Pointer<Byte>>::value, "");
static_assert(std::is_same<CToReactorT<int8_t*>, Pointer<SByte>>::value, "");
static_assert(std::is_same<CToReactorT<int16_t*>, Pointer<Short>>::value, "");
static_assert(std::is_same<CToReactorT<uint16_t*>, Pointer<UShort>>::value, "");
static_assert(std::is_same<CToReactorT<int32_t*>, Pointer<Int>>::value, "");
static_assert(std::is_same<CToReactorT<uint64_t*>, Pointer<Long>>::value, "");
static_assert(std::is_same<CToReactorT<uint32_t*>, Pointer<UInt>>::value, "");
static_assert(std::is_same<CToReactorT<float*>, Pointer<Float>>::value, "");
static_assert(std::is_same<CToReactorT<uint16_t**>, Pointer<Pointer<UShort>>>::value, "");
static_assert(std::is_same<CToReactorT<uint16_t***>, Pointer<Pointer<Pointer<UShort>>>>::value, "");
// Assert CToReactorT for unknown pointer types resolves to Pointer<Byte>.
struct S{};
static_assert(std::is_same<CToReactor<S*>, Pointer<Byte>>::value, "");
static_assert(std::is_same<CToReactor<S**>, Pointer<Pointer<Byte>>>::value, "");
static_assert(std::is_same<CToReactor<S***>, Pointer<Pointer<Pointer<Byte>>>>::value, "");
static_assert(std::is_same<CToReactorT<S*>, Pointer<Byte>>::value, "");
static_assert(std::is_same<CToReactorT<S**>, Pointer<Pointer<Byte>>>::value, "");
static_assert(std::is_same<CToReactorT<S***>, Pointer<Pointer<Pointer<Byte>>>>::value, "");
// Assert IsRValue<> resolves true for RValue<> types.
static_assert(IsRValue<RValue<Void>>::value, "");
......
......@@ -62,22 +62,24 @@ namespace rr
static constexpr bool value = true;
};
// CToReactor<T> resolves to the corresponding Reactor type for the given C
// CToReactorT<T> resolves to the corresponding Reactor type for the given C
// template type T.
template<typename T, typename ENABLE = void> struct CToReactorT;
template<typename T> using CToReactor = typename CToReactorT<T>::type;
// CToReactorT specializations for POD types.
template<> struct CToReactorT<void> { using type = Void; };
template<> struct CToReactorT<bool> { using type = Bool; };
template<> struct CToReactorT<uint8_t> { using type = Byte; };
template<> struct CToReactorT<int8_t> { using type = SByte; };
template<> struct CToReactorT<int16_t> { using type = Short; };
template<> struct CToReactorT<uint16_t> { using type = UShort; };
template<> struct CToReactorT<int32_t> { using type = Int; };
template<> struct CToReactorT<uint64_t> { using type = Long; };
template<> struct CToReactorT<uint32_t> { using type = UInt; };
template<> struct CToReactorT<float> { using type = Float; };
template<typename T, typename ENABLE = void> struct CToReactor;
template<typename T> using CToReactorT = typename CToReactor<T>::type;
// CToReactor specializations for POD types.
template<> struct CToReactor<void> { using type = Void; };
template<> struct CToReactor<bool> { using type = Bool; static Bool cast(bool); };
template<> struct CToReactor<uint8_t> { using type = Byte; static Byte cast(uint8_t); };
template<> struct CToReactor<int8_t> { using type = SByte; static SByte cast(int8_t); };
template<> struct CToReactor<int16_t> { using type = Short; static Short cast(int16_t); };
template<> struct CToReactor<uint16_t> { using type = UShort; static UShort cast(uint16_t); };
template<> struct CToReactor<int32_t> { using type = Int; static Int cast(int32_t); };
template<> struct CToReactor<uint32_t> { using type = UInt; static UInt cast(uint32_t); };
template<> struct CToReactor<float> { using type = Float; static Float cast(float); };
// TODO: Long has no constructor that takes a uint64_t
template<> struct CToReactor<uint64_t> { using type = Long; /* static Long cast(uint64_t); */ };
// CToReactorPtrT<T>::type resolves to the corresponding Reactor Pointer<>
// type for T*.
......@@ -86,32 +88,47 @@ namespace rr
// CToReactorPtrT<T>::type resolves to Pointer<Byte>.
template<typename T, typename ENABLE = void> struct CToReactorPtrT { using type = Pointer<Byte>; };
template<typename T> using CToReactorPtr = typename CToReactorPtrT<T>::type;
template<typename T> struct CToReactorPtrT<T, typename std::enable_if< IsDefined< typename CToReactorT<T>::type >::value>::type >
template<typename T> struct CToReactorPtrT<T, typename std::enable_if< IsDefined< CToReactorT<T> >::value>::type >
{
using type = Pointer< typename CToReactorT<T>::type >;
using type = Pointer< CToReactorT<T> >;
static type cast(T v) { return type(v); }
};
// CToReactorT specialization for pointer types.
// CToReactor specialization for pointer types.
// For T types that have a CToReactorT<> specialization,
// CToReactorT<T*>::type resolves to Pointer< CToReactorT<T> >, otherwise
// CToReactorT<T*>::type resolves to Pointer<Byte>.
template<typename T>
struct CToReactorT<T, typename std::enable_if<std::is_pointer<T>::value>::type>
struct CToReactor<T, typename std::enable_if<std::is_pointer<T>::value>::type>
{
using elem = typename std::remove_pointer<T>::type;
using type = CToReactorPtr<elem>;
static type cast(T v) { return type(v); }
};
// CToReactorT specialization for void*.
// CToReactor specialization for void*.
// Maps to Pointer<Byte> instead of Pointer<Void>.
template<> struct CToReactorT<void*> { using type = Pointer<Byte>; };
template<> struct CToReactor<void*>
{
using type = Pointer<Byte>;
static type cast(void* v);
};
// CToReactorT specialization for enum types.
// CToReactor specialization for void*.
// Maps to Pointer<Byte> instead of Pointer<Void>.
template<> struct CToReactor<const char*>
{
using type = Pointer<Byte>;
static type cast(const char* v);
};
// CToReactor specialization for enum types.
template<typename T>
struct CToReactorT<T, typename std::enable_if<std::is_enum<T>::value>::type>
struct CToReactor<T, typename std::enable_if<std::is_enum<T>::value>::type>
{
using underlying = typename std::underlying_type<T>::type;
using type = typename CToReactorT<underlying>::type;
using type = CToReactorT<underlying>;
static type cast(T v) { return type(v); }
};
// IsRValue::value is true if T is of type RValue<X>, where X is any type.
......@@ -125,14 +142,30 @@ namespace rr
template <typename T, typename Enable = void> struct IsReference { static constexpr bool value = false; };
template <typename T> struct IsReference<T, typename std::enable_if<IsDefined<typename T::reference_underlying_type>::value>::type> { static constexpr bool value = true; };
// ReactorType<T> returns the LValue Reactor type for T.
// ReactorTypeT<T> returns the LValue Reactor type for T.
// T can be a C-type, RValue or LValue.
template<typename T, typename ENABLE = void> struct ReactorTypeT;
template<typename T> using ReactorType = typename ReactorTypeT<T>::type;
template<typename T> struct ReactorTypeT<T, typename std::enable_if<IsDefined<CToReactor<T>>::value>::type> { using type = CToReactor<T>; };
template<typename T> struct ReactorTypeT<T, typename std::enable_if<IsRValue<T>::value>::type> { using type = typename T::rvalue_underlying_type; };
template<typename T> struct ReactorTypeT<T, typename std::enable_if<IsLValue<T>::value>::type> { using type = T; };
template<typename T> struct ReactorTypeT<T, typename std::enable_if<IsReference<T>::value>::type> { using type = T; };
template<typename T, typename ENABLE = void> struct ReactorType;
template<typename T> using ReactorTypeT = typename ReactorType<T>::type;
template<typename T> struct ReactorType<T, typename std::enable_if<IsDefined<CToReactorT<T>>::value>::type>
{
using type = CToReactorT<T>;
static type cast(T v) { return CToReactor<T>::cast(v); }
};
template<typename T> struct ReactorType<T, typename std::enable_if<IsRValue<T>::value>::type>
{
using type = typename T::rvalue_underlying_type;
static type cast(T v) { return type(v); }
};
template<typename T> struct ReactorType<T, typename std::enable_if<IsLValue<T>::value>::type>
{
using type = T;
static type cast(T v) { return type(v); }
};
template<typename T> struct ReactorType<T, typename std::enable_if<IsReference<T>::value>::type>
{
using type = T;
static type cast(T v) { return type(v); }
};
// Reactor types that can be used as a return type for a function.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment