1 // Copyright 2017 The Chromium Authors 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 PARTITION_ALLOC_PARTITION_ALLOC_BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_ 6 #define PARTITION_ALLOC_PARTITION_ALLOC_BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_ 7 8 #include <cassert> 9 #include <climits> 10 #include <cmath> 11 #include <cstddef> 12 #include <cstdint> 13 #include <cstdlib> 14 #include <limits> 15 #include <type_traits> 16 17 #include "build/build_config.h" 18 #include "partition_alloc/partition_alloc_base/numerics/safe_conversions.h" 19 20 #if BUILDFLAG(IS_ASMJS) 21 // Optimized safe math instructions are incompatible with asmjs. 22 #define PA_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) || \ 27 (__clang_major__ == 3 && __clang_minor__ >= 4))) || \ 28 (defined(__GNUC__) && __GNUC__ >= 5)) 29 #include "partition_alloc/partition_alloc_base/numerics/safe_math_clang_gcc_impl.h" 30 #define PA_BASE_HAS_OPTIMIZED_SAFE_MATH (1) 31 #else 32 #define PA_BASE_HAS_OPTIMIZED_SAFE_MATH (0) 33 #endif 34 35 namespace partition_alloc::internal::base::internal { 36 37 // These are the non-functioning boilerplate implementations of the optimized 38 // safe math routines. 39 #if !PA_BASE_HAS_OPTIMIZED_SAFE_MATH 40 template <typename T, typename U> 41 struct CheckedAddFastOp { 42 static const bool is_supported = false; 43 template <typename V> DoCheckedAddFastOp44 static constexpr bool Do(T, U, V*) { 45 // Force a compile failure if instantiated. 46 return CheckOnFailure::template HandleFailure<bool>(); 47 } 48 }; 49 50 template <typename T, typename U> 51 struct CheckedSubFastOp { 52 static const bool is_supported = false; 53 template <typename V> DoCheckedSubFastOp54 static constexpr bool Do(T, U, V*) { 55 // Force a compile failure if instantiated. 56 return CheckOnFailure::template HandleFailure<bool>(); 57 } 58 }; 59 60 template <typename T, typename U> 61 struct CheckedMulFastOp { 62 static const bool is_supported = false; 63 template <typename V> DoCheckedMulFastOp64 static constexpr bool Do(T, U, V*) { 65 // Force a compile failure if instantiated. 66 return CheckOnFailure::template HandleFailure<bool>(); 67 } 68 }; 69 70 template <typename T, typename U> 71 struct ClampedAddFastOp { 72 static const bool is_supported = false; 73 template <typename V> DoClampedAddFastOp74 static constexpr V Do(T, U) { 75 // Force a compile failure if instantiated. 76 return CheckOnFailure::template HandleFailure<V>(); 77 } 78 }; 79 80 template <typename T, typename U> 81 struct ClampedSubFastOp { 82 static const bool is_supported = false; 83 template <typename V> DoClampedSubFastOp84 static constexpr V Do(T, U) { 85 // Force a compile failure if instantiated. 86 return CheckOnFailure::template HandleFailure<V>(); 87 } 88 }; 89 90 template <typename T, typename U> 91 struct ClampedMulFastOp { 92 static const bool is_supported = false; 93 template <typename V> DoClampedMulFastOp94 static constexpr V Do(T, U) { 95 // Force a compile failure if instantiated. 96 return CheckOnFailure::template HandleFailure<V>(); 97 } 98 }; 99 100 template <typename T> 101 struct ClampedNegFastOp { 102 static const bool is_supported = false; DoClampedNegFastOp103 static constexpr T Do(T) { 104 // Force a compile failure if instantiated. 105 return CheckOnFailure::template HandleFailure<T>(); 106 } 107 }; 108 #endif // PA_BASE_HAS_OPTIMIZED_SAFE_MATH 109 #undef PA_BASE_HAS_OPTIMIZED_SAFE_MATH 110 111 // This is used for UnsignedAbs, where we need to support floating-point 112 // template instantiations even though we don't actually support the operations. 113 // However, there is no corresponding implementation of e.g. SafeUnsignedAbs, 114 // so the float versions will not compile. 115 template <typename Numeric, 116 bool IsInteger = std::is_integral_v<Numeric>, 117 bool IsFloat = std::is_floating_point_v<Numeric>> 118 struct UnsignedOrFloatForSize; 119 120 template <typename Numeric> 121 struct UnsignedOrFloatForSize<Numeric, true, false> { 122 using type = typename std::make_unsigned<Numeric>::type; 123 }; 124 125 template <typename Numeric> 126 struct UnsignedOrFloatForSize<Numeric, false, true> { 127 using type = Numeric; 128 }; 129 130 // Wrap the unary operations to allow SFINAE when instantiating integrals versus 131 // floating points. These don't perform any overflow checking. Rather, they 132 // exhibit well-defined overflow semantics and rely on the caller to detect 133 // if an overflow occurred. 134 135 template <typename T, 136 typename std::enable_if<std::is_integral_v<T>>::type* = nullptr> 137 constexpr T NegateWrapper(T value) { 138 using UnsignedT = typename std::make_unsigned<T>::type; 139 // This will compile to a NEG on Intel, and is normal negation on ARM. 140 return static_cast<T>(UnsignedT(0) - static_cast<UnsignedT>(value)); 141 } 142 143 template <typename T, 144 typename std::enable_if<std::is_floating_point_v<T>>::type* = nullptr> 145 constexpr T NegateWrapper(T value) { 146 return -value; 147 } 148 149 template <typename T, 150 typename std::enable_if<std::is_integral_v<T>>::type* = nullptr> 151 constexpr typename std::make_unsigned<T>::type InvertWrapper(T value) { 152 return ~value; 153 } 154 155 template <typename T, 156 typename std::enable_if<std::is_integral_v<T>>::type* = nullptr> 157 constexpr T AbsWrapper(T value) { 158 return static_cast<T>(SafeUnsignedAbs(value)); 159 } 160 161 template <typename T, 162 typename std::enable_if<std::is_floating_point_v<T>>::type* = nullptr> 163 constexpr T AbsWrapper(T value) { 164 return value < 0 ? -value : value; 165 } 166 167 template <template <typename, typename, typename> class M, 168 typename L, 169 typename R> 170 struct MathWrapper { 171 using math = M<typename UnderlyingType<L>::type, 172 typename UnderlyingType<R>::type, 173 void>; 174 using type = typename math::result_type; 175 }; 176 177 // The following macros are just boilerplate for the standard arithmetic 178 // operator overloads and variadic function templates. A macro isn't the nicest 179 // solution, but it beats rewriting these over and over again. 180 #define PA_BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME) \ 181 template <typename L, typename R, typename... Args> \ 182 constexpr auto CL_ABBR##OP_NAME(const L lhs, const R rhs, \ 183 const Args... args) { \ 184 return CL_ABBR##MathOp<CLASS##OP_NAME##Op, L, R, Args...>(lhs, rhs, \ 185 args...); \ 186 } 187 188 #define PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(CLASS, CL_ABBR, OP_NAME, OP, \ 189 CMP_OP) \ 190 /* Binary arithmetic operator for all CLASS##Numeric operations. */ \ 191 template <typename L, typename R, \ 192 typename std::enable_if<Is##CLASS##Op<L, R>::value>::type* = \ 193 nullptr> \ 194 constexpr CLASS##Numeric< \ 195 typename MathWrapper<CLASS##OP_NAME##Op, L, R>::type> \ 196 operator OP(const L lhs, const R rhs) { \ 197 return decltype(lhs OP rhs)::template MathOp<CLASS##OP_NAME##Op>(lhs, \ 198 rhs); \ 199 } \ 200 /* Assignment arithmetic operator implementation from CLASS##Numeric. */ \ 201 template <typename L> \ 202 template <typename R> \ 203 constexpr CLASS##Numeric<L>& CLASS##Numeric<L>::operator CMP_OP( \ 204 const R rhs) { \ 205 return MathOp<CLASS##OP_NAME##Op>(rhs); \ 206 } \ 207 /* Variadic arithmetic functions that return CLASS##Numeric. */ \ 208 PA_BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME) 209 210 } // namespace partition_alloc::internal::base::internal 211 212 #endif // PARTITION_ALLOC_PARTITION_ALLOC_BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_ 213