Backed out changeset 455f2bae38f3 (bug 1620197) for sm bustage on call-run.js. CLOSED TREE
authorBrindusan Cristian <cbrindusan@mozilla.com>
Tue, 10 Mar 2020 01:01:08 +0200
changeset 517683 edff668bc00397d485aeb91cfe456783e26c212b
parent 517682 cea91b0cc4f9ee4273ed2f77467389bf95df5d0e
child 517684 5e26d9f924c5d01a92c054e12aafe871202fa816
push id37199
push useropoprus@mozilla.com
push dateTue, 10 Mar 2020 03:43:44 +0000
treeherdermozilla-central@6f21f98dcfcd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1620197
milestone76.0a1
backs out455f2bae38f3dec9d30a1aa9025de7b7199f39f7
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out changeset 455f2bae38f3 (bug 1620197) for sm bustage on call-run.js. CLOSED TREE
js/src/jit-test/etc/wasm-spec-tests.patch
js/src/jit-test/tests/wasm/multi-value/call-js.js
js/src/jit-test/tests/wasm/multi-value/call-ref.js
js/src/jit-test/tests/wasm/multi-value/call-run.js
js/src/jit-test/tests/wasm/multi-value/call-validate.js
js/src/jit-test/tests/wasm/spec/func.wast.js
js/src/js.msg
js/src/wasm/WasmBaselineCompile.cpp
js/src/wasm/WasmCode.h
js/src/wasm/WasmConstants.h
js/src/wasm/WasmInstance.cpp
js/src/wasm/WasmOpIter.h
js/src/wasm/WasmStubs.cpp
js/src/wasm/WasmTextToBinary.cpp
js/src/wasm/WasmTypes.h
js/src/wasm/WasmValidate.cpp
testing/web-platform/meta/wasm/jsapi/constructor/multi-value.any.js.ini
--- a/js/src/jit-test/etc/wasm-spec-tests.patch
+++ b/js/src/jit-test/etc/wasm-spec-tests.patch
@@ -12,30 +12,8 @@ index 15518b4d7f60..9184b46010a4 100644
 +// upstream, skip these tests.
 +if (wasmMultiValueEnabled()) {
 +    quit();
 +}
 +
  // type.wast:52
  assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x86\x80\x80\x80\x00\x01\x60\x00\x02\x7f\x7f");
  
-diff --git a/js/src/jit-test/tests/wasm/spec/func.wast.js b/js/src/jit-test/tests/wasm/spec/func.wast.js
-index 9e3c1f09429f..dc86e5aceb3c 100644
---- a/js/src/jit-test/tests/wasm/spec/func.wast.js
-+++ b/js/src/jit-test/tests/wasm/spec/func.wast.js
-@@ -278,11 +278,17 @@ assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x85\x80\x80\x80\x00\x01\x60
- // func.wast:484
- assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x86\x80\x80\x80\x00\x01\x60\x02\x7c\x7e\x00\x03\x82\x80\x80\x80\x00\x01\x00\x0a\x8b\x80\x80\x80\x00\x01\x85\x80\x80\x80\x00\x00\x20\x01\x9a\x0b");
- 
-+// These two tests check that function types returning more than 2
-+// results are invalid, but with multi-value of course that's no longer
-+// the case.  Until the feature fully lands and we can import from
-+// upstream, skip these tests.
-+if (!wasmMultiValueEnabled()) {
- // func.wast:492
- assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x86\x80\x80\x80\x00\x01\x60\x00\x02\x7f\x7f\x03\x82\x80\x80\x80\x00\x01\x00\x0a\x89\x80\x80\x80\x00\x01\x83\x80\x80\x80\x00\x00\x00\x0b");
- 
- // func.wast:496
- assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x86\x80\x80\x80\x00\x01\x60\x00\x02\x7f\x7f\x03\x82\x80\x80\x80\x00\x01\x00\x0a\x89\x80\x80\x80\x00\x01\x83\x80\x80\x80\x00\x00\x00\x0b");
-+}
- 
- // func.wast:505
- assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x85\x80\x80\x80\x00\x01\x60\x00\x01\x7f\x03\x82\x80\x80\x80\x00\x01\x00\x0a\x88\x80\x80\x80\x00\x01\x82\x80\x80\x80\x00\x00\x0b");
deleted file mode 100644
--- a/js/src/jit-test/tests/wasm/multi-value/call-js.js
+++ /dev/null
@@ -1,23 +0,0 @@
-
-function expectRunFailure(text, pattern, imports) {
-    let instance = wasmEvalText(text, imports);
-    assertErrorMessage(() => instance.exports.run(),
-		       TypeError,
-                       pattern);
-}
-
-expectRunFailure(`
-  (module
-    (import "env" "f" (func $f (result i32 i32)))
-    (func (export "run") (result i32)
-      (call $f)
-      i32.sub))`,
-                 /calling JavaScript functions with multiple results from WebAssembly not yet implemented/,
-                 { env: { f: () => [52, 10] } });
-
-expectRunFailure(`
-  (module
-    (func (export "run") (result i32 i32)
-      (i32.const 52)
-      (i32.const 10)))`,
-                 /calling WebAssembly functions with multiple results from JavaScript not yet implemented/);
deleted file mode 100644
--- a/js/src/jit-test/tests/wasm/multi-value/call-ref.js
+++ /dev/null
@@ -1,31 +0,0 @@
-// |jit-test| skip-if: !wasmReftypesEnabled()
-
-let counter = 0;
-let i = new WebAssembly.Instance(
-    new WebAssembly.Module(wasmTextToBinary(`
-      (module
-        (func $boxNextInt (import "imports" "boxNextInt")
-          (result anyref))
-        (func $unboxInt (import "imports" "unboxInt")
-          (param anyref) (result i32))
-
-        (func $boxNextTwoInts (result anyref anyref)
-          call $boxNextInt
-          call $boxNextInt)
-
-        (func $unboxTwoInts (param anyref anyref) (result i32 i32)
-          local.get 0
-          call $unboxInt
-          local.get 1
-          call $unboxInt)
-
-        (func $addNextTwoInts (export "addNextTwoInts") (result i32)
-          call $boxNextTwoInts
-          call $unboxTwoInts
-          i32.add))`)),
-    {imports: {boxNextInt: () => ({val: counter++}),
-               unboxInt: box => box.val}});
-
-for (let n = 0; n < 200000; n += 2) {
-    assertEq(i.exports.addNextTwoInts(), n * 2 + 1);
-}
deleted file mode 100644
--- a/js/src/jit-test/tests/wasm/multi-value/call-run.js
+++ /dev/null
@@ -1,120 +0,0 @@
-wasmFullPass(`
-  (module
-    (func (result i32 i32)
-      (i32.const 52)
-      (i32.const 10))
-    (func (export "run") (result i32)
-      (call 0)
-      i32.sub))`,
-            42);
-
-wasmFullPass(`
-  (module
-    (func (param i32 i32) (result i32 i32)
-      (local.get 0)
-      (local.get 1))
-    (func (export "run") (result i32)
-      (i32.const 52)
-      (i32.const 10)
-      (call 0)
-      i32.sub))`,
-            42);
-
-wasmFullPass(`
-  (module
-    (func (param i32 i32 i32 i32 i32
-                 i32 i32 i32 i32 i32)
-          (result i32 i32)
-      (local.get 8)
-      (local.get 9))
-    (func (export "run") (result i32)
-      (i32.const 0)
-      (i32.const 1)
-      (i32.const 2)
-      (i32.const 3)
-      (i32.const 4)
-      (i32.const 5)
-      (i32.const 6)
-      (i32.const 7)
-      (i32.const 52)
-      (i32.const 10)
-      (call 0)
-      i32.sub))`,
-            42);
-
-wasmFullPass(`
-  (module
-    (func (param i32 i32 i32 i32 i32
-                 i32 i32 i32 i32 i32)
-          (result i32 i32)
-      (local i32 i32 i32 i32)
-      (local.get 8)
-      (local.get 9))
-    (func (export "run") (result i32)
-      (i32.const 0)
-      (i32.const 1)
-      (i32.const 2)
-      (i32.const 3)
-      (i32.const 4)
-      (i32.const 5)
-      (i32.const 6)
-      (i32.const 7)
-      (i32.const 52)
-      (i32.const 10)
-      (call 0)
-      i32.sub))`,
-            42);
-
-wasmFullPass(`
-  (module
-    (func (param i32 i32 i32 i32 i32
-                 i32 i32 i32 i32 i32)
-          (result i32 i32 i32)
-      (local i32 i32 i32 i32)
-      (local.get 7)
-      (local.get 8)
-      (local.get 9))
-    (func (export "run") (result i32)
-      (i32.const 0)
-      (i32.const 1)
-      (i32.const 2)
-      (i32.const 3)
-      (i32.const 4)
-      (i32.const 5)
-      (i32.const 6)
-      (i32.const 7)
-      (i32.const 52)
-      (i32.const 10)
-      (call 0)
-      i32.sub
-      i32.sub))`,
-            -35);
-
-wasmFullPass(`
-  (module
-    (func (param i32 i64 i32 i64 i32
-                 i64 i32 i64 i32 i64)
-          (result i64 i32 i64)
-      (local i32 i64 i32 i64)
-      (local.get 7)
-      (local.get 8)
-      (local.get 9))
-    (func (export "run") (result i32)
-      (i32.const 0)
-      (i64.const 1)
-      (i32.const 2)
-      (i64.const 3)
-      (i32.const 4)
-      (i64.const 5)
-      (i32.const 6)
-      (i64.const 7)
-      (i32.const 52)
-      (i64.const 10)
-      (call 0)
-      i32.wrap_i64
-      i32.sub
-      i64.extend_i32_s
-      i64.sub
-      i32.wrap_i64))`,
-            -35);
-
deleted file mode 100644
--- a/js/src/jit-test/tests/wasm/multi-value/call-validate.js
+++ /dev/null
@@ -1,47 +0,0 @@
-wasmValidateText(`
-  (module
-    (func (result i32 i32)
-      (i32.const 32)
-      (i32.const 10)))`);
-
-wasmValidateText(`
-  (module
-    (type $t (func (result i32 i32)))
-    (func (type $t)
-      (i32.const 32)
-      (i32.const 10)))`);
-
-wasmValidateText(`
-  (module
-    (func (result i32 i32)
-      (block (result i32 i32)
-        (i32.const 32)
-        (i32.const 10))))`);
-
-wasmValidateText(`
-  (module
-    (func $return-2 (result i32 i32)
-      (i32.const 32)
-      (i32.const 10))
-    (func $tail-call (result i32 i32)
-      (call 0)))`);
-
-wasmValidateText(`
-  (module
-    (func $return-2 (result i32 i32)
-      (i32.const 32)
-      (i32.const 10))
-    (func $add (result i32)
-      (call 0)
-      i32.add))`);
-
-wasmValidateText(`
-  (module
-    (func $return-2 (param i32 i32) (result i32 i32)
-      (local.get 0)
-      (local.get 1))
-    (func (export "run") (result i32)
-      (i32.const 32)
-      (i32.const 10)
-      (call 0)
-      i32.add))`);
--- a/js/src/jit-test/tests/wasm/spec/func.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/func.wast.js
@@ -273,27 +273,21 @@ assert_invalid("\x00\x61\x73\x6d\x01\x00
 assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x86\x80\x80\x80\x00\x01\x60\x01\x7f\x01\x7e\x03\x82\x80\x80\x80\x00\x01\x00\x0a\x8a\x80\x80\x80\x00\x01\x84\x80\x80\x80\x00\x00\x20\x00\x0b");
 
 // func.wast:480
 assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x85\x80\x80\x80\x00\x01\x60\x01\x7d\x00\x03\x82\x80\x80\x80\x00\x01\x00\x0a\x8b\x80\x80\x80\x00\x01\x85\x80\x80\x80\x00\x00\x20\x00\x45\x0b");
 
 // func.wast:484
 assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x86\x80\x80\x80\x00\x01\x60\x02\x7c\x7e\x00\x03\x82\x80\x80\x80\x00\x01\x00\x0a\x8b\x80\x80\x80\x00\x01\x85\x80\x80\x80\x00\x00\x20\x01\x9a\x0b");
 
-// These two tests check that function types returning more than 2
-// results are invalid, but with multi-value of course that's no longer
-// the case.  Until the feature fully lands and we can import from
-// upstream, skip these tests.
-if (!wasmMultiValueEnabled()) {
 // func.wast:492
 assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x86\x80\x80\x80\x00\x01\x60\x00\x02\x7f\x7f\x03\x82\x80\x80\x80\x00\x01\x00\x0a\x89\x80\x80\x80\x00\x01\x83\x80\x80\x80\x00\x00\x00\x0b");
 
 // func.wast:496
 assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x86\x80\x80\x80\x00\x01\x60\x00\x02\x7f\x7f\x03\x82\x80\x80\x80\x00\x01\x00\x0a\x89\x80\x80\x80\x00\x01\x83\x80\x80\x80\x00\x00\x00\x0b");
-}
 
 // func.wast:505
 assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x85\x80\x80\x80\x00\x01\x60\x00\x01\x7f\x03\x82\x80\x80\x80\x00\x01\x00\x0a\x88\x80\x80\x80\x00\x01\x82\x80\x80\x80\x00\x00\x0b");
 
 // func.wast:509
 assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x85\x80\x80\x80\x00\x01\x60\x00\x01\x7e\x03\x82\x80\x80\x80\x00\x01\x00\x0a\x88\x80\x80\x80\x00\x01\x82\x80\x80\x80\x00\x00\x0b");
 
 // func.wast:513
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -430,18 +430,16 @@ MSG_DEF(JSMSG_WASM_BAD_IMPORT_FIELD,   1
 MSG_DEF(JSMSG_WASM_BAD_FUNCREF_VALUE,  0, JSEXN_TYPEERR,     "can only pass WebAssembly exported functions to funcref")
 MSG_DEF(JSMSG_WASM_BAD_I64_TYPE,       0, JSEXN_TYPEERR,     "cannot pass i64 to or from JS")
 MSG_DEF(JSMSG_WASM_BAD_GLOBAL_TYPE,    0, JSEXN_TYPEERR,     "bad type for a WebAssembly.Global")
 MSG_DEF(JSMSG_WASM_NO_TRANSFER,        0, JSEXN_TYPEERR,     "cannot transfer WebAssembly/asm.js ArrayBuffer")
 MSG_DEF(JSMSG_WASM_TEXT_FAIL,          1, JSEXN_SYNTAXERR,   "wasm text error: {0}")
 MSG_DEF(JSMSG_WASM_MISSING_MAXIMUM,    0, JSEXN_TYPEERR,     "'shared' is true but maximum is not specified")
 MSG_DEF(JSMSG_WASM_GLOBAL_IMMUTABLE,   0, JSEXN_TYPEERR,     "can't set value of immutable global")
 MSG_DEF(JSMSG_WASM_NULL_REQUIRED,      0, JSEXN_TYPEERR,     "nullref requires a null value")
-MSG_DEF(JSMSG_WASM_MULTIPLE_RESULT_EXIT_UNIMPLEMENTED, 0, JSEXN_TYPEERR, "calling JavaScript functions with multiple results from WebAssembly not yet implemented")
-MSG_DEF(JSMSG_WASM_MULTIPLE_RESULT_ENTRY_UNIMPLEMENTED, 0, JSEXN_TYPEERR, "calling WebAssembly functions with multiple results from JavaScript not yet implemented")
 
 // Proxy
 MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE,   2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value")
 MSG_DEF(JSMSG_BAD_GETPROTOTYPEOF_TRAP_RETURN,0,JSEXN_TYPEERR,"proxy getPrototypeOf handler returned a non-object, non-null value")
 MSG_DEF(JSMSG_INCONSISTENT_GETPROTOTYPEOF_TRAP,0,JSEXN_TYPEERR,"proxy getPrototypeOf handler didn't return the target object's prototype")
 MSG_DEF(JSMSG_PROXY_SETPROTOTYPEOF_RETURNED_FALSE, 0, JSEXN_TYPEERR, "proxy setPrototypeOf handler returned false")
 MSG_DEF(JSMSG_PROXY_ISEXTENSIBLE_RETURNED_FALSE,0,JSEXN_TYPEERR,"proxy isExtensible handler must return the same extensibility as target")
 MSG_DEF(JSMSG_INCONSISTENT_SETPROTOTYPEOF_TRAP,0,JSEXN_TYPEERR,"proxy setPrototypeOf handler returned true, even though the target's prototype is immutable because the target is non-extensible")
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -1733,17 +1733,17 @@ class BaseStackFrame final : public Base
   }
 
   // An outgoing stack result area pointer is for stack results of callees of
   // the function being compiled.
   void computeOutgoingStackResultAreaPtr(const StackResultsLoc& results,
                                          RegPtr dest) {
     MOZ_ASSERT(results.height() <= masm.framePushed());
     uint32_t offsetFromSP = masm.framePushed() - results.height();
-    masm.moveStackPtrTo(dest);
+    masm.movePtr(AsRegister(sp_), dest);
     if (offsetFromSP) {
       masm.addPtr(Imm32(offsetFromSP), dest);
     }
   }
 
  private:
   // Offset off of sp_ for a local with offset `offset` from Frame.
   int32_t localOffset(int32_t offset) { return masm.framePushed() - offset; }
--- a/js/src/wasm/WasmCode.h
+++ b/js/src/wasm/WasmCode.h
@@ -234,17 +234,16 @@ class FuncExport {
   uint32_t eagerInterpEntryOffset() const {
     MOZ_ASSERT(pod.eagerInterpEntryOffset_ != UINT32_MAX);
     MOZ_ASSERT(hasEagerStubs());
     return pod.eagerInterpEntryOffset_;
   }
 
   bool canHaveJitEntry() const {
     return !funcType_.temporarilyUnsupportedReftypeForEntry() &&
-           !funcType_.temporarilyUnsupportedResultCountForEntry() &&
            JitOptions.enableWasmJitEntry;
   }
 
   bool clone(const FuncExport& src) {
     mozilla::PodAssign(&pod, &src.pod);
     return funcType_.clone(src.funcType_);
   }
 
--- a/js/src/wasm/WasmConstants.h
+++ b/js/src/wasm/WasmConstants.h
@@ -598,16 +598,21 @@ static const unsigned MaxFunctionBytes =
 
 // These limits pertain to our WebAssembly implementation only.
 
 static const unsigned MaxTableInitialLength = 10000000;
 static const unsigned MaxBrTableElems = 1000000;
 static const unsigned MaxMemoryInitialPages = 16384;
 static const unsigned MaxCodeSectionBytes = MaxModuleBytes;
 
+// FIXME: Temporary limit to function result counts.  Replace with MaxResults:
+// bug 1585909.
+
+static const unsigned MaxFuncResults = 1;
+
 // A magic value of the FramePointer to indicate after a return to the entry
 // stub that an exception has been caught and that we should throw.
 
 static const unsigned FailFP = 0xbad;
 
 // Asserted by Decoder::readVarU32.
 
 static const unsigned MaxVarU32DecodedBytes = 5;
--- a/js/src/wasm/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -118,22 +118,16 @@ bool Instance::callImport(JSContext* cx,
   }
 
   if (fi.funcType().hasI64ArgOrRet() && !HasI64BigIntSupport(cx)) {
     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
                              JSMSG_WASM_BAD_I64_TYPE);
     return false;
   }
 
-  if (fi.funcType().temporarilyUnsupportedResultCountForExit()) {
-    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
-                             JSMSG_WASM_MULTIPLE_RESULT_EXIT_UNIMPLEMENTED);
-    return false;
-  }
-
   MOZ_ASSERT(fi.funcType().args().length() == argc);
   for (size_t i = 0; i < argc; i++) {
     switch (fi.funcType().args()[i].kind()) {
       case ValType::I32: {
         args[i].set(Int32Value(*(int32_t*)&argv[i]));
         break;
       }
       case ValType::I64: {
@@ -1699,22 +1693,16 @@ bool Instance::callExport(JSContext* cx,
   }
 
   if (funcType->hasI64ArgOrRet() && !HasI64BigIntSupport(cx)) {
     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
                              JSMSG_WASM_BAD_I64_TYPE);
     return false;
   }
 
-  if (funcType->temporarilyUnsupportedResultCountForEntry()) {
-    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
-                             JSMSG_WASM_MULTIPLE_RESULT_ENTRY_UNIMPLEMENTED);
-    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
   // coercions specified by the wasm signature. The external entry point
   // unpacks this array into the system-ABI-specified registers and stack
   // memory and then calls into the internal entry point. The return value is
   // stored in the first element of the array (which, therefore, must have
   // length >= 1).
--- a/js/src/wasm/WasmOpIter.h
+++ b/js/src/wasm/WasmOpIter.h
@@ -2056,16 +2056,22 @@ inline bool OpIter<Policy>::readCallIndi
   }
 
   if (!env_.types[*funcTypeIndex].isFuncType()) {
     return fail("expected signature type");
   }
 
   const FuncType& funcType = env_.types[*funcTypeIndex].funcType();
 
+  // FIXME: Remove this check when full multi-value function returns land.
+  // Bug 1585909.
+  if (funcType.results().length() > MaxFuncResults) {
+    return fail("too many returns in signature");
+  }
+
 #ifdef WASM_PRIVATE_REFTYPES
   if (env_.tables[*tableIndex].importedOrExported &&
       funcType.exposesTypeIndex()) {
     return fail("cannot expose indexed reference type");
   }
 #endif
 
   if (!popCallArgs(funcType.args(), argValues)) {
@@ -2120,16 +2126,22 @@ inline bool OpIter<Policy>::readOldCallI
   }
 
   if (!env_.types[*funcTypeIndex].isFuncType()) {
     return fail("expected signature type");
   }
 
   const FuncType& funcType = env_.types[*funcTypeIndex].funcType();
 
+  // FIXME: Remove this check when full multi-value function returns land.
+  // Bug 1585909.
+  if (funcType.results().length() > MaxFuncResults) {
+    return fail("too many returns in signature");
+  }
+
   if (!popCallArgs(funcType.args(), argValues)) {
     return false;
   }
 
   if (!popWithType(ValType::I32, callee)) {
     return false;
   }
 
--- a/js/src/wasm/WasmStubs.cpp
+++ b/js/src/wasm/WasmStubs.cpp
@@ -636,22 +636,16 @@ static bool GenerateInterpEntry(MacroAss
   // WasmPush updates framePushed() unlike pushReturnAddress(), but that's
   // cancelled by the setFramePushed() below.
   WasmPush(masm, lr);
 #  else
   MOZ_CRASH("Implement this");
 #  endif
 #endif
 
-  if (fe.funcType().temporarilyUnsupportedResultCountForEntry()) {
-    // Unreachable as the Instance::callExport doesn't let us get here.
-    masm.breakpoint();
-    return FinishOffsets(masm, offsets);
-  }
-
   // Save all caller non-volatile registers before we clobber them here and in
   // the wasm callee (which does not preserve non-volatile registers).
   masm.setFramePushed(0);
   masm.PushRegsInMask(NonVolatileRegs);
   MOZ_ASSERT(masm.framePushed() == NonVolatileRegsPushSize);
 
   // Put the 'argv' argument into a non-argument/return/TLS register so that
   // we can use 'argv' while we fill in the arguments for the wasm callee.
@@ -1607,21 +1601,23 @@ static void FillArgumentArrayForExit(
   // 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);
 
   ArgTypeVector args(funcType);
   for (ABIArgIter i(args); !i.done(); i++) {
+    if (args.isSyntheticStackResultPointerArg(i.index())) {
+      MOZ_CRASH("Exit to function returning multiple values unimplemented");
+    }
+
     Address dst(masm.getStackPointer(), argOffset + i.index() * sizeof(Value));
 
     MIRType type = i.mirType();
-    MOZ_ASSERT(args.isSyntheticStackResultPointerArg(i.index()) ==
-               (type == MIRType::StackResults));
     switch (i->kind()) {
       case ABIArg::GPR:
         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);
@@ -1647,20 +1643,16 @@ static void FillArgumentArrayForExit(
             // This works also for FuncRef because it is distinguishable from
             // a boxed AnyRef.
             masm.movePtr(i->gpr(), scratch2);
             UnboxAnyrefIntoValue(masm, tls, scratch2, dst, scratch);
           } else {
             GenPrintPtr(DebugChannel::Import, masm, i->gpr());
             masm.storePtr(i->gpr(), dst);
           }
-        } else if (type == MIRType::StackResults) {
-          MOZ_ASSERT(!toValue, "Multi-result exit to JIT unimplemented");
-          GenPrintPtr(DebugChannel::Import, masm, i->gpr());
-          masm.storePtr(i->gpr(), dst);
         } 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());
@@ -1881,19 +1873,18 @@ static bool GenerateImportInterpExit(Mac
   MOZ_ALWAYS_TRUE(invokeArgTypes.append(typeArray, ArrayLength(typeArray)));
 
   // At the point of the call, the stack layout shall be (sp grows to the left):
   //  | stack args | padding | argv[] | padding | retaddr | caller stack args |
   // The padding between stack args and argv ensures that argv is aligned. The
   // padding between argv and retaddr ensures that sp is aligned.
   unsigned argOffset =
       AlignBytes(StackArgBytes(invokeArgTypes), sizeof(double));
-  // The abiArgCount includes a stack result pointer argument if needed.
-  unsigned abiArgCount = ArgTypeVector(fi.funcType()).length();
-  unsigned argBytes = std::max<size_t>(1, abiArgCount * sizeof(Value));
+  unsigned argBytes =
+      std::max<size_t>(1, fi.funcType().args().length()) * sizeof(Value);
   unsigned framePushed =
       StackDecrementForCall(ABIStackAlignment,
                             sizeof(Frame),  // pushed by prologue
                             argOffset + argBytes);
 
   GenerateExitPrologue(masm, framePushed, ExitReason::Fixed::ImportInterp,
                        offsets);
 
@@ -1950,32 +1941,26 @@ static bool GenerateImportInterpExit(Mac
     masm.storePtr(scratch,
                   Address(masm.getStackPointer(), i->offsetFromArgBase()));
   }
   i++;
   MOZ_ASSERT(i.done());
 
   // Make the call, test whether it succeeded, and extract the return value.
   AssertStackAlignment(masm, ABIStackAlignment);
-  ResultType resultType = ResultType::Vector(fi.funcType().results());
-  ValType registerResultType;
-  for (ABIResultIter iter(resultType); !iter.done(); iter.next()) {
-    if (iter.cur().inRegister()) {
-      MOZ_ASSERT(!registerResultType.isValid());
-      registerResultType = iter.cur().type();
-    }
-  }
-  if (!registerResultType.isValid()) {
+  const ValTypeVector& results = fi.funcType().results();
+  if (results.length() == 0) {
     masm.call(SymbolicAddress::CallImport_Void);
     masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
     GenPrintf(DebugChannel::Import, masm, "wasm-import[%u]; returns ",
               funcImportIndex);
     GenPrintf(DebugChannel::Import, masm, "void");
   } else {
-    switch (registerResultType.kind()) {
+    MOZ_ASSERT(results.length() == 1, "multi-value return unimplemented");
+    switch (results[0].kind()) {
       case ValType::I32:
         masm.call(SymbolicAddress::CallImport_I32);
         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;
@@ -2000,17 +1985,17 @@ static bool GenerateImportInterpExit(Mac
         masm.call(SymbolicAddress::CallImport_F64);
         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
         masm.loadDouble(argv, ReturnDoubleReg);
         GenPrintf(DebugChannel::Import, masm, "wasm-import[%u]; returns ",
                   funcImportIndex);
         GenPrintF64(DebugChannel::Import, masm, ReturnDoubleReg);
         break;
       case ValType::Ref:
-        switch (registerResultType.refTypeKind()) {
+        switch (results[0].refTypeKind()) {
           case RefType::Func:
             masm.call(SymbolicAddress::CallImport_FuncRef);
             masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg,
                               throwLabel);
             masm.loadPtr(argv, ReturnReg);
             GenPrintf(DebugChannel::Import, masm, "wasm-import[%u]; returns ",
                       funcImportIndex);
             GenPrintPtr(DebugChannel::Import, masm, ReturnReg);
@@ -2703,21 +2688,16 @@ bool wasm::GenerateEntryStubs(MacroAssem
                                offsets)) {
     return false;
   }
 
   if (isAsmJS || fe.funcType().temporarilyUnsupportedReftypeForEntry()) {
     return true;
   }
 
-  // Returning multiple values to JS not yet implemented (see bug 1595031).
-  if (fe.funcType().temporarilyUnsupportedResultCountForEntry()) {
-    return true;
-  }
-
   if (!GenerateJitEntry(masm, funcExportIndex, fe, callee, bigIntEnabled,
                         &offsets)) {
     return false;
   }
   if (!codeRanges->emplaceBack(CodeRange::JitEntry, fe.funcIndex(), offsets)) {
     return false;
   }
 
@@ -2752,22 +2732,16 @@ bool wasm::GenerateStubs(const ModuleEnv
                                       interpOffsets)) {
       return false;
     }
 
     if (fi.funcType().temporarilyUnsupportedReftypeForExit()) {
       continue;
     }
 
-    // Exit to JS returning multiple values not yet implemented (see bug
-    // 1595031).
-    if (fi.funcType().temporarilyUnsupportedResultCountForExit()) {
-      continue;
-    }
-
     JitExitOffsets jitOffsets;
     if (!GenerateImportJitExit(masm, fi, funcIndex, &throwLabel, &jitOffsets)) {
       return false;
     }
     if (!code->codeRanges.emplaceBack(funcIndex, jitOffsets)) {
       return false;
     }
   }
--- a/js/src/wasm/WasmTextToBinary.cpp
+++ b/js/src/wasm/WasmTextToBinary.cpp
@@ -2459,28 +2459,36 @@ static bool ParseFuncSig(WasmParseContex
       return false;
     }
   }
 
   *funcType = AstFuncType(std::move(args), std::move(results));
   return true;
 }
 
+// For ParseAnonFuncType: allow more than one results.
+enum class MultiResult { False, True };
+
 // This guarantees that the ref has an index when we return and won't need to be
 // resolved later.  If `withLookahead` then whatever comes after the type does
 // not cause an error and is not consumed, and any number of results are
 // allowed.
 static bool ParseAnonFuncType(WasmParseContext& c, AstRef* ref,
+                              MultiResult multiResult,
                               WithLookahead withLookahead) {
   MOZ_ASSERT(ref->isInvalid());
 
   AstFuncType funcType(c.lifo);
   if (!ParseFuncSig(c, &funcType, withLookahead)) {
     return false;
   }
+  if (multiResult == MultiResult::False && funcType.results().length() > 1) {
+    c.ts.generateError(c.ts.peek(), "too many results", c.error);
+    return false;
+  }
   uint32_t funcTypeIndex;
   if (!c.module->declare(std::move(funcType), &funcTypeIndex)) {
     return false;
   }
   ref->setIndex(funcTypeIndex);
 
   return true;
 }
@@ -2512,17 +2520,17 @@ static bool ParseBlockType(WasmParseCont
     return false;
   }
   if (vt.isValid()) {
     type->setVoidToSingle(vt);
     return true;
   }
 
   AstRef t;
-  if (!ParseAnonFuncType(c, &t, WithLookahead::True)) {
+  if (!ParseAnonFuncType(c, &t, MultiResult::True, WithLookahead::True)) {
     return false;
   }
   const AstFuncType& ft = c.module->types()[t.index()]->asFuncType();
   if (ft.args().length() == 0 && ft.results().length() == 0) {
     // Nothing; `*type` is void and remains so
   } else if (ft.args().length() == 0 && ft.results().length() == 1) {
     type->setVoidToSingle(ft.results()[0]);
   } else {
@@ -4189,17 +4197,17 @@ static bool MaybeParseTypeUse(WasmParseC
   return true;
 }
 
 static bool ParseFuncType(WasmParseContext& c, AstRef* ref) {
   if (!MaybeParseTypeUse(c, ref)) {
     return false;
   }
   if (ref->isInvalid()) {
-    if (!ParseAnonFuncType(c, ref, WithLookahead::False)) {
+    if (!ParseAnonFuncType(c, ref, MultiResult::False, WithLookahead::False)) {
       return false;
     }
   }
   return true;
 }
 
 static bool ParseFunc(WasmParseContext& c) {
   AstValTypeVector vars(c.lifo);
@@ -4276,16 +4284,20 @@ static bool ParseFunc(WasmParseContext& 
         if (!ParseLocalOrParam(c, &locals, &args)) {
           return false;
         }
         break;
       case WasmToken::Result:
         if (!ParseValueTypeList(c, &results)) {
           return false;
         }
+        if (results.length() > 1) {
+          c.ts.generateError(token, "too many results", c.error);
+          return false;
+        }
         break;
       default:
         c.ts.unget(token);
         AstExpr* expr = ParseExprInsideParens(c);
         if (!expr || !body.append(expr)) {
           return false;
         }
         break;
@@ -4365,16 +4377,20 @@ static AstTypeDef* ParseTypeDef(WasmPars
   }
 
   AstTypeDef* type = nullptr;
   if (c.ts.getIf(WasmToken::Func)) {
     AstFuncType funcType(c.lifo);
     if (!ParseFuncSig(c, &funcType, WithLookahead::False)) {
       return nullptr;
     }
+    if (funcType.results().length() > 1) {
+      c.ts.generateError(c.ts.peek(), "too many results", c.error);
+      return nullptr;
+    }
     type = new (c.lifo) AstFuncType(name, std::move(funcType));
   } else if (c.ts.getIf(WasmToken::Struct)) {
     AstStructType st(c.lifo);
     if (!ParseStructFields(c, &st)) {
       return nullptr;
     }
 
     type = new (c.lifo) AstStructType(name, std::move(st));
@@ -4780,17 +4796,18 @@ static AstImport* ParseImport(WasmParseC
         return nullptr;
       }
     } else {
       c.ts.unget(openParen);
     }
   }
 
   if (funcTypeRef.isInvalid()) {
-    if (!ParseAnonFuncType(c, &funcTypeRef, WithLookahead::False)) {
+    if (!ParseAnonFuncType(c, &funcTypeRef, MultiResult::False,
+                           WithLookahead::False)) {
       return nullptr;
     }
   }
 
   return new (c.lifo)
       AstImport(name, moduleName.text(), fieldName.text(), funcTypeRef);
 }
 
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -1081,26 +1081,16 @@ class FuncType {
     }
     for (ValType result : results()) {
       if (result == ValType::I64) {
         return true;
       }
     }
     return false;
   }
-  // Entry from JS to wasm is currently unimplemented for functions that return
-  // multiple values.
-  bool temporarilyUnsupportedResultCountForEntry() const {
-    return results().length() > 1;
-  }
-  // Calls out from wasm to JS that return multiple values is currently
-  // unsupported.
-  bool temporarilyUnsupportedResultCountForExit() const {
-    return results().length() > 1;
-  }
   // For JS->wasm jit entries, AnyRef parameters and returns are allowed,
   // as are all reference types apart from TypeIndex.
   bool temporarilyUnsupportedReftypeForEntry() const {
     for (ValType arg : args()) {
       if (arg.isReference() && !arg.isAnyRef()) {
         return true;
       }
     }
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -1209,16 +1209,18 @@ static bool DecodeFunctionBodyExprs(cons
   MOZ_CRASH("unreachable");
 
 #undef CHECK
 }
 
 bool wasm::ValidateFunctionBody(const ModuleEnvironment& env,
                                 uint32_t funcIndex, uint32_t bodySize,
                                 Decoder& d) {
+  MOZ_ASSERT(env.funcTypes[funcIndex]->results().length() <= MaxFuncResults);
+
   ValTypeVector locals;
   if (!locals.appendAll(env.funcTypes[funcIndex]->args())) {
     return false;
   }
 
   const uint8_t* bodyBegin = d.currentPosition();
 
   if (!DecodeLocalEntries(d, env.types, env.refTypesEnabled(),
@@ -1583,16 +1585,22 @@ static bool DecodeSignatureIndex(Decoder
   }
 
   const TypeDef& def = types[*funcTypeIndex];
 
   if (!def.isFuncType()) {
     return d.fail("signature index references non-signature");
   }
 
+  // FIXME: Remove this check when full multi-value function returns land.
+  // Bug 1585909.
+  if (def.funcType().results().length() > MaxFuncResults) {
+    return d.fail("too many returns in signature");
+  }
+
   return true;
 }
 
 static bool DecodeLimits(Decoder& d, Limits* limits,
                          Shareable allowShared = Shareable::False) {
   uint8_t flags;
   if (!d.readFixedU8(&flags)) {
     return d.fail("expected flags");
--- a/testing/web-platform/meta/wasm/jsapi/constructor/multi-value.any.js.ini
+++ b/testing/web-platform/meta/wasm/jsapi/constructor/multi-value.any.js.ini
@@ -9,18 +9,17 @@
     expected: FAIL
 
 
 [multi-value.any.js]
   [multiple return values from wasm to js]
     expected: FAIL
 
   [multiple return values inside wasm]
-    expected:
-      if not nightly_build: FAIL
+    expected: FAIL
 
   [multiple return values from js to wasm]
     expected: FAIL
 
 
 [multi-value.any.worker.html]
   [multiple return values from wasm to js]
     expected: FAIL