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: Ben Clayton <bclayton@google.com> Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Reactor/Reactor.cpp b/src/Reactor/Reactor.cpp index f079f32..56ab1da 100644 --- a/src/Reactor/Reactor.cpp +++ b/src/Reactor/Reactor.cpp
@@ -4200,13 +4200,6 @@ 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);
diff --git a/src/Reactor/Reactor.hpp b/src/Reactor/Reactor.hpp index 82fa23a..d201904 100644 --- a/src/Reactor/Reactor.hpp +++ b/src/Reactor/Reactor.hpp
@@ -17,6 +17,7 @@ #include "Nucleus.hpp" #include "Routine.hpp" +#include "Traits.hpp" #include <cassert> #include <cstddef> @@ -230,6 +231,8 @@ class RValue { public: + using rvalue_underlying_type = T; + explicit RValue(Value *rvalue); #ifdef ENABLE_RR_DEBUG_INFO @@ -2399,14 +2402,23 @@ void branch(RValue<Bool> cmp, BasicBlock *bodyBB, BasicBlock *endBB); + // 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(); + } + void Return(); - void Return(RValue<Int> ret); template<class T> - void Return(const Pointer<T> &ret); - - 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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...); }
diff --git a/src/Reactor/ReactorUnitTests.cpp b/src/Reactor/ReactorUnitTests.cpp index b52b722..81c3700 100644 --- a/src/Reactor/ReactorUnitTests.cpp +++ b/src/Reactor/ReactorUnitTests.cpp
@@ -1374,3 +1374,149 @@ ::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, "");
diff --git a/src/Reactor/Traits.hpp b/src/Reactor/Traits.hpp new file mode 100644 index 0000000..ce6ac34 --- /dev/null +++ b/src/Reactor/Traits.hpp
@@ -0,0 +1,175 @@ +// 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