xref: /aosp_15_r20/external/angle/src/common/base/anglebase/numerics/safe_math_shared_impl.h (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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