mirror of
				https://github.com/LadybirdBrowser/ladybird.git
				synced 2025-11-03 23:00:58 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			677 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			677 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (c) 2021-2023, 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/SIMD.h>
 | 
						|
#include <AK/StringView.h>
 | 
						|
#include <AK/Types.h>
 | 
						|
#include <limits.h>
 | 
						|
#include <math.h>
 | 
						|
 | 
						|
namespace Wasm::Operators {
 | 
						|
 | 
						|
using namespace AK::SIMD;
 | 
						|
 | 
						|
#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##sv;              \
 | 
						|
        }                                       \
 | 
						|
    }
 | 
						|
 | 
						|
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::ErrorOr<Lhs, StringView>("Integer division overflow"sv);
 | 
						|
            return AK::ErrorOr<Lhs, StringView>(value.value());
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    static StringView name() { return "/"sv; }
 | 
						|
};
 | 
						|
 | 
						|
struct Modulo {
 | 
						|
    template<typename Lhs, typename Rhs>
 | 
						|
    auto operator()(Lhs lhs, Rhs rhs) const
 | 
						|
    {
 | 
						|
        if (rhs == 0)
 | 
						|
            return AK::ErrorOr<Lhs, StringView>("Integer division overflow"sv);
 | 
						|
        if constexpr (IsSigned<Lhs>) {
 | 
						|
            if (rhs == -1)
 | 
						|
                return AK::ErrorOr<Lhs, StringView>(0); // Spec weirdness right here, signed division overflow is ignored.
 | 
						|
        }
 | 
						|
        return AK::ErrorOr<Lhs, StringView>(lhs % rhs);
 | 
						|
    }
 | 
						|
 | 
						|
    static StringView name() { return "%"sv; }
 | 
						|
};
 | 
						|
 | 
						|
struct BitShiftLeft {
 | 
						|
    template<typename Lhs, typename Rhs>
 | 
						|
    auto operator()(Lhs lhs, Rhs rhs) const { return lhs << (rhs % (sizeof(lhs) * 8)); }
 | 
						|
 | 
						|
    static StringView name() { return "<<"sv; }
 | 
						|
};
 | 
						|
 | 
						|
struct BitShiftRight {
 | 
						|
    template<typename Lhs, typename Rhs>
 | 
						|
    auto operator()(Lhs lhs, Rhs rhs) const { return lhs >> (rhs % (sizeof(lhs) * 8)); }
 | 
						|
 | 
						|
    static StringView name() { return ">>"sv; }
 | 
						|
};
 | 
						|
 | 
						|
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"sv; }
 | 
						|
};
 | 
						|
 | 
						|
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"sv; }
 | 
						|
};
 | 
						|
 | 
						|
template<size_t VectorSize>
 | 
						|
struct VectorShiftLeft {
 | 
						|
    auto operator()(u128 lhs, i32 rhs) const
 | 
						|
    {
 | 
						|
        auto shift_value = rhs % (sizeof(lhs) * 8 / VectorSize);
 | 
						|
        return bit_cast<u128>(bit_cast<Native128ByteVectorOf<NativeIntegralType<128 / VectorSize>, MakeUnsigned>>(lhs) << shift_value);
 | 
						|
    }
 | 
						|
    static StringView name()
 | 
						|
    {
 | 
						|
        switch (VectorSize) {
 | 
						|
        case 16:
 | 
						|
            return "vec(8x16)<<"sv;
 | 
						|
        case 8:
 | 
						|
            return "vec(16x8)<<"sv;
 | 
						|
        case 4:
 | 
						|
            return "vec(32x4)<<"sv;
 | 
						|
        case 2:
 | 
						|
            return "vec(64x2)<<"sv;
 | 
						|
        default:
 | 
						|
            VERIFY_NOT_REACHED();
 | 
						|
        }
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
template<size_t VectorSize, template<typename> typename SetSign>
 | 
						|
struct VectorShiftRight {
 | 
						|
    auto operator()(u128 lhs, i32 rhs) const
 | 
						|
    {
 | 
						|
        auto shift_value = rhs % (sizeof(lhs) * 8 / VectorSize);
 | 
						|
        return bit_cast<u128>(bit_cast<Native128ByteVectorOf<NativeIntegralType<128 / VectorSize>, SetSign>>(lhs) >> shift_value);
 | 
						|
    }
 | 
						|
    static StringView name()
 | 
						|
    {
 | 
						|
        switch (VectorSize) {
 | 
						|
        case 16:
 | 
						|
            return "vec(8x16)>>"sv;
 | 
						|
        case 8:
 | 
						|
            return "vec(16x8)>>"sv;
 | 
						|
        case 4:
 | 
						|
            return "vec(32x4)>>"sv;
 | 
						|
        case 2:
 | 
						|
            return "vec(64x2)>>"sv;
 | 
						|
        default:
 | 
						|
            VERIFY_NOT_REACHED();
 | 
						|
        }
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
struct VectorSwizzle {
 | 
						|
    auto operator()(u128 c1, u128 c2) const
 | 
						|
    {
 | 
						|
        // https://webassembly.github.io/spec/core/bikeshed/#-mathsfi8x16hrefsyntax-instr-vecmathsfswizzle%E2%91%A0
 | 
						|
        auto i = bit_cast<Native128ByteVectorOf<i8, MakeSigned>>(c2);
 | 
						|
        auto j = bit_cast<Native128ByteVectorOf<i8, MakeSigned>>(c1);
 | 
						|
        auto result = AK::SIMD::shuffle(i, j);
 | 
						|
        return bit_cast<u128>(result);
 | 
						|
    }
 | 
						|
    static StringView name() { return "vec(8x16).swizzle"sv; }
 | 
						|
};
 | 
						|
 | 
						|
template<size_t VectorSize, template<typename> typename SetSign>
 | 
						|
struct VectorExtractLane {
 | 
						|
    size_t lane;
 | 
						|
 | 
						|
    auto operator()(u128 c) const
 | 
						|
    {
 | 
						|
        auto result = bit_cast<Native128ByteVectorOf<NativeIntegralType<128 / VectorSize>, SetSign>>(c);
 | 
						|
        return result[lane];
 | 
						|
    }
 | 
						|
 | 
						|
    static StringView name()
 | 
						|
    {
 | 
						|
        switch (VectorSize) {
 | 
						|
        case 16:
 | 
						|
            return "vec(8x16).extract_lane"sv;
 | 
						|
        case 8:
 | 
						|
            return "vec(16x8).extract_lane"sv;
 | 
						|
        case 4:
 | 
						|
            return "vec(32x4).extract_lane"sv;
 | 
						|
        case 2:
 | 
						|
            return "vec(64x2).extract_lane"sv;
 | 
						|
        default:
 | 
						|
            VERIFY_NOT_REACHED();
 | 
						|
        }
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
template<size_t VectorSize>
 | 
						|
struct VectorExtractLaneFloat {
 | 
						|
    size_t lane;
 | 
						|
 | 
						|
    auto operator()(u128 c) const
 | 
						|
    {
 | 
						|
        auto result = bit_cast<NativeFloatingVectorType<128 / VectorSize, VectorSize>>(c);
 | 
						|
        return result[lane];
 | 
						|
    }
 | 
						|
 | 
						|
    static StringView name()
 | 
						|
    {
 | 
						|
        switch (VectorSize) {
 | 
						|
        case 16:
 | 
						|
            return "vec(8x16).extract_lane"sv;
 | 
						|
        case 8:
 | 
						|
            return "vec(16x8).extract_lane"sv;
 | 
						|
        case 4:
 | 
						|
            return "vec(32x4).extract_lane"sv;
 | 
						|
        case 2:
 | 
						|
            return "vec(64x2).extract_lane"sv;
 | 
						|
        default:
 | 
						|
            VERIFY_NOT_REACHED();
 | 
						|
        }
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
template<size_t VectorSize, typename TrueValueType = NativeIntegralType<128 / VectorSize>>
 | 
						|
struct VectorReplaceLane {
 | 
						|
    size_t lane;
 | 
						|
    using ValueType = Conditional<IsFloatingPoint<TrueValueType>, NativeFloatingType<128 / VectorSize>, NativeIntegralType<128 / VectorSize>>;
 | 
						|
 | 
						|
    auto operator()(u128 c, TrueValueType value) const
 | 
						|
    {
 | 
						|
        auto result = bit_cast<Native128ByteVectorOf<ValueType, MakeUnsigned>>(c);
 | 
						|
        result[lane] = static_cast<ValueType>(value);
 | 
						|
        return bit_cast<u128>(result);
 | 
						|
    }
 | 
						|
 | 
						|
    static StringView name()
 | 
						|
    {
 | 
						|
        switch (VectorSize) {
 | 
						|
        case 16:
 | 
						|
            return "vec(8x16).replace_lane"sv;
 | 
						|
        case 8:
 | 
						|
            return "vec(16x8).replace_lane"sv;
 | 
						|
        case 4:
 | 
						|
            return "vec(32x4).replace_lane"sv;
 | 
						|
        case 2:
 | 
						|
            return "vec(64x2).replace_lane"sv;
 | 
						|
        default:
 | 
						|
            VERIFY_NOT_REACHED();
 | 
						|
        }
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
template<size_t VectorSize, typename Op, template<typename> typename SetSign = MakeSigned>
 | 
						|
struct VectorCmpOp {
 | 
						|
    auto operator()(u128 c1, u128 c2) const
 | 
						|
    {
 | 
						|
        using ElementType = NativeIntegralType<128 / VectorSize>;
 | 
						|
        auto result = bit_cast<Native128ByteVectorOf<ElementType, SetSign>>(c1);
 | 
						|
        auto other = bit_cast<Native128ByteVectorOf<ElementType, SetSign>>(c2);
 | 
						|
        Op op;
 | 
						|
        for (size_t i = 0; i < VectorSize; ++i)
 | 
						|
            result[i] = op(result[i], other[i]) ? static_cast<MakeUnsigned<ElementType>>(-1) : 0;
 | 
						|
        return bit_cast<u128>(result);
 | 
						|
    }
 | 
						|
 | 
						|
    static StringView name()
 | 
						|
    {
 | 
						|
        switch (VectorSize) {
 | 
						|
        case 16:
 | 
						|
            return "vec(8x16).cmp"sv;
 | 
						|
        case 8:
 | 
						|
            return "vec(16x8).cmp"sv;
 | 
						|
        case 4:
 | 
						|
            return "vec(32x4).cmp"sv;
 | 
						|
        case 2:
 | 
						|
            return "vec(64x2).cmp"sv;
 | 
						|
        default:
 | 
						|
            VERIFY_NOT_REACHED();
 | 
						|
        }
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
template<size_t VectorSize, typename Op>
 | 
						|
struct VectorFloatCmpOp {
 | 
						|
    auto operator()(u128 c1, u128 c2) const
 | 
						|
    {
 | 
						|
        auto first = bit_cast<NativeFloatingVectorType<128, VectorSize, NativeFloatingType<128 / VectorSize>>>(c1);
 | 
						|
        auto other = bit_cast<NativeFloatingVectorType<128, VectorSize, NativeFloatingType<128 / VectorSize>>>(c2);
 | 
						|
        using ElementType = NativeIntegralType<128 / VectorSize>;
 | 
						|
        Native128ByteVectorOf<ElementType, MakeUnsigned> result;
 | 
						|
        Op op;
 | 
						|
        for (size_t i = 0; i < VectorSize; ++i)
 | 
						|
            result[i] = op(first[i], other[i]) ? static_cast<ElementType>(-1) : 0;
 | 
						|
        return bit_cast<u128>(result);
 | 
						|
    }
 | 
						|
 | 
						|
    static StringView name()
 | 
						|
    {
 | 
						|
        switch (VectorSize) {
 | 
						|
        case 4:
 | 
						|
            return "vecf(32x4).cmp"sv;
 | 
						|
        case 2:
 | 
						|
            return "vecf(64x2).cmp"sv;
 | 
						|
        default:
 | 
						|
            VERIFY_NOT_REACHED();
 | 
						|
        }
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
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"sv; }
 | 
						|
};
 | 
						|
 | 
						|
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"sv; }
 | 
						|
};
 | 
						|
 | 
						|
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"sv; }
 | 
						|
};
 | 
						|
 | 
						|
// Unary
 | 
						|
 | 
						|
struct EqualsZero {
 | 
						|
    template<typename Lhs>
 | 
						|
    auto operator()(Lhs lhs) const { return lhs == 0; }
 | 
						|
 | 
						|
    static StringView name() { return "== 0"sv; }
 | 
						|
};
 | 
						|
 | 
						|
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"sv; }
 | 
						|
};
 | 
						|
 | 
						|
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"sv; }
 | 
						|
};
 | 
						|
 | 
						|
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"sv; }
 | 
						|
};
 | 
						|
 | 
						|
struct Absolute {
 | 
						|
    template<typename Lhs>
 | 
						|
    auto operator()(Lhs lhs) const { return AK::abs(lhs); }
 | 
						|
 | 
						|
    static StringView name() { return "abs"sv; }
 | 
						|
};
 | 
						|
 | 
						|
struct Negate {
 | 
						|
    template<typename Lhs>
 | 
						|
    auto operator()(Lhs lhs) const { return -lhs; }
 | 
						|
 | 
						|
    static StringView name() { return "== 0"sv; }
 | 
						|
};
 | 
						|
 | 
						|
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"sv; }
 | 
						|
};
 | 
						|
 | 
						|
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"sv; }
 | 
						|
};
 | 
						|
 | 
						|
struct Truncate {
 | 
						|
    template<typename Lhs>
 | 
						|
    AK::ErrorOr<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"sv; }
 | 
						|
};
 | 
						|
 | 
						|
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"sv; }
 | 
						|
};
 | 
						|
 | 
						|
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"sv; }
 | 
						|
};
 | 
						|
 | 
						|
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"sv; }
 | 
						|
};
 | 
						|
 | 
						|
template<typename ResultT>
 | 
						|
struct CheckedTruncate {
 | 
						|
    template<typename Lhs>
 | 
						|
    AK::ErrorOr<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"sv; }
 | 
						|
};
 | 
						|
 | 
						|
template<typename ResultT>
 | 
						|
struct Extend {
 | 
						|
    template<typename Lhs>
 | 
						|
    ResultT operator()(Lhs lhs) const
 | 
						|
    {
 | 
						|
        return lhs;
 | 
						|
    }
 | 
						|
 | 
						|
    static StringView name() { return "extend"sv; }
 | 
						|
};
 | 
						|
 | 
						|
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"sv; }
 | 
						|
};
 | 
						|
 | 
						|
template<typename ResultT>
 | 
						|
struct Reinterpret {
 | 
						|
    template<typename Lhs>
 | 
						|
    ResultT operator()(Lhs lhs) const
 | 
						|
    {
 | 
						|
        return bit_cast<ResultT>(lhs);
 | 
						|
    }
 | 
						|
 | 
						|
    static StringView name() { return "reinterpret"sv; }
 | 
						|
};
 | 
						|
 | 
						|
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"sv; }
 | 
						|
};
 | 
						|
 | 
						|
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"sv; }
 | 
						|
};
 | 
						|
 | 
						|
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"sv; }
 | 
						|
};
 | 
						|
 | 
						|
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 = []<typename ConvertT>(ConvertT truncated_value) {
 | 
						|
            if (truncated_value < NumericLimits<ResultT>::min())
 | 
						|
                return NumericLimits<ResultT>::min();
 | 
						|
            if constexpr (IsSame<ConvertT, float>) {
 | 
						|
                if (truncated_value >= static_cast<ConvertT>(NumericLimits<ResultT>::max()))
 | 
						|
                    return NumericLimits<ResultT>::max();
 | 
						|
            } else {
 | 
						|
                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"sv; }
 | 
						|
};
 | 
						|
 | 
						|
}
 |