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