Vc  1.0.0-dev
SIMD Vector Classes for C++
operators.h
1 /* This file is part of the Vc library. {{{
2 Copyright © 2012-2015 Matthias Kretz <kretz@kde.org>
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7  * Redistributions of source code must retain the above copyright
8  notice, this list of conditions and the following disclaimer.
9  * Redistributions in binary form must reproduce the above copyright
10  notice, this list of conditions and the following disclaimer in the
11  documentation and/or other materials provided with the distribution.
12  * Neither the names of contributing organizations nor the
13  names of its contributors may be used to endorse or promote products
14  derived from this software without specific prior written permission.
15 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 
27 }}}*/
28 
29 #include "macros.h"
30 
31 namespace Vc_VERSIONED_NAMESPACE
32 {
33 namespace Common
34 {
35 template <bool C, typename T, typename F>
36 using conditional_t = typename std::conditional<C, T, F>::type;
37 
38 using std::is_convertible;
39 using std::is_floating_point;
40 using std::is_integral;
41 using std::is_same;
42 
43 template <typename T> constexpr bool isUnsigned()
44 {
45  return !std::is_same<Traits::decay<T>, bool>::value && Traits::is_unsigned<T>::value;
46 }
47 template <typename T> constexpr bool isIntegral()
48 {
49  return Traits::is_integral<T>::value;
50 }
51 template <typename T> constexpr bool isVector()
52 {
53  return Traits::is_simd_vector_internal<Traits::decay<T>>::value;
54 }
55 
56 template <typename T, bool = isIntegral<T>(), bool = isVector<T>()>
57 struct MakeUnsignedInternal;
58 template <template <typename, typename> class Vector__, typename T, typename Abi>
59 struct MakeUnsignedInternal<Vector__<T, Abi>, true, true>
60 {
61  using type = Vector__<typename std::make_unsigned<T>::type, Abi>;
62 };
63 template <typename T> struct MakeUnsignedInternal<T, false, true>
64 {
65  using type = T;
66 };
67 
68 template <typename Test, typename T>
69 using CopyUnsigned = typename MakeUnsignedInternal<T, isIntegral<T>() && isUnsigned<Test>()>::type;
70 
71 /* § 8.5.4 p7:
72  * A narrowing conversion is an implicit conversion
73  * — from a floating-point type to an integer type, or
74  * — from long double to double or float, or from double to float, except where the source is a constant
75  * expression and the actual value after conversion is within the range of values that can be represented
76  * (even if it cannot be represented exactly), or
77  * — from an integer type or unscoped enumeration type to a floating-point type, except where the source
78  * is a constant expression and the actual value after conversion will fit into the target type and will
79  * produce the original value when converted back to the original type, or
80  * — from an integer type or unscoped enumeration type to an integer type that cannot represent all the
81  * values of the original type, except where the source is a constant expression and the actual value after
82  * conversion will fit into the target type and will produce the original value when converted back to the
83  * original type.
84  */
85 template <typename From, typename To> constexpr bool isNarrowingFloatConversion()
86 {
87  return is_floating_point<From>::value &&
88  (is_integral<To>::value || (is_floating_point<To>::value && sizeof(From) > sizeof(To)));
89 }
90 
91 template <typename T> static constexpr bool convertsToSomeVector()
92 {
93  return is_convertible<T, double_v>::value || is_convertible<T, float_v>::value ||
94  is_convertible<T, int_v>::value || is_convertible<T, uint_v>::value ||
95  is_convertible<T, short_v>::value || is_convertible<T, ushort_v>::value;
96 }
97 
98 static_assert(isNarrowingFloatConversion<double, float>(), "");
99 static_assert(isNarrowingFloatConversion<long double, float>(), "");
100 static_assert(isNarrowingFloatConversion<long double, double>(), "");
101 static_assert(is_convertible<double, float_v>::value, "");
102 static_assert(false == ((is_convertible<double, float_v>::value ||
103  (isVector<double>() && is_convertible<float_v, double>::value)) &&
104  !isNarrowingFloatConversion<double, float_v::EntryType>()),
105  "");
106 
107 template <typename V, typename W>
108 using DetermineReturnType =
109  conditional_t<(is_same<V, int_v>::value || is_same<V, uint_v>::value) &&
110  (is_same<W, float>::value || is_same<W, float_v>::value),
111  float_v,
112  CopyUnsigned<W, V>>;
113 
114 template <typename V, typename W> constexpr bool participateInOverloadResolution()
115 {
116  return isVector<V>() && // one operand has to be a vector
117  !is_same<V, W>::value && // if they're the same type it's already
118  // covered by Vector::operatorX
119  convertsToSomeVector<W>(); // if the other operand is not convertible to a SIMD vector
120  // type at all then don't use our operator in overload
121  // resolution at all
122 }
123 
124 template <typename V, typename W> constexpr enable_if<isVector<V>(), bool> isValidOperandTypes()
125 {
126  // Vc does not allow operands that could possibly have different Vector::Size.
127  return isVector<W>()
128  ? (is_convertible<V, W>::value || is_convertible<W, V>::value)
129  : (is_convertible<W, DetermineReturnType<V, W>>::value &&
130  !isNarrowingFloatConversion<W, typename DetermineReturnType<V, W>::EntryType>());
131 }
132 
133 template <
134  typename V,
135  typename W,
136  bool VectorOperation = participateInOverloadResolution<V, W>() && isValidOperandTypes<V, W>()>
137 struct TypesForOperatorInternal
138 {
139 };
140 
141 template <typename V, typename W> struct TypesForOperatorInternal<V, W, true>
142 {
143  using type = DetermineReturnType<V, W>;
144 };
145 
146 template <typename L, typename R>
147 using TypesForOperator = typename TypesForOperatorInternal<
148  Traits::decay<conditional_t<isVector<L>(), L, R>>,
149  Traits::decay<conditional_t<!isVector<L>(), L, R>>>::type;
150 
151 template <
152  typename V,
153  typename W,
154  bool IsIncorrect = participateInOverloadResolution<V, W>() && !isValidOperandTypes<V, W>()>
155 struct IsIncorrectVectorOperands
156 {
157 };
158 template <typename V, typename W> struct IsIncorrectVectorOperands<V, W, true>
159 {
160  using type = void;
161 };
162 
163 template <typename L, typename R>
164 using Vc_does_not_allow_operands_to_a_binary_operator_which_can_have_different_SIMD_register_sizes_on_some_targets_and_thus_enforces_portability =
165  typename IsIncorrectVectorOperands<
166  Traits::decay<conditional_t<isVector<L>(), L, R>>,
167  Traits::decay<conditional_t<!isVector<L>(), L, R>>>::type;
168 } // namespace Common
169 
170 #define Vc_GENERIC_OPERATOR(op) \
171  template <typename L, typename R> \
172  Vc_ALWAYS_INLINE Common::TypesForOperator<L, R> operator op(L &&x, R &&y) \
173  { \
174  using V = Common::TypesForOperator<L, R>; \
175  return V(std::forward<L>(x)) op V(std::forward<R>(y)); \
176  }
177 
178 #define Vc_COMPARE_OPERATOR(op) \
179  template <typename L, typename R> \
180  Vc_ALWAYS_INLINE typename Common::TypesForOperator<L, R>::Mask operator op(L &&x, \
181  R &&y) \
182  { \
183  using V = Common::TypesForOperator<L, R>; \
184  return V(std::forward<L>(x)) op V(std::forward<R>(y)); \
185  }
186 
187 #define Vc_INVALID_OPERATOR(op) \
188  template <typename L, typename R> \
189  Common:: \
190  Vc_does_not_allow_operands_to_a_binary_operator_which_can_have_different_SIMD_register_sizes_on_some_targets_and_thus_enforces_portability< \
191  L, R> operator op(L &&, R &&) = delete;
192 // invalid operands to binary expression. Vc does not allow operands that can have a differing size
193 // on some targets.
194 
195 Vc_ALL_LOGICAL (Vc_GENERIC_OPERATOR)
196 Vc_ALL_BINARY (Vc_GENERIC_OPERATOR)
197 Vc_ALL_ARITHMETICS(Vc_GENERIC_OPERATOR)
198 Vc_ALL_COMPARES (Vc_COMPARE_OPERATOR)
199 
200 Vc_ALL_LOGICAL (Vc_INVALID_OPERATOR)
201 Vc_ALL_BINARY (Vc_INVALID_OPERATOR)
202 Vc_ALL_ARITHMETICS(Vc_INVALID_OPERATOR)
203 Vc_ALL_COMPARES (Vc_INVALID_OPERATOR)
204 
205 #undef Vc_GENERIC_OPERATOR
206 #undef Vc_COMPARE_OPERATOR
207 #undef Vc_INVALID_OPERATOR
208 
209 } // namespace Vc
Vector< float > float_v
vector of single precision
Definition: vector.h:58