WebAssembly JS API: check and test in-call / out-call values
authorjfbastien@apple.com <jfbastien@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 3 Jan 2017 20:24:36 +0000 (20:24 +0000)
committerjfbastien@apple.com <jfbastien@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 3 Jan 2017 20:24:36 +0000 (20:24 +0000)
https://bugs.webkit.org/show_bug.cgi?id=164876
<rdar://problem/29844107>

Reviewed by Saam Barati.

JSTests:

* wasm.yaml:
* wasm/assert.js: add an assert for NaN comparison
* wasm/fuzz/export-function.js: Added. Generate random wasm export
signatures, and call them with random parameters.
(const.paramExporter):
(const.setBuffer):
(const.types.generate):
(generate):
* wasm/js-api/export-arity.js: Added.
(const.paramExporter): Test that mismatched arities when JS calls
wasm follow the defined semantics: i32 is 0, f32 / f64 are NaN.
https://github.com/WebAssembly/design/blob/master/JS.md#exported-function-exotic-objects
* wasm/js-api/export-void-is-undef.js: Added. Test that "void"
wasm functions return "undefined" in JS.

Source/JavaScriptCore:

* wasm/WasmBinding.cpp:
(JSC::Wasm::wasmToJs): fix the wasm -> JS call coercions for f32 /
f64 which the assotiated tests inadvertently tripped on: the
previous code wasn't correctly performing JSValue boxing for
"double" values. This change is slightly involved because it
requires two scratch registers to materialize the
`DoubleEncodeOffset` value. This change therefore reorganizes the
code to first generate traps, then handle all integers (freeing
all GPRs), and then all the floating-point values.
* wasm/js/WebAssemblyFunction.cpp:
(JSC::callWebAssemblyFunction): Implement the defined semantics
for mismatched arities when JS calls wasm:
https://github.com/WebAssembly/design/blob/master/JS.md#exported-function-exotic-objects
  - i32 is 0, f32 / f64 are NaN.
  - wasm functions which return "void" are "undefined" in JS.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@210244 268f45cc-cd09-0410-ab3c-d52691b4dbfc

JSTests/ChangeLog
JSTests/wasm.yaml
JSTests/wasm/Builder.js
JSTests/wasm/assert.js
JSTests/wasm/fuzz/export-function.js [new file with mode: 0644]
JSTests/wasm/js-api/export-arity.js [new file with mode: 0644]
JSTests/wasm/js-api/export-void-is-undef.js [new file with mode: 0644]
JSTests/wasm/self-test/test_BuilderJSON.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/wasm/WasmBinding.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp

index 3d48e6d..632ee29 100644 (file)
@@ -1,3 +1,26 @@
+2017-01-03  JF Bastien  <jfbastien@apple.com>
+
+        WebAssembly JS API: check and test in-call / out-call values
+        https://bugs.webkit.org/show_bug.cgi?id=164876
+        <rdar://problem/29844107>
+
+        Reviewed by Saam Barati.
+
+        * wasm.yaml:
+        * wasm/assert.js: add an assert for NaN comparison
+        * wasm/fuzz/export-function.js: Added. Generate random wasm export
+        signatures, and call them with random parameters.
+        (const.paramExporter):
+        (const.setBuffer):
+        (const.types.generate):
+        (generate):
+        * wasm/js-api/export-arity.js: Added.
+        (const.paramExporter): Test that mismatched arities when JS calls
+        wasm follow the defined semantics: i32 is 0, f32 / f64 are NaN.
+        https://github.com/WebAssembly/design/blob/master/JS.md#exported-function-exotic-objects
+        * wasm/js-api/export-void-is-undef.js: Added. Test that "void"
+        wasm functions return "undefined" in JS.
+
 2017-01-02  JF Bastien  <jfbastien@apple.com>
 
         WebAssembly: handle and optimize wasm export → wasm import calls
index 77fd443..5737ba1 100644 (file)
@@ -27,6 +27,8 @@
   cmd: runWebAssembly
 - path: wasm/function-tests
   cmd: runWebAssembly
+- path: wasm/fuzz
+  cmd: runWebAssembly
 
 - path: wasm/spec-tests/address.wast.js
   cmd: runWebAssemblySpecTest :normal
index 26ba66e..1cc8a47 100644 (file)
@@ -752,7 +752,13 @@ export default class Builder {
             preamble: this._preamble,
             section: this._sections
         };
-        return JSON.stringify(obj);
+        // JSON.stringify serializes -0.0 as 0.0.
+        const replacer = (key, value) => {
+            if (value === 0.0 && 1.0 / value === -Infinity)
+                return "NEGATIVE_ZERO";
+            return value;
+        };
+        return JSON.stringify(obj, replacer);
     }
     AsmJS() {
         "use asm"; // For speed.
index 4d28720..cd92986 100644 (file)
@@ -72,11 +72,19 @@ export const falsy = (v, msg) => {
 };
 
 export const eq = (lhs, rhs, msg) => {
+    if (typeof lhs !== typeof rhs)
+        _fail(`Not the same: "${lhs}" and "${rhs}"`, msg);
     if (Array.isArray(lhs) && Array.isArray(rhs) && (lhs.length === rhs.length)) {
         for (let i = 0; i !== lhs.length; ++i)
             eq(lhs[i], rhs[i], msg);
-    } else if (lhs !== rhs)
+    } else if (lhs !== rhs) {
+        if (typeof lhs === "number" && isNaN(lhs) && isNaN(rhs))
+            return;
         _fail(`Not the same: "${lhs}" and "${rhs}"`, msg);
+    } else {
+        if (typeof lhs === "number" && (1.0 / lhs !== 1.0 / rhs)) // Distinguish -0.0 from 0.0.
+            _fail(`Not the same: "${lhs}" and "${rhs}"`, msg);
+    }
 };
 
 const canonicalizeI32 = (number) => {
diff --git a/JSTests/wasm/fuzz/export-function.js b/JSTests/wasm/fuzz/export-function.js
new file mode 100644 (file)
index 0000000..e4e286d
--- /dev/null
@@ -0,0 +1,80 @@
+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+const numRandomIterations = 128;
+
+// Generate wasm export functions of arity [0, max), using each valid
+// WebAssembly type as parameters. Make sure this number is high enough to force
+// non-register calls.
+const maxArities = 64;
+
+// Calls a "check" function for each parameter received.
+const paramExporter = (params, returnedParam, imports) => {
+    const ret = params.length ? params[returnedParam] : "void";
+    let builder = (new Builder())
+        .Type().End()
+        .Import()
+            .Function("imp", "checki32", { params: ["i32"] })
+            .Function("imp", "checkf32", { params: ["f32"] })
+            .Function("imp", "checkf64", { params: ["f64"] })
+        .End()
+        .Function().End()
+        .Export()
+            .Function("func")
+        .End()
+        .Code()
+            .Function("func", { params: params, ret: ret });
+    for (let i = 0; i < params.length; ++i) {
+        builder = builder.GetLocal(i);
+        switch (params[i]) {
+        case "i32": builder = builder.Call(0); break;
+        case "f32": builder = builder.Call(1); break;
+        case "f64": builder = builder.Call(2); break;
+        default: throw new Error(`Unexpected type`);
+        }
+    }
+    if (ret !== "void")
+        builder = builder.GetLocal(returnedParam);
+    builder = builder.Return().End().End();
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin);
+    return new WebAssembly.Instance(module, { imp: imports });
+};
+
+var buffer = new ArrayBuffer(8);
+var viewi16 = new Int16Array(buffer);
+var viewi32 = new Int32Array(buffer);
+var viewf32 = new Float32Array(buffer);
+var viewf64 = new Float64Array(buffer);
+const random16 = () => (Math.random() * (1 + 0xffff)) | 0;
+const setBuffer = () => {
+    for (let i = 0; i < 4; ++i)
+        viewi16[i] = random16();
+};
+const types = [
+    { type: "i32", generate: () => { setBuffer(); return viewi32[0]; } },
+    // i64 isn't supported.
+    { type: "f32", generate: () => { setBuffer(); return viewf32[0]; } },
+    { type: "f64", generate: () => { setBuffer(); return viewf64[0]; } },
+];
+
+for (let iteration = 0; iteration < numRandomIterations; ++iteration) {
+    const arity = (Math.random() * (maxArities + 1)) | 0;
+    let params = [];
+    let args = [];
+    for (let a = 0; a < arity; ++a) {
+        const type =( Math.random() * types.length) | 0;
+        params.push(types[type].type);
+        args.push(types[type].generate());
+    }
+    let numChecked = 0;
+    const imports = {
+        checki32: v => assert.eq(v, args[numChecked++]),
+        checkf32: v => assert.eq(v, args[numChecked++]),
+        checkf64: v => assert.eq(v, args[numChecked++]),
+    };
+    const returnedParam = (Math.random() * params.length) | 0;
+    const instance = paramExporter(params, returnedParam, imports);
+    const result = instance.exports.func(...args);
+    assert.eq(result, args.length ? args[returnedParam] : undefined);
+}
diff --git a/JSTests/wasm/js-api/export-arity.js b/JSTests/wasm/js-api/export-arity.js
new file mode 100644 (file)
index 0000000..4157953
--- /dev/null
@@ -0,0 +1,65 @@
+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+// Generate wasm export functions of arity [0, max), and call each of these
+// export functions from JS with [0, max) parameters, for each valid WebAssembly
+// type. Make sure this number is high enough to force non-register calls.
+const maxArities = 64;
+
+const paramExporter = (numParams, paramType, imports) => {
+    let builder = (new Builder())
+        .Type().End()
+        .Import()
+            .Function("imp", "check", { params: [paramType] })
+        .End()
+        .Function().End()
+        .Export()
+            .Function("func")
+        .End()
+        .Code()
+          .Function("func", { params: Array(numParams).fill(paramType) });
+    for (let i = 0; i < numParams; ++i)
+        builder = builder.GetLocal(i).Call(0); // Call the import for each received parameter.
+    builder = builder.Return().End().End();
+    const bin = builder.WebAssembly().get();
+    const module = new WebAssembly.Module(bin);
+    return new WebAssembly.Instance(module, { imp: imports });
+};
+
+const types = [
+    { type: "i32", value: 42, defaultWhenArityMismatch: 0 },
+    // i64 isn't supported.
+    { type: "f32", value: 32.0, defaultWhenArityMismatch: NaN },
+    { type: "f64", value: 64.0, defaultWhenArityMismatch: NaN },
+];
+
+for (let type of types) {
+    for (let wasmArity = 0; wasmArity < maxArities; ++wasmArity) {
+        let numParamsCallingWith = undefined;
+        let numChecked = 0;
+        const check = value => {
+            assert.isNumber(value);
+            if (numParamsCallingWith <= wasmArity) {
+                if (numChecked < numParamsCallingWith)
+                    assert.eq(value, type.value);
+                else
+                    assert.eq(value, type.defaultWhenArityMismatch);
+            }  else {
+                if (numChecked < wasmArity)
+                    assert.eq(value, type.value);
+                else
+                    assert.eq(value, type.defaultWhenArityMismatch);
+            }
+            ++numChecked;
+        };
+        const instance = paramExporter(wasmArity, type.type, { check: check });
+        for (let callerArity = 0; callerArity < maxArities; ++callerArity) {
+            numParamsCallingWith = callerArity;
+            const params = Array(callerArity).fill(type.value);
+            const result = instance.exports.func(...params);
+            assert.isUndef(result);
+            assert.eq(numChecked, wasmArity); // check() should be called as many times as the wasm function's arity.
+            numChecked = 0; // Reset the check counter for each arity iteration.
+        }
+    }
+}
diff --git a/JSTests/wasm/js-api/export-void-is-undef.js b/JSTests/wasm/js-api/export-void-is-undef.js
new file mode 100644 (file)
index 0000000..f9c93ac
--- /dev/null
@@ -0,0 +1,19 @@
+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+let builder = (new Builder())
+    .Type().End()
+    .Function().End()
+    .Export()
+        .Function("func")
+    .End()
+    .Code()
+    .Function("func", { params: [] })
+        .Return()
+    .End()
+.End();
+const bin = builder.WebAssembly().get();
+const module = new WebAssembly.Module(bin);
+const instance = new WebAssembly.Instance(module);
+
+assert.isUndef(instance.exports.func());
index 17db263..623f0f2 100644 (file)
@@ -510,7 +510,7 @@ const assertOpThrows = (opFn, message) => {
         assert.eq(j.section[1].data[0].code[0].name, "f32.const");
         assert.eq(j.section[1].data[0].code[0].arguments.length, 0);
         assert.eq(j.section[1].data[0].code[0].immediates.length, 1);
-        assert.eq(j.section[1].data[0].code[0].immediates[0], c);
+        assert.eq(j.section[1].data[0].code[0].immediates[0] === "NEGATIVE_ZERO" ? -0.0 : j.section[1].data[0].code[0].immediates[0], c);
     }
 })();
 
@@ -526,7 +526,7 @@ const assertOpThrows = (opFn, message) => {
         assert.eq(j.section[1].data[0].code[0].name, "f64.const");
         assert.eq(j.section[1].data[0].code[0].arguments.length, 0);
         assert.eq(j.section[1].data[0].code[0].immediates.length, 1);
-        assert.eq(j.section[1].data[0].code[0].immediates[0], c);
+        assert.eq(j.section[1].data[0].code[0].immediates[0] === "NEGATIVE_ZERO" ? -0.0 : j.section[1].data[0].code[0].immediates[0], c);
     }
 })();
 
index 3146ccb..21184c1 100644 (file)
@@ -1,3 +1,27 @@
+2017-01-03  JF Bastien  <jfbastien@apple.com>
+
+        WebAssembly JS API: check and test in-call / out-call values
+        https://bugs.webkit.org/show_bug.cgi?id=164876
+        <rdar://problem/29844107>
+
+        Reviewed by Saam Barati.
+
+        * wasm/WasmBinding.cpp:
+        (JSC::Wasm::wasmToJs): fix the wasm -> JS call coercions for f32 /
+        f64 which the assotiated tests inadvertently tripped on: the
+        previous code wasn't correctly performing JSValue boxing for
+        "double" values. This change is slightly involved because it
+        requires two scratch registers to materialize the
+        `DoubleEncodeOffset` value. This change therefore reorganizes the
+        code to first generate traps, then handle all integers (freeing
+        all GPRs), and then all the floating-point values.
+        * wasm/js/WebAssemblyFunction.cpp:
+        (JSC::callWebAssemblyFunction): Implement the defined semantics
+        for mismatched arities when JS calls wasm:
+        https://github.com/WebAssembly/design/blob/master/JS.md#exported-function-exotic-objects
+          - i32 is 0, f32 / f64 are NaN.
+          - wasm functions which return "void" are "undefined" in JS.
+
 2017-01-03  Per Arne Vollan  <pvollan@apple.com>
 
         [Win] jsc.exe sometimes never exits.
index 99702ec..66adae9 100644 (file)
@@ -74,72 +74,144 @@ static MacroAssemblerCodeRef wasmToJs(VM* vm, Bag<CallLinkInfo>& callLinkInfos,
     const unsigned stackOffset = WTF::roundUpToMultipleOf(stackAlignmentBytes(), numberOfBytesForCall);
     jit.subPtr(MacroAssembler::TrustedImm32(stackOffset), MacroAssembler::stackPointerRegister);
     JIT::Address calleeFrame = CCallHelpers::Address(MacroAssembler::stackPointerRegister, -static_cast<ptrdiff_t>(sizeof(CallerFrameAndPC)));
-
-    // FIXME make this a loop which switches on Signature if there are many arguments on the stack. It'll otherwise be huge for huge signatures. https://bugs.webkit.org/show_bug.cgi?id=165547
-    unsigned marshalledGPRs = 0;
-    unsigned marshalledFPRs = 0;
-    unsigned calleeFrameOffset = CallFrameSlot::firstArgument * static_cast<int>(sizeof(Register));
-    unsigned frOffset = CallFrameSlot::firstArgument * static_cast<int>(sizeof(Register));
+    
     for (unsigned argNum = 0; argNum < argCount; ++argNum) {
         Type argType = signature->argument(argNum);
         switch (argType) {
         case Void:
         case Func:
         case Anyfunc:
-        case I64:
-            // FIXME: Figure out the correct behavior here. I suspect we want such a stub to throw an exception immediately
+        case I64: {
+            // FIXME: Figure out the correct behavior here. I suspect we want such a stub to throw an exception immediately.
             // if called. https://bugs.webkit.org/show_bug.cgi?id=165991
             jit.breakpoint();
-            break;
-        case I32: {
-            GPRReg gprReg;
-            if (marshalledGPRs < wasmCC.m_gprArgs.size())
-                gprReg = wasmCC.m_gprArgs[marshalledGPRs].gpr();
-            else {
-                // We've already spilled all arguments, these registers are available as scratch.
-                gprReg = GPRInfo::argumentGPR0;
-                jit.load64(JIT::Address(GPRInfo::callFrameRegister, frOffset), gprReg);
-                frOffset += sizeof(Register);
-            }
-            ++marshalledGPRs;
-            jit.boxInt32(gprReg, JSValueRegs(gprReg), DoNotHaveTagRegisters);
-            jit.store64(gprReg, calleeFrame.withOffset(calleeFrameOffset));
-            calleeFrameOffset += sizeof(Register);
-            break;
+            LinkBuffer patchBuffer(*vm, jit, GLOBAL_THUNK_ID);
+            return FINALIZE_CODE(patchBuffer, ("WebAssembly import[%i] stub for signature %i", importIndex, signatureIndex));
         }
-        case F32: {
-            FPRReg fprReg;
-            if (marshalledFPRs < wasmCC.m_fprArgs.size())
-                fprReg = wasmCC.m_fprArgs[marshalledFPRs].fpr();
-            else {
-                // We've already spilled all arguments, these registers are available as scratch.
-                fprReg = FPRInfo::argumentFPR0;
-                jit.loadFloat(JIT::Address(GPRInfo::callFrameRegister, frOffset), fprReg);
-                frOffset += sizeof(Register);
-            }
-            jit.convertFloatToDouble(fprReg, fprReg);
-            jit.purifyNaN(fprReg);
-            jit.storeDouble(fprReg, calleeFrame.withOffset(calleeFrameOffset));
-            calleeFrameOffset += sizeof(Register);
-            ++marshalledFPRs;
-            break;
+        case I32:
+        case F32:
+        case F64:
+            continue;
         }
-        case F64: {
-            FPRReg fprReg;
-            if (marshalledFPRs < wasmCC.m_fprArgs.size())
-                fprReg = wasmCC.m_fprArgs[marshalledFPRs].fpr();
-            else {
-                // We've already spilled all arguments, these registers are available as scratch.
-                fprReg = FPRInfo::argumentFPR0;
-                jit.loadDouble(JIT::Address(GPRInfo::callFrameRegister, frOffset), fprReg);
-                frOffset += sizeof(Register);
+    }
+
+    // FIXME make these loops which switch on Signature if there are many arguments on the stack. It'll otherwise be huge for huge signatures. https://bugs.webkit.org/show_bug.cgi?id=165547
+    
+    // First go through the integer parameters, freeing up their register for use afterwards.
+    {
+        unsigned marshalledGPRs = 0;
+        unsigned marshalledFPRs = 0;
+        unsigned calleeFrameOffset = CallFrameSlot::firstArgument * static_cast<int>(sizeof(Register));
+        unsigned frOffset = CallFrameSlot::firstArgument * static_cast<int>(sizeof(Register));
+        for (unsigned argNum = 0; argNum < argCount; ++argNum) {
+            Type argType = signature->argument(argNum);
+            switch (argType) {
+            case Void:
+            case Func:
+            case Anyfunc:
+            case I64:
+                RELEASE_ASSERT_NOT_REACHED(); // Handled above.
+            case I32: {
+                GPRReg gprReg;
+                if (marshalledGPRs < wasmCC.m_gprArgs.size())
+                    gprReg = wasmCC.m_gprArgs[marshalledGPRs].gpr();
+                else {
+                    // We've already spilled all arguments, these registers are available as scratch.
+                    gprReg = GPRInfo::argumentGPR0;
+                    jit.load64(JIT::Address(GPRInfo::callFrameRegister, frOffset), gprReg);
+                    frOffset += sizeof(Register);
+                }
+                ++marshalledGPRs;
+                jit.boxInt32(gprReg, JSValueRegs(gprReg), DoNotHaveTagRegisters);
+                jit.store64(gprReg, calleeFrame.withOffset(calleeFrameOffset));
+                calleeFrameOffset += sizeof(Register);
+                break;
+            }
+            case F32:
+            case F64:
+                // Skipped: handled below.
+                if (marshalledFPRs >= wasmCC.m_fprArgs.size())
+                    frOffset += sizeof(Register);
+                ++marshalledFPRs;
+                calleeFrameOffset += sizeof(Register);
+                break;
             }
-            jit.purifyNaN(fprReg);
-            jit.storeDouble(fprReg, calleeFrame.withOffset(calleeFrameOffset));
-            calleeFrameOffset += sizeof(Register);
-            ++marshalledFPRs;
-            break;
         }
+    }
+    
+    {
+        // Integer registers have already been spilled, these are now available.
+        GPRReg doubleEncodeOffsetGPRReg = GPRInfo::argumentGPR0;
+        GPRReg scratch = GPRInfo::argumentGPR1;
+        bool hasMaterializedDoubleEncodeOffset = false;
+        auto materializeDoubleEncodeOffset = [&hasMaterializedDoubleEncodeOffset, &jit] (GPRReg dest) {
+            if (!hasMaterializedDoubleEncodeOffset) {
+                static_assert(DoubleEncodeOffset == 1ll << 48, "codegen assumes this below");
+                jit.move(JIT::TrustedImm32(1), dest);
+                jit.lshift64(JIT::TrustedImm32(48), dest);
+                hasMaterializedDoubleEncodeOffset = true;
+            }
+        };
+
+        unsigned marshalledGPRs = 0;
+        unsigned marshalledFPRs = 0;
+        unsigned calleeFrameOffset = CallFrameSlot::firstArgument * static_cast<int>(sizeof(Register));
+        unsigned frOffset = CallFrameSlot::firstArgument * static_cast<int>(sizeof(Register));
+        for (unsigned argNum = 0; argNum < argCount; ++argNum) {
+            Type argType = signature->argument(argNum);
+            switch (argType) {
+            case Void:
+            case Func:
+            case Anyfunc:
+            case I64:
+                RELEASE_ASSERT_NOT_REACHED(); // Handled above.
+            case I32:
+                // Skipped: handled above.
+                if (marshalledGPRs < wasmCC.m_gprArgs.size())
+                    frOffset += sizeof(Register);
+                ++marshalledGPRs;
+                calleeFrameOffset += sizeof(Register);
+                break;
+            case F32: {
+                FPRReg fprReg;
+                if (marshalledFPRs < wasmCC.m_fprArgs.size())
+                    fprReg = wasmCC.m_fprArgs[marshalledFPRs].fpr();
+                else {
+                    // We've already spilled all arguments, these registers are available as scratch.
+                    fprReg = FPRInfo::argumentFPR0;
+                    jit.loadFloat(JIT::Address(GPRInfo::callFrameRegister, frOffset), fprReg);
+                    frOffset += sizeof(Register);
+                }
+                jit.convertFloatToDouble(fprReg, fprReg);
+                jit.purifyNaN(fprReg);
+                jit.moveDoubleTo64(fprReg, scratch);
+                materializeDoubleEncodeOffset(doubleEncodeOffsetGPRReg);
+                jit.add64(doubleEncodeOffsetGPRReg, scratch);
+                jit.store64(scratch, calleeFrame.withOffset(calleeFrameOffset));
+                calleeFrameOffset += sizeof(Register);
+                ++marshalledFPRs;
+                break;
+            }
+            case F64: {
+                FPRReg fprReg;
+                if (marshalledFPRs < wasmCC.m_fprArgs.size())
+                    fprReg = wasmCC.m_fprArgs[marshalledFPRs].fpr();
+                else {
+                    // We've already spilled all arguments, these registers are available as scratch.
+                    fprReg = FPRInfo::argumentFPR0;
+                    jit.loadDouble(JIT::Address(GPRInfo::callFrameRegister, frOffset), fprReg);
+                    frOffset += sizeof(Register);
+                }
+                jit.purifyNaN(fprReg);
+                jit.moveDoubleTo64(fprReg, scratch);
+                materializeDoubleEncodeOffset(doubleEncodeOffsetGPRReg);
+                jit.add64(doubleEncodeOffsetGPRReg, scratch);
+                jit.store64(scratch, calleeFrame.withOffset(calleeFrameOffset));
+                calleeFrameOffset += sizeof(Register);
+                ++marshalledFPRs;
+                break;
+            }
+            }
         }
     }
 
index b810189..748e504 100644 (file)
@@ -56,10 +56,6 @@ static EncodedJSValue JSC_HOST_CALL callWebAssemblyFunction(ExecState* exec)
     Wasm::SignatureIndex signatureIndex = wasmFunction->signatureIndex();
     const Wasm::Signature* signature = Wasm::SignatureInformation::get(&vm, signatureIndex);
 
-    // FIXME is this the right behavior? https://bugs.webkit.org/show_bug.cgi?id=164876
-    if (exec->argumentCount() != signature->argumentCount())
-        return JSValue::encode(throwException(exec, scope, createNotEnoughArgumentsError(exec, defaultSourceAppender)));
-
     {
         // Check if we have a disallowed I64 use.
 
@@ -78,10 +74,9 @@ static EncodedJSValue JSC_HOST_CALL callWebAssemblyFunction(ExecState* exec)
         }
     }
 
-    // FIXME is this boxing correct? https://bugs.webkit.org/show_bug.cgi?id=164876
     Vector<JSValue> boxedArgs;
     for (unsigned argIndex = 0; argIndex < signature->argumentCount(); ++argIndex) {
-        JSValue arg = exec->uncheckedArgument(argIndex);
+        JSValue arg = exec->argument(argIndex);
         switch (signature->argument(argIndex)) {
         case Wasm::I32:
             arg = JSValue::decode(arg.toInt32(exec));
@@ -127,7 +122,6 @@ static EncodedJSValue JSC_HOST_CALL callWebAssemblyFunction(ExecState* exec)
     vm.topJSWebAssemblyInstance = prevJSWebAssemblyInstance;
     RETURN_IF_EXCEPTION(scope, { });
 
-    // FIXME is this correct? https://bugs.webkit.org/show_bug.cgi?id=164876
     switch (signature->returnType()) {
     case Wasm::Void:
         return JSValue::encode(jsUndefined());