summaryrefslogtreecommitdiff
path: root/src/wasm
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm')
-rw-r--r--src/wasm/literal.cpp684
-rw-r--r--src/wasm/wasm-binary.cpp303
-rw-r--r--src/wasm/wasm-s-parser.cpp127
-rw-r--r--src/wasm/wasm-validator.cpp232
-rw-r--r--src/wasm/wasm.cpp88
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();
}
}