1 // Copyright 2017 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_ 6 #define BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_ 7 8 #include <stddef.h> 9 #include <stdint.h> 10 11 #include <cassert> 12 #include <climits> 13 #include <cmath> 14 #include <cstdlib> 15 #include <limits> 16 #include <type_traits> 17 18 #include "anglebase/numerics/safe_conversions.h" 19 20 #if defined(OS_ASMJS) 21 // Optimized safe math instructions are incompatible with asmjs. 22 # define BASE_HAS_OPTIMIZED_SAFE_MATH (0) 23 // Where available use builtin math overflow support on Clang and GCC. 24 #elif !defined(__native_client__) && \ 25 ((defined(__clang__) && \ 26 ((__clang_major__ > 3) || (__clang_major__ == 3 && __clang_minor__ >= 4))) || \ 27 (defined(__GNUC__) && __GNUC__ >= 5)) 28 # include "anglebase/numerics/safe_math_clang_gcc_impl.h" 29 # define BASE_HAS_OPTIMIZED_SAFE_MATH (1) 30 #else 31 # define BASE_HAS_OPTIMIZED_SAFE_MATH (0) 32 #endif 33 34 namespace angle 35 { 36 namespace base 37 { 38 namespace internal 39 { 40 41 // These are the non-functioning boilerplate implementations of the optimized 42 // safe math routines. 43 #if !BASE_HAS_OPTIMIZED_SAFE_MATH 44 template <typename T, typename U> 45 struct CheckedAddFastOp 46 { 47 static const bool is_supported = false; 48 template <typename V> DoCheckedAddFastOp49 static constexpr bool Do(T, U, V *) 50 { 51 // Force a compile failure if instantiated. 52 return CheckOnFailure::template HandleFailure<bool>(); 53 } 54 }; 55 56 template <typename T, typename U> 57 struct CheckedSubFastOp 58 { 59 static const bool is_supported = false; 60 template <typename V> DoCheckedSubFastOp61 static constexpr bool Do(T, U, V *) 62 { 63 // Force a compile failure if instantiated. 64 return CheckOnFailure::template HandleFailure<bool>(); 65 } 66 }; 67 68 template <typename T, typename U> 69 struct CheckedMulFastOp 70 { 71 static const bool is_supported = false; 72 template <typename V> DoCheckedMulFastOp73 static constexpr bool Do(T, U, V *) 74 { 75 // Force a compile failure if instantiated. 76 return CheckOnFailure::template HandleFailure<bool>(); 77 } 78 }; 79 80 template <typename T, typename U> 81 struct ClampedAddFastOp 82 { 83 static const bool is_supported = false; 84 template <typename V> DoClampedAddFastOp85 static constexpr V Do(T, U) 86 { 87 // Force a compile failure if instantiated. 88 return CheckOnFailure::template HandleFailure<V>(); 89 } 90 }; 91 92 template <typename T, typename U> 93 struct ClampedSubFastOp 94 { 95 static const bool is_supported = false; 96 template <typename V> DoClampedSubFastOp97 static constexpr V Do(T, U) 98 { 99 // Force a compile failure if instantiated. 100 return CheckOnFailure::template HandleFailure<V>(); 101 } 102 }; 103 104 template <typename T, typename U> 105 struct ClampedMulFastOp 106 { 107 static const bool is_supported = false; 108 template <typename V> DoClampedMulFastOp109 static constexpr V Do(T, U) 110 { 111 // Force a compile failure if instantiated. 112 return CheckOnFailure::template HandleFailure<V>(); 113 } 114 }; 115 116 template <typename T> 117 struct ClampedNegFastOp 118 { 119 static const bool is_supported = false; DoClampedNegFastOp120 static constexpr T Do(T) 121 { 122 // Force a compile failure if instantiated. 123 return CheckOnFailure::template HandleFailure<T>(); 124 } 125 }; 126 #endif // BASE_HAS_OPTIMIZED_SAFE_MATH 127 #undef BASE_HAS_OPTIMIZED_SAFE_MATH 128 129 // This is used for UnsignedAbs, where we need to support floating-point 130 // template instantiations even though we don't actually support the operations. 131 // However, there is no corresponding implementation of e.g. SafeUnsignedAbs, 132 // so the float versions will not compile. 133 template <typename Numeric, 134 bool IsInteger = std::is_integral<Numeric>::value, 135 bool IsFloat = std::is_floating_point<Numeric>::value> 136 struct UnsignedOrFloatForSize; 137 138 template <typename Numeric> 139 struct UnsignedOrFloatForSize<Numeric, true, false> 140 { 141 using type = typename std::make_unsigned<Numeric>::type; 142 }; 143 144 template <typename Numeric> 145 struct UnsignedOrFloatForSize<Numeric, false, true> 146 { 147 using type = Numeric; 148 }; 149 150 // Wrap the unary operations to allow SFINAE when instantiating integrals versus 151 // floating points. These don't perform any overflow checking. Rather, they 152 // exhibit well-defined overflow semantics and rely on the caller to detect 153 // if an overflow occured. 154 155 template <typename T, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr> 156 constexpr T NegateWrapper(T value) 157 { 158 using UnsignedT = typename std::make_unsigned<T>::type; 159 // This will compile to a NEG on Intel, and is normal negation on ARM. 160 return static_cast<T>(UnsignedT(0) - static_cast<UnsignedT>(value)); 161 } 162 163 template <typename T, typename std::enable_if<std::is_floating_point<T>::value>::type * = nullptr> 164 constexpr T NegateWrapper(T value) 165 { 166 return -value; 167 } 168 169 template <typename T, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr> 170 constexpr typename std::make_unsigned<T>::type InvertWrapper(T value) 171 { 172 return ~value; 173 } 174 175 template <typename T, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr> 176 constexpr T AbsWrapper(T value) 177 { 178 return static_cast<T>(SafeUnsignedAbs(value)); 179 } 180 181 template <typename T, typename std::enable_if<std::is_floating_point<T>::value>::type * = nullptr> 182 constexpr T AbsWrapper(T value) 183 { 184 return value < 0 ? -value : value; 185 } 186 187 template <template <typename, typename, typename> class M, typename L, typename R> 188 struct MathWrapper 189 { 190 using math = M<typename UnderlyingType<L>::type, typename UnderlyingType<R>::type, void>; 191 using type = typename math::result_type; 192 }; 193 194 // The following macros are just boilerplate for the standard arithmetic 195 // operator overloads and variadic function templates. A macro isn't the nicest 196 // solution, but it beats rewriting these over and over again. 197 #define BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME) \ 198 template <typename L, typename R, typename... Args> \ 199 constexpr auto CL_ABBR##OP_NAME(const L lhs, const R rhs, const Args... args) \ 200 { \ 201 return CL_ABBR##MathOp<CLASS##OP_NAME##Op, L, R, Args...>(lhs, rhs, args...); \ 202 } 203 204 #define BASE_NUMERIC_ARITHMETIC_OPERATORS(CLASS, CL_ABBR, OP_NAME, OP, CMP_OP) \ 205 /* Binary arithmetic operator for all CLASS##Numeric operations. */ \ 206 template <typename L, typename R, \ 207 typename std::enable_if<Is##CLASS##Op<L, R>::value>::type * = nullptr> \ 208 constexpr CLASS##Numeric<typename MathWrapper<CLASS##OP_NAME##Op, L, R>::type> operator OP( \ 209 const L lhs, const R rhs) \ 210 { \ 211 return decltype(lhs OP rhs)::template MathOp<CLASS##OP_NAME##Op>(lhs, rhs); \ 212 } \ 213 /* Assignment arithmetic operator implementation from CLASS##Numeric. */ \ 214 template <typename L> \ 215 template <typename R> \ 216 constexpr CLASS##Numeric<L> &CLASS##Numeric<L>::operator CMP_OP(const R rhs) \ 217 { \ 218 return MathOp<CLASS##OP_NAME##Op>(rhs); \ 219 } \ 220 /* Variadic arithmetic functions that return CLASS##Numeric. */ \ 221 BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME) 222 223 } // namespace internal 224 } // namespace base 225 } // namespace angle 226 227 #endif // BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_ 228