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_SRC_PARTITION_ALLOC_PARTITION_ALLOC_BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_ 6 #define BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_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 "build/build_config.h" 19 #include "partition_alloc/partition_alloc_base/numerics/safe_conversions.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 "partition_alloc/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_v<Numeric>, 118 bool IsFloat = std::is_floating_point_v<Numeric>> 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_v<T>>::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 <typename T, 145 typename std::enable_if<std::is_floating_point_v<T>>::type* = nullptr> 146 constexpr T NegateWrapper(T value) { 147 return -value; 148 } 149 150 template <typename T, 151 typename std::enable_if<std::is_integral_v<T>>::type* = nullptr> 152 constexpr typename std::make_unsigned<T>::type InvertWrapper(T value) { 153 return ~value; 154 } 155 156 template <typename T, 157 typename std::enable_if<std::is_integral_v<T>>::type* = nullptr> 158 constexpr T AbsWrapper(T value) { 159 return static_cast<T>(SafeUnsignedAbs(value)); 160 } 161 162 template <typename T, 163 typename std::enable_if<std::is_floating_point_v<T>>::type* = nullptr> 164 constexpr T AbsWrapper(T value) { 165 return value < 0 ? -value : value; 166 } 167 168 template <template <typename, typename, typename> class M, 169 typename L, 170 typename R> 171 struct MathWrapper { 172 using math = M<typename UnderlyingType<L>::type, 173 typename UnderlyingType<R>::type, 174 void>; 175 using type = typename math::result_type; 176 }; 177 178 // The following macros are just boilerplate for the standard arithmetic 179 // operator overloads and variadic function templates. A macro isn't the nicest 180 // solution, but it beats rewriting these over and over again. 181 #define PA_BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME) \ 182 template <typename L, typename R, typename... Args> \ 183 constexpr auto CL_ABBR##OP_NAME(const L lhs, const R rhs, \ 184 const Args... args) { \ 185 return CL_ABBR##MathOp<CLASS##OP_NAME##Op, L, R, Args...>(lhs, rhs, \ 186 args...); \ 187 } 188 189 #define PA_BASE_NUMERIC_ARITHMETIC_OPERATORS(CLASS, CL_ABBR, OP_NAME, OP, \ 190 CMP_OP) \ 191 /* Binary arithmetic operator for all CLASS##Numeric operations. */ \ 192 template <typename L, typename R, \ 193 typename std::enable_if<Is##CLASS##Op<L, R>::value>::type* = \ 194 nullptr> \ 195 constexpr CLASS##Numeric< \ 196 typename MathWrapper<CLASS##OP_NAME##Op, L, R>::type> \ 197 operator OP(const L lhs, const R rhs) { \ 198 return decltype(lhs OP rhs)::template MathOp<CLASS##OP_NAME##Op>(lhs, \ 199 rhs); \ 200 } \ 201 /* Assignment arithmetic operator implementation from CLASS##Numeric. */ \ 202 template <typename L> \ 203 template <typename R> \ 204 constexpr CLASS##Numeric<L>& CLASS##Numeric<L>::operator CMP_OP( \ 205 const R rhs) { \ 206 return MathOp<CLASS##OP_NAME##Op>(rhs); \ 207 } \ 208 /* Variadic arithmetic functions that return CLASS##Numeric. */ \ 209 PA_BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME) 210 211 } // namespace partition_alloc::internal::base::internal 212 213 #endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_PARTITION_ALLOC_BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_ 214