summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/binaryen-c.cpp8
-rw-r--r--src/binaryen-c.h8
-rw-r--r--src/gen-s-parser.inc120
-rw-r--r--src/ir/cost.h10
-rw-r--r--src/js/binaryen.js-post.js40
-rw-r--r--src/literal.h13
-rw-r--r--src/passes/Print.cpp8
-rw-r--r--src/tools/fuzzing.h36
-rw-r--r--src/wasm-binary.h12
-rw-r--r--src/wasm-interpreter.h16
-rw-r--r--src/wasm-stack.h8
-rw-r--r--src/wasm.h3
-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
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: