1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #ifndef PANDA_RUNTIME_INTERPRETER_MATH_HELPERS_H_
17 #define PANDA_RUNTIME_INTERPRETER_MATH_HELPERS_H_
18
19 #include <cmath>
20 #include <functional>
21 #include <limits>
22 #include <type_traits>
23
24 #include "libpandabase/macros.h"
25
26 namespace panda::interpreter::math_helpers {
27
28 template <typename T>
29 struct bit_shl : public std::binary_function<T, T, T> { // NOLINT(readability-identifier-naming)
operatorbit_shl30 constexpr T operator()(const T &x, const T &y) const
31 {
32 using unsigned_type = std::make_unsigned_t<T>;
33 size_t mask = std::numeric_limits<unsigned_type>::digits - 1;
34 size_t shift = static_cast<unsigned_type>(y) & mask;
35 return static_cast<T>(static_cast<unsigned_type>(x) << shift);
36 }
37 };
38
39 template <typename T>
40 struct bit_shr : public std::binary_function<T, T, T> { // NOLINT(readability-identifier-naming)
operatorbit_shr41 constexpr T operator()(const T &x, const T &y) const
42 {
43 using unsigned_type = std::make_unsigned_t<T>;
44 size_t mask = std::numeric_limits<unsigned_type>::digits - 1;
45 size_t shift = static_cast<unsigned_type>(y) & mask;
46 return static_cast<T>(static_cast<unsigned_type>(x) >> shift);
47 }
48 };
49
50 template <typename T>
51 struct bit_ashr : public std::binary_function<T, T, T> { // NOLINT(readability-identifier-naming)
operatorbit_ashr52 constexpr T operator()(const T &x, const T &y) const
53 {
54 using unsigned_type = std::make_unsigned_t<T>;
55 size_t mask = std::numeric_limits<unsigned_type>::digits - 1;
56 size_t shift = static_cast<unsigned_type>(y) & mask;
57 return x >> shift; // NOLINT(hicpp-signed-bitwise)
58 }
59 };
60
61 template <typename T>
62 struct fmodulus : public std::binary_function<T, T, T> { // NOLINT(readability-identifier-naming)
operatorfmodulus63 constexpr T operator()(const T &x, const T &y) const
64 {
65 static_assert(std::is_floating_point_v<T>, "T should be floating point type");
66 return std::fmod(x, y);
67 }
68 };
69
70 template <typename T>
71 struct cmp : public std::binary_function<T, T, int32_t> { // NOLINT(readability-identifier-naming)
operatorcmp72 constexpr int32_t operator()(const T &x, const T &y) const
73 {
74 static_assert(std::is_integral_v<T>, "T should be integral type");
75
76 if (x > y) {
77 return 1;
78 }
79
80 if (x == y) {
81 return 0;
82 }
83
84 return -1;
85 }
86 };
87
88 template <typename T>
89 struct fcmpl : public std::binary_function<T, T, int32_t> { // NOLINT(readability-identifier-naming)
operatorfcmpl90 constexpr int32_t operator()(const T &x, const T &y) const
91 {
92 static_assert(std::is_floating_point_v<T>, "T should be floating point type");
93
94 if (std::isnan(x) || std::isnan(y)) {
95 return -1; // this is the difference between fcmpl and fcmpg for NAN
96 }
97
98 if (x < y) {
99 return -1;
100 }
101
102 if (x > y) {
103 return 1;
104 }
105
106 return 0;
107 }
108 };
109
110 template <typename T>
111 struct fcmpg : public std::binary_function<T, T, int32_t> { // NOLINT(readability-identifier-naming)
operatorfcmpg112 constexpr int32_t operator()(const T &x, const T &y) const
113 {
114 static_assert(std::is_floating_point_v<T>, "T should be floating point type");
115
116 if (std::isnan(x) || std::isnan(y)) {
117 return 1; // this is the difference between fcmpl and fcmpg for NAN
118 }
119
120 if (x < y) {
121 return -1;
122 }
123
124 if (x > y) {
125 return 1;
126 }
127
128 return 0;
129 }
130 };
131
132 template <typename T>
133 struct idivides : public std::binary_function<T, T, T> { // NOLINT(readability-identifier-naming)
operatoridivides134 constexpr T operator()(const T &x, const T &y) const
135 {
136 static_assert(std::is_integral_v<T>, "T should be integral type");
137
138 // Disable checks due to clang-tidy bug https://bugs.llvm.org/show_bug.cgi?id=32203
139 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
140 if constexpr (std::is_signed_v<T>) {
141 constexpr T MIN = std::numeric_limits<T>::min();
142
143 if (UNLIKELY(x == MIN && y == -1)) {
144 return MIN;
145 }
146 }
147
148 return x / y;
149 }
150 };
151
152 template <typename T>
153 struct imodulus : public std::binary_function<T, T, T> { // NOLINT(readability-identifier-naming)
operatorimodulus154 constexpr T operator()(const T &x, const T &y) const
155 {
156 static_assert(std::is_integral_v<T>, "T should be integral type");
157
158 // Disable checks due to clang-tidy bug https://bugs.llvm.org/show_bug.cgi?id=32203
159 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
160 if constexpr (std::is_signed_v<T>) {
161 constexpr T MIN = std::numeric_limits<T>::min();
162
163 if (UNLIKELY(x == MIN && y == -1)) {
164 return 0;
165 }
166 }
167
168 return x % y;
169 }
170 };
171
172 template <template <typename OpT> class Op, typename T>
SafeMath(T a,T b)173 ALWAYS_INLINE static inline T SafeMath(T a, T b)
174 {
175 using unsigned_T = std::make_unsigned_t<T>;
176 static_assert(std::is_signed<T>::value, "Expected T to be signed");
177 auto val1 = static_cast<unsigned_T>(a);
178 auto val2 = static_cast<unsigned_T>(b);
179 return static_cast<T>(Op<unsigned_T>()(val1, val2));
180 }
181
182 template <typename T>
183 struct Plus {
operatorPlus184 ALWAYS_INLINE inline T operator()(T a, T b)
185 {
186 return SafeMath<std::plus>(a, b);
187 }
188 };
189
190 template <typename T>
191 struct Minus {
operatorMinus192 ALWAYS_INLINE inline T operator()(T a, T b)
193 {
194 return SafeMath<std::minus>(a, b);
195 }
196 };
197
198 template <typename T>
199 struct Multiplies {
operatorMultiplies200 ALWAYS_INLINE inline T operator()(T a, T b)
201 {
202 return SafeMath<std::multiplies>(a, b);
203 }
204 };
205
206 template <typename T>
207 struct inc : public std::unary_function<T, T> { // NOLINT(readability-identifier-naming)
operatorinc208 constexpr T operator()(const T &x) const
209 {
210 return SafeMath<std::plus>(x, 1);
211 }
212 };
213
214 template <typename T>
215 struct dec : public std::unary_function<T, T> { // NOLINT(readability-identifier-naming)
operatordec216 constexpr T operator()(const T &x) const
217 {
218 return SafeMath<std::minus>(x, 1);
219 }
220 };
221
222 } // namespace panda::interpreter::math_helpers
223
224 #endif // PANDA_RUNTIME_INTERPRETER_MATH_HELPERS_H_
225