12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157 |
- /*
- * 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/SIMDExtras.h>
- #include <AK/StringView.h>
- #include <AK/Types.h>
- #include <LibWasm/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 Average {
- template<typename Lhs, typename Rhs>
- auto operator()(Lhs lhs, Rhs rhs) const
- {
- return static_cast<Lhs>((lhs + rhs + 1) / 2);
- }
- static StringView name() { return "avgr"sv; }
- };
- struct Q15Mul {
- template<typename Lhs, typename Rhs>
- auto operator()(Lhs lhs, Rhs rhs) const
- {
- return (lhs * rhs + 0x4000) >> 15;
- }
- static StringView name() { return "q15mul"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 BitAndNot {
- template<typename Lhs, typename Rhs>
- auto operator()(Lhs lhs, Rhs rhs) const { return lhs & ~rhs; }
- static StringView name() { return "andnot"sv; }
- };
- struct BitNot {
- template<typename Lhs>
- auto operator()(Lhs lhs) const { return ~lhs; }
- 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, template<typename> typename SetSign = MakeSigned>
- struct VectorAllTrue {
- auto operator()(u128 c) const
- {
- using ElementType = NativeIntegralType<128 / VectorSize>;
- auto any_false = bit_cast<Native128ByteVectorOf<ElementType, SetSign>>(c) == 0;
- return bit_cast<u128>(any_false) == 0;
- }
- static StringView name()
- {
- switch (VectorSize) {
- case 16:
- return "vec(8x16).all_true"sv;
- case 8:
- return "vec(16x8).all_true"sv;
- case 4:
- return "vec(32x4).all_true"sv;
- case 2:
- return "vec(64x2).all_true"sv;
- default:
- VERIFY_NOT_REACHED();
- }
- }
- };
- 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<SetSign<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>>(c1);
- auto j = bit_cast<Native128ByteVectorOf<i8, MakeSigned>>(c2);
- auto result = shuffle_or_0(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) {
- SetSign<ElementType> lhs = result[i];
- SetSign<ElementType> rhs = other[i];
- result[i] = op(lhs, rhs) ? 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) || isnan(rhs)) {
- return isnan(lhs) ? lhs : rhs;
- }
- if (lhs == 0 && rhs == 0) {
- return signbit(lhs) ? 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) || isnan(rhs)) {
- return isnan(lhs) ? lhs : rhs;
- }
- if (lhs == 0 && rhs == 0) {
- return signbit(lhs) ? rhs : lhs;
- }
- }
- return max(lhs, rhs);
- }
- static StringView name() { return "maximum"sv; }
- };
- struct PseudoMinimum {
- template<typename Lhs, typename Rhs>
- auto operator()(Lhs lhs, Rhs rhs) const
- {
- return rhs < lhs ? rhs : lhs;
- }
- static StringView name() { return "pseudo_minimum"sv; }
- };
- struct PseudoMaximum {
- template<typename Lhs, typename Rhs>
- auto operator()(Lhs lhs, Rhs rhs) const
- {
- return lhs < rhs ? rhs : lhs;
- }
- static StringView name() { return "pseudo_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) == 1 || sizeof(Lhs) == 2 || 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>
- Lhs operator()(Lhs lhs) const
- {
- if constexpr (IsFloatingPoint<Lhs>)
- return AK::abs(lhs);
- if constexpr (IsSigned<Lhs>) {
- if (lhs == NumericLimits<Lhs>::min())
- return NumericLimits<Lhs>::min(); // Return the negation of _i_ modulo 2^N: https://www.w3.org/TR/wasm-core-2/#-hrefop-iabsmathrmiabs_n-i step 3
- }
- return AK::abs(lhs);
- }
- static StringView name() { return "abs"sv; }
- };
- struct Negate {
- template<typename Lhs>
- Lhs operator()(Lhs lhs) const
- {
- if constexpr (IsFloatingPoint<Lhs>)
- return -lhs;
- if constexpr (IsSigned<Lhs>) {
- if (lhs == NumericLimits<Lhs>::min())
- return NumericLimits<Lhs>::min(); // Return the negation of _i_ modulo 2^N: https://www.w3.org/TR/wasm-core-2/#-hrefop-iabsmathrmiabs_n-i step 3
- }
- 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; }
- };
- template<size_t VectorSize, typename Op, template<typename> typename SetSign = MakeSigned>
- struct VectorIntegerExtOpPairwise {
- auto operator()(u128 c) const
- {
- using VectorResult = NativeVectorType<128 / VectorSize, VectorSize, SetSign>;
- using VectorInput = NativeVectorType<128 / (VectorSize * 2), VectorSize * 2, SetSign>;
- auto vector = bit_cast<VectorInput>(c);
- VectorResult result;
- Op op;
- // FIXME: Find a way to not loop here
- for (size_t i = 0; i < VectorSize; ++i) {
- result[i] = op(vector[i * 2], vector[(i * 2) + 1]);
- }
- return bit_cast<u128>(result);
- }
- static StringView name()
- {
- switch (VectorSize) {
- case 8:
- return "vec(16x8).ext_op_pairwise(8x16)"sv;
- case 4:
- return "vec(32x4).ext_op_pairwise(16x8)"sv;
- case 2:
- return "vec(64x2).ext_op_pairwise(32x4)"sv;
- default:
- VERIFY_NOT_REACHED();
- }
- }
- };
- enum class VectorExt {
- High,
- Low,
- };
- template<size_t VectorSize, VectorExt Mode, template<typename> typename SetSign = MakeSigned>
- struct VectorIntegerExt {
- auto operator()(u128 c) const
- {
- using VectorResult = NativeVectorType<128 / VectorSize, VectorSize, SetSign>;
- using VectorInput = NativeVectorType<128 / (VectorSize * 2), VectorSize * 2, SetSign>;
- auto vector = bit_cast<VectorInput>(c);
- VectorResult result;
- // FIXME: Find a way to not loop here
- for (size_t i = 0; i < VectorSize; ++i) {
- if constexpr (Mode == VectorExt::High)
- result[i] = vector[VectorSize + i];
- else if constexpr (Mode == VectorExt::Low)
- result[i] = vector[i];
- else
- VERIFY_NOT_REACHED();
- }
- return bit_cast<u128>(result);
- }
- static StringView name()
- {
- switch (VectorSize) {
- case 8:
- return "vec(16x8).ext(8x16)"sv;
- case 4:
- return "vec(32x4).ext(16x8)"sv;
- case 2:
- return "vec(64x2).ext(32x4)"sv;
- default:
- VERIFY_NOT_REACHED();
- }
- }
- };
- template<size_t VectorSize, typename Op, VectorExt Mode, template<typename> typename SetSign = MakeSigned>
- struct VectorIntegerExtOp {
- auto operator()(u128 lhs, u128 rhs) const
- {
- using VectorResult = NativeVectorType<128 / VectorSize, VectorSize, SetSign>;
- using VectorInput = NativeVectorType<128 / (VectorSize * 2), VectorSize * 2, SetSign>;
- auto first = bit_cast<VectorInput>(lhs);
- auto second = bit_cast<VectorInput>(rhs);
- VectorResult result;
- Op op;
- using ResultType = SetSign<NativeIntegralType<128 / VectorSize>>;
- // FIXME: Find a way to not loop here
- for (size_t i = 0; i < VectorSize; ++i) {
- if constexpr (Mode == VectorExt::High) {
- ResultType a = first[VectorSize + i];
- ResultType b = second[VectorSize + i];
- result[i] = op(a, b);
- } else if constexpr (Mode == VectorExt::Low) {
- ResultType a = first[i];
- ResultType b = second[i];
- result[i] = op(a, b);
- } else
- VERIFY_NOT_REACHED();
- }
- return bit_cast<u128>(result);
- }
- static StringView name()
- {
- switch (VectorSize) {
- case 8:
- return "vec(16x8).ext_op(8x16)"sv;
- case 4:
- return "vec(32x4).ext_op(16x8)"sv;
- case 2:
- return "vec(64x2).ext_op(32x4)"sv;
- default:
- VERIFY_NOT_REACHED();
- }
- }
- };
- template<size_t VectorSize, typename Op, template<typename> typename SetSign = MakeSigned>
- struct VectorIntegerBinaryOp {
- auto operator()(u128 lhs, u128 rhs) const
- {
- using VectorType = NativeVectorType<128 / VectorSize, VectorSize, SetSign>;
- auto first = bit_cast<VectorType>(lhs);
- auto second = bit_cast<VectorType>(rhs);
- VectorType result;
- Op op;
- // FIXME: Find a way to not loop here
- for (size_t i = 0; i < VectorSize; ++i) {
- result[i] = op(first[i], second[i]);
- }
- return bit_cast<u128>(result);
- }
- static StringView name()
- {
- switch (VectorSize) {
- case 16:
- return "vec(8x16).binary_op"sv;
- case 8:
- return "vec(16x8).binary_op"sv;
- case 4:
- return "vec(32x4).binary_op"sv;
- case 2:
- return "vec(64x2).binary_op"sv;
- default:
- VERIFY_NOT_REACHED();
- }
- }
- };
- template<size_t VectorSize>
- struct VectorBitmask {
- auto operator()(u128 lhs) const
- {
- using VectorType = NativeVectorType<128 / VectorSize, VectorSize, MakeSigned>;
- auto value = bit_cast<VectorType>(lhs);
- u32 result = 0;
- for (size_t i = 0; i < VectorSize; ++i)
- result |= static_cast<u32>(value[i] < 0) << i;
- return result;
- }
- static StringView name() { return "bitmask"sv; }
- };
- template<size_t VectorSize>
- struct VectorDotProduct {
- auto operator()(u128 lhs, u128 rhs) const
- {
- using VectorInput = NativeVectorType<128 / (VectorSize * 2), VectorSize * 2, MakeSigned>;
- using VectorResult = NativeVectorType<128 / VectorSize, VectorSize, MakeSigned>;
- auto v1 = bit_cast<VectorInput>(lhs);
- auto v2 = bit_cast<VectorInput>(rhs);
- VectorResult result;
- using ResultType = MakeUnsigned<NativeIntegralType<128 / VectorSize>>;
- for (size_t i = 0; i < VectorSize; ++i) {
- ResultType low = v1[i * 2] * v2[i * 2];
- ResultType high = v1[(i * 2) + 1] * v2[(i * 2) + 1];
- result[i] = low + high;
- }
- return bit_cast<u128>(result);
- }
- static StringView name() { return "dot"sv; }
- };
- template<size_t VectorSize, typename Element>
- struct VectorNarrow {
- auto operator()(u128 lhs, u128 rhs) const
- {
- using VectorInput = NativeVectorType<128 / (VectorSize / 2), VectorSize / 2, MakeSigned>;
- using VectorResult = NativeVectorType<128 / VectorSize, VectorSize, MakeUnsigned>;
- auto v1 = bit_cast<VectorInput>(lhs);
- auto v2 = bit_cast<VectorInput>(rhs);
- VectorResult result;
- for (size_t i = 0; i < (VectorSize / 2); ++i) {
- if (v1[i] <= NumericLimits<Element>::min())
- result[i] = NumericLimits<Element>::min();
- else if (v1[i] >= NumericLimits<Element>::max())
- result[i] = NumericLimits<Element>::max();
- else
- result[i] = v1[i];
- }
- for (size_t i = 0; i < (VectorSize / 2); ++i) {
- if (v2[i] <= NumericLimits<Element>::min())
- result[i + VectorSize / 2] = NumericLimits<Element>::min();
- else if (v2[i] >= NumericLimits<Element>::max())
- result[i + VectorSize / 2] = NumericLimits<Element>::max();
- else
- result[i + VectorSize / 2] = v2[i];
- }
- return bit_cast<u128>(result);
- }
- static StringView name() { return "narrow"sv; }
- };
- template<size_t VectorSize, typename Op, template<typename> typename SetSign = MakeSigned>
- struct VectorIntegerUnaryOp {
- auto operator()(u128 lhs) const
- {
- using VectorType = NativeVectorType<128 / VectorSize, VectorSize, SetSign>;
- auto value = bit_cast<VectorType>(lhs);
- VectorType result;
- Op op;
- // FIXME: Find a way to not loop here
- for (size_t i = 0; i < VectorSize; ++i) {
- result[i] = op(value[i]);
- }
- return bit_cast<u128>(result);
- }
- static StringView name()
- {
- switch (VectorSize) {
- case 16:
- return "vec(8x16).unary_op"sv;
- case 8:
- return "vec(16x8).unary_op"sv;
- case 4:
- return "vec(32x4).unary_op"sv;
- case 2:
- return "vec(64x2).unary_op"sv;
- default:
- VERIFY_NOT_REACHED();
- }
- }
- };
- template<size_t VectorSize, typename Op>
- struct VectorFloatBinaryOp {
- auto operator()(u128 lhs, u128 rhs) const
- {
- using VectorType = NativeFloatingVectorType<128, VectorSize, NativeFloatingType<128 / VectorSize>>;
- auto first = bit_cast<VectorType>(lhs);
- auto second = bit_cast<VectorType>(rhs);
- VectorType result;
- Op op;
- for (size_t i = 0; i < VectorSize; ++i) {
- result[i] = op(first[i], second[i]);
- }
- return bit_cast<u128>(result);
- }
- static StringView name()
- {
- switch (VectorSize) {
- case 4:
- return "vecf(32x4).binary_op"sv;
- case 2:
- return "vecf(64x2).binary_op"sv;
- default:
- VERIFY_NOT_REACHED();
- }
- }
- };
- template<size_t VectorSize, typename Op>
- struct VectorFloatUnaryOp {
- auto operator()(u128 lhs) const
- {
- using VectorType = NativeFloatingVectorType<128, VectorSize, NativeFloatingType<128 / VectorSize>>;
- auto value = bit_cast<VectorType>(lhs);
- VectorType result;
- Op op;
- for (size_t i = 0; i < VectorSize; ++i) {
- result[i] = op(value[i]);
- }
- return bit_cast<u128>(result);
- }
- static StringView name()
- {
- switch (VectorSize) {
- case 4:
- return "vecf(32x4).unary_op"sv;
- case 2:
- return "vecf(64x2).unary_op"sv;
- default:
- VERIFY_NOT_REACHED();
- }
- }
- };
- template<size_t ResultSize, size_t InputSize, typename ResultType, typename InputType, typename Op>
- struct VectorConvertOp {
- auto operator()(u128 lhs) const
- {
- using VectorInput = NativeVectorType<128 / InputSize, InputSize, MakeUnsigned>;
- using VectorResult = NativeVectorType<128 / ResultSize, ResultSize, MakeUnsigned>;
- auto value = bit_cast<VectorInput>(lhs);
- VectorResult result;
- Op op;
- auto size = min(InputSize, ResultSize);
- for (size_t i = 0; i < size; ++i)
- result[i] = bit_cast<ResultType>(op(bit_cast<InputType>(value[i])));
- // FIXME: We shouldn't need this, but the auto-vectorizer sometimes doesn't see that we
- // need to pad with zeroes when InputSize < ResultSize (i.e. converting from f64x2 -> f32x4).
- // So we put this here to make sure. Putting [[clang::optnone]] over this function resolves
- // this issue, but that would be pretty unacceptable...
- if constexpr (InputSize < ResultSize) {
- constexpr size_t remaining = ResultSize - InputSize;
- for (size_t i = 0; i < remaining; ++i)
- result[i + InputSize] = 0;
- }
- return bit_cast<u128>(result);
- }
- static StringView name()
- {
- switch (ResultSize) {
- case 4:
- return "vec(32x4).cvt_op"sv;
- case 2:
- return "vec(64x2).cvt_op"sv;
- default:
- VERIFY_NOT_REACHED();
- }
- }
- };
- 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>
- auto 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 interpretation = bit_cast<Lhs>(lhs);
- return static_cast<ResultT>(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 copysignf(__builtin_huge_valf(), lhs);
- 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; }
- };
- template<typename ResultT, typename Op>
- struct SaturatingOp {
- template<typename Lhs, typename Rhs>
- ResultT operator()(Lhs lhs, Rhs rhs) const
- {
- Op op;
- double result = op(lhs, rhs);
- if (result <= static_cast<double>(NumericLimits<ResultT>::min())) {
- return NumericLimits<ResultT>::min();
- }
- if (result >= static_cast<double>(NumericLimits<ResultT>::max())) {
- return NumericLimits<ResultT>::max();
- }
- return static_cast<ResultT>(result);
- }
- static StringView name() { return "saturating_op"sv; }
- };
- }
|