From 3325a9cc203932e58b6e85dfefe5feb6e72839a4 Mon Sep 17 00:00:00 2001 From: Thomas Lively <7121787+tlively@users.noreply.github.com> Date: Thu, 13 Dec 2018 17:40:27 -0800 Subject: SIMD (#1820) Implement and test the following functionality for SIMD. - Parsing and printing - Assembling and disassembling - Interpretation - C API - JS API --- src/wasm/literal.cpp | 684 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 676 insertions(+), 8 deletions(-) (limited to 'src/wasm/literal.cpp') diff --git a/src/wasm/literal.cpp b/src/wasm/literal.cpp index 7b9e64e43..8d44b9b73 100644 --- a/src/wasm/literal.cpp +++ b/src/wasm/literal.cpp @@ -28,6 +28,52 @@ namespace wasm { +template +using LaneArray = std::array; + +Literal::Literal(const uint8_t init[16]) : type(Type::v128) { + memcpy(&v128, init, 16); +} + +template +static void extractBytes(uint8_t (&dest)[16], const LaneArray& lanes) { + std::array bytes; + const size_t lane_width = 16 / Lanes; + for (size_t lane_idx = 0; lane_idx < Lanes; ++lane_idx) { + uint8_t bits[16]; + lanes[lane_idx].getBits(bits); + LaneT lane; + memcpy(&lane, bits, sizeof(lane)); + for (size_t offset = 0; offset < lane_width; ++offset) { + bytes.at(lane_idx * lane_width + offset) = uint8_t(lane >> (8 * offset)); + } + } + memcpy(&dest, bytes.data(), sizeof(bytes)); +} + +Literal::Literal(const LaneArray<16>& lanes) : type(Type::v128) { + extractBytes(v128, lanes); +} + +Literal::Literal(const LaneArray<8>& lanes) : type(Type::v128) { + extractBytes(v128, lanes); +} + +Literal::Literal(const LaneArray<4>& lanes) : type(Type::v128) { + extractBytes(v128, lanes); +} + +Literal::Literal(const LaneArray<2>& lanes) : type(Type::v128) { + extractBytes(v128, lanes); +} + +std::array Literal::getv128() const { + assert(type == Type::v128); + std::array ret; + memcpy(ret.data(), v128, sizeof(ret)); + return ret; +} + Literal Literal::castToF32() { assert(type == Type::i32); Literal ret(i32); @@ -72,20 +118,26 @@ double Literal::getFloat() const { } } -int64_t Literal::getBits() const { +void Literal::getBits(uint8_t (&buf)[16]) const { + memset(buf, 0, 16); switch (type) { - case Type::i32: case Type::f32: return i32; - case Type::i64: case Type::f64: return i64; - case Type::v128: assert(false && "v128 not implemented"); - case Type::none: case Type::unreachable: WASM_UNREACHABLE(); + case Type::i32: + case Type::f32: memcpy(buf, &i32, sizeof(i32)); break; + case Type::i64: + case Type::f64: memcpy(buf, &i64, sizeof(i64)); break; + case Type::v128: memcpy(buf, &v128, sizeof(v128)); break; + case Type::none: + case Type::unreachable: WASM_UNREACHABLE(); } - WASM_UNREACHABLE(); } bool Literal::operator==(const Literal& other) const { if (type != other.type) return false; if (type == none) return true; - return getBits() == other.getBits(); + uint8_t bits[16], other_bits[16]; + getBits(bits); + other.getBits(other_bits); + return memcmp(bits, other_bits, 16) == 0; } bool Literal::operator!=(const Literal& other) const { @@ -158,6 +210,15 @@ void Literal::printDouble(std::ostream& o, double d) { o << text; } +void Literal::printVec128(std::ostream& o, const std::array& v) { + o << std::hex; + for (auto i = 0; i < 16; ++i) { + o << "0x" << uint32_t(v[i]); + if (i < 15) o << " "; + } + o << std::dec; +} + std::ostream& operator<<(std::ostream& o, Literal literal) { prepareMinorColor(o) << printType(literal.type) << ".const "; switch (literal.type) { @@ -166,7 +227,7 @@ std::ostream& operator<<(std::ostream& o, Literal literal) { case Type::i64: o << literal.i64; break; case Type::f32: literal.printFloat(o, literal.getf32()); break; case Type::f64: literal.printDouble(o, literal.getf64()); break; - case Type::v128: assert(false && "v128 not implemented yet"); + case Type::v128: o << "i32 "; literal.printVec128(o, literal.getv128()); break; case Type::unreachable: WASM_UNREACHABLE(); } restoreNormalColor(o); @@ -450,6 +511,79 @@ Literal Literal::sub(const Literal& other) const { WASM_UNREACHABLE(); } +template +static T add_sat_s(T a, T b) { + static_assert(std::is_signed::value, "Trying to instantiate add_sat_s with unsigned type"); + using UT = typename std::make_unsigned::type; + UT ua = static_cast(a); + UT ub = static_cast(b); + UT ures = ua + ub; + // overflow if sign of result is different from sign of a and b + if (static_cast((ures ^ ua) & (ures ^ ub)) < 0) { + return (a < 0) + ? std::numeric_limits::min() + : std::numeric_limits::max(); + } + return static_cast(ures); +} + +template +static T sub_sat_s(T a, T b) { + static_assert(std::is_signed::value, "Trying to instantiate sub_sat_s with unsigned type"); + using UT = typename std::make_unsigned::type; + UT ua = static_cast(a); + UT ub = static_cast(b); + UT ures = ua - ub; + // overflow if a and b have different signs and result and a differ in sign + if (static_cast((ua ^ ub) & (ures ^ ua)) < 0) { + return (a < 0) + ? std::numeric_limits::min() + : std::numeric_limits::max(); + } + return static_cast(ures); +} + +template +static T add_sat_u(T a, T b) { + static_assert(std::is_unsigned::value, "Trying to instantiate add_sat_u with signed type"); + T res = a + b; + // overflow if result is less than arguments + return (res < a) ? std::numeric_limits::max() : res; +} + +template +static T sub_sat_u(T a, T b) { + static_assert(std::is_unsigned::value, "Trying to instantiate sub_sat_u with signed type"); + T res = a - b; + // overflow if result is greater than a + return (res > a) ? 0 : res; +} + +Literal Literal::addSatSI8(const Literal& other) const { + return Literal(add_sat_s(geti32(), other.geti32())); +} +Literal Literal::addSatUI8(const Literal& other) const { + return Literal(add_sat_u(geti32(), other.geti32())); +} +Literal Literal::addSatSI16(const Literal& other) const { + return Literal(add_sat_s(geti32(), other.geti32())); +} +Literal Literal::addSatUI16(const Literal& other) const { + return Literal(add_sat_u(geti32(), other.geti32())); +} +Literal Literal::subSatSI8(const Literal& other) const { + return Literal(sub_sat_s(geti32(), other.geti32())); +} +Literal Literal::subSatUI8(const Literal& other) const { + return Literal(sub_sat_u(geti32(), other.geti32())); +} +Literal Literal::subSatSI16(const Literal& other) const { + return Literal(sub_sat_s(geti32(), other.geti32())); +} +Literal Literal::subSatUI16(const Literal& other) const { + return Literal(sub_sat_u(geti32(), other.geti32())); +} + Literal Literal::mul(const Literal& other) const { switch (type) { case Type::i32: return Literal(uint32_t(i32) * uint32_t(other.i32)); @@ -784,4 +918,538 @@ Literal Literal::copysign(const Literal& other) const { } } +template +static LaneArray getLanes(const Literal& val) { + assert(val.type == Type::v128); + const size_t lane_width = 16 / Lanes; + std::array bytes = val.getv128(); + LaneArray lanes; + for (size_t lane_idx = 0; lane_idx < Lanes; ++lane_idx) { + LaneT lane(0); + for (size_t offset = 0; offset < lane_width; ++offset) { + lane |= LaneT(bytes.at(lane_idx * lane_width + offset)) << LaneT(8 * offset); + } + lanes.at(lane_idx) = Literal(lane); + } + return lanes; +} + +LaneArray<16> Literal::getLanesSI8x16() const { + return getLanes(*this); +} +LaneArray<16> Literal::getLanesUI8x16() const { + return getLanes(*this); +} +LaneArray<8> Literal::getLanesSI16x8() const { + return getLanes(*this); +} +LaneArray<8> Literal::getLanesUI16x8() const { + return getLanes(*this); +} +LaneArray<4> Literal::getLanesI32x4() const { + return getLanes(*this); +} +LaneArray<2> Literal::getLanesI64x2() const { + return getLanes(*this); +} +LaneArray<4> Literal::getLanesF32x4() const { + auto lanes = getLanesI32x4(); + for (size_t i = 0; i < lanes.size(); ++i) { + lanes[i] = lanes[i].castToF32(); + } + return lanes; +} +LaneArray<2> Literal::getLanesF64x2() const { + auto lanes = getLanesI64x2(); + for (size_t i = 0; i < lanes.size(); ++i) { + lanes[i] = lanes[i].castToF64(); + } + return lanes; +} + +Literal Literal::shuffleV8x16(const Literal& other, const std::array& mask) const { + assert(type == Type::v128); + uint8_t bytes[16]; + for (size_t i = 0; i < mask.size(); ++i) { + bytes[i] = (mask[i] < 16) ? v128[mask[i]] : other.v128[mask[i] - 16]; + } + return Literal(bytes); +} + +template +static Literal splat(const Literal& val) { + assert(val.type == Ty); + LaneArray lanes; + lanes.fill(val); + return Literal(lanes); +} + +Literal Literal::splatI8x16() const { return splat(*this); } +Literal Literal::splatI16x8() const { return splat(*this); } +Literal Literal::splatI32x4() const { return splat(*this); } +Literal Literal::splatI64x2() const { return splat(*this); } +Literal Literal::splatF32x4() const { return splat(*this); } +Literal Literal::splatF64x2() const { return splat(*this); } + +Literal Literal::extractLaneSI8x16(uint8_t idx) const { return getLanesSI8x16().at(idx); } +Literal Literal::extractLaneUI8x16(uint8_t idx) const { return getLanesUI8x16().at(idx); } +Literal Literal::extractLaneSI16x8(uint8_t idx) const { return getLanesSI16x8().at(idx); } +Literal Literal::extractLaneUI16x8(uint8_t idx) const { return getLanesUI16x8().at(idx); } +Literal Literal::extractLaneI32x4(uint8_t idx) const { return getLanesI32x4().at(idx); } +Literal Literal::extractLaneI64x2(uint8_t idx) const { return getLanesI64x2().at(idx); } +Literal Literal::extractLaneF32x4(uint8_t idx) const { return getLanesF32x4().at(idx); } +Literal Literal::extractLaneF64x2(uint8_t idx) const { return getLanesF64x2().at(idx); } + +template (Literal::*IntoLanes)() const> +static Literal replace(const Literal& val, const Literal& other, uint8_t idx) { + LaneArray lanes = (val.*IntoLanes)(); + lanes.at(idx) = other; + auto ret = Literal(lanes); + return ret; +} + +Literal Literal::replaceLaneI8x16(const Literal& other, uint8_t idx) const { + return replace<16, &Literal::getLanesUI8x16>(*this, other, idx); +} +Literal Literal::replaceLaneI16x8(const Literal& other, uint8_t idx) const { + return replace<8, &Literal::getLanesUI16x8>(*this, other, idx); +} +Literal Literal::replaceLaneI32x4(const Literal& other, uint8_t idx) const { + return replace<4, &Literal::getLanesI32x4>(*this, other, idx); +} +Literal Literal::replaceLaneI64x2(const Literal& other, uint8_t idx) const { + return replace<2, &Literal::getLanesI64x2>(*this, other, idx); +} +Literal Literal::replaceLaneF32x4(const Literal& other, uint8_t idx) const { + return replace<4, &Literal::getLanesF32x4>(*this, other, idx); +} +Literal Literal::replaceLaneF64x2(const Literal& other, uint8_t idx) const { + return replace<2, &Literal::getLanesF64x2>(*this, other, idx); +} + +template (Literal::*IntoLanes)() const, + Literal (Literal::*UnaryOp)(void) const> +static Literal unary(const Literal& val) { + LaneArray lanes = (val.*IntoLanes)(); + for (size_t i = 0; i < Lanes; ++i) { + lanes[i] = (lanes[i].*UnaryOp)(); + } + return Literal(lanes); +} + +Literal Literal::notV128() const { + std::array ones; + ones.fill(0xff); + return xorV128(Literal(ones.data())); +} +Literal Literal::negI8x16() const { + return unary<16, &Literal::getLanesUI8x16, &Literal::neg>(*this); +} +Literal Literal::negI16x8() const { + return unary<8, &Literal::getLanesUI16x8, &Literal::neg>(*this); +} +Literal Literal::negI32x4() const { + return unary<4, &Literal::getLanesI32x4, &Literal::neg>(*this); +} +Literal Literal::negI64x2() const { + return unary<2, &Literal::getLanesI64x2, &Literal::neg>(*this); +} +Literal Literal::absF32x4() const { + return unary<4, &Literal::getLanesF32x4, &Literal::abs>(*this); +} +Literal Literal::negF32x4() const { + return unary<4, &Literal::getLanesF32x4, &Literal::neg>(*this); +} +Literal Literal::sqrtF32x4() const { + return unary<4, &Literal::getLanesF32x4, &Literal::sqrt>(*this); +} +Literal Literal::absF64x2() const { + return unary<2, &Literal::getLanesF64x2, &Literal::abs>(*this); +} +Literal Literal::negF64x2() const { + return unary<2, &Literal::getLanesF64x2, &Literal::neg>(*this); +} +Literal Literal::sqrtF64x2() const { + return unary<2, &Literal::getLanesF64x2, &Literal::sqrt>(*this); +} +Literal Literal::truncSatToSI32x4() const { + return unary<4, &Literal::getLanesF32x4, &Literal::truncSatToSI32>(*this); +} +Literal Literal::truncSatToUI32x4() const { + return unary<4, &Literal::getLanesF32x4, &Literal::truncSatToUI32>(*this); +} +Literal Literal::truncSatToSI64x2() const { + return unary<2, &Literal::getLanesF64x2, &Literal::truncSatToSI64>(*this); +} +Literal Literal::truncSatToUI64x2() const { + return unary<2, &Literal::getLanesF64x2, &Literal::truncSatToUI64>(*this); +} +Literal Literal::convertSToF32x4() const { + return unary<4, &Literal::getLanesI32x4, &Literal::convertSIToF32>(*this); +} +Literal Literal::convertUToF32x4() const { + return unary<4, &Literal::getLanesI32x4, &Literal::convertUIToF32>(*this); +} +Literal Literal::convertSToF64x2() const { + return unary<2, &Literal::getLanesI64x2, &Literal::convertSIToF64>(*this); +} +Literal Literal::convertUToF64x2() const { + return unary<2, &Literal::getLanesI64x2, &Literal::convertUIToF64>(*this); +} + +template (Literal::*IntoLanes)() const> +static Literal any_true(const Literal& val) { + LaneArray lanes = (val.*IntoLanes)(); + for (size_t i = 0; i < Lanes; ++i) { + if (lanes[i] != Literal::makeZero(lanes[i].type)) { + return Literal(int32_t(1)); + } + } + return Literal(int32_t(0)); +} + +template (Literal::*IntoLanes)() const> +static Literal all_true(const Literal& val) { + LaneArray lanes = (val.*IntoLanes)(); + for (size_t i = 0; i < Lanes; ++i) { + if (lanes[i] == Literal::makeZero(lanes[i].type)) { + return Literal(int32_t(0)); + } + } + return Literal(int32_t(1)); +} + +Literal Literal::anyTrueI8x16() const { + return any_true<16, &Literal::getLanesUI8x16>(*this); +} +Literal Literal::allTrueI8x16() const { + return all_true<16, &Literal::getLanesUI8x16>(*this); +} +Literal Literal::anyTrueI16x8() const { + return any_true<8, &Literal::getLanesUI16x8>(*this); +} +Literal Literal::allTrueI16x8() const { + return all_true<8, &Literal::getLanesUI16x8>(*this); +} +Literal Literal::anyTrueI32x4() const { + return any_true<4, &Literal::getLanesI32x4>(*this); +} +Literal Literal::allTrueI32x4() const { + return all_true<4, &Literal::getLanesI32x4>(*this); +} +Literal Literal::anyTrueI64x2() const { + return any_true<2, &Literal::getLanesI64x2>(*this); +} +Literal Literal::allTrueI64x2() const { + return all_true<2, &Literal::getLanesI64x2>(*this); +} + +template (Literal::*IntoLanes)() const, + Literal (Literal::*ShiftOp)(const Literal&) const> +static Literal shift(const Literal& vec, const Literal& shift) { + assert(shift.type == Type::i32); + size_t lane_bits = 128 / Lanes; + LaneArray lanes = (vec.*IntoLanes)(); + for (size_t i = 0; i < Lanes; ++i) { + lanes[i] = (lanes[i].*ShiftOp)(Literal(int32_t(shift.geti32() % lane_bits))); + } + return Literal(lanes); +} + +Literal Literal::shlI8x16(const Literal& other) const { + return shift<16, &Literal::getLanesUI8x16, &Literal::shl>(*this, other); +} +Literal Literal::shrSI8x16(const Literal& other) const { + return shift<16, &Literal::getLanesSI8x16, &Literal::shrS>(*this, other); +} +Literal Literal::shrUI8x16(const Literal& other) const { + return shift<16, &Literal::getLanesUI8x16, &Literal::shrU>(*this, other); +} +Literal Literal::shlI16x8(const Literal& other) const { + return shift<8, &Literal::getLanesUI16x8, &Literal::shl>(*this, other); +} +Literal Literal::shrSI16x8(const Literal& other) const { + return shift<8, &Literal::getLanesSI16x8, &Literal::shrS>(*this, other); +} +Literal Literal::shrUI16x8(const Literal& other) const { + return shift<8, &Literal::getLanesUI16x8, &Literal::shrU>(*this, other); +} +Literal Literal::shlI32x4(const Literal& other) const { + return shift<4, &Literal::getLanesI32x4, &Literal::shl>(*this, other); +} +Literal Literal::shrSI32x4(const Literal& other) const { + return shift<4, &Literal::getLanesI32x4, &Literal::shrS>(*this, other); +} +Literal Literal::shrUI32x4(const Literal& other) const { + return shift<4, &Literal::getLanesI32x4, &Literal::shrU>(*this, other); +} +Literal Literal::shlI64x2(const Literal& other) const { + return shift<2, &Literal::getLanesI64x2, &Literal::shl>(*this, other); +} +Literal Literal::shrSI64x2(const Literal& other) const { + return shift<2, &Literal::getLanesI64x2, &Literal::shrS>(*this, other); +} +Literal Literal::shrUI64x2(const Literal& other) const { + return shift<2, &Literal::getLanesI64x2, &Literal::shrU>(*this, other); +} + +template (Literal::*IntoLanes)() const, + Literal (Literal::*CompareOp)(const Literal&) const, + typename LaneT = int32_t> +static Literal compare(const Literal& val, const Literal& other) { + LaneArray lanes = (val.*IntoLanes)(); + LaneArray other_lanes = (other.*IntoLanes)(); + for (size_t i = 0; i < Lanes; ++i) { + lanes[i] = (lanes[i].*CompareOp)(other_lanes[i]) == Literal(int32_t(1)) + ? Literal(LaneT(-1)) + : Literal(LaneT(0)); + } + return Literal(lanes); +} + +Literal Literal::eqI8x16(const Literal& other) const { + return compare<16, &Literal::getLanesUI8x16, &Literal::eq>(*this, other); +} +Literal Literal::neI8x16(const Literal& other) const { + return compare<16, &Literal::getLanesUI8x16, &Literal::ne>(*this, other); +} +Literal Literal::ltSI8x16(const Literal& other) const { + return compare<16, &Literal::getLanesSI8x16, &Literal::ltS>(*this, other); +} +Literal Literal::ltUI8x16(const Literal& other) const { + return compare<16, &Literal::getLanesUI8x16, &Literal::ltU>(*this, other); +} +Literal Literal::gtSI8x16(const Literal& other) const { + return compare<16, &Literal::getLanesSI8x16, &Literal::gtS>(*this, other); +} +Literal Literal::gtUI8x16(const Literal& other) const { + return compare<16, &Literal::getLanesUI8x16, &Literal::gtU>(*this, other); +} +Literal Literal::leSI8x16(const Literal& other) const { + return compare<16, &Literal::getLanesSI8x16, &Literal::leS>(*this, other); +} +Literal Literal::leUI8x16(const Literal& other) const { + return compare<16, &Literal::getLanesUI8x16, &Literal::leU>(*this, other); +} +Literal Literal::geSI8x16(const Literal& other) const { + return compare<16, &Literal::getLanesSI8x16, &Literal::geS>(*this, other); +} +Literal Literal::geUI8x16(const Literal& other) const { + return compare<16, &Literal::getLanesUI8x16, &Literal::geU>(*this, other); +} +Literal Literal::eqI16x8(const Literal& other) const { + return compare<8, &Literal::getLanesUI16x8, &Literal::eq>(*this, other); +} +Literal Literal::neI16x8(const Literal& other) const { + return compare<8, &Literal::getLanesUI16x8, &Literal::ne>(*this, other); +} +Literal Literal::ltSI16x8(const Literal& other) const { + return compare<8, &Literal::getLanesSI16x8, &Literal::ltS>(*this, other); +} +Literal Literal::ltUI16x8(const Literal& other) const { + return compare<8, &Literal::getLanesUI16x8, &Literal::ltU>(*this, other); +} +Literal Literal::gtSI16x8(const Literal& other) const { + return compare<8, &Literal::getLanesSI16x8, &Literal::gtS>(*this, other); +} +Literal Literal::gtUI16x8(const Literal& other) const { + return compare<8, &Literal::getLanesUI16x8, &Literal::gtU>(*this, other); +} +Literal Literal::leSI16x8(const Literal& other) const { + return compare<8, &Literal::getLanesSI16x8, &Literal::leS>(*this, other); +} +Literal Literal::leUI16x8(const Literal& other) const { + return compare<8, &Literal::getLanesUI16x8, &Literal::leU>(*this, other); +} +Literal Literal::geSI16x8(const Literal& other) const { + return compare<8, &Literal::getLanesSI16x8, &Literal::geS>(*this, other); +} +Literal Literal::geUI16x8(const Literal& other) const { + return compare<8, &Literal::getLanesUI16x8, &Literal::geU>(*this, other); +} +Literal Literal::eqI32x4(const Literal& other) const { + return compare<4, &Literal::getLanesI32x4, &Literal::eq>(*this, other); +} +Literal Literal::neI32x4(const Literal& other) const { + return compare<4, &Literal::getLanesI32x4, &Literal::ne>(*this, other); +} +Literal Literal::ltSI32x4(const Literal& other) const { + return compare<4, &Literal::getLanesI32x4, &Literal::ltS>(*this, other); +} +Literal Literal::ltUI32x4(const Literal& other) const { + return compare<4, &Literal::getLanesI32x4, &Literal::ltU>(*this, other); +} +Literal Literal::gtSI32x4(const Literal& other) const { + return compare<4, &Literal::getLanesI32x4, &Literal::gtS>(*this, other); +} +Literal Literal::gtUI32x4(const Literal& other) const { + return compare<4, &Literal::getLanesI32x4, &Literal::gtU>(*this, other); +} +Literal Literal::leSI32x4(const Literal& other) const { + return compare<4, &Literal::getLanesI32x4, &Literal::leS>(*this, other); +} +Literal Literal::leUI32x4(const Literal& other) const { + return compare<4, &Literal::getLanesI32x4, &Literal::leU>(*this, other); +} +Literal Literal::geSI32x4(const Literal& other) const { + return compare<4, &Literal::getLanesI32x4, &Literal::geS>(*this, other); +} +Literal Literal::geUI32x4(const Literal& other) const { + return compare<4, &Literal::getLanesI32x4, &Literal::geU>(*this, other); +} +Literal Literal::eqF32x4(const Literal& other) const { + return compare<4, &Literal::getLanesF32x4, &Literal::eq>(*this, other); +} +Literal Literal::neF32x4(const Literal& other) const { + return compare<4, &Literal::getLanesF32x4, &Literal::ne>(*this, other); +} +Literal Literal::ltF32x4(const Literal& other) const { + return compare<4, &Literal::getLanesF32x4, &Literal::lt>(*this, other); +} +Literal Literal::gtF32x4(const Literal& other) const { + return compare<4, &Literal::getLanesF32x4, &Literal::gt>(*this, other); +} +Literal Literal::leF32x4(const Literal& other) const { + return compare<4, &Literal::getLanesF32x4, &Literal::le>(*this, other); +} +Literal Literal::geF32x4(const Literal& other) const { + return compare<4, &Literal::getLanesF32x4, &Literal::ge>(*this, other); +} +Literal Literal::eqF64x2(const Literal& other) const { + return compare<2, &Literal::getLanesF64x2, &Literal::eq, int64_t>(*this, other); +} +Literal Literal::neF64x2(const Literal& other) const { + return compare<2, &Literal::getLanesF64x2, &Literal::ne, int64_t>(*this, other); +} +Literal Literal::ltF64x2(const Literal& other) const { + return compare<2, &Literal::getLanesF64x2, &Literal::lt, int64_t>(*this, other); +} +Literal Literal::gtF64x2(const Literal& other) const { + return compare<2, &Literal::getLanesF64x2, &Literal::gt, int64_t>(*this, other); +} +Literal Literal::leF64x2(const Literal& other) const { + return compare<2, &Literal::getLanesF64x2, &Literal::le, int64_t>(*this, other); +} +Literal Literal::geF64x2(const Literal& other) const { + return compare<2, &Literal::getLanesF64x2, &Literal::ge, int64_t>(*this, other); +} + +template (Literal::*IntoLanes)() const, + Literal (Literal::*BinaryOp)(const Literal&) const> +static Literal binary(const Literal& val, const Literal& other) { + LaneArray lanes = (val.*IntoLanes)(); + LaneArray other_lanes = (other.*IntoLanes)(); + for (size_t i = 0; i < Lanes; ++i) { + lanes[i] = (lanes[i].*BinaryOp)(other_lanes[i]); + } + return Literal(lanes); +} + +Literal Literal::andV128(const Literal& other) const { + return binary<4, &Literal::getLanesI32x4, &Literal::and_>(*this, other); +} +Literal Literal::orV128(const Literal& other) const { + return binary<4, &Literal::getLanesI32x4, &Literal::or_>(*this, other); +} +Literal Literal::xorV128(const Literal& other) const { + return binary<4, &Literal::getLanesI32x4, &Literal::xor_>(*this, other); +} +Literal Literal::addI8x16(const Literal& other) const { + return binary<16, &Literal::getLanesUI8x16, &Literal::add>(*this, other); +} +Literal Literal::addSaturateSI8x16(const Literal& other) const { + return binary<16, &Literal::getLanesUI8x16, &Literal::addSatSI8>(*this, other); +} +Literal Literal::addSaturateUI8x16(const Literal& other) const { + return binary<16, &Literal::getLanesSI8x16, &Literal::addSatUI8>(*this, other); +} +Literal Literal::subI8x16(const Literal& other) const { + return binary<16, &Literal::getLanesUI8x16, &Literal::sub>(*this, other); +} +Literal Literal::subSaturateSI8x16(const Literal& other) const { + return binary<16, &Literal::getLanesUI8x16, &Literal::subSatSI8>(*this, other); +} +Literal Literal::subSaturateUI8x16(const Literal& other) const { + return binary<16, &Literal::getLanesSI8x16, &Literal::subSatUI8>(*this, other); +} +Literal Literal::mulI8x16(const Literal& other) const { + return binary<16, &Literal::getLanesUI8x16, &Literal::mul>(*this, other); +} +Literal Literal::addI16x8(const Literal& other) const { + return binary<8, &Literal::getLanesUI16x8, &Literal::add>(*this, other); +} +Literal Literal::addSaturateSI16x8(const Literal& other) const { + return binary<8, &Literal::getLanesUI16x8, &Literal::addSatSI16>(*this, other); +} +Literal Literal::addSaturateUI16x8(const Literal& other) const { + return binary<8, &Literal::getLanesSI16x8, &Literal::addSatUI16>(*this, other); +} +Literal Literal::subI16x8(const Literal& other) const { + return binary<8, &Literal::getLanesUI16x8, &Literal::sub>(*this, other); +} +Literal Literal::subSaturateSI16x8(const Literal& other) const { + return binary<8, &Literal::getLanesUI16x8, &Literal::subSatSI16>(*this, other); +} +Literal Literal::subSaturateUI16x8(const Literal& other) const { + return binary<8, &Literal::getLanesSI16x8, &Literal::subSatUI16>(*this, other); +} +Literal Literal::mulI16x8(const Literal& other) const { + return binary<8, &Literal::getLanesUI16x8, &Literal::mul>(*this, other); +} +Literal Literal::addI32x4(const Literal& other) const { + return binary<4, &Literal::getLanesI32x4, &Literal::add>(*this, other); +} +Literal Literal::subI32x4(const Literal& other) const { + return binary<4, &Literal::getLanesI32x4, &Literal::sub>(*this, other); +} +Literal Literal::mulI32x4(const Literal& other) const { + return binary<4, &Literal::getLanesI32x4, &Literal::mul>(*this, other); +} +Literal Literal::addI64x2(const Literal& other) const { + return binary<2, &Literal::getLanesI64x2, &Literal::add>(*this, other); +} +Literal Literal::subI64x2(const Literal& other) const { + return binary<2, &Literal::getLanesI64x2, &Literal::sub>(*this, other); +} +Literal Literal::addF32x4(const Literal& other) const { + return binary<4, &Literal::getLanesF32x4, &Literal::add>(*this, other); +} +Literal Literal::subF32x4(const Literal& other) const { + return binary<4, &Literal::getLanesF32x4, &Literal::sub>(*this, other); +} +Literal Literal::mulF32x4(const Literal& other) const { + return binary<4, &Literal::getLanesF32x4, &Literal::mul>(*this, other); +} +Literal Literal::divF32x4(const Literal& other) const { + return binary<4, &Literal::getLanesF32x4, &Literal::div>(*this, other); +} +Literal Literal::minF32x4(const Literal& other) const { + return binary<4, &Literal::getLanesF32x4, &Literal::min>(*this, other); +} +Literal Literal::maxF32x4(const Literal& other) const { + return binary<4, &Literal::getLanesF32x4, &Literal::max>(*this, other); +} +Literal Literal::addF64x2(const Literal& other) const { + return binary<2, &Literal::getLanesF64x2, &Literal::add>(*this, other); +} +Literal Literal::subF64x2(const Literal& other) const { + return binary<2, &Literal::getLanesF64x2, &Literal::sub>(*this, other); +} +Literal Literal::mulF64x2(const Literal& other) const { + return binary<2, &Literal::getLanesF64x2, &Literal::mul>(*this, other); +} +Literal Literal::divF64x2(const Literal& other) const { + return binary<2, &Literal::getLanesF64x2, &Literal::div>(*this, other); +} +Literal Literal::minF64x2(const Literal& other) const { + return binary<2, &Literal::getLanesF64x2, &Literal::min>(*this, other); +} +Literal Literal::maxF64x2(const Literal& other) const { + return binary<2, &Literal::getLanesF64x2, &Literal::max>(*this, other); +} + +Literal Literal::bitselectV128(const Literal& left, const Literal& right) const { + return andV128(left).orV128(notV128().andV128(right)); +} + } // namespace wasm -- cgit v1.2.3