<!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>[201900] 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/201900">201900</a></dd>
<dt>Author</dt> <dd>fpizlo@apple.com</dd>
<dt>Date</dt> <dd>2016-06-09 19:03:33 -0700 (Thu, 09 Jun 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>Rare failure in stress/v8-deltablue-strict.js.ftl-eager
https://bugs.webkit.org/show_bug.cgi?id=158591

Reviewed by Saam Barati.
        
This is a simple and sensible fix to an amazing compiler bug that previously only
manifested rarely in the v8-deltablue-strict test. It required on average 1000 runs while
the system was under load for the bug to manifest. Fortunately, the bug is 100% repro with
concurrent JIT disabled in the new &quot;constant-fold-multi-get-by-offset-to-get-by-offset-on-
prototype-and-sink-allocation.js&quot; test.
        
The problem here is that we were allowing ourselves to be super sloppy with the meaning of
the two children of GetByOffset, and to a lesser extent, PutByOffset. The first two
children of these nodes have these meanings:
        
child1: the storage from which to load (or to which to store)
child2: the logical object base
        
Normally, child1 == child2, but child1 may point to a node that vends the storage pointer
in case we are using multiple indirections to get to the property. That's fairly common.
        
Where this gets nutty is that we don't validate the behavior of child1. Previously, the
DFG::Validate phase would accept code that had child1 point to one object and child2 point
to another object. That's bad because then, analyses will assume that we're loading from
one object while we are actually loading from another. One of the fixes is to make
Validate smarter about this, so that future problems with this get caught sooner.
        
The actual bug was in ConstantFoldingPhase. When we first wrote ConstantFoldingPhase's
logic for converting GetByIds and MultiGetByOffsets to GetByOffset, we assumed that this
was only for non-prototype loads. This was becuase the logic was originally written based
on a static GetByIdStatus analysis, which does not handle prototypes. So, as a shortcut,
we would convert the GetById (or MultiGetByOffset) to a GetByOffset by doing this
shuffling of children:
        
child1 got the storage pointer, which might be a new GetButterfly node that we created.
child2 got the old value of child1.
        
The bug was introduced when I later made it possible for a monomorphic prototype
MultiGetByOffset to be converted to a GetByOffset. Then this algorithm would mean that:
        
child1 got either a pointer to the prototype or a storage pointer derived from the
    prototype.
child2 got the old value of child1, which was a pointer to the base object (i.e. not the
    prototype).
        
This happens super rarely because most prototype loads that we can statically reason about
also happen to load constants, so we don't convert to GetByOffset at all. You need the
strange combination of a MultiGetByOffset (not GetById or GetByOffset) on some prototypes
and some static reasoning about the base so that we can convert it to a GetByOffset, but
not enough static reasoning that we can convert it to a constant.
        
Even if the bad thing happened, then this is not enough for it to cause symptons. If we
did nothing else - like none of the other optimizations succeeded - then this would
be OK because the backend will emit code based on child1, which is right. But disaster
strikes when the code otherwise looks sane enough for ObjectAllocationSinkingPhase to kick
in. This phase operates on child2, as any good phase should: child1 is only interesting
for knowing *how* to load, not *what* we are loading. The phase is right to ignore child1.

So the phase would assume that we are loading the prototype property (&quot;f&quot; in the new test
or &quot;addToGraph&quot; in deltablue) from the sunken base object allocation in the inlined
constructor. The base object has no such property, but the phase conservatively assumes
that it does indeed have such a property. That's just how the phase does things: it is
very abstract and general, so it assumes that the set of properties on an allocation is
the set of properties that accesses to the allocation speak of. Clearly, this GetByOffset
was speaking of the property as being on the allocation. When sinking completed, it would
convert the GetByOffset to the sunken (a.k.a. promoted) property. But nobody stored to
this property on the allocation, so we'd get the bottom value, which is 1927. Why 1927? I
don't remember anymore, but apparently I chose it. It helped here - when I started seeing
that value come up, it took a quick grep to realize that this was the object allocation
sinking phase's bottom value.
        
The real fix to the bug is to make Node::convertToGetByOffset() take an explicit new base
since its clients will use it to potentially create a load on a different object than the
base of the original operation, as in the relatively new
MultiGetByOffset(prototype)-&gt;GetByOffset optimization. As far as I know, the PutByOffset
code did not have the same bug because we don't have any optimizations that turn a PutById
or MultiPutByOffset into a PutByOffset on anything but the base object. But the logical
bug is definitely there: there's code in ConstantFoldingPhase that claims to be able to
convert any node to a PutByOffset on any base, but it actually silently reuses the
original node's child1 as the logical base (i.e. child2). This patch makes all of this
stuff explicit. You can't make this mistake anymore.

* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::emitGetByOffset):
(JSC::DFG::ConstantFoldingPhase::emitPutByOffset):
* dfg/DFGNode.h:
(JSC::DFG::Node::convertToGetStack):
(JSC::DFG::Node::convertToGetByOffset):
(JSC::DFG::Node::convertToMultiGetByOffset):
(JSC::DFG::Node::convertToPutByOffset):
* dfg/DFGValidate.cpp:
* tests/stress/constant-fold-multi-get-by-offset-to-get-by-offset-on-prototype-and-sink-allocation.js: Added.
(ThingA):
(ThingB):
(foo):
(bar):
* tests/stress/sink-to-impossible-multi-get-by-offset-on-prototypes.js: Added.
(ThingA):
(ThingB):
(ThingC):
(bar):
(foo):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGConstantFoldingPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGNodeh">trunk/Source/JavaScriptCore/dfg/DFGNode.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGValidatecpp">trunk/Source/JavaScriptCore/dfg/DFGValidate.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoretestsstressconstantfoldmultigetbyoffsettogetbyoffsetonprototypeandsinkallocationjs">trunk/Source/JavaScriptCore/tests/stress/constant-fold-multi-get-by-offset-to-get-by-offset-on-prototype-and-sink-allocation.js</a></li>
<li><a href="#trunkSourceJavaScriptCoretestsstresssinktoimpossiblemultigetbyoffsetonprototypesjs">trunk/Source/JavaScriptCore/tests/stress/sink-to-impossible-multi-get-by-offset-on-prototypes.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (201899 => 201900)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2016-06-10 01:54:13 UTC (rev 201899)
+++ trunk/Source/JavaScriptCore/ChangeLog        2016-06-10 02:03:33 UTC (rev 201900)
</span><span class="lines">@@ -1,3 +1,108 @@
</span><ins>+2016-06-09  Filip Pizlo  &lt;fpizlo@apple.com&gt;
+
+        Rare failure in stress/v8-deltablue-strict.js.ftl-eager
+        https://bugs.webkit.org/show_bug.cgi?id=158591
+
+        Reviewed by Saam Barati.
+        
+        This is a simple and sensible fix to an amazing compiler bug that previously only
+        manifested rarely in the v8-deltablue-strict test. It required on average 1000 runs while
+        the system was under load for the bug to manifest. Fortunately, the bug is 100% repro with
+        concurrent JIT disabled in the new &quot;constant-fold-multi-get-by-offset-to-get-by-offset-on-
+        prototype-and-sink-allocation.js&quot; test.
+        
+        The problem here is that we were allowing ourselves to be super sloppy with the meaning of
+        the two children of GetByOffset, and to a lesser extent, PutByOffset. The first two
+        children of these nodes have these meanings:
+        
+        child1: the storage from which to load (or to which to store)
+        child2: the logical object base
+        
+        Normally, child1 == child2, but child1 may point to a node that vends the storage pointer
+        in case we are using multiple indirections to get to the property. That's fairly common.
+        
+        Where this gets nutty is that we don't validate the behavior of child1. Previously, the
+        DFG::Validate phase would accept code that had child1 point to one object and child2 point
+        to another object. That's bad because then, analyses will assume that we're loading from
+        one object while we are actually loading from another. One of the fixes is to make
+        Validate smarter about this, so that future problems with this get caught sooner.
+        
+        The actual bug was in ConstantFoldingPhase. When we first wrote ConstantFoldingPhase's
+        logic for converting GetByIds and MultiGetByOffsets to GetByOffset, we assumed that this
+        was only for non-prototype loads. This was becuase the logic was originally written based
+        on a static GetByIdStatus analysis, which does not handle prototypes. So, as a shortcut,
+        we would convert the GetById (or MultiGetByOffset) to a GetByOffset by doing this
+        shuffling of children:
+        
+        child1 got the storage pointer, which might be a new GetButterfly node that we created.
+        child2 got the old value of child1.
+        
+        The bug was introduced when I later made it possible for a monomorphic prototype
+        MultiGetByOffset to be converted to a GetByOffset. Then this algorithm would mean that:
+        
+        child1 got either a pointer to the prototype or a storage pointer derived from the
+            prototype.
+        child2 got the old value of child1, which was a pointer to the base object (i.e. not the
+            prototype).
+        
+        This happens super rarely because most prototype loads that we can statically reason about
+        also happen to load constants, so we don't convert to GetByOffset at all. You need the
+        strange combination of a MultiGetByOffset (not GetById or GetByOffset) on some prototypes
+        and some static reasoning about the base so that we can convert it to a GetByOffset, but
+        not enough static reasoning that we can convert it to a constant.
+        
+        Even if the bad thing happened, then this is not enough for it to cause symptons. If we
+        did nothing else - like none of the other optimizations succeeded - then this would
+        be OK because the backend will emit code based on child1, which is right. But disaster
+        strikes when the code otherwise looks sane enough for ObjectAllocationSinkingPhase to kick
+        in. This phase operates on child2, as any good phase should: child1 is only interesting
+        for knowing *how* to load, not *what* we are loading. The phase is right to ignore child1.
+
+        So the phase would assume that we are loading the prototype property (&quot;f&quot; in the new test
+        or &quot;addToGraph&quot; in deltablue) from the sunken base object allocation in the inlined
+        constructor. The base object has no such property, but the phase conservatively assumes
+        that it does indeed have such a property. That's just how the phase does things: it is
+        very abstract and general, so it assumes that the set of properties on an allocation is
+        the set of properties that accesses to the allocation speak of. Clearly, this GetByOffset
+        was speaking of the property as being on the allocation. When sinking completed, it would
+        convert the GetByOffset to the sunken (a.k.a. promoted) property. But nobody stored to
+        this property on the allocation, so we'd get the bottom value, which is 1927. Why 1927? I
+        don't remember anymore, but apparently I chose it. It helped here - when I started seeing
+        that value come up, it took a quick grep to realize that this was the object allocation
+        sinking phase's bottom value.
+        
+        The real fix to the bug is to make Node::convertToGetByOffset() take an explicit new base
+        since its clients will use it to potentially create a load on a different object than the
+        base of the original operation, as in the relatively new
+        MultiGetByOffset(prototype)-&gt;GetByOffset optimization. As far as I know, the PutByOffset
+        code did not have the same bug because we don't have any optimizations that turn a PutById
+        or MultiPutByOffset into a PutByOffset on anything but the base object. But the logical
+        bug is definitely there: there's code in ConstantFoldingPhase that claims to be able to
+        convert any node to a PutByOffset on any base, but it actually silently reuses the
+        original node's child1 as the logical base (i.e. child2). This patch makes all of this
+        stuff explicit. You can't make this mistake anymore.
+
+        * dfg/DFGConstantFoldingPhase.cpp:
+        (JSC::DFG::ConstantFoldingPhase::emitGetByOffset):
+        (JSC::DFG::ConstantFoldingPhase::emitPutByOffset):
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::convertToGetStack):
+        (JSC::DFG::Node::convertToGetByOffset):
+        (JSC::DFG::Node::convertToMultiGetByOffset):
+        (JSC::DFG::Node::convertToPutByOffset):
+        * dfg/DFGValidate.cpp:
+        * tests/stress/constant-fold-multi-get-by-offset-to-get-by-offset-on-prototype-and-sink-allocation.js: Added.
+        (ThingA):
+        (ThingB):
+        (foo):
+        (bar):
+        * tests/stress/sink-to-impossible-multi-get-by-offset-on-prototypes.js: Added.
+        (ThingA):
+        (ThingB):
+        (ThingC):
+        (bar):
+        (foo):
+
</ins><span class="cx"> 2016-06-09  Mark Lam  &lt;mark.lam@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Make some methods const.
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGConstantFoldingPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp (201899 => 201900)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp        2016-06-10 01:54:13 UTC (rev 201899)
+++ trunk/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp        2016-06-10 02:03:33 UTC (rev 201900)
</span><span class="lines">@@ -707,7 +707,7 @@
</span><span class="cx">         data.identifierNumber = identifierNumber;
</span><span class="cx">         data.inferredType = inferredType;
</span><span class="cx">         
</span><del>-        node-&gt;convertToGetByOffset(data, propertyStorage);
</del><ins>+        node-&gt;convertToGetByOffset(data, propertyStorage, childEdge);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     void emitPutByOffset(unsigned indexInBlock, Node* node, const AbstractValue&amp; baseValue, const PutByIdVariant&amp; variant, unsigned identifierNumber)
</span><span class="lines">@@ -762,7 +762,7 @@
</span><span class="cx">         data.offset = variant.offset();
</span><span class="cx">         data.identifierNumber = identifierNumber;
</span><span class="cx">         
</span><del>-        node-&gt;convertToPutByOffset(data, propertyStorage);
</del><ins>+        node-&gt;convertToPutByOffset(data, propertyStorage, childEdge);
</ins><span class="cx">         node-&gt;origin.exitOK = canExit;
</span><span class="cx"> 
</span><span class="cx">         if (variant.kind() == PutByIdVariant::Transition) {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGNodeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGNode.h (201899 => 201900)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGNode.h        2016-06-10 01:54:13 UTC (rev 201899)
+++ trunk/Source/JavaScriptCore/dfg/DFGNode.h        2016-06-10 02:03:33 UTC (rev 201900)
</span><span class="lines">@@ -524,13 +524,12 @@
</span><span class="cx">         children.reset();
</span><span class="cx">     }
</span><span class="cx">     
</span><del>-    void convertToGetByOffset(StorageAccessData&amp; data, Edge storage)
</del><ins>+    void convertToGetByOffset(StorageAccessData&amp; data, Edge storage, Edge base)
</ins><span class="cx">     {
</span><span class="cx">         ASSERT(m_op == GetById || m_op == GetByIdFlush || m_op == MultiGetByOffset);
</span><span class="cx">         m_opInfo = bitwise_cast&lt;uintptr_t&gt;(&amp;data);
</span><del>-        children.setChild2(children.child1());
-        children.child2().setUseKind(KnownCellUse);
</del><span class="cx">         children.setChild1(storage);
</span><ins>+        children.setChild2(base);
</ins><span class="cx">         m_op = GetByOffset;
</span><span class="cx">         m_flags &amp;= ~NodeMustGenerate;
</span><span class="cx">     }
</span><span class="lines">@@ -544,12 +543,12 @@
</span><span class="cx">         ASSERT(m_flags &amp; NodeMustGenerate);
</span><span class="cx">     }
</span><span class="cx">     
</span><del>-    void convertToPutByOffset(StorageAccessData&amp; data, Edge storage)
</del><ins>+    void convertToPutByOffset(StorageAccessData&amp; data, Edge storage, Edge base)
</ins><span class="cx">     {
</span><span class="cx">         ASSERT(m_op == PutById || m_op == PutByIdDirect || m_op == PutByIdFlush || m_op == MultiPutByOffset);
</span><span class="cx">         m_opInfo = bitwise_cast&lt;uintptr_t&gt;(&amp;data);
</span><span class="cx">         children.setChild3(children.child2());
</span><del>-        children.setChild2(children.child1());
</del><ins>+        children.setChild2(base);
</ins><span class="cx">         children.setChild1(storage);
</span><span class="cx">         m_op = PutByOffset;
</span><span class="cx">     }
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGValidatecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGValidate.cpp (201899 => 201900)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGValidate.cpp        2016-06-10 01:54:13 UTC (rev 201899)
+++ trunk/Source/JavaScriptCore/dfg/DFGValidate.cpp        2016-06-10 02:03:33 UTC (rev 201900)
</span><span class="lines">@@ -297,6 +297,10 @@
</span><span class="cx">                 case Int52Constant:
</span><span class="cx">                     VALIDATE((node), node-&gt;isNumberConstant());
</span><span class="cx">                     break;
</span><ins>+                case GetByOffset:
+                case PutByOffset:
+                    VALIDATE((node), node-&gt;child1().node() == node-&gt;child2().node() || node-&gt;child1()-&gt;result() == NodeResultStorage);
+                    break;
</ins><span class="cx">                 default:
</span><span class="cx">                     break;
</span><span class="cx">                 }
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstressconstantfoldmultigetbyoffsettogetbyoffsetonprototypeandsinkallocationjs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/stress/constant-fold-multi-get-by-offset-to-get-by-offset-on-prototype-and-sink-allocation.js (0 => 201900)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/constant-fold-multi-get-by-offset-to-get-by-offset-on-prototype-and-sink-allocation.js                                (rev 0)
+++ trunk/Source/JavaScriptCore/tests/stress/constant-fold-multi-get-by-offset-to-get-by-offset-on-prototype-and-sink-allocation.js        2016-06-10 02:03:33 UTC (rev 201900)
</span><span class="lines">@@ -0,0 +1,38 @@
</span><ins>+function ThingA() {
+}
+
+ThingA.prototype = {f:1};
+
+function ThingB() {
+}
+
+ThingB.prototype = {f:2};
+
+function foo(o, p) {
+    return p ? o.f : -1;
+}
+
+for (var i = 0; i &lt; 10000; ++i) {
+    foo(new ThingA(), true);
+    foo(new ThingB(), true);
+    ThingA.prototype.f = i;
+    ThingB.prototype.f = i + 1;
+}
+
+function bar(p) {
+    return foo(new ThingA(), p);
+}
+
+ThingA.prototype.f = 42;
+
+for (var i = 0; i &lt; 10000; ++i) {
+    var result = bar(false);
+    if (result != -1)
+        throw new Error(&quot;Bad result in loop: &quot; + result);
+}
+
+var result = bar(true);
+if (result != 42)
+    throw new Error(&quot;Bad result: &quot; + result);
+
+
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstresssinktoimpossiblemultigetbyoffsetonprototypesjs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/stress/sink-to-impossible-multi-get-by-offset-on-prototypes.js (0 => 201900)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/sink-to-impossible-multi-get-by-offset-on-prototypes.js                                (rev 0)
+++ trunk/Source/JavaScriptCore/tests/stress/sink-to-impossible-multi-get-by-offset-on-prototypes.js        2016-06-10 02:03:33 UTC (rev 201900)
</span><span class="lines">@@ -0,0 +1,41 @@
</span><ins>+&quot;use strict&quot;;
+
+function ThingA() {
+}
+
+ThingA.prototype = {bug: 42};
+
+function ThingB() {
+}
+
+ThingB.prototype = {bug: 43};
+
+function ThingC() {
+}
+
+ThingC.prototype = {bug: 44};
+
+function bar(o, p) {
+    if (p)
+        return o.bug;
+    return null;
+}
+
+function foo(p) {
+    var o = new ThingC();
+    return bar(o, p);
+}
+
+noInline(foo);
+
+for (var i = 0; i &lt; 10000; ++i) {
+    bar(new ThingA(), true);
+    bar(new ThingB(), true);
+}
+
+for (var i = 0; i &lt; 10000; ++i)
+    foo(false);
+
+var result = foo(true);
+if (result != 44)
+    throw new Error(&quot;Bad result: &quot; + result);
</ins></span></pre>
</div>
</div>

</body>
</html>