summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBen Smith <binji@chromium.org>2020-03-25 12:43:23 -0700
committerGitHub <noreply@github.com>2020-03-25 12:43:23 -0700
commit1f397a3c829869f23ae0ff3e311d22f12d8c72a9 (patch)
tree870b00e154c59c3f79ffe9f5b8ec7b0903c8241d /src
parentd8771ed54edc85ee71e69b835cdaab57bd2722f4 (diff)
downloadwabt-1f397a3c829869f23ae0ff3e311d22f12d8c72a9.tar.gz
wabt-1f397a3c829869f23ae0ff3e311d22f12d8c72a9.tar.bz2
wabt-1f397a3c829869f23ae0ff3e311d22f12d8c72a9.zip
Update testsuite (for SIMD) (#1373)
Lots of changes necessary to make this work, as well as some bug fixes. The main change is allowing `nan:canonical` and `nan:arithmetic` as a possible value for each lane of a `v128`. This needs to propogate through the parser, IR, the JSON format, and the spec interpreter. This also changes the format of the spec JSON file, where a SIMD value is now stored as a list of values instead of a single u128: ``` {"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]} ``` Since the lane type can be `i8` and `i16`, these types can now be used in more places (not just the decompiler). They'll be used for the GC proposal too (for packed values), so I've updated them to use the binary value specified for that proposal. Here are the actual SIMD fixes: * SIMD lanes are malformed if they don't match the binary format, but invalid if they are smaller than the lane width. For example, `i8x16.extract_lane_s` is malformed if the lane is >= 256, because the lane is stored as a byte. But it is invalid if the lane is >= 16. * The `i8x16.narrow_i16x8_u`, `i16x8.narrow_i32x4_u` and `i64x2.load_32x2_u` instructions were not handling sign-extension propoerly. TODO: This code is pretty clumsy now; it would be better to have a universal `Value` and `ExpectedValue` that can be used everywhere, so the logic doesn't need to be duplicated.
Diffstat (limited to 'src')
-rw-r--r--src/binary-writer-spec.cc110
-rw-r--r--src/common.h1
-rw-r--r--src/interp/interp-math.h22
-rw-r--r--src/interp/interp-util.cc8
-rw-r--r--src/interp/interp.cc6
-rw-r--r--src/ir.h62
-rw-r--r--src/tools/spectest-interp.cc705
-rw-r--r--src/type.h6
-rw-r--r--src/wast-parser.cc243
-rw-r--r--src/wast-parser.h4
10 files changed, 841 insertions, 326 deletions
diff --git a/src/binary-writer-spec.cc b/src/binary-writer-spec.cc
index 71d8ff0f..27c83767 100644
--- a/src/binary-writer-spec.cc
+++ b/src/binary-writer-spec.cc
@@ -54,6 +54,8 @@ class BinaryWriterSpec {
void WriteLocation(const Location& loc);
void WriteVar(const Var& var);
void WriteTypeObject(Type type);
+ void WriteF32(uint32_t, ExpectedNan);
+ void WriteF64(uint64_t, ExpectedNan);
void WriteConst(const Const& const_);
void WriteConstVector(const ConstVector& consts);
void WriteAction(const Action& action);
@@ -160,6 +162,38 @@ void BinaryWriterSpec::WriteTypeObject(Type type) {
json_stream_->Writef("}");
}
+void BinaryWriterSpec::WriteF32(uint32_t f32_bits, ExpectedNan expected) {
+ switch (expected) {
+ case ExpectedNan::None:
+ json_stream_->Writef("\"%u\"", f32_bits);
+ break;
+
+ case ExpectedNan::Arithmetic:
+ WriteString("nan:arithmetic");
+ break;
+
+ case ExpectedNan::Canonical:
+ WriteString("nan:canonical");
+ break;
+ }
+}
+
+void BinaryWriterSpec::WriteF64(uint64_t f64_bits, ExpectedNan expected) {
+ switch (expected) {
+ case ExpectedNan::None:
+ json_stream_->Writef("\"%" PRIu64 "\"", f64_bits);
+ break;
+
+ case ExpectedNan::Arithmetic:
+ WriteString("nan:arithmetic");
+ break;
+
+ case ExpectedNan::Canonical:
+ WriteString("nan:canonical");
+ break;
+ }
+}
+
void BinaryWriterSpec::WriteConst(const Const& const_) {
json_stream_->Writef("{");
WriteKey("type");
@@ -181,39 +215,19 @@ void BinaryWriterSpec::WriteConst(const Const& const_) {
json_stream_->Writef("\"%" PRIu64 "\"", const_.u64());
break;
- case Type::F32: {
- /* TODO(binji): write as hex float */
+ case Type::F32:
WriteString("f32");
WriteSeparator();
WriteKey("value");
- if (const_.is_expected_nan()) {
- if (const_.expected() == ExpectedNan::Arithmetic) {
- WriteString("nan:arithmetic");
- } else {
- WriteString("nan:canonical");
- }
- } else {
- json_stream_->Writef("\"%u\"", const_.f32_bits());
- }
+ WriteF32(const_.f32_bits(), const_.expected_nan());
break;
- }
- case Type::F64: {
- /* TODO(binji): write as hex float */
+ case Type::F64:
WriteString("f64");
WriteSeparator();
WriteKey("value");
- if (const_.is_expected_nan()) {
- if (const_.expected() == ExpectedNan::Arithmetic) {
- WriteString("nan:arithmetic");
- } else {
- WriteString("nan:canonical");
- }
- } else {
- json_stream_->Writef("\"%" PRIu64 "\"", const_.f64_bits());
- }
+ WriteF64(const_.f64_bits(), const_.expected_nan());
break;
- }
case Type::Nullref:
WriteString("nullref");
@@ -243,10 +257,52 @@ void BinaryWriterSpec::WriteConst(const Const& const_) {
case Type::V128: {
WriteString("v128");
WriteSeparator();
+ WriteKey("lane_type");
+ WriteString(const_.lane_type().GetName());
+ WriteSeparator();
WriteKey("value");
- char buffer[128];
- WriteUint128(buffer, 128, const_.vec128());
- json_stream_->Writef("\"%s\"", buffer);
+ json_stream_->Writef("[");
+
+ for (int lane = 0; lane < const_.lane_count(); ++lane) {
+ switch (const_.lane_type()) {
+ case Type::I8:
+ json_stream_->Writef("\"%u\"", const_.v128_lane<uint8_t>(lane));
+ break;
+
+ case Type::I16:
+ json_stream_->Writef("\"%u\"", const_.v128_lane<uint16_t>(lane));
+ break;
+
+ case Type::I32:
+ json_stream_->Writef("\"%u\"", const_.v128_lane<uint32_t>(lane));
+ break;
+
+ case Type::I64:
+ json_stream_->Writef("\"%" PRIu64 "\"",
+ const_.v128_lane<uint64_t>(lane));
+ break;
+
+ case Type::F32:
+ WriteF32(const_.v128_lane<uint32_t>(lane),
+ const_.expected_nan(lane));
+ break;
+
+ case Type::F64:
+ WriteF64(const_.v128_lane<uint64_t>(lane),
+ const_.expected_nan(lane));
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+
+ if (lane != const_.lane_count() - 1) {
+ WriteSeparator();
+ }
+ }
+
+ json_stream_->Writef("]");
+
break;
}
diff --git a/src/common.h b/src/common.h
index 7acd71dc..87aee81a 100644
--- a/src/common.h
+++ b/src/common.h
@@ -151,7 +151,6 @@ struct v128 {
memcpy(&v[lane * sizeof(T)], &data, sizeof(data));
}
- private:
uint8_t v[16];
};
diff --git a/src/interp/interp-math.h b/src/interp/interp-math.h
index ccbc9cd6..c3876810 100644
--- a/src/interp/interp-math.h
+++ b/src/interp/interp-math.h
@@ -80,7 +80,6 @@ template <typename T> T WABT_VECTORCALL IntNot(T val) { return ~val; }
template <typename T> T WABT_VECTORCALL IntNeg(T val) { return ~val + 1; }
template <typename T> T WABT_VECTORCALL Add(T lhs, T rhs) { return CanonNaN(lhs + rhs); }
template <typename T> T WABT_VECTORCALL Sub(T lhs, T rhs) { return CanonNaN(lhs - rhs); }
-template <typename T> T WABT_VECTORCALL Mul(T lhs, T rhs) { return CanonNaN(lhs * rhs); }
template <typename T> T WABT_VECTORCALL IntAnd(T lhs, T rhs) { return lhs & rhs; }
template <typename T> T WABT_VECTORCALL IntOr(T lhs, T rhs) { return lhs | rhs; }
template <typename T> T WABT_VECTORCALL IntXor(T lhs, T rhs) { return lhs ^ rhs; }
@@ -92,6 +91,27 @@ template <typename T> T WABT_VECTORCALL IntAndNot(T lhs, T rhs) { return lhs & ~
template <typename T> T WABT_VECTORCALL IntAvgr(T lhs, T rhs) { return (lhs + rhs + 1) / 2; }
template <typename T> T WABT_VECTORCALL Xchg(T lhs, T rhs) { return rhs; }
+// Because of the integer promotion rules [1], any value of a type T which is
+// smaller than `int` will be converted to an `int`, as long as `int` can hold
+// any value of type T.
+//
+// So type `u16` will be promoted to `int`, since all values can be stored in
+// an int. Unfortunately, the product of two `u16` values cannot always be
+// stored in an `int` (e.g. 65535 * 65535). This triggers an error in UBSan.
+//
+// As a result, we make sure to promote the type ahead of time for `u16`. Note
+// that this isn't a problem for any other unsigned types.
+//
+// [1]; https://en.cppreference.com/w/cpp/language/implicit_conversion#Integral_promotion
+template <typename T> struct PromoteMul { using type = T; };
+template <> struct PromoteMul<u16> { using type = u32; };
+
+template <typename T>
+T WABT_VECTORCALL Mul(T lhs, T rhs) {
+ using U = typename PromoteMul<T>::type;
+ return CanonNaN(U(lhs) * U(rhs));
+}
+
template <typename T> struct Mask { using Type = T; };
template <> struct Mask<f32> { using Type = u32; };
template <> struct Mask<f64> { using Type = u64; };
diff --git a/src/interp/interp-util.cc b/src/interp/interp-util.cc
index 5c746ec4..60f17152 100644
--- a/src/interp/interp-util.cc
+++ b/src/interp/interp-util.cc
@@ -43,6 +43,12 @@ std::string TypedValueToString(const TypedValue& tv) {
simd.u32(1), simd.u32(2), simd.u32(3));
}
+ case Type::I8: // For SIMD lane.
+ return StringPrintf("i8:%u", tv.value.Get<u32>() & 0xff);
+
+ case Type::I16: // For SIMD lane.
+ return StringPrintf("i16:%u", tv.value.Get<u32>() & 0xffff);
+
case Type::Nullref:
return StringPrintf("nullref");
@@ -63,9 +69,7 @@ std::string TypedValueToString(const TypedValue& tv) {
case Type::Array:
case Type::Void:
case Type::Any:
- case Type::I8:
case Type::I8U:
- case Type::I16:
case Type::I16U:
case Type::I32U:
// These types are not concrete types and should never exist as a value
diff --git a/src/interp/interp.cc b/src/interp/interp.cc
index b0910357..672f5a4f 100644
--- a/src/interp/interp.cc
+++ b/src/interp/interp.cc
@@ -1580,9 +1580,9 @@ RunResult Thread::StepInternal(Trap::Ptr* out_trap) {
case O::V64X2LoadSplat: return DoSimdLoadSplat<u64x2, u64>(instr, out_trap);
case O::I8X16NarrowI16X8S: return DoSimdNarrow<s8x16, s16x8>();
- case O::I8X16NarrowI16X8U: return DoSimdNarrow<u8x16, u16x8>();
+ case O::I8X16NarrowI16X8U: return DoSimdNarrow<u8x16, s16x8>();
case O::I16X8NarrowI32X4S: return DoSimdNarrow<s16x8, s32x4>();
- case O::I16X8NarrowI32X4U: return DoSimdNarrow<u16x8, u32x4>();
+ case O::I16X8NarrowI32X4U: return DoSimdNarrow<u16x8, s32x4>();
case O::I16X8WidenLowI8X16S: return DoSimdWiden<s16x8, s8x16, true>();
case O::I16X8WidenHighI8X16S: return DoSimdWiden<s16x8, s8x16, false>();
case O::I16X8WidenLowI8X16U: return DoSimdWiden<u16x8, u8x16, true>();
@@ -1597,7 +1597,7 @@ RunResult Thread::StepInternal(Trap::Ptr* out_trap) {
case O::I32X4Load16X4S: return DoSimdLoadExtend<s32x4, s16x4>(instr, out_trap);
case O::I32X4Load16X4U: return DoSimdLoadExtend<u32x4, u16x4>(instr, out_trap);
case O::I64X2Load32X2S: return DoSimdLoadExtend<s64x2, s32x2>(instr, out_trap);
- case O::I64X2Load32X2U: return DoSimdLoadExtend<s64x2, s32x2>(instr, out_trap);
+ case O::I64X2Load32X2U: return DoSimdLoadExtend<u64x2, u32x2>(instr, out_trap);
case O::V128Andnot: return DoSimdBinop(IntAndNot<u64>);
case O::I8X16AvgrU: return DoSimdBinop(IntAvgr<u8>);
diff --git a/src/ir.h b/src/ir.h
index c46e48f5..6430b874 100644
--- a/src/ir.h
+++ b/src/ir.h
@@ -93,6 +93,19 @@ struct Const {
}
Type type() const { return type_; }
+ Type lane_type() const { assert(type_ == Type::V128); return lane_type_; }
+
+ int lane_count() const {
+ switch (lane_type()) {
+ case Type::I8: return 16;
+ case Type::I16: return 8;
+ case Type::I32: return 4;
+ case Type::I64: return 2;
+ case Type::F32: return 4;
+ case Type::F64: return 2;
+ default: WABT_UNREACHABLE;
+ }
+ }
uint32_t u32() const { return data_.u32(0); }
uint64_t u64() const { return data_.u64(0); }
@@ -101,40 +114,71 @@ struct Const {
uintptr_t ref_bits() const { return data_.To<uintptr_t>(0); }
v128 vec128() const { return data_; }
- bool is_expected_nan() const { return nan_ != ExpectedNan::None; }
- ExpectedNan expected() const { assert(is_expected_nan()); return nan_; }
+ template <typename T>
+ T v128_lane(int lane) const { return data_.To<T>(lane); }
void set_u32(uint32_t x) { From(Type::I32, x); }
void set_u64(uint64_t x) { From(Type::I64, x); }
void set_f32(uint32_t x) { From(Type::F32, x); }
void set_f64(uint64_t x) { From(Type::F64, x); }
- void set_vec128(v128 x) { From(Type::V128, x); }
+
+ void set_v128_u8(int lane, uint8_t x) { set_v128_lane(lane, Type::I8, x); }
+ void set_v128_u16(int lane, uint16_t x) { set_v128_lane(lane, Type::I16, x); }
+ void set_v128_u32(int lane, uint32_t x) { set_v128_lane(lane, Type::I32, x); }
+ void set_v128_u64(int lane, uint64_t x) { set_v128_lane(lane, Type::I64, x); }
+ void set_v128_f32(int lane, uint32_t x) { set_v128_lane(lane, Type::F32, x); }
+ void set_v128_f64(int lane, uint64_t x) { set_v128_lane(lane, Type::F64, x); }
// Only used for expectations. (e.g. wast assertions)
- void set_f32(ExpectedNan nan) { From<float>(Type::F32, 0); nan_ = nan; }
- void set_f64(ExpectedNan nan) { From<double>(Type::F64, 0); nan_ = nan; }
+ void set_f32(ExpectedNan nan) { set_f32(0); set_expected_nan(0, nan); }
+ void set_f64(ExpectedNan nan) { set_f64(0); set_expected_nan(0, nan); }
void set_hostref(uintptr_t x) { From(Type::Hostref, x); }
void set_nullref() { From<uintptr_t>(Type::Nullref, 0); }
void set_funcref() { From<uintptr_t>(Type::Funcref, 0); }
+ bool is_expected_nan(int lane = 0) const {
+ return expected_nan(lane) != ExpectedNan::None;
+ }
+
+ ExpectedNan expected_nan(int lane = 0) const {
+ return lane < 4 ? nan_[lane] : ExpectedNan::None;
+ }
+
+ void set_expected_nan(int lane, ExpectedNan nan) {
+ if (lane < 4) {
+ nan_[lane] = nan;
+ }
+ }
+
+ // v128 support
Location loc;
private:
template <typename T>
+ void set_v128_lane(int lane, Type lane_type, T x) {
+ lane_type_ = lane_type;
+ From(Type::V128, x, lane);
+ set_expected_nan(lane, ExpectedNan::None);
+ }
+
+ template <typename T>
Const(Type type, T data, const Location& loc = Location()) : loc(loc) {
From<T>(type, data);
}
template <typename T>
- void From(Type type, T data) {
+ void From(Type type, T data, int lane = 0) {
+ static_assert(sizeof(T) <= sizeof(data_), "Invalid cast!");
+ assert((lane + 1) * sizeof(T) <= sizeof(data_));
type_ = type;
- data_.From<T>(0, data);
- nan_ = ExpectedNan::None;
+ data_.From<T>(lane, data);
+ set_expected_nan(lane, ExpectedNan::None);
}
Type type_;
+ Type lane_type_; // Only valid if type_ == Type::V128.
v128 data_;
- ExpectedNan nan_ = ExpectedNan::None;
+ ExpectedNan nan_[4];
};
typedef std::vector<Const> ConstVector;
diff --git a/src/tools/spectest-interp.cc b/src/tools/spectest-interp.cc
index b0837ca7..cc61e8c3 100644
--- a/src/tools/spectest-interp.cc
+++ b/src/tools/spectest-interp.cc
@@ -160,11 +160,122 @@ class RegisterCommand : public CommandMixin<CommandType::Register> {
};
struct ExpectedValue {
- bool is_expected_nan;
TypedValue value;
- ExpectedNan expectedNan;
+ Type lane_type; // Only valid if value.type == Type::V128.
+ // Up to 4 NaN values used, depending on |value.type| and |lane_type|:
+ // | type | lane_type | valid |
+ // | f32 | | nan[0] |
+ // | f64 | | nan[0] |
+ // | v128 | f32 | nan[0] through nan[3] |
+ // | v128 | f64 | nan[0],nan[1] |
+ // | * | * | none valid |
+ ExpectedNan nan[4];
};
+int LaneCountFromType(Type type) {
+ switch (type) {
+ case Type::I8: return 16;
+ case Type::I16: return 8;
+ case Type::I32: return 4;
+ case Type::I64: return 2;
+ case Type::F32: return 4;
+ case Type::F64: return 2;
+ default: assert(false); return 0;
+ }
+}
+
+ExpectedValue GetLane(const ExpectedValue& ev, int lane) {
+ int lane_count = LaneCountFromType(ev.lane_type);
+ assert(ev.value.type == Type::V128);
+ assert(lane < lane_count);
+
+ ExpectedValue result;
+ result.value.type = ev.lane_type;
+
+ v128 vec = ev.value.value.Get<v128>();
+
+ for (int lane = 0; lane < lane_count; ++lane) {
+ switch (ev.lane_type) {
+ case Type::I8:
+ result.nan[0] = ExpectedNan::None;
+ result.value.value.Set<u32>(vec.u8(lane));
+ break;
+
+ case Type::I16:
+ result.nan[0] = ExpectedNan::None;
+ result.value.value.Set<u32>(vec.u16(lane));
+ break;
+
+ case Type::I32:
+ result.nan[0] = ExpectedNan::None;
+ result.value.value.Set<u32>(vec.u32(lane));
+ break;
+
+ case Type::I64:
+ result.nan[0] = ExpectedNan::None;
+ result.value.value.Set<u64>(vec.u64(lane));
+ break;
+
+ case Type::F32:
+ result.nan[0] = ev.nan[lane];
+ result.value.value.Set<f32>(Bitcast<f32>(vec.f32_bits(lane)));
+ break;
+
+ case Type::F64:
+ result.nan[0] = ev.nan[lane];
+ result.value.value.Set<f64>(Bitcast<f64>(vec.f64_bits(lane)));
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+ }
+ return result;
+}
+
+TypedValue GetLane(const TypedValue& tv, Type lane_type, int lane) {
+ int lane_count = LaneCountFromType(lane_type);
+ assert(tv.type == Type::V128);
+ assert(lane < lane_count);
+
+ TypedValue result;
+ result.type = lane_type;
+
+ v128 vec = tv.value.Get<v128>();
+
+ for (int lane = 0; lane < lane_count; ++lane) {
+ switch (lane_type) {
+ case Type::I8:
+ result.value.Set<u32>(vec.u8(lane));
+ break;
+
+ case Type::I16:
+ result.value.Set<u32>(vec.u16(lane));
+ break;
+
+ case Type::I32:
+ result.value.Set<u32>(vec.u32(lane));
+ break;
+
+ case Type::I64:
+ result.value.Set<u64>(vec.u64(lane));
+ break;
+
+ case Type::F32:
+ result.value.Set<f32>(Bitcast<f32>(vec.f32_bits(lane)));
+ break;
+
+ case Type::F64:
+ result.value.Set<f64>(Bitcast<f64>(vec.f64_bits(lane)));
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+ }
+ return result;
+}
+
class AssertReturnCommand : public CommandMixin<CommandType::AssertReturn> {
public:
Action action;
@@ -210,6 +321,10 @@ class JSONParser {
private:
void WABT_PRINTF_FORMAT(2, 3) PrintError(const char* format, ...);
+ // Whether to allow parsing of expectation-only forms (e.g. `nan:canonical`,
+ // `nan:arithmetic`, etc.)
+ enum class AllowExpected { No, Yes };
+
void PutbackChar();
int ReadChar();
void SkipWhitespace();
@@ -221,14 +336,32 @@ class JSONParser {
wabt::Result ParseKeyStringValue(const char* key, std::string* out_string);
wabt::Result ParseOptNameStringValue(std::string* out_string);
wabt::Result ParseLine(uint32_t* out_line_number);
+ wabt::Result ParseType(Type* out_type);
wabt::Result ParseTypeObject(Type* out_type);
wabt::Result ParseTypeVector(TypeVector* out_types);
wabt::Result ParseConst(TypedValue* out_value);
- wabt::Result ParseConstValue(TypedValue* out_value,
- string_view type_str,
- string_view value_str);
+ wabt::Result ParseI32Value(uint32_t* out_value, string_view value_str);
+ wabt::Result ParseI64Value(uint64_t* out_value, string_view value_str);
+ wabt::Result ParseF32Value(uint32_t* out_value,
+ ExpectedNan* out_nan,
+ string_view value_str,
+ AllowExpected);
+ wabt::Result ParseF64Value(uint64_t* out_value,
+ ExpectedNan* out_nan,
+ string_view value_str,
+ AllowExpected);
+ wabt::Result ParseLaneConstValue(Type lane_type,
+ int lane,
+ ExpectedValue* out_value,
+ string_view value_str,
+ AllowExpected);
+ wabt::Result ParseConstValue(Type type,
+ Value* out_value,
+ ExpectedNan* out_nan,
+ string_view value_str,
+ AllowExpected);
wabt::Result ParseConstVector(ValueTypes* out_types, Values* out_values);
- wabt::Result ParseExpectedValue(ExpectedValue* out_value);
+ wabt::Result ParseExpectedValue(ExpectedValue* out_value, AllowExpected);
wabt::Result ParseExpectedValues(std::vector<ExpectedValue>* out_values);
wabt::Result ParseAction(Action* out_action);
wabt::Result ParseActionResult();
@@ -435,43 +568,47 @@ wabt::Result JSONParser::ParseLine(uint32_t* out_line_number) {
return wabt::Result::Ok;
}
-wabt::Result JSONParser::ParseTypeObject(Type* out_type) {
+wabt::Result JSONParser::ParseType(Type* out_type) {
std::string type_str;
- EXPECT("{");
- PARSE_KEY_STRING_VALUE("type", &type_str);
- EXPECT("}");
+ CHECK_RESULT(ParseString(&type_str));
if (type_str == "i32") {
*out_type = Type::I32;
- return wabt::Result::Ok;
} else if (type_str == "f32") {
*out_type = Type::F32;
- return wabt::Result::Ok;
} else if (type_str == "i64") {
*out_type = Type::I64;
- return wabt::Result::Ok;
} else if (type_str == "f64") {
*out_type = Type::F64;
- return wabt::Result::Ok;
} else if (type_str == "v128") {
*out_type = Type::V128;
- return wabt::Result::Ok;
+ } else if (type_str == "i8") {
+ *out_type = Type::I8;
+ } else if (type_str == "i16") {
+ *out_type = Type::I16;
} else if (type_str == "funcref") {
*out_type = Type::Funcref;
- return wabt::Result::Ok;
} else if (type_str == "anyref") {
*out_type = Type::Anyref;
- return wabt::Result::Ok;
} else if (type_str == "nullref") {
*out_type = Type::Nullref;
- return wabt::Result::Ok;
} else if (type_str == "exnref") {
*out_type = Type::Exnref;
- return wabt::Result::Ok;
+ } else if (type_str == "hostref") {
+ *out_type = Type::Hostref;
} else {
PrintError("unknown type: \"%s\"", type_str.c_str());
return wabt::Result::Error;
}
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseTypeObject(Type* out_type) {
+ EXPECT("{");
+ EXPECT_KEY("type");
+ CHECK_RESULT(ParseType(out_type));
+ EXPECT("}");
+ return wabt::Result::Ok;
}
wabt::Result JSONParser::ParseTypeVector(TypeVector* out_types) {
@@ -491,121 +628,252 @@ wabt::Result JSONParser::ParseTypeVector(TypeVector* out_types) {
}
wabt::Result JSONParser::ParseConst(TypedValue* out_value) {
- std::string type_str;
- std::string value_str;
- EXPECT("{");
- PARSE_KEY_STRING_VALUE("type", &type_str);
- EXPECT(",");
- PARSE_KEY_STRING_VALUE("value", &value_str);
- EXPECT("}");
+ ExpectedValue expected;
+ CHECK_RESULT(ParseExpectedValue(&expected, AllowExpected::No));
+ *out_value = expected.value;
+ return wabt::Result::Ok;
+}
- return ParseConstValue(out_value, type_str, value_str);
+wabt::Result JSONParser::ParseI32Value(uint32_t* out_value,
+ string_view value_str) {
+ if (Failed(ParseInt32(value_str.begin(), value_str.end(), out_value,
+ ParseIntType::UnsignedOnly))) {
+ PrintError("invalid i32 literal");
+ return wabt::Result::Error;
+ }
+ return wabt::Result::Ok;
}
-wabt::Result JSONParser::ParseConstValue(TypedValue* out_value,
- string_view type_str,
- string_view value_str) {
- const char* value_start = value_str.data();
- const char* value_end = value_str.data() + value_str.size();
- if (type_str == "i32") {
- uint32_t value;
- if (Failed((ParseInt32(value_start, value_end, &value,
- ParseIntType::UnsignedOnly)))) {
- PrintError("invalid i32 literal");
- return wabt::Result::Error;
+wabt::Result JSONParser::ParseI64Value(uint64_t* out_value,
+ string_view value_str) {
+ if (Failed(ParseInt64(value_str.begin(), value_str.end(), out_value,
+ ParseIntType::UnsignedOnly))) {
+ PrintError("invalid i64 literal");
+ return wabt::Result::Error;
+ }
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseF32Value(uint32_t* out_value,
+ ExpectedNan* out_nan,
+ string_view value_str,
+ AllowExpected allow_expected) {
+ if (allow_expected == AllowExpected::Yes) {
+ *out_value = 0;
+ if (value_str == "nan:canonical") {
+ *out_nan = ExpectedNan::Canonical;
+ return wabt::Result::Ok;
+ } else if (value_str == "nan:arithmetic") {
+ *out_nan = ExpectedNan::Arithmetic;
+ return wabt::Result::Ok;
}
- out_value->type = ValueType::I32;
- out_value->value.Set(value);
- } else if (type_str == "f32") {
- uint32_t value_bits;
- if (Failed(ParseInt32(value_start, value_end, &value_bits,
- ParseIntType::UnsignedOnly))) {
- PrintError("invalid f32 literal");
- return wabt::Result::Error;
+ }
+
+ *out_nan = ExpectedNan::None;
+ if (Failed(ParseInt32(value_str.begin(), value_str.end(), out_value,
+ ParseIntType::UnsignedOnly))) {
+ PrintError("invalid f32 literal");
+ return wabt::Result::Error;
+ }
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseF64Value(uint64_t* out_value,
+ ExpectedNan* out_nan,
+ string_view value_str,
+ AllowExpected allow_expected) {
+ if (allow_expected == AllowExpected::Yes) {
+ *out_value = 0;
+ if (value_str == "nan:canonical") {
+ *out_nan = ExpectedNan::Canonical;
+ return wabt::Result::Ok;
+ } else if (value_str == "nan:arithmetic") {
+ *out_nan = ExpectedNan::Arithmetic;
+ return wabt::Result::Ok;
}
- out_value->type = ValueType::F32;
- out_value->value.Set(Bitcast<f32>(value_bits));
- } else if (type_str == "i64") {
- uint64_t value;
- if (Failed(ParseInt64(value_start, value_end, &value,
- ParseIntType::UnsignedOnly))) {
- PrintError("invalid i64 literal");
- return wabt::Result::Error;
+ }
+
+ *out_nan = ExpectedNan::None;
+ if (Failed(ParseInt64(value_str.begin(), value_str.end(), out_value,
+ ParseIntType::UnsignedOnly))) {
+ PrintError("invalid f64 literal");
+ return wabt::Result::Error;
+ }
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseLaneConstValue(Type lane_type,
+ int lane,
+ ExpectedValue* out_value,
+ string_view value_str,
+ AllowExpected allow_expected) {
+ v128& v = out_value->value.value.v128_;
+
+ switch (lane_type) {
+ case Type::I8: {
+ uint32_t value;
+ CHECK_RESULT(ParseI32Value(&value, value_str));
+ v.set_u8(lane, value);
+ break;
}
- out_value->type = ValueType::I64;
- out_value->value.Set(value);
- } else if (type_str == "f64") {
- uint64_t value_bits;
- if (Failed((ParseInt64(value_start, value_end, &value_bits,
- ParseIntType::UnsignedOnly)))) {
- PrintError("invalid f64 literal");
- return wabt::Result::Error;
+
+ case Type::I16: {
+ uint32_t value;
+ CHECK_RESULT(ParseI32Value(&value, value_str));
+ v.set_u16(lane, value);
+ break;
}
- out_value->type = ValueType::F64;
- out_value->value.Set(Bitcast<f64>(value_bits));
- } else if (type_str == "v128") {
- v128 value_bits;
- if (Failed(ParseUint128(value_start, value_end, &value_bits))) {
- PrintError("invalid v128 literal");
- return wabt::Result::Error;
+
+ case Type::I32: {
+ uint32_t value;
+ CHECK_RESULT(ParseI32Value(&value, value_str));
+ v.set_u32(lane, value);
+ break;
}
- out_value->type = ValueType::V128;
- out_value->value.Set(value_bits);
- } else if (type_str == "nullref") {
- out_value->type = ValueType::Nullref;
- out_value->value.Set(Ref::Null);
- } else if (type_str == "hostref") {
- uint32_t value;
- if (Failed(ParseInt32(value_start, value_end, &value,
- ParseIntType::UnsignedOnly))) {
- PrintError("invalid hostref literal");
- return wabt::Result::Error;
+
+ case Type::I64: {
+ uint64_t value;
+ CHECK_RESULT(ParseI64Value(&value, value_str));
+ v.set_u64(lane, value);
+ break;
}
- out_value->type = ValueType::Hostref;
- // TODO: hack, just whatever ref is at this index; but skip null (which is
- // always 0).
- out_value->value.Set(Ref{value + 1});
- } else if (type_str == "funcref") {
- uint32_t value;
- if (Failed(ParseInt32(value_start, value_end, &value,
- ParseIntType::UnsignedOnly))) {
- PrintError("invalid funcref literal");
+
+ case Type::F32: {
+ ExpectedNan nan;
+ uint32_t value_bits;
+ CHECK_RESULT(ParseF32Value(&value_bits, &nan, value_str, allow_expected));
+ v.set_f32_bits(lane, value_bits);
+ assert(lane < 4);
+ out_value->nan[lane] = nan;
+ break;
+ }
+
+ case Type::F64: {
+ ExpectedNan nan;
+ uint64_t value_bits;
+ CHECK_RESULT(ParseF64Value(&value_bits, &nan, value_str, allow_expected));
+ v.set_f64_bits(lane, value_bits);
+ assert(lane < 2);
+ out_value->nan[lane] = nan;
+ break;
+ }
+
+ default:
+ PrintError("unknown concrete type: \"%s\"", lane_type.GetName());
return wabt::Result::Error;
+ }
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseConstValue(Type type,
+ Value* out_value,
+ ExpectedNan* out_nan,
+ string_view value_str,
+ AllowExpected allow_expected) {
+ *out_nan = ExpectedNan::None;
+
+ switch (type) {
+ case Type::I32: {
+ uint32_t value;
+ CHECK_RESULT(ParseI32Value(&value, value_str));
+ out_value->Set(value);
+ break;
}
- out_value->type = ValueType::Funcref;
- out_value->value.Set(Ref{value});
- } else {
- PrintError("unknown concrete type: \"%s\"", type_str.to_string().c_str());
- return wabt::Result::Error;
+
+ case Type::F32: {
+ uint32_t value_bits;
+ CHECK_RESULT(
+ ParseF32Value(&value_bits, out_nan, value_str, allow_expected));
+ out_value->Set(Bitcast<f32>(value_bits));
+ break;
+ }
+
+ case Type::I64: {
+ uint64_t value;
+ CHECK_RESULT(ParseI64Value(&value, value_str));
+ out_value->Set(value);
+ break;
+ }
+
+ case Type::F64: {
+ uint64_t value_bits;
+ CHECK_RESULT(
+ ParseF64Value(&value_bits, out_nan, value_str, allow_expected));
+ out_value->Set(Bitcast<f64>(value_bits));
+ break;
+ }
+
+ case Type::V128:
+ assert(false); // Should use ParseLaneConstValue instead.
+ break;
+
+ case Type::Nullref: {
+ out_value->Set(Ref::Null);
+ break;
+ }
+
+ case Type::Hostref: {
+ uint32_t value;
+ CHECK_RESULT(ParseI32Value(&value, value_str));
+ // TODO: hack, just whatever ref is at this index; but skip null (which is
+ // always 0).
+ out_value->Set(Ref{value + 1});
+ break;
+ }
+
+ case Type::Funcref: {
+ uint32_t value;
+ CHECK_RESULT(ParseI32Value(&value, value_str));
+ out_value->Set(Ref{value});
+ break;
+ }
+
+ default:
+ PrintError("unknown concrete type: \"%s\"", type.GetName());
+ return wabt::Result::Error;
}
return wabt::Result::Ok;
}
-wabt::Result JSONParser::ParseExpectedValue(ExpectedValue* out_value) {
- std::string type_str;
+wabt::Result JSONParser::ParseExpectedValue(ExpectedValue* out_value,
+ AllowExpected allow_expected) {
+ Type type;
std::string value_str;
EXPECT("{");
- PARSE_KEY_STRING_VALUE("type", &type_str);
+ EXPECT_KEY("type");
+ CHECK_RESULT(ParseType(&type));
EXPECT(",");
- PARSE_KEY_STRING_VALUE("value", &value_str);
- EXPECT("}");
-
- if (type_str == "f32" || type_str == "f64") {
- out_value->value.type = type_str == "f32" ? ValueType::F32 : ValueType::F64;
- if (value_str == "nan:canonical") {
- out_value->is_expected_nan = true;
- out_value->expectedNan = ExpectedNan::Canonical;
- return wabt::Result::Ok;
- } else if (value_str == "nan:arithmetic") {
- out_value->is_expected_nan = true;
- out_value->expectedNan = ExpectedNan::Arithmetic;
- return wabt::Result::Ok;
+ if (type == Type::V128) {
+ Type lane_type;
+ EXPECT_KEY("lane_type");
+ CHECK_RESULT(ParseType(&lane_type));
+ EXPECT(",");
+ EXPECT_KEY("value");
+ EXPECT("[");
+
+ int lane_count = LaneCountFromType(lane_type);
+ for (int lane = 0; lane < lane_count; ++lane) {
+ CHECK_RESULT(ParseString(&value_str));
+ CHECK_RESULT(ParseLaneConstValue(lane_type, lane, out_value, value_str,
+ allow_expected));
+ if (lane < lane_count - 1) {
+ EXPECT(",");
+ }
}
+ EXPECT("]");
+ out_value->value.type = type;
+ out_value->lane_type = lane_type;
+ } else {
+ PARSE_KEY_STRING_VALUE("value", &value_str);
+ CHECK_RESULT(ParseConstValue(type, &out_value->value.value,
+ &out_value->nan[0], value_str,
+ allow_expected));
+ out_value->value.type = type;
}
+ EXPECT("}");
- out_value->is_expected_nan = false;
- return ParseConstValue(&out_value->value, type_str, value_str);
+ return wabt::Result::Ok;
}
wabt::Result JSONParser::ParseExpectedValues(
@@ -618,7 +886,7 @@ wabt::Result JSONParser::ParseExpectedValues(
EXPECT(",");
}
ExpectedValue value;
- CHECK_RESULT(ParseExpectedValue(&value));
+ CHECK_RESULT(ParseExpectedValue(&value, AllowExpected::Yes));
out_values->push_back(value);
first = false;
}
@@ -907,6 +1175,12 @@ class CommandRunner {
wabt::Result OnAssertTrapCommand(const AssertTrapCommand*);
wabt::Result OnAssertExhaustionCommand(const AssertExhaustionCommand*);
+ wabt::Result CheckAssertReturnResult(const AssertReturnCommand* command,
+ int index,
+ ExpectedValue expected,
+ TypedValue actual,
+ bool print_error);
+
void TallyCommand(wabt::Result);
wabt::Result ReadInvalidTextModule(string_view module_filename,
@@ -1323,39 +1597,6 @@ wabt::Result CommandRunner::OnAssertUninstantiableCommand(
return wabt::Result::Ok;
}
-static bool TypedValuesAreEqual(const TypedValue& expected,
- const TypedValue& actual) {
- assert(expected.type == actual.type || IsReference(expected.type));
- switch (expected.type) {
- case Type::I32:
- return expected.value.Get<u32>() == actual.value.Get<u32>();
-
- case Type::F32:
- return Bitcast<u32>(expected.value.Get<f32>()) ==
- Bitcast<u32>(actual.value.Get<f32>());
-
- case Type::I64:
- return expected.value.Get<u64>() == actual.value.Get<u64>();
-
- case Type::F64:
- return Bitcast<u64>(expected.value.Get<f64>()) ==
- Bitcast<u64>(actual.value.Get<f64>());
-
- case Type::V128:
- return expected.value.Get<v128>() == actual.value.Get<v128>();
-
- case Type::Nullref:
- return actual.value.Get<Ref>() == Ref::Null;
-
- case Type::Funcref:
- case Type::Hostref:
- return expected.value.Get<Ref>() == actual.value.Get<Ref>();
-
- default:
- WABT_UNREACHABLE;
- }
-}
-
static bool WABT_VECTORCALL IsCanonicalNan(f32 val) {
const u32 kQuietNan = 0x7fc00000U;
const u32 kQuietNegNan = 0xffc00000U;
@@ -1380,6 +1621,138 @@ static bool WABT_VECTORCALL IsArithmeticNan(f64 val) {
return (Bitcast<u64>(val) & kQuietNan) == kQuietNan;
}
+static std::string ExpectedValueToString(const ExpectedValue& ev) {
+ // Extend TypedValueToString to print expected nan values too.
+ switch (ev.value.type) {
+ case Type::F32:
+ case Type::F64:
+ switch (ev.nan[0]) {
+ case ExpectedNan::None:
+ return TypedValueToString(ev.value);
+
+ case ExpectedNan::Arithmetic:
+ return StringPrintf("%s:nan:arithmetic", ev.value.type.GetName());
+
+ case ExpectedNan::Canonical:
+ return StringPrintf("%s:nan:canonical", ev.value.type.GetName());
+ }
+ break;
+
+ case Type::V128: {
+ int lane_count = LaneCountFromType(ev.lane_type);
+ std::string result = "v128 ";
+ for (int lane = 0; lane < lane_count; ++lane) {
+ result += ExpectedValueToString(GetLane(ev, lane));
+ }
+ return result;
+ }
+
+ default:
+ break;
+ }
+ return TypedValueToString(ev.value);
+}
+
+wabt::Result CommandRunner::CheckAssertReturnResult(
+ const AssertReturnCommand* command,
+ int index,
+ ExpectedValue expected,
+ TypedValue actual,
+ bool print_error) {
+ assert(expected.value.type == actual.type ||
+ IsReference(expected.value.type));
+ bool ok = true;
+ switch (expected.value.type) {
+ case Type::I8:
+ case Type::I16:
+ case Type::I32:
+ ok = expected.value.value.Get<u32>() == actual.value.Get<u32>();
+ break;
+
+ case Type::I64:
+ ok = expected.value.value.Get<u64>() == actual.value.Get<u64>();
+ break;
+
+ case Type::F32:
+ switch (expected.nan[0]) {
+ case ExpectedNan::Arithmetic:
+ ok = IsArithmeticNan(actual.value.Get<f32>());
+ break;
+
+ case ExpectedNan::Canonical:
+ ok = IsCanonicalNan(actual.value.Get<f32>());
+ break;
+
+ case ExpectedNan::None:
+ ok = Bitcast<u32>(expected.value.value.Get<f32>()) ==
+ Bitcast<u32>(actual.value.Get<f32>());
+ break;
+ }
+ break;
+
+ case Type::F64:
+ switch (expected.nan[0]) {
+ case ExpectedNan::Arithmetic:
+ ok = IsArithmeticNan(actual.value.Get<f64>());
+ break;
+
+ case ExpectedNan::Canonical:
+ ok = IsCanonicalNan(actual.value.Get<f64>());
+ break;
+
+ case ExpectedNan::None:
+ ok = Bitcast<u64>(expected.value.value.Get<f64>()) ==
+ Bitcast<u64>(actual.value.Get<f64>());
+ break;
+ }
+ break;
+
+ case Type::V128: {
+ // Compare each lane as if it were its own value.
+ for (int lane = 0; lane < LaneCountFromType(expected.lane_type); ++lane) {
+ ExpectedValue lane_expected = GetLane(expected, lane);
+ TypedValue lane_actual = GetLane(actual, expected.lane_type, lane);
+
+ if (Failed(CheckAssertReturnResult(command, index, lane_expected,
+ lane_actual, false))) {
+ PrintError(command->line,
+ "mismatch in lane %u of result %u of assert_return: "
+ "expected %s, got %s",
+ lane, index, ExpectedValueToString(lane_expected).c_str(),
+ TypedValueToString(lane_actual).c_str());
+ ok = false;
+ }
+ }
+ break;
+ }
+
+ case Type::Nullref:
+ ok = actual.value.Get<Ref>() == Ref::Null;
+ break;
+
+ case Type::Funcref:
+ // A funcref expectation only requires that the reference be a function,
+ // but it doesn't check the actual index.
+ ok = store_.HasValueType(actual.value.Get<Ref>(), Type::Funcref);
+ break;
+
+ case Type::Hostref:
+ ok = expected.value.value.Get<Ref>() == actual.value.Get<Ref>();
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+
+ if (!ok && print_error) {
+ PrintError(command->line,
+ "mismatch in result %u of assert_return: expected %s, got %s",
+ index, ExpectedValueToString(expected).c_str(),
+ TypedValueToString(actual).c_str());
+ }
+ return ok ? wabt::Result::Ok : wabt::Result::Error;
+}
+
wabt::Result CommandRunner::OnAssertReturnCommand(
const AssertReturnCommand* command) {
ActionResult action_result =
@@ -1404,45 +1777,7 @@ wabt::Result CommandRunner::OnAssertReturnCommand(
const ExpectedValue& expected = command->expected[i];
TypedValue actual{action_result.types[i], action_result.values[i]};
- if (expected.is_expected_nan) {
- bool is_nan;
- if (expected.expectedNan == ExpectedNan::Arithmetic) {
- if (expected.value.type == Type::F64) {
- is_nan = IsArithmeticNan(actual.value.Get<f64>());
- } else {
- is_nan = IsArithmeticNan(actual.value.Get<f32>());
- }
- } else if (expected.expectedNan == ExpectedNan::Canonical) {
- if (expected.value.type == Type::F64) {
- is_nan = IsCanonicalNan(actual.value.Get<f64>());
- } else {
- is_nan = IsCanonicalNan(actual.value.Get<f32>());
- }
- } else {
- WABT_UNREACHABLE;
- }
- if (!is_nan) {
- PrintError(command->line, "expected result to be nan, got %s",
- TypedValueToString(actual).c_str());
- result = wabt::Result::Error;
- }
- } else if (expected.value.type == Type::Funcref) {
- if (!store_.HasValueType(actual.value.Get<Ref>(), Type::Funcref)) {
- PrintError(command->line,
- "mismatch in result %" PRIzd
- " of assert_return: expected funcref, got %s",
- i, TypedValueToString(actual).c_str());
- }
- } else {
- if (!TypedValuesAreEqual(expected.value, actual)) {
- PrintError(command->line,
- "mismatch in result %" PRIzd
- " of assert_return: expected %s, got %s",
- i, TypedValueToString(expected.value).c_str(),
- TypedValueToString(actual).c_str());
- result = wabt::Result::Error;
- }
- }
+ result |= CheckAssertReturnResult(command, i, expected, actual, true);
}
return result;
diff --git a/src/type.h b/src/type.h
index 2a7597ae..fb51e013 100644
--- a/src/type.h
+++ b/src/type.h
@@ -36,6 +36,8 @@ class Type {
F32 = -0x03, // 0x7d
F64 = -0x04, // 0x7c
V128 = -0x05, // 0x7b
+ I8 = -0x06, // 0x7a : packed-type only, used in gc and as v128 lane
+ I16 = -0x07, // 0x79 : packed-type only, used in gc and as v128 lane
Funcref = -0x10, // 0x70
Anyref = -0x11, // 0x6f
Nullref = -0x12, // 0x6e
@@ -48,9 +50,7 @@ class Type {
Any = 0, // Not actually specified, but useful for type-checking
Hostref = 2, // Not actually specified, but used in testing and type-checking
- I8 = 3, // Not actually specified, but used internally with load/store
I8U = 4, // Not actually specified, but used internally with load/store
- I16 = 5, // Not actually specified, but used internally with load/store
I16U = 6, // Not actually specified, but used internally with load/store
I32U = 7, // Not actually specified, but used internally with load/store
};
@@ -78,6 +78,8 @@ class Type {
case Type::F32: return "f32";
case Type::F64: return "f64";
case Type::V128: return "v128";
+ case Type::I8: return "i8";
+ case Type::I16: return "i16";
case Type::Funcref: return "funcref";
case Type::Func: return "func";
case Type::Exnref: return "exnref";
diff --git a/src/wast-parser.cc b/src/wast-parser.cc
index 408dfd93..268a38d8 100644
--- a/src/wast-parser.cc
+++ b/src/wast-parser.cc
@@ -2023,8 +2023,32 @@ Result WastParser::ParsePlainInstr(std::unique_ptr<Expr>* out_expr) {
case TokenType::SimdLaneOp: {
Token token = Consume();
ErrorUnlessOpcodeEnabled(token);
+ if (!PeekMatch(TokenType::Nat) && !PeekMatch(TokenType::Int)) {
+ return ErrorExpected({"a natural number"}, "123");
+ }
+
+ Literal literal = Consume().literal();
uint64_t lane_idx;
- CHECK_RESULT(ParseNat(&lane_idx));
+
+ // TODO: The simd tests currently allow a lane number with an optional +,
+ // but probably shouldn't. See
+ // https://github.com/WebAssembly/simd/issues/181#issuecomment-597386919
+ Result result = ParseInt64(literal.text.begin(), literal.text.end(),
+ &lane_idx, ParseIntType::SignedAndUnsigned);
+
+ if (Failed(result)) {
+ Error(loc, "invalid literal \"" PRIstringview "\"",
+ WABT_PRINTF_STRING_VIEW_ARG(literal.text));
+ return Result::Error;
+ }
+
+ // TODO: Should share lane validation logic w/ SimdShuffleOp below. (See
+ // comment below for explanation of 255 vs. 32)
+ if (lane_idx > 255) {
+ Error(loc, "lane index \"" PRIstringview "\" out-of-range [0, 32)",
+ WABT_PRINTF_STRING_VIEW_ARG(literal.text));
+ return Result::Error;
+ }
out_expr->reset(new SimdLaneOpExpr(token.opcode(), lane_idx, loc));
break;
}
@@ -2056,8 +2080,11 @@ Result WastParser::ParsePlainInstr(std::unique_ptr<Expr>* out_expr) {
return Result::Error;
}
- if (value > 31) {
- Error(loc, "shuffle index \"" PRIstringview "\" out-of-range [0, 32)",
+ // The valid range is only [0, 32), but it's only malformed if it can't
+ // fit in a byte.
+ if (value > 255) {
+ Error(loc,
+ "shuffle index \"" PRIstringview "\" out-of-range [0, 32)",
WABT_PRINTF_STRING_VIEW_ARG(literal.text));
return Result::Error;
}
@@ -2080,7 +2107,9 @@ Result WastParser::ParsePlainInstr(std::unique_ptr<Expr>* out_expr) {
return Result::Ok;
}
-Result WastParser::ParseSimdV128Const(Const* const_, TokenType token_type) {
+Result WastParser::ParseSimdV128Const(Const* const_,
+ TokenType token_type,
+ ConstType const_type) {
WABT_TRACE(ParseSimdV128Const);
uint8_t lane_count = 0;
@@ -2105,77 +2134,94 @@ Result WastParser::ParseSimdV128Const(Const* const_, TokenType token_type) {
}
Consume();
- uint8_t lane_size = sizeof(v128) / lane_count;
-
- // The bytes of the v128 are written here first:
- std::array<char, 16> v128_bytes{};
const_->loc = GetLocation();
- for (int i = 0; i < lane_count; ++i) {
+ for (int lane = 0; lane < lane_count; ++lane) {
Location loc = GetLocation();
// Check that the lane literal type matches the element type of the v128:
- if (!PeekMatch(TokenType::Int) && !PeekMatch(TokenType::Nat)) {
- if (integer) {
- return ErrorExpected({"a Nat or Integer literal"}, "123");
- } else if (!PeekMatch(TokenType::Float)) {
- return ErrorExpected({"a Float literal"}, "42.0");
- }
- }
+ Token token = GetToken();
+ switch (token.token_type()) {
+ case TokenType::Nat:
+ case TokenType::Int:
+ // OK.
+ break;
- Literal literal = Consume().literal();
+ case TokenType::Float:
+ case TokenType::NanArithmetic:
+ case TokenType::NanCanonical:
+ if (integer) {
+ goto error;
+ }
+ break;
- string_view sv = literal.text;
- const char* s = sv.begin();
- const char* end = sv.end();
+ error:
+ default:
+ if (integer) {
+ return ErrorExpected({"a Nat or Integer literal"}, "123");
+ } else {
+ return ErrorExpected({"a Float literal"}, "42.0");
+ }
+ }
- // Pointer to the lane in the v128 bytes:
- char* lane_ptr = &v128_bytes[lane_size * i];
Result result;
// For each type, parse the next literal, bound check it, and write it to
// the array of bytes:
if (integer) {
- switch(lane_count) {
- case 16:
- result = ParseInt8(s, end, reinterpret_cast<uint8_t*>(lane_ptr),
- ParseIntType::SignedAndUnsigned);
+ string_view sv = Consume().literal().text;
+ const char* s = sv.begin();
+ const char* end = sv.end();
+
+ switch (lane_count) {
+ case 16: {
+ uint8_t value = 0;
+ result = ParseInt8(s, end, &value, ParseIntType::SignedAndUnsigned);
+ const_->set_v128_u8(lane, value);
break;
- case 8:
- result = ParseInt16(s, end, reinterpret_cast<uint16_t*>(lane_ptr),
- ParseIntType::SignedAndUnsigned);
+ }
+ case 8: {
+ uint16_t value = 0;
+ result = ParseInt16(s, end, &value, ParseIntType::SignedAndUnsigned);
+ const_->set_v128_u16(lane, value);
break;
- case 4:
- result = ParseInt32(s, end, reinterpret_cast<uint32_t*>(lane_ptr),
- ParseIntType::SignedAndUnsigned);
+ }
+ case 4: {
+ uint32_t value = 0;
+ result = ParseInt32(s, end, &value, ParseIntType::SignedAndUnsigned);
+ const_->set_v128_u32(lane, value);
break;
- case 2:
- result = ParseInt64(s, end, reinterpret_cast<uint64_t*>(lane_ptr),
- ParseIntType::SignedAndUnsigned);
+ }
+ case 2: {
+ uint64_t value = 0;
+ result = ParseInt64(s, end, &value, ParseIntType::SignedAndUnsigned);
+ const_->set_v128_u64(lane, value);
break;
+ }
}
} else {
- switch(lane_count) {
+ Const lane_const_;
+ switch (lane_count) {
case 4:
- result = ParseFloat(literal.type, s, end,
- reinterpret_cast<uint32_t*>(lane_ptr));
+ result = ParseF32(&lane_const_, const_type);
+ const_->set_v128_f32(lane, lane_const_.f32_bits());
break;
+
case 2:
- result = ParseDouble(literal.type, s, end,
- reinterpret_cast<uint64_t*>(lane_ptr));
+ result = ParseF64(&lane_const_, const_type);
+ const_->set_v128_f64(lane, lane_const_.f64_bits());
break;
}
+
+ const_->set_expected_nan(lane, lane_const_.expected_nan());
}
if (Failed(result)) {
- Error(loc, "invalid literal \"" PRIstringview "\"",
- WABT_PRINTF_STRING_VIEW_ARG(literal.text));
+ Error(loc, "invalid literal \"%s\"", token.to_string().c_str());
return Result::Error;
}
}
- const_->set_vec128(Bitcast<v128>(v128_bytes));
-
return Result::Ok;
}
@@ -2196,83 +2242,91 @@ Result WastParser::ParseExpectedNan(ExpectedNan* expected) {
return Result::Ok;
}
-Result WastParser::ParseConst(Const* const_, ConstType type) {
+Result WastParser::ParseF32(Const* const_, ConstType const_type) {
+ ExpectedNan expected;
+ if (const_type == ConstType::Expectation &&
+ Succeeded(ParseExpectedNan(&expected))) {
+ const_->set_f32(expected);
+ return Result::Ok;
+ }
+ auto literal = Consume().literal();
+ uint32_t f32_bits;
+ Result result = ParseFloat(literal.type, literal.text.begin(),
+ literal.text.end(), &f32_bits);
+ const_->set_f32(f32_bits);
+ return result;
+}
+
+Result WastParser::ParseF64(Const* const_, ConstType const_type) {
+ ExpectedNan expected;
+ if (const_type == ConstType::Expectation &&
+ Succeeded(ParseExpectedNan(&expected))) {
+ const_->set_f64(expected);
+ return Result::Ok;
+ }
+ auto literal = Consume().literal();
+ uint64_t f64_bits;
+ Result result = ParseDouble(literal.type, literal.text.begin(),
+ literal.text.end(), &f64_bits);
+ const_->set_f64(f64_bits);
+ return result;
+}
+
+Result WastParser::ParseConst(Const* const_, ConstType const_type) {
WABT_TRACE(ParseConst);
- Token token = Consume();
- Opcode opcode = token.opcode();
- Literal literal;
- string_view sv;
- const char* s;
- const char* end;
+ Token opcode_token = Consume();
+ Opcode opcode = opcode_token.opcode();
const_->loc = GetLocation();
- TokenType token_type = Peek();
+ Token token = GetToken();
// V128 is fully handled by ParseSimdV128Const:
if (opcode != Opcode::V128Const) {
- switch (token_type) {
- case TokenType::Nat:
- case TokenType::Int:
- case TokenType::Float: {
- literal = Consume().literal();
- sv = literal.text;
- s = sv.begin();
- end = sv.end();
- break;
- }
- case TokenType::NanArithmetic:
- case TokenType::NanCanonical:
- break;
- default:
- return ErrorExpected({"a numeric literal"}, "123, -45, 6.7e8");
+ switch (token.token_type()) {
+ case TokenType::Nat:
+ case TokenType::Int:
+ case TokenType::Float:
+ // OK.
+ break;
+ case TokenType::NanArithmetic:
+ case TokenType::NanCanonical:
+ break;
+ default:
+ return ErrorExpected({"a numeric literal"}, "123, -45, 6.7e8");
}
}
Result result;
switch (opcode) {
case Opcode::I32Const: {
+ auto sv = Consume().literal().text;
uint32_t u32;
- result = ParseInt32(s, end, &u32, ParseIntType::SignedAndUnsigned);
+ result = ParseInt32(sv.begin(), sv.end(), &u32,
+ ParseIntType::SignedAndUnsigned);
const_->set_u32(u32);
break;
}
case Opcode::I64Const: {
+ auto sv = Consume().literal().text;
uint64_t u64;
- result = ParseInt64(s, end, &u64, ParseIntType::SignedAndUnsigned);
+ result = ParseInt64(sv.begin(), sv.end(), &u64,
+ ParseIntType::SignedAndUnsigned);
const_->set_u64(u64);
break;
}
- case Opcode::F32Const: {
- ExpectedNan expected;
- if (type == ConstType::Expectation &&
- Succeeded(ParseExpectedNan(&expected))) {
- const_->set_f32(expected);
- break;
- }
- uint32_t f32_bits;
- result = ParseFloat(literal.type, s, end, &f32_bits);
- const_->set_f32(f32_bits);
+ case Opcode::F32Const:
+ result = ParseF32(const_, const_type);
break;
- }
- case Opcode::F64Const: {
- ExpectedNan expected;
- if (type == ConstType::Expectation &&
- Succeeded(ParseExpectedNan(&expected))) {
- const_->set_f64(expected);
- break;
- }
- uint64_t f64_bits;
- result = ParseDouble(literal.type, s, end, &f64_bits);
- const_->set_f64(f64_bits);
+ case Opcode::F64Const:
+ result = ParseF64(const_, const_type);
break;
- }
case Opcode::V128Const:
- ErrorUnlessOpcodeEnabled(token);
+ ErrorUnlessOpcodeEnabled(opcode_token);
// Parse V128 Simd Const (16 bytes).
- result = ParseSimdV128Const(const_, token_type);
+ result = ParseSimdV128Const(const_, token.token_type(), const_type);
// ParseSimdV128Const report error already, just return here if parser get
// errors.
if (Failed(result)) {
@@ -2286,8 +2340,7 @@ Result WastParser::ParseConst(Const* const_, ConstType type) {
}
if (Failed(result)) {
- Error(const_->loc, "invalid literal \"" PRIstringview "\"",
- WABT_PRINTF_STRING_VIEW_ARG(literal.text));
+ Error(const_->loc, "invalid literal \"%s\"", token.to_string().c_str());
// Return if parser get errors.
return Result::Error;
}
diff --git a/src/wast-parser.h b/src/wast-parser.h
index 7ddaf3b8..7dd4d038 100644
--- a/src/wast-parser.h
+++ b/src/wast-parser.h
@@ -169,6 +169,8 @@ class WastParser {
Result ParseTerminatingInstrList(ExprList*);
Result ParseInstr(ExprList*);
Result ParsePlainInstr(std::unique_ptr<Expr>*);
+ Result ParseF32(Const*, ConstType type);
+ Result ParseF64(Const*, ConstType type);
Result ParseConst(Const*, ConstType type);
Result ParseHostRef(Const*);
Result ParseExpectedNan(ExpectedNan* expected);
@@ -214,7 +216,7 @@ class WastParser {
template <typename T>
Result ParseAssertScriptModuleCommand(TokenType, CommandPtr*);
- Result ParseSimdV128Const(Const*, TokenType);
+ Result ParseSimdV128Const(Const*, TokenType, ConstType);
void CheckImportOrdering(Module*);