<!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>[189620] 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/189620">189620</a></dd>
<dt>Author</dt> <dd>ggaren@apple.com</dd>
<dt>Date</dt> <dd>2015-09-11 09:08:39 -0700 (Fri, 11 Sep 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>JavaScriptCore should discard optimized code after some time
https://bugs.webkit.org/show_bug.cgi?id=149048

Reviewed by Michael Saboff.

This patch adds a new jettison type -- JettisonDueToOldAge -- and starts
using it for DFG and FTL code. Baseline and LLInt code will come in a
follow-up patch.

The primary goal is to save memory. Some popular websites leave about 10MB
of dead code sitting around immediately after they finish loading.

Throwing away code periodically might also save us from profiling
pathologies that lead to performance dead ends.

* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::visitAggregate): Updated for rename, and removed a
stale comment.

(JSC::CodeBlock::shouldVisitStrongly): Renamed to shouldVisitStrongly
because the practical effect of this function is to trigger a call to
visitStrongly.

(JSC::CodeBlock::isKnownToBeLiveDuringGC): Check the
m_visitStronglyHasBeenCalled flag instead of
shouldImmediatelyAssumeLivenessDuringScan / shouldVisitStrongly because
m_visitStronglyHasBeenCalled can be set by anybody even if the CodeBlock
would not otherwise visit itself strongly.

(JSC::CodeBlock::shouldJettisonDueToWeakReference): New helper function
for readability.

(JSC::CodeBlock::shouldJettisonDueToOldAge): New helper function that
tells if a CodeBlock is old enough for deletion.

(JSC::CodeBlock::determineLiveness): There's no need to check
shouldImmediatelyAssumeLivenessDuringScan here because we will not call
this function if shouldImmediatelyAssumeLivenessDuringScan is true.
Also, it's just not clear -- if someone chooses to call this function --
that it would be safe to ignore them simply because
shouldImmediatelyAssumeLivenessDuringScan was true.

(JSC::CodeBlock::finalizeLLIntInlineCaches): Moved code out into a helper
function to make the main function more readable.

(JSC::CodeBlock::finalizeBaselineJITInlineCaches): Ditto.

(JSC::CodeBlock::finalizeUnconditionally): Added code for jettisoning a
CodeBlock if it is too old. Moved large sections of code into helper
functions to aid readability in this function.

(JSC::CodeBlock::jettison): Account for the fact that we might jettison
a CodeBlock without OSR exit and without requiring a stack shoot-down.

* bytecode/CodeBlock.h:
(JSC::CodeBlock::setInstallTime):
(JSC::CodeBlock::timeSinceInstall): Track CodeBlock age to help us
decide when to delete.

* jit/JITCode.h:
(JSC::JITCode::timeToLive): Static limits on CodeBlock lifetime. I got
these numbers from the place where numbers come from. 

* profiler/ProfilerJettisonReason.cpp:
(WTF::printInternal):
* profiler/ProfilerJettisonReason.h: Updated for new jettison type.

* runtime/Executable.cpp:
(JSC::ScriptExecutable::installCode): Record install time so that we
can measure how old a CodeBlock is.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCorebytecodeCodeBlockcpp">trunk/Source/JavaScriptCore/bytecode/CodeBlock.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorebytecodeCodeBlockh">trunk/Source/JavaScriptCore/bytecode/CodeBlock.h</a></li>
<li><a href="#trunkSourceJavaScriptCorejitJITCodeh">trunk/Source/JavaScriptCore/jit/JITCode.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreprofilerProfilerJettisonReasoncpp">trunk/Source/JavaScriptCore/profiler/ProfilerJettisonReason.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreprofilerProfilerJettisonReasonh">trunk/Source/JavaScriptCore/profiler/ProfilerJettisonReason.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeExecutablecpp">trunk/Source/JavaScriptCore/runtime/Executable.cpp</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (189619 => 189620)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2015-09-11 16:05:05 UTC (rev 189619)
+++ trunk/Source/JavaScriptCore/ChangeLog        2015-09-11 16:08:39 UTC (rev 189620)
</span><span class="lines">@@ -1,3 +1,76 @@
</span><ins>+2015-09-11  Geoffrey Garen  &lt;ggaren@apple.com&gt;
+
+        JavaScriptCore should discard optimized code after some time
+        https://bugs.webkit.org/show_bug.cgi?id=149048
+
+        Reviewed by Michael Saboff.
+
+        This patch adds a new jettison type -- JettisonDueToOldAge -- and starts
+        using it for DFG and FTL code. Baseline and LLInt code will come in a
+        follow-up patch.
+
+        The primary goal is to save memory. Some popular websites leave about 10MB
+        of dead code sitting around immediately after they finish loading.
+
+        Throwing away code periodically might also save us from profiling
+        pathologies that lead to performance dead ends.
+
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::visitAggregate): Updated for rename, and removed a
+        stale comment.
+
+        (JSC::CodeBlock::shouldVisitStrongly): Renamed to shouldVisitStrongly
+        because the practical effect of this function is to trigger a call to
+        visitStrongly.
+
+        (JSC::CodeBlock::isKnownToBeLiveDuringGC): Check the
+        m_visitStronglyHasBeenCalled flag instead of
+        shouldImmediatelyAssumeLivenessDuringScan / shouldVisitStrongly because
+        m_visitStronglyHasBeenCalled can be set by anybody even if the CodeBlock
+        would not otherwise visit itself strongly.
+
+        (JSC::CodeBlock::shouldJettisonDueToWeakReference): New helper function
+        for readability.
+
+        (JSC::CodeBlock::shouldJettisonDueToOldAge): New helper function that
+        tells if a CodeBlock is old enough for deletion.
+
+        (JSC::CodeBlock::determineLiveness): There's no need to check
+        shouldImmediatelyAssumeLivenessDuringScan here because we will not call
+        this function if shouldImmediatelyAssumeLivenessDuringScan is true.
+        Also, it's just not clear -- if someone chooses to call this function --
+        that it would be safe to ignore them simply because
+        shouldImmediatelyAssumeLivenessDuringScan was true.
+
+        (JSC::CodeBlock::finalizeLLIntInlineCaches): Moved code out into a helper
+        function to make the main function more readable.
+
+        (JSC::CodeBlock::finalizeBaselineJITInlineCaches): Ditto.
+
+        (JSC::CodeBlock::finalizeUnconditionally): Added code for jettisoning a
+        CodeBlock if it is too old. Moved large sections of code into helper
+        functions to aid readability in this function.
+
+        (JSC::CodeBlock::jettison): Account for the fact that we might jettison
+        a CodeBlock without OSR exit and without requiring a stack shoot-down.
+
+        * bytecode/CodeBlock.h:
+        (JSC::CodeBlock::setInstallTime):
+        (JSC::CodeBlock::timeSinceInstall): Track CodeBlock age to help us
+        decide when to delete.
+
+        * jit/JITCode.h:
+        (JSC::JITCode::timeToLive): Static limits on CodeBlock lifetime. I got
+        these numbers from the place where numbers come from. 
+
+        * profiler/ProfilerJettisonReason.cpp:
+        (WTF::printInternal):
+        * profiler/ProfilerJettisonReason.h: Updated for new jettison type.
+
+        * runtime/Executable.cpp:
+        (JSC::ScriptExecutable::installCode): Record install time so that we
+        can measure how old a CodeBlock is.
+
</ins><span class="cx"> 2015-09-11  Andreas Kling  &lt;akling@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [JSC] Weak should only accept cell pointees.
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorebytecodeCodeBlockcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/bytecode/CodeBlock.cpp (189619 => 189620)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/bytecode/CodeBlock.cpp        2015-09-11 16:05:05 UTC (rev 189619)
+++ trunk/Source/JavaScriptCore/bytecode/CodeBlock.cpp        2015-09-11 16:08:39 UTC (rev 189620)
</span><span class="lines">@@ -2292,15 +2292,15 @@
</span><span class="cx"> 
</span><span class="cx">     visitor.append(&amp;m_unlinkedCode);
</span><span class="cx"> 
</span><del>-    // There are three things that may use unconditional finalizers: lazy bytecode freeing,
-    // inline cache clearing, and jettisoning. The probability of us wanting to do at
-    // least one of those things is probably quite close to 1. So we add one no matter what
-    // and when it runs, it figures out whether it has any work to do.
</del><ins>+    // There are two things that may use unconditional finalizers: inline cache clearing
+    // and jettisoning. The probability of us wanting to do at least one of those things
+    // is probably quite close to 1. So we add one no matter what and when it runs, it
+    // figures out whether it has any work to do.
</ins><span class="cx">     visitor.addUnconditionalFinalizer(this);
</span><span class="cx">     
</span><span class="cx">     m_allTransitionsHaveBeenMarked = false;
</span><span class="cx">     
</span><del>-    if (shouldImmediatelyAssumeLivenessDuringScan()) {
</del><ins>+    if (shouldVisitStrongly()) {
</ins><span class="cx">         visitStrongly(visitor);
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="lines">@@ -2329,7 +2329,7 @@
</span><span class="cx"> #endif // ENABLE(DFG_JIT)
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool CodeBlock::shouldImmediatelyAssumeLivenessDuringScan()
</del><ins>+bool CodeBlock::shouldVisitStrongly()
</ins><span class="cx"> {
</span><span class="cx"> #if ENABLE(DFG_JIT)
</span><span class="cx">     // Interpreter and Baseline JIT CodeBlocks don't need to be jettisoned when
</span><span class="lines">@@ -2349,7 +2349,6 @@
</span><span class="cx"> 
</span><span class="cx"> bool CodeBlock::isKnownToBeLiveDuringGC()
</span><span class="cx"> {
</span><del>-#if ENABLE(DFG_JIT)
</del><span class="cx">     // This should return true for:
</span><span class="cx">     // - Code blocks that behave like normal objects - i.e. if they are referenced then they
</span><span class="cx">     //   are live.
</span><span class="lines">@@ -2357,14 +2356,39 @@
</span><span class="cx">     // - Code blocks that survived the last GC if the current GC is an Eden GC. This is
</span><span class="cx">     //   because livenessHasBeenProved would have survived as true.
</span><span class="cx">     // - Code blocks that don't have any dead weak references.
</span><del>-    
-    return shouldImmediatelyAssumeLivenessDuringScan()
-        || m_jitCode-&gt;dfgCommon()-&gt;livenessHasBeenProved;
-#else
-    return true;
</del><ins>+
+    if (m_visitStronglyHasBeenCalled.load(std::memory_order_relaxed))
+        return true;
+
+#if ENABLE(DFG_JIT)
+    if (JITCode::isOptimizingJIT(jitType())) {
+        if (m_jitCode-&gt;dfgCommon()-&gt;livenessHasBeenProved)
+            return true;
+    }
</ins><span class="cx"> #endif
</span><ins>+
+    return false;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+bool CodeBlock::shouldJettisonDueToWeakReference()
+{
+    return !isKnownToBeLiveDuringGC();
+}
+
+bool CodeBlock::shouldJettisonDueToOldAge()
+{
+    if (m_visitStronglyHasBeenCalled.load(std::memory_order_relaxed))
+        return false;
+
+    if (!JITCode::isOptimizingJIT(jitType()))
+        return false;
+
+    if (timeSinceInstall() &lt; JITCode::timeToLive(jitType()))
+        return false;
+
+    return true;
+}
+
</ins><span class="cx"> #if ENABLE(DFG_JIT)
</span><span class="cx"> static bool shouldMarkTransition(DFG::WeakReferenceTransition&amp; transition)
</span><span class="cx"> {
</span><span class="lines">@@ -2474,9 +2498,6 @@
</span><span class="cx"> {
</span><span class="cx">     UNUSED_PARAM(visitor);
</span><span class="cx">     
</span><del>-    if (shouldImmediatelyAssumeLivenessDuringScan())
-        return;
-    
</del><span class="cx"> #if ENABLE(DFG_JIT)
</span><span class="cx">     // Check if we have any remaining work to do.
</span><span class="cx">     DFG::CommonData* dfgCommon = m_jitCode-&gt;dfgCommon();
</span><span class="lines">@@ -2520,156 +2541,145 @@
</span><span class="cx">     determineLiveness(visitor);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void CodeBlock::finalizeUnconditionally()
</del><ins>+void CodeBlock::finalizeLLIntInlineCaches()
</ins><span class="cx"> {
</span><del>-    Interpreter* interpreter = m_vm-&gt;interpreter;
-    bool ownedByWebAssemblyExecutable = false;
</del><span class="cx"> #if ENABLE(WEBASSEMBLY)
</span><del>-    ownedByWebAssemblyExecutable = m_ownerExecutable-&gt;isWebAssemblyExecutable();
</del><ins>+    if (m_ownerExecutable-&gt;isWebAssemblyExecutable())
+        return;
</ins><span class="cx"> #endif
</span><del>-    if (JITCode::couldBeInterpreted(jitType()) &amp;&amp; !ownedByWebAssemblyExecutable) {
-        const Vector&lt;unsigned&gt;&amp; propertyAccessInstructions = m_unlinkedCode-&gt;propertyAccessInstructions();
-        for (size_t size = propertyAccessInstructions.size(), i = 0; i &lt; size; ++i) {
-            Instruction* curInstruction = &amp;instructions()[propertyAccessInstructions[i]];
-            switch (interpreter-&gt;getOpcodeID(curInstruction[0].u.opcode)) {
-            case op_get_by_id:
-            case op_get_by_id_out_of_line:
-            case op_put_by_id:
-            case op_put_by_id_out_of_line:
-                if (!curInstruction[4].u.structure || Heap::isMarked(curInstruction[4].u.structure.get()))
-                    break;
-                if (Options::verboseOSR())
-                    dataLogF(&quot;Clearing LLInt property access with structure %p.\n&quot;, curInstruction[4].u.structure.get());
-                curInstruction[4].u.structure.clear();
-                curInstruction[5].u.operand = 0;
</del><ins>+
+    Interpreter* interpreter = m_vm-&gt;interpreter;
+    const Vector&lt;unsigned&gt;&amp; propertyAccessInstructions = m_unlinkedCode-&gt;propertyAccessInstructions();
+    for (size_t size = propertyAccessInstructions.size(), i = 0; i &lt; size; ++i) {
+        Instruction* curInstruction = &amp;instructions()[propertyAccessInstructions[i]];
+        switch (interpreter-&gt;getOpcodeID(curInstruction[0].u.opcode)) {
+        case op_get_by_id:
+        case op_get_by_id_out_of_line:
+        case op_put_by_id:
+        case op_put_by_id_out_of_line:
+            if (!curInstruction[4].u.structure || Heap::isMarked(curInstruction[4].u.structure.get()))
</ins><span class="cx">                 break;
</span><del>-            case op_put_by_id_transition_direct:
-            case op_put_by_id_transition_normal:
-            case op_put_by_id_transition_direct_out_of_line:
-            case op_put_by_id_transition_normal_out_of_line:
-                if (Heap::isMarked(curInstruction[4].u.structure.get())
-                    &amp;&amp; Heap::isMarked(curInstruction[6].u.structure.get())
-                    &amp;&amp; Heap::isMarked(curInstruction[7].u.structureChain.get()))
-                    break;
-                if (Options::verboseOSR()) {
-                    dataLogF(&quot;Clearing LLInt put transition with structures %p -&gt; %p, chain %p.\n&quot;,
-                            curInstruction[4].u.structure.get(),
-                            curInstruction[6].u.structure.get(),
-                            curInstruction[7].u.structureChain.get());
-                }
-                curInstruction[4].u.structure.clear();
-                curInstruction[6].u.structure.clear();
-                curInstruction[7].u.structureChain.clear();
-                curInstruction[0].u.opcode = interpreter-&gt;getOpcode(op_put_by_id);
</del><ins>+            if (Options::verboseOSR())
+                dataLogF(&quot;Clearing LLInt property access with structure %p.\n&quot;, curInstruction[4].u.structure.get());
+            curInstruction[4].u.structure.clear();
+            curInstruction[5].u.operand = 0;
+            break;
+        case op_put_by_id_transition_direct:
+        case op_put_by_id_transition_normal:
+        case op_put_by_id_transition_direct_out_of_line:
+        case op_put_by_id_transition_normal_out_of_line:
+            if (Heap::isMarked(curInstruction[4].u.structure.get())
+                &amp;&amp; Heap::isMarked(curInstruction[6].u.structure.get())
+                &amp;&amp; Heap::isMarked(curInstruction[7].u.structureChain.get()))
</ins><span class="cx">                 break;
</span><del>-            case op_get_array_length:
</del><ins>+            if (Options::verboseOSR()) {
+                dataLogF(&quot;Clearing LLInt put transition with structures %p -&gt; %p, chain %p.\n&quot;,
+                    curInstruction[4].u.structure.get(),
+                    curInstruction[6].u.structure.get(),
+                    curInstruction[7].u.structureChain.get());
+            }
+            curInstruction[4].u.structure.clear();
+            curInstruction[6].u.structure.clear();
+            curInstruction[7].u.structureChain.clear();
+            curInstruction[0].u.opcode = interpreter-&gt;getOpcode(op_put_by_id);
+            break;
+        case op_get_array_length:
+            break;
+        case op_to_this:
+            if (!curInstruction[2].u.structure || Heap::isMarked(curInstruction[2].u.structure.get()))
</ins><span class="cx">                 break;
</span><del>-            case op_to_this:
-                if (!curInstruction[2].u.structure || Heap::isMarked(curInstruction[2].u.structure.get()))
-                    break;
-                if (Options::verboseOSR())
-                    dataLogF(&quot;Clearing LLInt to_this with structure %p.\n&quot;, curInstruction[2].u.structure.get());
-                curInstruction[2].u.structure.clear();
-                curInstruction[3].u.toThisStatus = merge(
-                    curInstruction[3].u.toThisStatus, ToThisClearedByGC);
</del><ins>+            if (Options::verboseOSR())
+                dataLogF(&quot;Clearing LLInt to_this with structure %p.\n&quot;, curInstruction[2].u.structure.get());
+            curInstruction[2].u.structure.clear();
+            curInstruction[3].u.toThisStatus = merge(
+                curInstruction[3].u.toThisStatus, ToThisClearedByGC);
+            break;
+        case op_create_this: {
+            auto&amp; cacheWriteBarrier = curInstruction[4].u.jsCell;
+            if (!cacheWriteBarrier || cacheWriteBarrier.unvalidatedGet() == JSCell::seenMultipleCalleeObjects())
</ins><span class="cx">                 break;
</span><del>-            case op_create_this: {
-                auto&amp; cacheWriteBarrier = curInstruction[4].u.jsCell;
-                if (!cacheWriteBarrier || cacheWriteBarrier.unvalidatedGet() == JSCell::seenMultipleCalleeObjects())
-                    break;
-                JSCell* cachedFunction = cacheWriteBarrier.get();
-                if (Heap::isMarked(cachedFunction))
-                    break;
-                if (Options::verboseOSR())
-                    dataLogF(&quot;Clearing LLInt create_this with cached callee %p.\n&quot;, cachedFunction);
-                cacheWriteBarrier.clear();
</del><ins>+            JSCell* cachedFunction = cacheWriteBarrier.get();
+            if (Heap::isMarked(cachedFunction))
</ins><span class="cx">                 break;
</span><del>-            }
-            case op_resolve_scope: {
-                // Right now this isn't strictly necessary. Any symbol tables that this will refer to
-                // are for outer functions, and we refer to those functions strongly, and they refer
-                // to the symbol table strongly. But it's nice to be on the safe side.
-                WriteBarrierBase&lt;SymbolTable&gt;&amp; symbolTable = curInstruction[6].u.symbolTable;
-                if (!symbolTable || Heap::isMarked(symbolTable.get()))
-                    break;
-                if (Options::verboseOSR())
-                    dataLogF(&quot;Clearing dead symbolTable %p.\n&quot;, symbolTable.get());
-                symbolTable.clear();
</del><ins>+            if (Options::verboseOSR())
+                dataLogF(&quot;Clearing LLInt create_this with cached callee %p.\n&quot;, cachedFunction);
+            cacheWriteBarrier.clear();
+            break;
+        }
+        case op_resolve_scope: {
+            // Right now this isn't strictly necessary. Any symbol tables that this will refer to
+            // are for outer functions, and we refer to those functions strongly, and they refer
+            // to the symbol table strongly. But it's nice to be on the safe side.
+            WriteBarrierBase&lt;SymbolTable&gt;&amp; symbolTable = curInstruction[6].u.symbolTable;
+            if (!symbolTable || Heap::isMarked(symbolTable.get()))
</ins><span class="cx">                 break;
</span><del>-            }
-            case op_get_from_scope:
-            case op_put_to_scope: {
-                GetPutInfo getPutInfo = GetPutInfo(curInstruction[4].u.operand);
-                if (getPutInfo.resolveType() == GlobalVar || getPutInfo.resolveType() == GlobalVarWithVarInjectionChecks 
-                    || getPutInfo.resolveType() == LocalClosureVar || getPutInfo.resolveType() == GlobalLexicalVar || getPutInfo.resolveType() == GlobalLexicalVarWithVarInjectionChecks)
-                    continue;
-                WriteBarrierBase&lt;Structure&gt;&amp; structure = curInstruction[5].u.structure;
-                if (!structure || Heap::isMarked(structure.get()))
-                    break;
-                if (Options::verboseOSR())
-                    dataLogF(&quot;Clearing scope access with structure %p.\n&quot;, structure.get());
-                structure.clear();
</del><ins>+            if (Options::verboseOSR())
+                dataLogF(&quot;Clearing dead symbolTable %p.\n&quot;, symbolTable.get());
+            symbolTable.clear();
+            break;
+        }
+        case op_get_from_scope:
+        case op_put_to_scope: {
+            GetPutInfo getPutInfo = GetPutInfo(curInstruction[4].u.operand);
+            if (getPutInfo.resolveType() == GlobalVar || getPutInfo.resolveType() == GlobalVarWithVarInjectionChecks 
+                || getPutInfo.resolveType() == LocalClosureVar || getPutInfo.resolveType() == GlobalLexicalVar || getPutInfo.resolveType() == GlobalLexicalVarWithVarInjectionChecks)
+                continue;
+            WriteBarrierBase&lt;Structure&gt;&amp; structure = curInstruction[5].u.structure;
+            if (!structure || Heap::isMarked(structure.get()))
</ins><span class="cx">                 break;
</span><del>-            }
-            default:
-                OpcodeID opcodeID = interpreter-&gt;getOpcodeID(curInstruction[0].u.opcode);
-                ASSERT_WITH_MESSAGE_UNUSED(opcodeID, false, &quot;Unhandled opcode in CodeBlock::finalizeUnconditionally, %s(%d) at bc %u&quot;, opcodeNames[opcodeID], opcodeID, propertyAccessInstructions[i]);
-            }
</del><ins>+            if (Options::verboseOSR())
+                dataLogF(&quot;Clearing scope access with structure %p.\n&quot;, structure.get());
+            structure.clear();
+            break;
</ins><span class="cx">         }
</span><ins>+        default:
+            OpcodeID opcodeID = interpreter-&gt;getOpcodeID(curInstruction[0].u.opcode);
+            ASSERT_WITH_MESSAGE_UNUSED(opcodeID, false, &quot;Unhandled opcode in CodeBlock::finalizeUnconditionally, %s(%d) at bc %u&quot;, opcodeNames[opcodeID], opcodeID, propertyAccessInstructions[i]);
+        }
+    }
</ins><span class="cx"> 
</span><del>-        for (unsigned i = 0; i &lt; m_llintCallLinkInfos.size(); ++i) {
-            if (m_llintCallLinkInfos[i].isLinked() &amp;&amp; !Heap::isMarked(m_llintCallLinkInfos[i].callee.get())) {
-                if (Options::verboseOSR())
-                    dataLog(&quot;Clearing LLInt call from &quot;, *this, &quot;\n&quot;);
-                m_llintCallLinkInfos[i].unlink();
-            }
-            if (!!m_llintCallLinkInfos[i].lastSeenCallee &amp;&amp; !Heap::isMarked(m_llintCallLinkInfos[i].lastSeenCallee.get()))
-                m_llintCallLinkInfos[i].lastSeenCallee.clear();
</del><ins>+    for (unsigned i = 0; i &lt; m_llintCallLinkInfos.size(); ++i) {
+        if (m_llintCallLinkInfos[i].isLinked() &amp;&amp; !Heap::isMarked(m_llintCallLinkInfos[i].callee.get())) {
+            if (Options::verboseOSR())
+                dataLog(&quot;Clearing LLInt call from &quot;, *this, &quot;\n&quot;);
+            m_llintCallLinkInfos[i].unlink();
</ins><span class="cx">         }
</span><ins>+        if (!!m_llintCallLinkInfos[i].lastSeenCallee &amp;&amp; !Heap::isMarked(m_llintCallLinkInfos[i].lastSeenCallee.get()))
+            m_llintCallLinkInfos[i].lastSeenCallee.clear();
</ins><span class="cx">     }
</span><ins>+}
</ins><span class="cx"> 
</span><ins>+void CodeBlock::finalizeBaselineJITInlineCaches()
+{
+    for (auto iter = callLinkInfosBegin(); !!iter; ++iter)
+        (*iter)-&gt;visitWeak(*vm());
+
+    for (Bag&lt;StructureStubInfo&gt;::iterator iter = m_stubInfos.begin(); !!iter; ++iter) {
+        StructureStubInfo&amp; stubInfo = **iter;
+        stubInfo.visitWeakReferences(this);
+    }
+}
+
+void CodeBlock::finalizeUnconditionally()
+{
</ins><span class="cx"> #if ENABLE(DFG_JIT)
</span><del>-    // Check if we're not live. If we are, then jettison.
-    if (!isKnownToBeLiveDuringGC()) {
-        if (Options::verboseOSR())
-            dataLog(*this, &quot; has dead weak references, jettisoning during GC.\n&quot;);
-
-        if (DFG::shouldShowDisassembly()) {
-            dataLog(*this, &quot; will be jettisoned because of the following dead references:\n&quot;);
-            DFG::CommonData* dfgCommon = m_jitCode-&gt;dfgCommon();
-            for (unsigned i = 0; i &lt; dfgCommon-&gt;transitions.size(); ++i) {
-                DFG::WeakReferenceTransition&amp; transition = dfgCommon-&gt;transitions[i];
-                JSCell* origin = transition.m_codeOrigin.get();
-                JSCell* from = transition.m_from.get();
-                JSCell* to = transition.m_to.get();
-                if ((!origin || Heap::isMarked(origin)) &amp;&amp; Heap::isMarked(from))
-                    continue;
-                dataLog(&quot;    Transition under &quot;, RawPointer(origin), &quot;, &quot;, RawPointer(from), &quot; -&gt; &quot;, RawPointer(to), &quot;.\n&quot;);
-            }
-            for (unsigned i = 0; i &lt; dfgCommon-&gt;weakReferences.size(); ++i) {
-                JSCell* weak = dfgCommon-&gt;weakReferences[i].get();
-                if (Heap::isMarked(weak))
-                    continue;
-                dataLog(&quot;    Weak reference &quot;, RawPointer(weak), &quot;.\n&quot;);
-            }
-        }
-        
</del><ins>+    if (shouldJettisonDueToWeakReference()) {
</ins><span class="cx">         jettison(Profiler::JettisonDueToWeakReference);
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> #endif // ENABLE(DFG_JIT)
</span><span class="cx"> 
</span><ins>+    if (shouldJettisonDueToOldAge()) {
+        jettison(Profiler::JettisonDueToOldAge);
+        return;
+    }
+
+    if (JITCode::couldBeInterpreted(jitType()))
+        finalizeLLIntInlineCaches();
+
</ins><span class="cx"> #if ENABLE(JIT)
</span><del>-    // Handle inline caches.
-    if (!!jitCode()) {
-        for (auto iter = callLinkInfosBegin(); !!iter; ++iter)
-            (*iter)-&gt;visitWeak(*vm());
-
-        for (Bag&lt;StructureStubInfo&gt;::iterator iter = m_stubInfos.begin(); !!iter; ++iter) {
-            StructureStubInfo&amp; stubInfo = **iter;
-            stubInfo.visitWeakReferences(this);
-        }
-    }
</del><ins>+    if (!!jitCode())
+        finalizeBaselineJITInlineCaches();
</ins><span class="cx"> #endif
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -3068,6 +3078,28 @@
</span><span class="cx">         dataLog(&quot;.\n&quot;);
</span><span class="cx">     }
</span><span class="cx">     
</span><ins>+    if (reason == Profiler::JettisonDueToWeakReference) {
+        if (DFG::shouldShowDisassembly()) {
+            dataLog(*this, &quot; will be jettisoned because of the following dead references:\n&quot;);
+            DFG::CommonData* dfgCommon = m_jitCode-&gt;dfgCommon();
+            for (unsigned i = 0; i &lt; dfgCommon-&gt;transitions.size(); ++i) {
+                DFG::WeakReferenceTransition&amp; transition = dfgCommon-&gt;transitions[i];
+                JSCell* origin = transition.m_codeOrigin.get();
+                JSCell* from = transition.m_from.get();
+                JSCell* to = transition.m_to.get();
+                if ((!origin || Heap::isMarked(origin)) &amp;&amp; Heap::isMarked(from))
+                    continue;
+                dataLog(&quot;    Transition under &quot;, RawPointer(origin), &quot;, &quot;, RawPointer(from), &quot; -&gt; &quot;, RawPointer(to), &quot;.\n&quot;);
+            }
+            for (unsigned i = 0; i &lt; dfgCommon-&gt;weakReferences.size(); ++i) {
+                JSCell* weak = dfgCommon-&gt;weakReferences[i].get();
+                if (Heap::isMarked(weak))
+                    continue;
+                dataLog(&quot;    Weak reference &quot;, RawPointer(weak), &quot;.\n&quot;);
+            }
+        }
+    }
+
</ins><span class="cx">     DeferGCForAWhile deferGC(*m_heap);
</span><span class="cx">     RELEASE_ASSERT(JITCode::isOptimizingJIT(jitType()));
</span><span class="cx">     
</span><span class="lines">@@ -3078,14 +3110,14 @@
</span><span class="cx">     // 1) Make sure that if this CodeBlock is on the stack right now, then if we return to it
</span><span class="cx">     //    we should OSR exit at the top of the next bytecode instruction after the return.
</span><span class="cx">     // 2) Make sure that if we call the owner executable, then we shouldn't call this CodeBlock.
</span><del>-    
-    // This accomplishes the OSR-exit-on-return part, and does its own book-keeping about
-    // whether the invalidation has already happened.
-    if (!jitCode()-&gt;dfgCommon()-&gt;invalidate()) {
-        // Nothing to do since we've already been invalidated. That means that we cannot be
-        // the optimized replacement.
-        RELEASE_ASSERT(this != replacement());
-        return;
</del><ins>+
+    if (reason != Profiler::JettisonDueToOldAge) {
+        // This accomplishes (1), and does its own book-keeping about whether it has already happened.
+        if (!jitCode()-&gt;dfgCommon()-&gt;invalidate()) {
+            // We've already been invalidated.
+            RELEASE_ASSERT(this != replacement());
+            return;
+        }
</ins><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     if (DFG::shouldShowDisassembly())
</span><span class="lines">@@ -3100,14 +3132,17 @@
</span><span class="cx">             dataLog(&quot;    Did count reoptimization for &quot;, *this, &quot;\n&quot;);
</span><span class="cx">     }
</span><span class="cx">     
</span><del>-    // Now take care of the entrypoint.
</del><ins>+    // This accomplishes (2).
</ins><span class="cx">     if (this != replacement()) {
</span><span class="cx">         // This means that we were never the entrypoint. This can happen for OSR entry code
</span><span class="cx">         // blocks.
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx">     alternative()-&gt;optimizeAfterWarmUp();
</span><del>-    tallyFrequentExitSites();
</del><ins>+
+    if (reason != Profiler::JettisonDueToOldAge)
+        tallyFrequentExitSites();
+
</ins><span class="cx">     alternative()-&gt;install();
</span><span class="cx">     if (DFG::shouldShowDisassembly())
</span><span class="cx">         dataLog(&quot;    Did install baseline version of &quot;, *this, &quot;\n&quot;);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorebytecodeCodeBlockh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/bytecode/CodeBlock.h (189619 => 189620)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/bytecode/CodeBlock.h        2015-09-11 16:05:05 UTC (rev 189619)
+++ trunk/Source/JavaScriptCore/bytecode/CodeBlock.h        2015-09-11 16:08:39 UTC (rev 189620)
</span><span class="lines">@@ -834,6 +834,8 @@
</span><span class="cx">     void updateAllArrayPredictions();
</span><span class="cx">     void updateAllPredictions();
</span><span class="cx"> 
</span><ins>+    void setInstallTime(std::chrono::steady_clock::time_point installTime) { m_installTime = installTime; }
+
</ins><span class="cx">     unsigned frameRegisterCount();
</span><span class="cx">     int stackPointerOffset();
</span><span class="cx"> 
</span><span class="lines">@@ -917,6 +919,8 @@
</span><span class="cx"> protected:
</span><span class="cx">     virtual void visitWeakReferences(SlotVisitor&amp;) override;
</span><span class="cx">     virtual void finalizeUnconditionally() override;
</span><ins>+    void finalizeLLIntInlineCaches();
+    void finalizeBaselineJITInlineCaches();
</ins><span class="cx"> 
</span><span class="cx"> #if ENABLE(DFG_JIT)
</span><span class="cx">     void tallyFrequentExitSites();
</span><span class="lines">@@ -974,7 +978,9 @@
</span><span class="cx">     void dumpArrayProfiling(PrintStream&amp;, const Instruction*&amp;, bool&amp; hasPrintedProfiling);
</span><span class="cx">     void dumpRareCaseProfile(PrintStream&amp;, const char* name, RareCaseProfile*, bool&amp; hasPrintedProfiling);
</span><span class="cx">         
</span><del>-    bool shouldImmediatelyAssumeLivenessDuringScan();
</del><ins>+    bool shouldVisitStrongly();
+    bool shouldJettisonDueToWeakReference();
+    bool shouldJettisonDueToOldAge();
</ins><span class="cx">     
</span><span class="cx">     void propagateTransitions(SlotVisitor&amp;);
</span><span class="cx">     void determineLiveness(SlotVisitor&amp;);
</span><span class="lines">@@ -983,6 +989,12 @@
</span><span class="cx">     void stronglyVisitWeakReferences(SlotVisitor&amp;);
</span><span class="cx">     void visitOSRExitTargets(SlotVisitor&amp;);
</span><span class="cx"> 
</span><ins>+    std::chrono::milliseconds timeSinceInstall()
+    {
+        return std::chrono::duration_cast&lt;std::chrono::milliseconds&gt;(
+            std::chrono::steady_clock::now() - m_installTime);
+    }
+
</ins><span class="cx">     void createRareDataIfNecessary()
</span><span class="cx">     {
</span><span class="cx">         if (!m_rareData)
</span><span class="lines">@@ -1064,7 +1076,9 @@
</span><span class="cx">     uint32_t m_osrExitCounter;
</span><span class="cx">     uint16_t m_optimizationDelayCounter;
</span><span class="cx">     uint16_t m_reoptimizationRetryCounter;
</span><del>-    
</del><ins>+
+    std::chrono::steady_clock::time_point m_installTime;
+
</ins><span class="cx">     mutable CodeBlockHash m_hash;
</span><span class="cx"> 
</span><span class="cx">     std::unique_ptr&lt;BytecodeLivenessAnalysis&gt; m_livenessAnalysis;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorejitJITCodeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/jit/JITCode.h (189619 => 189620)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/jit/JITCode.h        2015-09-11 16:05:05 UTC (rev 189619)
+++ trunk/Source/JavaScriptCore/jit/JITCode.h        2015-09-11 16:08:39 UTC (rev 189620)
</span><span class="lines">@@ -121,7 +121,19 @@
</span><span class="cx">             return false;
</span><span class="cx">         }
</span><span class="cx">     }
</span><del>-    
</del><ins>+
+    static std::chrono::milliseconds timeToLive(JITType jitType)
+    {
+        switch (jitType) {
+        case DFGJIT:
+            return std::chrono::milliseconds(10000); // 10s
+        case FTLJIT:
+            return std::chrono::milliseconds(100000); // 100s
+        default:
+            return std::chrono::milliseconds::max();
+        }
+    }
+
</ins><span class="cx">     static bool isLowerTier(JITType expectedLower, JITType expectedHigher)
</span><span class="cx">     {
</span><span class="cx">         RELEASE_ASSERT(isExecutableScript(expectedLower));
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreprofilerProfilerJettisonReasoncpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/profiler/ProfilerJettisonReason.cpp (189619 => 189620)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/profiler/ProfilerJettisonReason.cpp        2015-09-11 16:05:05 UTC (rev 189619)
+++ trunk/Source/JavaScriptCore/profiler/ProfilerJettisonReason.cpp        2015-09-11 16:08:39 UTC (rev 189620)
</span><span class="lines">@@ -65,6 +65,9 @@
</span><span class="cx">     case JettisonDueToUnprofiledWatchpoint:
</span><span class="cx">         out.print(&quot;UnprofiledWatchpoint&quot;);
</span><span class="cx">         return;
</span><ins>+    case JettisonDueToOldAge:
+        out.print(&quot;JettisonDueToOldAge&quot;);
+        return;
</ins><span class="cx">     }
</span><span class="cx">     RELEASE_ASSERT_NOT_REACHED();
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreprofilerProfilerJettisonReasonh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/profiler/ProfilerJettisonReason.h (189619 => 189620)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/profiler/ProfilerJettisonReason.h        2015-09-11 16:05:05 UTC (rev 189619)
+++ trunk/Source/JavaScriptCore/profiler/ProfilerJettisonReason.h        2015-09-11 16:08:39 UTC (rev 189620)
</span><span class="lines">@@ -38,7 +38,8 @@
</span><span class="cx">     JettisonDueToBaselineLoopReoptimizationTriggerOnOSREntryFail,
</span><span class="cx">     JettisonDueToOSRExit,
</span><span class="cx">     JettisonDueToProfiledWatchpoint,
</span><del>-    JettisonDueToUnprofiledWatchpoint
</del><ins>+    JettisonDueToUnprofiledWatchpoint,
+    JettisonDueToOldAge
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } } // namespace JSC::Profiler
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeExecutablecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/Executable.cpp (189619 => 189620)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/Executable.cpp        2015-09-11 16:05:05 UTC (rev 189619)
+++ trunk/Source/JavaScriptCore/runtime/Executable.cpp        2015-09-11 16:08:39 UTC (rev 189620)
</span><span class="lines">@@ -208,6 +208,8 @@
</span><span class="cx">     if (debugger)
</span><span class="cx">         debugger-&gt;registerCodeBlock(genericCodeBlock);
</span><span class="cx"> 
</span><ins>+    genericCodeBlock-&gt;setInstallTime(std::chrono::steady_clock::now());
+
</ins><span class="cx">     Heap::heap(this)-&gt;writeBarrier(this);
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>