Bug 1511958 - part 3, main Wasm BigInt<->I64 interop patch draft
authorAsumu Takikawa <asumu@igalia.com>
Thu, 05 Dec 2019 02:52:21 +0000
changeset 2517407 c12cd9a18b984531ad2e6c5d0ff5b3f81c71e52d
parent 2517406 9927fec5044538633d2546939d2dda0a07d98cf5
child 2517408 f17297a6e22c63ff1c3cc93224107345acae91fe
push id460517
push userreviewbot
push dateThu, 05 Dec 2019 02:52:52 +0000
treeherdertry@1b2d78f9463f [default view] [failures only]
bugs1511958
milestone73.0a1
Bug 1511958 - part 3, main Wasm BigInt<->I64 interop patch Differential Diff: PHID-DIFF-nicu75i35okzysn65elw
js/src/jit/x64/MacroAssembler-x64.h
js/src/wasm/WasmBuiltins.cpp
js/src/wasm/WasmFrameIter.cpp
js/src/wasm/WasmInstance.cpp
js/src/wasm/WasmStubs.cpp
js/src/wasm/WasmTypes.h
--- a/js/src/jit/x64/MacroAssembler-x64.h
+++ b/js/src/jit/x64/MacroAssembler-x64.h
@@ -823,16 +823,19 @@ class MacroAssemblerX64 : public MacroAs
   }
 
   void unboxBigInt(const ValueOperand& src, Register dest) {
     unboxNonDouble(src, dest, JSVAL_TYPE_BIGINT);
   }
   void unboxBigInt(const Operand& src, Register dest) {
     unboxNonDouble(src, dest, JSVAL_TYPE_BIGINT);
   }
+  void unboxBigInt(const Address& src, Register dest) {
+    unboxNonDouble(src, dest, JSVAL_TYPE_BIGINT);
+  }
 
   void unboxObject(const ValueOperand& src, Register dest) {
     unboxNonDouble(src, dest, JSVAL_TYPE_OBJECT);
   }
   void unboxObject(const Operand& src, Register dest) {
     unboxNonDouble(src, dest, JSVAL_TYPE_OBJECT);
   }
   void unboxObject(const Address& src, Register dest) {
--- a/js/src/wasm/WasmBuiltins.cpp
+++ b/js/src/wasm/WasmBuiltins.cpp
@@ -25,16 +25,17 @@
 
 #include "jit/AtomicOperations.h"
 #include "jit/InlinableNatives.h"
 #include "jit/MacroAssembler.h"
 #include "jit/Simulator.h"
 #include "threading/Mutex.h"
 #include "util/Memory.h"
 #include "util/Poison.h"
+#include "vm/BigIntType.h"
 #include "wasm/WasmInstance.h"
 #include "wasm/WasmStubs.h"
 #include "wasm/WasmTypes.h"
 
 #include "debugger/DebugAPI-inl.h"
 #include "vm/Stack-inl.h"
 
 using namespace js;
@@ -524,16 +525,32 @@ static int32_t CoerceInPlace_ToInt32(Val
     *rawVal = PoisonedObjectValue(0x42);
     return false;
   }
 
   *rawVal = Int32Value(i32);
   return true;
 }
 
+#ifdef ENABLE_WASM_BIGINT
+static int32_t CoerceInPlace_ToBigInt(Value* rawVal) {
+  JSContext* cx = TlsContext.get();
+
+  RootedValue val(cx, *rawVal);
+  BigInt* bi = ToBigInt(cx, val);
+  if (!bi) {
+    *rawVal = PoisonedObjectValue(0x43);
+    return false;
+  }
+
+  *rawVal = BigIntValue(bi);
+  return true;
+}
+#endif
+
 static int32_t CoerceInPlace_ToNumber(Value* rawVal) {
   JSContext* cx = TlsContext.get();
 
   double dbl;
   RootedValue val(cx, *rawVal);
   if (!ToNumber(cx, val, &dbl)) {
     *rawVal = PoisonedObjectValue(0x42);
     return false;
@@ -590,25 +607,47 @@ static int32_t CoerceInPlace_JitEntry(in
           RootedAnyRef result(cx, AnyRef::null());
           if (!BoxAnyRef(cx, arg, &result)) {
             return false;
           }
           argv[i].setObject(*result.get().asJSObject());
         }
         break;
       }
+#ifdef ENABLE_WASM_BIGINT
+      case ValType::I64: {
+        // In this case we store a BigInt value as there is no value type
+        // corresponding directly to an I64. The conversion to I64 happens
+        // in the JIT entry stub.
+        BigInt* bigint = ToBigInt(cx, arg);
+        if (!bigint) {
+          return false;
+        }
+        argv[i] = BigIntValue(bigint);
+        break;
+      }
+#endif
       default: {
         MOZ_CRASH("unexpected input argument in CoerceInPlace_JitEntry");
       }
     }
   }
 
   return true;
 }
 
+#ifdef ENABLE_WASM_BIGINT
+// Allocate a BigInt without GC, corresponds to the similar VMFunction.
+static BigInt* AllocateBigInt() {
+  JSContext* cx = TlsContext.get();
+
+  return js::Allocate<BigInt, NoGC>(cx);
+}
+#endif
+
 static int64_t DivI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi,
                       uint32_t y_lo) {
   int64_t x = ((uint64_t)x_hi << 32) + x_lo;
   int64_t y = ((uint64_t)y_hi << 32) + y_lo;
   MOZ_ASSERT(x != INT64_MIN || y != -1);
   MOZ_ASSERT(y != 0);
   return x / y;
 }
@@ -769,28 +808,38 @@ void* wasm::AddressOf(SymbolicAddress im
     case SymbolicAddress::CallImport_AnyRef:
       *abiType = MakeABIFunctionType(
           ArgType_Int32,
           {ArgType_General, ArgType_Int32, ArgType_Int32, ArgType_General});
       return FuncCast(Instance::callImport_anyref, *abiType);
     case SymbolicAddress::CoerceInPlace_ToInt32:
       *abiType = Args_General1;
       return FuncCast(CoerceInPlace_ToInt32, *abiType);
+#ifdef ENABLE_WASM_BIGINT
+    case SymbolicAddress::CoerceInPlace_ToBigInt:
+      *abiType = Args_General1;
+      return FuncCast(CoerceInPlace_ToBigInt, *abiType);
+#endif
     case SymbolicAddress::CoerceInPlace_ToNumber:
       *abiType = Args_General1;
       return FuncCast(CoerceInPlace_ToNumber, *abiType);
     case SymbolicAddress::CoerceInPlace_JitEntry:
       *abiType = Args_General3;
       return FuncCast(CoerceInPlace_JitEntry, *abiType);
     case SymbolicAddress::ToInt32:
       *abiType = Args_Int_Double;
       return FuncCast<int32_t(double)>(JS::ToInt32, *abiType);
     case SymbolicAddress::BoxValue_Anyref:
       *abiType = Args_General1;
       return FuncCast(BoxValue_Anyref, *abiType);
+#ifdef ENABLE_WASM_BIGINT
+    case SymbolicAddress::AllocateBigInt:
+      *abiType = Args_General0;
+      return FuncCast(AllocateBigInt, *abiType);
+#endif
     case SymbolicAddress::DivI64:
       *abiType = Args_General4;
       return FuncCast(DivI64, *abiType);
     case SymbolicAddress::UDivI64:
       *abiType = Args_General4;
       return FuncCast(UDivI64, *abiType);
     case SymbolicAddress::ModI64:
       *abiType = Args_General4;
@@ -1059,16 +1108,19 @@ bool wasm::NeedsBuiltinThunk(SymbolicAdd
     case SymbolicAddress::CallImport_Void:  // GenerateImportInterpExit
     case SymbolicAddress::CallImport_I32:
     case SymbolicAddress::CallImport_I64:
     case SymbolicAddress::CallImport_F64:
     case SymbolicAddress::CallImport_FuncRef:
     case SymbolicAddress::CallImport_AnyRef:
     case SymbolicAddress::CoerceInPlace_ToInt32:  // GenerateImportJitExit
     case SymbolicAddress::CoerceInPlace_ToNumber:
+#if defined(ENABLE_WASM_BIGINT)
+    case SymbolicAddress::CoerceInPlace_ToBigInt:
+#endif
     case SymbolicAddress::BoxValue_Anyref:
 #if defined(JS_CODEGEN_MIPS32)
     case SymbolicAddress::js_jit_gAtomic64Lock:
 #endif
 #ifdef WASM_CODEGEN_DEBUG
     case SymbolicAddress::PrintI32:
     case SymbolicAddress::PrintPtr:
     case SymbolicAddress::PrintF32:
@@ -1088,16 +1140,19 @@ bool wasm::NeedsBuiltinThunk(SymbolicAdd
     case SymbolicAddress::Uint64ToDouble:
     case SymbolicAddress::Uint64ToFloat32:
     case SymbolicAddress::Int64ToDouble:
     case SymbolicAddress::Int64ToFloat32:
 #if defined(JS_CODEGEN_ARM)
     case SymbolicAddress::aeabi_idivmod:
     case SymbolicAddress::aeabi_uidivmod:
 #endif
+#if defined(ENABLE_WASM_BIGINT)
+    case SymbolicAddress::AllocateBigInt:
+#endif
     case SymbolicAddress::ModD:
     case SymbolicAddress::SinD:
     case SymbolicAddress::CosD:
     case SymbolicAddress::TanD:
     case SymbolicAddress::ASinD:
     case SymbolicAddress::ACosD:
     case SymbolicAddress::ATanD:
     case SymbolicAddress::CeilD:
--- a/js/src/wasm/WasmFrameIter.cpp
+++ b/js/src/wasm/WasmFrameIter.cpp
@@ -1260,16 +1260,19 @@ static const char* ThunkedNativeToDescri
     case SymbolicAddress::CallImport_Void:
     case SymbolicAddress::CallImport_I32:
     case SymbolicAddress::CallImport_I64:
     case SymbolicAddress::CallImport_F64:
     case SymbolicAddress::CallImport_FuncRef:
     case SymbolicAddress::CallImport_AnyRef:
     case SymbolicAddress::CoerceInPlace_ToInt32:
     case SymbolicAddress::CoerceInPlace_ToNumber:
+#ifdef ENABLE_WASM_BIGINT
+    case SymbolicAddress::CoerceInPlace_ToBigInt:
+#endif
     case SymbolicAddress::BoxValue_Anyref:
       MOZ_ASSERT(!NeedsBuiltinThunk(func),
                  "not in sync with NeedsBuiltinThunk");
       break;
     case SymbolicAddress::ToInt32:
       return "call to asm.js native ToInt32 coercion (in wasm)";
     case SymbolicAddress::DivI64:
       return "call to native i64.div_s (in wasm)";
@@ -1296,16 +1299,20 @@ static const char* ThunkedNativeToDescri
     case SymbolicAddress::Int64ToFloat32:
       return "call to native f32.convert_s/i64 (in wasm)";
 #if defined(JS_CODEGEN_ARM)
     case SymbolicAddress::aeabi_idivmod:
       return "call to native i32.div_s (in wasm)";
     case SymbolicAddress::aeabi_uidivmod:
       return "call to native i32.div_u (in wasm)";
 #endif
+#ifdef ENABLE_WASM_BIGINT
+    case SymbolicAddress::AllocateBigInt:
+      return "call to native Allocate<BigInt, NoGC> (in wasm)";
+#endif
     case SymbolicAddress::ModD:
       return "call to asm.js native f64 % (mod)";
     case SymbolicAddress::SinD:
       return "call to asm.js native f64 Math.sin";
     case SymbolicAddress::CosD:
       return "call to asm.js native f64 Math.cos";
     case SymbolicAddress::TanD:
       return "call to asm.js native f64 Math.tan";
--- a/js/src/wasm/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -23,16 +23,17 @@
 #include "jit/AtomicOperations.h"
 #include "jit/Disassemble.h"
 #include "jit/InlinableNatives.h"
 #include "jit/JitCommon.h"
 #include "jit/JitRealm.h"
 #include "jit/JitScript.h"
 #include "util/StringBuffer.h"
 #include "util/Text.h"
+#include "vm/BigIntType.h"
 #include "wasm/WasmBuiltins.h"
 #include "wasm/WasmModule.h"
 #include "wasm/WasmStubs.h"
 
 #include "gc/StoreBuffer-inl.h"
 #include "vm/ArrayBufferObject-inl.h"
 #include "vm/JSObject-inl.h"
 
@@ -111,17 +112,17 @@ bool Instance::callImport(JSContext* cx,
 
   const FuncImport& fi = metadata(tier).funcImports[funcImportIndex];
 
   InvokeArgs args(cx);
   if (!args.init(cx, argc)) {
     return false;
   }
 
-  if (fi.funcType().hasI64ArgOrRet()) {
+  if (fi.funcType().hasI64ArgOrRet() && !HasI64BigIntSupport(cx)) {
     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
                              JSMSG_WASM_BAD_I64_TYPE);
     return false;
   }
 
   MOZ_ASSERT(fi.funcType().args().length() == argc);
   for (size_t i = 0; i < argc; i++) {
     switch (fi.funcType().args()[i].code()) {
@@ -135,20 +136,33 @@ bool Instance::callImport(JSContext* cx,
         args[i].set(JS::CanonicalizedDoubleValue(*(double*)&argv[i]));
         break;
       case ValType::FuncRef:
         args[i].set(UnboxFuncRef(FuncRef::fromCompiledCode(*(void**)&argv[i])));
         break;
       case ValType::AnyRef:
         args[i].set(UnboxAnyRef(AnyRef::fromCompiledCode(*(void**)&argv[i])));
         break;
+      case ValType::I64: {
+#ifdef ENABLE_WASM_BIGINT
+        MOZ_ASSERT(HasI64BigIntSupport(cx));
+        // If bi is manipulated other than test & storing, it would need
+        // to be rooted here.
+        BigInt* bi = BigInt::createFromInt64(cx, *(int64_t*)&argv[i]);
+        if (!bi) {
+          return false;
+        }
+        args[i].set(BigIntValue(bi));
+        break;
+#else
+        MOZ_CRASH("unhandled type in callImport");
+#endif
+      }
       case ValType::Ref:
         MOZ_CRASH("temporarily unsupported Ref type in callImport");
-      case ValType::I64:
-        MOZ_CRASH("unhandled type in callImport");
       case ValType::NullRef:
         MOZ_CRASH("NullRef not expressible");
     }
   }
 
   FuncImportTls& import = funcImportTls(fi);
   RootedFunction importFun(cx, import.fun);
   MOZ_ASSERT(cx->realm() == importFun->realm());
@@ -217,34 +231,41 @@ bool Instance::callImport(JSContext* cx,
           return true;
         }
         break;
       case ValType::F64:
         if (!argTypes->hasType(TypeSet::DoubleType())) {
           return true;
         }
         break;
+      case ValType::I64:
+#ifdef ENABLE_WASM_BIGINT
+        if (!argTypes->hasType(TypeSet::BigIntType())) {
+          return true;
+        }
+        break;
+#else
+        MOZ_CRASH("NYI");
+#endif
       case ValType::AnyRef:
         // We don't know what type the value will be, so we can't really check
         // whether the callee will accept it.  It doesn't make much sense to see
         // if the callee accepts all of the types an AnyRef might represent
         // because most callees will not have been exposed to all those types
         // and so we'll never pass the test.  Instead, we must use the callee's
         // arg-type-checking entry point, and not check anything here.  See
         // FuncType::jitExitRequiresArgCheck().
         break;
       case ValType::FuncRef:
         // We handle FuncRef as we do AnyRef: by checking the type dynamically
         // in the callee.  Code in the stubs layer must box up the FuncRef as a
         // Value.
         break;
       case ValType::Ref:
         MOZ_CRASH("case guarded above");
-      case ValType::I64:
-        MOZ_CRASH("NYI");
       case ValType::NullRef:
         MOZ_CRASH("NullRef not expressible");
     }
   }
 
   // These arguments will be filled with undefined at runtime by the
   // arguments rectifier: check that the imported function can handle
   // undefined there.
@@ -284,19 +305,29 @@ Instance::callImport_i32(Instance* insta
 
   return ToInt32(cx, rval, (int32_t*)argv);
 }
 
 /* static */ int32_t /* 0 to signal trap; 1 to signal OK */
 Instance::callImport_i64(Instance* instance, int32_t funcImportIndex,
                          int32_t argc, uint64_t* argv) {
   JSContext* cx = TlsContext.get();
+#ifdef ENABLE_WASM_BIGINT
+  RootedValue rval(cx);
+  if (!instance->callImport(cx, funcImportIndex, argc, argv, &rval)) {
+    return false;
+  }
+
+  JS_TRY_VAR_OR_RETURN_FALSE(cx, *argv, ToBigInt64(cx, rval));
+  return true;
+#else
   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
                            JSMSG_WASM_BAD_I64_TYPE);
   return false;
+#endif
 }
 
 /* static */ int32_t /* 0 to signal trap; 1 to signal OK */
 Instance::callImport_f64(Instance* instance, int32_t funcImportIndex,
                          int32_t argc, uint64_t* argv) {
   JSContext* cx = TlsContext.get();
   RootedValue rval(cx);
   if (!instance->callImport(cx, funcImportIndex, argc, argv, &rval)) {
@@ -1621,17 +1652,17 @@ bool Instance::callExport(JSContext* cx,
   }
 
   void* interpEntry;
   const FuncType* funcType;
   if (!GetInterpEntry(*this, funcIndex, args, &interpEntry, &funcType)) {
     return false;
   }
 
-  if (funcType->hasI64ArgOrRet()) {
+  if (funcType->hasI64ArgOrRet() && !HasI64BigIntSupport(cx)) {
     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
                              JSMSG_WASM_BAD_I64_TYPE);
     return false;
   }
 
   // The calling convention for an external call into wasm is to pass an
   // array of 16-byte values where each value contains either a coerced int32
   // (in the low word), or a double value (in the low dword) value, with the
@@ -1656,18 +1687,34 @@ bool Instance::callExport(JSContext* cx,
     switch (funcType->arg(i).code()) {
       case ValType::I32:
         if (!ToInt32(cx, v, (int32_t*)&exportArgs[i])) {
           return false;
         }
         DebugCodegen(DebugChannel::Function, "i32(%d) ",
                      *(int32_t*)&exportArgs[i]);
         break;
-      case ValType::I64:
+      case ValType::I64: {
+#ifdef ENABLE_WASM_BIGINT
+        MOZ_ASSERT(HasI64BigIntSupport(cx),
+                   "unexpected i64 flowing into callExport");
+        RootedBigInt bigint(cx, ToBigInt(cx, v));
+        if (!bigint) {
+          return false;
+        }
+
+        int64_t* res = (int64_t*)&exportArgs[i];
+        *res = BigInt::toInt64(bigint);
+        DebugCodegen(DebugChannel::Function, "i64(%" PRId64 ") ",
+                     *(int64_t*)&exportArgs[i]);
+        break;
+#else
         MOZ_CRASH("unexpected i64 flowing into callExport");
+#endif
+      }
       case ValType::F32:
         if (!RoundFloat32(cx, v, (float*)&exportArgs[i])) {
           return false;
         }
         DebugCodegen(DebugChannel::Function, "f32(%f) ",
                      *(float*)&exportArgs[i]);
         break;
       case ValType::F64:
@@ -1766,18 +1813,32 @@ bool Instance::callExport(JSContext* cx,
   } else {
     MOZ_ASSERT(results.length() == 1, "multi-value return unimplemented");
     DebugCodegen(DebugChannel::Function, ": ");
     switch (results[0].code()) {
       case ValType::I32:
         args.rval().set(Int32Value(*(int32_t*)retAddr));
         DebugCodegen(DebugChannel::Function, "i32(%d)", *(int32_t*)retAddr);
         break;
-      case ValType::I64:
+      case ValType::I64: {
+#ifdef ENABLE_WASM_BIGINT
+        MOZ_ASSERT(HasI64BigIntSupport(cx),
+                   "unexpected i64 flowing from callExport");
+        // If bi is manipulated other than test & storing, it would need
+        // to be rooted here.
+        BigInt* bi = BigInt::createFromInt64(cx, *(int64_t*)retAddr);
+        if (!bi) {
+          return false;
+        }
+        args.rval().set(BigIntValue(bi));
+        break;
+#else
         MOZ_CRASH("unexpected i64 flowing from callExport");
+#endif
+      }
       case ValType::F32:
         args.rval().set(NumberValue(*(float*)retAddr));
         DebugCodegen(DebugChannel::Function, "f32(%f)", *(float*)retAddr);
         break;
       case ValType::F64:
         args.rval().set(NumberValue(*(double*)retAddr));
         DebugCodegen(DebugChannel::Function, "f64(%lf)", *(double*)retAddr);
         break;
--- a/js/src/wasm/WasmStubs.cpp
+++ b/js/src/wasm/WasmStubs.cpp
@@ -825,16 +825,61 @@ static void GenerateJitEntryThrow(MacroA
   masm.loadPtr(Address(WasmTlsReg, offsetof(TlsData, instance)),
                ScratchIonEntry);
   masm.loadPtr(
       Address(ScratchIonEntry, Instance::offsetOfJSJitExceptionHandler()),
       ScratchIonEntry);
   masm.jump(ScratchIonEntry);
 }
 
+// Helper function for allocating a BigInt and initializing it from an I64
+// in GenerateJitEntry and GenerateImportInterpExit. The return result is
+// written to scratch.
+#ifdef ENABLE_WASM_BIGINT
+static void GenerateBigIntInitialization(MacroAssembler& masm, unsigned offset,
+                                         Register64 input, Register scratch,
+                                         const FuncExport* fe, Label* fail) {
+#  if JS_BITS_PER_WORD == 32
+  MOZ_ASSERT(input.low != scratch);
+  MOZ_ASSERT(input.high != scratch);
+#  else
+  MOZ_ASSERT(input.reg != scratch);
+#  endif
+
+  // We need to avoid clobbering other argument registers and the input.
+  AllocatableRegisterSet regs(RegisterSet::Volatile());
+  LiveRegisterSet save(regs.asLiveSet());
+  masm.PushRegsInMask(save);
+
+  unsigned frameSize =
+      StackDecrementForCall(ABIStackAlignment, masm.framePushed() + offset, 0);
+  masm.reserveStack(frameSize);
+  masm.assertStackAlignment(ABIStackAlignment);
+
+  // Needs to use a different call type depending on stub it's used from.
+  if (fe) {
+    CallSymbolicAddress(masm, !fe->hasEagerStubs(),
+                        SymbolicAddress::AllocateBigInt);
+  } else {
+    masm.call(SymbolicAddress::AllocateBigInt);
+  }
+  masm.storeCallPointerResult(scratch);
+  masm.branchTest32(Assembler::Zero, scratch, scratch, fail);
+
+  masm.assertStackAlignment(ABIStackAlignment);
+  masm.freeStack(frameSize);
+
+  LiveRegisterSet ignore;
+  ignore.add(scratch);
+  masm.PopRegsInMaskIgnore(save, ignore);
+
+  masm.initializeBigInt64(Scalar::BigInt64, scratch, input);
+}
+#endif
+
 // Generate a stub that enters wasm from a jit code caller via the jit ABI.
 //
 // ARM64 note: This does not save the PseudoStackPointer so we must be sure to
 // recompute it on every return path, be it normal return or exception return.
 // The JIT code we return to assumes it is correct.
 
 static bool GenerateJitEntry(MacroAssembler& masm, size_t funcExportIndex,
                              const FuncExport& fe, const Maybe<ImmPtr>& funcPtr,
@@ -936,16 +981,28 @@ static bool GenerateJitEntry(MacroAssemb
 
         masm.bind(&storeBack);
         {
           ScratchTagScopeRelease _(&tag);
           masm.storeValue(JSVAL_TYPE_INT32, scratchG, jitArgAddr);
         }
         break;
       }
+#ifdef ENABLE_WASM_BIGINT
+      case ValType::I64: {
+        ScratchTagScope tag(masm, scratchV);
+        masm.splitTagForTest(scratchV, tag);
+
+        // For BigInt inputs, just skip. Otherwise go to C++ for other
+        // types that require creating a new BigInt or erroring.
+        masm.branchTestBigInt(Assembler::NotEqual, tag, &oolCall);
+        masm.jump(&next);
+        break;
+      }
+#endif
       case ValType::F32:
       case ValType::F64: {
         // Note we can reuse the same code for f32/f64 here, since for the
         // case of f32, the conversion of f64 to f32 will happen in the
         // second loop.
         ScratchTagScope tag(masm, scratchV);
         masm.splitTagForTest(scratchV, tag);
 
@@ -1028,16 +1085,42 @@ static bool GenerateJitEntry(MacroAssemb
         Register target = isStackArg ? ScratchIonEntry : iter->gpr();
         masm.unboxInt32(argv, target);
         GenPrintIsize(DebugChannel::Function, masm, target);
         if (isStackArg) {
           masm.storePtr(target, Address(sp, iter->offsetFromArgBase()));
         }
         break;
       }
+#ifdef ENABLE_WASM_BIGINT
+      case MIRType::Int64: {
+        // The coercion has provided a BigInt value by this point, which
+        // we need to convert to an I64 here.
+        if (isStackArg) {
+          Address dst(sp, iter->offsetFromArgBase());
+          Register src = scratchV.payloadOrValueReg();
+#  if JS_BITS_PER_WORD == 64
+          Register64 scratch64(scratchG);
+#  else
+          Register64 scratch64(scratchG, ABINonArgReg3);
+#  endif
+          masm.unboxBigInt(argv, src);
+          masm.loadBigInt64(src, scratch64);
+          GenPrintI64(DebugChannel::Function, masm, scratch64);
+          masm.store64(scratch64, dst);
+        } else {
+          Register src = scratchG;
+          Register64 target = iter->gpr64();
+          masm.unboxBigInt(argv, src);
+          masm.loadBigInt64(src, target);
+          GenPrintI64(DebugChannel::Function, masm, target);
+        }
+        break;
+      }
+#endif
       case MIRType::Float32: {
         FloatRegister target = isStackArg ? ABINonArgDoubleReg : iter->fpu();
         masm.unboxDouble(argv, ABINonArgDoubleReg);
         masm.convertDoubleToFloat32(ABINonArgDoubleReg, target);
         GenPrintF32(DebugChannel::Function, masm, target.asSingle());
         if (isStackArg) {
           masm.storeFloat32(target, Address(sp, iter->offsetFromArgBase()));
         }
@@ -1112,31 +1195,40 @@ static bool GenerateJitEntry(MacroAssemb
       }
       case ValType::F64: {
         masm.canonicalizeDouble(ReturnDoubleReg);
         GenPrintF64(DebugChannel::Function, masm, ReturnDoubleReg);
         ScratchDoubleScope fpscratch(masm);
         masm.boxDouble(ReturnDoubleReg, JSReturnOperand, fpscratch);
         break;
       }
+      case ValType::I64: {
+#ifdef ENABLE_WASM_BIGINT
+        GenPrintI64(DebugChannel::Function, masm, ReturnReg64);
+        GenerateBigIntInitialization(masm, 0, ReturnReg64, scratchG, &fe,
+                                     &exception);
+        masm.boxNonDouble(JSVAL_TYPE_BIGINT, scratchG, JSReturnOperand);
+        break;
+#else
+        MOZ_CRASH("unexpected return type when calling from ion to wasm");
+#endif
+      }
       case ValType::FuncRef:
         // For FuncRef use the AnyRef path for now, since that will work.
       case ValType::AnyRef: {
         // Per comment above, the call may have clobbered the Tls register, so
         // reload since unboxing will need it.
         GenerateJitEntryLoadTls(masm, /* frameSize */ 0);
         UnboxAnyrefIntoValueReg(masm, WasmTlsReg, ReturnReg, JSReturnOperand,
                                 WasmJitEntryReturnScratch);
         break;
       }
       case ValType::Ref:
         MOZ_CRASH("returning reference in jitentry NYI");
         break;
-      case ValType::I64:
-        MOZ_CRASH("unexpected return type when calling from ion to wasm");
       case ValType::NullRef:
         MOZ_CRASH("NullRef not expressible");
     }
   }
 
   GenPrintf(DebugChannel::Function, masm, "\n");
 
   MOZ_ASSERT(masm.framePushed() == 0);
@@ -1461,25 +1553,28 @@ static void StackCopy(MacroAssembler& ma
     MOZ_CRASH("StackCopy: unexpected type");
   }
 }
 
 typedef bool ToValue;
 
 // Note, when toValue is true then this may destroy the values in incoming
 // argument registers as a result of Spectre mitigation.
-static void FillArgumentArrayForExit(MacroAssembler& masm, Register tls,
-                                     unsigned funcImportIndex,
-                                     const ValTypeVector& args,
-                                     unsigned argOffset,
-                                     unsigned offsetToCallerStackArgs,
-                                     Register scratch, Register scratch2,
-                                     ToValue toValue) {
+static void FillArgumentArrayForExit(
+    MacroAssembler& masm, Register tls, unsigned funcImportIndex,
+    const ValTypeVector& args, unsigned argOffset,
+    unsigned offsetToCallerStackArgs, Register scratch, Register scratch2,
+    Register scratch3, ToValue toValue, Label* throwLabel) {
   MOZ_ASSERT(scratch != scratch2);
+  MOZ_ASSERT(scratch != scratch3);
+  MOZ_ASSERT(scratch2 != scratch3);
 
+  // This loop does not root the values that are being constructed in
+  // for the arguments. Allocations that are generated by code either
+  // in the loop or called from it should be NoGC allocations.
   GenPrintf(DebugChannel::Import, masm, "wasm-import[%u]; arguments ",
             funcImportIndex);
 
   for (ABIArgValTypeIter i(args); !i.done(); i++) {
     Address dst(masm.getStackPointer(), argOffset + i.index() * sizeof(Value));
 
     MIRType type = i.mirType();
     switch (i->kind()) {
@@ -1487,21 +1582,29 @@ static void FillArgumentArrayForExit(Mac
         if (type == MIRType::Int32) {
           GenPrintIsize(DebugChannel::Import, masm, i->gpr());
           if (toValue) {
             masm.storeValue(JSVAL_TYPE_INT32, i->gpr(), dst);
           } else {
             masm.store32(i->gpr(), dst);
           }
         } else if (type == MIRType::Int64) {
-          // We can't box int64 into Values (yet).
+          GenPrintI64(DebugChannel::Import, masm, i->gpr64());
+
           if (toValue) {
+#ifdef ENABLE_WASM_BIGINT
+            GenerateBigIntInitialization(masm, offsetToCallerStackArgs,
+                                         i->gpr64(), scratch, nullptr,
+                                         throwLabel);
+            masm.storeValue(JSVAL_TYPE_BIGINT, scratch, dst);
+#else
+            // Should be unreachable as I64 cases should error earlier.
             masm.breakpoint();
+#endif
           } else {
-            GenPrintI64(DebugChannel::Import, masm, i->gpr64());
             masm.store64(i->gpr64(), dst);
           }
         } else if (type == MIRType::RefOrNull) {
           if (toValue) {
             // This works also for FuncRef because it is distinguishable from
             // a boxed AnyRef.
             masm.movePtr(i->gpr(), scratch2);
             UnboxAnyrefIntoValue(masm, tls, scratch2, dst, scratch);
@@ -1512,17 +1615,29 @@ static void FillArgumentArrayForExit(Mac
         } else {
           MOZ_CRASH("FillArgumentArrayForExit, ABIArg::GPR: unexpected type");
         }
         break;
 #ifdef JS_CODEGEN_REGISTER_PAIR
       case ABIArg::GPR_PAIR:
         if (type == MIRType::Int64) {
           GenPrintI64(DebugChannel::Import, masm, i->gpr64());
-          masm.store64(i->gpr64(), dst);
+
+          if (toValue) {
+#  ifdef ENABLE_WASM_BIGINT
+            GenerateBigIntInitialization(masm, offsetToCallerStackArgs,
+                                         i->gpr64(), scratch, nullptr,
+                                         throwLabel);
+            masm.storeValue(JSVAL_TYPE_BIGINT, scratch, dst);
+#  else
+            masm.breakpoint();
+#  endif
+          } else {
+            masm.store64(i->gpr64(), dst);
+          }
         } else {
           MOZ_CRASH("wasm uses hardfp for function calls.");
         }
         break;
 #endif
       case ABIArg::FPU: {
         MOZ_ASSERT(IsFloatingPointType(type));
         FloatRegister srcReg = i->fpu();
@@ -1559,18 +1674,31 @@ static void FillArgumentArrayForExit(Mac
         Address src(masm.getStackPointer(),
                     offsetToCallerStackArgs + i->offsetFromArgBase());
         if (toValue) {
           if (type == MIRType::Int32) {
             masm.load32(src, scratch);
             GenPrintIsize(DebugChannel::Import, masm, scratch);
             masm.storeValue(JSVAL_TYPE_INT32, scratch, dst);
           } else if (type == MIRType::Int64) {
-            // We can't box int64 into Values (yet).
+#ifdef ENABLE_WASM_BIGINT
+#  if JS_BITS_PER_WORD == 64
+            Register64 scratch64(scratch2);
+#  else
+            Register64 scratch64(scratch2, scratch3);
+#  endif
+            masm.load64(src, scratch64);
+            GenPrintI64(DebugChannel::Import, masm, scratch64);
+            GenerateBigIntInitialization(masm, offsetToCallerStackArgs,
+                                         scratch64, scratch, nullptr,
+                                         throwLabel);
+            masm.storeValue(JSVAL_TYPE_BIGINT, scratch, dst);
+#else
             masm.breakpoint();
+#endif
           } else if (type == MIRType::RefOrNull) {
             // This works also for FuncRef because it is distinguishable from a
             // boxed AnyRef.
             masm.loadPtr(src, scratch);
             UnboxAnyrefIntoValue(masm, tls, scratch, dst, scratch2);
           } else if (IsFloatingPointType(type)) {
             ScratchDoubleScope dscratch(masm);
             FloatRegister fscratch = dscratch.asSingle();
@@ -1723,19 +1851,23 @@ static bool GenerateImportInterpExit(Mac
 
   GenerateExitPrologue(masm, framePushed, ExitReason::Fixed::ImportInterp,
                        offsets);
 
   // Fill the argument array.
   unsigned offsetToCallerStackArgs = sizeof(Frame) + masm.framePushed();
   Register scratch = ABINonArgReturnReg0;
   Register scratch2 = ABINonArgReturnReg1;
-  FillArgumentArrayForExit(
-      masm, WasmTlsReg, funcImportIndex, fi.funcType().args(), argOffset,
-      offsetToCallerStackArgs, scratch, scratch2, ToValue(false));
+  // The scratch3 reg does not need to be non-volatile, but has to be
+  // distinct from scratch & scratch2.
+  Register scratch3 = ABINonVolatileReg;
+  FillArgumentArrayForExit(masm, WasmTlsReg, funcImportIndex,
+                           fi.funcType().args(), argOffset,
+                           offsetToCallerStackArgs, scratch, scratch2, scratch3,
+                           ToValue(false), throwLabel);
 
   // Prepare the arguments for the call to Instance::callImport_*.
   ABIArgMIRTypeIter i(invokeArgTypes);
 
   // argument 0: Instance*
   Address instancePtr(WasmTlsReg, offsetof(TlsData, instance));
   if (i->kind() == ABIArg::GPR) {
     masm.loadPtr(instancePtr, i->gpr());
@@ -1794,17 +1926,21 @@ static bool GenerateImportInterpExit(Mac
         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
         masm.load32(argv, ReturnReg);
         GenPrintf(DebugChannel::Import, masm, "wasm-import[%u]; returns ",
                   funcImportIndex);
         GenPrintIsize(DebugChannel::Import, masm, ReturnReg);
         break;
       case ValType::I64:
         masm.call(SymbolicAddress::CallImport_I64);
-        masm.jump(throwLabel);
+        masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
+        masm.load64(argv, ReturnReg64);
+        GenPrintf(DebugChannel::Import, masm, "wasm-import[%u]; returns ",
+                  funcImportIndex);
+        GenPrintI64(DebugChannel::Import, masm, ReturnReg64);
         break;
       case ValType::F32:
         masm.call(SymbolicAddress::CallImport_F64);
         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
         masm.loadDouble(argv, ReturnDoubleReg);
         masm.convertDoubleToFloat32(ReturnDoubleReg, ReturnFloat32Reg);
         GenPrintf(DebugChannel::Import, masm, "wasm-import[%u]; returns ",
                   funcImportIndex);
@@ -1920,19 +2056,23 @@ static bool GenerateImportJitExit(MacroA
   masm.storeValue(UndefinedValue(), Address(masm.getStackPointer(), argOffset));
   argOffset += sizeof(Value);
 
   // 5. Fill the arguments.
   unsigned offsetToCallerStackArgs =
       jitFramePushed + sizeof(Frame) + frameAlignExtra;
   Register scratch = ABINonArgReturnReg1;   // Repeatedly clobbered
   Register scratch2 = ABINonArgReturnReg0;  // Reused as callee below
-  FillArgumentArrayForExit(
-      masm, WasmTlsReg, funcImportIndex, fi.funcType().args(), argOffset,
-      offsetToCallerStackArgs, scratch, scratch2, ToValue(true));
+  // The scratch3 reg does not need to be non-volatile, but has to be
+  // distinct from scratch & scratch2.
+  Register scratch3 = ABINonVolatileReg;
+  FillArgumentArrayForExit(masm, WasmTlsReg, funcImportIndex,
+                           fi.funcType().args(), argOffset,
+                           offsetToCallerStackArgs, scratch, scratch2, scratch3,
+                           ToValue(true), throwLabel);
   argOffset += fi.funcType().args().length() * sizeof(Value);
   MOZ_ASSERT(argOffset == sizeOfThisAndArgs + sizeOfPreFrame + frameAlignExtra);
 
   // 2. Callee, part 2 -- now that the register is free, set up the callee.
   Register callee = ABINonArgReturnReg0;  // Live until call
 
   // 2.1. Get JSFunction callee.
   masm.loadWasmGlobalPtr(fi.tlsDataOffset() + offsetof(FuncImportTls, fun),
@@ -2030,17 +2170,23 @@ static bool GenerateImportJitExit(MacroA
     MOZ_ASSERT(results.length() == 1, "multi-value return unimplemented");
     switch (results[0].code()) {
       case ValType::I32:
         masm.truncateValueToInt32(JSReturnOperand, ReturnDoubleReg, ReturnReg,
                                   &oolConvert);
         GenPrintIsize(DebugChannel::Import, masm, ReturnReg);
         break;
       case ValType::I64:
+#ifdef ENABLE_WASM_BIGINT
+        // No fastpath for now, go immediately to ool case
+        masm.jump(&oolConvert);
+#else
+        // Unreachable as callImport should not call the stub.
         masm.breakpoint();
+#endif
         break;
       case ValType::F32:
         masm.convertValueToFloat(JSReturnOperand, ReturnFloat32Reg,
                                  &oolConvert);
         GenPrintF32(DebugChannel::Import, masm, ReturnFloat32Reg);
         break;
       case ValType::F64:
         masm.convertValueToDouble(JSReturnOperand, ReturnDoubleReg,
@@ -2125,16 +2271,26 @@ static bool GenerateImportJitExit(MacroA
       MOZ_ASSERT(results.length() == 1, "multi-value return unimplemented");
       switch (results[0].code()) {
         case ValType::I32:
           masm.call(SymbolicAddress::CoerceInPlace_ToInt32);
           masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
           masm.unboxInt32(Address(masm.getStackPointer(), offsetToCoerceArgv),
                           ReturnReg);
           break;
+#ifdef ENABLE_WASM_BIGINT
+        case ValType::I64: {
+          masm.call(SymbolicAddress::CoerceInPlace_ToBigInt);
+          masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
+          Address argv(masm.getStackPointer(), offsetToCoerceArgv);
+          masm.unboxBigInt(argv, scratch);
+          masm.loadBigInt64(scratch, ReturnReg64);
+          break;
+        }
+#endif
         case ValType::F64:
         case ValType::F32:
           masm.call(SymbolicAddress::CoerceInPlace_ToNumber);
           masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
           masm.unboxDouble(Address(masm.getStackPointer(), offsetToCoerceArgv),
                            ReturnDoubleReg);
           if (results[0].code() == ValType::F32) {
             masm.convertDoubleToFloat32(ReturnDoubleReg, ReturnFloat32Reg);
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -1952,16 +1952,20 @@ enum class SymbolicAddress {
   CallImport_I32,
   CallImport_I64,
   CallImport_F64,
   CallImport_FuncRef,
   CallImport_AnyRef,
   CoerceInPlace_ToInt32,
   CoerceInPlace_ToNumber,
   CoerceInPlace_JitEntry,
+#ifdef ENABLE_WASM_BIGINT
+  CoerceInPlace_ToBigInt,
+  AllocateBigInt,
+#endif
   BoxValue_Anyref,
   DivI64,
   UDivI64,
   ModI64,
   UModI64,
   TruncateDoubleToInt64,
   TruncateDoubleToUint64,
   SaturatingTruncateDoubleToInt64,