diff options
Diffstat (limited to 'src/wasm')
-rw-r--r-- | src/wasm/literal.cpp | 87 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 26 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 101 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 8 |
4 files changed, 193 insertions, 29 deletions
diff --git a/src/wasm/literal.cpp b/src/wasm/literal.cpp index 7b248010e..ece92c6ee 100644 --- a/src/wasm/literal.cpp +++ b/src/wasm/literal.cpp @@ -22,6 +22,7 @@ #include "emscripten-optimizer/simple_ast.h" #include "pretty_printing.h" #include "support/bits.h" +#include "support/utilities.h" #include "ir/bits.h" @@ -232,30 +233,108 @@ Literal Literal::truncateToF32() const { return Literal(float(getf64())); } -Literal Literal::convertSToF32() const { +Literal Literal::truncSIToF32() const { if (type == Type::i32) return Literal(float(i32)); if (type == Type::i64) return Literal(float(i64)); WASM_UNREACHABLE(); } -Literal Literal::convertUToF32() const { +Literal Literal::truncUIToF32() const { if (type == Type::i32) return Literal(float(uint32_t(i32))); if (type == Type::i64) return Literal(float(uint64_t(i64))); WASM_UNREACHABLE(); } -Literal Literal::convertSToF64() const { +Literal Literal::truncSIToF64() const { if (type == Type::i32) return Literal(double(i32)); if (type == Type::i64) return Literal(double(i64)); WASM_UNREACHABLE(); } -Literal Literal::convertUToF64() const { +Literal Literal::truncUIToF64() const { if (type == Type::i32) return Literal(double(uint32_t(i32))); if (type == Type::i64) return Literal(double(uint64_t(i64))); WASM_UNREACHABLE(); } +template<typename F> +struct AsInt { + using type = void; +}; +template<> struct AsInt<float> { using type = int32_t; }; +template<> struct AsInt<double> { using type = int64_t; }; + +template<typename F, typename I, bool (*RangeCheck)(typename AsInt<F>::type)> +static Literal saturating_trunc(typename AsInt<F>::type val) { + if (std::isnan(bit_cast<F>(val))) { + return Literal(I(0)); + } + if (!RangeCheck(val)) { + if (std::signbit(bit_cast<F>(val))) { + return Literal(std::numeric_limits<I>::min()); + } else { + return Literal(std::numeric_limits<I>::max()); + } + } + return Literal(I(std::trunc(bit_cast<F>(val)))); +} + +Literal Literal::truncSatToSI32() const { + if (type == Type::f32) { + return saturating_trunc<float, int32_t, isInRangeI32TruncS>( + Literal(*this).castToI32().geti32() + ); + } + if (type == Type::f64) { + return saturating_trunc<double, int32_t, isInRangeI32TruncS>( + Literal(*this).castToI64().geti64() + ); + } + WASM_UNREACHABLE(); +} + +Literal Literal::truncSatToSI64() const { + if (type == Type::f32) { + return saturating_trunc<float, int64_t, isInRangeI64TruncS>( + Literal(*this).castToI32().geti32() + ); + } + if (type == Type::f64) { + return saturating_trunc<double, int64_t, isInRangeI64TruncS>( + Literal(*this).castToI64().geti64() + ); + } + WASM_UNREACHABLE(); +} + +Literal Literal::truncSatToUI32() const { + if (type == Type::f32) { + return saturating_trunc<float, uint32_t, isInRangeI32TruncU>( + Literal(*this).castToI32().geti32() + ); + } + if (type == Type::f64) { + return saturating_trunc<double, uint32_t, isInRangeI32TruncU>( + Literal(*this).castToI64().geti64() + ); + } + WASM_UNREACHABLE(); +} + +Literal Literal::truncSatToUI64() const { + if (type == Type::f32) { + return saturating_trunc<float, uint64_t, isInRangeI64TruncU>( + Literal(*this).castToI32().geti32() + ); + } + if (type == Type::f64) { + return saturating_trunc<double, uint64_t, isInRangeI64TruncU>( + Literal(*this).castToI64().geti64() + ); + } + WASM_UNREACHABLE(); +} + Literal Literal::eqz() const { switch (type) { case Type::i32: return eq(Literal(int32_t(0))); diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index eebf0bd63..b36e629b0 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -1692,6 +1692,12 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { throwError("invalid code after atomic prefix: " + std::to_string(code)); break; } + case BinaryConsts::TruncSatPrefix: { + uint32_t code = getU32LEB(); + if (maybeVisitTruncSat(curr, code)) break; + throwError("invalid code after nontrapping float-to-int prefix: " + std::to_string(code)); + break; + } default: { // otherwise, the code is a subcode TODO: optimize if (maybeVisitBinary(curr, code)) break; @@ -2276,6 +2282,26 @@ bool WasmBinaryBuilder::maybeVisitUnary(Expression*& out, uint8_t code) { return true; } +bool WasmBinaryBuilder::maybeVisitTruncSat(Expression*& out, uint32_t code) { + Unary* curr; + switch (code) { + case BinaryConsts::I32STruncSatF32: curr = allocator.alloc<Unary>(); curr->op = TruncSatSFloat32ToInt32; break; + case BinaryConsts::I32UTruncSatF32: curr = allocator.alloc<Unary>(); curr->op = TruncSatUFloat32ToInt32; break; + case BinaryConsts::I32STruncSatF64: curr = allocator.alloc<Unary>(); curr->op = TruncSatSFloat64ToInt32; break; + case BinaryConsts::I32UTruncSatF64: curr = allocator.alloc<Unary>(); curr->op = TruncSatUFloat64ToInt32; break; + case BinaryConsts::I64STruncSatF32: curr = allocator.alloc<Unary>(); curr->op = TruncSatSFloat32ToInt64; break; + case BinaryConsts::I64UTruncSatF32: curr = allocator.alloc<Unary>(); curr->op = TruncSatUFloat32ToInt64; break; + case BinaryConsts::I64STruncSatF64: curr = allocator.alloc<Unary>(); curr->op = TruncSatSFloat64ToInt64; break; + case BinaryConsts::I64UTruncSatF64: curr = allocator.alloc<Unary>(); curr->op = TruncSatUFloat64ToInt64; break; + default: return false; + } + if (debug) std::cerr << "zz node: Unary (nontrapping float-to-int)" << std::endl; + curr->value = popNonVoidExpression(); + curr->finalize(); + out = curr; + return true; +} + bool WasmBinaryBuilder::maybeVisitBinary(Expression*& out, uint8_t code) { Binary* curr; #define INT_TYPED_CODE(code) { \ diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index fa239688f..3f65c9f7a 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -723,36 +723,87 @@ void FunctionValidator::visitUnary(Unary* curr) { case ExtendUInt32: case ExtendS8Int32: case ExtendS16Int32: { - shouldBeEqual(curr->value->type, i32, curr, "extend type must be correct"); break; + shouldBeEqual(curr->value->type, i32, curr, "extend type must be correct"); + break; } case ExtendS8Int64: case ExtendS16Int64: case ExtendS32Int64: { - shouldBeEqual(curr->value->type, i64, curr, "extend type must be correct"); break; + shouldBeEqual(curr->value->type, i64, curr, "extend type must be correct"); + break; + } + case WrapInt64: { + shouldBeEqual(curr->value->type, i64, curr, "wrap type must be correct"); + break; + } + case TruncSFloat32ToInt32: + case TruncSFloat32ToInt64: + case TruncUFloat32ToInt32: + case TruncUFloat32ToInt64: { + shouldBeEqual(curr->value->type, f32, curr, "trunc type must be correct"); + break; + } + case TruncSatSFloat32ToInt32: + case TruncSatSFloat32ToInt64: + case TruncSatUFloat32ToInt32: + case TruncSatUFloat32ToInt64: { + shouldBeTrue(info.features.hasTruncSat(), curr, "nontrapping float-to-int conversions are disabled"); + shouldBeEqual(curr->value->type, f32, curr, "trunc type must be correct"); + break; + } + case TruncSFloat64ToInt32: + case TruncSFloat64ToInt64: + case TruncUFloat64ToInt32: + case TruncUFloat64ToInt64: { + shouldBeEqual(curr->value->type, f64, curr, "trunc type must be correct"); + break; + } + case TruncSatSFloat64ToInt32: + case TruncSatSFloat64ToInt64: + case TruncSatUFloat64ToInt32: + case TruncSatUFloat64ToInt64: { + shouldBeTrue(info.features.hasTruncSat(), curr, "nontrapping float-to-int conversions are disabled"); + shouldBeEqual(curr->value->type, f64, curr, "trunc type must be correct"); + break; + } + case ReinterpretFloat32: { + shouldBeEqual(curr->value->type, f32, curr, "reinterpret/f32 type must be correct"); + break; + } + case ReinterpretFloat64: { + shouldBeEqual(curr->value->type, f64, curr, "reinterpret/f64 type must be correct"); + break; + } + case ConvertUInt32ToFloat32: + case ConvertUInt32ToFloat64: + case ConvertSInt32ToFloat32: + case ConvertSInt32ToFloat64: { + shouldBeEqual(curr->value->type, i32, curr, "convert type must be correct"); + break; + } + case ConvertUInt64ToFloat32: + case ConvertUInt64ToFloat64: + case ConvertSInt64ToFloat32: + case ConvertSInt64ToFloat64: { + shouldBeEqual(curr->value->type, i64, curr, "convert type must be correct"); + break; + } + case PromoteFloat32: { + shouldBeEqual(curr->value->type, f32, curr, "promote type must be correct"); + break; + } + case DemoteFloat64: { + shouldBeEqual(curr->value->type, f64, curr, "demote type must be correct"); + break; + } + case ReinterpretInt32: { + shouldBeEqual(curr->value->type, i32, curr, "reinterpret/i32 type must be correct"); + break; + } + case ReinterpretInt64: { + shouldBeEqual(curr->value->type, i64, curr, "reinterpret/i64 type must be correct"); + break; } - case WrapInt64: shouldBeEqual(curr->value->type, i64, curr, "wrap type must be correct"); break; - case TruncSFloat32ToInt32: shouldBeEqual(curr->value->type, f32, curr, "trunc type must be correct"); break; - case TruncSFloat32ToInt64: shouldBeEqual(curr->value->type, f32, curr, "trunc type must be correct"); break; - case TruncUFloat32ToInt32: shouldBeEqual(curr->value->type, f32, curr, "trunc type must be correct"); break; - case TruncUFloat32ToInt64: shouldBeEqual(curr->value->type, f32, curr, "trunc type must be correct"); break; - case TruncSFloat64ToInt32: shouldBeEqual(curr->value->type, f64, curr, "trunc type must be correct"); break; - case TruncSFloat64ToInt64: shouldBeEqual(curr->value->type, f64, curr, "trunc type must be correct"); break; - case TruncUFloat64ToInt32: shouldBeEqual(curr->value->type, f64, curr, "trunc type must be correct"); break; - case TruncUFloat64ToInt64: shouldBeEqual(curr->value->type, f64, curr, "trunc type must be correct"); break; - case ReinterpretFloat32: shouldBeEqual(curr->value->type, f32, curr, "reinterpret/f32 type must be correct"); break; - case ReinterpretFloat64: shouldBeEqual(curr->value->type, f64, curr, "reinterpret/f64 type must be correct"); break; - case ConvertUInt32ToFloat32: shouldBeEqual(curr->value->type, i32, curr, "convert type must be correct"); break; - case ConvertUInt32ToFloat64: shouldBeEqual(curr->value->type, i32, curr, "convert type must be correct"); break; - case ConvertSInt32ToFloat32: shouldBeEqual(curr->value->type, i32, curr, "convert type must be correct"); break; - case ConvertSInt32ToFloat64: shouldBeEqual(curr->value->type, i32, curr, "convert type must be correct"); break; - case ConvertUInt64ToFloat32: shouldBeEqual(curr->value->type, i64, curr, "convert type must be correct"); break; - case ConvertUInt64ToFloat64: shouldBeEqual(curr->value->type, i64, curr, "convert type must be correct"); break; - case ConvertSInt64ToFloat32: shouldBeEqual(curr->value->type, i64, curr, "convert type must be correct"); break; - case ConvertSInt64ToFloat64: shouldBeEqual(curr->value->type, i64, curr, "convert type must be correct"); break; - case PromoteFloat32: shouldBeEqual(curr->value->type, f32, curr, "promote type must be correct"); break; - case DemoteFloat64: shouldBeEqual(curr->value->type, f64, curr, "demote type must be correct"); break; - case ReinterpretInt32: shouldBeEqual(curr->value->type, i32, curr, "reinterpret/i32 type must be correct"); break; - case ReinterpretInt64: shouldBeEqual(curr->value->type, i64, curr, "reinterpret/i64 type must be correct"); break; case InvalidUnary: WASM_UNREACHABLE(); } } diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index e0a9cff93..a1d7666ca 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -467,11 +467,19 @@ void Unary::finalize() { case TruncUFloat32ToInt32: case TruncSFloat64ToInt32: case TruncUFloat64ToInt32: + case TruncSatSFloat32ToInt32: + case TruncSatUFloat32ToInt32: + case TruncSatSFloat64ToInt32: + case TruncSatUFloat64ToInt32: case ReinterpretFloat32: type = i32; break; case TruncSFloat32ToInt64: case TruncUFloat32ToInt64: case TruncSFloat64ToInt64: case TruncUFloat64ToInt64: + case TruncSatSFloat32ToInt64: + case TruncSatUFloat32ToInt64: + case TruncSatSFloat64ToInt64: + case TruncSatUFloat64ToInt64: case ReinterpretFloat64: type = i64; break; case ReinterpretInt32: case ConvertSInt32ToFloat32: |