diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/binaryen-c.cpp | 8 | ||||
-rw-r--r-- | src/binaryen-c.h | 8 | ||||
-rw-r--r-- | src/gen-s-parser.inc | 120 | ||||
-rw-r--r-- | src/ir/cost.h | 10 | ||||
-rw-r--r-- | src/js/binaryen.js-post.js | 40 | ||||
-rw-r--r-- | src/literal.h | 13 | ||||
-rw-r--r-- | src/passes/Print.cpp | 8 | ||||
-rw-r--r-- | src/tools/fuzzing.h | 36 | ||||
-rw-r--r-- | src/wasm-binary.h | 12 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 16 | ||||
-rw-r--r-- | src/wasm-stack.h | 8 | ||||
-rw-r--r-- | src/wasm.h | 3 | ||||
-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 |
16 files changed, 434 insertions, 70 deletions
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index d098810e2..6248f8d91 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -394,6 +394,14 @@ BinaryenOp BinaryenAtomicRMWAnd(void) { return AtomicRMWOp::And; } BinaryenOp BinaryenAtomicRMWOr(void) { return AtomicRMWOp::Or; } BinaryenOp BinaryenAtomicRMWXor(void) { return AtomicRMWOp::Xor; } BinaryenOp BinaryenAtomicRMWXchg(void) { return AtomicRMWOp::Xchg; } +BinaryenOp BinaryenTruncSatSFloat32ToInt32(void) { return TruncSatSFloat32ToInt32; } +BinaryenOp BinaryenTruncSatSFloat32ToInt64(void) { return TruncSatSFloat32ToInt64; } +BinaryenOp BinaryenTruncSatUFloat32ToInt32(void) { return TruncSatUFloat32ToInt32; } +BinaryenOp BinaryenTruncSatUFloat32ToInt64(void) { return TruncSatUFloat32ToInt64; } +BinaryenOp BinaryenTruncSatSFloat64ToInt32(void) { return TruncSatSFloat64ToInt32; } +BinaryenOp BinaryenTruncSatSFloat64ToInt64(void) { return TruncSatSFloat64ToInt64; } +BinaryenOp BinaryenTruncSatUFloat64ToInt32(void) { return TruncSatUFloat64ToInt32; } +BinaryenOp BinaryenTruncSatUFloat64ToInt64(void) { return TruncSatUFloat64ToInt64; } BinaryenExpressionRef BinaryenBlock(BinaryenModuleRef module, const char* name, BinaryenExpressionRef* children, BinaryenIndex numChildren, BinaryenType type) { auto* ret = ((Module*)module)->allocator.alloc<Block>(); diff --git a/src/binaryen-c.h b/src/binaryen-c.h index 14fc38de3..dc47b379f 100644 --- a/src/binaryen-c.h +++ b/src/binaryen-c.h @@ -324,6 +324,14 @@ BinaryenOp BinaryenAtomicRMWAnd(void); BinaryenOp BinaryenAtomicRMWOr(void); BinaryenOp BinaryenAtomicRMWXor(void); BinaryenOp BinaryenAtomicRMWXchg(void); +BinaryenOp BinaryenTruncSatSFloat32ToInt32(void); +BinaryenOp BinaryenTruncSatSFloat32ToInt64(void); +BinaryenOp BinaryenTruncSatUFloat32ToInt32(void); +BinaryenOp BinaryenTruncSatUFloat32ToInt64(void); +BinaryenOp BinaryenTruncSatSFloat64ToInt32(void); +BinaryenOp BinaryenTruncSatSFloat64ToInt64(void); +BinaryenOp BinaryenTruncSatUFloat64ToInt32(void); +BinaryenOp BinaryenTruncSatUFloat64ToInt64(void); typedef void* BinaryenExpressionRef; diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc index 6cf004167..16399bfba 100644 --- a/src/gen-s-parser.inc +++ b/src/gen-s-parser.inc @@ -778,24 +778,56 @@ switch (op[0]) { case 't': { switch (op[10]) { case 's': { - switch (op[13]) { - case '3': - if (strcmp(op, "i32.trunc_s/f32") == 0) return makeUnary(s, UnaryOp::TruncSFloat32ToInt32); - goto parse_error; - case '6': - if (strcmp(op, "i32.trunc_s/f64") == 0) return makeUnary(s, UnaryOp::TruncSFloat64ToInt32); - goto parse_error; + switch (op[11]) { + case '/': { + switch (op[13]) { + case '3': + if (strcmp(op, "i32.trunc_s/f32") == 0) return makeUnary(s, UnaryOp::TruncSFloat32ToInt32); + goto parse_error; + case '6': + if (strcmp(op, "i32.trunc_s/f64") == 0) return makeUnary(s, UnaryOp::TruncSFloat64ToInt32); + goto parse_error; + default: goto parse_error; + } + } + case ':': { + switch (op[17]) { + case '3': + if (strcmp(op, "i32.trunc_s:sat/f32") == 0) return makeUnary(s, UnaryOp::TruncSatSFloat32ToInt32); + goto parse_error; + case '6': + if (strcmp(op, "i32.trunc_s:sat/f64") == 0) return makeUnary(s, UnaryOp::TruncSatSFloat64ToInt32); + goto parse_error; + default: goto parse_error; + } + } default: goto parse_error; } } case 'u': { - switch (op[13]) { - case '3': - if (strcmp(op, "i32.trunc_u/f32") == 0) return makeUnary(s, UnaryOp::TruncUFloat32ToInt32); - goto parse_error; - case '6': - if (strcmp(op, "i32.trunc_u/f64") == 0) return makeUnary(s, UnaryOp::TruncUFloat64ToInt32); - goto parse_error; + switch (op[11]) { + case '/': { + switch (op[13]) { + case '3': + if (strcmp(op, "i32.trunc_u/f32") == 0) return makeUnary(s, UnaryOp::TruncUFloat32ToInt32); + goto parse_error; + case '6': + if (strcmp(op, "i32.trunc_u/f64") == 0) return makeUnary(s, UnaryOp::TruncUFloat64ToInt32); + goto parse_error; + default: goto parse_error; + } + } + case ':': { + switch (op[17]) { + case '3': + if (strcmp(op, "i32.trunc_u:sat/f32") == 0) return makeUnary(s, UnaryOp::TruncSatUFloat32ToInt32); + goto parse_error; + case '6': + if (strcmp(op, "i32.trunc_u:sat/f64") == 0) return makeUnary(s, UnaryOp::TruncSatUFloat64ToInt32); + goto parse_error; + default: goto parse_error; + } + } default: goto parse_error; } } @@ -1275,24 +1307,56 @@ switch (op[0]) { case 't': { switch (op[10]) { case 's': { - switch (op[13]) { - case '3': - if (strcmp(op, "i64.trunc_s/f32") == 0) return makeUnary(s, UnaryOp::TruncSFloat32ToInt64); - goto parse_error; - case '6': - if (strcmp(op, "i64.trunc_s/f64") == 0) return makeUnary(s, UnaryOp::TruncSFloat64ToInt64); - goto parse_error; + switch (op[11]) { + case '/': { + switch (op[13]) { + case '3': + if (strcmp(op, "i64.trunc_s/f32") == 0) return makeUnary(s, UnaryOp::TruncSFloat32ToInt64); + goto parse_error; + case '6': + if (strcmp(op, "i64.trunc_s/f64") == 0) return makeUnary(s, UnaryOp::TruncSFloat64ToInt64); + goto parse_error; + default: goto parse_error; + } + } + case ':': { + switch (op[17]) { + case '3': + if (strcmp(op, "i64.trunc_s:sat/f32") == 0) return makeUnary(s, UnaryOp::TruncSatSFloat32ToInt64); + goto parse_error; + case '6': + if (strcmp(op, "i64.trunc_s:sat/f64") == 0) return makeUnary(s, UnaryOp::TruncSatSFloat64ToInt64); + goto parse_error; + default: goto parse_error; + } + } default: goto parse_error; } } case 'u': { - switch (op[13]) { - case '3': - if (strcmp(op, "i64.trunc_u/f32") == 0) return makeUnary(s, UnaryOp::TruncUFloat32ToInt64); - goto parse_error; - case '6': - if (strcmp(op, "i64.trunc_u/f64") == 0) return makeUnary(s, UnaryOp::TruncUFloat64ToInt64); - goto parse_error; + switch (op[11]) { + case '/': { + switch (op[13]) { + case '3': + if (strcmp(op, "i64.trunc_u/f32") == 0) return makeUnary(s, UnaryOp::TruncUFloat32ToInt64); + goto parse_error; + case '6': + if (strcmp(op, "i64.trunc_u/f64") == 0) return makeUnary(s, UnaryOp::TruncUFloat64ToInt64); + goto parse_error; + default: goto parse_error; + } + } + case ':': { + switch (op[17]) { + case '3': + if (strcmp(op, "i64.trunc_u:sat/f32") == 0) return makeUnary(s, UnaryOp::TruncSatUFloat32ToInt64); + goto parse_error; + case '6': + if (strcmp(op, "i64.trunc_u:sat/f64") == 0) return makeUnary(s, UnaryOp::TruncSatUFloat64ToInt64); + goto parse_error; + default: goto parse_error; + } + } default: goto parse_error; } } diff --git a/src/ir/cost.h b/src/ir/cost.h index 6dabf2fcb..e28f535e7 100644 --- a/src/ir/cost.h +++ b/src/ir/cost.h @@ -141,7 +141,15 @@ struct CostAnalyzer : public Visitor<CostAnalyzer, Index> { case ExtendS16Int32: case ExtendS8Int64: case ExtendS16Int64: - case ExtendS32Int64: ret = 1; break; + case ExtendS32Int64: + case TruncSatSFloat32ToInt32: + case TruncSatUFloat32ToInt32: + case TruncSatSFloat64ToInt32: + case TruncSatUFloat64ToInt32: + case TruncSatSFloat32ToInt64: + case TruncSatUFloat32ToInt64: + case TruncSatSFloat64ToInt64: + case TruncSatUFloat64ToInt64: ret = 1; break; case SqrtFloat32: case SqrtFloat64: ret = 2; break; case InvalidUnary: WASM_UNREACHABLE(); diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js index 06688d706..b63427935 100644 --- a/src/js/binaryen.js-post.js +++ b/src/js/binaryen.js-post.js @@ -101,6 +101,14 @@ Module['TruncSFloat64ToInt32'] = Module['_BinaryenTruncSFloat64ToInt32'](); Module['TruncSFloat64ToInt64'] = Module['_BinaryenTruncSFloat64ToInt64'](); Module['TruncUFloat64ToInt32'] = Module['_BinaryenTruncUFloat64ToInt32'](); Module['TruncUFloat64ToInt64'] = Module['_BinaryenTruncUFloat64ToInt64'](); +Module['TruncSatSFloat32ToInt32'] = Module['_BinaryenTruncSatSFloat32ToInt32'](); +Module['TruncSatSFloat32ToInt64'] = Module['_BinaryenTruncSatSFloat32ToInt64'](); +Module['TruncSatUFloat32ToInt32'] = Module['_BinaryenTruncSatUFloat32ToInt32'](); +Module['TruncSatUFloat32ToInt64'] = Module['_BinaryenTruncSatUFloat32ToInt64'](); +Module['TruncSatSFloat64ToInt32'] = Module['_BinaryenTruncSatSFloat64ToInt32'](); +Module['TruncSatSFloat64ToInt64'] = Module['_BinaryenTruncSatSFloat64ToInt64'](); +Module['TruncSatUFloat64ToInt32'] = Module['_BinaryenTruncSatUFloat64ToInt32'](); +Module['TruncSatUFloat64ToInt64'] = Module['_BinaryenTruncSatUFloat64ToInt64'](); Module['ReinterpretFloat32'] = Module['_BinaryenReinterpretFloat32'](); Module['ReinterpretFloat64'] = Module['_BinaryenReinterpretFloat64'](); Module['ConvertSInt32ToFloat32'] = Module['_BinaryenConvertSInt32ToFloat32'](); @@ -354,6 +362,22 @@ function wrapModule(module, self) { return Module['_BinaryenUnary'](module, Module['TruncUFloat64ToInt32'], value); }, }, + 'trunc_s_sat': { + 'f32': function(value) { + return Module['_BinaryenUnary'](module, Module['TruncSatSFloat32ToInt32'], value); + }, + 'f64': function(value) { + return Module['_BinaryenUnary'](module, Module['TruncSatSFloat64ToInt32'], value); + }, + }, + 'trunc_u_sat': { + 'f32': function(value) { + return Module['_BinaryenUnary'](module, Module['TruncSatUFloat32ToInt32'], value); + }, + 'f64': function(value) { + return Module['_BinaryenUnary'](module, Module['TruncSatUFloat64ToInt32'], value); + }, + }, 'reinterpret': function(value) { return Module['_BinaryenUnary'](module, Module['ReinterpretFloat32'], value); }, @@ -601,6 +625,22 @@ function wrapModule(module, self) { return Module['_BinaryenUnary'](module, Module['TruncUFloat64ToInt64'], value); }, }, + 'trunc_s_sat': { + 'f32': function(value) { + return Module['_BinaryenUnary'](module, Module['TruncSatSFloat32ToInt64'], value); + }, + 'f64': function(value) { + return Module['_BinaryenUnary'](module, Module['TruncSatSFloat64ToInt64'], value); + }, + }, + 'trunc_u_sat': { + 'f32': function(value) { + return Module['_BinaryenUnary'](module, Module['TruncSatUFloat32ToInt64'], value); + }, + 'f64': function(value) { + return Module['_BinaryenUnary'](module, Module['TruncSatUFloat64ToInt64'], value); + }, + }, 'reinterpret': function(value) { return Module['_BinaryenUnary'](module, Module['ReinterpretFloat64'], value); }, diff --git a/src/literal.h b/src/literal.h index aa9fd9705..1bc09c360 100644 --- a/src/literal.h +++ b/src/literal.h @@ -100,10 +100,15 @@ public: Literal truncateToI32() const; Literal truncateToF32() const; - Literal convertSToF32() const; - Literal convertUToF32() const; - Literal convertSToF64() const; - Literal convertUToF64() const; + Literal truncSIToF32() const; + Literal truncUIToF32() const; + Literal truncSIToF64() const; + Literal truncUIToF64() const; + + Literal truncSatToSI32() const; + Literal truncSatToSI64() const; + Literal truncSatToUI32() const; + Literal truncSatToUI64() const; Literal eqz() const; Literal neg() const; diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index e874321cd..51c7e9f97 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -292,6 +292,14 @@ struct PrintExpressionContents : public Visitor<PrintExpressionContents> { case ExtendS8Int64: o << "i64.extend8_s"; break; case ExtendS16Int64: o << "i64.extend16_s"; break; case ExtendS32Int64: o << "i64.extend32_s"; break; + case TruncSatSFloat32ToInt32: o << "i32.trunc_s:sat/f32"; break; + case TruncSatUFloat32ToInt32: o << "i32.trunc_u:sat/f32"; break; + case TruncSatSFloat64ToInt32: o << "i32.trunc_s:sat/f64"; break; + case TruncSatUFloat64ToInt32: o << "i32.trunc_u:sat/f64"; break; + case TruncSatSFloat32ToInt64: o << "i64.trunc_s:sat/f32"; break; + case TruncSatUFloat32ToInt64: o << "i64.trunc_u:sat/f32"; break; + case TruncSatSFloat64ToInt64: o << "i64.trunc_s:sat/f64"; break; + case TruncSatUFloat64ToInt64: o << "i64.trunc_u:sat/f64"; break; case InvalidUnary: WASM_UNREACHABLE(); } } diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h index 8f7123a78..243616c8a 100644 --- a/src/tools/fuzzing.h +++ b/src/tools/fuzzing.h @@ -1322,8 +1322,22 @@ private: break; } case 1: return makeUnary({ pick(EqZInt64, WrapInt64), make(i64) }); - case 2: return makeUnary({ pick(TruncSFloat32ToInt32, TruncUFloat32ToInt32, ReinterpretFloat32), make(f32) }); - case 3: return makeUnary({ pick(TruncSFloat64ToInt32, TruncUFloat64ToInt32), make(f64) }); + case 2: { + if (features.hasTruncSat()) { + return makeUnary({ pick(TruncSFloat32ToInt32, TruncUFloat32ToInt32, ReinterpretFloat32, TruncSatSFloat32ToInt32, TruncSatUFloat32ToInt32), make(f32) }); + } else { + return makeUnary({ pick(TruncSFloat32ToInt32, TruncUFloat32ToInt32, ReinterpretFloat32), make(f32) }); + } + break; + } + case 3: { + if (features.hasTruncSat()) { + return makeUnary({ pick(TruncSFloat64ToInt32, TruncUFloat64ToInt32, TruncSatSFloat64ToInt32, TruncSatUFloat64ToInt32), make(f64) }); + } else { + return makeUnary({ pick(TruncSFloat64ToInt32, TruncUFloat64ToInt32), make(f64) }); + } + break; + } } WASM_UNREACHABLE(); } @@ -1338,8 +1352,22 @@ private: break; } case 1: return makeUnary({ pick(ExtendSInt32, ExtendUInt32), make(i32) }); - case 2: return makeUnary({ pick(TruncSFloat32ToInt64, TruncUFloat32ToInt64), make(f32) }); - case 3: return makeUnary({ pick(TruncSFloat64ToInt64, TruncUFloat64ToInt64, ReinterpretFloat64), make(f64) }); + case 2: { + if (features.hasTruncSat()) { + return makeUnary({ pick(TruncSFloat32ToInt64, TruncUFloat32ToInt64, TruncSatSFloat32ToInt64, TruncSatUFloat32ToInt64), make(f32) }); + } else { + return makeUnary({ pick(TruncSFloat32ToInt64, TruncUFloat32ToInt64), make(f32) }); + } + break; + } + case 3: { + if (features.hasTruncSat()) { + return makeUnary({ pick(TruncSFloat64ToInt64, TruncUFloat64ToInt64, ReinterpretFloat64, TruncSatSFloat64ToInt64, TruncSatUFloat64ToInt64), make(f64) }); + } else { + return makeUnary({ pick(TruncSFloat64ToInt64, TruncUFloat64ToInt64, ReinterpretFloat64), make(f64) }); + } + break; + } } WASM_UNREACHABLE(); } diff --git a/src/wasm-binary.h b/src/wasm-binary.h index c767cf5eb..eeeba98db 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -548,6 +548,7 @@ enum ASTNodes { I64ExtendS16 = 0xc3, I64ExtendS32 = 0xc4, + TruncSatPrefix = 0xfc, AtomicPrefix = 0xfe }; @@ -627,6 +628,16 @@ enum AtomicOpcodes { AtomicCmpxchgOps_End = 0x4e }; +enum TruncSatOpcodes { + I32STruncSatF32 = 0x00, + I32UTruncSatF32 = 0x01, + I32STruncSatF64 = 0x02, + I32UTruncSatF64 = 0x03, + I64STruncSatF32 = 0x04, + I64UTruncSatF32 = 0x05, + I64STruncSatF64 = 0x06, + I64UTruncSatF64 = 0x07, +}; enum MemoryAccess { Offset = 0x10, // bit 4 @@ -944,6 +955,7 @@ public: bool maybeVisitConst(Expression*& out, uint8_t code); bool maybeVisitUnary(Expression*& out, uint8_t code); bool maybeVisitBinary(Expression*& out, uint8_t code); + bool maybeVisitTruncSat(Expression*& out, uint32_t code); void visitSelect(Select* curr); void visitReturn(Return* curr); bool maybeVisitHost(Expression*& out, uint8_t code); diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 39ea488ef..fed74282d 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -256,13 +256,13 @@ public: case ExtendUInt32: return value.extendToUI64(); case WrapInt64: return value.truncateToI32(); case ConvertUInt32ToFloat32: - case ConvertUInt64ToFloat32: return value.convertUToF32(); + case ConvertUInt64ToFloat32: return value.truncUIToF32(); case ConvertUInt32ToFloat64: - case ConvertUInt64ToFloat64: return value.convertUToF64(); + case ConvertUInt64ToFloat64: return value.truncUIToF64(); case ConvertSInt32ToFloat32: - case ConvertSInt64ToFloat32: return value.convertSToF32(); + case ConvertSInt64ToFloat32: return value.truncSIToF32(); case ConvertSInt32ToFloat64: - case ConvertSInt64ToFloat64: return value.convertSToF64(); + case ConvertSInt64ToFloat64: return value.truncSIToF64(); case ExtendS8Int32: case ExtendS8Int64: return value.extendS8(); case ExtendS16Int32: @@ -291,6 +291,14 @@ public: case TruncUFloat64ToInt32: case TruncUFloat32ToInt64: case TruncUFloat64ToInt64: return truncUFloat(curr, value); + case TruncSatSFloat32ToInt32: + case TruncSatSFloat64ToInt32: return value.truncSatToSI32(); + case TruncSatSFloat32ToInt64: + case TruncSatSFloat64ToInt64: return value.truncSatToSI64(); + case TruncSatUFloat32ToInt32: + case TruncSatUFloat64ToInt32: return value.truncSatToUI32(); + case TruncSatUFloat32ToInt64: + case TruncSatUFloat64ToInt64: return value.truncSatToUI64(); case ReinterpretFloat32: return value.castToI32(); case PromoteFloat32: return value.extendToF64(); case ReinterpretFloat64: return value.castToI64(); diff --git a/src/wasm-stack.h b/src/wasm-stack.h index 1a937f81d..6e1150981 100644 --- a/src/wasm-stack.h +++ b/src/wasm-stack.h @@ -961,6 +961,14 @@ void StackWriter<Mode, Parent>::visitUnary(Unary* curr) { case ExtendS8Int64: o << int8_t(BinaryConsts::I64ExtendS8); break; case ExtendS16Int64: o << int8_t(BinaryConsts::I64ExtendS16); break; case ExtendS32Int64: o << int8_t(BinaryConsts::I64ExtendS32); break; + case TruncSatSFloat32ToInt32: o << int8_t(BinaryConsts::TruncSatPrefix) << U32LEB(BinaryConsts::I32STruncSatF32); break; + case TruncSatUFloat32ToInt32: o << int8_t(BinaryConsts::TruncSatPrefix) << U32LEB(BinaryConsts::I32UTruncSatF32); break; + case TruncSatSFloat64ToInt32: o << int8_t(BinaryConsts::TruncSatPrefix) << U32LEB(BinaryConsts::I32STruncSatF64); break; + case TruncSatUFloat64ToInt32: o << int8_t(BinaryConsts::TruncSatPrefix) << U32LEB(BinaryConsts::I32UTruncSatF64); break; + case TruncSatSFloat32ToInt64: o << int8_t(BinaryConsts::TruncSatPrefix) << U32LEB(BinaryConsts::I64STruncSatF32); break; + case TruncSatUFloat32ToInt64: o << int8_t(BinaryConsts::TruncSatPrefix) << U32LEB(BinaryConsts::I64UTruncSatF32); break; + case TruncSatSFloat64ToInt64: o << int8_t(BinaryConsts::TruncSatPrefix) << U32LEB(BinaryConsts::I64STruncSatF64); break; + case TruncSatUFloat64ToInt64: o << int8_t(BinaryConsts::TruncSatPrefix) << U32LEB(BinaryConsts::I64UTruncSatF64); break; case InvalidUnary: WASM_UNREACHABLE(); } } diff --git a/src/wasm.h b/src/wasm.h index 1d5677134..b7b835ebe 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -119,6 +119,9 @@ enum UnaryOp { // Extend signed subword-sized integer. This differs from e.g. ExtendSInt32 // because the input integer is in an i64 value insetad of an i32 value. ExtendS8Int32, ExtendS16Int32, ExtendS8Int64, ExtendS16Int64, ExtendS32Int64, + // Saturating float-to-int + TruncSatSFloat32ToInt32, TruncSatUFloat32ToInt32, TruncSatSFloat64ToInt32, TruncSatUFloat64ToInt32, + TruncSatSFloat32ToInt64, TruncSatUFloat32ToInt64, TruncSatSFloat64ToInt64, TruncSatUFloat64ToInt64, InvalidUnary }; 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: |