summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
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*);