Consistently handle possible traps in all cases (#1062)

* consistently handle possible traps in asm.js ffi results

* consistently handle possible traps in i64/float conversions
diff --git a/src/asm2wasm.h b/src/asm2wasm.h
index f6d0443..90e258c 100644
--- a/src/asm2wasm.h
+++ b/src/asm2wasm.h
@@ -34,7 +34,6 @@
 #include "ast_utils.h"
 #include "wasm-builder.h"
 #include "wasm-emscripten.h"
-#include "wasm-printing.h"
 #include "wasm-module-building.h"
 
 namespace wasm {
@@ -788,7 +787,7 @@
   }
 
   // Some conversions might trap, so emit them safely if necessary
-  Expression* makeTrappingFloatToInt(bool signed_, Expression* value) {
+  Expression* makeTrappingFloatToInt32(bool signed_, Expression* value) {
     if (trapMode == TrapMode::Allow) {
       auto ret = allocator.alloc<Unary>();
       ret->value = value;
@@ -879,6 +878,76 @@
     return ret;
   }
 
+  Expression* makeTrappingFloatToInt64(bool signed_, Expression* value) {
+    if (trapMode == TrapMode::Allow) {
+      auto ret = allocator.alloc<Unary>();
+      ret->value = value;
+      bool isF64 = ret->value->type == f64;
+      if (signed_) {
+        ret->op = isF64 ? TruncSFloat64ToInt64 : TruncSFloat32ToInt64;
+      } else {
+        ret->op = isF64 ? TruncUFloat64ToInt64 : TruncUFloat32ToInt64;
+      }
+      ret->type = WasmType::i64;
+      return ret;
+    }
+    // WebAssembly traps on float-to-int overflows, but asm.js wouldn't, so we must do something
+    // First, normalize input to f64
+    auto input = value;
+    if (input->type == f32) {
+      auto conv = allocator.alloc<Unary>();
+      conv->op = PromoteFloat32;
+      conv->value = input;
+      conv->type = WasmType::f64;
+      input = conv;
+    }
+    // There is no "JS" way to handle this, as no i64s in JS, so always clamp if we don't allow traps
+    Call *ret = allocator.alloc<Call>();
+    ret->target = F64_TO_INT64;
+    ret->operands.push_back(input);
+    ret->type = i64;
+    static bool added = false;
+    if (!added) {
+      added = true;
+      auto func = new Function;
+      func->name = ret->target;
+      func->params.push_back(f64);
+      func->result = i64;
+      func->body = builder.makeUnary(TruncSFloat64ToInt64,
+        builder.makeGetLocal(0, f64)
+      );
+      // too small
+      func->body = builder.makeIf(
+        builder.makeBinary(LeFloat64,
+          builder.makeGetLocal(0, f64),
+          builder.makeConst(Literal(double(std::numeric_limits<int64_t>::min()) - 1))
+        ),
+        builder.makeConst(Literal(int64_t(std::numeric_limits<int64_t>::min()))),
+        func->body
+      );
+      // too big
+      func->body = builder.makeIf(
+        builder.makeBinary(GeFloat64,
+          builder.makeGetLocal(0, f64),
+          builder.makeConst(Literal(double(std::numeric_limits<int64_t>::max()) + 1))
+        ),
+        builder.makeConst(Literal(int64_t(std::numeric_limits<int64_t>::min()))), // NB: min here as well. anything out of range => to the min
+        func->body
+      );
+      // nan
+      func->body = builder.makeIf(
+        builder.makeBinary(NeFloat64,
+          builder.makeGetLocal(0, f64),
+          builder.makeGetLocal(0, f64)
+        ),
+        builder.makeConst(Literal(int64_t(std::numeric_limits<int64_t>::min()))), // NB: min here as well. anything invalid => to the min
+        func->body
+      );
+      wasm.addFunction(func);
+    }
+    return ret;
+  }
+
   Expression* truncateToInt32(Expression* value) {
     if (value->type == i64) return builder.makeUnary(UnaryOp::WrapInt64, value);
     // either i32, or a call_import whose type we don't know yet (but would be legalized to i32 anyhow)
@@ -1302,10 +1371,12 @@
       }
       auto importResult = getModule()->getFunctionType(getModule()->getImport(curr->target)->functionType)->result;
       if (curr->type != importResult) {
+        auto old = curr->type;
+        curr->type = importResult;
         if (importResult == f64) {
           // we use a JS f64 value which is the most general, and convert to it
-          switch (curr->type) {
-            case i32: replaceCurrent(parent->builder.makeUnary(TruncSFloat64ToInt32, curr)); break;
+          switch (old) {
+            case i32: replaceCurrent(parent->makeTrappingFloatToInt32(true /* signed, asm.js ffi */, curr)); break;
             case f32: replaceCurrent(parent->builder.makeUnary(DemoteFloat64, curr)); break;
             case none: {
               // this function returns a value, but we are not using it, so it must be dropped.
@@ -1315,11 +1386,10 @@
             default: WASM_UNREACHABLE();
           }
         } else {
-          assert(curr->type == none);
+          assert(old == none);
           // we don't want a return value here, but the import does provide one
           // autodrop will do that for us.
         }
-        curr->type = importResult;
       }
     }
 
@@ -1836,7 +1906,7 @@
         // ~, might be ~~ as a coercion or just a not
         if (ast[2]->isArray(UNARY_PREFIX) && ast[2][1] == B_NOT) {
           // if we have an unsigned coercion on us, it is an unsigned op
-          return makeTrappingFloatToInt(!isParentUnsignedCoercion(astStackHelper.getParent()), process(ast[2][2]));
+          return makeTrappingFloatToInt32(!isParentUnsignedCoercion(astStackHelper.getParent()), process(ast[2][2]));
         }
         // no bitwise unary not, so do xor with -1
         auto ret = allocator.alloc<Binary>();
@@ -2038,10 +2108,8 @@
                 if (name == I64_S2D) return builder.makeUnary(UnaryOp::ConvertSInt64ToFloat64, value);
                 if (name == I64_U2F) return builder.makeUnary(UnaryOp::ConvertUInt64ToFloat32, value);
                 if (name == I64_U2D) return builder.makeUnary(UnaryOp::ConvertUInt64ToFloat64, value);
-                if (name == I64_F2S) return builder.makeUnary(UnaryOp::TruncSFloat32ToInt64, value);
-                if (name == I64_D2S) return builder.makeUnary(UnaryOp::TruncSFloat64ToInt64, value);
-                if (name == I64_F2U) return builder.makeUnary(UnaryOp::TruncUFloat32ToInt64, value);
-                if (name == I64_D2U) return builder.makeUnary(UnaryOp::TruncUFloat64ToInt64, value);
+                if (name == I64_F2S || name == I64_D2S) return makeTrappingFloatToInt64(true /* signed */, value);
+                if (name == I64_F2U || name == I64_D2U) return makeTrappingFloatToInt64(false /* unsigned */, value);
                 if (name == I64_BC2D) return builder.makeUnary(UnaryOp::ReinterpretInt64, value);
                 if (name == I64_BC2I) return builder.makeUnary(UnaryOp::ReinterpretFloat64, value);
                 if (name == I64_CTTZ) return builder.makeUnary(UnaryOp::CtzInt64, value);
diff --git a/src/asmjs/shared-constants.cpp b/src/asmjs/shared-constants.cpp
index 02e80d3..f1c5ac5 100644
--- a/src/asmjs/shared-constants.cpp
+++ b/src/asmjs/shared-constants.cpp
@@ -40,6 +40,7 @@
                 ASM2WASM("asm2wasm"),
                 F64_REM("f64-rem"),
                 F64_TO_INT("f64-to-int"),
+                F64_TO_INT64("f64-to-int64"),
                 I32S_DIV("i32s-div"),
                 I32U_DIV("i32u-div"),
                 I32S_REM("i32s-rem"),
diff --git a/src/asmjs/shared-constants.h b/src/asmjs/shared-constants.h
index f130237..e3108c8 100644
--- a/src/asmjs/shared-constants.h
+++ b/src/asmjs/shared-constants.h
@@ -43,6 +43,7 @@
                 ASM2WASM,
                 F64_REM,
                 F64_TO_INT,
+                F64_TO_INT64,
                 I32S_DIV,
                 I32U_DIV,
                 I32S_REM,
diff --git a/test/unit.fromasm b/test/unit.fromasm
index e5ff236..c869e23 100644
--- a/test/unit.fromasm
+++ b/test/unit.fromasm
@@ -613,7 +613,7 @@
   (drop
    (if (result i32)
     (call $return_int)
-    (i32.trunc_s/f64
+    (call $f64-to-int
      (call $abort
       (f64.convert_s/i32
        (i32.const 5)
diff --git a/test/unit.fromasm.clamp b/test/unit.fromasm.clamp
index e21d0f2..370e66c 100644
--- a/test/unit.fromasm.clamp
+++ b/test/unit.fromasm.clamp
@@ -637,7 +637,7 @@
   (drop
    (if (result i32)
     (call $return_int)
-    (i32.trunc_s/f64
+    (call $f64-to-int
      (call $abort
       (f64.convert_s/i32
        (i32.const 5)
diff --git a/test/unit.fromasm.clamp.no-opts b/test/unit.fromasm.clamp.no-opts
index 18f16d3..8ff2656 100644
--- a/test/unit.fromasm.clamp.no-opts
+++ b/test/unit.fromasm.clamp.no-opts
@@ -1097,7 +1097,7 @@
   (set_local $x
    (if (result i32)
     (call $return_int)
-    (i32.trunc_s/f64
+    (call $f64-to-int
      (call $abort
       (f64.convert_s/i32
        (i32.const 5)
diff --git a/test/unit.fromasm.no-opts b/test/unit.fromasm.no-opts
index 841a4d3..dba91cf 100644
--- a/test/unit.fromasm.no-opts
+++ b/test/unit.fromasm.no-opts
@@ -1073,7 +1073,7 @@
   (set_local $x
    (if (result i32)
     (call $return_int)
-    (i32.trunc_s/f64
+    (call $f64-to-int
      (call $abort
       (f64.convert_s/i32
        (i32.const 5)
diff --git a/test/wasm-only.fromasm b/test/wasm-only.fromasm
index b8bda7f..f12226b 100644
--- a/test/wasm-only.fromasm
+++ b/test/wasm-only.fromasm
@@ -191,10 +191,38 @@
    )
   )
  )
+ (func $f64-to-int64 (param $0 f64) (result i64)
+  (if (result i64)
+   (f64.ne
+    (get_local $0)
+    (get_local $0)
+   )
+   (i64.const -9223372036854775808)
+   (if (result i64)
+    (f64.ge
+     (get_local $0)
+     (f64.const 9223372036854775808)
+    )
+    (i64.const -9223372036854775808)
+    (if (result i64)
+     (f64.le
+      (get_local $0)
+      (f64.const -9223372036854775808)
+     )
+     (i64.const -9223372036854775808)
+     (i64.trunc_s/f64
+      (get_local $0)
+     )
+    )
+   )
+  )
+ )
  (func $test64
   (local $0 i64)
   (local $1 i64)
   (local $2 i32)
+  (local $3 f32)
+  (local $4 f64)
   (drop
    (call $i64s-rem
     (call $i64u-rem
@@ -203,27 +231,27 @@
        (i64.mul
         (i64.sub
          (i64.add
-          (tee_local $0
+          (tee_local $1
            (i64.const 128849018897)
           )
           (i64.const 100)
          )
-         (get_local $0)
+         (get_local $1)
         )
-        (get_local $0)
+        (get_local $1)
        )
-       (get_local $0)
+       (get_local $1)
       )
-      (get_local $0)
+      (get_local $1)
      )
-     (get_local $0)
+     (get_local $1)
     )
-    (get_local $0)
+    (get_local $1)
    )
   )
   (i64.store
    (i32.const 120)
-   (tee_local $1
+   (tee_local $0
     (i64.load
      (i32.const 120)
     )
@@ -231,30 +259,62 @@
   )
   (i64.store
    (i32.const 120)
-   (get_local $1)
+   (get_local $0)
   )
   (i64.store align=2
    (i32.const 120)
-   (get_local $1)
+   (get_local $0)
   )
   (i64.store align=4
    (i32.const 120)
-   (get_local $1)
+   (get_local $0)
   )
   (i64.store
    (i32.const 120)
-   (get_local $1)
+   (get_local $0)
   )
   (set_local $2
    (i32.wrap/i64
-    (get_local $1)
+    (get_local $0)
    )
   )
-  (set_local $1
+  (set_local $0
    (i64.extend_u/i32
     (get_local $2)
    )
   )
+  (drop
+   (call $f64-to-int64
+    (f64.promote/f32
+     (tee_local $3
+      (f32.convert_u/i64
+       (get_local $0)
+      )
+     )
+    )
+   )
+  )
+  (drop
+   (call $f64-to-int64
+    (tee_local $4
+     (f64.convert_u/i64
+      (get_local $0)
+     )
+    )
+   )
+  )
+  (drop
+   (call $f64-to-int64
+    (f64.promote/f32
+     (get_local $3)
+    )
+   )
+  )
+  (drop
+   (call $f64-to-int64
+    (get_local $4)
+   )
+  )
  )
  (func $imports (result i64)
   (call $legalfunc$illegalImport
diff --git a/test/wasm-only.fromasm.clamp b/test/wasm-only.fromasm.clamp
index b8bda7f..f12226b 100644
--- a/test/wasm-only.fromasm.clamp
+++ b/test/wasm-only.fromasm.clamp
@@ -191,10 +191,38 @@
    )
   )
  )
+ (func $f64-to-int64 (param $0 f64) (result i64)
+  (if (result i64)
+   (f64.ne
+    (get_local $0)
+    (get_local $0)
+   )
+   (i64.const -9223372036854775808)
+   (if (result i64)
+    (f64.ge
+     (get_local $0)
+     (f64.const 9223372036854775808)
+    )
+    (i64.const -9223372036854775808)
+    (if (result i64)
+     (f64.le
+      (get_local $0)
+      (f64.const -9223372036854775808)
+     )
+     (i64.const -9223372036854775808)
+     (i64.trunc_s/f64
+      (get_local $0)
+     )
+    )
+   )
+  )
+ )
  (func $test64
   (local $0 i64)
   (local $1 i64)
   (local $2 i32)
+  (local $3 f32)
+  (local $4 f64)
   (drop
    (call $i64s-rem
     (call $i64u-rem
@@ -203,27 +231,27 @@
        (i64.mul
         (i64.sub
          (i64.add
-          (tee_local $0
+          (tee_local $1
            (i64.const 128849018897)
           )
           (i64.const 100)
          )
-         (get_local $0)
+         (get_local $1)
         )
-        (get_local $0)
+        (get_local $1)
        )
-       (get_local $0)
+       (get_local $1)
       )
-      (get_local $0)
+      (get_local $1)
      )
-     (get_local $0)
+     (get_local $1)
     )
-    (get_local $0)
+    (get_local $1)
    )
   )
   (i64.store
    (i32.const 120)
-   (tee_local $1
+   (tee_local $0
     (i64.load
      (i32.const 120)
     )
@@ -231,30 +259,62 @@
   )
   (i64.store
    (i32.const 120)
-   (get_local $1)
+   (get_local $0)
   )
   (i64.store align=2
    (i32.const 120)
-   (get_local $1)
+   (get_local $0)
   )
   (i64.store align=4
    (i32.const 120)
-   (get_local $1)
+   (get_local $0)
   )
   (i64.store
    (i32.const 120)
-   (get_local $1)
+   (get_local $0)
   )
   (set_local $2
    (i32.wrap/i64
-    (get_local $1)
+    (get_local $0)
    )
   )
-  (set_local $1
+  (set_local $0
    (i64.extend_u/i32
     (get_local $2)
    )
   )
+  (drop
+   (call $f64-to-int64
+    (f64.promote/f32
+     (tee_local $3
+      (f32.convert_u/i64
+       (get_local $0)
+      )
+     )
+    )
+   )
+  )
+  (drop
+   (call $f64-to-int64
+    (tee_local $4
+     (f64.convert_u/i64
+      (get_local $0)
+     )
+    )
+   )
+  )
+  (drop
+   (call $f64-to-int64
+    (f64.promote/f32
+     (get_local $3)
+    )
+   )
+  )
+  (drop
+   (call $f64-to-int64
+    (get_local $4)
+   )
+  )
  )
  (func $imports (result i64)
   (call $legalfunc$illegalImport
diff --git a/test/wasm-only.fromasm.clamp.no-opts b/test/wasm-only.fromasm.clamp.no-opts
index 97533d7..baac47f 100644
--- a/test/wasm-only.fromasm.clamp.no-opts
+++ b/test/wasm-only.fromasm.clamp.no-opts
@@ -341,6 +341,32 @@
    )
   )
  )
+ (func $f64-to-int64 (param $0 f64) (result i64)
+  (if (result i64)
+   (f64.ne
+    (get_local $0)
+    (get_local $0)
+   )
+   (i64.const -9223372036854775808)
+   (if (result i64)
+    (f64.ge
+     (get_local $0)
+     (f64.const 9223372036854775808)
+    )
+    (i64.const -9223372036854775808)
+    (if (result i64)
+     (f64.le
+      (get_local $0)
+      (f64.const -9223372036854775808)
+     )
+     (i64.const -9223372036854775808)
+     (i64.trunc_s/f64
+      (get_local $0)
+     )
+    )
+   )
+  )
+ )
  (func $test64
   (local $x i64)
   (local $y i64)
@@ -573,22 +599,26 @@
    )
   )
   (set_local $x
-   (i64.trunc_s/f32
-    (get_local $float32)
+   (call $f64-to-int64
+    (f64.promote/f32
+     (get_local $float32)
+    )
    )
   )
   (set_local $x
-   (i64.trunc_s/f64
+   (call $f64-to-int64
     (get_local $float64)
    )
   )
   (set_local $x
-   (i64.trunc_u/f32
-    (get_local $float32)
+   (call $f64-to-int64
+    (f64.promote/f32
+     (get_local $float32)
+    )
    )
   )
   (set_local $x
-   (i64.trunc_u/f64
+   (call $f64-to-int64
     (get_local $float64)
    )
   )
diff --git a/test/wasm-only.fromasm.no-opts b/test/wasm-only.fromasm.no-opts
index 97533d7..baac47f 100644
--- a/test/wasm-only.fromasm.no-opts
+++ b/test/wasm-only.fromasm.no-opts
@@ -341,6 +341,32 @@
    )
   )
  )
+ (func $f64-to-int64 (param $0 f64) (result i64)
+  (if (result i64)
+   (f64.ne
+    (get_local $0)
+    (get_local $0)
+   )
+   (i64.const -9223372036854775808)
+   (if (result i64)
+    (f64.ge
+     (get_local $0)
+     (f64.const 9223372036854775808)
+    )
+    (i64.const -9223372036854775808)
+    (if (result i64)
+     (f64.le
+      (get_local $0)
+      (f64.const -9223372036854775808)
+     )
+     (i64.const -9223372036854775808)
+     (i64.trunc_s/f64
+      (get_local $0)
+     )
+    )
+   )
+  )
+ )
  (func $test64
   (local $x i64)
   (local $y i64)
@@ -573,22 +599,26 @@
    )
   )
   (set_local $x
-   (i64.trunc_s/f32
-    (get_local $float32)
+   (call $f64-to-int64
+    (f64.promote/f32
+     (get_local $float32)
+    )
    )
   )
   (set_local $x
-   (i64.trunc_s/f64
+   (call $f64-to-int64
     (get_local $float64)
    )
   )
   (set_local $x
-   (i64.trunc_u/f32
-    (get_local $float32)
+   (call $f64-to-int64
+    (f64.promote/f32
+     (get_local $float32)
+    )
    )
   )
   (set_local $x
-   (i64.trunc_u/f64
+   (call $f64-to-int64
     (get_local $float64)
    )
   )