Serenity Operating System
1/*
2 * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#pragma once
8
9#include <AK/BitCast.h>
10#include <AK/BuiltinWrappers.h>
11#include <AK/Result.h>
12#include <AK/StringView.h>
13#include <AK/Types.h>
14#include <limits.h>
15#include <math.h>
16
17namespace Operators {
18
19#define DEFINE_BINARY_OPERATOR(Name, operation) \
20 struct Name { \
21 template<typename Lhs, typename Rhs> \
22 auto operator()(Lhs lhs, Rhs rhs) const \
23 { \
24 return lhs operation rhs; \
25 } \
26 \
27 static StringView name() \
28 { \
29 return #operation##sv; \
30 } \
31 }
32
33DEFINE_BINARY_OPERATOR(Equals, ==);
34DEFINE_BINARY_OPERATOR(NotEquals, !=);
35DEFINE_BINARY_OPERATOR(GreaterThan, >);
36DEFINE_BINARY_OPERATOR(LessThan, <);
37DEFINE_BINARY_OPERATOR(LessThanOrEquals, <=);
38DEFINE_BINARY_OPERATOR(GreaterThanOrEquals, >=);
39DEFINE_BINARY_OPERATOR(Add, +);
40DEFINE_BINARY_OPERATOR(Subtract, -);
41DEFINE_BINARY_OPERATOR(Multiply, *);
42DEFINE_BINARY_OPERATOR(BitAnd, &);
43DEFINE_BINARY_OPERATOR(BitOr, |);
44DEFINE_BINARY_OPERATOR(BitXor, ^);
45
46#undef DEFINE_BINARY_OPERATOR
47
48struct Divide {
49 template<typename Lhs, typename Rhs>
50 auto operator()(Lhs lhs, Rhs rhs) const
51 {
52 if constexpr (IsFloatingPoint<Lhs>) {
53 return lhs / rhs;
54 } else {
55 Checked value(lhs);
56 value /= rhs;
57 if (value.has_overflow())
58 return AK::Result<Lhs, StringView>("Integer division overflow"sv);
59 return AK::Result<Lhs, StringView>(value.value());
60 }
61 }
62
63 static StringView name() { return "/"sv; }
64};
65struct Modulo {
66 template<typename Lhs, typename Rhs>
67 auto operator()(Lhs lhs, Rhs rhs) const
68 {
69 if (rhs == 0)
70 return AK::Result<Lhs, StringView>("Integer division overflow"sv);
71 if constexpr (IsSigned<Lhs>) {
72 if (rhs == -1)
73 return AK::Result<Lhs, StringView>(0); // Spec weirdness right here, signed division overflow is ignored.
74 }
75 return AK::Result<Lhs, StringView>(lhs % rhs);
76 }
77
78 static StringView name() { return "%"sv; }
79};
80struct BitShiftLeft {
81 template<typename Lhs, typename Rhs>
82 auto operator()(Lhs lhs, Rhs rhs) const { return lhs << (rhs % (sizeof(lhs) * 8)); }
83
84 static StringView name() { return "<<"sv; }
85};
86struct BitShiftRight {
87 template<typename Lhs, typename Rhs>
88 auto operator()(Lhs lhs, Rhs rhs) const { return lhs >> (rhs % (sizeof(lhs) * 8)); }
89
90 static StringView name() { return ">>"sv; }
91};
92struct BitRotateLeft {
93 template<typename Lhs, typename Rhs>
94 auto operator()(Lhs lhs, Rhs rhs) const
95 {
96 // generates a single 'rol' instruction if shift is positive
97 // otherwise generate a `ror`
98 auto const mask = CHAR_BIT * sizeof(Lhs) - 1;
99 rhs &= mask;
100 return (lhs << rhs) | (lhs >> ((-rhs) & mask));
101 }
102
103 static StringView name() { return "rotate_left"sv; }
104};
105struct BitRotateRight {
106 template<typename Lhs, typename Rhs>
107 auto operator()(Lhs lhs, Rhs rhs) const
108 {
109 // generates a single 'ror' instruction if shift is positive
110 // otherwise generate a `rol`
111 auto const mask = CHAR_BIT * sizeof(Lhs) - 1;
112 rhs &= mask;
113 return (lhs >> rhs) | (lhs << ((-rhs) & mask));
114 }
115
116 static StringView name() { return "rotate_right"sv; }
117};
118struct Minimum {
119 template<typename Lhs, typename Rhs>
120 auto operator()(Lhs lhs, Rhs rhs) const
121 {
122 if constexpr (IsFloatingPoint<Lhs> || IsFloatingPoint<Rhs>) {
123 if (isnan(lhs))
124 return lhs;
125 if (isnan(rhs))
126 return rhs;
127 if (isinf(lhs))
128 return lhs > 0 ? rhs : lhs;
129 if (isinf(rhs))
130 return rhs > 0 ? lhs : rhs;
131 }
132 return min(lhs, rhs);
133 }
134
135 static StringView name() { return "minimum"sv; }
136};
137struct Maximum {
138 template<typename Lhs, typename Rhs>
139 auto operator()(Lhs lhs, Rhs rhs) const
140 {
141 if constexpr (IsFloatingPoint<Lhs> || IsFloatingPoint<Rhs>) {
142 if (isnan(lhs))
143 return lhs;
144 if (isnan(rhs))
145 return rhs;
146 if (isinf(lhs))
147 return lhs > 0 ? lhs : rhs;
148 if (isinf(rhs))
149 return rhs > 0 ? rhs : lhs;
150 }
151 return max(lhs, rhs);
152 }
153
154 static StringView name() { return "maximum"sv; }
155};
156struct CopySign {
157 template<typename Lhs, typename Rhs>
158 auto operator()(Lhs lhs, Rhs rhs) const
159 {
160 if constexpr (IsSame<Lhs, float>)
161 return copysignf(lhs, rhs);
162 else if constexpr (IsSame<Lhs, double>)
163 return copysign(lhs, rhs);
164 else
165 static_assert(DependentFalse<Lhs, Rhs>, "Invalid types to CopySign");
166 }
167
168 static StringView name() { return "copysign"sv; }
169};
170
171// Unary
172
173struct EqualsZero {
174 template<typename Lhs>
175 auto operator()(Lhs lhs) const { return lhs == 0; }
176
177 static StringView name() { return "== 0"sv; }
178};
179struct CountLeadingZeros {
180 template<typename Lhs>
181 i32 operator()(Lhs lhs) const
182 {
183 if (lhs == 0)
184 return sizeof(Lhs) * CHAR_BIT;
185
186 if constexpr (sizeof(Lhs) == 4 || sizeof(Lhs) == 8)
187 return count_leading_zeroes(MakeUnsigned<Lhs>(lhs));
188 else
189 VERIFY_NOT_REACHED();
190 }
191
192 static StringView name() { return "clz"sv; }
193};
194struct CountTrailingZeros {
195 template<typename Lhs>
196 i32 operator()(Lhs lhs) const
197 {
198 if (lhs == 0)
199 return sizeof(Lhs) * CHAR_BIT;
200
201 if constexpr (sizeof(Lhs) == 4 || sizeof(Lhs) == 8)
202 return count_trailing_zeroes(MakeUnsigned<Lhs>(lhs));
203 else
204 VERIFY_NOT_REACHED();
205 }
206
207 static StringView name() { return "ctz"sv; }
208};
209struct PopCount {
210 template<typename Lhs>
211 auto operator()(Lhs lhs) const
212 {
213 if constexpr (sizeof(Lhs) == 4 || sizeof(Lhs) == 8)
214 return popcount(MakeUnsigned<Lhs>(lhs));
215 else
216 VERIFY_NOT_REACHED();
217 }
218
219 static StringView name() { return "popcnt"sv; }
220};
221struct Absolute {
222 template<typename Lhs>
223 auto operator()(Lhs lhs) const { return AK::abs(lhs); }
224
225 static StringView name() { return "abs"sv; }
226};
227struct Negate {
228 template<typename Lhs>
229 auto operator()(Lhs lhs) const { return -lhs; }
230
231 static StringView name() { return "== 0"sv; }
232};
233struct Ceil {
234 template<typename Lhs>
235 auto operator()(Lhs lhs) const
236 {
237 if constexpr (IsSame<Lhs, float>)
238 return ceilf(lhs);
239 else if constexpr (IsSame<Lhs, double>)
240 return ceil(lhs);
241 else
242 VERIFY_NOT_REACHED();
243 }
244
245 static StringView name() { return "ceil"sv; }
246};
247struct Floor {
248 template<typename Lhs>
249 auto operator()(Lhs lhs) const
250 {
251 if constexpr (IsSame<Lhs, float>)
252 return floorf(lhs);
253 else if constexpr (IsSame<Lhs, double>)
254 return floor(lhs);
255 else
256 VERIFY_NOT_REACHED();
257 }
258
259 static StringView name() { return "floor"sv; }
260};
261struct Truncate {
262 template<typename Lhs>
263 Result<Lhs, StringView> operator()(Lhs lhs) const
264 {
265 if constexpr (IsSame<Lhs, float>)
266 return truncf(lhs);
267 else if constexpr (IsSame<Lhs, double>)
268 return trunc(lhs);
269 else
270 VERIFY_NOT_REACHED();
271 }
272
273 static StringView name() { return "truncate"sv; }
274};
275struct NearbyIntegral {
276 template<typename Lhs>
277 auto operator()(Lhs lhs) const
278 {
279 if constexpr (IsSame<Lhs, float>)
280 return nearbyintf(lhs);
281 else if constexpr (IsSame<Lhs, double>)
282 return nearbyint(lhs);
283 else
284 VERIFY_NOT_REACHED();
285 }
286
287 static StringView name() { return "round"sv; }
288};
289struct SquareRoot {
290 template<typename Lhs>
291 auto operator()(Lhs lhs) const
292 {
293 if constexpr (IsSame<Lhs, float>)
294 return sqrtf(lhs);
295 else if constexpr (IsSame<Lhs, double>)
296 return sqrt(lhs);
297 else
298 VERIFY_NOT_REACHED();
299 }
300
301 static StringView name() { return "sqrt"sv; }
302};
303
304template<typename Result>
305struct Wrap {
306 template<typename Lhs>
307 Result operator()(Lhs lhs) const
308 {
309 return static_cast<MakeUnsigned<Result>>(bit_cast<MakeUnsigned<Lhs>>(lhs));
310 }
311
312 static StringView name() { return "wrap"sv; }
313};
314
315template<typename ResultT>
316struct CheckedTruncate {
317 template<typename Lhs>
318 AK::Result<ResultT, StringView> operator()(Lhs lhs) const
319 {
320 if (isnan(lhs) || isinf(lhs)) // "undefined", let's just trap.
321 return "Truncation undefined behavior"sv;
322
323 Lhs truncated;
324 if constexpr (IsSame<float, Lhs>)
325 truncated = truncf(lhs);
326 else if constexpr (IsSame<double, Lhs>)
327 truncated = trunc(lhs);
328 else
329 VERIFY_NOT_REACHED();
330
331 // FIXME: This function assumes that all values of ResultT are representable in Lhs
332 // the assumption comes from the fact that this was used exclusively by LibJS,
333 // which only considers values that are all representable in 'double'.
334 if (!AK::is_within_range<ResultT>(truncated))
335 return "Truncation out of range"sv;
336
337 return static_cast<ResultT>(truncated);
338 }
339
340 static StringView name() { return "truncate.checked"sv; }
341};
342
343template<typename ResultT>
344struct Extend {
345 template<typename Lhs>
346 ResultT operator()(Lhs lhs) const
347 {
348 return lhs;
349 }
350
351 static StringView name() { return "extend"sv; }
352};
353
354template<typename ResultT>
355struct Convert {
356 template<typename Lhs>
357 ResultT operator()(Lhs lhs) const
358 {
359 auto signed_interpretation = bit_cast<MakeSigned<Lhs>>(lhs);
360 return static_cast<ResultT>(signed_interpretation);
361 }
362
363 static StringView name() { return "convert"sv; }
364};
365
366template<typename ResultT>
367struct Reinterpret {
368 template<typename Lhs>
369 ResultT operator()(Lhs lhs) const
370 {
371 return bit_cast<ResultT>(lhs);
372 }
373
374 static StringView name() { return "reinterpret"sv; }
375};
376
377struct Promote {
378 double operator()(float lhs) const
379 {
380 if (isnan(lhs))
381 return nan(""); // FIXME: Ensure canonical NaN remains canonical
382 return static_cast<double>(lhs);
383 }
384
385 static StringView name() { return "promote"sv; }
386};
387
388struct Demote {
389 float operator()(double lhs) const
390 {
391 if (isnan(lhs))
392 return nanf(""); // FIXME: Ensure canonical NaN remains canonical
393
394 if (isinf(lhs))
395 return __builtin_huge_valf();
396
397 return static_cast<float>(lhs);
398 }
399
400 static StringView name() { return "demote"sv; }
401};
402
403template<typename InitialType>
404struct SignExtend {
405 template<typename Lhs>
406 Lhs operator()(Lhs lhs) const
407 {
408 auto unsigned_representation = bit_cast<MakeUnsigned<Lhs>>(lhs);
409 auto truncated_unsigned_representation = static_cast<MakeUnsigned<InitialType>>(unsigned_representation);
410 auto initial_value = bit_cast<InitialType>(truncated_unsigned_representation);
411 return static_cast<Lhs>(initial_value);
412 }
413
414 static StringView name() { return "extend"sv; }
415};
416
417template<typename ResultT>
418struct SaturatingTruncate {
419 template<typename Lhs>
420 ResultT operator()(Lhs lhs) const
421 {
422 if (isnan(lhs))
423 return 0;
424
425 if (isinf(lhs)) {
426 if (lhs < 0)
427 return NumericLimits<ResultT>::min();
428 return NumericLimits<ResultT>::max();
429 }
430
431 // FIXME: This assumes that all values in ResultT are representable in 'double'.
432 // that assumption is not correct, which makes this function yield incorrect values
433 // for 'edge' values of type i64.
434 constexpr auto convert = []<typename ConvertT>(ConvertT truncated_value) {
435 if (truncated_value < NumericLimits<ResultT>::min())
436 return NumericLimits<ResultT>::min();
437 if constexpr (IsSame<ConvertT, float>) {
438 if (truncated_value >= static_cast<ConvertT>(NumericLimits<ResultT>::max()))
439 return NumericLimits<ResultT>::max();
440 } else {
441 if (static_cast<double>(truncated_value) >= static_cast<double>(NumericLimits<ResultT>::max()))
442 return NumericLimits<ResultT>::max();
443 }
444 return static_cast<ResultT>(truncated_value);
445 };
446
447 if constexpr (IsSame<Lhs, float>)
448 return convert(truncf(lhs));
449 else
450 return convert(trunc(lhs));
451 }
452
453 static StringView name() { return "truncate.saturating"sv; }
454};
455
456}