3434#include " ast_utils.h"
3535#include " wasm-builder.h"
3636#include " wasm-emscripten.h"
37- #include " wasm-printing.h"
3837#include " wasm-module-building.h"
3938
4039namespace wasm {
@@ -788,7 +787,7 @@ class Asm2WasmBuilder {
788787 }
789788
790789 // Some conversions might trap, so emit them safely if necessary
791- Expression* makeTrappingFloatToInt (bool signed_, Expression* value) {
790+ Expression* makeTrappingFloatToInt32 (bool signed_, Expression* value) {
792791 if (trapMode == TrapMode::Allow) {
793792 auto ret = allocator.alloc <Unary>();
794793 ret->value = value;
@@ -879,6 +878,76 @@ class Asm2WasmBuilder {
879878 return ret;
880879 }
881880
881+ Expression* makeTrappingFloatToInt64 (bool signed_, Expression* value) {
882+ if (trapMode == TrapMode::Allow) {
883+ auto ret = allocator.alloc <Unary>();
884+ ret->value = value;
885+ bool isF64 = ret->value ->type == f64 ;
886+ if (signed_) {
887+ ret->op = isF64 ? TruncSFloat64ToInt64 : TruncSFloat32ToInt64;
888+ } else {
889+ ret->op = isF64 ? TruncUFloat64ToInt64 : TruncUFloat32ToInt64;
890+ }
891+ ret->type = WasmType::i64 ;
892+ return ret;
893+ }
894+ // WebAssembly traps on float-to-int overflows, but asm.js wouldn't, so we must do something
895+ // First, normalize input to f64
896+ auto input = value;
897+ if (input->type == f32 ) {
898+ auto conv = allocator.alloc <Unary>();
899+ conv->op = PromoteFloat32;
900+ conv->value = input;
901+ conv->type = WasmType::f64 ;
902+ input = conv;
903+ }
904+ // There is no "JS" way to handle this, as no i64s in JS, so always clamp if we don't allow traps
905+ Call *ret = allocator.alloc <Call>();
906+ ret->target = F64_TO_INT64;
907+ ret->operands .push_back (input);
908+ ret->type = i64 ;
909+ static bool added = false ;
910+ if (!added) {
911+ added = true ;
912+ auto func = new Function;
913+ func->name = ret->target ;
914+ func->params .push_back (f64 );
915+ func->result = i64 ;
916+ func->body = builder.makeUnary (TruncSFloat64ToInt64,
917+ builder.makeGetLocal (0 , f64 )
918+ );
919+ // too small
920+ func->body = builder.makeIf (
921+ builder.makeBinary (LeFloat64,
922+ builder.makeGetLocal (0 , f64 ),
923+ builder.makeConst (Literal (double (std::numeric_limits<int64_t >::min ()) - 1 ))
924+ ),
925+ builder.makeConst (Literal (int64_t (std::numeric_limits<int64_t >::min ()))),
926+ func->body
927+ );
928+ // too big
929+ func->body = builder.makeIf (
930+ builder.makeBinary (GeFloat64,
931+ builder.makeGetLocal (0 , f64 ),
932+ builder.makeConst (Literal (double (std::numeric_limits<int64_t >::max ()) + 1 ))
933+ ),
934+ builder.makeConst (Literal (int64_t (std::numeric_limits<int64_t >::min ()))), // NB: min here as well. anything out of range => to the min
935+ func->body
936+ );
937+ // nan
938+ func->body = builder.makeIf (
939+ builder.makeBinary (NeFloat64,
940+ builder.makeGetLocal (0 , f64 ),
941+ builder.makeGetLocal (0 , f64 )
942+ ),
943+ builder.makeConst (Literal (int64_t (std::numeric_limits<int64_t >::min ()))), // NB: min here as well. anything invalid => to the min
944+ func->body
945+ );
946+ wasm.addFunction (func);
947+ }
948+ return ret;
949+ }
950+
882951 Expression* truncateToInt32 (Expression* value) {
883952 if (value->type == i64 ) return builder.makeUnary (UnaryOp::WrapInt64, value);
884953 // either i32, or a call_import whose type we don't know yet (but would be legalized to i32 anyhow)
@@ -1302,10 +1371,12 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
13021371 }
13031372 auto importResult = getModule ()->getFunctionType (getModule ()->getImport (curr->target )->functionType )->result ;
13041373 if (curr->type != importResult) {
1374+ auto old = curr->type ;
1375+ curr->type = importResult;
13051376 if (importResult == f64 ) {
13061377 // we use a JS f64 value which is the most general, and convert to it
1307- switch (curr-> type ) {
1308- case i32 : replaceCurrent (parent->builder . makeUnary (TruncSFloat64ToInt32 , curr)); break ;
1378+ switch (old ) {
1379+ case i32 : replaceCurrent (parent->makeTrappingFloatToInt32 ( true /* signed, asm.js ffi */ , curr)); break ;
13091380 case f32 : replaceCurrent (parent->builder .makeUnary (DemoteFloat64, curr)); break ;
13101381 case none: {
13111382 // this function returns a value, but we are not using it, so it must be dropped.
@@ -1315,11 +1386,10 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
13151386 default : WASM_UNREACHABLE ();
13161387 }
13171388 } else {
1318- assert (curr-> type == none);
1389+ assert (old == none);
13191390 // we don't want a return value here, but the import does provide one
13201391 // autodrop will do that for us.
13211392 }
1322- curr->type = importResult;
13231393 }
13241394 }
13251395
@@ -1836,7 +1906,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
18361906 // ~, might be ~~ as a coercion or just a not
18371907 if (ast[2 ]->isArray (UNARY_PREFIX) && ast[2 ][1 ] == B_NOT) {
18381908 // if we have an unsigned coercion on us, it is an unsigned op
1839- return makeTrappingFloatToInt (!isParentUnsignedCoercion (astStackHelper.getParent ()), process (ast[2 ][2 ]));
1909+ return makeTrappingFloatToInt32 (!isParentUnsignedCoercion (astStackHelper.getParent ()), process (ast[2 ][2 ]));
18401910 }
18411911 // no bitwise unary not, so do xor with -1
18421912 auto ret = allocator.alloc <Binary>();
@@ -2038,10 +2108,8 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
20382108 if (name == I64_S2D) return builder.makeUnary (UnaryOp::ConvertSInt64ToFloat64, value);
20392109 if (name == I64_U2F) return builder.makeUnary (UnaryOp::ConvertUInt64ToFloat32, value);
20402110 if (name == I64_U2D) return builder.makeUnary (UnaryOp::ConvertUInt64ToFloat64, value);
2041- if (name == I64_F2S) return builder.makeUnary (UnaryOp::TruncSFloat32ToInt64, value);
2042- if (name == I64_D2S) return builder.makeUnary (UnaryOp::TruncSFloat64ToInt64, value);
2043- if (name == I64_F2U) return builder.makeUnary (UnaryOp::TruncUFloat32ToInt64, value);
2044- if (name == I64_D2U) return builder.makeUnary (UnaryOp::TruncUFloat64ToInt64, value);
2111+ if (name == I64_F2S || name == I64_D2S) return makeTrappingFloatToInt64 (true /* signed */ , value);
2112+ if (name == I64_F2U || name == I64_D2U) return makeTrappingFloatToInt64 (false /* unsigned */ , value);
20452113 if (name == I64_BC2D) return builder.makeUnary (UnaryOp::ReinterpretInt64, value);
20462114 if (name == I64_BC2I) return builder.makeUnary (UnaryOp::ReinterpretFloat64, value);
20472115 if (name == I64_CTTZ) return builder.makeUnary (UnaryOp::CtzInt64, value);
0 commit comments