Commit 351be42e by Ben Clayton

Add traits for Reactor arguments and return types

Provides a number of template trait types for Reactor primitive types: CToReactorT<T> transforms the C-type (T) into a suitable Reactor equivalent. Pointer types are now generalized, allowing for translations of T** types. IsRValue<T>::value and IsLValue<T>::value are true iff T is of the respective Reactor type. ReactorType<T> resolves to the corresponding LValue Reactor type for T, where T can be a C-type, RValue or LValue. ValueOf<T> returns a rr::Value* for a C-value, RValue or LValue. This is now used by a single definition of Return(), fixing bad type coercion which in turn produced illegal LLVM IR. CanBeUsedAsReturn<T>::value is true iff T can be used as a function return type. CanBeUsedAsParameter<T>::value is true iff T can be used as a function parameter type. Function signatures are now statically checked that all parameter types and the return type are valid, providing a human readable error message if an invalid type is used (verified on gcc, clang and msvc). Added a whole bunch of static asserts in ReactorUnitTests.cpp to sanity check the templates. Bug: b/131914569 Change-Id: I3267354d6a3f68545079365a657757969ec0047d Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/30455 Kokoro-Presubmit: kokoro <noreply+kokoro@google.com> Tested-by: 's avatarBen Clayton <bclayton@google.com> Reviewed-by: 's avatarNicolas Capens <nicolascapens@google.com>
parent d1a5a893
......@@ -4200,13 +4200,6 @@ namespace rr
Nucleus::setInsertBlock(Nucleus::createBasicBlock());
}
void Return(RValue<Int> ret)
{
Nucleus::createRet(ret.value);
// Place any unreachable instructions in an unreferenced block.
Nucleus::setInsertBlock(Nucleus::createBasicBlock());
}
void branch(RValue<Bool> cmp, BasicBlock *bodyBB, BasicBlock *endBB)
{
Nucleus::createCondBr(cmp.value, bodyBB, endBB);
......
......@@ -17,6 +17,7 @@
#include "Nucleus.hpp"
#include "Routine.hpp"
#include "Traits.hpp"
#include <cassert>
#include <cstddef>
......@@ -230,6 +231,8 @@ namespace rr
class RValue
{
public:
using rvalue_underlying_type = T;
explicit RValue(Value *rvalue);
#ifdef ENABLE_RR_DEBUG_INFO
......@@ -2399,14 +2402,23 @@ namespace rr
void branch(RValue<Bool> cmp, BasicBlock *bodyBB, BasicBlock *endBB);
void Return();
void Return(RValue<Int> ret);
// ValueOf returns a rr::Value* for the given C-type, RValue<T>, or LValue<T>.
template <typename T>
inline Value* ValueOf(const T &v)
{
return ReactorType<T>(v).loadValue();
}
template<class T>
void Return(const Pointer<T> &ret);
void Return();
template<class T>
void Return(RValue<Pointer<T>> ret);
void Return(const T &ret)
{
static_assert(CanBeUsedAsReturn< ReactorType<T> >::value, "Unsupported type for Return()");
Nucleus::createRet(ValueOf<T>(ret));
// Place any unreachable instructions in an unreferenced block.
Nucleus::setInsertBlock(Nucleus::createBasicBlock());
}
// Generic template, leave undefined!
template<typename FunctionType>
......@@ -2416,6 +2428,9 @@ namespace rr
template<typename Return, typename... Arguments>
class Function<Return(Arguments...)>
{
// Static assert that the function signature is valid.
static_assert(sizeof(AssertFunctionSignatureIsValid<Return(Arguments...)>) >= 0, "Invalid function signature");
public:
Function();
......@@ -2968,24 +2983,6 @@ namespace rr
return RValue<T>(Nucleus::createSelect(condition.value, trueValue, falseValue));
}
template<class T>
void Return(const Pointer<T> &ret)
{
RR_DEBUG_INFO_UPDATE_LOC();
Nucleus::createRet(Nucleus::createLoad(ret.address, Pointer<T>::getType()));
// Place any unreachable instructions in an unreferenced block.
Nucleus::setInsertBlock(Nucleus::createBasicBlock());
}
template<class T>
void Return(RValue<Pointer<T>> ret)
{
RR_DEBUG_INFO_UPDATE_LOC();
Nucleus::createRet(ret.value);
// Place any unreachable instructions in an unreferenced block.
Nucleus::setInsertBlock(Nucleus::createBasicBlock());
}
template<typename Return, typename... Arguments>
Function<Return(Arguments...)>::Function()
{
......@@ -3069,26 +3066,6 @@ namespace rr
return ReinterpretCast<T>(val);
}
template <typename T>
inline Value* valueOf(RValue<T> v) { return v.value; }
template <typename T>
inline Value* valueOf(LValue<T> v) { return valueOf(RValue<T>(v.loadValue())); }
template<typename T>
struct CToReactor;
template<> struct CToReactor<void> { using type = Void; };
template<> struct CToReactor<int> { using type = Int; };
template<> struct CToReactor<unsigned int> { using type = UInt; };
template<> struct CToReactor<float> { using type = Float; };
template<> struct CToReactor<int*> { using type = Pointer<Int>; };
template<> struct CToReactor<float*> { using type = Pointer<Float>; };
// Pointers to non-reactor types are treated as uint8_t*.
template<typename T>
struct CToReactor<T*> { using type = Pointer<Byte>; };
// Returns a reactor pointer to the fixed-address ptr.
RValue<Pointer<Byte>> ConstantPointer(void const * ptr);
......@@ -3104,24 +3081,24 @@ namespace rr
class CallHelper<Return(Arguments...)>
{
public:
using RReturn = typename CToReactor<Return>::type;
using RReturn = CToReactor<Return>;
static inline RReturn Call(Return(fptr)(Arguments...), typename CToReactor<Arguments>::type... args)
static inline RReturn Call(Return(fptr)(Arguments...), CToReactor<Arguments>... args)
{
return RValue<RReturn>(rr::Call(
ConstantPointer(reinterpret_cast<void*>(fptr)),
RReturn::getType(),
{ valueOf(args) ... },
{ CToReactor<Arguments>::type::getType() ... }));
{ ValueOf(args) ... },
{ CToReactor<Arguments>::getType() ... }));
}
static inline RReturn Call(Pointer<Byte> fptr, typename CToReactor<Arguments>::type... args)
static inline RReturn Call(Pointer<Byte> fptr, CToReactor<Arguments>... args)
{
return RValue<RReturn>(rr::Call(
fptr,
RReturn::getType(),
{ valueOf(args) ... },
{ CToReactor<Arguments>::type::getType() ... }));
{ ValueOf(args) ... },
{ CToReactor<Arguments>::getType() ... }));
}
};
......@@ -3129,26 +3106,26 @@ namespace rr
class CallHelper<void(Arguments...)>
{
public:
static inline void Call(void(fptr)(Arguments...), typename CToReactor<Arguments>::type... args)
static inline void Call(void(fptr)(Arguments...), CToReactor<Arguments>... args)
{
rr::Call(ConstantPointer(reinterpret_cast<void*>(fptr)),
Void::getType(),
{ valueOf(args) ... },
{ CToReactor<Arguments>::type::getType() ... });
{ ValueOf(args) ... },
{ CToReactor<Arguments>::getType() ... });
}
static inline void Call(Pointer<Byte> fptr, typename CToReactor<Arguments>::type... args)
static inline void Call(Pointer<Byte> fptr, CToReactor<Arguments>... args)
{
rr::Call(fptr,
Void::getType(),
{ valueOf(args) ... },
{ CToReactor<Arguments>::type::getType() ... });
{ ValueOf(args) ... },
{ CToReactor<Arguments>::getType() ... });
}
};
// Calls the function pointer fptr with the given arguments args.
template<typename Return, typename ... Arguments>
inline typename CToReactor<Return>::type Call(Return(fptr)(Arguments...), typename CToReactor<Arguments>::type... args)
inline CToReactor<Return> Call(Return(fptr)(Arguments...), CToReactor<Arguments>... args)
{
return CallHelper<Return(Arguments...)>::Call(fptr, args...);
}
......
......@@ -1374,3 +1374,149 @@ int main(int argc, char **argv)
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
////////////////////////////////
// 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>.
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, "");
// Assert IsRValue<> resolves true for RValue<> types.
static_assert(IsRValue<RValue<Void>>::value, "");
static_assert(IsRValue<RValue<Bool>>::value, "");
static_assert(IsRValue<RValue<Byte>>::value, "");
static_assert(IsRValue<RValue<SByte>>::value, "");
static_assert(IsRValue<RValue<Short>>::value, "");
static_assert(IsRValue<RValue<UShort>>::value, "");
static_assert(IsRValue<RValue<Int>>::value, "");
static_assert(IsRValue<RValue<Long>>::value, "");
static_assert(IsRValue<RValue<UInt>>::value, "");
static_assert(IsRValue<RValue<Float>>::value, "");
// Assert IsLValue<> resolves true for LValue types.
static_assert(IsLValue<Bool>::value, "");
static_assert(IsLValue<Byte>::value, "");
static_assert(IsLValue<SByte>::value, "");
static_assert(IsLValue<Short>::value, "");
static_assert(IsLValue<UShort>::value, "");
static_assert(IsLValue<Int>::value, "");
static_assert(IsLValue<Long>::value, "");
static_assert(IsLValue<UInt>::value, "");
static_assert(IsLValue<Float>::value, "");
// Assert IsRValue<> resolves false for LValue types.
static_assert(!IsRValue<Void>::value, "");
static_assert(!IsRValue<Bool>::value, "");
static_assert(!IsRValue<Byte>::value, "");
static_assert(!IsRValue<SByte>::value, "");
static_assert(!IsRValue<Short>::value, "");
static_assert(!IsRValue<UShort>::value, "");
static_assert(!IsRValue<Int>::value, "");
static_assert(!IsRValue<Long>::value, "");
static_assert(!IsRValue<UInt>::value, "");
static_assert(!IsRValue<Float>::value, "");
// Assert IsRValue<> resolves false for C types.
static_assert(!IsRValue<void>::value, "");
static_assert(!IsRValue<bool>::value, "");
static_assert(!IsRValue<uint8_t>::value, "");
static_assert(!IsRValue<int8_t>::value, "");
static_assert(!IsRValue<int16_t>::value, "");
static_assert(!IsRValue<uint16_t>::value, "");
static_assert(!IsRValue<int32_t>::value, "");
static_assert(!IsRValue<uint64_t>::value, "");
static_assert(!IsRValue<uint32_t>::value, "");
static_assert(!IsRValue<float>::value, "");
// Assert IsLValue<> resolves false for RValue<> types.
static_assert(!IsLValue<RValue<Void>>::value, "");
static_assert(!IsLValue<RValue<Bool>>::value, "");
static_assert(!IsLValue<RValue<Byte>>::value, "");
static_assert(!IsLValue<RValue<SByte>>::value, "");
static_assert(!IsLValue<RValue<Short>>::value, "");
static_assert(!IsLValue<RValue<UShort>>::value, "");
static_assert(!IsLValue<RValue<Int>>::value, "");
static_assert(!IsLValue<RValue<Long>>::value, "");
static_assert(!IsLValue<RValue<UInt>>::value, "");
static_assert(!IsLValue<RValue<Float>>::value, "");
// Assert IsLValue<> resolves false for Void type.
static_assert(!IsLValue<Void>::value, "");
// Assert IsLValue<> resolves false for C types.
static_assert(!IsLValue<void>::value, "");
static_assert(!IsLValue<bool>::value, "");
static_assert(!IsLValue<uint8_t>::value, "");
static_assert(!IsLValue<int8_t>::value, "");
static_assert(!IsLValue<int16_t>::value, "");
static_assert(!IsLValue<uint16_t>::value, "");
static_assert(!IsLValue<int32_t>::value, "");
static_assert(!IsLValue<uint64_t>::value, "");
static_assert(!IsLValue<uint32_t>::value, "");
static_assert(!IsLValue<float>::value, "");
// Assert IsDefined<> resolves true for RValue<> types.
static_assert(IsDefined<RValue<Void>>::value, "");
static_assert(IsDefined<RValue<Bool>>::value, "");
static_assert(IsDefined<RValue<Byte>>::value, "");
static_assert(IsDefined<RValue<SByte>>::value, "");
static_assert(IsDefined<RValue<Short>>::value, "");
static_assert(IsDefined<RValue<UShort>>::value, "");
static_assert(IsDefined<RValue<Int>>::value, "");
static_assert(IsDefined<RValue<Long>>::value, "");
static_assert(IsDefined<RValue<UInt>>::value, "");
static_assert(IsDefined<RValue<Float>>::value, "");
// Assert IsDefined<> resolves true for LValue types.
static_assert(IsDefined<Void>::value, "");
static_assert(IsDefined<Bool>::value, "");
static_assert(IsDefined<Byte>::value, "");
static_assert(IsDefined<SByte>::value, "");
static_assert(IsDefined<Short>::value, "");
static_assert(IsDefined<UShort>::value, "");
static_assert(IsDefined<Int>::value, "");
static_assert(IsDefined<Long>::value, "");
static_assert(IsDefined<UInt>::value, "");
static_assert(IsDefined<Float>::value, "");
// Assert IsDefined<> resolves true for C types.
static_assert(IsDefined<void>::value, "");
static_assert(IsDefined<bool>::value, "");
static_assert(IsDefined<uint8_t>::value, "");
static_assert(IsDefined<int8_t>::value, "");
static_assert(IsDefined<int16_t>::value, "");
static_assert(IsDefined<uint16_t>::value, "");
static_assert(IsDefined<int32_t>::value, "");
static_assert(IsDefined<uint64_t>::value, "");
static_assert(IsDefined<uint32_t>::value, "");
static_assert(IsDefined<float>::value, "");
// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef rr_Traits_hpp
#define rr_Traits_hpp
#include <stdint.h>
#include <type_traits>
#ifdef Bool
#undef Bool // b/127920555
#endif
namespace rr
{
// Forward declarations
class Value;
class Void;
class Bool;
class Byte;
class SByte;
class Short;
class UShort;
class Int;
class UInt;
class Long;
class Half;
class Float;
template<class T> class Pointer;
template<class T> class LValue;
template<class T> class RValue;
// IsDefined<T>::value is true if T is a valid type, otherwise false.
template <typename T, typename Enable = void>
struct IsDefined
{
static constexpr bool value = false;
};
template <typename T>
struct IsDefined<T, typename std::enable_if<(sizeof(T)>0)>::type>
{
static constexpr bool value = true;
};
template <>
struct IsDefined<void>
{
static constexpr bool value = true;
};
// CToReactor<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; };
// CToReactorPtrT<T>::type resolves to the corresponding Reactor Pointer<>
// type for T*.
// For T types that have a CToReactorT<> specialization,
// CToReactorPtrT<T>::type resolves to Pointer< CToReactorT<T> >, otherwise
// 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 >
{
using type = Pointer< typename CToReactorT<T>::type >;
};
// CToReactorT 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>
{
using elem = typename std::remove_pointer<T>::type;
using type = CToReactorPtr<elem>;
};
// CToReactorT specialization for void*.
// Maps to Pointer<Byte> instead of Pointer<Void>.
template<> struct CToReactorT<void*> { using type = Pointer<Byte>; };
// CToReactorT specialization for enum types.
template<typename T>
struct CToReactorT<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;
};
// IsRValue::value is true if T is of type RValue<X>, where X is any type.
template <typename T, typename Enable = void> struct IsRValue { static constexpr bool value = false; };
template <typename T> struct IsRValue<T, typename std::enable_if<IsDefined<typename T::rvalue_underlying_type>::value>::type> { static constexpr bool value = true; };
// IsLValue::value is true if T is of, or derives from type LValue<T>.
template <typename T> struct IsLValue { static constexpr bool value = std::is_base_of<LValue<T>, T>::value; };
// ReactorType<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; };
// Reactor types that can be used as a return type for a function.
template <typename T> struct CanBeUsedAsReturn { static constexpr bool value = false; };
template <> struct CanBeUsedAsReturn<Void> { static constexpr bool value = true; };
template <> struct CanBeUsedAsReturn<Int> { static constexpr bool value = true; };
template <> struct CanBeUsedAsReturn<UInt> { static constexpr bool value = true; };
template <> struct CanBeUsedAsReturn<Float> { static constexpr bool value = true; };
template <typename T> struct CanBeUsedAsReturn<Pointer<T>> { static constexpr bool value = true; };
// Reactor types that can be used as a parameter types for a function.
template <typename T> struct CanBeUsedAsParameter { static constexpr bool value = false; };
template <> struct CanBeUsedAsParameter<Int> { static constexpr bool value = true; };
template <> struct CanBeUsedAsParameter<UInt> { static constexpr bool value = true; };
template <> struct CanBeUsedAsParameter<Float> { static constexpr bool value = true; };
template <typename T> struct CanBeUsedAsParameter<Pointer<T>> { static constexpr bool value = true; };
// AssertParameterTypeIsValid statically asserts that all template parameter
// types can be used as a Reactor function parameter.
template<typename T, typename ... other>
struct AssertParameterTypeIsValid : AssertParameterTypeIsValid<other...>
{
static_assert(CanBeUsedAsParameter<T>::value, "Invalid parameter type");
};
template<typename T>
struct AssertParameterTypeIsValid<T>
{
static_assert(CanBeUsedAsParameter<T>::value, "Invalid parameter type");
};
// AssertFunctionSignatureIsValid statically asserts that the Reactor
// function signature is valid.
template<typename Return, typename... Arguments>
class AssertFunctionSignatureIsValid;
template<typename Return>
class AssertFunctionSignatureIsValid<Return(Void)> {};
template<typename Return, typename... Arguments>
class AssertFunctionSignatureIsValid<Return(Arguments...)>
{
static_assert(CanBeUsedAsReturn<Return>::value, "Invalid return type");
static_assert(sizeof(AssertParameterTypeIsValid<Arguments...>) >= 0, "");
};
} // namespace rr
#endif // rr_Traits_hpp
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