diff --git a/scripts/gen-s-parser.py b/scripts/gen-s-parser.py index 9c39acb48cf..d15c07e8eca 100755 --- a/scripts/gen-s-parser.py +++ b/scripts/gen-s-parser.py @@ -540,6 +540,10 @@ ("i32x4.trunc_sat_f64x2_u_zero", "makeUnary(UnaryOp::TruncSatZeroUVecF64x2ToVecI32x4)"), ("f32x4.demote_f64x2_zero", "makeUnary(UnaryOp::DemoteZeroVecF64x2ToVecF32x4)"), ("f64x2.promote_low_f32x4", "makeUnary(UnaryOp::PromoteLowVecF32x4ToVecF64x2)"), + ("i16x8.trunc_sat_f16x8_s", "makeUnary(UnaryOp::TruncSatSVecF16x8ToVecI16x8)"), + ("i16x8.trunc_sat_f16x8_u", "makeUnary(UnaryOp::TruncSatUVecF16x8ToVecI16x8)"), + ("f16x8.convert_i16x8_s", "makeUnary(UnaryOp::ConvertSVecI16x8ToVecF16x8)"), + ("f16x8.convert_i16x8_u", "makeUnary(UnaryOp::ConvertUVecI16x8ToVecF16x8)"), # relaxed SIMD ops ("i8x16.relaxed_swizzle", "makeBinary(BinaryOp::RelaxedSwizzleVecI8x16)"), diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc index f7d12a1beb8..75fda4f7a6a 100644 --- a/src/gen-s-parser.inc +++ b/src/gen-s-parser.inc @@ -326,12 +326,34 @@ switch (buf[0]) { default: goto parse_error; } } - case 'c': - if (op == "f16x8.ceil"sv) { - CHECK_ERR(makeUnary(ctx, pos, annotations, UnaryOp::CeilVecF16x8)); - return Ok{}; + case 'c': { + switch (buf[7]) { + case 'e': + if (op == "f16x8.ceil"sv) { + CHECK_ERR(makeUnary(ctx, pos, annotations, UnaryOp::CeilVecF16x8)); + return Ok{}; + } + goto parse_error; + case 'o': { + switch (buf[20]) { + case 's': + if (op == "f16x8.convert_i16x8_s"sv) { + CHECK_ERR(makeUnary(ctx, pos, annotations, UnaryOp::ConvertSVecI16x8ToVecF16x8)); + return Ok{}; + } + goto parse_error; + case 'u': + if (op == "f16x8.convert_i16x8_u"sv) { + CHECK_ERR(makeUnary(ctx, pos, annotations, UnaryOp::ConvertUVecI16x8ToVecF16x8)); + return Ok{}; + } + goto parse_error; + default: goto parse_error; + } + } + default: goto parse_error; } - goto parse_error; + } case 'd': if (op == "f16x8.div"sv) { CHECK_ERR(makeBinary(ctx, pos, annotations, BinaryOp::DivVecF16x8)); @@ -2038,6 +2060,23 @@ switch (buf[0]) { default: goto parse_error; } } + case 't': { + switch (buf[22]) { + case 's': + if (op == "i16x8.trunc_sat_f16x8_s"sv) { + CHECK_ERR(makeUnary(ctx, pos, annotations, UnaryOp::TruncSatSVecF16x8ToVecI16x8)); + return Ok{}; + } + goto parse_error; + case 'u': + if (op == "i16x8.trunc_sat_f16x8_u"sv) { + CHECK_ERR(makeUnary(ctx, pos, annotations, UnaryOp::TruncSatUVecF16x8ToVecI16x8)); + return Ok{}; + } + goto parse_error; + default: goto parse_error; + } + } default: goto parse_error; } } diff --git a/src/ir/child-typer.h b/src/ir/child-typer.h index 638bb9c33ae..499a7e4ddfc 100644 --- a/src/ir/child-typer.h +++ b/src/ir/child-typer.h @@ -423,6 +423,10 @@ template struct ChildTyper : OverriddenVisitor { case RelaxedTruncUVecF32x4ToVecI32x4: case RelaxedTruncZeroSVecF64x2ToVecI32x4: case RelaxedTruncZeroUVecF64x2ToVecI32x4: + case TruncSatSVecF16x8ToVecI16x8: + case TruncSatUVecF16x8ToVecI16x8: + case ConvertSVecI16x8ToVecF16x8: + case ConvertUVecI16x8ToVecF16x8: case AnyTrueVec128: case AllTrueVecI8x16: case AllTrueVecI16x8: diff --git a/src/ir/cost.h b/src/ir/cost.h index d11a9bfac07..fcee6c18edb 100644 --- a/src/ir/cost.h +++ b/src/ir/cost.h @@ -257,6 +257,10 @@ struct CostAnalyzer : public OverriddenVisitor { case RelaxedTruncUVecF32x4ToVecI32x4: case RelaxedTruncZeroSVecF64x2ToVecI32x4: case RelaxedTruncZeroUVecF64x2ToVecI32x4: + case TruncSatSVecF16x8ToVecI16x8: + case TruncSatUVecF16x8ToVecI16x8: + case ConvertSVecI16x8ToVecF16x8: + case ConvertUVecI16x8ToVecF16x8: ret = 1; break; case InvalidUnary: diff --git a/src/literal.h b/src/literal.h index 6aa348084aa..50666083eb4 100644 --- a/src/literal.h +++ b/src/literal.h @@ -377,14 +377,18 @@ class Literal { Literal extendS32() const; Literal wrapToI32() const; + Literal convertSIToF16() const; + Literal convertUIToF16() const; Literal convertSIToF32() const; Literal convertUIToF32() const; Literal convertSIToF64() const; Literal convertUIToF64() const; Literal convertF32ToF16() const; + Literal truncSatToSI16() const; Literal truncSatToSI32() const; Literal truncSatToSI64() const; + Literal truncSatToUI16() const; Literal truncSatToUI32() const; Literal truncSatToUI64() const; @@ -693,6 +697,10 @@ class Literal { Literal truncSatZeroUToI32x4() const; Literal demoteZeroToF32x4() const; Literal promoteLowToF64x2() const; + Literal truncSatToSI16x8() const; + Literal truncSatToUI16x8() const; + Literal convertSToF16x8() const; + Literal convertUToF16x8() const; Literal swizzleI8x16(const Literal& other) const; Literal relaxedMaddF16x8(const Literal& left, const Literal& right) const; Literal relaxedNmaddF16x8(const Literal& left, const Literal& right) const; diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 427bff32905..8549342042a 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -1368,6 +1368,18 @@ struct PrintExpressionContents case RelaxedTruncZeroUVecF64x2ToVecI32x4: o << "i32x4.relaxed_trunc_f64x2_u_zero"; break; + case TruncSatSVecF16x8ToVecI16x8: + o << "i16x8.trunc_sat_f16x8_s"; + break; + case TruncSatUVecF16x8ToVecI16x8: + o << "i16x8.trunc_sat_f16x8_u"; + break; + case ConvertSVecI16x8ToVecF16x8: + o << "f16x8.convert_i16x8_s"; + break; + case ConvertUVecI16x8ToVecF16x8: + o << "f16x8.convert_i16x8_u"; + break; case InvalidUnary: WASM_UNREACHABLE("unvalid unary operator"); } diff --git a/src/support/safe_integer.cpp b/src/support/safe_integer.cpp index 3a50b50eaa2..86ba2547a53 100644 --- a/src/support/safe_integer.cpp +++ b/src/support/safe_integer.cpp @@ -98,6 +98,11 @@ int64_t wasm::toSInteger64(double x) { * 1 11111111 111...11 => 0xffffffff => -nan(0x7fffff) */ +bool wasm::isInRangeI16TruncS(int32_t i) { + uint32_t u = i; + return (u < 0x47000000U) || (u >= 0x80000000U && u <= 0xc7000000U); +} + bool wasm::isInRangeI32TruncS(int32_t i) { uint32_t u = i; return (u < 0x4f000000U) || (u >= 0x80000000U && u <= 0xcf000000U); @@ -108,6 +113,11 @@ bool wasm::isInRangeI64TruncS(int32_t i) { return (u < 0x5f000000U) || (u >= 0x80000000U && u <= 0xdf000000U); } +bool wasm::isInRangeI16TruncU(int32_t i) { + uint32_t u = i; + return (u < 0x47800000) || (u >= 0x80000000U && u < 0xbf800000U); +} + bool wasm::isInRangeI32TruncU(int32_t i) { uint32_t u = i; return (u < 0x4f800000U) || (u >= 0x80000000U && u < 0xbf800000U); diff --git a/src/support/safe_integer.h b/src/support/safe_integer.h index 031c6c32365..6888ab25f3f 100644 --- a/src/support/safe_integer.h +++ b/src/support/safe_integer.h @@ -32,8 +32,10 @@ uint64_t toUInteger64(double x); int64_t toSInteger64(double x); // The isInRange* functions all expect to be passed the binary representation // of a float or double. +bool isInRangeI16TruncS(int32_t i); bool isInRangeI32TruncS(int32_t i); bool isInRangeI64TruncS(int32_t i); +bool isInRangeI16TruncU(int32_t i); bool isInRangeI32TruncU(int32_t i); bool isInRangeI64TruncU(int32_t i); bool isInRangeI32TruncS(int64_t i); diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index a66fa6772d0..a8b8f7855ef 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -3143,7 +3143,11 @@ Expression* TranslateToFuzzReader::makeUnary(Type type) { CeilVecF16x8, FloorVecF16x8, TruncVecF16x8, - NearestVecF16x8)), + NearestVecF16x8, + TruncSatSVecF16x8ToVecI16x8, + TruncSatUVecF16x8ToVecI16x8, + ConvertSVecI16x8ToVecF16x8, + ConvertUVecI16x8ToVecF16x8)), make(Type::v128)}); } WASM_UNREACHABLE("invalid value"); diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 5021b6a297f..35f13195260 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -1039,6 +1039,10 @@ enum ASTNodes { F16x8Max = 0x142, F16x8Pmin = 0x143, F16x8Pmax = 0x144, + I16x8TruncSatF16x8S = 0x145, + I16x8TruncSatF16x8U = 0x146, + F16x8ConvertI16x8S = 0x147, + F16x8ConvertI16x8U = 0x148, // bulk memory opcodes diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 25303abfe45..9659a5c3495 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -632,6 +632,14 @@ class ExpressionRunner : public OverriddenVisitor { return value.demoteZeroToF32x4(); case PromoteLowVecF32x4ToVecF64x2: return value.promoteLowToF64x2(); + case TruncSatSVecF16x8ToVecI16x8: + return value.truncSatToSI16x8(); + case TruncSatUVecF16x8ToVecI16x8: + return value.truncSatToUI16x8(); + case ConvertSVecI16x8ToVecF16x8: + return value.convertSToF16x8(); + case ConvertUVecI16x8ToVecF16x8: + return value.convertUToF16x8(); case InvalidUnary: WASM_UNREACHABLE("invalid unary op"); } diff --git a/src/wasm.h b/src/wasm.h index a86f7701371..e54d628bd4c 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -238,6 +238,10 @@ enum UnaryOp { // Half precision SIMD SplatVecF16x8, + TruncSatSVecF16x8ToVecI16x8, + TruncSatUVecF16x8ToVecI16x8, + ConvertSVecI16x8ToVecF16x8, + ConvertUVecI16x8ToVecF16x8, InvalidUnary }; diff --git a/src/wasm/literal.cpp b/src/wasm/literal.cpp index 6aaba729ae2..b53378cfa44 100644 --- a/src/wasm/literal.cpp +++ b/src/wasm/literal.cpp @@ -798,6 +798,20 @@ Literal Literal::wrapToI32() const { return Literal((int32_t)i64); } +Literal Literal::convertSIToF16() const { + if (type == Type::i32) { + return Literal(fp16_ieee_from_fp32_value(float(i32))); + } + WASM_UNREACHABLE("invalid type"); +} + +Literal Literal::convertUIToF16() const { + if (type == Type::i32) { + return Literal(fp16_ieee_from_fp32_value(float(uint16_t(i32)))); + } + WASM_UNREACHABLE("invalid type"); +} + Literal Literal::convertSIToF32() const { if (type == Type::i32) { return Literal(float(i32)); @@ -861,6 +875,14 @@ static Literal saturating_trunc(typename AsInt::type val) { return Literal(I(std::trunc(bit_cast(val)))); } +Literal Literal::truncSatToSI16() const { + if (type == Type::f32) { + return saturating_trunc( + Literal(*this).castToI32().geti32()); + } + WASM_UNREACHABLE("invalid type"); +} + Literal Literal::truncSatToSI32() const { if (type == Type::f32) { return saturating_trunc( @@ -885,6 +907,14 @@ Literal Literal::truncSatToSI64() const { WASM_UNREACHABLE("invalid type"); } +Literal Literal::truncSatToUI16() const { + if (type == Type::f32) { + return saturating_trunc( + Literal(*this).castToI32().geti32()); + } + WASM_UNREACHABLE("invalid type"); +} + Literal Literal::truncSatToUI32() const { if (type == Type::f32) { return saturating_trunc( @@ -1997,6 +2027,19 @@ Literal Literal::convertUToF32x4() const { return unary<4, &Literal::getLanesI32x4, &Literal::convertUIToF32>(*this); } +Literal Literal::truncSatToSI16x8() const { + return unary<8, &Literal::getLanesF16x8, &Literal::truncSatToSI16>(*this); +} +Literal Literal::truncSatToUI16x8() const { + return unary<8, &Literal::getLanesF16x8, &Literal::truncSatToUI16>(*this); +} +Literal Literal::convertSToF16x8() const { + return unary<8, &Literal::getLanesSI16x8, &Literal::convertSIToF16>(*this); +} +Literal Literal::convertUToF16x8() const { + return unary<8, &Literal::getLanesSI16x8, &Literal::convertUIToF16>(*this); +} + Literal Literal::anyTrueV128() const { auto lanes = getLanesI32x4(); for (size_t i = 0; i < 4; ++i) { diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index cb9ea3731ff..3bb33529bdd 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -6522,6 +6522,22 @@ bool WasmBinaryReader::maybeVisitSIMDUnary(Expression*& out, uint32_t code) { curr = allocator.alloc(); curr->op = RelaxedTruncZeroUVecF64x2ToVecI32x4; break; + case BinaryConsts::I16x8TruncSatF16x8S: + curr = allocator.alloc(); + curr->op = TruncSatSVecF16x8ToVecI16x8; + break; + case BinaryConsts::I16x8TruncSatF16x8U: + curr = allocator.alloc(); + curr->op = TruncSatUVecF16x8ToVecI16x8; + break; + case BinaryConsts::F16x8ConvertI16x8S: + curr = allocator.alloc(); + curr->op = ConvertSVecI16x8ToVecF16x8; + break; + case BinaryConsts::F16x8ConvertI16x8U: + curr = allocator.alloc(); + curr->op = ConvertUVecI16x8ToVecF16x8; + break; default: return false; } diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index a1446c2de9e..7194229fea0 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -1324,6 +1324,22 @@ void BinaryInstWriter::visitUnary(Unary* curr) { o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4RelaxedTruncF64x2UZero); break; + case TruncSatSVecF16x8ToVecI16x8: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::I16x8TruncSatF16x8S); + break; + case TruncSatUVecF16x8ToVecI16x8: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::I16x8TruncSatF16x8U); + break; + case ConvertSVecI16x8ToVecF16x8: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::F16x8ConvertI16x8S); + break; + case ConvertUVecI16x8ToVecF16x8: + o << int8_t(BinaryConsts::SIMDPrefix) + << U32LEB(BinaryConsts::F16x8ConvertI16x8U); + break; case InvalidUnary: WASM_UNREACHABLE("invalid unary op"); } diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 40726d7cd12..a86187fa788 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -2160,6 +2160,10 @@ void FunctionValidator::visitUnary(Unary* curr) { case RelaxedTruncUVecF32x4ToVecI32x4: case RelaxedTruncZeroSVecF64x2ToVecI32x4: case RelaxedTruncZeroUVecF64x2ToVecI32x4: + case TruncSatSVecF16x8ToVecI16x8: + case TruncSatUVecF16x8ToVecI16x8: + case ConvertSVecI16x8ToVecF16x8: + case ConvertUVecI16x8ToVecF16x8: shouldBeEqual(curr->type, Type(Type::v128), curr, "expected v128 type"); shouldBeEqual( curr->value->type, Type(Type::v128), curr, "expected v128 operand"); diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 87ae6ac5a38..84fd9a06ffc 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -703,6 +703,10 @@ void Unary::finalize() { case RelaxedTruncUVecF32x4ToVecI32x4: case RelaxedTruncZeroSVecF64x2ToVecI32x4: case RelaxedTruncZeroUVecF64x2ToVecI32x4: + case TruncSatSVecF16x8ToVecI16x8: + case TruncSatUVecF16x8ToVecI16x8: + case ConvertSVecI16x8ToVecF16x8: + case ConvertUVecI16x8ToVecF16x8: type = Type::v128; break; case AnyTrueVec128: diff --git a/test/lit/basic/f16.wast b/test/lit/basic/f16.wast index ba806bb5733..faf7006d4df 100644 --- a/test/lit/basic/f16.wast +++ b/test/lit/basic/f16.wast @@ -534,6 +534,69 @@ (local.get $2) ) ) + ;; CHECK-TEXT: (func $i16x8.trunc_sat_f16x8_s (type $1) (param $0 v128) (result v128) + ;; CHECK-TEXT-NEXT: (i16x8.trunc_sat_f16x8_s + ;; CHECK-TEXT-NEXT: (local.get $0) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $i16x8.trunc_sat_f16x8_s (type $1) (param $0 v128) (result v128) + ;; CHECK-BIN-NEXT: (i16x8.trunc_sat_f16x8_s + ;; CHECK-BIN-NEXT: (local.get $0) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + (func $i16x8.trunc_sat_f16x8_s (param $0 v128) (result v128) + (i16x8.trunc_sat_f16x8_s + (local.get $0) + ) + ) + + ;; CHECK-TEXT: (func $i16x8.trunc_sat_f16x8_u (type $1) (param $0 v128) (result v128) + ;; CHECK-TEXT-NEXT: (i16x8.trunc_sat_f16x8_u + ;; CHECK-TEXT-NEXT: (local.get $0) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $i16x8.trunc_sat_f16x8_u (type $1) (param $0 v128) (result v128) + ;; CHECK-BIN-NEXT: (i16x8.trunc_sat_f16x8_u + ;; CHECK-BIN-NEXT: (local.get $0) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + (func $i16x8.trunc_sat_f16x8_u (param $0 v128) (result v128) + (i16x8.trunc_sat_f16x8_u + (local.get $0) + ) + ) + + ;; CHECK-TEXT: (func $f16x8.convert_i16x8_s (type $1) (param $0 v128) (result v128) + ;; CHECK-TEXT-NEXT: (f16x8.convert_i16x8_s + ;; CHECK-TEXT-NEXT: (local.get $0) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $f16x8.convert_i16x8_s (type $1) (param $0 v128) (result v128) + ;; CHECK-BIN-NEXT: (f16x8.convert_i16x8_s + ;; CHECK-BIN-NEXT: (local.get $0) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + (func $f16x8.convert_i16x8_s (param $0 v128) (result v128) + (f16x8.convert_i16x8_s + (local.get $0) + ) + ) + + ;; CHECK-TEXT: (func $f16x8.convert_i16x8_u (type $1) (param $0 v128) (result v128) + ;; CHECK-TEXT-NEXT: (f16x8.convert_i16x8_u + ;; CHECK-TEXT-NEXT: (local.get $0) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $f16x8.convert_i16x8_u (type $1) (param $0 v128) (result v128) + ;; CHECK-BIN-NEXT: (f16x8.convert_i16x8_u + ;; CHECK-BIN-NEXT: (local.get $0) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + (func $f16x8.convert_i16x8_u (param $0 v128) (result v128) + (f16x8.convert_i16x8_u + (local.get $0) + ) + ) ) ;; CHECK-BIN-NODEBUG: (type $0 (func (param v128 v128) (result v128))) @@ -740,3 +803,27 @@ ;; CHECK-BIN-NODEBUG-NEXT: (local.get $2) ;; CHECK-BIN-NODEBUG-NEXT: ) ;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $28 (type $1) (param $0 v128) (result v128) +;; CHECK-BIN-NODEBUG-NEXT: (i16x8.trunc_sat_f16x8_s +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $29 (type $1) (param $0 v128) (result v128) +;; CHECK-BIN-NODEBUG-NEXT: (i16x8.trunc_sat_f16x8_u +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $30 (type $1) (param $0 v128) (result v128) +;; CHECK-BIN-NODEBUG-NEXT: (f16x8.convert_i16x8_s +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $31 (type $1) (param $0 v128) (result v128) +;; CHECK-BIN-NODEBUG-NEXT: (f16x8.convert_i16x8_u +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) diff --git a/test/spec/f16.wast b/test/spec/f16.wast index f0d2b067827..b495fe2b64e 100644 --- a/test/spec/f16.wast +++ b/test/spec/f16.wast @@ -34,6 +34,10 @@ (func (export "f16x8.nearest") (param $0 v128) (result v128) (f16x8.nearest (local.get $0))) (func (export "f16x8.relaxed_madd") (param $0 v128) (param $1 v128) (param $2 v128) (result v128) (f16x8.relaxed_madd (local.get $0) (local.get $1) (local.get $2))) (func (export "f16x8.relaxed_nmadd") (param $0 v128) (param $1 v128) (param $2 v128) (result v128) (f16x8.relaxed_nmadd (local.get $0) (local.get $1) (local.get $2))) + (func (export "i16x8.trunc_sat_f16x8_s") (param $0 v128) (result v128) (i16x8.trunc_sat_f16x8_s (local.get $0))) + (func (export "i16x8.trunc_sat_f16x8_u") (param $0 v128) (result v128) (i16x8.trunc_sat_f16x8_u (local.get $0))) + (func (export "f16x8.convert_i16x8_s") (param $0 v128) (result v128) (f16x8.convert_i16x8_s (local.get $0))) + (func (export "f16x8.convert_i16x8_u") (param $0 v128) (result v128) (f16x8.convert_i16x8_u (local.get $0))) ;; Multiple operation tests: (func (export "splat_replace") (result v128) (f16x8.replace_lane 0 (f16x8.splat (f32.const 1)) (f32.const 99)) ) @@ -223,3 +227,23 @@ (assert_return (invoke "splat_replace") (v128.const i16x8 0x5630 0x3c00 0x3c00 0x3c00 0x3c00 0x3c00 0x3c00 0x3c00) ) + +;; conversions +(assert_return (invoke "i16x8.trunc_sat_f16x8_s" + ;; 42 nan inf -inf 65504 -65504 0 0 + (v128.const i16x8 0x5140 0x7e00 0x7c00 0xfc00 0x7bff 0xfbff 0 0)) + (v128.const i16x8 42 0 32767 -32768 32767 -32768 0 0)) +(assert_return (invoke "i16x8.trunc_sat_f16x8_u" + ;; 42 nan inf -inf 65504 -65504 0 0 + (v128.const i16x8 0x5140 0x7e00 0x7c00 0xfc00 0x7bff 0xfbff 0 0)) + (v128.const i16x8 42 0 65535 0 65504 0 0 0)) +(assert_return (invoke "f16x8.convert_i16x8_s" + ;; + (v128.const i16x8 0 1 -1 32767 -32768 0 0 0)) + ;; 0 1 -1 32767 -32768 0 0 0 + (v128.const i16x8 0 0x3c00 0xbc00 0x7800 0xf800 0 0 0)) +(assert_return (invoke "f16x8.convert_i16x8_u" + ;; Unlike f32/64, f16 can't represent the full 2^16 integer range so 2^16 becomes infinity. + (v128.const i16x8 0 1 -1 -32 0 0 0 0)) + ;; 1 inf 65504 + (v128.const i16x8 0 0x3c00 0x7c00 0x7bff 0 0 0 0))