<!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>[189658] 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/189658">189658</a></dd>
<dt>Author</dt> <dd>fpizlo@apple.com</dd>
<dt>Date</dt> <dd>2015-09-12 13:52:26 -0700 (Sat, 12 Sep 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>REGRESSION(<a href="http://trac.webkit.org/projects/webkit/changeset/189585">r189585</a>): run-perf-tests Speedometer fails with a console error
https://bugs.webkit.org/show_bug.cgi?id=149066

Reviewed by Michael Saboff.

The bug here was that the new IC code was calling actionForCell() more than once. That's
illegal, since when actionForCell() returns RetryCacheLater, it means that it changed some
object's Structure. The Repatch code was doing things like &quot;if (actionForCell(blah) ==
AttemptToCache)&quot; in more than one place, so that if the first such expression was false, then
we'd fall through to the next one. It's possible for the first call to return RetryCacheLater,
in which case our view of the world just got clobbered and we need to return, and then the
second call will probably return AttemptToCache because it *thinks* that we had bailed the last
time and we're now in a future IC invocation.

The solution is to cache the actionForCell() result. This is a bit tricky, because we need to
do this after we check if we're in a proxy.

Debugging bugs like these requires adding ad hoc bisection code in various places. We already
had the basic hooks for this. This patch makes those hooks a bit more useful. In the case of
the LLInt-&gt;JIT tier-up hooks, it adds a CodeBlock* argument so that we can bisect based on the
CodeBlock. In the case of Repatch, it puts the Options::forceICFailure() check in a helper
function that also takes ExecState*, which allows us to bisect on either CodeBlock or
CodeOrigin.

* jit/Repatch.cpp:
(JSC::actionForCell):
(JSC::forceICFailure):
(JSC::tryCacheGetByID):
(JSC::tryCachePutByID):
(JSC::tryRepatchIn):
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::shouldJIT):
(JSC::LLInt::jitCompileAndSetHeuristics):
(JSC::LLInt::entryOSR):
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* tests/stress/retry-cache-later.js:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCorejitRepatchcpp">trunk/Source/JavaScriptCore/jit/Repatch.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorellintLLIntSlowPathscpp">trunk/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoretestsstressretrycachelaterjs">trunk/Source/JavaScriptCore/tests/stress/retry-cache-later.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (189657 => 189658)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2015-09-12 19:02:17 UTC (rev 189657)
+++ trunk/Source/JavaScriptCore/ChangeLog        2015-09-12 20:52:26 UTC (rev 189658)
</span><span class="lines">@@ -1,3 +1,42 @@
</span><ins>+2015-09-11  Filip Pizlo  &lt;fpizlo@apple.com&gt;
+
+        REGRESSION(r189585): run-perf-tests Speedometer fails with a console error
+        https://bugs.webkit.org/show_bug.cgi?id=149066
+
+        Reviewed by Michael Saboff.
+
+        The bug here was that the new IC code was calling actionForCell() more than once. That's
+        illegal, since when actionForCell() returns RetryCacheLater, it means that it changed some
+        object's Structure. The Repatch code was doing things like &quot;if (actionForCell(blah) ==
+        AttemptToCache)&quot; in more than one place, so that if the first such expression was false, then
+        we'd fall through to the next one. It's possible for the first call to return RetryCacheLater,
+        in which case our view of the world just got clobbered and we need to return, and then the
+        second call will probably return AttemptToCache because it *thinks* that we had bailed the last
+        time and we're now in a future IC invocation.
+
+        The solution is to cache the actionForCell() result. This is a bit tricky, because we need to
+        do this after we check if we're in a proxy.
+
+        Debugging bugs like these requires adding ad hoc bisection code in various places. We already
+        had the basic hooks for this. This patch makes those hooks a bit more useful. In the case of
+        the LLInt-&gt;JIT tier-up hooks, it adds a CodeBlock* argument so that we can bisect based on the
+        CodeBlock. In the case of Repatch, it puts the Options::forceICFailure() check in a helper
+        function that also takes ExecState*, which allows us to bisect on either CodeBlock or
+        CodeOrigin.
+
+        * jit/Repatch.cpp:
+        (JSC::actionForCell):
+        (JSC::forceICFailure):
+        (JSC::tryCacheGetByID):
+        (JSC::tryCachePutByID):
+        (JSC::tryRepatchIn):
+        * llint/LLIntSlowPaths.cpp:
+        (JSC::LLInt::shouldJIT):
+        (JSC::LLInt::jitCompileAndSetHeuristics):
+        (JSC::LLInt::entryOSR):
+        (JSC::LLInt::LLINT_SLOW_PATH_DECL):
+        * tests/stress/retry-cache-later.js:
+
</ins><span class="cx"> 2015-09-11  Sukolsak Sakshuwong  &lt;sukolsak@gmail.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Implement the relational instructions for floats in WebAssembly
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorejitRepatchcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/jit/Repatch.cpp (189657 => 189658)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/jit/Repatch.cpp        2015-09-12 19:02:17 UTC (rev 189657)
+++ trunk/Source/JavaScriptCore/jit/Repatch.cpp        2015-09-12 20:52:26 UTC (rev 189658)
</span><span class="lines">@@ -211,9 +211,14 @@
</span><span class="cx">     return AttemptToCache;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+static bool forceICFailure(ExecState*)
+{
+    return Options::forceICFailure();
+}
+
</ins><span class="cx"> static InlineCacheAction tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier&amp; propertyName, const PropertySlot&amp; slot, StructureStubInfo&amp; stubInfo)
</span><span class="cx"> {
</span><del>-    if (Options::forceICFailure())
</del><ins>+    if (forceICFailure(exec))
</ins><span class="cx">         return GiveUpOnCache;
</span><span class="cx">     
</span><span class="cx">     // FIXME: Cache property access for immediates.
</span><span class="lines">@@ -236,6 +241,18 @@
</span><span class="cx"> 
</span><span class="cx">         JSCell* baseCell = baseValue.asCell();
</span><span class="cx">         Structure* structure = baseCell-&gt;structure(vm);
</span><ins>+
+        bool loadTargetFromProxy = false;
+        if (baseCell-&gt;type() == PureForwardingProxyType) {
+            baseValue = jsCast&lt;JSProxy*&gt;(baseCell)-&gt;target();
+            baseCell = baseValue.asCell();
+            structure = baseCell-&gt;structure(vm);
+            loadTargetFromProxy = true;
+        }
+
+        InlineCacheAction action = actionForCell(vm, baseCell);
+        if (action != AttemptToCache)
+            return action;
</ins><span class="cx">         
</span><span class="cx">         // Optimize self access.
</span><span class="cx">         if (stubInfo.cacheType == CacheType::Unset
</span><span class="lines">@@ -243,26 +260,15 @@
</span><span class="cx">             &amp;&amp; slot.slotBase() == baseValue
</span><span class="cx">             &amp;&amp; !slot.watchpointSet()
</span><span class="cx">             &amp;&amp; MacroAssembler::isCompactPtrAlignedAddressOffset(maxOffsetRelativeToPatchedStorage(slot.cachedOffset()))
</span><del>-            &amp;&amp; actionForCell(vm, baseCell) == AttemptToCache
-            &amp;&amp; !structure-&gt;needImpurePropertyWatchpoint()) {
</del><ins>+            &amp;&amp; action == AttemptToCache
+            &amp;&amp; !structure-&gt;needImpurePropertyWatchpoint()
+            &amp;&amp; !loadTargetFromProxy) {
</ins><span class="cx">             structure-&gt;startWatchingPropertyForReplacements(vm, slot.cachedOffset());
</span><span class="cx">             repatchByIdSelfAccess(codeBlock, stubInfo, structure, slot.cachedOffset(), operationGetByIdOptimize, true);
</span><span class="cx">             stubInfo.initGetByIdSelf(vm, codeBlock-&gt;ownerExecutable(), structure, slot.cachedOffset());
</span><span class="cx">             return RetryCacheLater;
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        bool loadTargetFromProxy = false;
-        if (baseCell-&gt;type() == PureForwardingProxyType) {
-            baseValue = jsCast&lt;JSProxy*&gt;(baseCell)-&gt;target();
-            baseCell = baseValue.asCell();
-            structure = baseCell-&gt;structure(vm);
-            loadTargetFromProxy = true;
-        }
-
-        InlineCacheAction action = actionForCell(vm, baseCell);
-        if (action != AttemptToCache)
-            return action;
-
</del><span class="cx">         PropertyOffset offset = slot.isUnset() ? invalidOffset : slot.cachedOffset();
</span><span class="cx">         
</span><span class="cx">         ObjectPropertyConditionSet conditionSet;
</span><span class="lines">@@ -346,7 +352,7 @@
</span><span class="cx"> 
</span><span class="cx"> static InlineCacheAction tryCachePutByID(ExecState* exec, JSValue baseValue, Structure* structure, const Identifier&amp; ident, const PutPropertySlot&amp; slot, StructureStubInfo&amp; stubInfo, PutKind putKind)
</span><span class="cx"> {
</span><del>-    if (Options::forceICFailure())
</del><ins>+    if (forceICFailure(exec))
</ins><span class="cx">         return GiveUpOnCache;
</span><span class="cx">     
</span><span class="cx">     CodeBlock* codeBlock = exec-&gt;codeBlock();
</span><span class="lines">@@ -372,7 +378,7 @@
</span><span class="cx">             if (stubInfo.cacheType == CacheType::Unset
</span><span class="cx">                 &amp;&amp; MacroAssembler::isPtrAlignedAddressOffset(offsetToPatchedStorage)
</span><span class="cx">                 &amp;&amp; !structure-&gt;needImpurePropertyWatchpoint()) {
</span><del>-                
</del><ins>+
</ins><span class="cx">                 repatchByIdSelfAccess(
</span><span class="cx">                     codeBlock, stubInfo, structure, slot.cachedOffset(),
</span><span class="cx">                     appropriateOptimizingPutByIdFunction(slot, putKind), false);
</span><span class="lines">@@ -471,7 +477,7 @@
</span><span class="cx">     ExecState* exec, JSCell* base, const Identifier&amp; ident, bool wasFound,
</span><span class="cx">     const PropertySlot&amp; slot, StructureStubInfo&amp; stubInfo)
</span><span class="cx"> {
</span><del>-    if (Options::forceICFailure())
</del><ins>+    if (forceICFailure(exec))
</ins><span class="cx">         return GiveUpOnCache;
</span><span class="cx">     
</span><span class="cx">     if (!base-&gt;structure()-&gt;propertyAccessesAreCacheable())
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorellintLLIntSlowPathscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp (189657 => 189658)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp        2015-09-12 19:02:17 UTC (rev 189657)
+++ trunk/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp        2015-09-12 20:52:26 UTC (rev 189658)
</span><span class="lines">@@ -286,7 +286,7 @@
</span><span class="cx"> enum EntryKind { Prologue, ArityCheck };
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(JIT)
</span><del>-inline bool shouldJIT(ExecState* exec)
</del><ins>+inline bool shouldJIT(ExecState* exec, CodeBlock*)
</ins><span class="cx"> {
</span><span class="cx">     // You can modify this to turn off JITting without rebuilding the world.
</span><span class="cx">     return exec-&gt;vm().canUseJIT();
</span><span class="lines">@@ -299,7 +299,7 @@
</span><span class="cx">     DeferGCForAWhile deferGC(vm.heap); // My callers don't set top callframe, so we don't want to GC here at all.
</span><span class="cx">     
</span><span class="cx">     codeBlock-&gt;updateAllValueProfilePredictions();
</span><del>-    
</del><ins>+
</ins><span class="cx">     if (!codeBlock-&gt;checkIfJITThresholdReached()) {
</span><span class="cx">         if (Options::verboseOSR())
</span><span class="cx">             dataLogF(&quot;    JIT threshold should be lifted.\n&quot;);
</span><span class="lines">@@ -347,7 +347,7 @@
</span><span class="cx">             codeBlock-&gt;llintExecuteCounter(), &quot;\n&quot;);
</span><span class="cx">     }
</span><span class="cx">     
</span><del>-    if (!shouldJIT(exec)) {
</del><ins>+    if (!shouldJIT(exec, codeBlock)) {
</ins><span class="cx">         codeBlock-&gt;dontJITAnytimeSoon();
</span><span class="cx">         LLINT_RETURN_TWO(0, 0);
</span><span class="cx">     }
</span><span class="lines">@@ -405,7 +405,7 @@
</span><span class="cx">             codeBlock-&gt;llintExecuteCounter(), &quot;\n&quot;);
</span><span class="cx">     }
</span><span class="cx">     
</span><del>-    if (!shouldJIT(exec)) {
</del><ins>+    if (!shouldJIT(exec, codeBlock)) {
</ins><span class="cx">         codeBlock-&gt;dontJITAnytimeSoon();
</span><span class="cx">         LLINT_RETURN_TWO(0, 0);
</span><span class="cx">     }
</span><span class="lines">@@ -443,7 +443,7 @@
</span><span class="cx">             codeBlock-&gt;llintExecuteCounter(), &quot;\n&quot;);
</span><span class="cx">     }
</span><span class="cx">     
</span><del>-    if (shouldJIT(exec))
</del><ins>+    if (shouldJIT(exec, codeBlock))
</ins><span class="cx">         jitCompileAndSetHeuristics(codeBlock, exec);
</span><span class="cx">     else
</span><span class="cx">         codeBlock-&gt;dontJITAnytimeSoon();
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstressretrycachelaterjs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/stress/retry-cache-later.js (0 => 189658)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/retry-cache-later.js                                (rev 0)
+++ trunk/Source/JavaScriptCore/tests/stress/retry-cache-later.js        2015-09-12 20:52:26 UTC (rev 189658)
</span><span class="lines">@@ -0,0 +1,18 @@
</span><ins>+//@ runMiscNoCJITTest(&quot;--useLLInt=false&quot;, &quot;--useDFGJIT=false&quot;)
+
+function foo(o) {
+    return o.i7;
+}
+
+var o = {};
+for (var i = 0; i &lt; 100; ++i)
+    o[&quot;i&quot; + i] = i;
+for (var i = 0; i &lt; 100; i+=2)
+    delete o[&quot;i&quot; + i];
+
+for (var i = 0; i &lt; 100; ++i) {
+    var result = foo(o);
+    if (result != 7)
+        throw &quot;Error: bad result: &quot; + result;
+}
+
</ins></span></pre>
</div>
</div>

</body>
</html>