<!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>[190289] trunk</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/190289">190289</a></dd>
<dt>Author</dt> <dd>msaboff@apple.com</dd>
<dt>Date</dt> <dd>2015-09-28 15:37:36 -0700 (Mon, 28 Sep 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>[ES6] Implement tail calls in the FTL
https://bugs.webkit.org/show_bug.cgi?id=148664

Source/JavaScriptCore:

Patch by basile_clement@apple.com &lt;basile_clement@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc&gt; on 2015-09-28
Reviewed by Filip Pizlo.

This patch implements the tail call opcodes in the FTL, making tail
calls available through all tiers. The changes are relatively
straightforward, although the frame shuffler had to be extended to
handle the possibility of running out of stack when spilling or
building a slow path frame. The other tiers always ensure that we have
enough stack space to build the new frame at the bottom of the old one,
but that is not true for the FTL.

Moreover, for efficiency, this adds to the shuffler the ability to
record the state of the TagTypeNumber, and to re-use the same register
when doing several consecutive integer boxings with no spilling in
between.

* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/ValueRecovery.h:
(JSC::ValueRecovery::inRegister):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleInlining):
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGNode.h:
(JSC::DFG::Node::isFunctionTerminal):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::emitCall):
* dfg/DFGTierUpCheckInjectionPhase.cpp:
(JSC::DFG::TierUpCheckInjectionPhase::run):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLCompile.cpp:
(JSC::FTL::mmAllocateDataSection):
* ftl/FTLInlineCacheSize.cpp:
(JSC::FTL::sizeOfTailCallVarargs):
(JSC::FTL::sizeOfTailCallForwardVarargs):
(JSC::FTL::sizeOfICFor):
* ftl/FTLInlineCacheSize.h:
* ftl/FTLJSCall.cpp:
(JSC::FTL::JSCall::JSCall):
* ftl/FTLJSCallBase.cpp:
(JSC::FTL::JSCallBase::emit):
(JSC::FTL::JSCallBase::link):
* ftl/FTLJSCallBase.h:
* ftl/FTLJSCallVarargs.cpp:
(JSC::FTL::JSCallVarargs::JSCallVarargs):
(JSC::FTL::JSCallVarargs::emit):
* ftl/FTLJSTailCall.cpp: Added.
(JSC::FTL::getRegisterWithAddend):
(JSC::FTL::recoveryFor):
(JSC::FTL::sizeFor):
(JSC::FTL::JSTailCall::JSTailCall):
(JSC::FTL::m_instructionOffset):
(JSC::FTL::JSTailCall::emit):
* ftl/FTLJSTailCall.h: Copied from Source/JavaScriptCore/ftl/FTLJSCallBase.h.
(JSC::FTL::JSTailCall::stackmapID):
(JSC::FTL::JSTailCall::estimatedSize):
(JSC::FTL::JSTailCall::numArguments):
(JSC::FTL::JSTailCall::operator&lt;):
* ftl/FTLLocation.h:
(JSC::FTL::Location::operator bool):
(JSC::FTL::Location::operator!):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::DFG::LowerDFGToLLVM::lower):
(JSC::FTL::DFG::LowerDFGToLLVM::compileNode):
(JSC::FTL::DFG::LowerDFGToLLVM::compileTailCall):
(JSC::FTL::DFG::LowerDFGToLLVM::compileCallOrConstructVarargs):
(JSC::FTL::DFG::LowerDFGToLLVM::callPreflight):
(JSC::FTL::DFG::LowerDFGToLLVM::exitValueForTailCall):
* ftl/FTLState.h:
* jit/AssemblyHelpers.cpp:
(JSC::AssemblyHelpers::emitExceptionCheck):
* jit/CallFrameShuffleData.h:
* jit/CallFrameShuffler.cpp:
(JSC::CallFrameShuffler::CallFrameShuffler):
(JSC::CallFrameShuffler::dump):
(JSC::CallFrameShuffler::spill):
(JSC::CallFrameShuffler::extendFrameIfNeeded):
(JSC::CallFrameShuffler::prepareForSlowPath):
(JSC::CallFrameShuffler::prepareAny):
* jit/CallFrameShuffler.h:
(JSC::CallFrameShuffler::restoreGPR):
(JSC::CallFrameShuffler::getFreeRegister):
(JSC::CallFrameShuffler::getFreeTempGPR):
(JSC::CallFrameShuffler::ensureTempGPR):
(JSC::CallFrameShuffler::addNew):
* jit/CallFrameShuffler64.cpp:
(JSC::CallFrameShuffler::emitBox):
(JSC::CallFrameShuffler::tryAcquireTagTypeNumber):
* jit/JITCall.cpp:
(JSC::JIT::compileOpCall):
* jit/Reg.h:
(JSC::Reg::Reg):
(JSC::Reg::isHashTableDeletedValue):
(JSC::Reg::deleted):
(JSC::RegHash::hash):
(JSC::RegHash::equal):
* test/es6.yaml:

LayoutTests:

Reviewed by Filip Pizlo.

Added one new tail call test and enabled other tail call tests.

* js/caller-property-expected.txt:
* js/script-tests/caller-property.js:
(string_appeared_here.nonStrictCallee):
(strictCallee):
(nonStrictCaller):
(strictCaller):
(strictTailCaller):
(getFooGetter):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsjscallerpropertyexpectedtxt">trunk/LayoutTests/js/caller-property-expected.txt</a></li>
<li><a href="#trunkLayoutTestsjsscripttestscallerpropertyjs">trunk/LayoutTests/js/script-tests/caller-property.js</a></li>
<li><a href="#trunkSourceJavaScriptCoreCMakeListstxt">trunk/Source/JavaScriptCore/CMakeLists.txt</a></li>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreJavaScriptCorevcxprojJavaScriptCorevcxproj">trunk/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj</a></li>
<li><a href="#trunkSourceJavaScriptCoreJavaScriptCorevcxprojJavaScriptCorevcxprojfilters">trunk/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters</a></li>
<li><a href="#trunkSourceJavaScriptCoreJavaScriptCorexcodeprojprojectpbxproj">trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGByteCodeParsercpp">trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGClobberizeh">trunk/Source/JavaScriptCore/dfg/DFGClobberize.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGNodeh">trunk/Source/JavaScriptCore/dfg/DFGNode.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGSpeculativeJIT64cpp">trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGTierUpCheckInjectionPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGTierUpCheckInjectionPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLCapabilitiescpp">trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLCompilecpp">trunk/Source/JavaScriptCore/ftl/FTLCompile.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLInlineCacheSizecpp">trunk/Source/JavaScriptCore/ftl/FTLInlineCacheSize.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLInlineCacheSizeh">trunk/Source/JavaScriptCore/ftl/FTLInlineCacheSize.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLJSCallcpp">trunk/Source/JavaScriptCore/ftl/FTLJSCall.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLJSCallBasecpp">trunk/Source/JavaScriptCore/ftl/FTLJSCallBase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLJSCallBaseh">trunk/Source/JavaScriptCore/ftl/FTLJSCallBase.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLJSCallVarargscpp">trunk/Source/JavaScriptCore/ftl/FTLJSCallVarargs.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLLocationh">trunk/Source/JavaScriptCore/ftl/FTLLocation.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLLowerDFGToLLVMcpp">trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLStateh">trunk/Source/JavaScriptCore/ftl/FTLState.h</a></li>
<li><a href="#trunkSourceJavaScriptCorejitAssemblyHelperscpp">trunk/Source/JavaScriptCore/jit/AssemblyHelpers.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorejitCallFrameShuffleDatah">trunk/Source/JavaScriptCore/jit/CallFrameShuffleData.h</a></li>
<li><a href="#trunkSourceJavaScriptCorejitCallFrameShufflercpp">trunk/Source/JavaScriptCore/jit/CallFrameShuffler.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorejitCallFrameShufflerh">trunk/Source/JavaScriptCore/jit/CallFrameShuffler.h</a></li>
<li><a href="#trunkSourceJavaScriptCorejitCallFrameShuffler64cpp">trunk/Source/JavaScriptCore/jit/CallFrameShuffler64.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorejitJITCallcpp">trunk/Source/JavaScriptCore/jit/JITCall.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorejitRegh">trunk/Source/JavaScriptCore/jit/Reg.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeOptionsh">trunk/Source/JavaScriptCore/runtime/Options.h</a></li>
<li><a href="#trunkSourceJavaScriptCoretestses6yaml">trunk/Source/JavaScriptCore/tests/es6.yaml</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreftlFTLJSTailCallcpp">trunk/Source/JavaScriptCore/ftl/FTLJSTailCall.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLJSTailCallh">trunk/Source/JavaScriptCore/ftl/FTLJSTailCall.h</a></li>
<li><a href="#trunkSourceJavaScriptCoretestsstressdfgtailcallsjs">trunk/Source/JavaScriptCore/tests/stress/dfg-tail-calls.js</a></li>
<li><a href="#trunkSourceJavaScriptCoretestsstressmutualtailcallnostackoverflowjs">trunk/Source/JavaScriptCore/tests/stress/mutual-tail-call-no-stack-overflow.js</a></li>
<li><a href="#trunkSourceJavaScriptCoretestsstresstailcallnostackoverflowjs">trunk/Source/JavaScriptCore/tests/stress/tail-call-no-stack-overflow.js</a></li>
<li><a href="#trunkSourceJavaScriptCoretestsstresstailcallrecognizejs">trunk/Source/JavaScriptCore/tests/stress/tail-call-recognize.js</a></li>
<li><a href="#trunkSourceJavaScriptCoretestsstresstailcallvarargsnostackoverflowjs">trunk/Source/JavaScriptCore/tests/stress/tail-call-varargs-no-stack-overflow.js</a></li>
<li><a href="#trunkSourceJavaScriptCoretestsstresstailcallsdontoverwritelivestackjs">trunk/Source/JavaScriptCore/tests/stress/tail-calls-dont-overwrite-live-stack.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/LayoutTests/ChangeLog        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -1,3 +1,21 @@
</span><ins>+2015-09-28  Michael Saboff  &lt;msaboff@apple.com&gt;
+
+        [ES6] Implement tail calls in the FTL
+        https://bugs.webkit.org/show_bug.cgi?id=148664
+
+        Reviewed by Filip Pizlo.
+
+        Added one new tail call test and enabled other tail call tests.
+
+        * js/caller-property-expected.txt:
+        * js/script-tests/caller-property.js:
+        (string_appeared_here.nonStrictCallee):
+        (strictCallee):
+        (nonStrictCaller):
+        (strictCaller):
+        (strictTailCaller):
+        (getFooGetter):
+
</ins><span class="cx"> 2015-09-28  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
</span><span class="cx"> 
</span><span class="cx">         relatedNode should be retargeted respecting slots
</span></span></pre></div>
<a id="trunkLayoutTestsjscallerpropertyexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/js/caller-property-expected.txt (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/js/caller-property-expected.txt        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/LayoutTests/js/caller-property-expected.txt        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -10,10 +10,14 @@
</span><span class="cx"> PASS nonStrictCaller(strictCallee) threw exception TypeError: Type error.
</span><span class="cx"> PASS strictCaller(nonStrictCallee) threw exception TypeError: Function.caller used to retrieve strict caller.
</span><span class="cx"> PASS strictCaller(strictCallee) threw exception TypeError: Type error.
</span><ins>+PASS strictTailCaller(nonStrictCallee) is null
+PASS strictTailCaller(strictCallee) threw exception TypeError: Type error.
</ins><span class="cx"> PASS nonStrictCaller(boundNonStrictCallee) is nonStrictCaller
</span><span class="cx"> PASS nonStrictCaller(boundStrictCallee) threw exception TypeError: Type error.
</span><span class="cx"> PASS strictCaller(boundNonStrictCallee) threw exception TypeError: Function.caller used to retrieve strict caller.
</span><span class="cx"> PASS strictCaller(boundStrictCallee) threw exception TypeError: Type error.
</span><ins>+PASS strictTailCaller(boundNonStrictCallee) is null
+PASS strictTailCaller(boundStrictCallee) threw exception TypeError: Type error.
</ins><span class="cx"> PASS nonStrictGetter(nonStrictAccessor) is nonStrictGetter
</span><span class="cx"> PASS nonStrictSetter(nonStrictAccessor) is true
</span><span class="cx"> PASS nonStrictGetter(strictAccessor) threw exception TypeError: Type error.
</span></span></pre></div>
<a id="trunkLayoutTestsjsscripttestscallerpropertyjs"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/js/script-tests/caller-property.js (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/js/script-tests/caller-property.js        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/LayoutTests/js/script-tests/caller-property.js        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -23,11 +23,15 @@
</span><span class="cx"> function nonStrictCallee() { return nonStrictCallee.caller; }
</span><span class="cx"> function strictCallee() { &quot;use strict&quot;; return strictCallee.caller; }
</span><span class="cx"> function nonStrictCaller(x) { return x(); }
</span><del>-function strictCaller(x) { &quot;use strict&quot;; return x(); }
</del><ins>+// Tail calls leak and show our caller's caller, which is null here
+function strictCaller(x) { &quot;use strict&quot;; var result = x(); return result; }
+function strictTailCaller(x) { &quot;use strict&quot;; return x(); }
</ins><span class="cx"> shouldBe(&quot;nonStrictCaller(nonStrictCallee)&quot;, &quot;nonStrictCaller&quot;);
</span><span class="cx"> shouldThrow(&quot;nonStrictCaller(strictCallee)&quot;, '&quot;TypeError: Type error&quot;');
</span><span class="cx"> shouldThrow(&quot;strictCaller(nonStrictCallee)&quot;, '&quot;TypeError: Function.caller used to retrieve strict caller&quot;');
</span><span class="cx"> shouldThrow(&quot;strictCaller(strictCallee)&quot;, '&quot;TypeError: Type error&quot;');
</span><ins>+shouldBe(&quot;strictTailCaller(nonStrictCallee)&quot;, &quot;null&quot;);
+shouldThrow(&quot;strictTailCaller(strictCallee)&quot;, '&quot;TypeError: Type error&quot;');
</ins><span class="cx"> 
</span><span class="cx"> // .caller within a bound function reaches the caller, ignoring the binding.
</span><span class="cx"> var boundNonStrictCallee = nonStrictCallee.bind();
</span><span class="lines">@@ -36,6 +40,8 @@
</span><span class="cx"> shouldThrow(&quot;nonStrictCaller(boundStrictCallee)&quot;, '&quot;TypeError: Type error&quot;');
</span><span class="cx"> shouldThrow(&quot;strictCaller(boundNonStrictCallee)&quot;, '&quot;TypeError: Function.caller used to retrieve strict caller&quot;');
</span><span class="cx"> shouldThrow(&quot;strictCaller(boundStrictCallee)&quot;, '&quot;TypeError: Type error&quot;');
</span><ins>+shouldBe(&quot;strictTailCaller(boundNonStrictCallee)&quot;, &quot;null&quot;);
+shouldThrow(&quot;strictTailCaller(boundStrictCallee)&quot;, '&quot;TypeError: Type error&quot;');
</ins><span class="cx"> 
</span><span class="cx"> // Check that .caller works (or throws) as expected, over an accessor call.
</span><span class="cx"> function getFooGetter(x) { return Object.getOwnPropertyDescriptor(x, 'foo').get; }
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreCMakeListstxt"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/CMakeLists.txt (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/CMakeLists.txt        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/CMakeLists.txt        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -903,6 +903,7 @@
</span><span class="cx">         ftl/FTLJSCall.cpp
</span><span class="cx">         ftl/FTLJSCallBase.cpp
</span><span class="cx">         ftl/FTLJSCallVarargs.cpp
</span><ins>+        ftl/FTLJSTailCall.cpp
</ins><span class="cx">         ftl/FTLLink.cpp
</span><span class="cx">         ftl/FTLLocation.cpp
</span><span class="cx">         ftl/FTLLowerDFGToLLVM.cpp
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/ChangeLog        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -1,3 +1,107 @@
</span><ins>+2015-09-28  basile_clement@apple.com  &lt;basile_clement@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc&gt;
+
+        [ES6] Implement tail calls in the FTL
+        https://bugs.webkit.org/show_bug.cgi?id=148664
+
+        Reviewed by Filip Pizlo.
+
+        This patch implements the tail call opcodes in the FTL, making tail
+        calls available through all tiers. The changes are relatively
+        straightforward, although the frame shuffler had to be extended to
+        handle the possibility of running out of stack when spilling or
+        building a slow path frame. The other tiers always ensure that we have
+        enough stack space to build the new frame at the bottom of the old one,
+        but that is not true for the FTL.
+
+        Moreover, for efficiency, this adds to the shuffler the ability to
+        record the state of the TagTypeNumber, and to re-use the same register
+        when doing several consecutive integer boxings with no spilling in
+        between.
+
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * bytecode/ValueRecovery.h:
+        (JSC::ValueRecovery::inRegister):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleInlining):
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::isFunctionTerminal):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::emitCall):
+        * dfg/DFGTierUpCheckInjectionPhase.cpp:
+        (JSC::DFG::TierUpCheckInjectionPhase::run):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLCompile.cpp:
+        (JSC::FTL::mmAllocateDataSection):
+        * ftl/FTLInlineCacheSize.cpp:
+        (JSC::FTL::sizeOfTailCallVarargs):
+        (JSC::FTL::sizeOfTailCallForwardVarargs):
+        (JSC::FTL::sizeOfICFor):
+        * ftl/FTLInlineCacheSize.h:
+        * ftl/FTLJSCall.cpp:
+        (JSC::FTL::JSCall::JSCall):
+        * ftl/FTLJSCallBase.cpp:
+        (JSC::FTL::JSCallBase::emit):
+        (JSC::FTL::JSCallBase::link):
+        * ftl/FTLJSCallBase.h:
+        * ftl/FTLJSCallVarargs.cpp:
+        (JSC::FTL::JSCallVarargs::JSCallVarargs):
+        (JSC::FTL::JSCallVarargs::emit):
+        * ftl/FTLJSTailCall.cpp: Added.
+        (JSC::FTL::getRegisterWithAddend):
+        (JSC::FTL::recoveryFor):
+        (JSC::FTL::sizeFor):
+        (JSC::FTL::JSTailCall::JSTailCall):
+        (JSC::FTL::m_instructionOffset):
+        (JSC::FTL::JSTailCall::emit):
+        * ftl/FTLJSTailCall.h: Copied from Source/JavaScriptCore/ftl/FTLJSCallBase.h.
+        (JSC::FTL::JSTailCall::stackmapID):
+        (JSC::FTL::JSTailCall::estimatedSize):
+        (JSC::FTL::JSTailCall::numArguments):
+        (JSC::FTL::JSTailCall::operator&lt;):
+        * ftl/FTLLocation.h:
+        (JSC::FTL::Location::operator bool):
+        (JSC::FTL::Location::operator!):
+        * ftl/FTLLowerDFGToLLVM.cpp:
+        (JSC::FTL::DFG::LowerDFGToLLVM::lower):
+        (JSC::FTL::DFG::LowerDFGToLLVM::compileNode):
+        (JSC::FTL::DFG::LowerDFGToLLVM::compileTailCall):
+        (JSC::FTL::DFG::LowerDFGToLLVM::compileCallOrConstructVarargs):
+        (JSC::FTL::DFG::LowerDFGToLLVM::callPreflight):
+        (JSC::FTL::DFG::LowerDFGToLLVM::exitValueForTailCall):
+        * ftl/FTLState.h:
+        * jit/AssemblyHelpers.cpp:
+        (JSC::AssemblyHelpers::emitExceptionCheck):
+        * jit/CallFrameShuffleData.h:
+        * jit/CallFrameShuffler.cpp:
+        (JSC::CallFrameShuffler::CallFrameShuffler):
+        (JSC::CallFrameShuffler::dump):
+        (JSC::CallFrameShuffler::spill):
+        (JSC::CallFrameShuffler::extendFrameIfNeeded):
+        (JSC::CallFrameShuffler::prepareForSlowPath):
+        (JSC::CallFrameShuffler::prepareAny):
+        * jit/CallFrameShuffler.h:
+        (JSC::CallFrameShuffler::restoreGPR):
+        (JSC::CallFrameShuffler::getFreeRegister):
+        (JSC::CallFrameShuffler::getFreeTempGPR):
+        (JSC::CallFrameShuffler::ensureTempGPR):
+        (JSC::CallFrameShuffler::addNew):
+        * jit/CallFrameShuffler64.cpp:
+        (JSC::CallFrameShuffler::emitBox):
+        (JSC::CallFrameShuffler::tryAcquireTagTypeNumber):
+        * jit/JITCall.cpp:
+        (JSC::JIT::compileOpCall):
+        * jit/Reg.h:
+        (JSC::Reg::Reg):
+        (JSC::Reg::isHashTableDeletedValue):
+        (JSC::Reg::deleted):
+        (JSC::RegHash::hash):
+        (JSC::RegHash::equal):
+        * test/es6.yaml:
+
</ins><span class="cx"> 2015-09-28  Keith Miller  &lt;keith_miller@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         ObjectPropertyConditionSet::mergedWith does not produce a minimal intersection.
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreJavaScriptCorevcxprojJavaScriptCorevcxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -539,6 +539,7 @@
</span><span class="cx">     &lt;ClCompile Include=&quot;..\ftl\FTLJSCall.cpp&quot; /&gt;
</span><span class="cx">     &lt;ClCompile Include=&quot;..\ftl\FTLJSCallBase.cpp&quot; /&gt;
</span><span class="cx">     &lt;ClCompile Include=&quot;..\ftl\FTLJSCallVarargs.cpp&quot; /&gt;
</span><ins>+    &lt;ClCompile Include=&quot;..\ftl\FTLJSTailCall.cpp&quot; /&gt;
</ins><span class="cx">     &lt;ClCompile Include=&quot;..\ftl\FTLLink.cpp&quot; /&gt;
</span><span class="cx">     &lt;ClCompile Include=&quot;..\ftl\FTLLocation.cpp&quot; /&gt;
</span><span class="cx">     &lt;ClCompile Include=&quot;..\ftl\FTLLowerDFGToLLVM.cpp&quot; /&gt;
</span><span class="lines">@@ -1299,6 +1300,7 @@
</span><span class="cx">     &lt;ClInclude Include=&quot;..\ftl\FTLJSCall.h&quot; /&gt;
</span><span class="cx">     &lt;ClInclude Include=&quot;..\ftl\FTLJSCallBase.h&quot; /&gt;
</span><span class="cx">     &lt;ClInclude Include=&quot;..\ftl\FTLJSCallVarargs.h&quot; /&gt;
</span><ins>+    &lt;ClInclude Include=&quot;..\ftl\FTLJSTailCall.h&quot; /&gt;
</ins><span class="cx">     &lt;ClInclude Include=&quot;..\ftl\FTLLink.h&quot; /&gt;
</span><span class="cx">     &lt;ClInclude Include=&quot;..\ftl\FTLLocation.h&quot; /&gt;
</span><span class="cx">     &lt;ClInclude Include=&quot;..\ftl\FTLLowerDFGToLLVM.h&quot; /&gt;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreJavaScriptCorevcxprojJavaScriptCorevcxprojfilters"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -1581,6 +1581,9 @@
</span><span class="cx">     &lt;ClCompile Include=&quot;..\ftl\FTLJSCall.cpp&quot;&gt;
</span><span class="cx">       &lt;Filter&gt;ftl&lt;/Filter&gt;
</span><span class="cx">     &lt;/ClCompile&gt;
</span><ins>+    &lt;ClCompile Include=&quot;..\ftl\FTLJSTailCall.cpp&quot;&gt;
+      &lt;Filter&gt;ftl&lt;/Filter&gt;
+    &lt;/ClCompile&gt;
</ins><span class="cx">     &lt;ClCompile Include=&quot;..\ftl\FTLLink.cpp&quot;&gt;
</span><span class="cx">       &lt;Filter&gt;ftl&lt;/Filter&gt;
</span><span class="cx">     &lt;/ClCompile&gt;
</span><span class="lines">@@ -4144,6 +4147,9 @@
</span><span class="cx">     &lt;ClInclude Include=&quot;..\ftl\FTLJSCall.h&quot;&gt;
</span><span class="cx">       &lt;Filter&gt;ftl&lt;/Filter&gt;
</span><span class="cx">     &lt;/ClInclude&gt;
</span><ins>+    &lt;ClInclude Include=&quot;..\ftl\FTLJSTailCall.h&quot;&gt;
+      &lt;Filter&gt;ftl&lt;/Filter&gt;
+    &lt;/ClInclude&gt;
</ins><span class="cx">     &lt;ClInclude Include=&quot;..\ftl\FTLLink.h&quot;&gt;
</span><span class="cx">       &lt;Filter&gt;ftl&lt;/Filter&gt;
</span><span class="cx">     &lt;/ClInclude&gt;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreJavaScriptCorexcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -979,6 +979,8 @@
</span><span class="cx">                 623A37EC1B87A7C000754209 /* RegisterMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 623A37EB1B87A7BD00754209 /* RegisterMap.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">                 627673231B680C1E00FD9F2E /* CallMode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 627673211B680C1E00FD9F2E /* CallMode.cpp */; };
</span><span class="cx">                 627673241B680C1E00FD9F2E /* CallMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 627673221B680C1E00FD9F2E /* CallMode.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><ins>+                62774DAA1B8D4B190006F05A /* FTLJSTailCall.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 62774DA81B8D4B190006F05A /* FTLJSTailCall.cpp */; };
+                62774DAB1B8D4B190006F05A /* FTLJSTailCall.h in Headers */ = {isa = PBXBuildFile; fileRef = 62774DA91B8D4B190006F05A /* FTLJSTailCall.h */; };
</ins><span class="cx">                 62D2D38F1ADF103F000206C1 /* FunctionRareData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 62D2D38D1ADF103F000206C1 /* FunctionRareData.cpp */; };
</span><span class="cx">                 62D2D3901ADF103F000206C1 /* FunctionRareData.h in Headers */ = {isa = PBXBuildFile; fileRef = 62D2D38E1ADF103F000206C1 /* FunctionRareData.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">                 62D755D41B84FB3D001801FA /* CallFrameShuffler64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 62D755D31B84FB39001801FA /* CallFrameShuffler64.cpp */; };
</span><span class="lines">@@ -2797,6 +2799,8 @@
</span><span class="cx">                 623A37EB1B87A7BD00754209 /* RegisterMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegisterMap.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 627673211B680C1E00FD9F2E /* CallMode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CallMode.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 627673221B680C1E00FD9F2E /* CallMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CallMode.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><ins>+                62774DA81B8D4B190006F05A /* FTLJSTailCall.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FTLJSTailCall.cpp; path = ftl/FTLJSTailCall.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
+                62774DA91B8D4B190006F05A /* FTLJSTailCall.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTLJSTailCall.h; path = ftl/FTLJSTailCall.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</ins><span class="cx">                 62A9A29E1B0BED4800BD54CA /* DFGLazyNode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGLazyNode.cpp; path = dfg/DFGLazyNode.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 62A9A29F1B0BED4800BD54CA /* DFGLazyNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGLazyNode.h; path = dfg/DFGLazyNode.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 62D2D38D1ADF103F000206C1 /* FunctionRareData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FunctionRareData.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="lines">@@ -3975,6 +3979,8 @@
</span><span class="cx">                                 0FD1202E1A8AED12000F5280 /* FTLJSCallBase.h */,
</span><span class="cx">                                 0FD120311A8C85BD000F5280 /* FTLJSCallVarargs.cpp */,
</span><span class="cx">                                 0FD120321A8C85BD000F5280 /* FTLJSCallVarargs.h */,
</span><ins>+                                62774DA81B8D4B190006F05A /* FTLJSTailCall.cpp */,
+                                62774DA91B8D4B190006F05A /* FTLJSTailCall.h */,
</ins><span class="cx">                                 0F8F2B93172E049E007DBDA5 /* FTLLink.cpp */,
</span><span class="cx">                                 0F8F2B94172E049E007DBDA5 /* FTLLink.h */,
</span><span class="cx">                                 0FCEFADD180738C000472CE4 /* FTLLocation.cpp */,
</span><span class="lines">@@ -6318,6 +6324,7 @@
</span><span class="cx">                                 0F6B1CB6185FC9E900845D97 /* FTLJSCall.h in Headers */,
</span><span class="cx">                                 0FD120301A8AED12000F5280 /* FTLJSCallBase.h in Headers */,
</span><span class="cx">                                 0FD120341A8C85BD000F5280 /* FTLJSCallVarargs.h in Headers */,
</span><ins>+                                62774DAB1B8D4B190006F05A /* FTLJSTailCall.h in Headers */,
</ins><span class="cx">                                 0F8F2B96172E04A3007DBDA5 /* FTLLink.h in Headers */,
</span><span class="cx">                                 0FCEFAE0180738C000472CE4 /* FTLLocation.h in Headers */,
</span><span class="cx">                                 0FEA0A10170513DB00BB722C /* FTLLowerDFGToLLVM.h in Headers */,
</span><span class="lines">@@ -7690,6 +7697,7 @@
</span><span class="cx">                                 0F6B1CB5185FC9E900845D97 /* FTLJSCall.cpp in Sources */,
</span><span class="cx">                                 0FD1202F1A8AED12000F5280 /* FTLJSCallBase.cpp in Sources */,
</span><span class="cx">                                 0FD120331A8C85BD000F5280 /* FTLJSCallVarargs.cpp in Sources */,
</span><ins>+                                62774DAA1B8D4B190006F05A /* FTLJSTailCall.cpp in Sources */,
</ins><span class="cx">                                 0F8F2B95172E04A0007DBDA5 /* FTLLink.cpp in Sources */,
</span><span class="cx">                                 0FCEFADF180738C000472CE4 /* FTLLocation.cpp in Sources */,
</span><span class="cx">                                 0FEA0A0F170513DB00BB722C /* FTLLowerDFGToLLVM.cpp in Sources */,
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGByteCodeParsercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -1882,11 +1882,15 @@
</span><span class="cx">         m_currentIndex = nextOffset;
</span><span class="cx">         m_exitOK = true;
</span><span class="cx">         processSetLocalQueue(); // This only comes into play for intrinsics, since normal inlined code will leave an empty queue.
</span><del>-        addToGraph(Jump);
</del><ins>+        if (Node* terminal = m_currentBlock-&gt;terminal())
+            ASSERT_UNUSED(terminal, terminal-&gt;op() == TailCall || terminal-&gt;op() == TailCallVarargs);
+        else {
+            addToGraph(Jump);
+            landingBlocks.append(m_currentBlock);
+        }
</ins><span class="cx">         if (verbose)
</span><span class="cx">             dataLog(&quot;Marking &quot;, RawPointer(m_currentBlock), &quot; as linked (tail of poly inlinee)\n&quot;);
</span><span class="cx">         m_currentBlock-&gt;didLink();
</span><del>-        landingBlocks.append(m_currentBlock);
</del><span class="cx"> 
</span><span class="cx">         if (verbose)
</span><span class="cx">             dataLog(&quot;Finished inlining &quot;, callLinkStatus[i], &quot; at &quot;, currentCodeOrigin(), &quot;.\n&quot;);
</span><span class="lines">@@ -1919,8 +1923,12 @@
</span><span class="cx">     m_currentIndex = nextOffset;
</span><span class="cx">     m_exitOK = true; // Origin changed, so it's fine to exit again.
</span><span class="cx">     processSetLocalQueue();
</span><del>-    addToGraph(Jump);
-    landingBlocks.append(m_currentBlock);
</del><ins>+    if (Node* terminal = m_currentBlock-&gt;terminal())
+        ASSERT_UNUSED(terminal, terminal-&gt;op() == TailCall || terminal-&gt;op() == TailCallVarargs);
+    else {
+        addToGraph(Jump);
+        landingBlocks.append(m_currentBlock);
+    }
</ins><span class="cx">     
</span><span class="cx">     RefPtr&lt;BasicBlock&gt; continuationBlock = adoptRef(
</span><span class="cx">         new BasicBlock(UINT_MAX, m_numArguments, m_numLocals, PNaN));
</span><span class="lines">@@ -3664,7 +3672,7 @@
</span><span class="cx">                 // We could be the dummy jump to a return after a non-inlined, non-emulated tail call in a ternary operator
</span><span class="cx">                 Node* terminal = m_currentBlock-&gt;terminal();
</span><span class="cx">                 ASSERT_UNUSED(terminal, terminal-&gt;op() == TailCall || terminal-&gt;op() == TailCallVarargs);
</span><del>-                LAST_OPCODE(op_ret);
</del><ins>+                LAST_OPCODE(op_jmp);
</ins><span class="cx">             }
</span><span class="cx">             int relativeOffset = currentInstruction[1].u.operand;
</span><span class="cx">             addToGraph(Jump, OpInfo(m_currentIndex + relativeOffset));
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGClobberizeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGClobberize.h (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGClobberize.h        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/dfg/DFGClobberize.h        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -1035,8 +1035,6 @@
</span><span class="cx">         
</span><span class="cx">     case ThrowReferenceError:
</span><span class="cx">         write(SideState);
</span><del>-        read(HeapObjectCount);
-        write(HeapObjectCount);
</del><span class="cx">         return;
</span><span class="cx">         
</span><span class="cx">     case CountExecution:
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGNodeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGNode.h (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGNode.h        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/dfg/DFGNode.h        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -1118,6 +1118,14 @@
</span><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    bool isFunctionTerminal()
+    {
+        if (isTerminal() &amp;&amp; !numSuccessors())
+            return true;
+
+        return false;
+    }
+
</ins><span class="cx">     unsigned targetBytecodeOffsetDuringParsing()
</span><span class="cx">     {
</span><span class="cx">         ASSERT(isJump());
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGSpeculativeJIT64cpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -771,6 +771,7 @@
</span><span class="cx">             calleeGPR = callee.gpr();
</span><span class="cx">             callee.use();
</span><span class="cx"> 
</span><ins>+            shuffleData.tagTypeNumber = GPRInfo::tagTypeNumberRegister;
</ins><span class="cx">             shuffleData.numLocals = m_jit.graph().frameRegisterCount();
</span><span class="cx">             shuffleData.callee = ValueRecovery::inGPR(calleeGPR, DataFormatJS);
</span><span class="cx">             shuffleData.args.resize(numPassedArgs);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGTierUpCheckInjectionPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGTierUpCheckInjectionPhase.cpp (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGTierUpCheckInjectionPhase.cpp        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/dfg/DFGTierUpCheckInjectionPhase.cpp        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -95,7 +95,7 @@
</span><span class="cx">             }
</span><span class="cx">             
</span><span class="cx">             NodeAndIndex terminal = block-&gt;findTerminal();
</span><del>-            if (terminal.node-&gt;op() == Return) {
</del><ins>+            if (terminal.node-&gt;isFunctionTerminal()) {
</ins><span class="cx">                 insertionSet.insertNode(
</span><span class="cx">                     terminal.index, SpecNone, CheckTierUpAtReturn, terminal.node-&gt;origin);
</span><span class="cx">             }
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLCapabilitiescpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -130,10 +130,16 @@
</span><span class="cx">     case NotifyWrite:
</span><span class="cx">     case StoreBarrier:
</span><span class="cx">     case Call:
</span><ins>+    case TailCall:
+    case TailCallInlinedCaller:
</ins><span class="cx">     case Construct:
</span><span class="cx">     case CallVarargs:
</span><ins>+    case TailCallVarargs:
+    case TailCallVarargsInlinedCaller:
+    case ConstructVarargs:
</ins><span class="cx">     case CallForwardVarargs:
</span><del>-    case ConstructVarargs:
</del><ins>+    case TailCallForwardVarargs:
+    case TailCallForwardVarargsInlinedCaller:
</ins><span class="cx">     case ConstructForwardVarargs:
</span><span class="cx">     case LoadVarargs:
</span><span class="cx">     case ValueToInt32:
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLCompilecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLCompile.cpp (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLCompile.cpp        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/ftl/FTLCompile.cpp        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -618,6 +618,22 @@
</span><span class="cx">             call.link(vm, linkBuffer, state.finalizer-&gt;handleExceptionsLinkBuffer-&gt;entrypoint());
</span><span class="cx">         });
</span><span class="cx">     }
</span><ins>+
+    adjustCallICsForStackmaps(state.jsTailCalls, recordMap);
+
+    for (unsigned i = state.jsTailCalls.size(); i--;) {
+        JSTailCall&amp; call = state.jsTailCalls[i];
+
+        CCallHelpers fastPathJIT(&amp;vm, codeBlock);
+        call.emit(*state.jitCode.get(), fastPathJIT);
+
+        char* startOfIC = bitwise_cast&lt;char*&gt;(generatedFunction) + call.m_instructionOffset;
+        size_t sizeOfIC = call.estimatedSize();
+
+        generateInlineIfPossibleOutOfLineIfNot(state, vm, codeBlock, fastPathJIT, startOfIC, sizeOfIC, &quot;tail call inline cache&quot;, [&amp;] (LinkBuffer&amp; linkBuffer, CCallHelpers&amp;, bool) {
+            call.link(vm, linkBuffer);
+        });
+    }
</ins><span class="cx">     
</span><span class="cx">     auto iter = recordMap.find(state.handleStackOverflowExceptionStackmapID);
</span><span class="cx">     // It's sort of remotely possible that we won't have an in-band exception handling
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLInlineCacheSizecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLInlineCacheSize.cpp (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLInlineCacheSize.cpp        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/ftl/FTLInlineCacheSize.cpp        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -82,6 +82,15 @@
</span><span class="cx"> #endif
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+size_t sizeOfTailCallVarargs()
+{
+#if CPU(ARM64)
+    return 188 + sizeOfCallVarargs();
+#else
+    return 151 + sizeOfCallVarargs();
+#endif
+}
+
</ins><span class="cx"> size_t sizeOfCallForwardVarargs()
</span><span class="cx"> {
</span><span class="cx"> #if CPU(ARM64)
</span><span class="lines">@@ -91,6 +100,15 @@
</span><span class="cx"> #endif
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+size_t sizeOfTailCallForwardVarargs()
+{
+#if CPU(ARM64)
+    return 188 + sizeOfCallForwardVarargs();
+#else
+    return 151 + sizeOfCallForwardVarargs();
+#endif
+}
+
</ins><span class="cx"> size_t sizeOfConstructVarargs()
</span><span class="cx"> {
</span><span class="cx">     return sizeOfCallVarargs(); // Should be the same size.
</span><span class="lines">@@ -121,9 +139,15 @@
</span><span class="cx">     case Construct:
</span><span class="cx">         return sizeOfCall();
</span><span class="cx">     case CallVarargs:
</span><ins>+    case TailCallVarargsInlinedCaller:
</ins><span class="cx">         return sizeOfCallVarargs();
</span><ins>+    case TailCallVarargs:
+        return sizeOfTailCallVarargs();
</ins><span class="cx">     case CallForwardVarargs:
</span><ins>+    case TailCallForwardVarargsInlinedCaller:
</ins><span class="cx">         return sizeOfCallForwardVarargs();
</span><ins>+    case TailCallForwardVarargs:
+        return sizeOfTailCallForwardVarargs();
</ins><span class="cx">     case ConstructVarargs:
</span><span class="cx">         return sizeOfConstructVarargs();
</span><span class="cx">     case ConstructForwardVarargs:
</span><span class="lines">@@ -131,7 +155,7 @@
</span><span class="cx">     case In:
</span><span class="cx">         return sizeOfIn();
</span><span class="cx">     default:
</span><del>-        return 0;
</del><ins>+        RELEASE_ASSERT_NOT_REACHED();
</ins><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLInlineCacheSizeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLInlineCacheSize.h (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLInlineCacheSize.h        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/ftl/FTLInlineCacheSize.h        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -40,7 +40,9 @@
</span><span class="cx"> size_t sizeOfPutById();
</span><span class="cx"> size_t sizeOfCall();
</span><span class="cx"> size_t sizeOfCallVarargs();
</span><ins>+size_t sizeOfTailCallVarargs();
</ins><span class="cx"> size_t sizeOfCallForwardVarargs();
</span><ins>+size_t sizeOfTailCallForwardVarargs();
</ins><span class="cx"> size_t sizeOfConstructVarargs();
</span><span class="cx"> size_t sizeOfConstructForwardVarargs();
</span><span class="cx"> size_t sizeOfIn();
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLJSCallcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLJSCall.cpp (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLJSCall.cpp        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/ftl/FTLJSCall.cpp        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -48,7 +48,7 @@
</span><span class="cx">     , m_stackmapID(stackmapID)
</span><span class="cx">     , m_instructionOffset(0)
</span><span class="cx"> {
</span><del>-    ASSERT(node-&gt;op() == Call || node-&gt;op() == Construct);
</del><ins>+    ASSERT(node-&gt;op() == Call || node-&gt;op() == Construct || node-&gt;op() == TailCallInlinedCaller);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void JSCall::emit(CCallHelpers&amp; jit, unsigned stackSizeForLocals)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLJSCallBasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLJSCallBase.cpp (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLJSCallBase.cpp        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/ftl/FTLJSCallBase.cpp        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -55,16 +55,29 @@
</span><span class="cx">     CCallHelpers::Jump slowPath = jit.branchPtrWithPatch(
</span><span class="cx">         CCallHelpers::NotEqual, GPRInfo::regT0, m_targetToCheck,
</span><span class="cx">         CCallHelpers::TrustedImmPtr(0));
</span><del>-    
-    m_fastCall = jit.nearCall();
-    CCallHelpers::Jump done = jit.jump();
-    
</del><ins>+
+    CCallHelpers::Jump done;
+
+    if (CallLinkInfo::callModeFor(m_type) == CallMode::Tail) {
+        jit.emitRestoreCalleeSaves();
+        jit.prepareForTailCallSlow();
+        m_fastCall = jit.nearTailCall();
+    } else {
+        m_fastCall = jit.nearCall();
+        done = jit.jump();
+    }
+
</ins><span class="cx">     slowPath.link(&amp;jit);
</span><del>-    
</del><ins>+
</ins><span class="cx">     jit.move(CCallHelpers::TrustedImmPtr(m_callLinkInfo), GPRInfo::regT2);
</span><span class="cx">     m_slowCall = jit.nearCall();
</span><del>-    
-    done.link(&amp;jit);
</del><ins>+
+    if (CallLinkInfo::callModeFor(m_type) == CallMode::Tail)
+        jit.abortWithReason(JITDidReturnFromTailCall);
+    else
+        done.link(&amp;jit);
+
+    m_callLinkInfo-&gt;setUpCall(m_type, m_origin, GPRInfo::regT0);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void JSCallBase::link(VM&amp; vm, LinkBuffer&amp; linkBuffer)
</span><span class="lines">@@ -72,9 +85,8 @@
</span><span class="cx">     linkBuffer.link(
</span><span class="cx">         m_slowCall, FunctionPtr(vm.getCTIStub(linkCallThunkGenerator).code().executableAddress()));
</span><span class="cx"> 
</span><del>-    m_callLinkInfo-&gt;setUpCallFromFTL(m_type, m_origin, linkBuffer.locationOfNearCall(m_slowCall),
-        linkBuffer.locationOf(m_targetToCheck), linkBuffer.locationOfNearCall(m_fastCall),
-        GPRInfo::regT0);
</del><ins>+    m_callLinkInfo-&gt;setCallLocations(linkBuffer.locationOfNearCall(m_slowCall),
+        linkBuffer.locationOf(m_targetToCheck), linkBuffer.locationOfNearCall(m_fastCall));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> } } // namespace JSC::FTL
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLJSCallBaseh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLJSCallBase.h (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLJSCallBase.h        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/ftl/FTLJSCallBase.h        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -50,7 +50,7 @@
</span><span class="cx">     void emit(CCallHelpers&amp;);
</span><span class="cx">     void link(VM&amp;, LinkBuffer&amp;);
</span><span class="cx">     
</span><del>-private:
</del><ins>+protected:
</ins><span class="cx">     CallLinkInfo::CallType m_type;
</span><span class="cx">     CodeOrigin m_origin;
</span><span class="cx">     CCallHelpers::DataLabelPtr m_targetToCheck;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLJSCallVarargscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLJSCallVarargs.cpp (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLJSCallVarargs.cpp        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/ftl/FTLJSCallVarargs.cpp        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -51,12 +51,15 @@
</span><span class="cx">     , m_node(node)
</span><span class="cx">     , m_callBase(
</span><span class="cx">         (node-&gt;op() == ConstructVarargs || node-&gt;op() == ConstructForwardVarargs)
</span><del>-        ? CallLinkInfo::ConstructVarargs : CallLinkInfo::CallVarargs,
</del><ins>+        ? CallLinkInfo::ConstructVarargs : (node-&gt;op() == TailCallVarargs || node-&gt;op() == TailCallForwardVarargs)
+        ? CallLinkInfo::TailCallVarargs : CallLinkInfo::CallVarargs,
</ins><span class="cx">         node-&gt;origin.semantic)
</span><span class="cx">     , m_instructionOffset(0)
</span><span class="cx"> {
</span><span class="cx">     ASSERT(
</span><span class="cx">         node-&gt;op() == CallVarargs || node-&gt;op() == CallForwardVarargs
</span><ins>+        || node-&gt;op() == TailCallVarargsInlinedCaller || node-&gt;op() == TailCallForwardVarargsInlinedCaller
+        || node-&gt;op() == TailCallVarargs || node-&gt;op() == TailCallForwardVarargs
</ins><span class="cx">         || node-&gt;op() == ConstructVarargs || node-&gt;op() == ConstructForwardVarargs);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -83,11 +86,15 @@
</span><span class="cx">     
</span><span class="cx">     switch (m_node-&gt;op()) {
</span><span class="cx">     case CallVarargs:
</span><ins>+    case TailCallVarargs:
+    case TailCallVarargsInlinedCaller:
</ins><span class="cx">     case ConstructVarargs:
</span><span class="cx">         argumentsGPR = GPRInfo::argumentGPR1;
</span><span class="cx">         thisGPR = GPRInfo::argumentGPR2;
</span><span class="cx">         break;
</span><span class="cx">     case CallForwardVarargs:
</span><ins>+    case TailCallForwardVarargs:
+    case TailCallForwardVarargsInlinedCaller:
</ins><span class="cx">     case ConstructForwardVarargs:
</span><span class="cx">         thisGPR = GPRInfo::argumentGPR1;
</span><span class="cx">         forwarding = true;
</span><span class="lines">@@ -196,7 +203,7 @@
</span><span class="cx">     // Henceforth we make the call. The base FTL call machinery expects the callee in regT0 and for the
</span><span class="cx">     // stack frame to already be set up, which it is.
</span><span class="cx">     jit.store64(GPRInfo::regT0, CCallHelpers::calleeFrameSlot(JSStack::Callee));
</span><del>-    
</del><ins>+
</ins><span class="cx">     m_callBase.emit(jit);
</span><span class="cx">     
</span><span class="cx">     // Undo the damage we've done.
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLJSTailCallcpp"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/ftl/FTLJSTailCall.cpp (0 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLJSTailCall.cpp                                (rev 0)
+++ trunk/Source/JavaScriptCore/ftl/FTLJSTailCall.cpp        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -0,0 +1,326 @@
</span><ins>+/*
+ * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include &quot;config.h&quot;
+#include &quot;FTLJSTailCall.h&quot;
+
+#if ENABLE(FTL_JIT)
+
+#include &quot;CallFrameShuffler.h&quot;
+#include &quot;DFGNode.h&quot;
+#include &quot;FTLJITCode.h&quot;
+#include &quot;FTLLocation.h&quot;
+#include &quot;FTLStackMaps.h&quot;
+#include &quot;JSCJSValueInlines.h&quot;
+#include &quot;LinkBuffer.h&quot;
+
+namespace JSC { namespace FTL {
+
+using namespace DFG;
+
+namespace {
+
+FTL::Location getRegisterWithAddend(const ExitValue&amp; value, StackMaps::Record&amp; record, StackMaps&amp; stackmaps)
+{
+    if (value.kind() != ExitValueArgument)
+        return { };
+
+    auto location =
+        FTL::Location::forStackmaps(&amp;stackmaps, record.locations[value.exitArgument().argument()]);
+
+    if (location.kind() != Location::Register || !location.addend())
+        return { };
+
+    RELEASE_ASSERT(location.isGPR());
+    return location;
+}
+
+ValueRecovery recoveryFor(const ExitValue&amp; value, StackMaps::Record&amp; record, StackMaps&amp; stackmaps)
+{
+    switch (value.kind()) {
+    case ExitValueConstant:
+        return ValueRecovery::constant(value.constant());
+
+    case ExitValueArgument: {
+        auto location =
+            FTL::Location::forStackmaps(&amp;stackmaps, record.locations[value.exitArgument().argument()]);
+        auto format = value.exitArgument().format();
+
+        switch (location.kind()) {
+        case Location::Register:
+            // We handle the addend outside
+            return ValueRecovery::inRegister(location.dwarfReg().reg(), format);
+
+        case Location::Indirect:
+            // Oh LLVM, you crazy...
+            RELEASE_ASSERT(location.dwarfReg().reg() == Reg(MacroAssembler::framePointerRegister));
+            RELEASE_ASSERT(!(location.offset() % sizeof(void*)));
+            return ValueRecovery::displacedInJSStack(VirtualRegister { static_cast&lt;int&gt;(location.offset() / sizeof(void*)) }, format);
+
+        case Location::Constant:
+            return ValueRecovery::constant(JSValue::decode(location.constant()));
+
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+        }
+    }
+
+    case ExitValueInJSStack:
+        return ValueRecovery::displacedInJSStack(value.virtualRegister(), DataFormatJS);
+
+    case ExitValueInJSStackAsInt32:
+        return ValueRecovery::displacedInJSStack(value.virtualRegister(), DataFormatInt32);
+
+    case ExitValueInJSStackAsInt52:
+        return ValueRecovery::displacedInJSStack(value.virtualRegister(), DataFormatInt52);
+
+    case ExitValueInJSStackAsDouble:
+        return ValueRecovery::displacedInJSStack(value.virtualRegister(), DataFormatDouble);
+
+    default:
+        RELEASE_ASSERT_NOT_REACHED();
+    }
+}
+
+// This computes an estimated size (in bits) for the sequence of
+// instructions required to load, box, and store a value of a given
+// type, assuming no spilling is required.
+uint32_t sizeFor(DataFormat format)
+{
+    switch (format) {
+    case DataFormatInt32:
+        // Boxing is zero-extending and tagging
+#if CPU(X86_64)
+        return 6 + sizeFor(DataFormatJS);
+#elif CPU(ARM64)
+        return 8 + sizeFor(DataFormatJS);
+#else
+        return sizeOfZeroExtend32 + sizeOfOrImm64 + sizeFor(DataFormatJS);
+#endif
+
+    case DataFormatInt52:
+        // Boxing is first a conversion to StrictInt52, then
+        // StrictInt52 boxing
+#if CPU(X86_64)
+        return 4 + sizeFor(DataFormatStrictInt52);
+#elif CPU(ARM64)
+        return 4 + sizeFor(DataFormatStrictInt52);
+#else
+        return sizeOfShiftImm32 + sizeFor(DataFormatStrictInt52);
+#endif
+
+    case DataFormatStrictInt52:
+        // Boxing is first a conversion to double, then double boxing
+#if CPU(X86_64)
+        return 8 + sizeFor(DataFormatDouble);
+#elif CPU(ARM64)
+        return 4 + sizeFor(DataFormatDouble);
+#else
+        return sizeOfConvertInt64ToDouble + sizeFor(DataFormatDouble);
+#endif
+
+    case DataFormatDouble:
+        // Boxing is purifying, moving to a GPR, and tagging
+#if CPU(X86_64)
+        return 38 + sizeFor(DataFormatJS);
+#elif CPU(ARM64)
+        return 28 + sizeFor(DataFormatJS);
+#else
+        return sizeOfPurifyNaN + sizeOfSubImm64 + sizeOfMoveDoubleTo64 + sizeFor(DataFormatJS);
+#endif
+
+    case DataFormatBoolean:
+        // Boxing is adding ValueFalse
+#if CPU(X86_64)
+        return 4 + sizeFor(DataFormatJS);
+#elif CPU(ARM64)
+        return 4 + sizeFor(DataFormatJS);
+#else
+        return sizeOfAddImm32 + sizeFor(DataFormatJS);
+#endif
+
+    case DataFormatJS:
+        // We will load (in a GPR or FPR) then store the value
+#if CPU(X86_64)
+        return 8;
+#elif CPU(ARM64)
+        return 8;
+#else
+        return sizeOfLoad + sizeOfStore;
+#endif
+
+    default:
+        RELEASE_ASSERT_NOT_REACHED();
+    }
+}
+
+} // anonymous namespace
+
+JSTailCall::JSTailCall(unsigned stackmapID, Node* node, Vector&lt;ExitValue&gt; arguments)
+    : JSCallBase(CallLinkInfo::TailCall, node-&gt;origin.semantic)
+    , m_stackmapID(stackmapID)
+    , m_arguments { WTF::move(arguments) }
+    , m_instructionOffset(0)
+{
+    ASSERT(node-&gt;op() == TailCall);
+    ASSERT(numArguments() == node-&gt;numChildren() - 1);
+
+    // Estimate the size of the inline cache, assuming that every
+    // value goes from the stack to the stack (in practice, this will
+    // seldom be true, giving us some amount of leeway) and that no
+    // spilling will occur (in practice, this will almost always be
+    // true).
+
+    // We first compute the new frame base and load the fp/lr
+    // registers final values. On debug builds, we also need to
+    // account for the fp-sp delta check (twice: fast and slow path).
+#if CPU(X86_64)
+    m_estimatedSize = 56;
+#if !ASSERT_DISABLED
+    m_estimatedSize += 26;
+#  endif
+#elif CPU(ARM64)
+    m_estimatedSize = 44;
+#if !ASSERT_DISABLED
+    m_estimatedSize += 24;
+#  endif
+#else
+    UNREACHABLE_FOR_PLATFORM();
+#endif
+
+    // Arguments will probably be loaded &amp; stored twice (fast &amp; slow)
+    for (ExitValue&amp; arg : m_arguments)
+        m_estimatedSize += 2 * sizeFor(arg.dataFormat());
+
+    // We also have the slow path check, the two calls, and the
+    // CallLinkInfo load for the slow path
+#if CPU(X86_64)
+    m_estimatedSize += 55;
+#elif CPU(ARM64)
+    m_estimatedSize += 44;
+#else
+    m_estimatedSize += sizeOfCall + sizeOfJump + sizeOfLoad + sizeOfSlowPathCheck;
+#endif
+}
+
+void JSTailCall::emit(JITCode&amp; jitCode, CCallHelpers&amp; jit)
+{
+    StackMaps::Record* record { nullptr };
+    
+    for (unsigned i = jitCode.stackmaps.records.size(); i--;) {
+        record = &amp;jitCode.stackmaps.records[i];
+        if (record-&gt;patchpointID == m_stackmapID)
+            break;
+    }
+
+    RELEASE_ASSERT(record-&gt;patchpointID == m_stackmapID);
+
+    m_callLinkInfo = jit.codeBlock()-&gt;addCallLinkInfo();
+
+    CallFrameShuffleData shuffleData;
+
+    // The callee was the first passed argument, and must be in a GPR because
+    // we used the &quot;anyregcc&quot; calling convention
+    auto calleeLocation =
+        FTL::Location::forStackmaps(nullptr, record-&gt;locations[0]);
+    GPRReg calleeGPR = calleeLocation.directGPR();
+    shuffleData.callee = ValueRecovery::inGPR(calleeGPR, DataFormatJS);
+
+    // The tag type number was the second argument, if there was one
+    auto tagTypeNumberLocation =
+        FTL::Location::forStackmaps(&amp;jitCode.stackmaps, record-&gt;locations[1]);
+    if (tagTypeNumberLocation.isGPR() &amp;&amp; !tagTypeNumberLocation.addend())
+        shuffleData.tagTypeNumber = tagTypeNumberLocation.directGPR();
+
+    shuffleData.args.grow(numArguments());
+    HashMap&lt;Reg, Vector&lt;std::pair&lt;ValueRecovery*, int32_t&gt;&gt;&gt; withAddend;
+    size_t numAddends { 0 };
+    for (size_t i = 0; i &lt; numArguments(); ++i) {
+        shuffleData.args[i] = recoveryFor(m_arguments[i], *record, jitCode.stackmaps);
+        if (FTL::Location addend = getRegisterWithAddend(m_arguments[i], *record, jitCode.stackmaps)) {
+            withAddend.add(
+                addend.dwarfReg().reg(),
+                Vector&lt;std::pair&lt;ValueRecovery*, int32_t&gt;&gt;()).iterator-&gt;value.append(
+                    std::make_pair(&amp;shuffleData.args[i], addend.addend()));
+            numAddends++;
+        }
+    }
+
+    numAddends = WTF::roundUpToMultipleOf(stackAlignmentRegisters(), numAddends);
+
+    shuffleData.numLocals = static_cast&lt;int64_t&gt;(jitCode.stackmaps.stackSizeForLocals()) / sizeof(void*) + numAddends;
+
+    ASSERT(!numAddends == withAddend.isEmpty());
+
+    if (!withAddend.isEmpty()) {
+        jit.subPtr(MacroAssembler::TrustedImm32(numAddends * sizeof(void*)), MacroAssembler::stackPointerRegister);
+        VirtualRegister spillBase { 1 - static_cast&lt;int&gt;(shuffleData.numLocals) };
+        for (auto entry : withAddend) {
+            for (auto pair : entry.value) {
+                ASSERT(numAddends &gt; 0);
+                VirtualRegister spillSlot { spillBase + --numAddends };
+                ASSERT(entry.key.isGPR());
+                jit.addPtr(MacroAssembler::TrustedImm32(pair.second), entry.key.gpr());
+                jit.storePtr(entry.key.gpr(), CCallHelpers::addressFor(spillSlot));
+                jit.subPtr(MacroAssembler::TrustedImm32(pair.second), entry.key.gpr());
+                *pair.first = ValueRecovery::displacedInJSStack(spillSlot, pair.first-&gt;dataFormat());
+            }
+        }
+        ASSERT(numAddends &lt; stackAlignmentRegisters());
+    }
+
+    shuffleData.args.resize(numArguments());
+    for (size_t i = 0; i &lt; numArguments(); ++i)
+        shuffleData.args[i] = recoveryFor(m_arguments[i], *record, jitCode.stackmaps);
+
+    shuffleData.setupCalleeSaveRegisters(jit.codeBlock());
+
+    CCallHelpers::Jump slowPath = jit.branchPtrWithPatch(
+        CCallHelpers::NotEqual, calleeGPR, m_targetToCheck,
+        CCallHelpers::TrustedImmPtr(0));
+
+    m_callLinkInfo-&gt;setFrameShuffleData(shuffleData);
+    CallFrameShuffler(jit, shuffleData).prepareForTailCall();
+
+    m_fastCall = jit.nearTailCall();
+
+    slowPath.link(&amp;jit);
+
+    CallFrameShuffler slowPathShuffler(jit, shuffleData);
+    slowPathShuffler.setCalleeJSValueRegs(JSValueRegs { GPRInfo::regT0 });
+    slowPathShuffler.prepareForSlowPath();
+
+    jit.move(CCallHelpers::TrustedImmPtr(m_callLinkInfo), GPRInfo::regT2);
+
+    m_slowCall = jit.nearCall();
+
+    jit.abortWithReason(JITDidReturnFromTailCall);
+
+    m_callLinkInfo-&gt;setUpCall(m_type, m_origin, calleeGPR);
+}
+
+} } // namespace JSC::FTL
+
+#endif // ENABLE(FTL_JIT)
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLJSTailCallh"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/ftl/FTLJSTailCall.h (0 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLJSTailCall.h                                (rev 0)
+++ trunk/Source/JavaScriptCore/ftl/FTLJSTailCall.h        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -0,0 +1,74 @@
</span><ins>+/*
+ * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef FTLJSTailCall_h
+#define FTLJSTailCall_h
+
+#if ENABLE(FTL_JIT)
+
+#include &quot;FTLExitArgumentList.h&quot;
+#include &quot;FTLExitValue.h&quot;
+#include &quot;FTLJSCallBase.h&quot;
+
+namespace JSC {
+
+namespace DFG {
+struct Node;
+}
+
+namespace FTL {
+
+class JSTailCall : public JSCallBase {
+public:
+    JSTailCall(unsigned stackmapID, DFG::Node*, Vector&lt;ExitValue&gt; arguments);
+
+    void emit(JITCode&amp;, CCallHelpers&amp;);
+    
+    unsigned stackmapID() const { return m_stackmapID; }
+
+    unsigned estimatedSize() const { return m_estimatedSize; }
+
+    unsigned numArguments() const { return m_arguments.size(); }
+
+    bool operator&lt;(const JSTailCall&amp; other) const
+    {
+        return m_instructionOffset &lt; other.m_instructionOffset;
+    }
+    
+private:
+    unsigned m_stackmapID;
+    Vector&lt;ExitValue&gt; m_arguments;
+    unsigned m_estimatedSize;
+
+public:
+    uint32_t m_instructionOffset;
+};
+
+} } // namespace JSC::FTL
+
+#endif // ENABLE(FTL_JIT)
+
+#endif // FTLJSTailCall_h
+
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLLocationh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLLocation.h (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLLocation.h        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/ftl/FTLLocation.h        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -120,7 +120,9 @@
</span><span class="cx">         return u.constant;
</span><span class="cx">     }
</span><span class="cx">     
</span><del>-    bool operator!() const { return kind() == Unprocessed &amp;&amp; !u.variable.offset; }
</del><ins>+    explicit operator bool() const { return kind() != Unprocessed || u.variable.offset; }
+
+    bool operator!() const { return !static_cast&lt;bool&gt;(*this); }
</ins><span class="cx">     
</span><span class="cx">     bool isHashTableDeletedValue() const { return kind() == Unprocessed &amp;&amp; u.variable.offset; }
</span><span class="cx">     
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLLowerDFGToLLVMcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -173,7 +173,11 @@
</span><span class="cx">             for (Node* node : *block) {
</span><span class="cx">                 switch (node-&gt;op()) {
</span><span class="cx">                 case CallVarargs:
</span><ins>+                case TailCallVarargs:
+                case TailCallVarargsInlinedCaller:
</ins><span class="cx">                 case CallForwardVarargs:
</span><ins>+                case TailCallForwardVarargs:
+                case TailCallForwardVarargsInlinedCaller:
</ins><span class="cx">                 case ConstructVarargs:
</span><span class="cx">                 case ConstructForwardVarargs:
</span><span class="cx">                     hasVarargs = true;
</span><span class="lines">@@ -723,11 +727,19 @@
</span><span class="cx">             compileLogicalNot();
</span><span class="cx">             break;
</span><span class="cx">         case Call:
</span><ins>+        case TailCallInlinedCaller:
</ins><span class="cx">         case Construct:
</span><span class="cx">             compileCallOrConstruct();
</span><span class="cx">             break;
</span><ins>+        case TailCall:
+            compileTailCall();
+            break;
</ins><span class="cx">         case CallVarargs:
</span><span class="cx">         case CallForwardVarargs:
</span><ins>+        case TailCallVarargs:
+        case TailCallVarargsInlinedCaller:
+        case TailCallForwardVarargs:
+        case TailCallForwardVarargsInlinedCaller:
</ins><span class="cx">         case ConstructVarargs:
</span><span class="cx">         case ConstructForwardVarargs:
</span><span class="cx">             compileCallOrConstructVarargs();
</span><span class="lines">@@ -4400,6 +4412,41 @@
</span><span class="cx">         
</span><span class="cx">         setJSValue(call);
</span><span class="cx">     }
</span><ins>+
+    void compileTailCall()
+    {
+        int numArgs = m_node-&gt;numChildren() - 1;
+        ExitArgumentList exitArguments;
+        exitArguments.reserveCapacity(numArgs + 6);
+
+        unsigned stackmapID = m_stackmapIDs++;
+        exitArguments.append(lowJSValue(m_graph.varArgChild(m_node, 0)));
+        exitArguments.append(m_tagTypeNumber);
+
+        Vector&lt;ExitValue&gt; callArguments(numArgs);
+
+        bool needsTagTypeNumber { false };
+        for (int i = 0; i &lt; numArgs; ++i) {
+            callArguments[i] =
+                exitValueForTailCall(exitArguments, m_graph.varArgChild(m_node, 1 + i).node());
+            if (callArguments[i].dataFormat() == DataFormatInt32)
+                needsTagTypeNumber = true;
+        }
+
+        JSTailCall tailCall(stackmapID, m_node, WTF::move(callArguments));
+
+        exitArguments.insert(0, m_out.constInt32(needsTagTypeNumber ? 2 : 1));
+        exitArguments.insert(0, constNull(m_out.ref8));
+        exitArguments.insert(0, m_out.constInt32(tailCall.estimatedSize()));
+        exitArguments.insert(0, m_out.constInt64(stackmapID));
+
+        LValue call =
+            m_out.call(m_out.patchpointVoidIntrinsic(), exitArguments);
+        setInstructionCallingConvention(call, LLVMAnyRegCallConv);
+        m_out.unreachable();
+
+        m_ftlState.jsTailCalls.append(tailCall);
+    }
</ins><span class="cx">     
</span><span class="cx">     void compileCallOrConstructVarargs()
</span><span class="cx">     {
</span><span class="lines">@@ -4410,10 +4457,14 @@
</span><span class="cx">         
</span><span class="cx">         switch (m_node-&gt;op()) {
</span><span class="cx">         case CallVarargs:
</span><ins>+        case TailCallVarargs:
+        case TailCallVarargsInlinedCaller:
</ins><span class="cx">         case ConstructVarargs:
</span><span class="cx">             jsArguments = lowJSValue(m_node-&gt;child2());
</span><span class="cx">             break;
</span><span class="cx">         case CallForwardVarargs:
</span><ins>+        case TailCallForwardVarargs:
+        case TailCallForwardVarargsInlinedCaller:
</ins><span class="cx">         case ConstructForwardVarargs:
</span><span class="cx">             break;
</span><span class="cx">         default:
</span><span class="lines">@@ -4440,8 +4491,16 @@
</span><span class="cx">         setInstructionCallingConvention(call, LLVMCCallConv);
</span><span class="cx">         
</span><span class="cx">         m_ftlState.jsCallVarargses.append(JSCallVarargs(stackmapID, m_node));
</span><del>-        
-        setJSValue(call);
</del><ins>+
+        switch (m_node-&gt;op()) {
+        case TailCallVarargs:
+        case TailCallForwardVarargs:
+            m_out.unreachable();
+            break;
+
+        default:
+            setJSValue(call);
+        }
</ins><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     void compileLoadVarargs()
</span><span class="lines">@@ -8256,7 +8315,14 @@
</span><span class="cx">     }
</span><span class="cx">     void callPreflight()
</span><span class="cx">     {
</span><del>-        callPreflight(m_node-&gt;origin.semantic);
</del><ins>+        CodeOrigin codeOrigin = m_node-&gt;origin.semantic;
+
+        if (m_node-&gt;op() == TailCallInlinedCaller
+            || m_node-&gt;op() == TailCallVarargsInlinedCaller
+            || m_node-&gt;op() == TailCallForwardVarargsInlinedCaller)
+            codeOrigin =*codeOrigin.inlineCallFrame-&gt;getCallerSkippingDeadFrames();
+
+        callPreflight(codeOrigin);
</ins><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     void callCheck()
</span><span class="lines">@@ -8527,13 +8593,46 @@
</span><span class="cx">         DFG_CRASH(m_graph, m_node, toCString(&quot;Cannot find value for node: &quot;, node).data());
</span><span class="cx">         return ExitValue::dead();
</span><span class="cx">     }
</span><del>-    
</del><ins>+
</ins><span class="cx">     ExitValue exitArgument(ExitArgumentList&amp; arguments, DataFormat format, LValue value)
</span><span class="cx">     {
</span><span class="cx">         ExitValue result = ExitValue::exitArgument(ExitArgument(format, arguments.size()));
</span><span class="cx">         arguments.append(value);
</span><span class="cx">         return result;
</span><span class="cx">     }
</span><ins>+
+    ExitValue exitValueForTailCall(ExitArgumentList&amp; arguments, Node* node)
+    {
+        ASSERT(node-&gt;shouldGenerate());
+        ASSERT(node-&gt;hasResult());
+
+        switch (node-&gt;op()) {
+        case JSConstant:
+        case Int52Constant:
+        case DoubleConstant:
+            return ExitValue::constant(node-&gt;asJSValue());
+
+        default:
+            break;
+        }
+
+        LoweredNodeValue value = m_jsValueValues.get(node);
+        if (isValid(value))
+            return exitArgument(arguments, DataFormatJS, value.value());
+
+        value = m_int32Values.get(node);
+        if (isValid(value))
+            return exitArgument(arguments, DataFormatInt32, value.value());
+
+        value = m_booleanValues.get(node);
+        if (isValid(value)) {
+            LValue valueToPass = m_out.zeroExt(value.value(), m_out.int32);
+            return exitArgument(arguments, DataFormatBoolean, valueToPass);
+        }
+
+        // Doubles and Int52 have been converted by ValueRep()
+        DFG_CRASH(m_graph, m_node, toCString(&quot;Cannot find value for node: &quot;, node).data());
+    }
</ins><span class="cx">     
</span><span class="cx">     bool doesKill(Edge edge)
</span><span class="cx">     {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLStateh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLState.h (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLState.h        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/ftl/FTLState.h        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -37,6 +37,7 @@
</span><span class="cx"> #include &quot;FTLJITFinalizer.h&quot;
</span><span class="cx"> #include &quot;FTLJSCall.h&quot;
</span><span class="cx"> #include &quot;FTLJSCallVarargs.h&quot;
</span><ins>+#include &quot;FTLJSTailCall.h&quot;
</ins><span class="cx"> #include &quot;FTLStackMaps.h&quot;
</span><span class="cx"> #include &quot;FTLState.h&quot;
</span><span class="cx"> #include &lt;wtf/Noncopyable.h&gt;
</span><span class="lines">@@ -79,6 +80,7 @@
</span><span class="cx">     SegmentedVector&lt;CheckInDescriptor&gt; checkIns;
</span><span class="cx">     Vector&lt;JSCall&gt; jsCalls;
</span><span class="cx">     Vector&lt;JSCallVarargs&gt; jsCallVarargses;
</span><ins>+    Vector&lt;JSTailCall&gt; jsTailCalls;
</ins><span class="cx">     Vector&lt;CString&gt; codeSectionNames;
</span><span class="cx">     Vector&lt;CString&gt; dataSectionNames;
</span><span class="cx">     void* unwindDataSection;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorejitAssemblyHelperscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/jit/AssemblyHelpers.cpp (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/jit/AssemblyHelpers.cpp        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/jit/AssemblyHelpers.cpp        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -338,7 +338,7 @@
</span><span class="cx">     
</span><span class="cx">     if (width == NormalJumpWidth)
</span><span class="cx">         return result;
</span><del>-    
</del><ins>+
</ins><span class="cx">     PatchableJump realJump = patchableJump();
</span><span class="cx">     result.link(this);
</span><span class="cx">     
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorejitCallFrameShuffleDatah"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/jit/CallFrameShuffleData.h (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/jit/CallFrameShuffleData.h        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/jit/CallFrameShuffleData.h        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -39,6 +39,7 @@
</span><span class="cx">     Vector&lt;ValueRecovery&gt; args;
</span><span class="cx"> #if USE(JSVALUE64)
</span><span class="cx">     RegisterMap&lt;ValueRecovery&gt; registers;
</span><ins>+    GPRReg tagTypeNumber { InvalidGPRReg };
</ins><span class="cx"> 
</span><span class="cx">     void setupCalleeSaveRegisters(CodeBlock*);
</span><span class="cx"> #endif
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorejitCallFrameShufflercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/jit/CallFrameShuffler.cpp (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/jit/CallFrameShuffler.cpp        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/jit/CallFrameShuffler.cpp        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -71,6 +71,10 @@
</span><span class="cx">         else
</span><span class="cx">             addNew(reg.fpr(), data.registers[reg]);
</span><span class="cx">     }
</span><ins>+
+    m_tagTypeNumber = data.tagTypeNumber;
+    if (m_tagTypeNumber != InvalidGPRReg)
+        lockGPR(m_tagTypeNumber);
</ins><span class="cx"> #endif
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -80,12 +84,12 @@
</span><span class="cx">     static const char* dangerDelimiter       = &quot; X-------------------------------X &quot;;
</span><span class="cx">     static const char* dangerBoundsDelimiter = &quot; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX &quot;;
</span><span class="cx">     static const char* emptySpace            = &quot;                                   &quot;;
</span><del>-    ASSERT(m_alignedNewFrameSize &lt;= numLocals());
</del><span class="cx">     out.print(&quot;          &quot;);
</span><span class="cx">     out.print(&quot;           Old frame               &quot;);
</span><span class="cx">     out.print(&quot;           New frame               &quot;);
</span><span class="cx">     out.print(&quot;\n&quot;);
</span><del>-    for (int i = 0; i &lt; m_alignedOldFrameSize + numLocals() + 3; ++i) {
</del><ins>+    int totalSize = m_alignedOldFrameSize + std::max(numLocals(), m_alignedNewFrameSize) + 3;
+    for (int i = 0; i &lt; totalSize; ++i) {
</ins><span class="cx">         VirtualRegister old { m_alignedOldFrameSize - i - 1 };
</span><span class="cx">         VirtualRegister newReg { old + m_frameDelta };
</span><span class="cx"> 
</span><span class="lines">@@ -204,6 +208,10 @@
</span><span class="cx">         out.print(&quot;   Old frame offset is &quot;, m_oldFrameOffset, &quot;\n&quot;);
</span><span class="cx">     if (m_newFrameOffset)
</span><span class="cx">         out.print(&quot;   New frame offset is &quot;, m_newFrameOffset, &quot;\n&quot;);
</span><ins>+#if USE(JSVALUE64)
+    if (m_tagTypeNumber != InvalidGPRReg)
+        out.print(&quot;   TagTypeNumber is currently in &quot;, m_tagTypeNumber, &quot;\n&quot;);
+#endif
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> CachedRecovery* CallFrameShuffler::getCachedRecovery(ValueRecovery recovery)
</span><span class="lines">@@ -247,17 +255,26 @@
</span><span class="cx">     ASSERT(cachedRecovery.recovery().isInRegisters());
</span><span class="cx"> 
</span><span class="cx">     VirtualRegister spillSlot { 0 };
</span><del>-    for (VirtualRegister slot = firstOld(); slot &lt;= lastOld(); slot -= 1) {
-        ASSERT(slot &lt; newAsOld(firstNew()));
</del><ins>+    for (VirtualRegister slot = firstOld(); slot &lt;= lastOld(); slot += 1) {
+        if (slot &gt;= newAsOld(firstNew()))
+            break;
+
</ins><span class="cx">         if (getOld(slot))
</span><span class="cx">             continue;
</span><span class="cx"> 
</span><span class="cx">         spillSlot = slot;
</span><span class="cx">         break;
</span><span class="cx">     }
</span><del>-    // We must have enough slots to be able to fit the whole
-    // callee's frame for the slow path.
-    RELEASE_ASSERT(spillSlot.isLocal());
</del><ins>+    // We must have enough slots to be able to fit the whole callee's
+    // frame for the slow path - unless we are in the FTL. In that
+    // case, we are allowed to extend the frame *once*, since we are
+    // guaranteed to have enough available space for that.
+    if (spillSlot &gt;= newAsOld(firstNew()) || !spillSlot.isLocal()) {
+        RELEASE_ASSERT(!m_didExtendFrame);
+        extendFrameIfNeeded();
+        spill(cachedRecovery);
+        return;
+    }
</ins><span class="cx"> 
</span><span class="cx">     if (verbose)
</span><span class="cx">         dataLog(&quot;   * Spilling &quot;, cachedRecovery.recovery(), &quot; into &quot;, spillSlot, &quot;\n&quot;);
</span><span class="lines">@@ -286,6 +303,38 @@
</span><span class="cx">         dataLog(&quot;  Skipping the fp-sp delta check since there is too much pressure&quot;);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void CallFrameShuffler::extendFrameIfNeeded()
+{
+    ASSERT(!m_didExtendFrame);
+    ASSERT(!isUndecided());
+
+    VirtualRegister firstRead { firstOld() };
+    for (; firstRead &lt;= virtualRegisterForLocal(0); firstRead += 1) {
+        if (getOld(firstRead))
+            break;
+    }
+    size_t availableSize = static_cast&lt;size_t&gt;(firstRead.offset() - firstOld().offset());
+    size_t wantedSize = m_newFrame.size() + m_newFrameOffset;
+
+    if (availableSize &lt; wantedSize) {
+        size_t delta = WTF::roundUpToMultipleOf(stackAlignmentRegisters(), wantedSize - availableSize);
+        m_oldFrame.grow(m_oldFrame.size() + delta);
+        for (size_t i = 0; i &lt; delta; ++i)
+            m_oldFrame[m_oldFrame.size() - i - 1] = nullptr;
+        m_jit.subPtr(MacroAssembler::TrustedImm32(delta * sizeof(Register)), MacroAssembler::stackPointerRegister);
+
+        if (isSlowPath())
+            m_frameDelta = numLocals() + JSStack::CallerFrameAndPCSize;
+        else
+            m_oldFrameOffset = numLocals();
+
+        if (verbose)
+            dataLogF(&quot;  Not enough space - extending the old frame %zu slot\n&quot;, delta);
+    }
+
+    m_didExtendFrame = true;
+}
+
</ins><span class="cx"> void CallFrameShuffler::prepareForSlowPath()
</span><span class="cx"> {
</span><span class="cx">     ASSERT(isUndecided());
</span><span class="lines">@@ -296,8 +345,16 @@
</span><span class="cx">     m_newFrameOffset = -JSStack::CallerFrameAndPCSize;
</span><span class="cx"> 
</span><span class="cx">     if (verbose)
</span><del>-        dataLog(&quot;\n\nPreparing frame for slow path call:\n&quot;, *this);
</del><ins>+        dataLog(&quot;\n\nPreparing frame for slow path call:\n&quot;);
</ins><span class="cx"> 
</span><ins>+    // When coming from the FTL, we need to extend the frame. In other
+    // cases, we may end up extending the frame if we previously
+    // spilled things (e.g. in polymorphic cache).
+    extendFrameIfNeeded();
+
+    if (verbose)
+        dataLog(*this);
+
</ins><span class="cx">     prepareAny();
</span><span class="cx"> 
</span><span class="cx">     if (verbose)
</span><span class="lines">@@ -646,6 +703,11 @@
</span><span class="cx">         ASSERT_UNUSED(writesOK, writesOK);
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+#if USE(JSVALUE64)
+    if (m_tagTypeNumber != InvalidGPRReg &amp;&amp; m_newRegisters[m_tagTypeNumber])
+        releaseGPR(m_tagTypeNumber);
+#endif
+
</ins><span class="cx">     // Handle 2) by loading all registers. We don't have to do any
</span><span class="cx">     // writes, since they have been taken care of above.
</span><span class="cx">     if (verbose)
</span><span class="lines">@@ -660,6 +722,11 @@
</span><span class="cx">         ASSERT(cachedRecovery-&gt;targets().isEmpty());
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+#if USE(JSVALUE64)
+    if (m_tagTypeNumber != InvalidGPRReg)
+        releaseGPR(m_tagTypeNumber);
+#endif
+
</ins><span class="cx">     // At this point, we have read everything we cared about from the
</span><span class="cx">     // stack, and written everything we had to to the stack.
</span><span class="cx">     if (verbose)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorejitCallFrameShufflerh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/jit/CallFrameShuffler.h (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/jit/CallFrameShuffler.h        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/jit/CallFrameShuffler.h        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -73,6 +73,21 @@
</span><span class="cx">         m_lockedRegisters.clear(gpr);
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    void restoreGPR(GPRReg gpr)
+    {
+        if (!m_newRegisters[gpr])
+            return;
+
+        ensureGPR();
+#if USE(JSVALUE32_64)
+        GPRReg tempGPR { getFreeGPR() };
+        lockGPR(tempGPR);
+        ensureGPR();
+        releaseGPR(tempGPR);
+#endif
+        emitDisplace(*m_newRegisters[gpr]);
+    }
+
</ins><span class="cx">     // You can only take a snapshot if the recovery has not started
</span><span class="cx">     // yet. The only operations that are valid before taking a
</span><span class="cx">     // snapshot are lockGPR(), acquireGPR() and releaseGPR().
</span><span class="lines">@@ -309,6 +324,10 @@
</span><span class="cx">         return reg &gt;= firstOld() &amp;&amp; reg &lt;= lastOld();
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    bool m_didExtendFrame { false };
+
+    void extendFrameIfNeeded();
+
</ins><span class="cx">     // This stores, for each slot in the new frame, information about
</span><span class="cx">     // the recovery for the value that should eventually go into that
</span><span class="cx">     // slot.
</span><span class="lines">@@ -385,13 +404,19 @@
</span><span class="cx">     // We also use this to lock registers temporarily, for instance to
</span><span class="cx">     // ensure that we have at least 2 available registers for loading
</span><span class="cx">     // a pair on 32bits.
</span><del>-    RegisterSet m_lockedRegisters;
</del><ins>+    mutable RegisterSet m_lockedRegisters;
</ins><span class="cx"> 
</span><span class="cx">     // This stores the current recoveries present in registers. A null
</span><span class="cx">     // CachedRecovery means we can trash the current value as we don't
</span><span class="cx">     // care about it. 
</span><span class="cx">     RegisterMap&lt;CachedRecovery*&gt; m_registers;
</span><span class="cx"> 
</span><ins>+#if USE(JSVALUE64)
+    mutable GPRReg m_tagTypeNumber;
+
+    bool tryAcquireTagTypeNumber();
+#endif
+
</ins><span class="cx">     // This stores, for each register, information about the recovery
</span><span class="cx">     // for the value that should eventually go into that register. The
</span><span class="cx">     // only registers that have a target recovery will be callee-save
</span><span class="lines">@@ -421,9 +446,26 @@
</span><span class="cx">                     nonTemp = reg;
</span><span class="cx">             }
</span><span class="cx">         }
</span><ins>+
+#if USE(JSVALUE64)
+        if (!nonTemp &amp;&amp; m_tagTypeNumber != InvalidGPRReg &amp;&amp; check(Reg { m_tagTypeNumber })) {
+            ASSERT(m_lockedRegisters.get(m_tagTypeNumber));
+            m_lockedRegisters.clear(m_tagTypeNumber);
+            nonTemp = Reg { m_tagTypeNumber };
+            m_tagTypeNumber = InvalidGPRReg;
+        }
+#endif
</ins><span class="cx">         return nonTemp;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    GPRReg getFreeTempGPR() const
+    {
+        Reg freeTempGPR { getFreeRegister([this] (Reg reg) { return reg.isGPR() &amp;&amp; !m_newRegisters[reg]; }) };
+        if (!freeTempGPR)
+            return InvalidGPRReg;
+        return freeTempGPR.gpr();
+    }
+
</ins><span class="cx">     GPRReg getFreeGPR() const
</span><span class="cx">     {
</span><span class="cx">         Reg freeGPR { getFreeRegister([] (Reg reg) { return reg.isGPR(); }) };
</span><span class="lines">@@ -519,6 +561,31 @@
</span><span class="cx">             });
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    void ensureTempGPR()
+    {
+        if (getFreeTempGPR() != InvalidGPRReg)
+            return;
+
+        if (verbose)
+            dataLog(&quot;  Finding a temp GPR to spill\n&quot;);
+        ensureRegister(
+            [this] (const CachedRecovery&amp; cachedRecovery) {
+                if (cachedRecovery.recovery().isInGPR()) {
+                    return !m_lockedRegisters.get(cachedRecovery.recovery().gpr()) 
+                        &amp;&amp; !m_newRegisters[cachedRecovery.recovery().gpr()];
+                }
+#if USE(JSVALUE32_64)
+                if (cachedRecovery.recovery().technique() == InPair) {
+                    return !m_lockedRegisters.get(cachedRecovery.recovery().tagGPR())
+                        &amp;&amp; !m_lockedRegisters.get(cachedRecovery.recovery().payloadGPR())
+                        &amp;&amp; !m_newRegisters[cachedRecovery.recovery().tagGPR()]
+                        &amp;&amp; !m_newRegisters[cachedRecovery.recovery().payloadGPR()];
+                }
+#endif
+                return false;
+            });
+    }
+
</ins><span class="cx">     void ensureGPR()
</span><span class="cx">     {
</span><span class="cx">         if (getFreeGPR() != InvalidGPRReg)
</span><span class="lines">@@ -573,16 +640,24 @@
</span><span class="cx">     {
</span><span class="cx">         ASSERT(jsValueRegs &amp;&amp; !getNew(jsValueRegs));
</span><span class="cx">         CachedRecovery* cachedRecovery = addCachedRecovery(recovery);
</span><del>-        ASSERT(!cachedRecovery-&gt;wantedJSValueRegs());
-        cachedRecovery-&gt;setWantedJSValueRegs(jsValueRegs);
</del><span class="cx"> #if USE(JSVALUE64)
</span><ins>+        if (cachedRecovery-&gt;wantedJSValueRegs())
+            m_newRegisters[cachedRecovery-&gt;wantedJSValueRegs().gpr()] = nullptr;
</ins><span class="cx">         m_newRegisters[jsValueRegs.gpr()] = cachedRecovery;
</span><span class="cx"> #else
</span><ins>+        if (JSValueRegs oldRegs { cachedRecovery-&gt;wantedJSValueRegs() }) {
+            if (oldRegs.payloadGPR())
+                m_newRegisters[oldRegs.payloadGPR()] = nullptr;
+            if (oldRegs.tagGPR())
+                m_newRegisters[oldRegs.tagGPR()] = nullptr;
+        }
</ins><span class="cx">         if (jsValueRegs.payloadGPR() != InvalidGPRReg)
</span><span class="cx">             m_newRegisters[jsValueRegs.payloadGPR()] = cachedRecovery;
</span><span class="cx">         if (jsValueRegs.tagGPR() != InvalidGPRReg)
</span><span class="cx">             m_newRegisters[jsValueRegs.tagGPR()] = cachedRecovery;
</span><span class="cx"> #endif
</span><ins>+        ASSERT(!cachedRecovery-&gt;wantedJSValueRegs());
+        cachedRecovery-&gt;setWantedJSValueRegs(jsValueRegs);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     void addNew(FPRReg fpr, ValueRecovery recovery)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorejitCallFrameShuffler64cpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/jit/CallFrameShuffler64.cpp (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/jit/CallFrameShuffler64.cpp        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/jit/CallFrameShuffler64.cpp        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -87,9 +87,15 @@
</span><span class="cx">             m_jit.zeroExtend32ToPtr(
</span><span class="cx">                 cachedRecovery.recovery().gpr(),
</span><span class="cx">                 cachedRecovery.recovery().gpr());
</span><del>-            // We have to do this the hard way.
-            m_jit.or64(MacroAssembler::TrustedImm64(TagTypeNumber),
-                cachedRecovery.recovery().gpr());
</del><ins>+            m_lockedRegisters.set(cachedRecovery.recovery().gpr());
+            if (tryAcquireTagTypeNumber())
+                m_jit.or64(m_tagTypeNumber, cachedRecovery.recovery().gpr());
+            else {
+                // We have to do this the hard way
+                m_jit.or64(MacroAssembler::TrustedImm64(TagTypeNumber),
+                    cachedRecovery.recovery().gpr());
+            }
+            m_lockedRegisters.clear(cachedRecovery.recovery().gpr());
</ins><span class="cx">             cachedRecovery.setRecovery(
</span><span class="cx">                 ValueRecovery::inGPR(cachedRecovery.recovery().gpr(), DataFormatJS));
</span><span class="cx">             if (verbose)
</span><span class="lines">@@ -141,7 +147,12 @@
</span><span class="cx">             ASSERT(resultGPR != InvalidGPRReg);
</span><span class="cx">             m_jit.purifyNaN(cachedRecovery.recovery().fpr());
</span><span class="cx">             m_jit.moveDoubleTo64(cachedRecovery.recovery().fpr(), resultGPR);
</span><del>-            m_jit.sub64(MacroAssembler::TrustedImm64(TagTypeNumber), resultGPR);
</del><ins>+            m_lockedRegisters.set(resultGPR);
+            if (tryAcquireTagTypeNumber())
+                m_jit.sub64(m_tagTypeNumber, resultGPR);
+            else
+                m_jit.sub64(MacroAssembler::TrustedImm64(TagTypeNumber), resultGPR);
+            m_lockedRegisters.clear(resultGPR);
</ins><span class="cx">             updateRecovery(cachedRecovery, ValueRecovery::inGPR(resultGPR, DataFormatJS));
</span><span class="cx">             if (verbose)
</span><span class="cx">                 dataLog(&quot; into &quot;, cachedRecovery.recovery(), &quot;\n&quot;);
</span><span class="lines">@@ -337,7 +348,22 @@
</span><span class="cx"> 
</span><span class="cx">     ASSERT(m_registers[wantedReg] == &amp;cachedRecovery);
</span><span class="cx"> }
</span><ins>+    
+bool CallFrameShuffler::tryAcquireTagTypeNumber()
+{
+    if (m_tagTypeNumber != InvalidGPRReg)
+        return true;
</ins><span class="cx"> 
</span><ins>+    m_tagTypeNumber = getFreeGPR();
+
+    if (m_tagTypeNumber == InvalidGPRReg)
+        return false;
+
+    m_lockedRegisters.set(m_tagTypeNumber);
+    m_jit.move(MacroAssembler::TrustedImm64(TagTypeNumber), m_tagTypeNumber);
+    return true;
+}
+
</ins><span class="cx"> } // namespace JSC
</span><span class="cx"> 
</span><span class="cx"> #endif // ENABLE(JIT) &amp;&amp; USE(JSVALUE64)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorejitJITCallcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/jit/JITCall.cpp (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/jit/JITCall.cpp        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/jit/JITCall.cpp        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -193,6 +193,7 @@
</span><span class="cx"> 
</span><span class="cx">     if (opcodeID == op_tail_call) {
</span><span class="cx">         CallFrameShuffleData shuffleData;
</span><ins>+        shuffleData.tagTypeNumber = GPRInfo::tagTypeNumberRegister;
</ins><span class="cx">         shuffleData.numLocals =
</span><span class="cx">             instruction[4].u.operand - sizeof(CallerFrameAndPC) / sizeof(Register);
</span><span class="cx">         shuffleData.args.resize(instruction[3].u.operand);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorejitRegh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/jit/Reg.h (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/jit/Reg.h        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/jit/Reg.h        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -55,6 +55,11 @@
</span><span class="cx">         : m_index(invalid())
</span><span class="cx">     {
</span><span class="cx">     }
</span><ins>+
+    Reg(WTF::HashTableDeletedValueType)
+        : m_index(deleted())
+    {
+    }
</ins><span class="cx">     
</span><span class="cx">     Reg(MacroAssembler::RegisterID reg)
</span><span class="cx">         : m_index(MacroAssembler::registerIndex(reg))
</span><span class="lines">@@ -102,6 +107,8 @@
</span><span class="cx">     bool isSet() const { return m_index != invalid(); }
</span><span class="cx">     bool operator!() const { return !isSet(); }
</span><span class="cx">     explicit operator bool() const { return isSet(); }
</span><ins>+
+    bool isHashTableDeletedValue() const { return m_index == deleted(); }
</ins><span class="cx">     
</span><span class="cx">     bool isGPR() const
</span><span class="cx">     {
</span><span class="lines">@@ -165,12 +172,34 @@
</span><span class="cx"> 
</span><span class="cx"> private:
</span><span class="cx">     static uint8_t invalid() { return 0xff; }
</span><ins>+
+    static uint8_t deleted() { return 0xfe; }
</ins><span class="cx">     
</span><span class="cx">     uint8_t m_index;
</span><span class="cx"> };
</span><span class="cx"> 
</span><ins>+struct RegHash {
+    static unsigned hash(const Reg&amp; key) { return key.hash(); }
+    static bool equal(const Reg&amp; a, const Reg&amp; b) { return a == b; }
+    static const bool safeToCompareToEmptyOrDeleted = true;
+};
+
</ins><span class="cx"> } // namespace JSC
</span><span class="cx"> 
</span><ins>+namespace WTF {
+
+template&lt;typename T&gt; struct DefaultHash;
+template&lt;&gt; struct DefaultHash&lt;JSC::Reg&gt; {
+    typedef JSC::RegHash Hash;
+};
+
+template&lt;typename T&gt; struct HashTraits;
+template&lt;&gt; struct HashTraits&lt;JSC::Reg&gt; : SimpleClassHashTraits&lt;JSC::Reg&gt; {
+    static const bool emptyValueIsZero = false;
+ };
+
+} // namespace WTF
+
</ins><span class="cx"> #endif // ENABLE(JIT)
</span><span class="cx"> 
</span><span class="cx"> #endif // Reg_h
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeOptionsh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/Options.h (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/Options.h        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/runtime/Options.h        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -128,7 +128,7 @@
</span><span class="cx">     v(bool, forceProfilerBytecodeGeneration, false, nullptr) \
</span><span class="cx">     \
</span><span class="cx">     v(bool, enableFunctionDotArguments, true, nullptr) \
</span><del>-    v(bool, enableTailCalls, false, nullptr) \
</del><ins>+    v(bool, enableTailCalls, true, nullptr) \
</ins><span class="cx">     \
</span><span class="cx">     /* showDisassembly implies showDFGDisassembly. */ \
</span><span class="cx">     v(bool, showDisassembly, false, &quot;dumps disassembly of all JIT compiled code upon compilation&quot;) \
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestses6yaml"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/tests/es6.yaml (190288 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/es6.yaml        2015-09-28 22:11:08 UTC (rev 190288)
+++ trunk/Source/JavaScriptCore/tests/es6.yaml        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -877,9 +877,9 @@
</span><span class="cx"> - path: es6/Promise_Promise[Symbol.species].js
</span><span class="cx">   cmd: runES6 :fail
</span><span class="cx"> - path: es6/proper_tail_calls_tail_call_optimisation_direct_recursion.js
</span><del>-  cmd: runES6 :fail
</del><ins>+  cmd: runES6 :normal
</ins><span class="cx"> - path: es6/proper_tail_calls_tail_call_optimisation_mutual_recursion.js
</span><del>-  cmd: runES6 :fail
</del><ins>+  cmd: runES6 :normal
</ins><span class="cx"> - path: es6/prototype_of_bound_functions_arrow_functions.js
</span><span class="cx">   cmd: runES6 :fail
</span><span class="cx"> - path: es6/prototype_of_bound_functions_basic_functions.js
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstressdfgtailcallsjs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/stress/dfg-tail-calls.js (0 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/dfg-tail-calls.js                                (rev 0)
+++ trunk/Source/JavaScriptCore/tests/stress/dfg-tail-calls.js        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -0,0 +1,56 @@
</span><ins>+(function nonInlinedTailCall() {
+    function callee() { if (callee.caller != nonInlinedTailCall) throw new Error(); }
+    noInline(callee);
+
+    function caller() { &quot;use strict&quot;; return callee(); }
+
+    for (var i = 0; i &lt; 10000; ++i)
+        caller();
+
+    function loop(n) { &quot;use strict&quot;; if (n &gt; 0) return loop(n - 1); }
+    noInline(loop);
+
+    loop(1000000);
+})();
+
+(function inlinedTailCall() {
+    function callee() { if (callee.caller != inlinedTailCall) throw new Error(); }
+    function caller() { &quot;use strict&quot;; return callee(); }
+
+    for (var i = 0; i &lt; 10000; ++i)
+        caller();
+
+    function loop(n) { &quot;use strict&quot;; if (n &gt; 0) return loop(n - 1); }
+
+    loop(1000000);
+})();
+
+(function nonInlinedEmulatedTailCall() {
+    function emulator() { caller(); }
+    function callee() { if (callee.caller != emulator) throw new Error(); }
+    noInline(callee);
+    function caller() { &quot;use strict&quot;; return callee(); }
+
+    for (var i = 0; i &lt; 10000; ++i)
+        emulator();
+
+    function pad(n) { &quot;use strict&quot;; return loop(n); }
+    function loop(n) { &quot;use strict&quot;; if (n &gt; 0) return pad(n - 1); }
+    noInline(loop);
+
+    loop(1000000);
+})();
+
+(function inlinedEmulatedTailCall() {
+    function emulator() { caller(); }
+    function callee() { if (callee.caller != emulator) throw new Error(); }
+    function caller() { &quot;use strict&quot;; return callee(); }
+
+    for (var i = 0; i &lt; 10000; ++i)
+        emulator();
+
+    function pad(n) { &quot;use strict&quot;; return loop(n); }
+    function loop(n) { &quot;use strict&quot;; if (n &gt; 0) return pad(n - 1); }
+
+    loop(1000000);
+})();
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstressmutualtailcallnostackoverflowjs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/stress/mutual-tail-call-no-stack-overflow.js (0 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/mutual-tail-call-no-stack-overflow.js                                (rev 0)
+++ trunk/Source/JavaScriptCore/tests/stress/mutual-tail-call-no-stack-overflow.js        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -0,0 +1,71 @@
</span><ins>+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+function sloppyCountdown(n) {
+    function even(n) {
+        if (n == 0)
+            return n;
+        return odd(n - 1);
+    }
+
+    function odd(n) {
+        if (n == 1)
+            return n;
+        return even(n - 1);
+    }
+
+    if (n % 2 === 0)
+        return even(n);
+    else
+        return odd(n);
+}
+
+function strictCountdown(n) {
+    &quot;use strict&quot;;
+
+    function even(n) {
+        if (n == 0)
+            return n;
+        return odd(n - 1);
+    }
+
+    function odd(n) {
+        if (n == 1)
+            return n;
+        return even(n - 1);
+    }
+
+    if (n % 2 === 0)
+        return even(n);
+    else
+        return odd(n);
+}
+
+shouldThrow(function () { sloppyCountdown(100000); }, &quot;RangeError: Maximum call stack size exceeded.&quot;);
+strictCountdown(100000);
+
+// Parity alterning
+function odd(n) {
+    &quot;use strict&quot;;
+    if (n &gt; 0)
+        return even(n, 0);
+}
+
+function even(n) {
+    &quot;use strict&quot;;
+    return odd(n - 1);
+}
+
+odd(100000);
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstresstailcallnostackoverflowjs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/stress/tail-call-no-stack-overflow.js (0 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/tail-call-no-stack-overflow.js                                (rev 0)
+++ trunk/Source/JavaScriptCore/tests/stress/tail-call-no-stack-overflow.js        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -0,0 +1,45 @@
</span><ins>+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+function sloppyLoop(n) {
+    if (n &gt; 0)
+        return sloppyLoop(n - 1);
+}
+
+function strictLoop(n) {
+    &quot;use strict&quot;;
+    if (n &gt; 0)
+        return strictLoop(n - 1);
+}
+
+// We have two of these so that we can test different stack alignments
+function strictLoopArityFixup1(n, dummy) {
+    &quot;use strict&quot;;
+    if (n &gt; 0)
+        return strictLoopArityFixup1(n - 1);
+}
+
+function strictLoopArityFixup2(n, dummy1, dummy2) {
+    &quot;use strict&quot;;
+    if (n &gt; 0)
+        return strictLoopArityFixup2(n - 1);
+}
+
+shouldThrow(function () { sloppyLoop(100000); }, 'RangeError: Maximum call stack size exceeded.');
+
+// These should not throw
+strictLoop(100000);
+strictLoopArityFixup1(1000000);
+strictLoopArityFixup2(1000000);
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstresstailcallrecognizejs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/stress/tail-call-recognize.js (0 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/tail-call-recognize.js                                (rev 0)
+++ trunk/Source/JavaScriptCore/tests/stress/tail-call-recognize.js        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -0,0 +1,178 @@
</span><ins>+function callerMustBeRun() {
+    if (!Object.is(callerMustBeRun.caller, runTests))
+        throw Error(&quot;Wrong caller, expected run but got &quot;, callerMustBeRun.caller);
+}
+
+function callerMustBeStrict() {
+    var errorThrown = false;
+    try {
+        callerMustBeStrict.caller;
+    } catch (e) {
+        errorThrown = true;
+    }
+    if (!errorThrown)
+        throw Error(&quot;Wrong caller, expected strict caller but got &quot;, callerMustBeStrict.caller);
+}
+
+function runTests() {
+    // Statement tests
+    (function simpleTailCall() {
+        &quot;use strict&quot;;
+        return callerMustBeRun();
+    })();
+
+    (function noTailCallInTry() {
+        &quot;use strict&quot;;
+        try {
+            return callerMustBeStrict();
+        } catch (e) {
+            throw e;
+        }
+    })();
+
+    (function tailCallInCatch() {
+        &quot;use strict&quot;;
+        try { } catch (e) { return callerMustBeRun(); }
+    })();
+
+    (function tailCallInFinally() {
+        &quot;use strict&quot;;
+        try { } finally { return callerMustBeRun(); }
+    })();
+
+    (function tailCallInFinallyWithCatch() {
+        &quot;use strict&quot;;
+        try { } catch (e) { } finally { return callerMustBeRun(); }
+    })();
+
+    (function tailCallInFinallyWithCatchTaken() {
+        &quot;use strict&quot;;
+        try { throw null; } catch (e) { } finally { return callerMustBeRun(); }
+    })();
+
+    (function noTailCallInCatchIfFinally() {
+        &quot;use strict&quot;;
+        try { throw null; } catch (e) { return callerMustBeStrict(); } finally { }
+    })();
+
+    (function tailCallInFor() {
+        &quot;use strict&quot;;
+        for (var i = 0; i &lt; 10; ++i)
+            return callerMustBeRun();
+    })();
+
+    (function tailCallInWhile() {
+        &quot;use strict&quot;;
+        while (true)
+            return callerMustBeRun();
+    })();
+
+    (function tailCallInDoWhile() {
+        &quot;use strict&quot;;
+        do
+            return callerMustBeRun();
+        while (true);
+    })();
+
+    (function noTailCallInForIn() {
+        &quot;use strict&quot;;
+        for (var x in [1, 2])
+            return callerMustBeStrict();
+    })();
+
+    (function noTailCallInForOf() {
+        &quot;use strict&quot;;
+        for (var x of [1, 2])
+            return callerMustBeStrict();
+    })();
+
+    (function tailCallInIf() {
+        &quot;use strict&quot;;
+        if (true)
+            return callerMustBeRun();
+    })();
+
+    (function tailCallInElse() {
+        &quot;use strict&quot;;
+        if (false) throw new Error(&quot;WTF&quot;);
+        else return callerMustBeRun();
+    })();
+
+    (function tailCallInSwitchCase() {
+        &quot;use strict&quot;;
+        switch (0) {
+        case 0: return callerMustBeRun();
+        }
+    })();
+
+    (function tailCallInSwitchDefault() {
+        &quot;use strict&quot;;
+        switch (0) {
+        default: return callerMustBeRun();
+        }
+    })();
+
+    (function tailCallWithLabel() {
+        &quot;use strict&quot;;
+        dummy: return callerMustBeRun();
+    })();
+
+    // Expression tests, we don't enumerate all the cases where there
+    // *shouldn't* be a tail call
+
+    (function tailCallComma() {
+        &quot;use strict&quot;;
+        return callerMustBeStrict(), callerMustBeRun();
+    })();
+
+    (function tailCallTernaryLeft() {
+        &quot;use strict&quot;;
+        return true ? callerMustBeRun() : unreachable();
+    })();
+
+    (function tailCallTernaryRight() {
+        &quot;use strict&quot;;
+        return false ? unreachable() : callerMustBeRun();
+    })();
+
+    (function tailCallLogicalAnd() {
+        &quot;use strict&quot;;
+        return true &amp;&amp; callerMustBeRun();
+    })();
+
+    (function tailCallLogicalOr() {
+        &quot;use strict&quot;;
+        return false || callerMustBeRun();
+    })();
+
+    (function memberTailCall() {
+        &quot;use strict&quot;;
+        return { f: callerMustBeRun }.f();
+    })();
+
+    (function bindTailCall() {
+        &quot;use strict&quot;;
+        return callerMustBeRun.bind()();
+    })();
+
+    // Function.prototype tests
+
+    (function applyTailCall() {
+        &quot;use strict&quot;;
+        return callerMustBeRun.apply();
+    })();
+
+    (function callTailCall() {
+        &quot;use strict&quot;;
+        return callerMustBeRun.call();
+    })();
+
+    // No tail call for constructors
+    (function noTailConstruct() {
+        &quot;use strict&quot;;
+        return new callerMustBeStrict();
+    })();
+}
+
+for (var i = 0; i &lt; 10000; ++i)
+    runTests();
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstresstailcallvarargsnostackoverflowjs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/stress/tail-call-varargs-no-stack-overflow.js (0 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/tail-call-varargs-no-stack-overflow.js                                (rev 0)
+++ trunk/Source/JavaScriptCore/tests/stress/tail-call-varargs-no-stack-overflow.js        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -0,0 +1,28 @@
</span><ins>+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+function sloppyLoop(n) {
+    if (n &gt; 0)
+        return sloppyLoop(...[n - 1]);
+}
+
+function strictLoop(n) {
+    &quot;use strict&quot;;
+    if (n &gt; 0)
+        return strictLoop(...[n - 1]);
+}
+
+shouldThrow(function () { sloppyLoop(100000); }, 'RangeError: Maximum call stack size exceeded.');
+strictLoop(100000);
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstresstailcallsdontoverwritelivestackjs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/stress/tail-calls-dont-overwrite-live-stack.js (0 => 190289)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/tail-calls-dont-overwrite-live-stack.js                                (rev 0)
+++ trunk/Source/JavaScriptCore/tests/stress/tail-calls-dont-overwrite-live-stack.js        2015-09-28 22:37:36 UTC (rev 190289)
</span><span class="lines">@@ -0,0 +1,30 @@
</span><ins>+&quot;use strict&quot;;
+
+function tail(a, b) { }
+noInline(tail);
+
+var obj = {
+    method: function (x) {
+        return tail(x, x);
+    },
+
+    get fromNative() { return tail(0, 0); }
+};
+noInline(obj.method);
+
+function getThis(x) { return this; }
+noInline(getThis);
+
+for (var i = 0; i &lt; 10000; ++i) {
+    var that = getThis(obj.method(42));
+
+    if (!Object.is(that, undefined))
+        throw new Error(&quot;Wrong 'this' value in call, expected undefined but got &quot; + that);
+
+    that = getThis(obj.method(...[42]));
+    if (!Object.is(that, undefined))
+        throw new Error(&quot;Wrong 'this' value in varargs call, expected undefined but got &quot; + that);
+
+    if (!Object.is(obj.fromNative, undefined))
+        throw new Error(&quot;Wrong 'fromNative' value, expected undefined but got &quot; + obj.fromNative);
+}
</ins></span></pre>
</div>
</div>

</body>
</html>