<!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
<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.</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 <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.
+
</ins><span class="cx"> 2017-01-02 JF Bastien <jfbastien@apple.com>
</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) => {
+ if (value === 0.0 && 1.0 / value === -Infinity)
+ return "NEGATIVE_ZERO";
+ return value;
+ };
+ return JSON.stringify(obj, replacer);
</ins><span class="cx"> }
</span><span class="cx"> AsmJS() {
</span><span class="cx"> "use asm"; // 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) => {
</span><ins>+ if (typeof lhs !== typeof rhs)
+ _fail(`Not the same: "${lhs}" and "${rhs}"`, msg);
</ins><span class="cx"> if (Array.isArray(lhs) && Array.isArray(rhs) && (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 === "number" && isNaN(lhs) && isNaN(rhs))
+ return;
</ins><span class="cx"> _fail(`Not the same: "${lhs}" and "${rhs}"`, msg);
</span><ins>+ } 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);
+ }
</ins><span class="cx"> };
</span><span class="cx">
</span><span class="cx"> const canonicalizeI32 = (number) => {
</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 "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);
+}
</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) => {
+ 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.
+ }
+ }
+}
</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("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());
</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, "f32.const");
</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] === "NEGATIVE_ZERO" ? -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, "f64.const");
</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] === "NEGATIVE_ZERO" ? -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 <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.
+
</ins><span class="cx"> 2017-01-03 Per Arne Vollan <pvollan@apple.com>
</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<ptrdiff_t>(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<int>(sizeof(Register));
- unsigned frOffset = CallFrameSlot::firstArgument * static_cast<int>(sizeof(Register));
</del><ins>+
</ins><span class="cx"> for (unsigned argNum = 0; argNum < argCount; ++argNum) {
</span><span class="cx"> Type argType = signature->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 < 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, ("WebAssembly import[%i] stub for signature %i", importIndex, signatureIndex));
</ins><span class="cx"> }
</span><del>- 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);
</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<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;
</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 >= wasmCC.m_fprArgs.size())
+ frOffset += sizeof(Register);
+ ++marshalledFPRs;
+ calleeFrameOffset += sizeof(Register);
+ break;
+ }
</ins><span class="cx"> }
</span><del>- 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);
</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 = [&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;
</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<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;
+ }
+ }
</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->signatureIndex();
</span><span class="cx"> const Wasm::Signature* signature = Wasm::SignatureInformation::get(&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->argumentCount() != signature->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<JSValue> boxedArgs;
</span><span class="cx"> for (unsigned argIndex = 0; argIndex < signature->argumentCount(); ++argIndex) {
</span><del>- JSValue arg = exec->uncheckedArgument(argIndex);
</del><ins>+ JSValue arg = exec->argument(argIndex);
</ins><span class="cx"> switch (signature->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->returnType()) {
</span><span class="cx"> case Wasm::Void:
</span><span class="cx"> return JSValue::encode(jsUndefined());
</span></span></pre>
</div>
</div>
</body>
</html>