diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/binary-writer-spec.cc | 110 | ||||
-rw-r--r-- | src/common.h | 1 | ||||
-rw-r--r-- | src/interp/interp-math.h | 22 | ||||
-rw-r--r-- | src/interp/interp-util.cc | 8 | ||||
-rw-r--r-- | src/interp/interp.cc | 6 | ||||
-rw-r--r-- | src/ir.h | 62 | ||||
-rw-r--r-- | src/tools/spectest-interp.cc | 705 | ||||
-rw-r--r-- | src/type.h | 6 | ||||
-rw-r--r-- | src/wast-parser.cc | 243 | ||||
-rw-r--r-- | src/wast-parser.h | 4 |
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>); @@ -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; @@ -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*); |