diff options
Diffstat (limited to 'src/wasm')
-rw-r--r-- | src/wasm/literal.cpp | 684 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 303 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 127 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 232 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 88 |
5 files changed, 1402 insertions, 32 deletions
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<int N> +using LaneArray = std::array<Literal, N>; + +Literal::Literal(const uint8_t init[16]) : type(Type::v128) { + memcpy(&v128, init, 16); +} + +template<typename LaneT, int Lanes> +static void extractBytes(uint8_t (&dest)[16], const LaneArray<Lanes>& lanes) { + std::array<uint8_t, 16> 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<uint8_t, 16>(v128, lanes); +} + +Literal::Literal(const LaneArray<8>& lanes) : type(Type::v128) { + extractBytes<uint16_t, 8>(v128, lanes); +} + +Literal::Literal(const LaneArray<4>& lanes) : type(Type::v128) { + extractBytes<uint32_t, 4>(v128, lanes); +} + +Literal::Literal(const LaneArray<2>& lanes) : type(Type::v128) { + extractBytes<uint64_t, 2>(v128, lanes); +} + +std::array<uint8_t, 16> Literal::getv128() const { + assert(type == Type::v128); + std::array<uint8_t, 16> 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<uint8_t, 16>& 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<typename T> +static T add_sat_s(T a, T b) { + static_assert(std::is_signed<T>::value, "Trying to instantiate add_sat_s with unsigned type"); + using UT = typename std::make_unsigned<T>::type; + UT ua = static_cast<UT>(a); + UT ub = static_cast<UT>(b); + UT ures = ua + ub; + // overflow if sign of result is different from sign of a and b + if (static_cast<T>((ures ^ ua) & (ures ^ ub)) < 0) { + return (a < 0) + ? std::numeric_limits<T>::min() + : std::numeric_limits<T>::max(); + } + return static_cast<T>(ures); +} + +template<typename T> +static T sub_sat_s(T a, T b) { + static_assert(std::is_signed<T>::value, "Trying to instantiate sub_sat_s with unsigned type"); + using UT = typename std::make_unsigned<T>::type; + UT ua = static_cast<UT>(a); + UT ub = static_cast<UT>(b); + UT ures = ua - ub; + // overflow if a and b have different signs and result and a differ in sign + if (static_cast<T>((ua ^ ub) & (ures ^ ua)) < 0) { + return (a < 0) + ? std::numeric_limits<T>::min() + : std::numeric_limits<T>::max(); + } + return static_cast<T>(ures); +} + +template<typename T> +static T add_sat_u(T a, T b) { + static_assert(std::is_unsigned<T>::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<T>::max() : res; +} + +template<typename T> +static T sub_sat_u(T a, T b) { + static_assert(std::is_unsigned<T>::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<int8_t>(geti32(), other.geti32())); +} +Literal Literal::addSatUI8(const Literal& other) const { + return Literal(add_sat_u<uint8_t>(geti32(), other.geti32())); +} +Literal Literal::addSatSI16(const Literal& other) const { + return Literal(add_sat_s<int16_t>(geti32(), other.geti32())); +} +Literal Literal::addSatUI16(const Literal& other) const { + return Literal(add_sat_u<uint16_t>(geti32(), other.geti32())); +} +Literal Literal::subSatSI8(const Literal& other) const { + return Literal(sub_sat_s<int8_t>(geti32(), other.geti32())); +} +Literal Literal::subSatUI8(const Literal& other) const { + return Literal(sub_sat_u<uint8_t>(geti32(), other.geti32())); +} +Literal Literal::subSatSI16(const Literal& other) const { + return Literal(sub_sat_s<int16_t>(geti32(), other.geti32())); +} +Literal Literal::subSatUI16(const Literal& other) const { + return Literal(sub_sat_u<uint16_t>(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<typename LaneT, int Lanes> +static LaneArray<Lanes> getLanes(const Literal& val) { + assert(val.type == Type::v128); + const size_t lane_width = 16 / Lanes; + std::array<uint8_t, 16> bytes = val.getv128(); + LaneArray<Lanes> 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<int8_t, 16>(*this); +} +LaneArray<16> Literal::getLanesUI8x16() const { + return getLanes<uint8_t, 16>(*this); +} +LaneArray<8> Literal::getLanesSI16x8() const { + return getLanes<int16_t, 8>(*this); +} +LaneArray<8> Literal::getLanesUI16x8() const { + return getLanes<uint16_t, 8>(*this); +} +LaneArray<4> Literal::getLanesI32x4() const { + return getLanes<int32_t, 4>(*this); +} +LaneArray<2> Literal::getLanesI64x2() const { + return getLanes<int64_t, 2>(*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<uint8_t, 16>& 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<Type Ty, int Lanes> +static Literal splat(const Literal& val) { + assert(val.type == Ty); + LaneArray<Lanes> lanes; + lanes.fill(val); + return Literal(lanes); +} + +Literal Literal::splatI8x16() const { return splat<Type::i32, 16>(*this); } +Literal Literal::splatI16x8() const { return splat<Type::i32, 8>(*this); } +Literal Literal::splatI32x4() const { return splat<Type::i32, 4>(*this); } +Literal Literal::splatI64x2() const { return splat<Type::i64, 2>(*this); } +Literal Literal::splatF32x4() const { return splat<Type::f32, 4>(*this); } +Literal Literal::splatF64x2() const { return splat<Type::f64, 2>(*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<int Lanes, LaneArray<Lanes> (Literal::*IntoLanes)() const> +static Literal replace(const Literal& val, const Literal& other, uint8_t idx) { + LaneArray<Lanes> 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<int Lanes, LaneArray<Lanes> (Literal::*IntoLanes)() const, + Literal (Literal::*UnaryOp)(void) const> +static Literal unary(const Literal& val) { + LaneArray<Lanes> 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<uint8_t, 16> 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<int Lanes, LaneArray<Lanes> (Literal::*IntoLanes)() const> +static Literal any_true(const Literal& val) { + LaneArray<Lanes> 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<int Lanes, LaneArray<Lanes> (Literal::*IntoLanes)() const> +static Literal all_true(const Literal& val) { + LaneArray<Lanes> 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<int Lanes, LaneArray<Lanes> (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> 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<int Lanes, LaneArray<Lanes> (Literal::*IntoLanes)() const, + Literal (Literal::*CompareOp)(const Literal&) const, + typename LaneT = int32_t> +static Literal compare(const Literal& val, const Literal& other) { + LaneArray<Lanes> lanes = (val.*IntoLanes)(); + LaneArray<Lanes> 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<int Lanes, LaneArray<Lanes> (Literal::*IntoLanes)() const, + Literal (Literal::*BinaryOp)(const Literal&) const> +static Literal binary(const Literal& val, const Literal& other) { + LaneArray<Lanes> lanes = (val.*IntoLanes)(); + LaneArray<Lanes> 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 diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 2a7bff51f..a373a5ec6 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -757,6 +757,14 @@ uint64_t WasmBinaryBuilder::getInt64() { return ret; } +uint8_t WasmBinaryBuilder::getLaneIdx(size_t lanes) { + if (debug) std::cerr << "<==" << std::endl; + auto ret = getInt8(); + if (ret >= lanes) throwError("Illegal lane index"); + if (debug) std::cerr << "getLaneIdx(" << lanes << "): " << ret << " ==>" << std::endl; + return ret; +} + Literal WasmBinaryBuilder::getFloat32Literal() { if (debug) std::cerr << "<==" << std::endl; auto ret = Literal(getInt32()); @@ -773,6 +781,17 @@ Literal WasmBinaryBuilder::getFloat64Literal() { return ret; } +Literal WasmBinaryBuilder::getVec128Literal() { + if (debug) std::cerr << "<==" << std::endl; + std::array<uint8_t, 16> bytes; + for (auto i = 0; i < 16; ++i) { + bytes[i] = getInt8(); + } + auto ret = Literal(bytes.data()); + if (debug) std::cerr << "getVec128: " << ret << " ==>" << std::endl; + return ret; +} + uint32_t WasmBinaryBuilder::getU32LEB() { if (debug) std::cerr << "<==" << std::endl; U32LEB ret; @@ -822,6 +841,7 @@ Type WasmBinaryBuilder::getType() { case BinaryConsts::EncodedType::i64: return i64; case BinaryConsts::EncodedType::f32: return f32; case BinaryConsts::EncodedType::f64: return f64; + case BinaryConsts::EncodedType::v128: return v128; case BinaryConsts::EncodedType::AnyFunc: case BinaryConsts::EncodedType::Func: throwError("invalid wasm type: " + std::to_string(type)); @@ -1677,7 +1697,7 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { case BinaryConsts::End: case BinaryConsts::Else: curr = nullptr; break; case BinaryConsts::AtomicPrefix: { - code = getInt8(); + code = static_cast<uint8_t>(getU32LEB()); if (maybeVisitLoad(curr, code, /*isAtomic=*/true)) break; if (maybeVisitStore(curr, code, /*isAtomic=*/true)) break; if (maybeVisitAtomicRMW(curr, code)) break; @@ -1688,11 +1708,26 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { break; } case BinaryConsts::TruncSatPrefix: { - uint32_t code = getU32LEB(); - if (maybeVisitTruncSat(curr, code)) break; + auto opcode = getU32LEB(); + if (maybeVisitTruncSat(curr, opcode)) break; throwError("invalid code after nontrapping float-to-int prefix: " + std::to_string(code)); break; } + case BinaryConsts::SIMDPrefix: { + auto opcode = getU32LEB(); + if (maybeVisitSIMDBinary(curr, opcode)) break; + if (maybeVisitSIMDUnary(curr, opcode)) break; + if (maybeVisitSIMDConst(curr, opcode)) break; + if (maybeVisitSIMDLoad(curr, opcode)) break; + if (maybeVisitSIMDStore(curr, opcode)) break; + if (maybeVisitSIMDExtract(curr, opcode)) break; + if (maybeVisitSIMDReplace(curr, opcode)) break; + if (maybeVisitSIMDShuffle(curr, opcode)) break; + if (maybeVisitSIMDBitselect(curr, opcode)) break; + if (maybeVisitSIMDShift(curr, opcode)) break; + throwError("invalid code after SIMD prefix: " + std::to_string(opcode)); + break; + } default: { // otherwise, the code is a subcode TODO: optimize if (maybeVisitBinary(curr, code)) break; @@ -2077,7 +2112,6 @@ bool WasmBinaryBuilder::maybeVisitStore(Expression*& out, uint8_t code, bool isA return true; } - bool WasmBinaryBuilder::maybeVisitAtomicRMW(Expression*& out, uint8_t code) { if (code < BinaryConsts::AtomicRMWOps_Begin || code > BinaryConsts::AtomicRMWOps_End) return false; auto* curr = allocator.alloc<AtomicRMW>(); @@ -2359,6 +2393,267 @@ bool WasmBinaryBuilder::maybeVisitBinary(Expression*& out, uint8_t code) { #undef FLOAT_TYPED_CODE } +bool WasmBinaryBuilder::maybeVisitSIMDBinary(Expression*& out, uint32_t code) { + Binary* curr; + switch (code) { + case BinaryConsts::I8x16Eq: curr = allocator.alloc<Binary>(); curr->op = EqVecI8x16; break; + case BinaryConsts::I8x16Ne: curr = allocator.alloc<Binary>(); curr->op = NeVecI8x16; break; + case BinaryConsts::I8x16LtS: curr = allocator.alloc<Binary>(); curr->op = LtSVecI8x16; break; + case BinaryConsts::I8x16LtU: curr = allocator.alloc<Binary>(); curr->op = LtUVecI8x16; break; + case BinaryConsts::I8x16GtS: curr = allocator.alloc<Binary>(); curr->op = GtSVecI8x16; break; + case BinaryConsts::I8x16GtU: curr = allocator.alloc<Binary>(); curr->op = GtUVecI8x16; break; + case BinaryConsts::I8x16LeS: curr = allocator.alloc<Binary>(); curr->op = LeSVecI8x16; break; + case BinaryConsts::I8x16LeU: curr = allocator.alloc<Binary>(); curr->op = LeUVecI8x16; break; + case BinaryConsts::I8x16GeS: curr = allocator.alloc<Binary>(); curr->op = GeSVecI8x16; break; + case BinaryConsts::I8x16GeU: curr = allocator.alloc<Binary>(); curr->op = GeUVecI8x16; break; + case BinaryConsts::I16x8Eq: curr = allocator.alloc<Binary>(); curr->op = EqVecI16x8; break; + case BinaryConsts::I16x8Ne: curr = allocator.alloc<Binary>(); curr->op = NeVecI16x8; break; + case BinaryConsts::I16x8LtS: curr = allocator.alloc<Binary>(); curr->op = LtSVecI16x8; break; + case BinaryConsts::I16x8LtU: curr = allocator.alloc<Binary>(); curr->op = LtUVecI16x8; break; + case BinaryConsts::I16x8GtS: curr = allocator.alloc<Binary>(); curr->op = GtSVecI16x8; break; + case BinaryConsts::I16x8GtU: curr = allocator.alloc<Binary>(); curr->op = GtUVecI16x8; break; + case BinaryConsts::I16x8LeS: curr = allocator.alloc<Binary>(); curr->op = LeSVecI16x8; break; + case BinaryConsts::I16x8LeU: curr = allocator.alloc<Binary>(); curr->op = LeUVecI16x8; break; + case BinaryConsts::I16x8GeS: curr = allocator.alloc<Binary>(); curr->op = GeSVecI16x8; break; + case BinaryConsts::I16x8GeU: curr = allocator.alloc<Binary>(); curr->op = GeUVecI16x8; break; + case BinaryConsts::I32x4Eq: curr = allocator.alloc<Binary>(); curr->op = EqVecI32x4; break; + case BinaryConsts::I32x4Ne: curr = allocator.alloc<Binary>(); curr->op = NeVecI32x4; break; + case BinaryConsts::I32x4LtS: curr = allocator.alloc<Binary>(); curr->op = LtSVecI32x4; break; + case BinaryConsts::I32x4LtU: curr = allocator.alloc<Binary>(); curr->op = LtUVecI32x4; break; + case BinaryConsts::I32x4GtS: curr = allocator.alloc<Binary>(); curr->op = GtSVecI32x4; break; + case BinaryConsts::I32x4GtU: curr = allocator.alloc<Binary>(); curr->op = GtUVecI32x4; break; + case BinaryConsts::I32x4LeS: curr = allocator.alloc<Binary>(); curr->op = LeSVecI32x4; break; + case BinaryConsts::I32x4LeU: curr = allocator.alloc<Binary>(); curr->op = LeUVecI32x4; break; + case BinaryConsts::I32x4GeS: curr = allocator.alloc<Binary>(); curr->op = GeSVecI32x4; break; + case BinaryConsts::I32x4GeU: curr = allocator.alloc<Binary>(); curr->op = GeUVecI32x4; break; + case BinaryConsts::F32x4Eq: curr = allocator.alloc<Binary>(); curr->op = EqVecF32x4; break; + case BinaryConsts::F32x4Ne: curr = allocator.alloc<Binary>(); curr->op = NeVecF32x4; break; + case BinaryConsts::F32x4Lt: curr = allocator.alloc<Binary>(); curr->op = LtVecF32x4; break; + case BinaryConsts::F32x4Gt: curr = allocator.alloc<Binary>(); curr->op = GtVecF32x4; break; + case BinaryConsts::F32x4Le: curr = allocator.alloc<Binary>(); curr->op = LeVecF32x4; break; + case BinaryConsts::F32x4Ge: curr = allocator.alloc<Binary>(); curr->op = GeVecF32x4; break; + case BinaryConsts::F64x2Eq: curr = allocator.alloc<Binary>(); curr->op = EqVecF64x2; break; + case BinaryConsts::F64x2Ne: curr = allocator.alloc<Binary>(); curr->op = NeVecF64x2; break; + case BinaryConsts::F64x2Lt: curr = allocator.alloc<Binary>(); curr->op = LtVecF64x2; break; + case BinaryConsts::F64x2Gt: curr = allocator.alloc<Binary>(); curr->op = GtVecF64x2; break; + case BinaryConsts::F64x2Le: curr = allocator.alloc<Binary>(); curr->op = LeVecF64x2; break; + case BinaryConsts::F64x2Ge: curr = allocator.alloc<Binary>(); curr->op = GeVecF64x2; break; + case BinaryConsts::V128And: curr = allocator.alloc<Binary>(); curr->op = AndVec128; break; + case BinaryConsts::V128Or: curr = allocator.alloc<Binary>(); curr->op = OrVec128; break; + case BinaryConsts::V128Xor: curr = allocator.alloc<Binary>(); curr->op = XorVec128; break; + case BinaryConsts::I8x16Add: curr = allocator.alloc<Binary>(); curr->op = AddVecI8x16; break; + case BinaryConsts::I8x16AddSatS: curr = allocator.alloc<Binary>(); curr->op = AddSatSVecI8x16; break; + case BinaryConsts::I8x16AddSatU: curr = allocator.alloc<Binary>(); curr->op = AddSatUVecI8x16; break; + case BinaryConsts::I8x16Sub: curr = allocator.alloc<Binary>(); curr->op = SubVecI8x16; break; + case BinaryConsts::I8x16SubSatS: curr = allocator.alloc<Binary>(); curr->op = SubSatSVecI8x16; break; + case BinaryConsts::I8x16SubSatU: curr = allocator.alloc<Binary>(); curr->op = SubSatUVecI8x16; break; + case BinaryConsts::I8x16Mul: curr = allocator.alloc<Binary>(); curr->op = MulVecI8x16; break; + case BinaryConsts::I16x8Add: curr = allocator.alloc<Binary>(); curr->op = AddVecI16x8; break; + case BinaryConsts::I16x8AddSatS: curr = allocator.alloc<Binary>(); curr->op = AddSatSVecI16x8; break; + case BinaryConsts::I16x8AddSatU: curr = allocator.alloc<Binary>(); curr->op = AddSatUVecI16x8; break; + case BinaryConsts::I16x8Sub: curr = allocator.alloc<Binary>(); curr->op = SubVecI16x8; break; + case BinaryConsts::I16x8SubSatS: curr = allocator.alloc<Binary>(); curr->op = SubSatSVecI16x8; break; + case BinaryConsts::I16x8SubSatU: curr = allocator.alloc<Binary>(); curr->op = SubSatUVecI16x8; break; + case BinaryConsts::I16x8Mul: curr = allocator.alloc<Binary>(); curr->op = MulVecI16x8; break; + case BinaryConsts::I32x4Add: curr = allocator.alloc<Binary>(); curr->op = AddVecI32x4; break; + case BinaryConsts::I32x4Sub: curr = allocator.alloc<Binary>(); curr->op = SubVecI32x4; break; + case BinaryConsts::I32x4Mul: curr = allocator.alloc<Binary>(); curr->op = MulVecI32x4; break; + case BinaryConsts::I64x2Add: curr = allocator.alloc<Binary>(); curr->op = AddVecI64x2; break; + case BinaryConsts::I64x2Sub: curr = allocator.alloc<Binary>(); curr->op = SubVecI64x2; break; + case BinaryConsts::F32x4Add: curr = allocator.alloc<Binary>(); curr->op = AddVecF32x4; break; + case BinaryConsts::F32x4Sub: curr = allocator.alloc<Binary>(); curr->op = SubVecF32x4; break; + case BinaryConsts::F32x4Mul: curr = allocator.alloc<Binary>(); curr->op = MulVecF32x4; break; + case BinaryConsts::F32x4Div: curr = allocator.alloc<Binary>(); curr->op = DivVecF32x4; break; + case BinaryConsts::F32x4Min: curr = allocator.alloc<Binary>(); curr->op = MinVecF32x4; break; + case BinaryConsts::F32x4Max: curr = allocator.alloc<Binary>(); curr->op = MaxVecF32x4; break; + case BinaryConsts::F64x2Add: curr = allocator.alloc<Binary>(); curr->op = AddVecF64x2; break; + case BinaryConsts::F64x2Sub: curr = allocator.alloc<Binary>(); curr->op = SubVecF64x2; break; + case BinaryConsts::F64x2Mul: curr = allocator.alloc<Binary>(); curr->op = MulVecF64x2; break; + case BinaryConsts::F64x2Div: curr = allocator.alloc<Binary>(); curr->op = DivVecF64x2; break; + case BinaryConsts::F64x2Min: curr = allocator.alloc<Binary>(); curr->op = MinVecF64x2; break; + case BinaryConsts::F64x2Max: curr = allocator.alloc<Binary>(); curr->op = MaxVecF64x2; break; + default: return false; + } + if (debug) std::cerr << "zz node: Binary" << std::endl; + curr->right = popNonVoidExpression(); + curr->left = popNonVoidExpression(); + curr->finalize(); + out = curr; + return true; +} +bool WasmBinaryBuilder::maybeVisitSIMDUnary(Expression*& out, uint32_t code) { + Unary* curr; + switch (code) { + case BinaryConsts::I8x16Splat: curr = allocator.alloc<Unary>(); curr->op = SplatVecI8x16; break; + case BinaryConsts::I16x8Splat: curr = allocator.alloc<Unary>(); curr->op = SplatVecI16x8; break; + case BinaryConsts::I32x4Splat: curr = allocator.alloc<Unary>(); curr->op = SplatVecI32x4; break; + case BinaryConsts::I64x2Splat: curr = allocator.alloc<Unary>(); curr->op = SplatVecI64x2; break; + case BinaryConsts::F32x4Splat: curr = allocator.alloc<Unary>(); curr->op = SplatVecF32x4; break; + case BinaryConsts::F64x2Splat: curr = allocator.alloc<Unary>(); curr->op = SplatVecF64x2; break; + case BinaryConsts::V128Not: curr = allocator.alloc<Unary>(); curr->op = NotVec128; break; + case BinaryConsts::I8x16Neg: curr = allocator.alloc<Unary>(); curr->op = NegVecI8x16; break; + case BinaryConsts::I8x16AnyTrue: curr = allocator.alloc<Unary>(); curr->op = AnyTrueVecI8x16; break; + case BinaryConsts::I8x16AllTrue: curr = allocator.alloc<Unary>(); curr->op = AllTrueVecI8x16; break; + case BinaryConsts::I16x8Neg: curr = allocator.alloc<Unary>(); curr->op = NegVecI16x8; break; + case BinaryConsts::I16x8AnyTrue: curr = allocator.alloc<Unary>(); curr->op = AnyTrueVecI16x8; break; + case BinaryConsts::I16x8AllTrue: curr = allocator.alloc<Unary>(); curr->op = AllTrueVecI16x8; break; + case BinaryConsts::I32x4Neg: curr = allocator.alloc<Unary>(); curr->op = NegVecI32x4; break; + case BinaryConsts::I32x4AnyTrue: curr = allocator.alloc<Unary>(); curr->op = AnyTrueVecI32x4; break; + case BinaryConsts::I32x4AllTrue: curr = allocator.alloc<Unary>(); curr->op = AllTrueVecI32x4; break; + case BinaryConsts::I64x2Neg: curr = allocator.alloc<Unary>(); curr->op = NegVecI64x2; break; + case BinaryConsts::I64x2AnyTrue: curr = allocator.alloc<Unary>(); curr->op = AnyTrueVecI64x2; break; + case BinaryConsts::I64x2AllTrue: curr = allocator.alloc<Unary>(); curr->op = AllTrueVecI64x2; break; + case BinaryConsts::F32x4Abs: curr = allocator.alloc<Unary>(); curr->op = AbsVecF32x4; break; + case BinaryConsts::F32x4Neg: curr = allocator.alloc<Unary>(); curr->op = NegVecF32x4; break; + case BinaryConsts::F32x4Sqrt: curr = allocator.alloc<Unary>(); curr->op = SqrtVecF32x4; break; + case BinaryConsts::F64x2Abs: curr = allocator.alloc<Unary>(); curr->op = AbsVecF64x2; break; + case BinaryConsts::F64x2Neg: curr = allocator.alloc<Unary>(); curr->op = NegVecF64x2; break; + case BinaryConsts::F64x2Sqrt: curr = allocator.alloc<Unary>(); curr->op = SqrtVecF64x2; break; + case BinaryConsts::I32x4TruncSatSF32x4: curr = allocator.alloc<Unary>(); curr->op = TruncSatSVecF32x4ToVecI32x4; break; + case BinaryConsts::I32x4TruncSatUF32x4: curr = allocator.alloc<Unary>(); curr->op = TruncSatUVecF32x4ToVecI32x4; break; + case BinaryConsts::I64x2TruncSatSF64x2: curr = allocator.alloc<Unary>(); curr->op = TruncSatSVecF64x2ToVecI64x2; break; + case BinaryConsts::I64x2TruncSatUF64x2: curr = allocator.alloc<Unary>(); curr->op = TruncSatUVecF64x2ToVecI64x2; break; + case BinaryConsts::F32x4ConvertSI32x4: curr = allocator.alloc<Unary>(); curr->op = ConvertSVecI32x4ToVecF32x4; break; + case BinaryConsts::F32x4ConvertUI32x4: curr = allocator.alloc<Unary>(); curr->op = ConvertUVecI32x4ToVecF32x4; break; + case BinaryConsts::F64x2ConvertSI64x2: curr = allocator.alloc<Unary>(); curr->op = ConvertSVecI64x2ToVecF64x2; break; + case BinaryConsts::F64x2ConvertUI64x2: curr = allocator.alloc<Unary>(); curr->op = ConvertUVecI64x2ToVecF64x2; break; + default: return false; + } + curr->value = popNonVoidExpression(); + curr->finalize(); + out = curr; + return true; +} + +bool WasmBinaryBuilder::maybeVisitSIMDConst(Expression*& out, uint32_t code) { + if (code != BinaryConsts::V128Const) { + return false; + } + auto* curr = allocator.alloc<Const>(); + curr->value = getVec128Literal(); + curr->finalize(); + out = curr; + return true; +} + +bool WasmBinaryBuilder::maybeVisitSIMDLoad(Expression*& out, uint32_t code) { + if (code != BinaryConsts::V128Load) { + return false; + } + auto* curr = allocator.alloc<Load>(); + curr->type = v128; + curr->bytes = 16; + readMemoryAccess(curr->align, curr->offset); + curr->ptr = popNonVoidExpression(); + curr->finalize(); + out = curr; + return true; +} + +bool WasmBinaryBuilder::maybeVisitSIMDStore(Expression*& out, uint32_t code) { + if (code != BinaryConsts::V128Store) { + return false; + } + auto* curr = allocator.alloc<Store>(); + curr->bytes = 16; + curr->valueType = v128; + readMemoryAccess(curr->align, curr->offset); + curr->value = popNonVoidExpression(); + curr->ptr = popNonVoidExpression(); + curr->finalize(); + out = curr; + return true; +} + +bool WasmBinaryBuilder::maybeVisitSIMDExtract(Expression*& out, uint32_t code) { + SIMDExtract* curr; + switch (code) { + case BinaryConsts::I8x16ExtractLaneS: curr = allocator.alloc<SIMDExtract>(); curr->op = ExtractLaneSVecI8x16; curr->idx = getLaneIdx(16); break; + case BinaryConsts::I8x16ExtractLaneU: curr = allocator.alloc<SIMDExtract>(); curr->op = ExtractLaneUVecI8x16; curr->idx = getLaneIdx(16); break; + case BinaryConsts::I16x8ExtractLaneS: curr = allocator.alloc<SIMDExtract>(); curr->op = ExtractLaneSVecI16x8; curr->idx = getLaneIdx(8); break; + case BinaryConsts::I16x8ExtractLaneU: curr = allocator.alloc<SIMDExtract>(); curr->op = ExtractLaneUVecI16x8; curr->idx = getLaneIdx(8); break; + case BinaryConsts::I32x4ExtractLane: curr = allocator.alloc<SIMDExtract>(); curr->op = ExtractLaneVecI32x4; curr->idx = getLaneIdx(4); break; + case BinaryConsts::I64x2ExtractLane: curr = allocator.alloc<SIMDExtract>(); curr->op = ExtractLaneVecI64x2; curr->idx = getLaneIdx(2); break; + case BinaryConsts::F32x4ExtractLane: curr = allocator.alloc<SIMDExtract>(); curr->op = ExtractLaneVecF32x4; curr->idx = getLaneIdx(4); break; + case BinaryConsts::F64x2ExtractLane: curr = allocator.alloc<SIMDExtract>(); curr->op = ExtractLaneVecF64x2; curr->idx = getLaneIdx(2); break; + default: return false; + } + curr->vec = popNonVoidExpression(); + curr->finalize(); + out = curr; + return true; +} + +bool WasmBinaryBuilder::maybeVisitSIMDReplace(Expression*& out, uint32_t code) { + SIMDReplace* curr; + switch (code) { + case BinaryConsts::I8x16ReplaceLane: curr = allocator.alloc<SIMDReplace>(); curr->op = ReplaceLaneVecI8x16; curr->idx = getLaneIdx(16); break; + case BinaryConsts::I16x8ReplaceLane: curr = allocator.alloc<SIMDReplace>(); curr->op = ReplaceLaneVecI16x8; curr->idx = getLaneIdx(8); break; + case BinaryConsts::I32x4ReplaceLane: curr = allocator.alloc<SIMDReplace>(); curr->op = ReplaceLaneVecI32x4; curr->idx = getLaneIdx(4); break; + case BinaryConsts::I64x2ReplaceLane: curr = allocator.alloc<SIMDReplace>(); curr->op = ReplaceLaneVecI64x2; curr->idx = getLaneIdx(2); break; + case BinaryConsts::F32x4ReplaceLane: curr = allocator.alloc<SIMDReplace>(); curr->op = ReplaceLaneVecF32x4; curr->idx = getLaneIdx(4); break; + case BinaryConsts::F64x2ReplaceLane: curr = allocator.alloc<SIMDReplace>(); curr->op = ReplaceLaneVecF64x2; curr->idx = getLaneIdx(2); break; + default: return false; + } + curr->value = popNonVoidExpression(); + curr->vec = popNonVoidExpression(); + curr->finalize(); + out = curr; + return true; +} + +bool WasmBinaryBuilder::maybeVisitSIMDShuffle(Expression*& out, uint32_t code) { + if (code != BinaryConsts::V8x16Shuffle) { + return false; + } + auto* curr = allocator.alloc<SIMDShuffle>(); + for (auto i = 0; i < 16; ++i) { + curr->mask[i] = getLaneIdx(32); + } + curr->right = popNonVoidExpression(); + curr->left = popNonVoidExpression(); + curr->finalize(); + out = curr; + return true; +} + +bool WasmBinaryBuilder::maybeVisitSIMDBitselect(Expression*& out, uint32_t code) { + if (code != BinaryConsts::V128Bitselect) { + return false; + } + auto* curr = allocator.alloc<SIMDBitselect>(); + curr->cond = popNonVoidExpression(); + curr->right = popNonVoidExpression(); + curr->left = popNonVoidExpression(); + curr->finalize(); + out = curr; + return true; +} + +bool WasmBinaryBuilder::maybeVisitSIMDShift(Expression*& out, uint32_t code) { + SIMDShift* curr; + switch (code) { + case BinaryConsts::I8x16Shl: curr = allocator.alloc<SIMDShift>(); curr->op = ShlVecI8x16; break; + case BinaryConsts::I8x16ShrS: curr = allocator.alloc<SIMDShift>(); curr->op = ShrSVecI8x16; break; + case BinaryConsts::I8x16ShrU: curr = allocator.alloc<SIMDShift>(); curr->op = ShrUVecI8x16; break; + case BinaryConsts::I16x8Shl: curr = allocator.alloc<SIMDShift>(); curr->op = ShlVecI16x8; break; + case BinaryConsts::I16x8ShrS: curr = allocator.alloc<SIMDShift>(); curr->op = ShrSVecI16x8; break; + case BinaryConsts::I16x8ShrU: curr = allocator.alloc<SIMDShift>(); curr->op = ShrUVecI16x8; break; + case BinaryConsts::I32x4Shl: curr = allocator.alloc<SIMDShift>(); curr->op = ShlVecI32x4; break; + case BinaryConsts::I32x4ShrS: curr = allocator.alloc<SIMDShift>(); curr->op = ShrSVecI32x4; break; + case BinaryConsts::I32x4ShrU: curr = allocator.alloc<SIMDShift>(); curr->op = ShrUVecI32x4; break; + case BinaryConsts::I64x2Shl: curr = allocator.alloc<SIMDShift>(); curr->op = ShlVecI64x2; break; + case BinaryConsts::I64x2ShrS: curr = allocator.alloc<SIMDShift>(); curr->op = ShrSVecI64x2; break; + case BinaryConsts::I64x2ShrU: curr = allocator.alloc<SIMDShift>(); curr->op = ShrUVecI64x2; break; + default: return false; + } + curr->shift = popNonVoidExpression(); + curr->vec = popNonVoidExpression(); + curr->finalize(); + out = curr; + return true; +} + void WasmBinaryBuilder::visitSelect(Select* curr) { if (debug) std::cerr << "zz node: Select" << std::endl; curr->condition = popNonVoidExpression(); diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index de1e4f2e9..ac60cb208 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -632,6 +632,11 @@ Type SExpressionWasmBuilder::stringToType(const char* str, bool allowError, bool if (str[1] == '3' && str[2] == '2' && (prefix || str[3] == 0)) return f32; if (str[1] == '6' && str[2] == '4' && (prefix || str[3] == 0)) return f64; } + if (str[0] == 'v') { + if (str[1] == '1' && str[2] == '2' && str[3] == '8' && (prefix || str[4] == 0)) { + return v128; + } + } if (allowError) return none; throw ParseException("invalid wasm type"); } @@ -859,8 +864,69 @@ Expression* SExpressionWasmBuilder::makeThenOrElse(Element& s) { } Expression* SExpressionWasmBuilder::makeConst(Element& s, Type type) { - auto ret = parseConst(s[1]->str(), type, allocator); - if (!ret) throw ParseException("bad const"); + if (type != v128) { + auto ret = parseConst(s[1]->str(), type, allocator); + if (!ret) throw ParseException("bad const"); + return ret; + } + + auto ret = allocator.alloc<Const>(); + auto getLiteral = [](Expression* expr) { + if (expr == nullptr) { + throw ParseException("Could not parse v128 lane"); + } + return expr->cast<Const>()->value; + }; + Type lane_t = stringToType(s[1]->str()); + size_t lanes = s.size() - 2; + switch (lanes) { + case 2: { + if (lane_t != i64 && lane_t != f64) { + throw ParseException("Unexpected v128 literal lane type"); + } + std::array<Literal, 2> lanes; + for (size_t i = 0; i < 2; ++i) { + lanes[i] = getLiteral(parseConst(s[i+2]->str(), lane_t, allocator)); + } + ret->value = Literal(lanes); + break; + } + case 4: { + if (lane_t != i32 && lane_t != f32) { + throw ParseException("Unexpected v128 literal lane type"); + } + std::array<Literal, 4> lanes; + for (size_t i = 0; i < 4; ++i) { + lanes[i] = getLiteral(parseConst(s[i+2]->str(), lane_t, allocator)); + } + ret->value = Literal(lanes); + break; + } + case 8: { + if (lane_t != i32) { + throw ParseException("Unexpected v128 literal lane type"); + } + std::array<Literal, 8> lanes; + for (size_t i = 0; i < 8; ++i) { + lanes[i] = getLiteral(parseConst(s[i+2]->str(), lane_t, allocator)); + } + ret->value = Literal(lanes); + break; + } + case 16: { + if (lane_t != i32) { + throw ParseException("Unexpected v128 literal lane type"); + } + std::array<Literal, 16> lanes; + for (size_t i = 0; i < 16; ++i) { + lanes[i] = getLiteral(parseConst(s[i+2]->str(), lane_t, allocator)); + } + ret->value = Literal(lanes); + break; + } + default: throw ParseException("Unexpected number of lanes in v128 literal"); + } + ret->finalize(); return ret; } @@ -1011,6 +1077,63 @@ Expression* SExpressionWasmBuilder::makeAtomicWake(Element& s) { return ret; } +static uint8_t parseLaneIdx(const Element* s, size_t lanes) { + const char *str = s->c_str(); + char *end; + auto n = static_cast<unsigned long long>(strtoll(str, &end, 10)); + if (end == str || *end != '\0') throw ParseException("Expected lane index"); + if (n > lanes) throw ParseException("lane index must be less than " + std::to_string(lanes)); + return uint8_t(n); +} + +Expression* SExpressionWasmBuilder::makeSIMDExtract(Element& s, SIMDExtractOp op, size_t lanes) { + auto ret = allocator.alloc<SIMDExtract>(); + ret->op = op; + ret->idx = parseLaneIdx(s[1], lanes); + ret->vec = parseExpression(s[2]); + ret->finalize(); + return ret; +} + +Expression* SExpressionWasmBuilder::makeSIMDReplace(Element& s, SIMDReplaceOp op, size_t lanes) { + auto ret = allocator.alloc<SIMDReplace>(); + ret->op = op; + ret->idx = parseLaneIdx(s[1], lanes); + ret->vec = parseExpression(s[2]); + ret->value = parseExpression(s[3]); + ret->finalize(); + return ret; +} + +Expression* SExpressionWasmBuilder::makeSIMDShuffle(Element& s) { + auto ret = allocator.alloc<SIMDShuffle>(); + for (size_t i = 0; i < 16; ++i) { + ret->mask[i] = parseLaneIdx(s[i+1], 32); + } + ret->left = parseExpression(s[17]); + ret->right = parseExpression(s[18]); + ret->finalize(); + return ret; +} + +Expression* SExpressionWasmBuilder::makeSIMDBitselect(Element& s) { + auto ret = allocator.alloc<SIMDBitselect>(); + ret->left = parseExpression(s[1]); + ret->right = parseExpression(s[2]); + ret->cond = parseExpression(s[3]); + ret->finalize(); + return ret; +} + +Expression* SExpressionWasmBuilder::makeSIMDShift(Element& s, SIMDShiftOp op) { + auto ret = allocator.alloc<SIMDShift>(); + ret->op = op; + ret->vec = parseExpression(s[1]); + ret->shift = parseExpression(s[2]); + ret->finalize(); + return ret; +} + Expression* SExpressionWasmBuilder::makeIf(Element& s) { auto ret = allocator.alloc<If>(); Index i = 1; diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 3f65c9f7a..e1838bb71 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -245,6 +245,11 @@ public: void visitAtomicCmpxchg(AtomicCmpxchg* curr); void visitAtomicWait(AtomicWait* curr); void visitAtomicWake(AtomicWake* curr); + void visitSIMDExtract(SIMDExtract* curr); + void visitSIMDReplace(SIMDReplace* curr); + void visitSIMDShuffle(SIMDShuffle* curr); + void visitSIMDBitselect(SIMDBitselect* curr); + void visitSIMDShift(SIMDShift* curr); void visitBinary(Binary* curr); void visitUnary(Unary* curr); void visitSelect(Select* curr); @@ -498,6 +503,7 @@ void FunctionValidator::visitSetGlobal(SetGlobal* curr) { void FunctionValidator::visitLoad(Load* curr) { if (curr->isAtomic) shouldBeTrue(info.features.hasAtomics(), curr, "Atomic operation (atomics are disabled)"); + if (curr->type == v128) shouldBeTrue(info.features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)"); shouldBeFalse(curr->isAtomic && !getModule()->memory.shared, curr, "Atomic operation with non-shared memory"); validateMemBytes(curr->bytes, curr->type, curr); validateAlignment(curr->align, curr->type, curr->bytes, curr->isAtomic, curr); @@ -510,9 +516,10 @@ void FunctionValidator::visitLoad(Load* curr) { void FunctionValidator::visitStore(Store* curr) { if (curr->isAtomic) shouldBeTrue(info.features.hasAtomics(), curr, "Atomic operation (atomics are disabled)"); + if (curr->valueType == v128) shouldBeTrue(info.features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)"); shouldBeFalse(curr->isAtomic && !getModule()->memory.shared, curr, "Atomic operation with non-shared memory"); validateMemBytes(curr->bytes, curr->valueType, curr); - validateAlignment(curr->align, curr->type, curr->bytes, curr->isAtomic, curr); + validateAlignment(curr->align, curr->valueType, curr->bytes, curr->isAtomic, curr); shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "store pointer type must be i32"); shouldBeUnequal(curr->value->type, none, curr, "store value type must not be none"); shouldBeEqualOrFirstIsUnreachable(curr->value->type, curr->valueType, curr, "store value type must match"); @@ -561,20 +568,77 @@ void FunctionValidator::visitAtomicWake(AtomicWake* curr) { shouldBeEqualOrFirstIsUnreachable(curr->wakeCount->type, i32, curr, "AtomicWake wakeCount type must be i32"); } +void FunctionValidator::visitSIMDExtract(SIMDExtract* curr) { + shouldBeTrue(info.features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)"); + shouldBeEqualOrFirstIsUnreachable(curr->vec->type, v128, curr, "extract_lane must operate on a v128"); + Type lane_t = none; + size_t lanes = 0; + switch (curr->op) { + case ExtractLaneSVecI8x16: + case ExtractLaneUVecI8x16: lane_t = i32; lanes = 16; break; + case ExtractLaneSVecI16x8: + case ExtractLaneUVecI16x8: lane_t = i32; lanes = 8; break; + case ExtractLaneVecI32x4: lane_t = i32; lanes = 4; break; + case ExtractLaneVecI64x2: lane_t = i64; lanes = 2; break; + case ExtractLaneVecF32x4: lane_t = f32; lanes = 4; break; + case ExtractLaneVecF64x2: lane_t = f64; lanes = 2; break; + } + shouldBeEqualOrFirstIsUnreachable(curr->type, lane_t, curr, "extract_lane must have same type as vector lane"); + shouldBeTrue(curr->idx < lanes, curr, "invalid lane index"); +} + +void FunctionValidator::visitSIMDReplace(SIMDReplace* curr) { + shouldBeTrue(info.features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)"); + shouldBeEqualOrFirstIsUnreachable(curr->type, v128, curr, "replace_lane must have type v128"); + shouldBeEqualOrFirstIsUnreachable(curr->vec->type, v128, curr, "replace_lane must operate on a v128"); + Type lane_t = none; + size_t lanes = 0; + switch (curr->op) { + case ReplaceLaneVecI8x16: lane_t = i32; lanes = 16; break; + case ReplaceLaneVecI16x8: lane_t = i32; lanes = 8; break; + case ReplaceLaneVecI32x4: lane_t = i32; lanes = 4; break; + case ReplaceLaneVecI64x2: lane_t = i64; lanes = 2; break; + case ReplaceLaneVecF32x4: lane_t = f32; lanes = 4; break; + case ReplaceLaneVecF64x2: lane_t = f64; lanes = 2; break; + } + shouldBeEqualOrFirstIsUnreachable(curr->value->type, lane_t, curr, "unexpected value type"); + shouldBeTrue(curr->idx < lanes, curr, "invalid lane index"); +} + +void FunctionValidator::visitSIMDShuffle(SIMDShuffle* curr) { + shouldBeTrue(info.features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)"); + shouldBeEqualOrFirstIsUnreachable(curr->type, v128, curr, "v128.shuffle must have type v128"); + shouldBeEqualOrFirstIsUnreachable(curr->left->type, v128, curr, "expected operand of type v128"); + shouldBeEqualOrFirstIsUnreachable(curr->right->type, v128, curr, "expected operand of type v128"); + for (uint8_t idx : curr->mask) { + shouldBeTrue(idx < 32, curr, "Invalid lane index in mask"); + } +} + +void FunctionValidator::visitSIMDBitselect(SIMDBitselect* curr) { + shouldBeTrue(info.features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)"); + shouldBeEqualOrFirstIsUnreachable(curr->type, v128, curr, "v128.bitselect must have type v128"); + shouldBeEqualOrFirstIsUnreachable(curr->left->type, v128, curr, "expected operand of type v128"); + shouldBeEqualOrFirstIsUnreachable(curr->right->type, v128, curr, "expected operand of type v128"); + shouldBeEqualOrFirstIsUnreachable(curr->cond->type, v128, curr, "expected operand of type v128"); +} + +void FunctionValidator::visitSIMDShift(SIMDShift* curr) { + shouldBeTrue(info.features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)"); + shouldBeEqualOrFirstIsUnreachable(curr->type, v128, curr, "vector shift must have type v128"); + shouldBeEqualOrFirstIsUnreachable(curr->vec->type, v128, curr, "expected operand of type v128"); + shouldBeEqualOrFirstIsUnreachable(curr->shift->type, i32, curr, "expected shift amount to have type i32"); +} + void FunctionValidator::validateMemBytes(uint8_t bytes, Type type, Expression* curr) { - switch (bytes) { - case 1: - case 2: - case 4: break; - case 8: { - // if we have a concrete type for the load, then we know the size of the mem operation and - // can validate it - if (type != unreachable) { - shouldBeEqual(getTypeSize(type), 8U, curr, "8-byte mem operations are only allowed with 8-byte wasm types"); - } - break; - } - default: info.fail("Memory operations must be 1,2,4, or 8 bytes", curr, getFunction()); + switch (type) { + case i32: shouldBeTrue(bytes == 1 || bytes == 2 || bytes == 4, curr, "expected i32 operation to touch 1, 2, or 4 bytes"); break; + case i64: shouldBeTrue(bytes == 1 || bytes == 2 || bytes == 4 || bytes == 8, curr, "expected i64 operation to touch 1, 2, 4, or 8 bytes"); break; + case f32: shouldBeEqual(bytes, uint8_t(4), curr, "expected f32 operation to touch 4 bytes"); break; + case f64: shouldBeEqual(bytes, uint8_t(8), curr, "expected f64 operation to touch 8 bytes"); break; + case v128: shouldBeEqual(bytes, uint8_t(16), curr, "expected v128 operation to touch 16 bytes"); break; + case none: WASM_UNREACHABLE(); + case unreachable: break; } } @@ -671,6 +735,86 @@ void FunctionValidator::visitBinary(Binary* curr) { shouldBeEqualOrFirstIsUnreachable(curr->left->type, f64, curr, "f64 op"); break; } + case EqVecI8x16: + case NeVecI8x16: + case LtSVecI8x16: + case LtUVecI8x16: + case LeSVecI8x16: + case LeUVecI8x16: + case GtSVecI8x16: + case GtUVecI8x16: + case GeSVecI8x16: + case GeUVecI8x16: + case EqVecI16x8: + case NeVecI16x8: + case LtSVecI16x8: + case LtUVecI16x8: + case LeSVecI16x8: + case LeUVecI16x8: + case GtSVecI16x8: + case GtUVecI16x8: + case GeSVecI16x8: + case GeUVecI16x8: + case EqVecI32x4: + case NeVecI32x4: + case LtSVecI32x4: + case LtUVecI32x4: + case LeSVecI32x4: + case LeUVecI32x4: + case GtSVecI32x4: + case GtUVecI32x4: + case GeSVecI32x4: + case GeUVecI32x4: + case EqVecF32x4: + case NeVecF32x4: + case LtVecF32x4: + case LeVecF32x4: + case GtVecF32x4: + case GeVecF32x4: + case EqVecF64x2: + case NeVecF64x2: + case LtVecF64x2: + case LeVecF64x2: + case GtVecF64x2: + case GeVecF64x2: + case AndVec128: + case OrVec128: + case XorVec128: + case AddVecI8x16: + case AddSatSVecI8x16: + case AddSatUVecI8x16: + case SubVecI8x16: + case SubSatSVecI8x16: + case SubSatUVecI8x16: + case MulVecI8x16: + case AddVecI16x8: + case AddSatSVecI16x8: + case AddSatUVecI16x8: + case SubVecI16x8: + case SubSatSVecI16x8: + case SubSatUVecI16x8: + case MulVecI16x8: + case AddVecI32x4: + case SubVecI32x4: + case MulVecI32x4: + case AddVecI64x2: + case SubVecI64x2: + case AddVecF32x4: + case SubVecF32x4: + case MulVecF32x4: + case DivVecF32x4: + case MinVecF32x4: + case MaxVecF32x4: + case AddVecF64x2: + case SubVecF64x2: + case MulVecF64x2: + case DivVecF64x2: + case MinVecF64x2: + case MaxVecF64x2: { + shouldBeEqualOrFirstIsUnreachable(curr->left->type, v128, curr, "v128 op"); + shouldBeEqualOrFirstIsUnreachable(curr->right->type, v128, curr, "v128 op"); + break; + } case InvalidBinary: WASM_UNREACHABLE(); } } @@ -804,6 +948,57 @@ void FunctionValidator::visitUnary(Unary* curr) { shouldBeEqual(curr->value->type, i64, curr, "reinterpret/i64 type must be correct"); break; } + case SplatVecI8x16: + case SplatVecI16x8: + case SplatVecI32x4: + shouldBeEqual(curr->type, v128, curr, "expected splat to have v128 type"); + shouldBeEqual(curr->value->type, i32, curr, "expected i32 splat value"); + break; + case SplatVecI64x2: + shouldBeEqual(curr->type, v128, curr, "expected splat to have v128 type"); + shouldBeEqual(curr->value->type, i64, curr, "expected i64 splat value"); + break; + case SplatVecF32x4: + shouldBeEqual(curr->type, v128, curr, "expected splat to have v128 type"); + shouldBeEqual(curr->value->type, f32, curr, "expected f32 splat value"); + break; + case SplatVecF64x2: + shouldBeEqual(curr->type, v128, curr, "expected splat to have v128 type"); + shouldBeEqual(curr->value->type, f64, curr, "expected i64 splat value"); + break; + case NotVec128: + case NegVecI8x16: + case NegVecI16x8: + case NegVecI32x4: + case NegVecI64x2: + case AbsVecF32x4: + case NegVecF32x4: + case SqrtVecF32x4: + case AbsVecF64x2: + case NegVecF64x2: + case SqrtVecF64x2: + case TruncSatSVecF32x4ToVecI32x4: + case TruncSatUVecF32x4ToVecI32x4: + case TruncSatSVecF64x2ToVecI64x2: + case TruncSatUVecF64x2ToVecI64x2: + case ConvertSVecI32x4ToVecF32x4: + case ConvertUVecI32x4ToVecF32x4: + case ConvertSVecI64x2ToVecF64x2: + case ConvertUVecI64x2ToVecF64x2: + shouldBeEqual(curr->type, v128, curr, "expected v128 type"); + shouldBeEqual(curr->value->type, v128, curr, "expected v128 operand"); + break; + case AnyTrueVecI8x16: + case AllTrueVecI8x16: + case AnyTrueVecI16x8: + case AllTrueVecI16x8: + case AnyTrueVecI32x4: + case AllTrueVecI32x4: + case AnyTrueVecI64x2: + case AllTrueVecI64x2: + shouldBeEqual(curr->type, i32, curr, "expected boolean reduction to have i32 type"); + shouldBeEqual(curr->value->type, v128, curr, "expected v128 operand"); + break; case InvalidUnary: WASM_UNREACHABLE(); } } @@ -895,7 +1090,8 @@ void FunctionValidator::validateAlignment(size_t align, Type type, Index bytes, case 1: case 2: case 4: - case 8: break; + case 8: + case 16: break; default:{ info.fail("bad alignment: " + std::to_string(align), curr, getFunction()); break; @@ -913,9 +1109,9 @@ void FunctionValidator::validateAlignment(size_t align, Type type, Index bytes, shouldBeTrue(align <= 8, curr, "alignment must not exceed natural"); break; } - case v128: assert(false && "v128 not implemented yet"); - case none: - case unreachable: {} + case v128: + case unreachable: break; + case none: WASM_UNREACHABLE(); } } diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index fe7927870..87278d3cd 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -104,6 +104,11 @@ const char* getExpressionName(Expression* curr) { case Expression::Id::AtomicRMWId: return "atomic_rmw"; case Expression::Id::AtomicWaitId: return "atomic_wait"; case Expression::Id::AtomicWakeId: return "atomic_wake"; + case Expression::Id::SIMDExtractId: return "simd_extract"; + case Expression::Id::SIMDReplaceId: return "simd_replace"; + case Expression::Id::SIMDShuffleId: return "simd_shuffle"; + case Expression::Id::SIMDBitselectId: return "simd_bitselect"; + case Expression::Id::SIMDShiftId: return "simd_shift"; case Expression::Id::NumExpressionIds: WASM_UNREACHABLE(); } WASM_UNREACHABLE(); @@ -416,6 +421,56 @@ void AtomicWake::finalize() { } } +void SIMDExtract::finalize() { + assert(vec); + switch (op) { + case ExtractLaneSVecI8x16: + case ExtractLaneUVecI8x16: + case ExtractLaneSVecI16x8: + case ExtractLaneUVecI16x8: + case ExtractLaneVecI32x4: type = i32; break; + case ExtractLaneVecI64x2: type = i64; break; + case ExtractLaneVecF32x4: type = f32; break; + case ExtractLaneVecF64x2: type = f64; break; + default: WASM_UNREACHABLE(); + } + if (vec->type == unreachable) { + type = unreachable; + } +} + +void SIMDReplace::finalize() { + assert(vec && value); + type = v128; + if (vec->type == unreachable || value->type == unreachable) { + type = unreachable; + } +} + +void SIMDShuffle::finalize() { + assert(left && right); + type = v128; + if (left->type == unreachable || right->type == unreachable) { + type = unreachable; + } +} + +void SIMDBitselect::finalize() { + assert(left && right && cond); + type = v128; + if (left->type == unreachable || right->type == unreachable || cond->type == unreachable) { + type = unreachable; + } +} + +void SIMDShift::finalize() { + assert(vec && shift); + type = v128; + if (vec->type == unreachable || shift->type == unreachable) { + type = unreachable; + } +} + Const* Const::set(Literal value_) { value = value_; type = value.type; @@ -491,6 +546,39 @@ void Unary::finalize() { case ConvertUInt32ToFloat64: case ConvertSInt64ToFloat64: case ConvertUInt64ToFloat64: type = f64; break; + case SplatVecI8x16: + case SplatVecI16x8: + case SplatVecI32x4: + case SplatVecI64x2: + case SplatVecF32x4: + case SplatVecF64x2: + case NotVec128: + case NegVecI8x16: + case NegVecI16x8: + case NegVecI32x4: + case NegVecI64x2: + case AbsVecF32x4: + case NegVecF32x4: + case SqrtVecF32x4: + case AbsVecF64x2: + case NegVecF64x2: + case SqrtVecF64x2: + case TruncSatSVecF32x4ToVecI32x4: + case TruncSatUVecF32x4ToVecI32x4: + case TruncSatSVecF64x2ToVecI64x2: + case TruncSatUVecF64x2ToVecI64x2: + case ConvertSVecI32x4ToVecF32x4: + case ConvertUVecI32x4ToVecF32x4: + case ConvertSVecI64x2ToVecF64x2: + case ConvertUVecI64x2ToVecF64x2: type = v128; break; + case AnyTrueVecI8x16: + case AllTrueVecI8x16: + case AnyTrueVecI16x8: + case AllTrueVecI16x8: + case AnyTrueVecI32x4: + case AllTrueVecI32x4: + case AnyTrueVecI64x2: + case AllTrueVecI64x2: type = i32; break; case InvalidUnary: WASM_UNREACHABLE(); } } |