Serenity Operating System
at master 456 lines 13 kB view raw
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}