summaryrefslogtreecommitdiff
path: root/src/wasm
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm')
-rw-r--r--src/wasm/literal.cpp87
-rw-r--r--src/wasm/wasm-binary.cpp26
-rw-r--r--src/wasm/wasm-validator.cpp101
-rw-r--r--src/wasm/wasm.cpp8
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: