123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445 |
- /*
- * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
- #pragma once
- #include <AK/BitCast.h>
- #include <AK/BuiltinWrappers.h>
- #include <AK/Result.h>
- #include <AK/StringView.h>
- #include <AK/Types.h>
- #include <limits.h>
- #include <math.h>
- namespace Operators {
- #define DEFINE_BINARY_OPERATOR(Name, operation) \
- struct Name { \
- template<typename Lhs, typename Rhs> \
- auto operator()(Lhs lhs, Rhs rhs) const { return lhs operation rhs; } \
- \
- static StringView name() { return #operation; } \
- }
- DEFINE_BINARY_OPERATOR(Equals, ==);
- DEFINE_BINARY_OPERATOR(NotEquals, !=);
- DEFINE_BINARY_OPERATOR(GreaterThan, >);
- DEFINE_BINARY_OPERATOR(LessThan, <);
- DEFINE_BINARY_OPERATOR(LessThanOrEquals, <=);
- DEFINE_BINARY_OPERATOR(GreaterThanOrEquals, >=);
- DEFINE_BINARY_OPERATOR(Add, +);
- DEFINE_BINARY_OPERATOR(Subtract, -);
- DEFINE_BINARY_OPERATOR(Multiply, *);
- DEFINE_BINARY_OPERATOR(BitAnd, &);
- DEFINE_BINARY_OPERATOR(BitOr, |);
- DEFINE_BINARY_OPERATOR(BitXor, ^);
- #undef DEFINE_BINARY_OPERATOR
- struct Divide {
- template<typename Lhs, typename Rhs>
- auto operator()(Lhs lhs, Rhs rhs) const
- {
- if constexpr (IsFloatingPoint<Lhs>) {
- return lhs / rhs;
- } else {
- Checked value(lhs);
- value /= rhs;
- if (value.has_overflow())
- return AK::Result<Lhs, StringView>("Integer division overflow"sv);
- return AK::Result<Lhs, StringView>(value.value());
- }
- }
- static StringView name() { return "/"; }
- };
- struct Modulo {
- template<typename Lhs, typename Rhs>
- auto operator()(Lhs lhs, Rhs rhs) const
- {
- if (rhs == 0)
- return AK::Result<Lhs, StringView>("Integer division overflow"sv);
- if constexpr (IsSigned<Lhs>) {
- if (rhs == -1)
- return AK::Result<Lhs, StringView>(0); // Spec weirdness right here, signed division overflow is ignored.
- }
- return AK::Result<Lhs, StringView>(lhs % rhs);
- }
- static StringView name() { return "%"; }
- };
- struct BitShiftLeft {
- template<typename Lhs, typename Rhs>
- auto operator()(Lhs lhs, Rhs rhs) const { return lhs << (rhs % (sizeof(lhs) * 8)); }
- static StringView name() { return "<<"; }
- };
- struct BitShiftRight {
- template<typename Lhs, typename Rhs>
- auto operator()(Lhs lhs, Rhs rhs) const { return lhs >> (rhs % (sizeof(lhs) * 8)); }
- static StringView name() { return ">>"; }
- };
- struct BitRotateLeft {
- template<typename Lhs, typename Rhs>
- auto operator()(Lhs lhs, Rhs rhs) const
- {
- // generates a single 'rol' instruction if shift is positive
- // otherwise generate a `ror`
- auto const mask = CHAR_BIT * sizeof(Lhs) - 1;
- rhs &= mask;
- return (lhs << rhs) | (lhs >> ((-rhs) & mask));
- }
- static StringView name() { return "rotate_left"; }
- };
- struct BitRotateRight {
- template<typename Lhs, typename Rhs>
- auto operator()(Lhs lhs, Rhs rhs) const
- {
- // generates a single 'ror' instruction if shift is positive
- // otherwise generate a `rol`
- auto const mask = CHAR_BIT * sizeof(Lhs) - 1;
- rhs &= mask;
- return (lhs >> rhs) | (lhs << ((-rhs) & mask));
- }
- static StringView name() { return "rotate_right"; }
- };
- struct Minimum {
- template<typename Lhs, typename Rhs>
- auto operator()(Lhs lhs, Rhs rhs) const
- {
- if constexpr (IsFloatingPoint<Lhs> || IsFloatingPoint<Rhs>) {
- if (isnan(lhs))
- return lhs;
- if (isnan(rhs))
- return rhs;
- if (isinf(lhs))
- return lhs > 0 ? rhs : lhs;
- if (isinf(rhs))
- return rhs > 0 ? lhs : rhs;
- }
- return min(lhs, rhs);
- }
- static StringView name() { return "minimum"; }
- };
- struct Maximum {
- template<typename Lhs, typename Rhs>
- auto operator()(Lhs lhs, Rhs rhs) const
- {
- if constexpr (IsFloatingPoint<Lhs> || IsFloatingPoint<Rhs>) {
- if (isnan(lhs))
- return lhs;
- if (isnan(rhs))
- return rhs;
- if (isinf(lhs))
- return lhs > 0 ? lhs : rhs;
- if (isinf(rhs))
- return rhs > 0 ? rhs : lhs;
- }
- return max(lhs, rhs);
- }
- static StringView name() { return "maximum"; }
- };
- struct CopySign {
- template<typename Lhs, typename Rhs>
- auto operator()(Lhs lhs, Rhs rhs) const
- {
- if constexpr (IsSame<Lhs, float>)
- return copysignf(lhs, rhs);
- else if constexpr (IsSame<Lhs, double>)
- return copysign(lhs, rhs);
- else
- static_assert(DependentFalse<Lhs, Rhs>, "Invalid types to CopySign");
- }
- static StringView name() { return "copysign"; }
- };
- // Unary
- struct EqualsZero {
- template<typename Lhs>
- auto operator()(Lhs lhs) const { return lhs == 0; }
- static StringView name() { return "== 0"; }
- };
- struct CountLeadingZeros {
- template<typename Lhs>
- i32 operator()(Lhs lhs) const
- {
- if (lhs == 0)
- return sizeof(Lhs) * CHAR_BIT;
- if constexpr (sizeof(Lhs) == 4 || sizeof(Lhs) == 8)
- return count_leading_zeroes(MakeUnsigned<Lhs>(lhs));
- else
- VERIFY_NOT_REACHED();
- }
- static StringView name() { return "clz"; }
- };
- struct CountTrailingZeros {
- template<typename Lhs>
- i32 operator()(Lhs lhs) const
- {
- if (lhs == 0)
- return sizeof(Lhs) * CHAR_BIT;
- if constexpr (sizeof(Lhs) == 4 || sizeof(Lhs) == 8)
- return count_trailing_zeroes(MakeUnsigned<Lhs>(lhs));
- else
- VERIFY_NOT_REACHED();
- }
- static StringView name() { return "ctz"; }
- };
- struct PopCount {
- template<typename Lhs>
- auto operator()(Lhs lhs) const
- {
- if constexpr (sizeof(Lhs) == 4 || sizeof(Lhs) == 8)
- return popcount(MakeUnsigned<Lhs>(lhs));
- else
- VERIFY_NOT_REACHED();
- }
- static StringView name() { return "popcnt"; }
- };
- struct Absolute {
- template<typename Lhs>
- auto operator()(Lhs lhs) const { return AK::abs(lhs); }
- static StringView name() { return "abs"; }
- };
- struct Negate {
- template<typename Lhs>
- auto operator()(Lhs lhs) const { return -lhs; }
- static StringView name() { return "== 0"; }
- };
- struct Ceil {
- template<typename Lhs>
- auto operator()(Lhs lhs) const
- {
- if constexpr (IsSame<Lhs, float>)
- return ceilf(lhs);
- else if constexpr (IsSame<Lhs, double>)
- return ceil(lhs);
- else
- VERIFY_NOT_REACHED();
- }
- static StringView name() { return "ceil"; }
- };
- struct Floor {
- template<typename Lhs>
- auto operator()(Lhs lhs) const
- {
- if constexpr (IsSame<Lhs, float>)
- return floorf(lhs);
- else if constexpr (IsSame<Lhs, double>)
- return floor(lhs);
- else
- VERIFY_NOT_REACHED();
- }
- static StringView name() { return "floor"; }
- };
- struct Truncate {
- template<typename Lhs>
- Result<Lhs, StringView> operator()(Lhs lhs) const
- {
- if constexpr (IsSame<Lhs, float>)
- return truncf(lhs);
- else if constexpr (IsSame<Lhs, double>)
- return trunc(lhs);
- else
- VERIFY_NOT_REACHED();
- }
- static StringView name() { return "truncate"; }
- };
- struct NearbyIntegral {
- template<typename Lhs>
- auto operator()(Lhs lhs) const
- {
- if constexpr (IsSame<Lhs, float>)
- return nearbyintf(lhs);
- else if constexpr (IsSame<Lhs, double>)
- return nearbyint(lhs);
- else
- VERIFY_NOT_REACHED();
- }
- static StringView name() { return "round"; }
- };
- struct SquareRoot {
- template<typename Lhs>
- auto operator()(Lhs lhs) const
- {
- if constexpr (IsSame<Lhs, float>)
- return sqrtf(lhs);
- else if constexpr (IsSame<Lhs, double>)
- return sqrt(lhs);
- else
- VERIFY_NOT_REACHED();
- }
- static StringView name() { return "sqrt"; }
- };
- template<typename Result>
- struct Wrap {
- template<typename Lhs>
- Result operator()(Lhs lhs) const
- {
- return static_cast<MakeUnsigned<Result>>(bit_cast<MakeUnsigned<Lhs>>(lhs));
- }
- static StringView name() { return "wrap"; }
- };
- template<typename ResultT>
- struct CheckedTruncate {
- template<typename Lhs>
- AK::Result<ResultT, StringView> operator()(Lhs lhs) const
- {
- if (isnan(lhs) || isinf(lhs)) // "undefined", let's just trap.
- return "Truncation undefined behavior"sv;
- Lhs truncated;
- if constexpr (IsSame<float, Lhs>)
- truncated = truncf(lhs);
- else if constexpr (IsSame<double, Lhs>)
- truncated = trunc(lhs);
- else
- VERIFY_NOT_REACHED();
- // FIXME: This function assumes that all values of ResultT are representable in Lhs
- // the assumption comes from the fact that this was used exclusively by LibJS,
- // which only considers values that are all representable in 'double'.
- if (!AK::is_within_range<ResultT>(truncated))
- return "Truncation out of range"sv;
- return static_cast<ResultT>(truncated);
- }
- static StringView name() { return "truncate.checked"; }
- };
- template<typename ResultT>
- struct Extend {
- template<typename Lhs>
- ResultT operator()(Lhs lhs) const
- {
- return lhs;
- }
- static StringView name() { return "extend"; }
- };
- template<typename ResultT>
- struct Convert {
- template<typename Lhs>
- ResultT operator()(Lhs lhs) const
- {
- auto signed_interpretation = bit_cast<MakeSigned<Lhs>>(lhs);
- return static_cast<ResultT>(signed_interpretation);
- }
- static StringView name() { return "convert"; }
- };
- template<typename ResultT>
- struct Reinterpret {
- template<typename Lhs>
- ResultT operator()(Lhs lhs) const
- {
- return bit_cast<ResultT>(lhs);
- }
- static StringView name() { return "reinterpret"; }
- };
- struct Promote {
- double operator()(float lhs) const
- {
- if (isnan(lhs))
- return nan(""); // FIXME: Ensure canonical NaN remains canonical
- return static_cast<double>(lhs);
- }
- static StringView name() { return "promote"; }
- };
- struct Demote {
- float operator()(double lhs) const
- {
- if (isnan(lhs))
- return nanf(""); // FIXME: Ensure canonical NaN remains canonical
- if (isinf(lhs))
- return __builtin_huge_valf();
- return static_cast<float>(lhs);
- }
- static StringView name() { return "demote"; }
- };
- template<typename InitialType>
- struct SignExtend {
- template<typename Lhs>
- Lhs operator()(Lhs lhs) const
- {
- auto unsigned_representation = bit_cast<MakeUnsigned<Lhs>>(lhs);
- auto truncated_unsigned_representation = static_cast<MakeUnsigned<InitialType>>(unsigned_representation);
- auto initial_value = bit_cast<InitialType>(truncated_unsigned_representation);
- return static_cast<Lhs>(initial_value);
- }
- static StringView name() { return "extend"; }
- };
- template<typename ResultT>
- struct SaturatingTruncate {
- template<typename Lhs>
- ResultT operator()(Lhs lhs) const
- {
- if (isnan(lhs))
- return 0;
- if (isinf(lhs)) {
- if (lhs < 0)
- return NumericLimits<ResultT>::min();
- return NumericLimits<ResultT>::max();
- }
- // FIXME: This assumes that all values in ResultT are representable in 'double'.
- // that assumption is not correct, which makes this function yield incorrect values
- // for 'edge' values of type i64.
- constexpr auto convert = [](auto truncated_value) {
- if (truncated_value < NumericLimits<ResultT>::min())
- return NumericLimits<ResultT>::min();
- if (static_cast<double>(truncated_value) > static_cast<double>(NumericLimits<ResultT>::max()))
- return NumericLimits<ResultT>::max();
- return static_cast<ResultT>(truncated_value);
- };
- if constexpr (IsSame<Lhs, float>)
- return convert(truncf(lhs));
- else
- return convert(trunc(lhs));
- }
- static StringView name() { return "truncate.saturating"; }
- };
- }
|