<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[210244] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/210244">210244</a></dd>
<dt>Author</dt> <dd>jfbastien@apple.com</dd>
<dt>Date</dt> <dd>2017-01-03 12:24:36 -0800 (Tue, 03 Jan 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>WebAssembly JS API: check and test in-call / out-call values
https://bugs.webkit.org/show_bug.cgi?id=164876
&lt;rdar://problem/29844107&gt;

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 &quot;void&quot;
wasm functions return &quot;undefined&quot; in JS.

Source/JavaScriptCore:

* wasm/WasmBinding.cpp:
(JSC::Wasm::wasmToJs): fix the wasm -&gt; JS call coercions for f32 /
f64 which the assotiated tests inadvertently tripped on: the
previous code wasn't correctly performing JSValue boxing for
&quot;double&quot; 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 &quot;void&quot; are &quot;undefined&quot; in JS.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkJSTestsChangeLog">trunk/JSTests/ChangeLog</a></li>
<li><a href="#trunkJSTestswasmBuilderjs">trunk/JSTests/wasm/Builder.js</a></li>
<li><a href="#trunkJSTestswasmassertjs">trunk/JSTests/wasm/assert.js</a></li>
<li><a href="#trunkJSTestswasmselftesttest_BuilderJSONjs">trunk/JSTests/wasm/self-test/test_BuilderJSON.js</a></li>
<li><a href="#trunkJSTestswasmyaml">trunk/JSTests/wasm.yaml</a></li>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCorewasmWasmBindingcpp">trunk/Source/JavaScriptCore/wasm/WasmBinding.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorewasmjsWebAssemblyFunctioncpp">trunk/Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li>trunk/JSTests/wasm/fuzz/</li>
<li><a href="#trunkJSTestswasmfuzzexportfunctionjs">trunk/JSTests/wasm/fuzz/export-function.js</a></li>
<li><a href="#trunkJSTestswasmjsapiexportarityjs">trunk/JSTests/wasm/js-api/export-arity.js</a></li>
<li><a href="#trunkJSTestswasmjsapiexportvoidisundefjs">trunk/JSTests/wasm/js-api/export-void-is-undef.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkJSTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/JSTests/ChangeLog (210243 => 210244)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/ChangeLog        2017-01-03 19:30:18 UTC (rev 210243)
+++ trunk/JSTests/ChangeLog        2017-01-03 20:24:36 UTC (rev 210244)
</span><span class="lines">@@ -1,3 +1,26 @@
</span><ins>+2017-01-03  JF Bastien  &lt;jfbastien@apple.com&gt;
+
+        WebAssembly JS API: check and test in-call / out-call values
+        https://bugs.webkit.org/show_bug.cgi?id=164876
+        &lt;rdar://problem/29844107&gt;
+
+        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 &quot;void&quot;
+        wasm functions return &quot;undefined&quot; in JS.
+
</ins><span class="cx"> 2017-01-02  JF Bastien  &lt;jfbastien@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         WebAssembly: handle and optimize wasm export → wasm import calls
</span></span></pre></div>
<a id="trunkJSTestswasmBuilderjs"></a>
<div class="modfile"><h4>Modified: trunk/JSTests/wasm/Builder.js (210243 => 210244)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/wasm/Builder.js        2017-01-03 19:30:18 UTC (rev 210243)
+++ trunk/JSTests/wasm/Builder.js        2017-01-03 20:24:36 UTC (rev 210244)
</span><span class="lines">@@ -752,7 +752,13 @@
</span><span class="cx">             preamble: this._preamble,
</span><span class="cx">             section: this._sections
</span><span class="cx">         };
</span><del>-        return JSON.stringify(obj);
</del><ins>+        // JSON.stringify serializes -0.0 as 0.0.
+        const replacer = (key, value) =&gt; {
+            if (value === 0.0 &amp;&amp; 1.0 / value === -Infinity)
+                return &quot;NEGATIVE_ZERO&quot;;
+            return value;
+        };
+        return JSON.stringify(obj, replacer);
</ins><span class="cx">     }
</span><span class="cx">     AsmJS() {
</span><span class="cx">         &quot;use asm&quot;; // For speed.
</span></span></pre></div>
<a id="trunkJSTestswasmassertjs"></a>
<div class="modfile"><h4>Modified: trunk/JSTests/wasm/assert.js (210243 => 210244)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/wasm/assert.js        2017-01-03 19:30:18 UTC (rev 210243)
+++ trunk/JSTests/wasm/assert.js        2017-01-03 20:24:36 UTC (rev 210244)
</span><span class="lines">@@ -72,11 +72,19 @@
</span><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> export const eq = (lhs, rhs, msg) =&gt; {
</span><ins>+    if (typeof lhs !== typeof rhs)
+        _fail(`Not the same: &quot;${lhs}&quot; and &quot;${rhs}&quot;`, msg);
</ins><span class="cx">     if (Array.isArray(lhs) &amp;&amp; Array.isArray(rhs) &amp;&amp; (lhs.length === rhs.length)) {
</span><span class="cx">         for (let i = 0; i !== lhs.length; ++i)
</span><span class="cx">             eq(lhs[i], rhs[i], msg);
</span><del>-    } else if (lhs !== rhs)
</del><ins>+    } else if (lhs !== rhs) {
+        if (typeof lhs === &quot;number&quot; &amp;&amp; isNaN(lhs) &amp;&amp; isNaN(rhs))
+            return;
</ins><span class="cx">         _fail(`Not the same: &quot;${lhs}&quot; and &quot;${rhs}&quot;`, msg);
</span><ins>+    } else {
+        if (typeof lhs === &quot;number&quot; &amp;&amp; (1.0 / lhs !== 1.0 / rhs)) // Distinguish -0.0 from 0.0.
+            _fail(`Not the same: &quot;${lhs}&quot; and &quot;${rhs}&quot;`, msg);
+    }
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> const canonicalizeI32 = (number) =&gt; {
</span></span></pre></div>
<a id="trunkJSTestswasmfuzzexportfunctionjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/wasm/fuzz/export-function.js (0 => 210244)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/wasm/fuzz/export-function.js                                (rev 0)
+++ trunk/JSTests/wasm/fuzz/export-function.js        2017-01-03 20:24:36 UTC (rev 210244)
</span><span class="lines">@@ -0,0 +1,80 @@
</span><ins>+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 &quot;check&quot; function for each parameter received.
+const paramExporter = (params, returnedParam, imports) =&gt; {
+    const ret = params.length ? params[returnedParam] : &quot;void&quot;;
+    let builder = (new Builder())
+        .Type().End()
+        .Import()
+            .Function(&quot;imp&quot;, &quot;checki32&quot;, { params: [&quot;i32&quot;] })
+            .Function(&quot;imp&quot;, &quot;checkf32&quot;, { params: [&quot;f32&quot;] })
+            .Function(&quot;imp&quot;, &quot;checkf64&quot;, { params: [&quot;f64&quot;] })
+        .End()
+        .Function().End()
+        .Export()
+            .Function(&quot;func&quot;)
+        .End()
+        .Code()
+            .Function(&quot;func&quot;, { params: params, ret: ret });
+    for (let i = 0; i &lt; params.length; ++i) {
+        builder = builder.GetLocal(i);
+        switch (params[i]) {
+        case &quot;i32&quot;: builder = builder.Call(0); break;
+        case &quot;f32&quot;: builder = builder.Call(1); break;
+        case &quot;f64&quot;: builder = builder.Call(2); break;
+        default: throw new Error(`Unexpected type`);
+        }
+    }
+    if (ret !== &quot;void&quot;)
+        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 = () =&gt; (Math.random() * (1 + 0xffff)) | 0;
+const setBuffer = () =&gt; {
+    for (let i = 0; i &lt; 4; ++i)
+        viewi16[i] = random16();
+};
+const types = [
+    { type: &quot;i32&quot;, generate: () =&gt; { setBuffer(); return viewi32[0]; } },
+    // i64 isn't supported.
+    { type: &quot;f32&quot;, generate: () =&gt; { setBuffer(); return viewf32[0]; } },
+    { type: &quot;f64&quot;, generate: () =&gt; { setBuffer(); return viewf64[0]; } },
+];
+
+for (let iteration = 0; iteration &lt; numRandomIterations; ++iteration) {
+    const arity = (Math.random() * (maxArities + 1)) | 0;
+    let params = [];
+    let args = [];
+    for (let a = 0; a &lt; 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 =&gt; assert.eq(v, args[numChecked++]),
+        checkf32: v =&gt; assert.eq(v, args[numChecked++]),
+        checkf64: v =&gt; 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);
+}
</ins></span></pre></div>
<a id="trunkJSTestswasmjsapiexportarityjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/wasm/js-api/export-arity.js (0 => 210244)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/wasm/js-api/export-arity.js                                (rev 0)
+++ trunk/JSTests/wasm/js-api/export-arity.js        2017-01-03 20:24:36 UTC (rev 210244)
</span><span class="lines">@@ -0,0 +1,65 @@
</span><ins>+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) =&gt; {
+    let builder = (new Builder())
+        .Type().End()
+        .Import()
+            .Function(&quot;imp&quot;, &quot;check&quot;, { params: [paramType] })
+        .End()
+        .Function().End()
+        .Export()
+            .Function(&quot;func&quot;)
+        .End()
+        .Code()
+          .Function(&quot;func&quot;, { params: Array(numParams).fill(paramType) });
+    for (let i = 0; i &lt; 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: &quot;i32&quot;, value: 42, defaultWhenArityMismatch: 0 },
+    // i64 isn't supported.
+    { type: &quot;f32&quot;, value: 32.0, defaultWhenArityMismatch: NaN },
+    { type: &quot;f64&quot;, value: 64.0, defaultWhenArityMismatch: NaN },
+];
+
+for (let type of types) {
+    for (let wasmArity = 0; wasmArity &lt; maxArities; ++wasmArity) {
+        let numParamsCallingWith = undefined;
+        let numChecked = 0;
+        const check = value =&gt; {
+            assert.isNumber(value);
+            if (numParamsCallingWith &lt;= wasmArity) {
+                if (numChecked &lt; numParamsCallingWith)
+                    assert.eq(value, type.value);
+                else
+                    assert.eq(value, type.defaultWhenArityMismatch);
+            }  else {
+                if (numChecked &lt; 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 &lt; 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.
+        }
+    }
+}
</ins></span></pre></div>
<a id="trunkJSTestswasmjsapiexportvoidisundefjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/wasm/js-api/export-void-is-undef.js (0 => 210244)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/wasm/js-api/export-void-is-undef.js                                (rev 0)
+++ trunk/JSTests/wasm/js-api/export-void-is-undef.js        2017-01-03 20:24:36 UTC (rev 210244)
</span><span class="lines">@@ -0,0 +1,19 @@
</span><ins>+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+let builder = (new Builder())
+    .Type().End()
+    .Function().End()
+    .Export()
+        .Function(&quot;func&quot;)
+    .End()
+    .Code()
+    .Function(&quot;func&quot;, { 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());
</ins></span></pre></div>
<a id="trunkJSTestswasmselftesttest_BuilderJSONjs"></a>
<div class="modfile"><h4>Modified: trunk/JSTests/wasm/self-test/test_BuilderJSON.js (210243 => 210244)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/wasm/self-test/test_BuilderJSON.js        2017-01-03 19:30:18 UTC (rev 210243)
+++ trunk/JSTests/wasm/self-test/test_BuilderJSON.js        2017-01-03 20:24:36 UTC (rev 210244)
</span><span class="lines">@@ -510,7 +510,7 @@
</span><span class="cx">         assert.eq(j.section[1].data[0].code[0].name, &quot;f32.const&quot;);
</span><span class="cx">         assert.eq(j.section[1].data[0].code[0].arguments.length, 0);
</span><span class="cx">         assert.eq(j.section[1].data[0].code[0].immediates.length, 1);
</span><del>-        assert.eq(j.section[1].data[0].code[0].immediates[0], c);
</del><ins>+        assert.eq(j.section[1].data[0].code[0].immediates[0] === &quot;NEGATIVE_ZERO&quot; ? -0.0 : j.section[1].data[0].code[0].immediates[0], c);
</ins><span class="cx">     }
</span><span class="cx"> })();
</span><span class="cx"> 
</span><span class="lines">@@ -526,7 +526,7 @@
</span><span class="cx">         assert.eq(j.section[1].data[0].code[0].name, &quot;f64.const&quot;);
</span><span class="cx">         assert.eq(j.section[1].data[0].code[0].arguments.length, 0);
</span><span class="cx">         assert.eq(j.section[1].data[0].code[0].immediates.length, 1);
</span><del>-        assert.eq(j.section[1].data[0].code[0].immediates[0], c);
</del><ins>+        assert.eq(j.section[1].data[0].code[0].immediates[0] === &quot;NEGATIVE_ZERO&quot; ? -0.0 : j.section[1].data[0].code[0].immediates[0], c);
</ins><span class="cx">     }
</span><span class="cx"> })();
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkJSTestswasmyaml"></a>
<div class="modfile"><h4>Modified: trunk/JSTests/wasm.yaml (210243 => 210244)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/wasm.yaml        2017-01-03 19:30:18 UTC (rev 210243)
+++ trunk/JSTests/wasm.yaml        2017-01-03 20:24:36 UTC (rev 210244)
</span><span class="lines">@@ -27,6 +27,8 @@
</span><span class="cx">   cmd: runWebAssembly
</span><span class="cx"> - path: wasm/function-tests
</span><span class="cx">   cmd: runWebAssembly
</span><ins>+- path: wasm/fuzz
+  cmd: runWebAssembly
</ins><span class="cx"> 
</span><span class="cx"> - path: wasm/spec-tests/address.wast.js
</span><span class="cx">   cmd: runWebAssemblySpecTest :normal
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (210243 => 210244)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2017-01-03 19:30:18 UTC (rev 210243)
+++ trunk/Source/JavaScriptCore/ChangeLog        2017-01-03 20:24:36 UTC (rev 210244)
</span><span class="lines">@@ -1,3 +1,27 @@
</span><ins>+2017-01-03  JF Bastien  &lt;jfbastien@apple.com&gt;
+
+        WebAssembly JS API: check and test in-call / out-call values
+        https://bugs.webkit.org/show_bug.cgi?id=164876
+        &lt;rdar://problem/29844107&gt;
+
+        Reviewed by Saam Barati.
+
+        * wasm/WasmBinding.cpp:
+        (JSC::Wasm::wasmToJs): fix the wasm -&gt; JS call coercions for f32 /
+        f64 which the assotiated tests inadvertently tripped on: the
+        previous code wasn't correctly performing JSValue boxing for
+        &quot;double&quot; 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 &quot;void&quot; are &quot;undefined&quot; in JS.
+
</ins><span class="cx"> 2017-01-03  Per Arne Vollan  &lt;pvollan@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [Win] jsc.exe sometimes never exits.
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorewasmWasmBindingcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/wasm/WasmBinding.cpp (210243 => 210244)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/wasm/WasmBinding.cpp        2017-01-03 19:30:18 UTC (rev 210243)
+++ trunk/Source/JavaScriptCore/wasm/WasmBinding.cpp        2017-01-03 20:24:36 UTC (rev 210244)
</span><span class="lines">@@ -74,12 +74,7 @@
</span><span class="cx">     const unsigned stackOffset = WTF::roundUpToMultipleOf(stackAlignmentBytes(), numberOfBytesForCall);
</span><span class="cx">     jit.subPtr(MacroAssembler::TrustedImm32(stackOffset), MacroAssembler::stackPointerRegister);
</span><span class="cx">     JIT::Address calleeFrame = CCallHelpers::Address(MacroAssembler::stackPointerRegister, -static_cast&lt;ptrdiff_t&gt;(sizeof(CallerFrameAndPC)));
</span><del>-
-    // 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&lt;int&gt;(sizeof(Register));
-    unsigned frOffset = CallFrameSlot::firstArgument * static_cast&lt;int&gt;(sizeof(Register));
</del><ins>+    
</ins><span class="cx">     for (unsigned argNum = 0; argNum &lt; argCount; ++argNum) {
</span><span class="cx">         Type argType = signature-&gt;argument(argNum);
</span><span class="cx">         switch (argType) {
</span><span class="lines">@@ -86,61 +81,138 @@
</span><span class="cx">         case Void:
</span><span class="cx">         case Func:
</span><span class="cx">         case Anyfunc:
</span><del>-        case I64:
-            // FIXME: Figure out the correct behavior here. I suspect we want such a stub to throw an exception immediately
</del><ins>+        case I64: {
+            // FIXME: Figure out the correct behavior here. I suspect we want such a stub to throw an exception immediately.
</ins><span class="cx">             // if called. https://bugs.webkit.org/show_bug.cgi?id=165991
</span><span class="cx">             jit.breakpoint();
</span><del>-            break;
-        case I32: {
-            GPRReg gprReg;
-            if (marshalledGPRs &lt; 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;
</del><ins>+            LinkBuffer patchBuffer(*vm, jit, GLOBAL_THUNK_ID);
+            return FINALIZE_CODE(patchBuffer, (&quot;WebAssembly import[%i] stub for signature %i&quot;, importIndex, signatureIndex));
</ins><span class="cx">         }
</span><del>-        case F32: {
-            FPRReg fprReg;
-            if (marshalledFPRs &lt; 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);
</del><ins>+        case I32:
+        case F32:
+        case F64:
+            continue;
+        }
+    }
+
+    // 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&lt;int&gt;(sizeof(Register));
+        unsigned frOffset = CallFrameSlot::firstArgument * static_cast&lt;int&gt;(sizeof(Register));
+        for (unsigned argNum = 0; argNum &lt; argCount; ++argNum) {
+            Type argType = signature-&gt;argument(argNum);
+            switch (argType) {
+            case Void:
+            case Func:
+            case Anyfunc:
+            case I64:
+                RELEASE_ASSERT_NOT_REACHED(); // Handled above.
+            case I32: {
+                GPRReg gprReg;
+                if (marshalledGPRs &lt; 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;
</ins><span class="cx">             }
</span><del>-            jit.convertFloatToDouble(fprReg, fprReg);
-            jit.purifyNaN(fprReg);
-            jit.storeDouble(fprReg, calleeFrame.withOffset(calleeFrameOffset));
-            calleeFrameOffset += sizeof(Register);
-            ++marshalledFPRs;
-            break;
</del><ins>+            case F32:
+            case F64:
+                // Skipped: handled below.
+                if (marshalledFPRs &gt;= wasmCC.m_fprArgs.size())
+                    frOffset += sizeof(Register);
+                ++marshalledFPRs;
+                calleeFrameOffset += sizeof(Register);
+                break;
+            }
</ins><span class="cx">         }
</span><del>-        case F64: {
-            FPRReg fprReg;
-            if (marshalledFPRs &lt; 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);
</del><ins>+    }
+    
+    {
+        // Integer registers have already been spilled, these are now available.
+        GPRReg doubleEncodeOffsetGPRReg = GPRInfo::argumentGPR0;
+        GPRReg scratch = GPRInfo::argumentGPR1;
+        bool hasMaterializedDoubleEncodeOffset = false;
+        auto materializeDoubleEncodeOffset = [&amp;hasMaterializedDoubleEncodeOffset, &amp;jit] (GPRReg dest) {
+            if (!hasMaterializedDoubleEncodeOffset) {
+                static_assert(DoubleEncodeOffset == 1ll &lt;&lt; 48, &quot;codegen assumes this below&quot;);
+                jit.move(JIT::TrustedImm32(1), dest);
+                jit.lshift64(JIT::TrustedImm32(48), dest);
+                hasMaterializedDoubleEncodeOffset = true;
</ins><span class="cx">             }
</span><del>-            jit.purifyNaN(fprReg);
-            jit.storeDouble(fprReg, calleeFrame.withOffset(calleeFrameOffset));
-            calleeFrameOffset += sizeof(Register);
-            ++marshalledFPRs;
-            break;
</del><ins>+        };
+
+        unsigned marshalledGPRs = 0;
+        unsigned marshalledFPRs = 0;
+        unsigned calleeFrameOffset = CallFrameSlot::firstArgument * static_cast&lt;int&gt;(sizeof(Register));
+        unsigned frOffset = CallFrameSlot::firstArgument * static_cast&lt;int&gt;(sizeof(Register));
+        for (unsigned argNum = 0; argNum &lt; argCount; ++argNum) {
+            Type argType = signature-&gt;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 &lt; wasmCC.m_gprArgs.size())
+                    frOffset += sizeof(Register);
+                ++marshalledGPRs;
+                calleeFrameOffset += sizeof(Register);
+                break;
+            case F32: {
+                FPRReg fprReg;
+                if (marshalledFPRs &lt; 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 &lt; 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;
+            }
+            }
</ins><span class="cx">         }
</span><del>-        }
</del><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     GPRReg importJSCellGPRReg = GPRInfo::regT0; // Callee needs to be in regT0 for slow path below.
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorewasmjsWebAssemblyFunctioncpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp (210243 => 210244)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp        2017-01-03 19:30:18 UTC (rev 210243)
+++ trunk/Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp        2017-01-03 20:24:36 UTC (rev 210244)
</span><span class="lines">@@ -56,10 +56,6 @@
</span><span class="cx">     Wasm::SignatureIndex signatureIndex = wasmFunction-&gt;signatureIndex();
</span><span class="cx">     const Wasm::Signature* signature = Wasm::SignatureInformation::get(&amp;vm, signatureIndex);
</span><span class="cx"> 
</span><del>-    // FIXME is this the right behavior? https://bugs.webkit.org/show_bug.cgi?id=164876
-    if (exec-&gt;argumentCount() != signature-&gt;argumentCount())
-        return JSValue::encode(throwException(exec, scope, createNotEnoughArgumentsError(exec, defaultSourceAppender)));
-
</del><span class="cx">     {
</span><span class="cx">         // Check if we have a disallowed I64 use.
</span><span class="cx"> 
</span><span class="lines">@@ -78,10 +74,9 @@
</span><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    // FIXME is this boxing correct? https://bugs.webkit.org/show_bug.cgi?id=164876
</del><span class="cx">     Vector&lt;JSValue&gt; boxedArgs;
</span><span class="cx">     for (unsigned argIndex = 0; argIndex &lt; signature-&gt;argumentCount(); ++argIndex) {
</span><del>-        JSValue arg = exec-&gt;uncheckedArgument(argIndex);
</del><ins>+        JSValue arg = exec-&gt;argument(argIndex);
</ins><span class="cx">         switch (signature-&gt;argument(argIndex)) {
</span><span class="cx">         case Wasm::I32:
</span><span class="cx">             arg = JSValue::decode(arg.toInt32(exec));
</span><span class="lines">@@ -127,7 +122,6 @@
</span><span class="cx">     vm.topJSWebAssemblyInstance = prevJSWebAssemblyInstance;
</span><span class="cx">     RETURN_IF_EXCEPTION(scope, { });
</span><span class="cx"> 
</span><del>-    // FIXME is this correct? https://bugs.webkit.org/show_bug.cgi?id=164876
</del><span class="cx">     switch (signature-&gt;returnType()) {
</span><span class="cx">     case Wasm::Void:
</span><span class="cx">         return JSValue::encode(jsUndefined());
</span></span></pre>
</div>
</div>

</body>
</html>