<!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>[192035] trunk/Source/JavaScriptCore</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/192035">192035</a></dd>
<dt>Author</dt> <dd>fpizlo@apple.com</dd>
<dt>Date</dt> <dd>2015-11-04 14:15:00 -0800 (Wed, 04 Nov 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>B3 should be able to compile a Check
https://bugs.webkit.org/show_bug.cgi?id=150878

Reviewed by Saam Barati.

The Check opcode in B3 is going to be our main OSR exit mechanism. It is a stackmap
value, so you can pass it any number of additional arguments, and you will get to find
out how those arguments are represented at the point that the value lands in the machine
code. Unlike a Patchpoint, a Check branches on a value, with the goal of supporting full
compare/branch fusion. The stackmap's generator runs in an out-of-line path to which
that branch is linked.

This change fills in the glue necessary to compile a Check and it includes a simple
test of this functionality. That test also happens to check that such simple code will
never use callee-saves, which I think is sensible.

* b3/B3LowerToAir.cpp:
(JSC::B3::Air::LowerToAir::append):
(JSC::B3::Air::LowerToAir::ensureSpecial):
(JSC::B3::Air::LowerToAir::fillStackmap):
(JSC::B3::Air::LowerToAir::tryStackSlot):
(JSC::B3::Air::LowerToAir::tryPatchpoint):
(JSC::B3::Air::LowerToAir::tryCheck):
(JSC::B3::Air::LowerToAir::tryUpsilon):
* b3/B3LoweringMatcher.patterns:
* b3/testb3.cpp:
(JSC::B3::testSimplePatchpoint):
(JSC::B3::testSimpleCheck):
(JSC::B3::run):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3LowerToAircpp">trunk/Source/JavaScriptCore/b3/B3LowerToAir.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3LoweringMatcherpatterns">trunk/Source/JavaScriptCore/b3/B3LoweringMatcher.patterns</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3testb3cpp">trunk/Source/JavaScriptCore/b3/testb3.cpp</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (192034 => 192035)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2015-11-04 21:46:10 UTC (rev 192034)
+++ trunk/Source/JavaScriptCore/ChangeLog        2015-11-04 22:15:00 UTC (rev 192035)
</span><span class="lines">@@ -1,3 +1,35 @@
</span><ins>+2015-11-03  Filip Pizlo  &lt;fpizlo@apple.com&gt;
+
+        B3 should be able to compile a Check
+        https://bugs.webkit.org/show_bug.cgi?id=150878
+
+        Reviewed by Saam Barati.
+
+        The Check opcode in B3 is going to be our main OSR exit mechanism. It is a stackmap
+        value, so you can pass it any number of additional arguments, and you will get to find
+        out how those arguments are represented at the point that the value lands in the machine
+        code. Unlike a Patchpoint, a Check branches on a value, with the goal of supporting full
+        compare/branch fusion. The stackmap's generator runs in an out-of-line path to which
+        that branch is linked.
+
+        This change fills in the glue necessary to compile a Check and it includes a simple
+        test of this functionality. That test also happens to check that such simple code will
+        never use callee-saves, which I think is sensible.
+
+        * b3/B3LowerToAir.cpp:
+        (JSC::B3::Air::LowerToAir::append):
+        (JSC::B3::Air::LowerToAir::ensureSpecial):
+        (JSC::B3::Air::LowerToAir::fillStackmap):
+        (JSC::B3::Air::LowerToAir::tryStackSlot):
+        (JSC::B3::Air::LowerToAir::tryPatchpoint):
+        (JSC::B3::Air::LowerToAir::tryCheck):
+        (JSC::B3::Air::LowerToAir::tryUpsilon):
+        * b3/B3LoweringMatcher.patterns:
+        * b3/testb3.cpp:
+        (JSC::B3::testSimplePatchpoint):
+        (JSC::B3::testSimpleCheck):
+        (JSC::B3::run):
+
</ins><span class="cx"> 2015-10-30  Keith Miller  &lt;keith_miller@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Fix endless OSR exits when creating a rope that contains an object that ToPrimitive's to a number.
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3LowerToAircpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3LowerToAir.cpp (192034 => 192035)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3LowerToAir.cpp        2015-11-04 21:46:10 UTC (rev 192034)
+++ trunk/Source/JavaScriptCore/b3/B3LowerToAir.cpp        2015-11-04 22:15:00 UTC (rev 192035)
</span><span class="lines">@@ -35,6 +35,7 @@
</span><span class="cx"> #include &quot;B3AddressMatcher.h&quot;
</span><span class="cx"> #include &quot;B3ArgumentRegValue.h&quot;
</span><span class="cx"> #include &quot;B3BasicBlockInlines.h&quot;
</span><ins>+#include &quot;B3CheckSpecial.h&quot;
</ins><span class="cx"> #include &quot;B3Commutativity.h&quot;
</span><span class="cx"> #include &quot;B3IndexMap.h&quot;
</span><span class="cx"> #include &quot;B3IndexSet.h&quot;
</span><span class="lines">@@ -462,12 +463,44 @@
</span><span class="cx">         insts.last().append(Inst(opcode, currentValue, std::forward&lt;Arguments&gt;(arguments)...));
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    template&lt;typename T&gt;
-    void ensureSpecial(T*&amp; field)
</del><ins>+    template&lt;typename T, typename... Arguments&gt;
+    T* ensureSpecial(T*&amp; field, Arguments&amp;&amp;... arguments)
</ins><span class="cx">     {
</span><del>-        if (!field)
-            field = static_cast&lt;T*&gt;(code.addSpecial(std::make_unique&lt;T&gt;()));
</del><ins>+        if (!field) {
+            field = static_cast&lt;T*&gt;(
+                code.addSpecial(std::make_unique&lt;T&gt;(std::forward&lt;Arguments&gt;(arguments)...)));
+        }
+        return field;
</ins><span class="cx">     }
</span><ins>+
+    void fillStackmap(Inst&amp; inst, StackmapValue* stackmap, unsigned numSkipped)
+    {
+        for (unsigned i = numSkipped; i &lt; stackmap-&gt;numChildren(); ++i) {
+            ConstrainedValue value = stackmap-&gt;constrainedChild(i);
+
+            Arg arg;
+            switch (value.rep().kind()) {
+            case ValueRep::Any:
+                arg = immOrTmp(value.value());
+                break;
+            case ValueRep::SomeRegister:
+                arg = tmp(value.value());
+                break;
+            case ValueRep::Register:
+                arg = Tmp(value.rep().reg());
+                append(Move, immOrTmp(value.value()), arg);
+                break;
+            case ValueRep::StackArgument:
+                arg = Arg::callArg(value.rep().offsetFromSP());
+                appendStore(value.value(), arg);
+                break;
+            default:
+                RELEASE_ASSERT_NOT_REACHED();
+                break;
+            }
+            inst.args.append(arg);
+        }
+    }
</ins><span class="cx">     
</span><span class="cx">     IndexSet&lt;Value&gt; locked; // These are values that will have no Tmp in Air.
</span><span class="cx">     IndexMap&lt;Value, Tmp&gt; valueToTmp; // These are values that must have a Tmp in Air. We say that a Value* with a non-null Tmp is &quot;pinned&quot;.
</span><span class="lines">@@ -483,8 +516,6 @@
</span><span class="cx">     unsigned currentIndex;
</span><span class="cx">     Value* currentValue;
</span><span class="cx"> 
</span><del>-    PatchpointSpecial* patchpointSpecial { 0 };
-
</del><span class="cx">     // The address selector will match any pattern where the input operands are available as Tmps.
</span><span class="cx">     // It doesn't care about sharing. It will happily emit the same address expression over and over
</span><span class="cx">     // again regardless of the expression's complexity. This works out fine, since at the machine
</span><span class="lines">@@ -837,6 +868,7 @@
</span><span class="cx">         return true;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    PatchpointSpecial* patchpointSpecial { nullptr };
</ins><span class="cx">     bool tryPatchpoint()
</span><span class="cx">     {
</span><span class="cx">         PatchpointValue* patchpointValue = currentValue-&gt;as&lt;PatchpointValue&gt;();
</span><span class="lines">@@ -847,34 +879,47 @@
</span><span class="cx">         if (patchpointValue-&gt;type() != Void)
</span><span class="cx">             inst.args.append(tmp(patchpointValue));
</span><span class="cx"> 
</span><del>-        for (ConstrainedValue value : patchpointValue-&gt;constrainedChildren()) {
-            Arg arg;
-            switch (value.rep().kind()) {
-            case ValueRep::Any:
-                arg = immOrTmp(value.value());
-                break;
-            case ValueRep::SomeRegister:
-                arg = tmp(value.value());
-                break;
-            case ValueRep::Register:
-                arg = Tmp(value.rep().reg());
-                append(Move, immOrTmp(value.value()), arg);
-                break;
-            case ValueRep::StackArgument:
-                arg = Arg::callArg(value.rep().offsetFromSP());
-                appendStore(value.value(), arg);
-                break;
-            default:
-                RELEASE_ASSERT_NOT_REACHED();
-                break;
-            }
-            inst.args.append(arg);
-        }
</del><ins>+        fillStackmap(inst, patchpointValue, 0);
</ins><span class="cx">         
</span><span class="cx">         insts.last().append(WTF::move(inst));
</span><span class="cx">         return true;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    CheckSpecial* checkBranchTest32Special { nullptr };
+    CheckSpecial* checkBranchTest64Special { nullptr };
+    bool tryCheck(Value* value)
+    {
+        if (!isInt(value-&gt;type())) {
+            // FIXME: Implement double branches.
+            // https://bugs.webkit.org/show_bug.cgi?id=150727
+            return false;
+        }
+
+        CheckSpecial* special;
+        switch (value-&gt;type()) {
+        case Int32:
+            special = ensureSpecial(checkBranchTest32Special, BranchTest32, 3);
+            break;
+        case Int64:
+            special = ensureSpecial(checkBranchTest64Special, BranchTest64, 3);
+            break;
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+            break;
+        }
+
+        CheckValue* checkValue = currentValue-&gt;as&lt;CheckValue&gt;();
+
+        Inst inst(
+            Patch, checkValue, Arg::special(special),
+            Arg::resCond(MacroAssembler::NonZero), tmp(value), Arg::imm(-1));
+
+        fillStackmap(inst, checkValue, 1);
+
+        insts.last().append(WTF::move(inst));
+        return true;
+    }
+
</ins><span class="cx">     bool tryUpsilon(Value* value)
</span><span class="cx">     {
</span><span class="cx">         append(
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3LoweringMatcherpatterns"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3LoweringMatcher.patterns (192034 => 192035)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3LoweringMatcher.patterns        2015-11-04 21:46:10 UTC (rev 192034)
+++ trunk/Source/JavaScriptCore/b3/B3LoweringMatcher.patterns        2015-11-04 22:15:00 UTC (rev 192035)
</span><span class="lines">@@ -56,6 +56,7 @@
</span><span class="cx"> FramePointer = FramePointer()
</span><span class="cx"> 
</span><span class="cx"> Patchpoint = Patchpoint()
</span><ins>+Check = Check(value)
</ins><span class="cx"> 
</span><span class="cx"> Upsilon = Upsilon(value)
</span><span class="cx"> Phi = Phi()
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3testb3cpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/testb3.cpp (192034 => 192035)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/testb3.cpp        2015-11-04 21:46:10 UTC (rev 192034)
+++ trunk/Source/JavaScriptCore/b3/testb3.cpp        2015-11-04 22:15:00 UTC (rev 192035)
</span><span class="lines">@@ -1996,6 +1996,33 @@
</span><span class="cx">     CHECK(compileAndRun&lt;int&gt;(proc, 1, 2) == 3);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void testSimpleCheck()
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* arg = root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR0);
+    CheckValue* check = root-&gt;appendNew&lt;CheckValue&gt;(proc, Check, Origin(), arg);
+    check-&gt;setGenerator(
+        [&amp;] (CCallHelpers&amp; jit, const StackmapGenerationParams&amp; params) {
+            CHECK(params.reps.size() == 1);
+            CHECK(params.reps[0].isConstant());
+            CHECK(params.reps[0].value() == 1);
+
+            // This should always work because a function this simple should never have callee
+            // saves.
+            jit.move(CCallHelpers::TrustedImm32(42), GPRInfo::returnValueGPR);
+            jit.emitFunctionEpilogue();
+            jit.ret();
+        });
+    root-&gt;appendNew&lt;ControlValue&gt;(
+        proc, Return, Origin(), root-&gt;appendNew&lt;Const32Value&gt;(proc, Origin(), 0));
+
+    MacroAssemblerCodeRef code = compile(proc);
+    
+    CHECK(invoke&lt;int&gt;(code, 0) == 0);
+    CHECK(invoke&lt;int&gt;(code, 1) == 42);
+}
+
</ins><span class="cx"> #define RUN(test) do {                          \
</span><span class="cx">         if (!shouldRun(#test))                  \
</span><span class="cx">             break;                              \
</span><span class="lines">@@ -2271,6 +2298,7 @@
</span><span class="cx">     RUN(testComplex(4, 384));
</span><span class="cx"> 
</span><span class="cx">     RUN(testSimplePatchpoint());
</span><ins>+    RUN(testSimpleCheck());
</ins><span class="cx"> 
</span><span class="cx">     if (!didRun)
</span><span class="cx">         usage();
</span></span></pre>
</div>
</div>

</body>
</html>