<!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>[183752] 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/183752">183752</a></dd>
<dt>Author</dt> <dd>basile_clement@apple.com</dd>
<dt>Date</dt> <dd>2015-05-04 11:37:58 -0700 (Mon, 04 May 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>Object allocation not sinking properly through CheckStructure
https://bugs.webkit.org/show_bug.cgi?id=144465

Reviewed by Filip Pizlo.

Currently, sinking an allocation through a CheckStructure will
completely ignore all structure checking, which is obviously wrong.

A CheckStructureImmediate node type was present for that purpose, but
the CheckStructures were not properly replaced.  This ensures that
CheckStructure nodes are replaced by CheckStructureImmediate nodes when
sunk through, and that structure checking happens correctly.

* dfg/DFGNode.h:
(JSC::DFG::Node::convertToCheckStructureImmediate): Added.
(JSC::DFG::Node::hasStructureSet):
* dfg/DFGObjectAllocationSinkingPhase.cpp:
(JSC::DFG::ObjectAllocationSinkingPhase::promoteSunkenFields):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::compileCheckStructure):
(JSC::FTL::LowerDFGToLLVM::compileCheckStructureImmediate):
(JSC::FTL::LowerDFGToLLVM::checkStructure):
* tests/stress/sink_checkstructure.js: Added.
(foo):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGNodeh">trunk/Source/JavaScriptCore/dfg/DFGNode.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGObjectAllocationSinkingPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLLowerDFGToLLVMcpp">trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoretestsstresssink_checkstructurejs">trunk/Source/JavaScriptCore/tests/stress/sink_checkstructure.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (183751 => 183752)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2015-05-04 18:26:00 UTC (rev 183751)
+++ trunk/Source/JavaScriptCore/ChangeLog        2015-05-04 18:37:58 UTC (rev 183752)
</span><span class="lines">@@ -1,3 +1,30 @@
</span><ins>+2015-05-04  Basile Clement  &lt;basile_clement@apple.com&gt;
+
+        Object allocation not sinking properly through CheckStructure
+        https://bugs.webkit.org/show_bug.cgi?id=144465
+
+        Reviewed by Filip Pizlo.
+
+        Currently, sinking an allocation through a CheckStructure will
+        completely ignore all structure checking, which is obviously wrong.
+
+        A CheckStructureImmediate node type was present for that purpose, but
+        the CheckStructures were not properly replaced.  This ensures that
+        CheckStructure nodes are replaced by CheckStructureImmediate nodes when
+        sunk through, and that structure checking happens correctly.
+
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::convertToCheckStructureImmediate): Added.
+        (JSC::DFG::Node::hasStructureSet):
+        * dfg/DFGObjectAllocationSinkingPhase.cpp:
+        (JSC::DFG::ObjectAllocationSinkingPhase::promoteSunkenFields):
+        * ftl/FTLLowerDFGToLLVM.cpp:
+        (JSC::FTL::LowerDFGToLLVM::compileCheckStructure):
+        (JSC::FTL::LowerDFGToLLVM::compileCheckStructureImmediate):
+        (JSC::FTL::LowerDFGToLLVM::checkStructure):
+        * tests/stress/sink_checkstructure.js: Added.
+        (foo):
+
</ins><span class="cx"> 2015-05-01  Geoffrey Garen  &lt;ggaren@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         REGRESSION(r183570): jslib-traverse-jquery is 22% slower
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGNodeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGNode.h (183751 => 183752)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGNode.h        2015-05-04 18:26:00 UTC (rev 183751)
+++ trunk/Source/JavaScriptCore/dfg/DFGNode.h        2015-05-04 18:37:58 UTC (rev 183752)
</span><span class="lines">@@ -414,6 +414,13 @@
</span><span class="cx">         setOpAndDefaultFlags(CheckStructure);
</span><span class="cx">         m_opInfo = bitwise_cast&lt;uintptr_t&gt;(set); 
</span><span class="cx">     }
</span><ins>+
+    void convertToCheckStructureImmediate(Node* structure)
+    {
+        ASSERT(op() == CheckStructure);
+        m_op = CheckStructureImmediate;
+        children.setChild1(Edge(structure, CellUse));
+    }
</ins><span class="cx">     
</span><span class="cx">     void replaceWith(Node* other)
</span><span class="cx">     {
</span><span class="lines">@@ -1334,6 +1341,7 @@
</span><span class="cx">     {
</span><span class="cx">         switch (op()) {
</span><span class="cx">         case CheckStructure:
</span><ins>+        case CheckStructureImmediate:
</ins><span class="cx">             return true;
</span><span class="cx">         default:
</span><span class="cx">             return false;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGObjectAllocationSinkingPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp (183751 => 183752)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp        2015-05-04 18:26:00 UTC (rev 183751)
+++ trunk/Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp        2015-05-04 18:37:58 UTC (rev 183752)
</span><span class="lines">@@ -728,8 +728,17 @@
</span><span class="cx">                             m_localMapping.set(location, value.node());
</span><span class="cx">                     },
</span><span class="cx">                     [&amp;] (PromotedHeapLocation location) {
</span><del>-                        if (m_sinkCandidates.contains(location.base()))
-                            node-&gt;replaceWith(resolve(block, location));
</del><ins>+                        if (m_sinkCandidates.contains(location.base())) {
+                            switch (node-&gt;op()) {
+                            case CheckStructure:
+                                node-&gt;convertToCheckStructureImmediate(resolve(block, location));
+                                break;
+
+                            default:
+                                node-&gt;replaceWith(resolve(block, location));
+                                break;
+                            }
+                        }
</ins><span class="cx">                     });
</span><span class="cx">             }
</span><span class="cx">             
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLLowerDFGToLLVMcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp (183751 => 183752)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp        2015-05-04 18:26:00 UTC (rev 183751)
+++ trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp        2015-05-04 18:37:58 UTC (rev 183752)
</span><span class="lines">@@ -1882,7 +1882,11 @@
</span><span class="cx">         
</span><span class="cx">         LValue structureID = m_out.load32(cell, m_heaps.JSCell_structureID);
</span><span class="cx">         
</span><del>-        checkStructure(structureID, jsValueValue(cell), exitKind, m_node-&gt;structureSet());
</del><ins>+        checkStructure(
+            structureID, jsValueValue(cell), exitKind, m_node-&gt;structureSet(),
+            [this] (Structure* structure) {
+                return weakStructureID(structure);
+            });
</ins><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     void compileCheckCell()
</span><span class="lines">@@ -5089,8 +5093,12 @@
</span><span class="cx">     
</span><span class="cx">     void compileCheckStructureImmediate()
</span><span class="cx">     {
</span><del>-        LValue structureID = lowCell(m_node-&gt;child1());
-        checkStructure(structureID, noValue(), BadCache, m_node-&gt;structureSet());
</del><ins>+        LValue structure = lowCell(m_node-&gt;child1());
+        checkStructure(
+            structure, noValue(), BadCache, m_node-&gt;structureSet(),
+            [this] (Structure* structure) {
+                return weakStructure(structure);
+            });
</ins><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     void compileMaterializeNewObject()
</span><span class="lines">@@ -5430,14 +5438,15 @@
</span><span class="cx">         return getArgumentsStart(m_node-&gt;origin.semantic.inlineCallFrame);
</span><span class="cx">     }
</span><span class="cx">     
</span><ins>+    template&lt;typename Functor&gt;
</ins><span class="cx">     void checkStructure(
</span><del>-        LValue structureID, const FormattedValue&amp; formattedValue, ExitKind exitKind,
-        const StructureSet&amp; set)
</del><ins>+        LValue structureDiscriminant, const FormattedValue&amp; formattedValue, ExitKind exitKind,
+        const StructureSet&amp; set, const Functor&amp; weakStructureDiscriminant)
</ins><span class="cx">     {
</span><span class="cx">         if (set.size() == 1) {
</span><span class="cx">             speculate(
</span><span class="cx">                 exitKind, formattedValue, 0,
</span><del>-                m_out.notEqual(structureID, weakStructureID(set[0])));
</del><ins>+                m_out.notEqual(structureDiscriminant, weakStructureDiscriminant(set[0])));
</ins><span class="cx">             return;
</span><span class="cx">         }
</span><span class="cx">         
</span><span class="lines">@@ -5447,14 +5456,14 @@
</span><span class="cx">         for (unsigned i = 0; i &lt; set.size() - 1; ++i) {
</span><span class="cx">             LBasicBlock nextStructure = FTL_NEW_BLOCK(m_out, (&quot;checkStructure nextStructure&quot;));
</span><span class="cx">             m_out.branch(
</span><del>-                m_out.equal(structureID, weakStructureID(set[i])),
</del><ins>+                m_out.equal(structureDiscriminant, weakStructureDiscriminant(set[i])),
</ins><span class="cx">                 unsure(continuation), unsure(nextStructure));
</span><span class="cx">             m_out.appendTo(nextStructure);
</span><span class="cx">         }
</span><span class="cx">         
</span><span class="cx">         speculate(
</span><span class="cx">             exitKind, formattedValue, 0,
</span><del>-            m_out.notEqual(structureID, weakStructureID(set.last())));
</del><ins>+            m_out.notEqual(structureDiscriminant, weakStructureDiscriminant(set.last())));
</ins><span class="cx">         
</span><span class="cx">         m_out.jump(continuation);
</span><span class="cx">         m_out.appendTo(continuation, lastNext);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstresssink_checkstructurejs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/stress/sink_checkstructure.js (0 => 183752)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/sink_checkstructure.js                                (rev 0)
+++ trunk/Source/JavaScriptCore/tests/stress/sink_checkstructure.js        2015-05-04 18:37:58 UTC (rev 183752)
</span><span class="lines">@@ -0,0 +1,17 @@
</span><ins>+function foo(p, q) {
+    var o = {};
+    if (p) o.f = 42;
+    if (q) { o.f++; return o; }
+}
+noInline(foo);
+
+var expected = foo(false, true).f;
+
+for (var i = 0; i &lt; 1000000; i++) {
+    foo(true, true);
+}
+
+var result = foo(false, true).f;
+
+if (!Object.is(result, expected))
+    throw &quot;Error: expected &quot; + expected + &quot;; FTL produced &quot; + result;
</ins></span></pre>
</div>
</div>

</body>
</html>