<!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>[210090] 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/210090">210090</a></dd>
<dt>Author</dt> <dd>keith_miller@apple.com</dd>
<dt>Date</dt> <dd>2016-12-21 17:06:20 -0800 (Wed, 21 Dec 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>WebAssembly: Allow br, br_if, and br_table to act as a return
https://bugs.webkit.org/show_bug.cgi?id=166393

Reviewed by Saam Barati.

JSTests:

Add tests for breaks acting as returns and fix tests that
validate error messages.

* wasm/function-tests/br-as-return.js: Added.
* wasm/function-tests/br-if-as-return.js: Added.
* wasm/function-tests/br-table-as-return.js: Added.
* wasm/function-tests/if-no-else-non-void.js:
* wasm/function-tests/struct.js: Added.
* wasm/js-api/global-error.js:
(assert.throws):
* wasm/js-api/table.js:
(assert.throws):

Source/JavaScriptCore:

This patch allows br, br_if, and br_table to treat branching to
the size of the control stack to act as a return. This change was
made by adding a new block type to the wasm function parser,
TopLevel. Adding this new block eliminates a lot of the special
case code we had in the parser previously. The only special case
we need is when the end opcode is parsed from the top level.  The
B3 IR generator needs to automatically emit a return at that
point.

Also, this patch adds the function number to validation errors
in the function parser. The current error message is not helpful
otherwise.

* wasm/WasmB3IRGenerator.cpp:
(JSC::Wasm::B3IRGenerator::ControlData::dump):
(JSC::Wasm::B3IRGenerator::addTopLevel):
* wasm/WasmFunctionParser.h:
* wasm/WasmPlan.cpp:
(JSC::Wasm::Plan::parseAndValidateModule):
(JSC::Wasm::Plan::run):
* wasm/WasmValidate.cpp:
(JSC::Wasm::Validate::ControlData::dump):
(JSC::Wasm::Validate::Validate):
(JSC::Wasm::Validate::addTopLevel):
(JSC::Wasm::validateFunction):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkJSTestsChangeLog">trunk/JSTests/ChangeLog</a></li>
<li><a href="#trunkJSTestswasmfunctiontestsifnoelsenonvoidjs">trunk/JSTests/wasm/function-tests/if-no-else-non-void.js</a></li>
<li><a href="#trunkJSTestswasmjsapiglobalerrorjs">trunk/JSTests/wasm/js-api/global-error.js</a></li>
<li><a href="#trunkJSTestswasmjsapitablejs">trunk/JSTests/wasm/js-api/table.js</a></li>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCorewasmWasmB3IRGeneratorcpp">trunk/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorewasmWasmFunctionParserh">trunk/Source/JavaScriptCore/wasm/WasmFunctionParser.h</a></li>
<li><a href="#trunkSourceJavaScriptCorewasmWasmPlancpp">trunk/Source/JavaScriptCore/wasm/WasmPlan.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorewasmWasmValidatecpp">trunk/Source/JavaScriptCore/wasm/WasmValidate.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkJSTestswasmfunctiontestsbrasreturnjs">trunk/JSTests/wasm/function-tests/br-as-return.js</a></li>
<li><a href="#trunkJSTestswasmfunctiontestsbrifasreturnjs">trunk/JSTests/wasm/function-tests/br-if-as-return.js</a></li>
<li><a href="#trunkJSTestswasmfunctiontestsbrtableasreturnjs">trunk/JSTests/wasm/function-tests/br-table-as-return.js</a></li>
<li><a href="#trunkJSTestswasmfunctiontestsstructjs">trunk/JSTests/wasm/function-tests/struct.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkJSTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/JSTests/ChangeLog (210089 => 210090)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/ChangeLog        2016-12-22 00:53:25 UTC (rev 210089)
+++ trunk/JSTests/ChangeLog        2016-12-22 01:06:20 UTC (rev 210090)
</span><span class="lines">@@ -1,3 +1,23 @@
</span><ins>+2016-12-21  Keith Miller  &lt;keith_miller@apple.com&gt;
+
+        WebAssembly: Allow br, br_if, and br_table to act as a return
+        https://bugs.webkit.org/show_bug.cgi?id=166393
+
+        Reviewed by Saam Barati.
+
+        Add tests for breaks acting as returns and fix tests that
+        validate error messages.
+
+        * wasm/function-tests/br-as-return.js: Added.
+        * wasm/function-tests/br-if-as-return.js: Added.
+        * wasm/function-tests/br-table-as-return.js: Added.
+        * wasm/function-tests/if-no-else-non-void.js:
+        * wasm/function-tests/struct.js: Added.
+        * wasm/js-api/global-error.js:
+        (assert.throws):
+        * wasm/js-api/table.js:
+        (assert.throws):
+
</ins><span class="cx"> 2016-12-21  Saam Barati  &lt;sbarati@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         WebAssembly: Import spec tests
</span></span></pre></div>
<a id="trunkJSTestswasmfunctiontestsbrasreturnjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/wasm/function-tests/br-as-return.js (0 => 210090)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/wasm/function-tests/br-as-return.js                                (rev 0)
+++ trunk/JSTests/wasm/function-tests/br-as-return.js        2016-12-22 01:06:20 UTC (rev 210090)
</span><span class="lines">@@ -0,0 +1,31 @@
</span><ins>+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+const builder = (new Builder())
+      .Type().End()
+      .Function().End()
+      .Export()
+          .Function(&quot;br1&quot;)
+          .Function(&quot;br0&quot;)
+      .End()
+      .Code()
+          .Function(&quot;br1&quot;, { params: [], ret: &quot;i32&quot; })
+              .Block(&quot;void&quot;, b =&gt; {
+                  return b.I32Const(0)
+                  .Br(1)
+              })
+             .Unreachable()
+          .End()
+
+          .Function(&quot;br0&quot;, { params: [], ret: &quot;i32&quot; })
+              .I32Const(0)
+              .Br(0)
+          .End()
+
+      .End();
+
+const bin = builder.WebAssembly().get();
+const module = new WebAssembly.Module(bin);
+const instance = new WebAssembly.Instance(module);
+assert.eq(instance.exports.br1(), 0)
+assert.eq(instance.exports.br0(), 0)
</ins></span></pre></div>
<a id="trunkJSTestswasmfunctiontestsbrifasreturnjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/wasm/function-tests/br-if-as-return.js (0 => 210090)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/wasm/function-tests/br-if-as-return.js                                (rev 0)
+++ trunk/JSTests/wasm/function-tests/br-if-as-return.js        2016-12-22 01:06:20 UTC (rev 210090)
</span><span class="lines">@@ -0,0 +1,33 @@
</span><ins>+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+const builder = (new Builder())
+      .Type().End()
+      .Function().End()
+      .Export()
+          .Function(&quot;br1&quot;)
+          .Function(&quot;br0&quot;)
+      .End()
+      .Code()
+          .Function(&quot;br1&quot;, { params: [], ret: &quot;i32&quot; })
+              .Block(&quot;void&quot;, b =&gt; {
+                  return b.I32Const(0)
+                  .I32Const(1)
+                  .BrIf(1)
+              })
+             .Unreachable()
+          .End()
+
+          .Function(&quot;br0&quot;, { params: [], ret: &quot;i32&quot; })
+              .I32Const(0)
+              .I32Const(1)
+              .BrIf(0)
+          .End()
+
+      .End();
+
+const bin = builder.WebAssembly().get();
+const module = new WebAssembly.Module(bin);
+const instance = new WebAssembly.Instance(module);
+assert.eq(instance.exports.br1(), 0)
+assert.eq(instance.exports.br0(), 0)
</ins></span></pre></div>
<a id="trunkJSTestswasmfunctiontestsbrtableasreturnjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/wasm/function-tests/br-table-as-return.js (0 => 210090)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/wasm/function-tests/br-table-as-return.js                                (rev 0)
+++ trunk/JSTests/wasm/function-tests/br-table-as-return.js        2016-12-22 01:06:20 UTC (rev 210090)
</span><span class="lines">@@ -0,0 +1,52 @@
</span><ins>+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+const builder = (new Builder())
+      .Type().End()
+      .Function().End()
+      .Export()
+          .Function(&quot;br1Default&quot;)
+          .Function(&quot;br0Default&quot;)
+          .Function(&quot;br1Case&quot;)
+          .Function(&quot;br0Case&quot;)
+      .End()
+      .Code()
+          .Function(&quot;br1Default&quot;, { params: [], ret: &quot;i32&quot; })
+              .Block(&quot;void&quot;, b =&gt; {
+                  return b.I32Const(0)
+                  .I32Const(100)
+                  .BrTable(1, 1)
+              })
+             .Unreachable()
+          .End()
+
+          .Function(&quot;br0Default&quot;, { params: [], ret: &quot;i32&quot; })
+              .I32Const(0)
+              .I32Const(100)
+              .BrTable(0, 0)
+          .End()
+
+          .Function(&quot;br1Case&quot;, { params: [], ret: &quot;i32&quot; })
+              .Block(&quot;void&quot;, b =&gt; {
+                  return b.I32Const(0)
+                  .I32Const(100)
+                  .BrTable(1, 1)
+              })
+             .Unreachable()
+          .End()
+
+          .Function(&quot;br0Case&quot;, { params: [], ret: &quot;i32&quot; })
+              .I32Const(0)
+              .I32Const(0)
+              .BrTable(0, 0)
+          .End()
+
+      .End();
+
+const bin = builder.WebAssembly().get();
+const module = new WebAssembly.Module(bin);
+const instance = new WebAssembly.Instance(module);
+assert.eq(instance.exports.br1Default(), 0)
+assert.eq(instance.exports.br0Default(), 0)
+assert.eq(instance.exports.br1Case(), 0)
+assert.eq(instance.exports.br0Case(), 0)
</ins></span></pre></div>
<a id="trunkJSTestswasmfunctiontestsifnoelsenonvoidjs"></a>
<div class="modfile"><h4>Modified: trunk/JSTests/wasm/function-tests/if-no-else-non-void.js (210089 => 210090)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/wasm/function-tests/if-no-else-non-void.js        2016-12-22 00:53:25 UTC (rev 210089)
+++ trunk/JSTests/wasm/function-tests/if-no-else-non-void.js        2016-12-22 01:06:20 UTC (rev 210090)
</span><span class="lines">@@ -13,4 +13,4 @@
</span><span class="cx">       .End();
</span><span class="cx"> 
</span><span class="cx"> const bin = builder.WebAssembly().get();
</span><del>-assert.throws(() =&gt; new WebAssembly.Module(bin), WebAssembly.CompileError, &quot;WebAssembly.Module doesn't validate: If-block had a non-void result type: I32 but had no else-block (evaluating 'new WebAssembly.Module(bin)')&quot;);
</del><ins>+assert.throws(() =&gt; new WebAssembly.Module(bin), WebAssembly.CompileError, &quot;WebAssembly.Module doesn't validate: If-block had a non-void result type: I32 but had no else-block, in function at index 0 (evaluating 'new WebAssembly.Module(bin)')&quot;);
</ins></span></pre></div>
<a id="trunkJSTestswasmfunctiontestsstructjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/wasm/function-tests/struct.js (0 => 210090)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/wasm/function-tests/struct.js                                (rev 0)
+++ trunk/JSTests/wasm/function-tests/struct.js        2016-12-22 01:06:20 UTC (rev 210090)
</span><span class="lines">@@ -0,0 +1,28 @@
</span><ins>+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+const builder = (new Builder())
+
+builder.Type().End()
+    .Function().End()
+    .Memory().InitialMaxPages(1, 1).End()
+    .Export()
+        .Function(&quot;callFunc&quot;)
+    .End()
+    .Code()
+        .Function(&quot;callFunc&quot;, { params: [&quot;i32&quot;, &quot;i32&quot;], ret: &quot;i32&quot; })
+            .GetLocal(0)
+            .If(&quot;void&quot;, b =&gt; {
+                return b.GetLocal(0)
+                .GetLocal(1)
+                .I32Load(0, 4)
+                .I32Sub()
+                .Return()
+            })
+            .I32Const(42)
+            .Return()
+        .End()
+    .End();
+const bin = builder.WebAssembly().get();
+const module = new WebAssembly.Module(bin);
+const instance = new WebAssembly.Instance(module);
</ins></span></pre></div>
<a id="trunkJSTestswasmjsapiglobalerrorjs"></a>
<div class="modfile"><h4>Modified: trunk/JSTests/wasm/js-api/global-error.js (210089 => 210090)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/wasm/js-api/global-error.js        2016-12-22 00:53:25 UTC (rev 210089)
+++ trunk/JSTests/wasm/js-api/global-error.js        2016-12-22 01:06:20 UTC (rev 210090)
</span><span class="lines">@@ -103,7 +103,7 @@
</span><span class="cx">     const bin = builder.WebAssembly();
</span><span class="cx">     bin.trim();
</span><span class="cx"> 
</span><del>-    assert.throws(() =&gt; new WebAssembly.Module(bin.get()), WebAssembly.CompileError, &quot;WebAssembly.Module doesn't validate: set_global 0 is immutable (evaluating 'new WebAssembly.Module(bin.get())')&quot;);
</del><ins>+    assert.throws(() =&gt; new WebAssembly.Module(bin.get()), WebAssembly.CompileError, &quot;WebAssembly.Module doesn't validate: set_global 0 is immutable, in function at index 0 (evaluating 'new WebAssembly.Module(bin.get())')&quot;);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -130,7 +130,7 @@
</span><span class="cx">     const bin = builder.WebAssembly();
</span><span class="cx">     bin.trim();
</span><span class="cx"> 
</span><del>-    assert.throws(() =&gt; new WebAssembly.Module(bin.get()), WebAssembly.CompileError, &quot;WebAssembly.Module doesn't validate: set_global 1 of unknown global, limit is 1 (evaluating 'new WebAssembly.Module(bin.get())')&quot;);
</del><ins>+    assert.throws(() =&gt; new WebAssembly.Module(bin.get()), WebAssembly.CompileError, &quot;WebAssembly.Module doesn't validate: set_global 1 of unknown global, limit is 1, in function at index 0 (evaluating 'new WebAssembly.Module(bin.get())')&quot;);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -156,7 +156,7 @@
</span><span class="cx">     const bin = builder.WebAssembly();
</span><span class="cx">     bin.trim();
</span><span class="cx"> 
</span><del>-    assert.throws(() =&gt; new WebAssembly.Module(bin.get()), WebAssembly.CompileError, &quot;WebAssembly.Module doesn't validate: set_global 0 with type F32 with a variable of type I32 (evaluating 'new WebAssembly.Module(bin.get())')&quot;);
</del><ins>+    assert.throws(() =&gt; new WebAssembly.Module(bin.get()), WebAssembly.CompileError, &quot;WebAssembly.Module doesn't validate: set_global 0 with type F32 with a variable of type I32, in function at index 0 (evaluating 'new WebAssembly.Module(bin.get())')&quot;);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> for ( let imp of [undefined, null, {}, () =&gt; {}, &quot;number&quot;, new Number(4)]) {
</span></span></pre></div>
<a id="trunkJSTestswasmjsapitablejs"></a>
<div class="modfile"><h4>Modified: trunk/JSTests/wasm/js-api/table.js (210089 => 210090)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/wasm/js-api/table.js        2016-12-22 00:53:25 UTC (rev 210089)
+++ trunk/JSTests/wasm/js-api/table.js        2016-12-22 01:06:20 UTC (rev 210090)
</span><span class="lines">@@ -42,7 +42,7 @@
</span><span class="cx">                 .CallIndirect(0, 0)
</span><span class="cx">             .End()
</span><span class="cx">         .End();
</span><del>-    assert.throws(() =&gt; new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, &quot;WebAssembly.Module doesn't parse at byte 4 / 7: call_indirect is only valid when a table is defined or imported (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')&quot;);
</del><ins>+    assert.throws(() =&gt; new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, &quot;WebAssembly.Module doesn't parse at byte 4 / 7: call_indirect is only valid when a table is defined or imported, in function at index 0 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')&quot;);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> {
</span><span class="lines">@@ -61,7 +61,7 @@
</span><span class="cx">                 .CallIndirect(0, 1)
</span><span class="cx">             .End()
</span><span class="cx">         .End();
</span><del>-    assert.throws(() =&gt; new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, &quot;WebAssembly.Module doesn't parse at byte 6 / 7: call_indirect's 'reserved' varuint1 must be 0x0 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')&quot;);
</del><ins>+    assert.throws(() =&gt; new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, &quot;WebAssembly.Module doesn't parse at byte 6 / 7: call_indirect's 'reserved' varuint1 must be 0x0, in function at index 0 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')&quot;);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (210089 => 210090)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2016-12-22 00:53:25 UTC (rev 210089)
+++ trunk/Source/JavaScriptCore/ChangeLog        2016-12-22 01:06:20 UTC (rev 210090)
</span><span class="lines">@@ -1,3 +1,36 @@
</span><ins>+2016-12-21  Keith Miller  &lt;keith_miller@apple.com&gt;
+
+        WebAssembly: Allow br, br_if, and br_table to act as a return
+        https://bugs.webkit.org/show_bug.cgi?id=166393
+
+        Reviewed by Saam Barati.
+
+        This patch allows br, br_if, and br_table to treat branching to
+        the size of the control stack to act as a return. This change was
+        made by adding a new block type to the wasm function parser,
+        TopLevel. Adding this new block eliminates a lot of the special
+        case code we had in the parser previously. The only special case
+        we need is when the end opcode is parsed from the top level.  The
+        B3 IR generator needs to automatically emit a return at that
+        point.
+
+        Also, this patch adds the function number to validation errors
+        in the function parser. The current error message is not helpful
+        otherwise.
+
+        * wasm/WasmB3IRGenerator.cpp:
+        (JSC::Wasm::B3IRGenerator::ControlData::dump):
+        (JSC::Wasm::B3IRGenerator::addTopLevel):
+        * wasm/WasmFunctionParser.h:
+        * wasm/WasmPlan.cpp:
+        (JSC::Wasm::Plan::parseAndValidateModule):
+        (JSC::Wasm::Plan::run):
+        * wasm/WasmValidate.cpp:
+        (JSC::Wasm::Validate::ControlData::dump):
+        (JSC::Wasm::Validate::Validate):
+        (JSC::Wasm::Validate::addTopLevel):
+        (JSC::Wasm::validateFunction):
+
</ins><span class="cx"> 2016-12-21  JF Bastien  &lt;jfbastien@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         WebAssembly JS API: cleanup &amp; pass VM around to {Compile/Runtime}Error
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorewasmWasmB3IRGeneratorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp (210089 => 210090)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp        2016-12-22 00:53:25 UTC (rev 210089)
+++ trunk/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp        2016-12-22 01:06:20 UTC (rev 210090)
</span><span class="lines">@@ -87,14 +87,17 @@
</span><span class="cx">         {
</span><span class="cx">             switch (type()) {
</span><span class="cx">             case BlockType::If:
</span><del>-                out.print(&quot;If:    &quot;);
</del><ins>+                out.print(&quot;If:       &quot;);
</ins><span class="cx">                 break;
</span><span class="cx">             case BlockType::Block:
</span><del>-                out.print(&quot;Block: &quot;);
</del><ins>+                out.print(&quot;Block:    &quot;);
</ins><span class="cx">                 break;
</span><span class="cx">             case BlockType::Loop:
</span><del>-                out.print(&quot;Loop:  &quot;);
</del><ins>+                out.print(&quot;Loop:     &quot;);
</ins><span class="cx">                 break;
</span><ins>+            case BlockType::TopLevel:
+                out.print(&quot;TopLevel: &quot;);
+                break;
</ins><span class="cx">             }
</span><span class="cx">             out.print(&quot;Continuation: &quot;, *continuation, &quot;, Special: &quot;);
</span><span class="cx">             if (special)
</span><span class="lines">@@ -178,6 +181,7 @@
</span><span class="cx">     PartialResult WARN_UNUSED_RETURN addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType&amp; result);
</span><span class="cx"> 
</span><span class="cx">     // Control flow
</span><ins>+    ControlData WARN_UNUSED_RETURN addTopLevel(Type signature);
</ins><span class="cx">     ControlData WARN_UNUSED_RETURN addBlock(Type signature);
</span><span class="cx">     ControlData WARN_UNUSED_RETURN addLoop(Type signature);
</span><span class="cx">     PartialResult WARN_UNUSED_RETURN addIf(ExpressionType condition, Type signature, ControlData&amp; result);
</span><span class="lines">@@ -184,7 +188,7 @@
</span><span class="cx">     PartialResult WARN_UNUSED_RETURN addElse(ControlData&amp;, const ExpressionList&amp;);
</span><span class="cx">     PartialResult WARN_UNUSED_RETURN addElseToUnreachable(ControlData&amp;);
</span><span class="cx"> 
</span><del>-    PartialResult WARN_UNUSED_RETURN addReturn(const ExpressionList&amp; returnValues);
</del><ins>+    PartialResult WARN_UNUSED_RETURN addReturn(const ControlData&amp;, const ExpressionList&amp; returnValues);
</ins><span class="cx">     PartialResult WARN_UNUSED_RETURN addBranch(ControlData&amp;, ExpressionType condition, const ExpressionList&amp; returnValues);
</span><span class="cx">     PartialResult WARN_UNUSED_RETURN addSwitch(ExpressionType condition, const Vector&lt;ControlData*&gt;&amp; targets, ControlData&amp; defaultTargets, const ExpressionList&amp; expressionStack);
</span><span class="cx">     PartialResult WARN_UNUSED_RETURN endBlock(ControlEntry&amp;, ExpressionList&amp; expressionStack);
</span><span class="lines">@@ -553,6 +557,11 @@
</span><span class="cx">     return nullptr;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+B3IRGenerator::ControlData B3IRGenerator::addTopLevel(Type signature)
+{
+    return ControlData(m_proc, signature, BlockType::TopLevel, m_proc.addBlock());
+}
+
</ins><span class="cx"> B3IRGenerator::ControlData B3IRGenerator::addBlock(Type signature)
</span><span class="cx"> {
</span><span class="cx">     return ControlData(m_proc, signature, BlockType::Block, m_proc.addBlock());
</span><span class="lines">@@ -601,7 +610,7 @@
</span><span class="cx">     return { };
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-auto B3IRGenerator::addReturn(const ExpressionList&amp; returnValues) -&gt; PartialResult
</del><ins>+auto B3IRGenerator::addReturn(const ControlData&amp;, const ExpressionList&amp; returnValues) -&gt; PartialResult
</ins><span class="cx"> {
</span><span class="cx">     ASSERT(returnValues.size() &lt;= 1);
</span><span class="cx">     if (returnValues.size())
</span><span class="lines">@@ -671,6 +680,10 @@
</span><span class="cx">     for (Variable* result : data.result)
</span><span class="cx">         entry.enclosedExpressionStack.append(m_currentBlock-&gt;appendNew&lt;VariableValue&gt;(m_proc, B3::Get, Origin(), result));
</span><span class="cx"> 
</span><ins>+    // TopLevel does not have any code after this so we need to make sure we emit a return here.
+    if (data.type() == BlockType::TopLevel)
+        return addReturn(entry.controlData, entry.enclosedExpressionStack);
+
</ins><span class="cx">     return { };
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorewasmWasmFunctionParserh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/wasm/WasmFunctionParser.h (210089 => 210090)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/wasm/WasmFunctionParser.h        2016-12-22 00:53:25 UTC (rev 210089)
+++ trunk/Source/JavaScriptCore/wasm/WasmFunctionParser.h        2016-12-22 01:06:20 UTC (rev 210090)
</span><span class="lines">@@ -35,7 +35,8 @@
</span><span class="cx"> enum class BlockType {
</span><span class="cx">     If,
</span><span class="cx">     Block,
</span><del>-    Loop
</del><ins>+    Loop,
+    TopLevel
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> template&lt;typename Context&gt;
</span><span class="lines">@@ -60,7 +61,6 @@
</span><span class="cx">     PartialResult WARN_UNUSED_RETURN parseBody();
</span><span class="cx">     PartialResult WARN_UNUSED_RETURN parseExpression(OpType);
</span><span class="cx">     PartialResult WARN_UNUSED_RETURN parseUnreachableExpression(OpType);
</span><del>-    PartialResult WARN_UNUSED_RETURN addReturn();
</del><span class="cx">     PartialResult WARN_UNUSED_RETURN unifyControl(Vector&lt;ExpressionType&gt;&amp;, unsigned level);
</span><span class="cx"> 
</span><span class="cx"> #define WASM_TRY_POP_EXPRESSION_STACK_INTO(result, what) do {                               \
</span><span class="lines">@@ -126,8 +126,9 @@
</span><span class="cx"> template&lt;typename Context&gt;
</span><span class="cx"> auto FunctionParser&lt;Context&gt;::parseBody() -&gt; PartialResult
</span><span class="cx"> {
</span><del>-    while (true) {
-        uint8_t op;
</del><ins>+    m_controlStack.append({ ExpressionList(), m_context.addTopLevel(m_signature-&gt;returnType()) });
+    uint8_t op;
+    while (m_controlStack.size()) {
</ins><span class="cx">         WASM_PARSER_FAIL_IF(!parseUInt8(op), &quot;can't decode opcode&quot;);
</span><span class="cx">         WASM_PARSER_FAIL_IF(!isValidOpType(op), &quot;invalid opcode &quot;, op);
</span><span class="cx"> 
</span><span class="lines">@@ -136,12 +137,6 @@
</span><span class="cx">             m_context.dump(m_controlStack, m_expressionStack);
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        if (op == End &amp;&amp; !m_controlStack.size()) {
-            if (m_unreachableBlocks)
-                return { };
-            return addReturn();
-        }
-
</del><span class="cx">         if (m_unreachableBlocks)
</span><span class="cx">             WASM_FAIL_IF_HELPER_FAILS(parseUnreachableExpression(static_cast&lt;OpType&gt;(op)));
</span><span class="cx">         else
</span><span class="lines">@@ -148,21 +143,7 @@
</span><span class="cx">             WASM_FAIL_IF_HELPER_FAILS(parseExpression(static_cast&lt;OpType&gt;(op)));
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    RELEASE_ASSERT_NOT_REACHED();
-}
-
-template&lt;typename Context&gt;
-auto FunctionParser&lt;Context&gt;::addReturn() -&gt; PartialResult
-{
-    ExpressionList returnValues;
-    if (m_signature-&gt;returnType() != Void) {
-        ExpressionType returnValue;
-        WASM_TRY_POP_EXPRESSION_STACK_INTO(returnValue, &quot;return&quot;);
-        returnValues.append(returnValue);
-    }
-
-    m_unreachableBlocks = 1;
-    WASM_TRY_ADD_TO_CONTEXT(addReturn(returnValues));
</del><ins>+    ASSERT(op == OpType::End);
</ins><span class="cx">     return { };
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -433,12 +414,13 @@
</span><span class="cx"> 
</span><span class="cx">     case BrTable: {
</span><span class="cx">         uint32_t numberOfTargets;
</span><ins>+        uint32_t defaultTarget;
</ins><span class="cx">         ExpressionType condition;
</span><del>-        uint32_t defaultTarget;
</del><ins>+        Vector&lt;ControlType*&gt; targets;
+
</ins><span class="cx">         WASM_PARSER_FAIL_IF(!parseVarUInt32(numberOfTargets), &quot;can't get the number of targets for br_table&quot;);
</span><span class="cx">         WASM_PARSER_FAIL_IF(numberOfTargets == std::numeric_limits&lt;uint32_t&gt;::max(), &quot;br_table's number of targets is too big &quot;, numberOfTargets);
</span><span class="cx"> 
</span><del>-        Vector&lt;ControlType*&gt; targets;
</del><span class="cx">         WASM_PARSER_FAIL_IF(!targets.tryReserveCapacity(numberOfTargets), &quot;can't allocate memory for &quot;, numberOfTargets, &quot; br_table targets&quot;);
</span><span class="cx">         for (uint32_t i = 0; i &lt; numberOfTargets; ++i) {
</span><span class="cx">             uint32_t target;
</span><span class="lines">@@ -449,6 +431,7 @@
</span><span class="cx"> 
</span><span class="cx">         WASM_PARSER_FAIL_IF(!parseVarUInt32(defaultTarget), &quot;can't get default target for br_table&quot;);
</span><span class="cx">         WASM_PARSER_FAIL_IF(defaultTarget &gt;= m_controlStack.size(), &quot;br_table's default target &quot;, defaultTarget, &quot; exceeds control stack size &quot;, m_controlStack.size());
</span><ins>+
</ins><span class="cx">         WASM_TRY_POP_EXPRESSION_STACK_INTO(condition, &quot;br_table condition&quot;);
</span><span class="cx">         WASM_TRY_ADD_TO_CONTEXT(addSwitch(condition, targets, m_controlStack[m_controlStack.size() - 1 - defaultTarget].controlData, m_expressionStack));
</span><span class="cx"> 
</span><span class="lines">@@ -457,7 +440,16 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     case Return: {
</span><del>-        return addReturn();
</del><ins>+        ExpressionList returnValues;
+        if (m_signature-&gt;returnType() != Void) {
+            ExpressionType returnValue;
+            WASM_TRY_POP_EXPRESSION_STACK_INTO(returnValue, &quot;return&quot;);
+            returnValues.append(returnValue);
+        }
+
+        WASM_TRY_ADD_TO_CONTEXT(addReturn(m_controlStack[0].controlData, returnValues));
+        m_unreachableBlocks = 1;
+        return { };
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     case End: {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorewasmWasmPlancpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/wasm/WasmPlan.cpp (210089 => 210090)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/wasm/WasmPlan.cpp        2016-12-22 00:53:25 UTC (rev 210089)
+++ trunk/Source/JavaScriptCore/wasm/WasmPlan.cpp        2016-12-22 01:06:20 UTC (rev 210090)
</span><span class="lines">@@ -96,7 +96,7 @@
</span><span class="cx">                     dataLog(RawPointer(reinterpret_cast&lt;void*&gt;(functionStart[i])), &quot;, &quot;);
</span><span class="cx">                 dataLogLn();
</span><span class="cx">             }
</span><del>-            m_errorMessage = validationResult.error(); // FIXME make this an Expected.
</del><ins>+            m_errorMessage = makeString(validationResult.error(), &quot;, in function at index &quot;, String::number(functionIndex)); // FIXME make this an Expected.
</ins><span class="cx">             return false;
</span><span class="cx">         }
</span><span class="cx">     }
</span><span class="lines">@@ -178,7 +178,7 @@
</span><span class="cx">                 auto locker = holdLock(m_lock);
</span><span class="cx">                 if (!m_errorMessage) {
</span><span class="cx">                     // Multiple compiles could fail simultaneously. We arbitrarily choose the first.
</span><del>-                    m_errorMessage = parseAndCompileResult.error(); // FIXME make this an Expected.
</del><ins>+                    m_errorMessage = makeString(parseAndCompileResult.error(), &quot;, in function at index &quot;, String::number(functionIndex)); // FIXME make this an Expected.
</ins><span class="cx">                 }
</span><span class="cx">                 m_currentIndex = m_functionLocationInBinary.size();
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorewasmWasmValidatecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/wasm/WasmValidate.cpp (210089 => 210090)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/wasm/WasmValidate.cpp        2016-12-22 00:53:25 UTC (rev 210089)
+++ trunk/Source/JavaScriptCore/wasm/WasmValidate.cpp        2016-12-22 01:06:20 UTC (rev 210090)
</span><span class="lines">@@ -52,15 +52,19 @@
</span><span class="cx">         {
</span><span class="cx">             switch (type()) {
</span><span class="cx">             case BlockType::If:
</span><del>-                out.print(&quot;If:    &quot;);
</del><ins>+                out.print(&quot;If:       &quot;);
</ins><span class="cx">                 break;
</span><span class="cx">             case BlockType::Block:
</span><del>-                out.print(&quot;Block: &quot;);
</del><ins>+                out.print(&quot;Block:    &quot;);
</ins><span class="cx">                 break;
</span><span class="cx">             case BlockType::Loop:
</span><del>-                out.print(&quot;Loop:  &quot;);
</del><ins>+                out.print(&quot;Loop:     &quot;);
</ins><span class="cx">                 break;
</span><ins>+            case BlockType::TopLevel:
+                out.print(&quot;TopLevel: &quot;);
+                break;
</ins><span class="cx">             }
</span><ins>+            out.print(makeString(signature()));
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         bool hasNonVoidSignature() const { return m_signature != Void; }
</span><span class="lines">@@ -116,6 +120,7 @@
</span><span class="cx">     Result WARN_UNUSED_RETURN addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType&amp; result);
</span><span class="cx"> 
</span><span class="cx">     // Control flow
</span><ins>+    ControlData WARN_UNUSED_RETURN addTopLevel(Type signature);
</ins><span class="cx">     ControlData WARN_UNUSED_RETURN addBlock(Type signature);
</span><span class="cx">     ControlData WARN_UNUSED_RETURN addLoop(Type signature);
</span><span class="cx">     Result WARN_UNUSED_RETURN addIf(ExpressionType condition, Type signature, ControlData&amp; result);
</span><span class="lines">@@ -122,7 +127,7 @@
</span><span class="cx">     Result WARN_UNUSED_RETURN addElse(ControlData&amp;, const ExpressionList&amp;);
</span><span class="cx">     Result WARN_UNUSED_RETURN addElseToUnreachable(ControlData&amp;);
</span><span class="cx"> 
</span><del>-    Result WARN_UNUSED_RETURN addReturn(const ExpressionList&amp; returnValues);
</del><ins>+    Result WARN_UNUSED_RETURN addReturn(ControlData&amp; topLevel, const ExpressionList&amp; returnValues);
</ins><span class="cx">     Result WARN_UNUSED_RETURN addBranch(ControlData&amp;, ExpressionType condition, const ExpressionList&amp; expressionStack);
</span><span class="cx">     Result WARN_UNUSED_RETURN addSwitch(ExpressionType condition, const Vector&lt;ControlData*&gt;&amp; targets, ControlData&amp; defaultTarget, const ExpressionList&amp; expressionStack);
</span><span class="cx">     Result WARN_UNUSED_RETURN endBlock(ControlEntry&amp;, ExpressionList&amp; expressionStack);
</span><span class="lines">@@ -136,9 +141,8 @@
</span><span class="cx"> 
</span><span class="cx">     bool hasMemory() const { return !!m_module.memory; }
</span><span class="cx"> 
</span><del>-    Validate(ExpressionType returnType, const ModuleInformation&amp; module)
-        : m_returnType(returnType)
-        , m_module(module)
</del><ins>+    Validate(const ModuleInformation&amp; module)
+        : m_module(module)
</ins><span class="cx">     {
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -150,7 +154,6 @@
</span><span class="cx"> 
</span><span class="cx">     Result checkBranchTarget(ControlData&amp; target, const ExpressionList&amp; expressionStack);
</span><span class="cx"> 
</span><del>-    ExpressionType m_returnType;
</del><span class="cx">     Vector&lt;Type&gt; m_locals;
</span><span class="cx">     const ModuleInformation&amp; m_module;
</span><span class="cx"> };
</span><span class="lines">@@ -206,6 +209,11 @@
</span><span class="cx">     return { };
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+Validate::ControlType Validate::addTopLevel(Type signature)
+{
+    return ControlData(BlockType::TopLevel, signature);
+}
+
</ins><span class="cx"> Validate::ControlType Validate::addBlock(Type signature)
</span><span class="cx"> {
</span><span class="cx">     return ControlData(BlockType::Block, signature);
</span><span class="lines">@@ -244,12 +252,13 @@
</span><span class="cx">     return { };
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-auto Validate::addReturn(const ExpressionList&amp; returnValues) -&gt; Result
</del><ins>+auto Validate::addReturn(ControlType&amp; topLevel, const ExpressionList&amp; returnValues) -&gt; Result
</ins><span class="cx"> {
</span><del>-    if (m_returnType == Void)
</del><ins>+    ASSERT(topLevel.type() == BlockType::TopLevel);
+    if (topLevel.signature() == Void)
</ins><span class="cx">         return { };
</span><span class="cx">     ASSERT(returnValues.size() == 1);
</span><del>-    WASM_VALIDATOR_FAIL_IF(m_returnType != returnValues[0], &quot;return type &quot;, returnValues[0], &quot; doesn't match function's return type &quot;, m_returnType);
</del><ins>+    WASM_VALIDATOR_FAIL_IF(topLevel.signature() != returnValues[0], &quot;return type &quot;, returnValues[0], &quot; doesn't match function's return type &quot;, topLevel.signature());
</ins><span class="cx">     return { };
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -355,7 +364,7 @@
</span><span class="cx"> 
</span><span class="cx"> Expected&lt;void, String&gt; validateFunction(VM* vm, const uint8_t* source, size_t length, const Signature* signature, const ImmutableFunctionIndexSpace&amp; functionIndexSpace, const ModuleInformation&amp; module)
</span><span class="cx"> {
</span><del>-    Validate context(signature-&gt;returnType(), module);
</del><ins>+    Validate context(module);
</ins><span class="cx">     FunctionParser&lt;Validate&gt; validator(vm, context, source, length, signature, functionIndexSpace, module);
</span><span class="cx">     WASM_FAIL_IF_HELPER_FAILS(validator.parse());
</span><span class="cx">     return { };
</span></span></pre>
</div>
</div>

</body>
</html>