<!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>[209121] 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/209121">209121</a></dd>
<dt>Author</dt> <dd>sbarati@apple.com</dd>
<dt>Date</dt> <dd>2016-11-29 22:24:44 -0800 (Tue, 29 Nov 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>We should be able optimize the pattern where we spread a function's rest parameter to another call
https://bugs.webkit.org/show_bug.cgi?id=163865

Reviewed by Filip Pizlo.

JSTests:

* microbenchmarks/default-derived-constructor.js: Added.
(createClassHierarchy.let.currentClass):
(createClassHierarchy):
* stress/call-varargs-spread.js: Added.
(assert):
(bar):
(foo):
* stress/load-varargs-on-new-array-with-spread-convert-to-static-loads.js: Added.
(assert):
(baz):
(bar):
(foo):
* stress/new-array-with-spread-with-normal-spread-and-phantom-spread.js: Added.
(assert):
(foo):
(escape):
(bar):
* stress/phantom-new-array-with-spread-osr-exit.js: Added.
(assert):
(baz):
(bar):
(effects):
(foo):
* stress/phantom-spread-forward-varargs.js: Added.
(assert):
(test1.bar):
(test1.foo):
(test1):
(test2.bar):
(test2.foo):
(test3.baz):
(test3.bar):
(test3.foo):
(test4.baz):
(test4.bar):
(test4.foo):
(test5.baz):
(test5.bar):
(test5.foo):
* stress/phantom-spread-osr-exit.js: Added.
(assert):
(baz):
(bar):
(effects):
(foo):
* stress/spread-call-convert-to-static-call.js: Added.
(assert):
(baz):
(bar):
(foo):
* stress/spread-forward-call-varargs-stack-overflow.js: Added.
(assert):
(identity):
(bar):
(foo):
* stress/spread-forward-varargs-rest-parameter-change-iterator-protocol-2.js: Added.
(assert):
(baz.Array.prototype.Symbol.iterator):
(baz):
(bar):
(foo):
(test):
* stress/spread-forward-varargs-rest-parameter-change-iterator-protocol.js: Added.
(assert):
(baz.Array.prototype.Symbol.iterator):
(baz):
(bar):
(foo):
* stress/spread-forward-varargs-stack-overflow.js: Added.
(assert):
(bar):
(foo):

Source/JavaScriptCore:

This patch optimizes the following patterns to prevent both the allocation
of the rest parameter, and the execution of the iterator protocol:

```
function foo(...args) {
    let arr = [...args];
}

and

function foo(...args) {
    bar(...args);
}
```

To do this, I've extended the arguments elimination phase to reason
about Spread and NewArrayWithSpread. I've added two new nodes, PhantomSpread
and PhantomNewArrayWithSpread. PhantomSpread is only allowed over rest
parameters that don't escape. If the rest parameter *does* escape, we can't
convert the spread into a phantom because it would not be sound w.r.t JS
semantics because we would be reading from the call frame even though
the rest array may have changed.

Note that NewArrayWithSpread also understands what to do when one of its
arguments is PhantomSpread(@PhantomCreateRest) even if it itself is escaped.

PhantomNewArrayWithSpread is only allowed over a series of
PhantomSpread(@PhantomCreateRest) nodes. Like with PhantomSpread, PhantomNewArrayWithSpread
is only allowed if none of its arguments that are being spread are escaped
and if it itself is not escaped.

Because there is a dependency between a node being a candidate and
the escaped state of the node's children, I've extended the notion
of escaping a node inside the arguments elimination phase. Now, when
any node is escaped, we must consider all other candidates that are may
now no longer be valid.

For example:

```
function foo(...args) {
    escape(args);
    bar(...args);
}
```

In the above program, we don't know if the function call to escape()
modifies args, therefore, the spread can not become phantom because
the execution of the spread may not be as simple as reading the
arguments from the call frame.

Unfortunately, the arguments elimination phase does not consider control
flow when doing its escape analysis. It would be good to integrate this
phase with the object allocation sinking phase. To see why, consider
an example where we don't eliminate the spread and allocation of the rest
parameter even though we could:

```
function foo(rareCondition, ...args) {
    bar(...args);
    if (rareCondition)
        baz(args);
}
```

There are only a few users of the PhantomSpread and PhantomNewArrayWithSpread
nodes. PhantomSpread is only used by PhantomNewArrayWithSpread and NewArrayWithSpread.
PhantomNewArrayWithSpread is only used by ForwardVarargs and the various
*Call*ForwardVarargs nodes. The users of these phantoms know how to produce
what the phantom node would have produced. For example, NewArrayWithSpread
knows how to produce the values that would have been produced by PhantomSpread(@PhantomCreateRest)
by directly reading from the call frame.

This patch is a 6% speedup on my MBP on ES6SampleBench.

* b3/B3LowerToAir.cpp:
(JSC::B3::Air::LowerToAir::tryAppendLea):
* b3/B3ValueRep.h:
* builtins/BuiltinExecutables.cpp:
(JSC::BuiltinExecutables::createDefaultConstructor):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter&lt;AbstractStateType&gt;::executeEffects):
* dfg/DFGArgumentsEliminationPhase.cpp:
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGForAllKills.h:
(JSC::DFG::forAllKillsInBlock):
* dfg/DFGNode.h:
(JSC::DFG::Node::hasConstant):
(JSC::DFG::Node::constant):
(JSC::DFG::Node::bitVector):
(JSC::DFG::Node::isPhantomAllocation):
* dfg/DFGNodeType.h:
* dfg/DFGOSRAvailabilityAnalysisPhase.cpp:
(JSC::DFG::OSRAvailabilityAnalysisPhase::run):
(JSC::DFG::LocalOSRAvailabilityCalculator::LocalOSRAvailabilityCalculator):
(JSC::DFG::LocalOSRAvailabilityCalculator::executeNode):
* dfg/DFGOSRAvailabilityAnalysisPhase.h:
* dfg/DFGObjectAllocationSinkingPhase.cpp:
* dfg/DFGPreciseLocalClobberize.h:
(JSC::DFG::PreciseLocalClobberizeAdaptor::readTop):
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGPromotedHeapLocation.cpp:
(WTF::printInternal):
* dfg/DFGPromotedHeapLocation.h:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGValidate.cpp:
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::LowerDFGToB3):
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileNewArrayWithSpread):
(JSC::FTL::DFG::LowerDFGToB3::compileSpread):
(JSC::FTL::DFG::LowerDFGToB3::compileCallOrConstructVarargsSpread):
(JSC::FTL::DFG::LowerDFGToB3::compileCallOrConstructVarargs):
(JSC::FTL::DFG::LowerDFGToB3::compileForwardVarargs):
(JSC::FTL::DFG::LowerDFGToB3::getSpreadLengthFromInlineCallFrame):
(JSC::FTL::DFG::LowerDFGToB3::compileForwardVarargsWithSpread):
* ftl/FTLOperations.cpp:
(JSC::FTL::operationPopulateObjectInOSR):
(JSC::FTL::operationMaterializeObjectInOSR):
* jit/SetupVarargsFrame.cpp:
(JSC::emitSetupVarargsFrameFastCase):
* jsc.cpp:
(GlobalObject::finishCreation):
(functionMaxArguments):
* runtime/JSFixedArray.h:
(JSC::JSFixedArray::createFromArray):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkJSTestsChangeLog">trunk/JSTests/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3LowerToAircpp">trunk/Source/JavaScriptCore/b3/B3LowerToAir.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3ValueReph">trunk/Source/JavaScriptCore/b3/B3ValueRep.h</a></li>
<li><a href="#trunkSourceJavaScriptCorebuiltinsBuiltinExecutablescpp">trunk/Source/JavaScriptCore/builtins/BuiltinExecutables.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGAbstractInterpreterInlinesh">trunk/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGArgumentsEliminationPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGArgumentsEliminationPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGClobberizeh">trunk/Source/JavaScriptCore/dfg/DFGClobberize.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGDoesGCcpp">trunk/Source/JavaScriptCore/dfg/DFGDoesGC.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGFixupPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGForAllKillsh">trunk/Source/JavaScriptCore/dfg/DFGForAllKills.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGNodeh">trunk/Source/JavaScriptCore/dfg/DFGNode.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGNodeTypeh">trunk/Source/JavaScriptCore/dfg/DFGNodeType.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGOSRAvailabilityAnalysisPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGOSRAvailabilityAnalysisPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGOSRAvailabilityAnalysisPhaseh">trunk/Source/JavaScriptCore/dfg/DFGOSRAvailabilityAnalysisPhase.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGObjectAllocationSinkingPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGPreciseLocalClobberizeh">trunk/Source/JavaScriptCore/dfg/DFGPreciseLocalClobberize.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGPredictionPropagationPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGPromotedHeapLocationcpp">trunk/Source/JavaScriptCore/dfg/DFGPromotedHeapLocation.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGPromotedHeapLocationh">trunk/Source/JavaScriptCore/dfg/DFGPromotedHeapLocation.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGSafeToExecuteh">trunk/Source/JavaScriptCore/dfg/DFGSafeToExecute.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGSpeculativeJIT32_64cpp">trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGSpeculativeJIT64cpp">trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGValidatecpp">trunk/Source/JavaScriptCore/dfg/DFGValidate.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLCapabilitiescpp">trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLLowerDFGToB3cpp">trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLOperationscpp">trunk/Source/JavaScriptCore/ftl/FTLOperations.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorejitSetupVarargsFramecpp">trunk/Source/JavaScriptCore/jit/SetupVarargsFrame.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorejsccpp">trunk/Source/JavaScriptCore/jsc.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSFixedArrayh">trunk/Source/JavaScriptCore/runtime/JSFixedArray.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkJSTestsmicrobenchmarksdefaultderivedconstructorjs">trunk/JSTests/microbenchmarks/default-derived-constructor.js</a></li>
<li><a href="#trunkJSTestsstresscallvarargsspreadjs">trunk/JSTests/stress/call-varargs-spread.js</a></li>
<li><a href="#trunkJSTestsstressloadvarargsonnewarraywithspreadconverttostaticloadsjs">trunk/JSTests/stress/load-varargs-on-new-array-with-spread-convert-to-static-loads.js</a></li>
<li><a href="#trunkJSTestsstressnewarraywithspreadwithnormalspreadandphantomspreadjs">trunk/JSTests/stress/new-array-with-spread-with-normal-spread-and-phantom-spread.js</a></li>
<li><a href="#trunkJSTestsstressphantomnewarraywithspreadosrexitjs">trunk/JSTests/stress/phantom-new-array-with-spread-osr-exit.js</a></li>
<li><a href="#trunkJSTestsstressphantomspreadforwardvarargsjs">trunk/JSTests/stress/phantom-spread-forward-varargs.js</a></li>
<li><a href="#trunkJSTestsstressphantomspreadosrexitjs">trunk/JSTests/stress/phantom-spread-osr-exit.js</a></li>
<li><a href="#trunkJSTestsstressspreadcallconverttostaticcalljs">trunk/JSTests/stress/spread-call-convert-to-static-call.js</a></li>
<li><a href="#trunkJSTestsstressspreadforwardcallvarargsstackoverflowjs">trunk/JSTests/stress/spread-forward-call-varargs-stack-overflow.js</a></li>
<li><a href="#trunkJSTestsstressspreadforwardvarargsrestparameterchangeiteratorprotocol2js">trunk/JSTests/stress/spread-forward-varargs-rest-parameter-change-iterator-protocol-2.js</a></li>
<li><a href="#trunkJSTestsstressspreadforwardvarargsrestparameterchangeiteratorprotocoljs">trunk/JSTests/stress/spread-forward-varargs-rest-parameter-change-iterator-protocol.js</a></li>
<li><a href="#trunkJSTestsstressspreadforwardvarargsstackoverflowjs">trunk/JSTests/stress/spread-forward-varargs-stack-overflow.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkJSTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/JSTests/ChangeLog (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/ChangeLog        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/JSTests/ChangeLog        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -1,3 +1,83 @@
</span><ins>+2016-11-29  Saam Barati  &lt;sbarati@apple.com&gt;
+
+        We should be able optimize the pattern where we spread a function's rest parameter to another call
+        https://bugs.webkit.org/show_bug.cgi?id=163865
+
+        Reviewed by Filip Pizlo.
+
+        * microbenchmarks/default-derived-constructor.js: Added.
+        (createClassHierarchy.let.currentClass):
+        (createClassHierarchy):
+        * stress/call-varargs-spread.js: Added.
+        (assert):
+        (bar):
+        (foo):
+        * stress/load-varargs-on-new-array-with-spread-convert-to-static-loads.js: Added.
+        (assert):
+        (baz):
+        (bar):
+        (foo):
+        * stress/new-array-with-spread-with-normal-spread-and-phantom-spread.js: Added.
+        (assert):
+        (foo):
+        (escape):
+        (bar):
+        * stress/phantom-new-array-with-spread-osr-exit.js: Added.
+        (assert):
+        (baz):
+        (bar):
+        (effects):
+        (foo):
+        * stress/phantom-spread-forward-varargs.js: Added.
+        (assert):
+        (test1.bar):
+        (test1.foo):
+        (test1):
+        (test2.bar):
+        (test2.foo):
+        (test3.baz):
+        (test3.bar):
+        (test3.foo):
+        (test4.baz):
+        (test4.bar):
+        (test4.foo):
+        (test5.baz):
+        (test5.bar):
+        (test5.foo):
+        * stress/phantom-spread-osr-exit.js: Added.
+        (assert):
+        (baz):
+        (bar):
+        (effects):
+        (foo):
+        * stress/spread-call-convert-to-static-call.js: Added.
+        (assert):
+        (baz):
+        (bar):
+        (foo):
+        * stress/spread-forward-call-varargs-stack-overflow.js: Added.
+        (assert):
+        (identity):
+        (bar):
+        (foo):
+        * stress/spread-forward-varargs-rest-parameter-change-iterator-protocol-2.js: Added.
+        (assert):
+        (baz.Array.prototype.Symbol.iterator):
+        (baz):
+        (bar):
+        (foo):
+        (test):
+        * stress/spread-forward-varargs-rest-parameter-change-iterator-protocol.js: Added.
+        (assert):
+        (baz.Array.prototype.Symbol.iterator):
+        (baz):
+        (bar):
+        (foo):
+        * stress/spread-forward-varargs-stack-overflow.js: Added.
+        (assert):
+        (bar):
+        (foo):
+
</ins><span class="cx"> 2016-11-29  Caitlin Potter  &lt;caitp@igalia.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [JSC] always wrap AwaitExpression operand in a new Promise
</span></span></pre></div>
<a id="trunkJSTestsmicrobenchmarksdefaultderivedconstructorjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/microbenchmarks/default-derived-constructor.js (0 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/microbenchmarks/default-derived-constructor.js                                (rev 0)
+++ trunk/JSTests/microbenchmarks/default-derived-constructor.js        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -0,0 +1,17 @@
</span><ins>+function createClassHierarchy(depth) {
+    let currentClass = class Base { };
+    for (let i = 0; i &lt; depth; i++) {
+        currentClass = class extends currentClass {};
+    }
+    return currentClass;
+}
+
+let ctor = createClassHierarchy(10);
+let start = Date.now();
+for (let i = 0; i &lt; 500000; i++) {
+    let x = new ctor({}, {}, 20, 30, 40, 50, 60, {}, 80, true, false);
+}
+
+const verbose = false;
+if (verbose)
+    print(Date.now() - start);
</ins></span></pre></div>
<a id="trunkJSTestsstresscallvarargsspreadjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/call-varargs-spread.js (0 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/stress/call-varargs-spread.js                                (rev 0)
+++ trunk/JSTests/stress/call-varargs-spread.js        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -0,0 +1,28 @@
</span><ins>+function assert(b, m = &quot;&quot;) {
+    if (!b)
+        throw new Error(&quot;Bad assert: &quot; + m);
+}
+noInline(assert);
+
+function bar(...args) {
+    return args;
+}
+noInline(bar);
+
+function foo(a, ...args) {
+    let x = bar(...args, 42, ...args); 
+    return x;
+}
+noInline(foo);
+
+for (let i = 0; i &lt; 10000; i++) {
+    let r = foo(i, i+1, i+2, i+3);
+    assert(r.length === 7);
+    assert(r[0] === i+1, JSON.stringify(r));
+    assert(r[1] === i+2, JSON.stringify(r));
+    assert(r[2] === i+3, JSON.stringify(r));
+    assert(r[3] === 42, JSON.stringify(r));
+    assert(r[4] === i+1, JSON.stringify(r));
+    assert(r[5] === i+2, JSON.stringify(r));
+    assert(r[6] === i+3, JSON.stringify(r));
+}
</ins></span></pre></div>
<a id="trunkJSTestsstressloadvarargsonnewarraywithspreadconverttostaticloadsjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/load-varargs-on-new-array-with-spread-convert-to-static-loads.js (0 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/stress/load-varargs-on-new-array-with-spread-convert-to-static-loads.js                                (rev 0)
+++ trunk/JSTests/stress/load-varargs-on-new-array-with-spread-convert-to-static-loads.js        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -0,0 +1,28 @@
</span><ins>+function assert(b) {
+    if (!b)
+        throw new Error(&quot;Bad!&quot;);
+}
+noInline(assert);
+
+function baz(...args) {
+    return args;
+}
+function bar(a, ...args) {
+    return baz(...args, 42, ...args);
+}
+function foo(a, b, c, d) {
+    return bar(a, b, c, d);
+}
+noInline(foo);
+
+for (let i = 0; i &lt; 10000; i++) {
+    let r = foo(i, i+1, i+2, i+3);
+    assert(r.length === 7);
+    assert(r[0] === i+1);
+    assert(r[1] === i+2);
+    assert(r[2] === i+3);
+    assert(r[3] === 42);
+    assert(r[4] === i+1);
+    assert(r[5] === i+2);
+    assert(r[6] === i+3);
+}
</ins></span></pre></div>
<a id="trunkJSTestsstressnewarraywithspreadwithnormalspreadandphantomspreadjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/new-array-with-spread-with-normal-spread-and-phantom-spread.js (0 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/stress/new-array-with-spread-with-normal-spread-and-phantom-spread.js                                (rev 0)
+++ trunk/JSTests/stress/new-array-with-spread-with-normal-spread-and-phantom-spread.js        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -0,0 +1,33 @@
</span><ins>+function assert(b) {
+    if (!b)
+        throw new Error(&quot;Bad assertion&quot;)
+}
+noInline(assert);
+
+function foo(a, ...args) {
+    let r = [...a, ...args];
+    return r;
+}
+noInline(foo);
+
+function escape(a) { return a; }
+noInline(escape);
+function bar(a, ...args) {
+    escape(args);
+    let r = [...a, ...args];
+    return r;
+}
+noInline(foo);
+
+for (let i = 0; i &lt; 50000; i++) {
+    for (let f of [foo, bar]) {
+        let o = {};
+        let a = [o, 20];
+        let r = f(a, 30, 40);
+        assert(r.length === 4);
+        assert(r[0] === o);
+        assert(r[1] === 20);
+        assert(r[2] === 30);
+        assert(r[3] === 40);
+    }
+}
</ins></span></pre></div>
<a id="trunkJSTestsstressphantomnewarraywithspreadosrexitjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/phantom-new-array-with-spread-osr-exit.js (0 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/stress/phantom-new-array-with-spread-osr-exit.js                                (rev 0)
+++ trunk/JSTests/stress/phantom-new-array-with-spread-osr-exit.js        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -0,0 +1,45 @@
</span><ins>+function assert(b) {
+    if (!b)
+        throw new Error(&quot;Bad assertion!&quot;);
+}
+noInline(assert);
+
+let value = false;
+
+function baz(x) {
+    if (typeof x !== &quot;number&quot;) {
+        value = true;
+    }
+    return x;
+}
+noInline(baz);
+
+function bar(...args) {
+    return args;
+}
+
+let didEffects = false; 
+function effects() { didEffects = true; }
+noInline(effects);
+
+function foo(a, ...args) {
+    let theArgs = [...args, a, ...args];
+    baz(a);
+    if (value) {
+        effects();
+    }
+    let r = bar.apply(null, theArgs);
+    return r;
+}
+noInline(foo);
+
+for (let i = 0; i &lt; 100000; i++) {
+    foo(i, i+1);
+    assert(!didEffects);
+}
+let o = {};
+let [a, b, c] = foo(o, 20);
+assert(a === 20);
+assert(b === o);
+assert(c === 20);
+assert(didEffects);
</ins></span></pre></div>
<a id="trunkJSTestsstressphantomspreadforwardvarargsjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/phantom-spread-forward-varargs.js (0 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/stress/phantom-spread-forward-varargs.js                                (rev 0)
+++ trunk/JSTests/stress/phantom-spread-forward-varargs.js        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -0,0 +1,117 @@
</span><ins>+&quot;use strict&quot;;
+
+function assert(b, m=&quot;&quot;) {
+    if (!b)
+        throw new Error(&quot;Bad assertion: &quot; + m);
+}
+noInline(assert);
+
+function test1() {
+    function bar(a, b, c, d) {
+        return [a, b, c, d];
+    }
+    function foo(...args) {
+        return bar(...args);
+    }
+    noInline(foo);
+
+    for (let i = 0; i &lt; 10000; i++) {
+        let [a, b, c, d] = foo(i, i+1, i+2, i+3);
+        assert(a === i);
+        assert(b === i+1);
+        assert(c === i+2);
+        assert(d === i+3) ;
+    }
+}
+
+function test2() {
+    function bar(...args) {
+        return args;
+    }
+    function foo(a, ...args) {
+        return bar(...args, a, ...args);
+    }
+    noInline(foo);
+
+    for (let i = 0; i &lt; 10000; i++) {
+        let r = foo(i, i+1, i+2, i+3);
+        assert(r.length === 7);
+        let [a, b, c, d, e, f, g] = r;
+        assert(a === i+1);
+        assert(b === i+2);
+        assert(c === i+3);
+        assert(d === i);
+        assert(e === i+1);
+        assert(f === i+2);
+        assert(g === i+3);
+    }
+}
+
+function test3() {
+    function baz(...args) {
+        return args;
+    }
+    function bar(...args) {
+        return baz(...args);
+    }
+    function foo(a, b, c, ...args) {
+        return bar(...args, a, ...args);
+    }
+    noInline(foo);
+
+    for (let i = 0; i &lt; 100000; i++) {
+        let r = foo(i, i+1, i+2, i+3);
+        assert(r.length === 3);
+        let [a, b, c] = r;
+        assert(a === i+3);
+        assert(b === i);
+        assert(c === i+3);
+    }
+}
+
+function test4() {
+    function baz(...args) {
+        return args;
+    }
+    function bar(...args) {
+        return baz(...args);
+    }
+    function foo(a, b, c, d, ...args) {
+        return bar(...args, a, ...args);
+    }
+    noInline(foo);
+
+    for (let i = 0; i &lt; 100000; i++) {
+        let r = foo(i, i+1, i+2, i+3);
+        assert(r.length === 1);
+        assert(r[0] === i);
+    }
+}
+
+function test5() {
+    function baz(a, b, c) {
+        return [a, b, c];
+    }
+    function bar(...args) {
+        return baz(...args);
+    }
+    function foo(a, b, c, d, ...args) {
+        return bar(...args, a, ...args);
+    }
+    noInline(foo);
+
+    for (let i = 0; i &lt; 100000; i++) {
+        let r = foo(i, i+1, i+2, i+3);
+        assert(r.length === 3);
+        let [a, b, c] = r;
+        assert(a === i);
+        assert(b === undefined);
+        assert(c === undefined);
+    }
+}
+
+test1();
+test2();
+test3();
+test4();
+test5();
</ins></span></pre></div>
<a id="trunkJSTestsstressphantomspreadosrexitjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/phantom-spread-osr-exit.js (0 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/stress/phantom-spread-osr-exit.js                                (rev 0)
+++ trunk/JSTests/stress/phantom-spread-osr-exit.js        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -0,0 +1,43 @@
</span><ins>+function assert(b) {
+    if (!b)
+        throw new Error(&quot;Bad assertion!&quot;);
+}
+noInline(assert);
+
+let value = false;
+
+function baz(x) {
+    if (typeof x !== &quot;number&quot;) {
+        value = true;
+    }
+    return x;
+}
+noInline(baz);
+
+function bar(...args) {
+    return args;
+}
+
+let didEffects = false; 
+function effects() { didEffects = true; }
+noInline(effects);
+
+function foo(a, ...args) {
+    let r = bar(...args, baz(a), ...args);
+    if (value) {
+        effects();
+    }
+    return r;
+}
+noInline(foo);
+
+for (let i = 0; i &lt; 100000; i++) {
+    foo(i, i+1);
+    assert(!didEffects);
+}
+let o = {};
+let [a, b, c] = foo(o, 20);
+assert(a === 20);
+assert(b === o);
+assert(c === 20);
+assert(didEffects);
</ins></span></pre></div>
<a id="trunkJSTestsstressspreadcallconverttostaticcalljs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/spread-call-convert-to-static-call.js (0 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/stress/spread-call-convert-to-static-call.js                                (rev 0)
+++ trunk/JSTests/stress/spread-call-convert-to-static-call.js        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -0,0 +1,29 @@
</span><ins>+function assert(b) {
+    if (!b)
+        throw new Error(&quot;Bad!&quot;);
+}
+noInline(assert);
+
+function baz(...args) {
+    return args;
+}
+noInline(baz);
+function bar(a, ...args) {
+    return baz(...args, 42, ...args);
+}
+function foo(a, b, c, d) {
+    return bar(a, b, c, d);
+}
+noInline(foo);
+
+for (let i = 0; i &lt; 10000; i++) {
+    let r = foo(i, i+1, i+2, i+3);
+    assert(r.length === 7);
+    assert(r[0] === i+1);
+    assert(r[1] === i+2);
+    assert(r[2] === i+3);
+    assert(r[3] === 42);
+    assert(r[4] === i+1);
+    assert(r[5] === i+2);
+    assert(r[6] === i+3);
+}
</ins></span></pre></div>
<a id="trunkJSTestsstressspreadforwardcallvarargsstackoverflowjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/spread-forward-call-varargs-stack-overflow.js (0 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/stress/spread-forward-call-varargs-stack-overflow.js                                (rev 0)
+++ trunk/JSTests/stress/spread-forward-call-varargs-stack-overflow.js        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -0,0 +1,57 @@
</span><ins>+function assert(b) {
+    if (!b)
+        throw new Error(&quot;Bad assertion&quot;);
+}
+noInline(assert);
+
+function identity(a) { return a; }
+noInline(identity);
+
+function bar(...args) {
+    return args;
+}
+noInline(bar);
+
+function foo(a, ...args) {
+    let arg = identity(a);
+    try {
+        let r = bar(...args, ...args);
+        return r;
+    } catch(e) {
+        return arg;
+    }
+}
+noInline(foo);
+
+for (let i = 0; i &lt; 40000; i++) {
+    let args = [];
+    for (let i = 0; i &lt; 400; i++) {
+        args.push(i);
+    }
+
+    let o = {};
+    let r = foo(o, ...args);
+    let i = 0;
+    for (let arg of args) {
+        assert(r[i] === arg);
+        i++;
+    }
+    for (let arg of args) {
+        assert(r[i] === arg);
+        i++;
+    }
+}
+
+for (let i = 0; i &lt; 20; i++) {
+    let threw = false;
+    let o = {};
+    let args = [];
+    let argCount = maxArguments() * (2/3);
+    argCount = argCount | 0;
+    for (let i = 0; i &lt; argCount; i++) {
+        args.push(i);
+    }
+
+    let r = foo(o, ...args);
+    assert(r === o);
+}
</ins></span></pre></div>
<a id="trunkJSTestsstressspreadforwardvarargsrestparameterchangeiteratorprotocol2js"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/spread-forward-varargs-rest-parameter-change-iterator-protocol-2.js (0 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/stress/spread-forward-varargs-rest-parameter-change-iterator-protocol-2.js                                (rev 0)
+++ trunk/JSTests/stress/spread-forward-varargs-rest-parameter-change-iterator-protocol-2.js        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -0,0 +1,46 @@
</span><ins>+function assert(b, m=&quot;&quot;) {
+    if (!b)
+        throw new Error(&quot;Bad assertion: &quot; + m);
+}
+noInline(assert);
+
+let called = false;
+function baz(c) {
+    if (c) {
+        Array.prototype[Symbol.iterator] = function() {
+            assert(false, &quot;should not be called!&quot;);
+            let i = 0;
+            return {
+                next() {
+                    assert(false, &quot;should not be called!&quot;);
+                }
+            };
+        }
+    }
+}
+noInline(baz);
+
+function bar(...args) {
+    return args;
+}
+noInline(bar);
+
+function foo(c, ...args) {
+    args = [...args];
+    baz(c);
+    return bar.apply(null, args);
+}
+noInline(foo);
+
+function test(c) {
+    const args = [{}, 20, [], 45];
+    let r = foo(c, ...args);
+    assert(r.length === r.length);
+    for (let i = 0; i &lt; r.length; i++)
+        assert(r[i] === args[i]);
+}
+noInline(test);
+for (let i = 0; i &lt; 40000; i++) {
+    test(false);
+}
+test(true);
</ins></span></pre></div>
<a id="trunkJSTestsstressspreadforwardvarargsrestparameterchangeiteratorprotocoljs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/spread-forward-varargs-rest-parameter-change-iterator-protocol.js (0 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/stress/spread-forward-varargs-rest-parameter-change-iterator-protocol.js                                (rev 0)
+++ trunk/JSTests/stress/spread-forward-varargs-rest-parameter-change-iterator-protocol.js        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -0,0 +1,49 @@
</span><ins>+function assert(b) {
+    if (!b)
+        throw new Error(&quot;Bad assertion&quot;);
+}
+noInline(assert);
+
+let called = false;
+function baz(c) {
+    if (c) {
+        Array.prototype[Symbol.iterator] = function() {
+            let i = 0;
+            return {
+                next() {
+                    i++;
+                    if (i === 2)
+                        return {done: true};
+                    return {value: 40, done: false};
+                }
+            };
+        }
+    }
+}
+noInline(baz);
+
+function bar(...args) {
+    return args;
+}
+noInline(bar);
+
+function foo(c, ...args) {
+    baz(c);
+    return bar(...args);
+}
+noInline(foo);
+
+for (let i = 0; i &lt; 10000; i++) {
+    const c = false;
+    const args = [{}, 20, [], 45];
+    let r = foo(c, ...args);
+    assert(r.length === r.length);
+    for (let i = 0; i &lt; r.length; i++)
+        assert(r[i] === args[i]);
+}
+
+const c = true;
+const args = [{}, 20, [], 45];
+let r = foo(c, ...args);
+assert(r.length === 1);
+assert(r[0] === 40);
</ins></span></pre></div>
<a id="trunkJSTestsstressspreadforwardvarargsstackoverflowjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/spread-forward-varargs-stack-overflow.js (0 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/stress/spread-forward-varargs-stack-overflow.js                                (rev 0)
+++ trunk/JSTests/stress/spread-forward-varargs-stack-overflow.js        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -0,0 +1,52 @@
</span><ins>+function assert(b) {
+    if (!b)
+        throw new Error(&quot;Bad assertion&quot;);
+}
+noInline(assert);
+
+function bar(...args) {
+    return args;
+}
+
+function foo(a, ...args) {
+    try {
+        let r = bar(...args, ...args);
+        return r;
+    } catch(e) {
+        return a;
+    }
+}
+noInline(foo);
+
+for (let i = 0; i &lt; 10000; i++) {
+    let args = [];
+    for (let i = 0; i &lt; 400; i++) {
+        args.push(i);
+    }
+
+    let o = {};
+    let r = foo(o, ...args);
+    let i = 0;
+    for (let arg of args) {
+        assert(r[i] === arg);
+        i++;
+    }
+    for (let arg of args) {
+        assert(r[i] === arg);
+        i++;
+    }
+}
+
+for (let i = 0; i &lt; 20; i++) {
+    let threw = false;
+    let o = {};
+    let args = [];
+    let argCount = maxArguments() * (2/3);
+    argCount = argCount | 0;
+    for (let i = 0; i &lt; argCount; i++) {
+        args.push(i);
+    }
+
+    let r = foo(o, ...args);
+    assert(r === o);
+}
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/ChangeLog        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -1,3 +1,149 @@
</span><ins>+2016-11-29  Saam Barati  &lt;sbarati@apple.com&gt;
+
+        We should be able optimize the pattern where we spread a function's rest parameter to another call
+        https://bugs.webkit.org/show_bug.cgi?id=163865
+
+        Reviewed by Filip Pizlo.
+
+        This patch optimizes the following patterns to prevent both the allocation
+        of the rest parameter, and the execution of the iterator protocol:
+        
+        ```
+        function foo(...args) {
+            let arr = [...args];
+        }
+        
+        and
+        
+        function foo(...args) {
+            bar(...args);
+        }
+        ```
+        
+        To do this, I've extended the arguments elimination phase to reason
+        about Spread and NewArrayWithSpread. I've added two new nodes, PhantomSpread
+        and PhantomNewArrayWithSpread. PhantomSpread is only allowed over rest
+        parameters that don't escape. If the rest parameter *does* escape, we can't
+        convert the spread into a phantom because it would not be sound w.r.t JS
+        semantics because we would be reading from the call frame even though
+        the rest array may have changed.
+        
+        Note that NewArrayWithSpread also understands what to do when one of its
+        arguments is PhantomSpread(@PhantomCreateRest) even if it itself is escaped.
+        
+        PhantomNewArrayWithSpread is only allowed over a series of
+        PhantomSpread(@PhantomCreateRest) nodes. Like with PhantomSpread, PhantomNewArrayWithSpread
+        is only allowed if none of its arguments that are being spread are escaped
+        and if it itself is not escaped.
+        
+        Because there is a dependency between a node being a candidate and
+        the escaped state of the node's children, I've extended the notion
+        of escaping a node inside the arguments elimination phase. Now, when
+        any node is escaped, we must consider all other candidates that are may
+        now no longer be valid.
+        
+        For example:
+        
+        ```
+        function foo(...args) {
+            escape(args);
+            bar(...args);
+        }
+        ```
+        
+        In the above program, we don't know if the function call to escape()
+        modifies args, therefore, the spread can not become phantom because
+        the execution of the spread may not be as simple as reading the
+        arguments from the call frame.
+        
+        Unfortunately, the arguments elimination phase does not consider control
+        flow when doing its escape analysis. It would be good to integrate this
+        phase with the object allocation sinking phase. To see why, consider
+        an example where we don't eliminate the spread and allocation of the rest
+        parameter even though we could:
+        
+        ```
+        function foo(rareCondition, ...args) {
+            bar(...args);
+            if (rareCondition)
+                baz(args);
+        }
+        ```
+        
+        There are only a few users of the PhantomSpread and PhantomNewArrayWithSpread
+        nodes. PhantomSpread is only used by PhantomNewArrayWithSpread and NewArrayWithSpread.
+        PhantomNewArrayWithSpread is only used by ForwardVarargs and the various
+        *Call*ForwardVarargs nodes. The users of these phantoms know how to produce
+        what the phantom node would have produced. For example, NewArrayWithSpread
+        knows how to produce the values that would have been produced by PhantomSpread(@PhantomCreateRest)
+        by directly reading from the call frame.
+        
+        This patch is a 6% speedup on my MBP on ES6SampleBench.
+
+        * b3/B3LowerToAir.cpp:
+        (JSC::B3::Air::LowerToAir::tryAppendLea):
+        * b3/B3ValueRep.h:
+        * builtins/BuiltinExecutables.cpp:
+        (JSC::BuiltinExecutables::createDefaultConstructor):
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter&lt;AbstractStateType&gt;::executeEffects):
+        * dfg/DFGArgumentsEliminationPhase.cpp:
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGForAllKills.h:
+        (JSC::DFG::forAllKillsInBlock):
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::hasConstant):
+        (JSC::DFG::Node::constant):
+        (JSC::DFG::Node::bitVector):
+        (JSC::DFG::Node::isPhantomAllocation):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOSRAvailabilityAnalysisPhase.cpp:
+        (JSC::DFG::OSRAvailabilityAnalysisPhase::run):
+        (JSC::DFG::LocalOSRAvailabilityCalculator::LocalOSRAvailabilityCalculator):
+        (JSC::DFG::LocalOSRAvailabilityCalculator::executeNode):
+        * dfg/DFGOSRAvailabilityAnalysisPhase.h:
+        * dfg/DFGObjectAllocationSinkingPhase.cpp:
+        * dfg/DFGPreciseLocalClobberize.h:
+        (JSC::DFG::PreciseLocalClobberizeAdaptor::readTop):
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGPromotedHeapLocation.cpp:
+        (WTF::printInternal):
+        * dfg/DFGPromotedHeapLocation.h:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGValidate.cpp:
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::LowerDFGToB3):
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileNewArrayWithSpread):
+        (JSC::FTL::DFG::LowerDFGToB3::compileSpread):
+        (JSC::FTL::DFG::LowerDFGToB3::compileCallOrConstructVarargsSpread):
+        (JSC::FTL::DFG::LowerDFGToB3::compileCallOrConstructVarargs):
+        (JSC::FTL::DFG::LowerDFGToB3::compileForwardVarargs):
+        (JSC::FTL::DFG::LowerDFGToB3::getSpreadLengthFromInlineCallFrame):
+        (JSC::FTL::DFG::LowerDFGToB3::compileForwardVarargsWithSpread):
+        * ftl/FTLOperations.cpp:
+        (JSC::FTL::operationPopulateObjectInOSR):
+        (JSC::FTL::operationMaterializeObjectInOSR):
+        * jit/SetupVarargsFrame.cpp:
+        (JSC::emitSetupVarargsFrameFastCase):
+        * jsc.cpp:
+        (GlobalObject::finishCreation):
+        (functionMaxArguments):
+        * runtime/JSFixedArray.h:
+        (JSC::JSFixedArray::createFromArray):
+
</ins><span class="cx"> 2016-11-29  Commit Queue  &lt;commit-queue@webkit.org&gt;
</span><span class="cx"> 
</span><span class="cx">         Unreviewed, rolling out r209058 and r209074.
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3LowerToAircpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3LowerToAir.cpp (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3LowerToAir.cpp        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/b3/B3LowerToAir.cpp        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -1929,7 +1929,7 @@
</span><span class="cx">             &amp;&amp; canBeInternal(value-&gt;child(0))
</span><span class="cx">             &amp;&amp; value-&gt;child(0)-&gt;opcode() == Add) {
</span><span class="cx">             innerAdd = value-&gt;child(0);
</span><del>-            offset = value-&gt;child(1)-&gt;asInt32();
</del><ins>+            offset = static_cast&lt;int32_t&gt;(value-&gt;child(1)-&gt;asInt());
</ins><span class="cx">             value = value-&gt;child(0);
</span><span class="cx">         }
</span><span class="cx">         
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3ValueReph"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3ValueRep.h (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3ValueRep.h        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/b3/B3ValueRep.h        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -78,7 +78,7 @@
</span><span class="cx">         // As an input representation, this forces a particular register and states that
</span><span class="cx">         // the register is used late. This means that the register is used after the result
</span><span class="cx">         // is defined (i.e, the result will interfere with this as an input).
</span><del>-        // It's not valid for this to be used as a result kind.
</del><ins>+        // It's not a valid output representation.
</ins><span class="cx">         LateRegister,
</span><span class="cx"> 
</span><span class="cx">         // As an output representation, this tells us what stack slot B3 picked. It's not a valid
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorebuiltinsBuiltinExecutablescpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/builtins/BuiltinExecutables.cpp (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/builtins/BuiltinExecutables.cpp        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/builtins/BuiltinExecutables.cpp        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -45,7 +45,7 @@
</span><span class="cx"> UnlinkedFunctionExecutable* BuiltinExecutables::createDefaultConstructor(ConstructorKind constructorKind, const Identifier&amp; name)
</span><span class="cx"> {
</span><span class="cx">     static NeverDestroyed&lt;const String&gt; baseConstructorCode(ASCIILiteral(&quot;(function () { })&quot;));
</span><del>-    static NeverDestroyed&lt;const String&gt; derivedConstructorCode(ASCIILiteral(&quot;(function () { super(...arguments); })&quot;));
</del><ins>+    static NeverDestroyed&lt;const String&gt; derivedConstructorCode(ASCIILiteral(&quot;(function (...args) { super(...args); })&quot;));
</ins><span class="cx"> 
</span><span class="cx">     switch (constructorKind) {
</span><span class="cx">     case ConstructorKind::None:
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGAbstractInterpreterInlinesh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -1975,6 +1975,8 @@
</span><span class="cx">     case PhantomDirectArguments:
</span><span class="cx">     case PhantomClonedArguments:
</span><span class="cx">     case PhantomCreateRest:
</span><ins>+    case PhantomSpread:
+    case PhantomNewArrayWithSpread:
</ins><span class="cx">     case BottomValue:
</span><span class="cx">         m_state.setDidClobber(true); // Prevent constant folding.
</span><span class="cx">         // This claims to return bottom.
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGArgumentsEliminationPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGArgumentsEliminationPhase.cpp (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGArgumentsEliminationPhase.cpp        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/dfg/DFGArgumentsEliminationPhase.cpp        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -91,7 +91,7 @@
</span><span class="cx">     // Just finds nodes that we know how to work with.
</span><span class="cx">     void identifyCandidates()
</span><span class="cx">     {
</span><del>-        for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
</del><ins>+        for (BasicBlock* block : m_graph.blocksInPreOrder()) {
</ins><span class="cx">             for (Node* node : *block) {
</span><span class="cx">                 switch (node-&gt;op()) {
</span><span class="cx">                 case CreateDirectArguments:
</span><span class="lines">@@ -109,6 +109,38 @@
</span><span class="cx">                         m_candidates.add(node);
</span><span class="cx">                     }
</span><span class="cx">                     break;
</span><ins>+
+                case Spread:
+                    if (m_graph.isWatchingHavingABadTimeWatchpoint(node)) {
+                        // We check ArrayUse here because ArrayUse indicates that the iterator
+                        // protocol for Arrays is non-observable by user code (e.g, it hasn't
+                        // been changed).
+                        if (node-&gt;child1().useKind() == ArrayUse &amp;&amp; node-&gt;child1()-&gt;op() == CreateRest &amp;&amp; m_candidates.contains(node-&gt;child1().node()))
+                            m_candidates.add(node);
+                    }
+                    break;
+
+                case NewArrayWithSpread: {
+                    if (m_graph.isWatchingHavingABadTimeWatchpoint(node)) {
+                        BitVector* bitVector = node-&gt;bitVector();
+                        // We only allow for Spreads to be of rest nodes for now.
+                        bool isOK = true;
+                        for (unsigned i = 0; i &lt; node-&gt;numChildren(); i++) {
+                            if (bitVector-&gt;get(i)) {
+                                Node* child = m_graph.varArgChild(node, i).node();
+                                isOK = child-&gt;op() == Spread &amp;&amp; child-&gt;child1()-&gt;op() == CreateRest &amp;&amp; m_candidates.contains(child);
+                                if (!isOK)
+                                    break;
+                            }
+                        }
+
+                        if (!isOK)
+                            break;
+
+                        m_candidates.add(node);
+                    }
+                    break;
+                }
</ins><span class="cx">                     
</span><span class="cx">                 case CreateScopedArguments:
</span><span class="cx">                     // FIXME: We could handle this if it wasn't for the fact that scoped arguments are
</span><span class="lines">@@ -126,6 +158,62 @@
</span><span class="cx">         if (verbose)
</span><span class="cx">             dataLog(&quot;Candidates: &quot;, listDump(m_candidates), &quot;\n&quot;);
</span><span class="cx">     }
</span><ins>+
+    bool isStillValidCandidate(Node* candidate)
+    {
+        switch (candidate-&gt;op()) {
+        case Spread:
+            return m_candidates.contains(candidate-&gt;child1().node());
+
+        case NewArrayWithSpread: {
+            BitVector* bitVector = candidate-&gt;bitVector();
+            for (unsigned i = 0; i &lt; candidate-&gt;numChildren(); i++) {
+                if (bitVector-&gt;get(i)) {
+                    if (!m_candidates.contains(m_graph.varArgChild(candidate, i).node()))
+                        return false;
+                }
+            }
+            return true;
+        }
+
+        default:
+            return true;
+        }
+
+        RELEASE_ASSERT_NOT_REACHED();
+        return false;
+    }
+
+    void removeInvalidCandidates()
+    {
+        bool changed;
+        do {
+            changed = false;
+            Vector&lt;Node*, 1&gt; toRemove;
+
+            for (Node* candidate : m_candidates) {
+                if (!isStillValidCandidate(candidate))
+                    toRemove.append(candidate);
+            }
+
+            if (toRemove.size()) {
+                changed = true;
+                for (Node* node : toRemove)
+                    m_candidates.remove(node);
+            }
+
+        } while (changed);
+    }
+
+    void transitivelyRemoveCandidate(Node* node, Node* source = nullptr)
+    {
+        bool removed = m_candidates.remove(node);
+        if (removed &amp;&amp; verbose &amp;&amp; source)
+            dataLog(&quot;eliminating candidate: &quot;, node, &quot; because it escapes from: &quot;, source, &quot;\n&quot;);
+
+        if (removed)
+            removeInvalidCandidates();
+    }
</ins><span class="cx">     
</span><span class="cx">     // Look for escaping sites, and remove from the candidates set if we see an escape.
</span><span class="cx">     void eliminateCandidatesThatEscape()
</span><span class="lines">@@ -133,9 +221,7 @@
</span><span class="cx">         auto escape = [&amp;] (Edge edge, Node* source) {
</span><span class="cx">             if (!edge)
</span><span class="cx">                 return;
</span><del>-            bool removed = m_candidates.remove(edge.node());
-            if (removed &amp;&amp; verbose)
-                dataLog(&quot;eliminating candidate: &quot;, edge.node(), &quot; because it escapes from: &quot;, source, &quot;\n&quot;);
</del><ins>+            transitivelyRemoveCandidate(edge.node(), source);
</ins><span class="cx">         };
</span><span class="cx">         
</span><span class="cx">         auto escapeBasedOnArrayMode = [&amp;] (ArrayMode mode, Edge edge, Node* source) {
</span><span class="lines">@@ -187,6 +273,8 @@
</span><span class="cx">                 break;
</span><span class="cx">             }
</span><span class="cx">         };
</span><ins>+
+        removeInvalidCandidates();
</ins><span class="cx">         
</span><span class="cx">         for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
</span><span class="cx">             for (Node* node : *block) {
</span><span class="lines">@@ -201,11 +289,42 @@
</span><span class="cx">                     break;
</span><span class="cx"> 
</span><span class="cx">                 case GetArrayLength:
</span><del>-                    escapeBasedOnArrayMode(node-&gt;arrayMode(), node-&gt;child1(), node);
</del><ins>+                    // FIXME: It would not be hard to support NewArrayWithSpread here if it is only over Spread(CreateRest) nodes.
</ins><span class="cx">                     escape(node-&gt;child2(), node);
</span><span class="cx">                     break;
</span><ins>+                
+                case NewArrayWithSpread: {
+                    BitVector* bitVector = node-&gt;bitVector();
+                    bool isWatchingHavingABadTimeWatchpoint = m_graph.isWatchingHavingABadTimeWatchpoint(node); 
+                    for (unsigned i = 0; i &lt; node-&gt;numChildren(); i++) {
+                        Edge child = m_graph.varArgChild(node, i);
+                        bool dontEscape;
+                        if (bitVector-&gt;get(i)) {
+                            dontEscape = child-&gt;op() == Spread
+                                &amp;&amp; child-&gt;child1().useKind() == ArrayUse
+                                &amp;&amp; child-&gt;child1()-&gt;op() == CreateRest
+                                &amp;&amp; isWatchingHavingABadTimeWatchpoint;
+                        } else
+                            dontEscape = false;
+
+                        if (!dontEscape)
+                            escape(child, node);
+                    }
+
+                    break;
+                }
+
+                case Spread: {
+                    bool isOK = node-&gt;child1().useKind() == ArrayUse &amp;&amp; node-&gt;child1()-&gt;op() == CreateRest;
+                    if (!isOK)
+                        escape(node-&gt;child1(), node);
+                    break;
+                }
+
</ins><span class="cx">                     
</span><span class="cx">                 case LoadVarargs:
</span><ins>+                    if (node-&gt;loadVarargsData()-&gt;offset &amp;&amp; node-&gt;child1()-&gt;op() == NewArrayWithSpread)
+                        escape(node-&gt;child1(), node);
</ins><span class="cx">                     break;
</span><span class="cx">                     
</span><span class="cx">                 case CallVarargs:
</span><span class="lines">@@ -214,6 +333,8 @@
</span><span class="cx">                 case TailCallVarargsInlinedCaller:
</span><span class="cx">                     escape(node-&gt;child1(), node);
</span><span class="cx">                     escape(node-&gt;child2(), node);
</span><ins>+                    if (node-&gt;callVarargsData()-&gt;firstVarArgOffset &amp;&amp; node-&gt;child3()-&gt;op() == NewArrayWithSpread)
+                        escape(node-&gt;child3(), node);
</ins><span class="cx">                     break;
</span><span class="cx"> 
</span><span class="cx">                 case Check:
</span><span class="lines">@@ -263,6 +384,10 @@
</span><span class="cx">                         ASSERT(m_graph.isWatchingHavingABadTimeWatchpoint(node));
</span><span class="cx">                         structure = globalObject-&gt;restParameterStructure();
</span><span class="cx">                         break;
</span><ins>+                    case NewArrayWithSpread:
+                        ASSERT(m_graph.isWatchingHavingABadTimeWatchpoint(node));
+                        structure = globalObject-&gt;originalArrayStructureForIndexingType(ArrayWithContiguous);
+                        break;
</ins><span class="cx">                     default:
</span><span class="cx">                         RELEASE_ASSERT_NOT_REACHED();
</span><span class="cx">                     }
</span><span class="lines">@@ -390,7 +515,7 @@
</span><span class="cx">                     if (nodeIndex == block-&gt;size() &amp;&amp; candidate-&gt;owner != block) {
</span><span class="cx">                         if (verbose)
</span><span class="cx">                             dataLog(&quot;eliminating candidate: &quot;, candidate, &quot; because it is clobbered by: &quot;, block-&gt;at(nodeIndex), &quot;\n&quot;);
</span><del>-                        m_candidates.remove(candidate);
</del><ins>+                        transitivelyRemoveCandidate(candidate);
</ins><span class="cx">                         return;
</span><span class="cx">                     }
</span><span class="cx">                     
</span><span class="lines">@@ -417,7 +542,7 @@
</span><span class="cx">                         if (found) {
</span><span class="cx">                             if (verbose)
</span><span class="cx">                                 dataLog(&quot;eliminating candidate: &quot;, candidate, &quot; because it is clobbered by &quot;, block-&gt;at(nodeIndex), &quot;\n&quot;);
</span><del>-                            m_candidates.remove(candidate);
</del><ins>+                            transitivelyRemoveCandidate(candidate);
</ins><span class="cx">                             return;
</span><span class="cx">                         }
</span><span class="cx">                     }
</span><span class="lines">@@ -460,10 +585,11 @@
</span><span class="cx">                     // We traverse in such a way that we are guaranteed to see a def before a use.
</span><span class="cx">                     // Therefore, we should have already transformed the allocation before the use
</span><span class="cx">                     // of an allocation.
</span><del>-                    ASSERT(candidate-&gt;op() == PhantomCreateRest || candidate-&gt;op() == PhantomDirectArguments || candidate-&gt;op() == PhantomClonedArguments);
</del><ins>+                    ASSERT(candidate-&gt;op() == PhantomCreateRest || candidate-&gt;op() == PhantomDirectArguments || candidate-&gt;op() == PhantomClonedArguments
+                        || candidate-&gt;op() == PhantomSpread || candidate-&gt;op() == PhantomNewArrayWithSpread);
</ins><span class="cx">                     return true;
</span><span class="cx">                 };
</span><del>-        
</del><ins>+
</ins><span class="cx">                 switch (node-&gt;op()) {
</span><span class="cx">                 case CreateDirectArguments:
</span><span class="cx">                     if (!m_candidates.contains(node))
</span><span class="lines">@@ -488,7 +614,21 @@
</span><span class="cx">                     
</span><span class="cx">                     node-&gt;setOpAndDefaultFlags(PhantomClonedArguments);
</span><span class="cx">                     break;
</span><ins>+
+                case Spread:
+                    if (!m_candidates.contains(node))
+                        break;
</ins><span class="cx">                     
</span><ins>+                    node-&gt;setOpAndDefaultFlags(PhantomSpread);
+                    break;
+
+                case NewArrayWithSpread:
+                    if (!m_candidates.contains(node))
+                        break;
+                    
+                    node-&gt;setOpAndDefaultFlags(PhantomNewArrayWithSpread);
+                    break;
+                    
</ins><span class="cx">                 case GetFromArguments: {
</span><span class="cx">                     Node* candidate = node-&gt;child1().node();
</span><span class="cx">                     if (!isEliminatedAllocation(candidate))
</span><span class="lines">@@ -599,94 +739,185 @@
</span><span class="cx">                     if (!isEliminatedAllocation(candidate))
</span><span class="cx">                         break;
</span><span class="cx">                     
</span><ins>+                    // LoadVarargs can exit, so it better be exitOK.
+                    DFG_ASSERT(m_graph, node, node-&gt;origin.exitOK);
+                    bool canExit = true;
</ins><span class="cx">                     LoadVarargsData* varargsData = node-&gt;loadVarargsData();
</span><del>-                    unsigned numberOfArgumentsToSkip = 0;
-                    if (candidate-&gt;op() == PhantomCreateRest)
-                        numberOfArgumentsToSkip = candidate-&gt;numberOfArgumentsToSkip();
-                    varargsData-&gt;offset += numberOfArgumentsToSkip;
</del><span class="cx"> 
</span><del>-                    InlineCallFrame* inlineCallFrame = candidate-&gt;origin.semantic.inlineCallFrame;
</del><ins>+                    auto storeArgumentCountIncludingThis = [&amp;] (unsigned argumentCountIncludingThis) {
+                        Node* argumentCountIncludingThisNode = insertionSet.insertConstant(
+                            nodeIndex, node-&gt;origin.withExitOK(canExit),
+                            jsNumber(argumentCountIncludingThis));
+                        insertionSet.insertNode(
+                            nodeIndex, SpecNone, MovHint, node-&gt;origin.takeValidExit(canExit),
+                            OpInfo(varargsData-&gt;count.offset()), Edge(argumentCountIncludingThisNode));
+                        insertionSet.insertNode(
+                            nodeIndex, SpecNone, PutStack, node-&gt;origin.withExitOK(canExit),
+                            OpInfo(m_graph.m_stackAccessData.add(varargsData-&gt;count, FlushedInt32)),
+                            Edge(argumentCountIncludingThisNode, KnownInt32Use));
+                    };
</ins><span class="cx"> 
</span><del>-                    if (inlineCallFrame
-                        &amp;&amp; !inlineCallFrame-&gt;isVarargs()) {
</del><ins>+                    auto storeValue = [&amp;] (Node* value, unsigned storeIndex) {
+                        VirtualRegister reg = varargsData-&gt;start + storeIndex;
+                        StackAccessData* data =
+                            m_graph.m_stackAccessData.add(reg, FlushedJSValue);
+                        
+                        insertionSet.insertNode(
+                            nodeIndex, SpecNone, MovHint, node-&gt;origin.takeValidExit(canExit),
+                            OpInfo(reg.offset()), Edge(value));
+                        insertionSet.insertNode(
+                            nodeIndex, SpecNone, PutStack, node-&gt;origin.withExitOK(canExit),
+                            OpInfo(data), Edge(value));
+                    };
</ins><span class="cx"> 
</span><del>-                        unsigned argumentCountIncludingThis = inlineCallFrame-&gt;arguments.size();
-                        if (argumentCountIncludingThis &gt; varargsData-&gt;offset)
-                            argumentCountIncludingThis -= varargsData-&gt;offset;
-                        else
-                            argumentCountIncludingThis = 1;
-                        RELEASE_ASSERT(argumentCountIncludingThis &gt;= 1);
</del><ins>+                    if (candidate-&gt;op() == PhantomNewArrayWithSpread) {
+                        bool canConvertToStaticLoadStores = true;
+                        BitVector* bitVector = candidate-&gt;bitVector();
</ins><span class="cx"> 
</span><del>-                        if (argumentCountIncludingThis &lt;= varargsData-&gt;limit) {
-                            // LoadVarargs can exit, so it better be exitOK.
-                            DFG_ASSERT(m_graph, node, node-&gt;origin.exitOK);
-                            bool canExit = true;
-                            
-                            Node* argumentCountIncludingThisNode = insertionSet.insertConstant(
-                                nodeIndex, node-&gt;origin.withExitOK(canExit),
-                                jsNumber(argumentCountIncludingThis));
-                            insertionSet.insertNode(
-                                nodeIndex, SpecNone, MovHint, node-&gt;origin.takeValidExit(canExit),
-                                OpInfo(varargsData-&gt;count.offset()), Edge(argumentCountIncludingThisNode));
-                            insertionSet.insertNode(
-                                nodeIndex, SpecNone, PutStack, node-&gt;origin.withExitOK(canExit),
-                                OpInfo(m_graph.m_stackAccessData.add(varargsData-&gt;count, FlushedInt32)),
-                                Edge(argumentCountIncludingThisNode, KnownInt32Use));
-                            
-                            DFG_ASSERT(m_graph, node, varargsData-&gt;limit - 1 &gt;= varargsData-&gt;mandatoryMinimum);
-                            // Define our limit to exclude &quot;this&quot;, since that's a bit easier to reason about.
-                            unsigned limit = varargsData-&gt;limit - 1;
-                            Node* undefined = nullptr;
-                            for (unsigned storeIndex = 0; storeIndex &lt; limit; ++storeIndex) {
-                                // First determine if we have an element we can load, and load it if
-                                // possible.
-                                
-                                unsigned loadIndex = storeIndex + varargsData-&gt;offset;
-                                
-                                Node* value;
-                                if (loadIndex + 1 &lt; inlineCallFrame-&gt;arguments.size()) {
-                                    VirtualRegister reg = virtualRegisterForArgument(loadIndex + 1) + inlineCallFrame-&gt;stackOffset;
-                                    StackAccessData* data = m_graph.m_stackAccessData.add(
-                                        reg, FlushedJSValue);
-                                    
-                                    value = insertionSet.insertNode(
-                                        nodeIndex, SpecNone, GetStack, node-&gt;origin.withExitOK(canExit),
-                                        OpInfo(data));
-                                } else {
-                                    // FIXME: We shouldn't have to store anything if
-                                    // storeIndex &gt;= varargsData-&gt;mandatoryMinimum, but we will still
-                                    // have GetStacks in that range. So if we don't do the stores, we'll
-                                    // have degenerate IR: we'll have GetStacks of something that didn't
-                                    // have PutStacks.
-                                    // https://bugs.webkit.org/show_bug.cgi?id=147434
-                                    
</del><ins>+                        for (unsigned i = 0; i &lt; candidate-&gt;numChildren(); i++) {
+                            if (bitVector-&gt;get(i)) {
+                                Node* child = m_graph.varArgChild(candidate, i).node();
+                                ASSERT(child-&gt;op() == PhantomSpread &amp;&amp; child-&gt;child1()-&gt;op() == PhantomCreateRest);
+                                InlineCallFrame* inlineCallFrame = child-&gt;child1()-&gt;origin.semantic.inlineCallFrame;
+                                if (!inlineCallFrame || inlineCallFrame-&gt;isVarargs()) {
+                                    canConvertToStaticLoadStores = false;
+                                    break;
+                                }
+                            }
+                        }
+
+                        if (canConvertToStaticLoadStores) {
+                            unsigned argumentCountIncludingThis = 1; // |this|
+                            for (unsigned i = 0; i &lt; candidate-&gt;numChildren(); i++) {
+                                if (bitVector-&gt;get(i)) {
+                                    Node* child = m_graph.varArgChild(candidate, i).node();
+                                    ASSERT(child-&gt;op() == PhantomSpread &amp;&amp; child-&gt;child1()-&gt;op() == PhantomCreateRest);
+                                    unsigned numberOfArgumentsToSkip = child-&gt;child1()-&gt;numberOfArgumentsToSkip();
+                                    InlineCallFrame* inlineCallFrame = child-&gt;child1()-&gt;origin.semantic.inlineCallFrame;
+                                    unsigned numberOfSpreadArguments;
+                                    unsigned frameArgumentCount = inlineCallFrame-&gt;arguments.size() - 1;
+                                    if (frameArgumentCount &gt;= numberOfArgumentsToSkip)
+                                        numberOfSpreadArguments = frameArgumentCount - numberOfArgumentsToSkip;
+                                    else
+                                        numberOfSpreadArguments = 0;
+
+                                    argumentCountIncludingThis += numberOfSpreadArguments;
+                                } else
+                                    ++argumentCountIncludingThis;
+                            }
+
+                            if (argumentCountIncludingThis &lt;= varargsData-&gt;limit) {
+                                storeArgumentCountIncludingThis(argumentCountIncludingThis);
+
+                                DFG_ASSERT(m_graph, node, varargsData-&gt;limit - 1 &gt;= varargsData-&gt;mandatoryMinimum);
+                                // Define our limit to exclude &quot;this&quot;, since that's a bit easier to reason about.
+                                unsigned limit = varargsData-&gt;limit - 1;
+                                unsigned storeIndex = 0;
+                                for (unsigned i = 0; i &lt; candidate-&gt;numChildren(); i++) {
+                                    if (bitVector-&gt;get(i)) {
+                                        Node* child = m_graph.varArgChild(candidate, i).node();
+                                        ASSERT(child-&gt;op() == PhantomSpread &amp;&amp; child-&gt;child1()-&gt;op() == PhantomCreateRest);
+                                        unsigned numberOfArgumentsToSkip = child-&gt;child1()-&gt;numberOfArgumentsToSkip();
+                                        InlineCallFrame* inlineCallFrame = child-&gt;child1()-&gt;origin.semantic.inlineCallFrame;
+                                        unsigned frameArgumentCount = inlineCallFrame-&gt;arguments.size() - 1;
+                                        for (unsigned loadIndex = numberOfArgumentsToSkip; loadIndex &lt; frameArgumentCount; ++loadIndex) {
+                                            VirtualRegister reg = virtualRegisterForArgument(loadIndex + 1) + inlineCallFrame-&gt;stackOffset;
+                                            StackAccessData* data = m_graph.m_stackAccessData.add(reg, FlushedJSValue);
+                                            Node* value = insertionSet.insertNode(
+                                                nodeIndex, SpecNone, GetStack, node-&gt;origin.withExitOK(canExit),
+                                                OpInfo(data));
+                                            storeValue(value, storeIndex);
+                                            ++storeIndex;
+                                        }
+                                    } else {
+                                        Node* value = m_graph.varArgChild(candidate, i).node();
+                                        storeValue(value, storeIndex);
+                                        ++storeIndex;
+                                    }
+                                }
+
+                                RELEASE_ASSERT(storeIndex &lt;= limit);
+                                Node* undefined = nullptr;
+                                for (; storeIndex &lt; limit; ++storeIndex) {
</ins><span class="cx">                                     if (!undefined) {
</span><span class="cx">                                         undefined = insertionSet.insertConstant(
</span><span class="cx">                                             nodeIndex, node-&gt;origin.withExitOK(canExit), jsUndefined());
</span><span class="cx">                                     }
</span><del>-                                    value = undefined;
</del><ins>+                                    storeValue(undefined, storeIndex);
</ins><span class="cx">                                 }
</span><del>-                                
-                                // Now that we have a value, store it.
-                                
-                                VirtualRegister reg = varargsData-&gt;start + storeIndex;
-                                StackAccessData* data =
-                                    m_graph.m_stackAccessData.add(reg, FlushedJSValue);
-                                
-                                insertionSet.insertNode(
-                                    nodeIndex, SpecNone, MovHint, node-&gt;origin.takeValidExit(canExit),
-                                    OpInfo(reg.offset()), Edge(value));
-                                insertionSet.insertNode(
-                                    nodeIndex, SpecNone, PutStack, node-&gt;origin.withExitOK(canExit),
-                                    OpInfo(data), Edge(value));
</del><span class="cx">                             }
</span><del>-                            
</del><ins>+
</ins><span class="cx">                             node-&gt;remove();
</span><span class="cx">                             node-&gt;origin.exitOK = canExit;
</span><span class="cx">                             break;
</span><span class="cx">                         }
</span><ins>+                    } else {
+                        unsigned numberOfArgumentsToSkip = 0;
+                        if (candidate-&gt;op() == PhantomCreateRest)
+                            numberOfArgumentsToSkip = candidate-&gt;numberOfArgumentsToSkip();
+                        varargsData-&gt;offset += numberOfArgumentsToSkip;
+
+                        InlineCallFrame* inlineCallFrame = candidate-&gt;origin.semantic.inlineCallFrame;
+
+                        if (inlineCallFrame
+                            &amp;&amp; !inlineCallFrame-&gt;isVarargs()) {
+
+                            unsigned argumentCountIncludingThis = inlineCallFrame-&gt;arguments.size();
+                            if (argumentCountIncludingThis &gt; varargsData-&gt;offset)
+                                argumentCountIncludingThis -= varargsData-&gt;offset;
+                            else
+                                argumentCountIncludingThis = 1;
+                            RELEASE_ASSERT(argumentCountIncludingThis &gt;= 1);
+
+                            if (argumentCountIncludingThis &lt;= varargsData-&gt;limit) {
+                                
+                                storeArgumentCountIncludingThis(argumentCountIncludingThis);
+
+                                DFG_ASSERT(m_graph, node, varargsData-&gt;limit - 1 &gt;= varargsData-&gt;mandatoryMinimum);
+                                // Define our limit to exclude &quot;this&quot;, since that's a bit easier to reason about.
+                                unsigned limit = varargsData-&gt;limit - 1;
+                                Node* undefined = nullptr;
+                                for (unsigned storeIndex = 0; storeIndex &lt; limit; ++storeIndex) {
+                                    // First determine if we have an element we can load, and load it if
+                                    // possible.
+                                    
+                                    Node* value = nullptr;
+                                    unsigned loadIndex = storeIndex + varargsData-&gt;offset;
+
+                                    if (loadIndex + 1 &lt; inlineCallFrame-&gt;arguments.size()) {
+                                        VirtualRegister reg = virtualRegisterForArgument(loadIndex + 1) + inlineCallFrame-&gt;stackOffset;
+                                        StackAccessData* data = m_graph.m_stackAccessData.add(
+                                            reg, FlushedJSValue);
+                                        
+                                        value = insertionSet.insertNode(
+                                            nodeIndex, SpecNone, GetStack, node-&gt;origin.withExitOK(canExit),
+                                            OpInfo(data));
+                                    } else {
+                                        // FIXME: We shouldn't have to store anything if
+                                        // storeIndex &gt;= varargsData-&gt;mandatoryMinimum, but we will still
+                                        // have GetStacks in that range. So if we don't do the stores, we'll
+                                        // have degenerate IR: we'll have GetStacks of something that didn't
+                                        // have PutStacks.
+                                        // https://bugs.webkit.org/show_bug.cgi?id=147434
+                                        
+                                        if (!undefined) {
+                                            undefined = insertionSet.insertConstant(
+                                                nodeIndex, node-&gt;origin.withExitOK(canExit), jsUndefined());
+                                        }
+                                        value = undefined;
+                                    }
+                                    
+                                    // Now that we have a value, store it.
+                                    storeValue(value, storeIndex);
+                                }
+                                
+                                node-&gt;remove();
+                                node-&gt;origin.exitOK = canExit;
+                                break;
+                            }
+                        }
</ins><span class="cx">                     }
</span><del>-                    
</del><ins>+
</ins><span class="cx">                     node-&gt;setOpAndDefaultFlags(ForwardVarargs);
</span><span class="cx">                     break;
</span><span class="cx">                 }
</span><span class="lines">@@ -698,27 +929,8 @@
</span><span class="cx">                     Node* candidate = node-&gt;child3().node();
</span><span class="cx">                     if (!isEliminatedAllocation(candidate))
</span><span class="cx">                         break;
</span><del>-                    
-                    unsigned numberOfArgumentsToSkip = 0;
-                    if (candidate-&gt;op() == PhantomCreateRest)
-                        numberOfArgumentsToSkip = candidate-&gt;numberOfArgumentsToSkip();
-                    CallVarargsData* varargsData = node-&gt;callVarargsData();
-                    varargsData-&gt;firstVarArgOffset += numberOfArgumentsToSkip;
</del><span class="cx"> 
</span><del>-                    InlineCallFrame* inlineCallFrame = candidate-&gt;origin.semantic.inlineCallFrame;
-                    if (inlineCallFrame &amp;&amp; !inlineCallFrame-&gt;isVarargs()) {
-                        Vector&lt;Node*&gt; arguments;
-                        for (unsigned i = 1 + varargsData-&gt;firstVarArgOffset; i &lt; inlineCallFrame-&gt;arguments.size(); ++i) {
-                            StackAccessData* data = m_graph.m_stackAccessData.add(
-                                virtualRegisterForArgument(i) + inlineCallFrame-&gt;stackOffset,
-                                FlushedJSValue);
-                            
-                            Node* value = insertionSet.insertNode(
-                                nodeIndex, SpecNone, GetStack, node-&gt;origin, OpInfo(data));
-                            
-                            arguments.append(value);
-                        }
-                        
</del><ins>+                    auto convertToStaticArgumentCountCall = [&amp;] (const Vector&lt;Node*&gt;&amp; arguments) {
</ins><span class="cx">                         unsigned firstChild = m_graph.m_varArgChildren.size();
</span><span class="cx">                         m_graph.m_varArgChildren.append(node-&gt;child1());
</span><span class="cx">                         m_graph.m_varArgChildren.append(node-&gt;child2());
</span><span class="lines">@@ -743,25 +955,95 @@
</span><span class="cx">                         node-&gt;children = AdjacencyList(
</span><span class="cx">                             AdjacencyList::Variable,
</span><span class="cx">                             firstChild, m_graph.m_varArgChildren.size() - firstChild);
</span><del>-                        break;
-                    }
</del><ins>+                    };
+
+                    auto convertToForwardsCall = [&amp;] () {
+                        switch (node-&gt;op()) {
+                        case CallVarargs:
+                            node-&gt;setOpAndDefaultFlags(CallForwardVarargs);
+                            break;
+                        case ConstructVarargs:
+                            node-&gt;setOpAndDefaultFlags(ConstructForwardVarargs);
+                            break;
+                        case TailCallVarargs:
+                            node-&gt;setOpAndDefaultFlags(TailCallForwardVarargs);
+                            break;
+                        case TailCallVarargsInlinedCaller:
+                            node-&gt;setOpAndDefaultFlags(TailCallForwardVarargsInlinedCaller);
+                            break;
+                        default:
+                            RELEASE_ASSERT_NOT_REACHED();
+                        }
+                    };
</ins><span class="cx">                     
</span><del>-                    switch (node-&gt;op()) {
-                    case CallVarargs:
-                        node-&gt;setOpAndDefaultFlags(CallForwardVarargs);
-                        break;
-                    case ConstructVarargs:
-                        node-&gt;setOpAndDefaultFlags(ConstructForwardVarargs);
-                        break;
-                    case TailCallVarargs:
-                        node-&gt;setOpAndDefaultFlags(TailCallForwardVarargs);
-                        break;
-                    case TailCallVarargsInlinedCaller:
-                        node-&gt;setOpAndDefaultFlags(TailCallForwardVarargsInlinedCaller);
-                        break;
-                    default:
-                        RELEASE_ASSERT_NOT_REACHED();
</del><ins>+                    if (candidate-&gt;op() == PhantomNewArrayWithSpread) {
+                        bool canTransformToStaticArgumentCountCall = true;
+                        BitVector* bitVector = candidate-&gt;bitVector();
+                        for (unsigned i = 0; i &lt; candidate-&gt;numChildren(); i++) {
+                            if (bitVector-&gt;get(i)) {
+                                Node* node = m_graph.varArgChild(candidate, i).node();
+                                ASSERT(node-&gt;op() == PhantomSpread);
+                                ASSERT(node-&gt;child1()-&gt;op() == PhantomCreateRest);
+                                InlineCallFrame* inlineCallFrame = node-&gt;child1()-&gt;origin.semantic.inlineCallFrame;
+                                if (!inlineCallFrame || inlineCallFrame-&gt;isVarargs()) {
+                                    canTransformToStaticArgumentCountCall = false;
+                                    break;
+                                }
+                            }
+                        }
+
+                        if (canTransformToStaticArgumentCountCall) {
+                            Vector&lt;Node*&gt; arguments;
+                            for (unsigned i = 0; i &lt; candidate-&gt;numChildren(); i++) {
+                                Node* child = m_graph.varArgChild(candidate, i).node();
+                                if (bitVector-&gt;get(i)) {
+                                    ASSERT(child-&gt;op() == PhantomSpread);
+                                    ASSERT(child-&gt;child1()-&gt;op() == PhantomCreateRest);
+                                    InlineCallFrame* inlineCallFrame = child-&gt;child1()-&gt;origin.semantic.inlineCallFrame;
+                                    unsigned numberOfArgumentsToSkip = child-&gt;child1()-&gt;numberOfArgumentsToSkip();
+                                    for (unsigned i = 1 + numberOfArgumentsToSkip; i &lt; inlineCallFrame-&gt;arguments.size(); ++i) {
+                                        StackAccessData* data = m_graph.m_stackAccessData.add(
+                                            virtualRegisterForArgument(i) + inlineCallFrame-&gt;stackOffset,
+                                            FlushedJSValue);
+                                        
+                                        Node* value = insertionSet.insertNode(
+                                            nodeIndex, SpecNone, GetStack, node-&gt;origin, OpInfo(data));
+                                        
+                                        arguments.append(value);
+                                    }
+                                } else
+                                    arguments.append(child);
+                            }
+
+                            convertToStaticArgumentCountCall(arguments);
+                        } else
+                            convertToForwardsCall();
+                    } else {
+                        unsigned numberOfArgumentsToSkip = 0;
+                        if (candidate-&gt;op() == PhantomCreateRest)
+                            numberOfArgumentsToSkip = candidate-&gt;numberOfArgumentsToSkip();
+                        CallVarargsData* varargsData = node-&gt;callVarargsData();
+                        varargsData-&gt;firstVarArgOffset += numberOfArgumentsToSkip;
+
+                        InlineCallFrame* inlineCallFrame = candidate-&gt;origin.semantic.inlineCallFrame;
+                        if (inlineCallFrame &amp;&amp; !inlineCallFrame-&gt;isVarargs()) {
+                            Vector&lt;Node*&gt; arguments;
+                            for (unsigned i = 1 + varargsData-&gt;firstVarArgOffset; i &lt; inlineCallFrame-&gt;arguments.size(); ++i) {
+                                StackAccessData* data = m_graph.m_stackAccessData.add(
+                                    virtualRegisterForArgument(i) + inlineCallFrame-&gt;stackOffset,
+                                    FlushedJSValue);
+                                
+                                Node* value = insertionSet.insertNode(
+                                    nodeIndex, SpecNone, GetStack, node-&gt;origin, OpInfo(data));
+                                
+                                arguments.append(value);
+                            }
+                            
+                            convertToStaticArgumentCountCall(arguments);
+                        } else
+                            convertToForwardsCall();
</ins><span class="cx">                     }
</span><ins>+
</ins><span class="cx">                     break;
</span><span class="cx">                 }
</span><span class="cx">                     
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGClobberizeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGClobberize.h (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGClobberize.h        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/dfg/DFGClobberize.h        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -478,6 +478,8 @@
</span><span class="cx">         write(HeapObjectCount);
</span><span class="cx">         return;
</span><span class="cx"> 
</span><ins>+    case PhantomSpread:
+    case PhantomNewArrayWithSpread:
</ins><span class="cx">     case PhantomCreateRest:
</span><span class="cx">         // Even though it's phantom, it still has the property that one can't be replaced with another.
</span><span class="cx">         read(HeapObjectCount);
</span><span class="lines">@@ -1130,6 +1132,13 @@
</span><span class="cx">     case NewArrayWithSpread: {
</span><span class="cx">         // This also reads from JSFixedArray's data store, but we don't have any way of describing that yet.
</span><span class="cx">         read(HeapObjectCount);
</span><ins>+        for (unsigned i = 0; i &lt; node-&gt;numChildren(); i++) {
+            Node* child = graph.varArgChild(node, i).node();
+            if (child-&gt;op() == PhantomSpread) {
+                read(Stack);
+                break;
+            }
+        }
</ins><span class="cx">         write(HeapObjectCount);
</span><span class="cx">         return;
</span><span class="cx">     }
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGDoesGCcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGDoesGC.cpp (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGDoesGC.cpp        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/dfg/DFGDoesGC.cpp        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -248,6 +248,8 @@
</span><span class="cx">     case PhantomCreateActivation:
</span><span class="cx">     case PhantomDirectArguments:
</span><span class="cx">     case PhantomCreateRest:
</span><ins>+    case PhantomNewArrayWithSpread:
+    case PhantomSpread:
</ins><span class="cx">     case PhantomClonedArguments:
</span><span class="cx">     case GetMyArgumentByVal:
</span><span class="cx">     case GetMyArgumentByValOutOfBounds:
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGFixupPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -1439,6 +1439,8 @@
</span><span class="cx">         case PhantomCreateActivation:
</span><span class="cx">         case PhantomDirectArguments:
</span><span class="cx">         case PhantomCreateRest:
</span><ins>+        case PhantomSpread:
+        case PhantomNewArrayWithSpread:
</ins><span class="cx">         case PhantomClonedArguments:
</span><span class="cx">         case GetMyArgumentByVal:
</span><span class="cx">         case GetMyArgumentByValOutOfBounds:
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGForAllKillsh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGForAllKills.h (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGForAllKills.h        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/dfg/DFGForAllKills.h        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -153,7 +153,7 @@
</span><span class="cx">     for (Node* node : combinedLiveness.liveAtTail[block])
</span><span class="cx">         functor(block-&gt;size(), node);
</span><span class="cx">     
</span><del>-    LocalOSRAvailabilityCalculator localAvailability;
</del><ins>+    LocalOSRAvailabilityCalculator localAvailability(graph);
</ins><span class="cx">     localAvailability.beginBlock(block);
</span><span class="cx">     // Start at the second node, because the functor is expected to only inspect nodes from the start of
</span><span class="cx">     // the block up to nodeIndex (exclusive), so if nodeIndex is zero then the functor has nothing to do.
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGNodeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGNode.h (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGNode.h        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/dfg/DFGNode.h        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -459,7 +459,6 @@
</span><span class="cx">             
</span><span class="cx">         case PhantomDirectArguments:
</span><span class="cx">         case PhantomClonedArguments:
</span><del>-        case PhantomCreateRest:
</del><span class="cx">             // These pretend to be the empty value constant for the benefit of the DFG backend, which
</span><span class="cx">             // otherwise wouldn't take kindly to a node that doesn't compute a value.
</span><span class="cx">             return true;
</span><span class="lines">@@ -473,7 +472,7 @@
</span><span class="cx">     {
</span><span class="cx">         ASSERT(hasConstant());
</span><span class="cx">         
</span><del>-        if (op() == PhantomDirectArguments || op() == PhantomClonedArguments || op() == PhantomCreateRest) {
</del><ins>+        if (op() == PhantomDirectArguments || op() == PhantomClonedArguments) {
</ins><span class="cx">             // These pretend to be the empty value constant for the benefit of the DFG backend, which
</span><span class="cx">             // otherwise wouldn't take kindly to a node that doesn't compute a value.
</span><span class="cx">             return FrozenValue::emptySingleton();
</span><span class="lines">@@ -1073,7 +1072,7 @@
</span><span class="cx"> 
</span><span class="cx">     BitVector* bitVector()
</span><span class="cx">     {
</span><del>-        ASSERT(op() == NewArrayWithSpread);
</del><ins>+        ASSERT(op() == NewArrayWithSpread || op() == PhantomNewArrayWithSpread);
</ins><span class="cx">         return m_opInfo.as&lt;BitVector*&gt;();
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -1769,6 +1768,8 @@
</span><span class="cx">         case PhantomNewObject:
</span><span class="cx">         case PhantomDirectArguments:
</span><span class="cx">         case PhantomCreateRest:
</span><ins>+        case PhantomSpread:
+        case PhantomNewArrayWithSpread:
</ins><span class="cx">         case PhantomClonedArguments:
</span><span class="cx">         case PhantomNewFunction:
</span><span class="cx">         case PhantomNewGeneratorFunction:
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGNodeTypeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGNodeType.h (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGNodeType.h        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/dfg/DFGNodeType.h        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -345,6 +345,8 @@
</span><span class="cx">     macro(CreateDirectArguments, NodeResultJS) \
</span><span class="cx">     macro(PhantomDirectArguments, NodeResultJS | NodeMustGenerate) \
</span><span class="cx">     macro(PhantomCreateRest, NodeResultJS | NodeMustGenerate) \
</span><ins>+    macro(PhantomSpread, NodeResultJS | NodeMustGenerate) \
+    macro(PhantomNewArrayWithSpread, NodeResultJS | NodeMustGenerate | NodeHasVarArgs) \
</ins><span class="cx">     macro(CreateScopedArguments, NodeResultJS) \
</span><span class="cx">     macro(CreateClonedArguments, NodeResultJS) \
</span><span class="cx">     macro(PhantomClonedArguments, NodeResultJS | NodeMustGenerate) \
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGOSRAvailabilityAnalysisPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGOSRAvailabilityAnalysisPhase.cpp (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGOSRAvailabilityAnalysisPhase.cpp        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/dfg/DFGOSRAvailabilityAnalysisPhase.cpp        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -66,7 +66,7 @@
</span><span class="cx"> 
</span><span class="cx">         // This could be made more efficient by processing blocks in reverse postorder.
</span><span class="cx">         
</span><del>-        LocalOSRAvailabilityCalculator calculator;
</del><ins>+        LocalOSRAvailabilityCalculator calculator(m_graph);
</ins><span class="cx">         bool changed;
</span><span class="cx">         do {
</span><span class="cx">             changed = false;
</span><span class="lines">@@ -105,7 +105,8 @@
</span><span class="cx">     return runPhase&lt;OSRAvailabilityAnalysisPhase&gt;(graph);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-LocalOSRAvailabilityCalculator::LocalOSRAvailabilityCalculator()
</del><ins>+LocalOSRAvailabilityCalculator::LocalOSRAvailabilityCalculator(Graph&amp; graph)
+    : m_graph(graph)
</ins><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -164,7 +165,7 @@
</span><span class="cx">         }
</span><span class="cx">         break;
</span><span class="cx">     }
</span><del>-        
</del><ins>+    
</ins><span class="cx">     case PhantomCreateRest:
</span><span class="cx">     case PhantomDirectArguments:
</span><span class="cx">     case PhantomClonedArguments: {
</span><span class="lines">@@ -208,6 +209,17 @@
</span><span class="cx">             Availability(node-&gt;child2().node()));
</span><span class="cx">         break;
</span><span class="cx">     }
</span><ins>+
+    case PhantomSpread:
+        m_availability.m_heap.set(PromotedHeapLocation(SpreadPLoc, node), Availability(node-&gt;child1().node()));
+        break;
+
+    case PhantomNewArrayWithSpread:
+        for (unsigned i = 0; i &lt; node-&gt;numChildren(); i++) {
+            Node* child = m_graph.varArgChild(node, i).node();
+            m_availability.m_heap.set(PromotedHeapLocation(NewArrayWithSpreadArgumentPLoc, node, i), Availability(child));
+        }
+        break;
</ins><span class="cx">         
</span><span class="cx">     default:
</span><span class="cx">         break;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGOSRAvailabilityAnalysisPhaseh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGOSRAvailabilityAnalysisPhase.h (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGOSRAvailabilityAnalysisPhase.h        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/dfg/DFGOSRAvailabilityAnalysisPhase.h        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -46,7 +46,7 @@
</span><span class="cx"> // having run the availability analysis.
</span><span class="cx"> class LocalOSRAvailabilityCalculator {
</span><span class="cx"> public:
</span><del>-    LocalOSRAvailabilityCalculator();
</del><ins>+    LocalOSRAvailabilityCalculator(Graph&amp;);
</ins><span class="cx">     ~LocalOSRAvailabilityCalculator();
</span><span class="cx">     
</span><span class="cx">     void beginBlock(BasicBlock*);
</span><span class="lines">@@ -54,6 +54,7 @@
</span><span class="cx">     void executeNode(Node*);
</span><span class="cx">     
</span><span class="cx">     AvailabilityMap m_availability;
</span><ins>+    Graph&amp; m_graph;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } } // namespace JSC::DFG
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGObjectAllocationSinkingPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -1644,7 +1644,7 @@
</span><span class="cx"> 
</span><span class="cx">         // Place Phis in the right places, replace all uses of any load with the appropriate
</span><span class="cx">         // value, and create the materialization nodes.
</span><del>-        LocalOSRAvailabilityCalculator availabilityCalculator;
</del><ins>+        LocalOSRAvailabilityCalculator availabilityCalculator(m_graph);
</ins><span class="cx">         m_graph.clearReplacements();
</span><span class="cx">         for (BasicBlock* block : m_graph.blocksInPreOrder()) {
</span><span class="cx">             m_heap = m_heapAtHead[block];
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGPreciseLocalClobberizeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGPreciseLocalClobberize.h (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGPreciseLocalClobberize.h        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/dfg/DFGPreciseLocalClobberize.h        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -106,35 +106,13 @@
</span><span class="cx">     
</span><span class="cx">     void readTop()
</span><span class="cx">     {
</span><del>-        switch (m_node-&gt;op()) {
-        case GetMyArgumentByVal:
-        case GetMyArgumentByValOutOfBounds:
-        case ForwardVarargs:
-        case CallForwardVarargs:
-        case ConstructForwardVarargs:
-        case TailCallForwardVarargs:
-        case TailCallForwardVarargsInlinedCaller: {
-
-            InlineCallFrame* inlineCallFrame;
-            if (m_node-&gt;hasArgumentsChild() &amp;&amp; m_node-&gt;argumentsChild())
-                inlineCallFrame = m_node-&gt;argumentsChild()-&gt;origin.semantic.inlineCallFrame;
-            else
-                inlineCallFrame = m_node-&gt;origin.semantic.inlineCallFrame;
-
-            unsigned numberOfArgumentsToSkip = 0;
-            if (m_node-&gt;op() == GetMyArgumentByVal || m_node-&gt;op() == GetMyArgumentByValOutOfBounds) {
-                // The value of numberOfArgumentsToSkip guarantees that GetMyArgumentByVal* will never
-                // read any arguments below the number of arguments to skip. For example, if numberOfArgumentsToSkip is 2,
-                // we will never read argument 0 or argument 1.
-                numberOfArgumentsToSkip = m_node-&gt;numberOfArgumentsToSkip();
-            }
-
</del><ins>+        auto readFrame = [&amp;] (InlineCallFrame* inlineCallFrame, unsigned numberOfArgumentsToSkip) {
</ins><span class="cx">             if (!inlineCallFrame) {
</span><span class="cx">                 // Read the outermost arguments and argument count.
</span><span class="cx">                 for (unsigned i = 1 + numberOfArgumentsToSkip; i &lt; static_cast&lt;unsigned&gt;(m_graph.m_codeBlock-&gt;numParameters()); i++)
</span><span class="cx">                     m_read(virtualRegisterForArgument(i));
</span><span class="cx">                 m_read(VirtualRegister(CallFrameSlot::argumentCount));
</span><del>-                break;
</del><ins>+                return;
</ins><span class="cx">             }
</span><span class="cx">             
</span><span class="cx">             for (unsigned i = 1 + numberOfArgumentsToSkip; i &lt; inlineCallFrame-&gt;arguments.size(); i++)
</span><span class="lines">@@ -141,8 +119,64 @@
</span><span class="cx">                 m_read(VirtualRegister(inlineCallFrame-&gt;stackOffset + virtualRegisterForArgument(i).offset()));
</span><span class="cx">             if (inlineCallFrame-&gt;isVarargs())
</span><span class="cx">                 m_read(VirtualRegister(inlineCallFrame-&gt;stackOffset + CallFrameSlot::argumentCount));
</span><ins>+        };
+
+        auto readNewArrayWithSpreadNode = [&amp;] (Node* arrayWithSpread) {
+            ASSERT(arrayWithSpread-&gt;op() == NewArrayWithSpread || arrayWithSpread-&gt;op() == PhantomNewArrayWithSpread);
+            BitVector* bitVector = arrayWithSpread-&gt;bitVector();
+            for (unsigned i = 0; i &lt; arrayWithSpread-&gt;numChildren(); i++) {
+                if (bitVector-&gt;get(i)) {
+                    Node* child = m_graph.varArgChild(arrayWithSpread, i).node();
+                    if (child-&gt;op() == PhantomSpread) {
+                        ASSERT(child-&gt;child1()-&gt;op() == PhantomCreateRest);
+                        InlineCallFrame* inlineCallFrame = child-&gt;child1()-&gt;origin.semantic.inlineCallFrame;
+                        unsigned numberOfArgumentsToSkip = child-&gt;child1()-&gt;numberOfArgumentsToSkip();
+                        readFrame(inlineCallFrame, numberOfArgumentsToSkip);
+                    }
+                }
+            }
+        };
+
+        bool isForwardingNode = false;
+        switch (m_node-&gt;op()) {
+        case ForwardVarargs:
+        case CallForwardVarargs:
+        case ConstructForwardVarargs:
+        case TailCallForwardVarargs:
+        case TailCallForwardVarargsInlinedCaller:
+            isForwardingNode = true;
+            FALLTHROUGH;
+        case GetMyArgumentByVal:
+        case GetMyArgumentByValOutOfBounds: {
+
+            if (isForwardingNode &amp;&amp; m_node-&gt;hasArgumentsChild() &amp;&amp; m_node-&gt;argumentsChild() &amp;&amp; m_node-&gt;argumentsChild()-&gt;op() == PhantomNewArrayWithSpread) {
+                Node* arrayWithSpread = m_node-&gt;argumentsChild().node();
+                readNewArrayWithSpreadNode(arrayWithSpread);
+            } else {
+                InlineCallFrame* inlineCallFrame;
+                if (m_node-&gt;hasArgumentsChild() &amp;&amp; m_node-&gt;argumentsChild())
+                    inlineCallFrame = m_node-&gt;argumentsChild()-&gt;origin.semantic.inlineCallFrame;
+                else
+                    inlineCallFrame = m_node-&gt;origin.semantic.inlineCallFrame;
+
+                unsigned numberOfArgumentsToSkip = 0;
+                if (m_node-&gt;op() == GetMyArgumentByVal || m_node-&gt;op() == GetMyArgumentByValOutOfBounds) {
+                    // The value of numberOfArgumentsToSkip guarantees that GetMyArgumentByVal* will never
+                    // read any arguments below the number of arguments to skip. For example, if numberOfArgumentsToSkip is 2,
+                    // we will never read argument 0 or argument 1.
+                    numberOfArgumentsToSkip = m_node-&gt;numberOfArgumentsToSkip();
+                }
+
+                readFrame(inlineCallFrame, numberOfArgumentsToSkip);
+            }
+
</ins><span class="cx">             break;
</span><span class="cx">         }
</span><ins>+        
+        case NewArrayWithSpread: {
+            readNewArrayWithSpreadNode(m_node);
+            break;
+        }
</ins><span class="cx"> 
</span><span class="cx">         case GetArgument: {
</span><span class="cx">             InlineCallFrame* inlineCallFrame = m_node-&gt;origin.semantic.inlineCallFrame;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGPredictionPropagationPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -1012,6 +1012,8 @@
</span><span class="cx">         case PhantomCreateActivation:
</span><span class="cx">         case PhantomDirectArguments:
</span><span class="cx">         case PhantomCreateRest:
</span><ins>+        case PhantomSpread:
+        case PhantomNewArrayWithSpread:
</ins><span class="cx">         case PhantomClonedArguments:
</span><span class="cx">         case GetMyArgumentByVal:
</span><span class="cx">         case GetMyArgumentByValOutOfBounds:
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGPromotedHeapLocationcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGPromotedHeapLocation.cpp (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGPromotedHeapLocation.cpp        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/dfg/DFGPromotedHeapLocation.cpp        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -114,6 +114,14 @@
</span><span class="cx">     case VectorLengthPLoc:
</span><span class="cx">         out.print(&quot;VectorLengthPLoc&quot;);
</span><span class="cx">         return;
</span><ins>+
+    case SpreadPLoc:
+        out.print(&quot;SpreadPLoc&quot;);
+        return;
+
+    case NewArrayWithSpreadArgumentPLoc:
+        out.print(&quot;NewArrayWithSpreadArgumentPLoc&quot;);
+        return;
</ins><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     RELEASE_ASSERT_NOT_REACHED();
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGPromotedHeapLocationh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGPromotedHeapLocation.h (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGPromotedHeapLocation.h        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/dfg/DFGPromotedHeapLocation.h        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -61,7 +61,9 @@
</span><span class="cx">     NamedPropertyPLoc,
</span><span class="cx">     PublicLengthPLoc,
</span><span class="cx">     StructurePLoc,
</span><del>-    VectorLengthPLoc
</del><ins>+    VectorLengthPLoc,
+    SpreadPLoc,
+    NewArrayWithSpreadArgumentPLoc,
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> class PromotedLocationDescriptor {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGSafeToExecuteh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGSafeToExecute.h (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGSafeToExecute.h        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/dfg/DFGSafeToExecute.h        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -360,6 +360,8 @@
</span><span class="cx">     case MaterializeCreateActivation:
</span><span class="cx">     case PhantomDirectArguments:
</span><span class="cx">     case PhantomCreateRest:
</span><ins>+    case PhantomSpread:
+    case PhantomNewArrayWithSpread:
</ins><span class="cx">     case PhantomClonedArguments:
</span><span class="cx">     case GetMyArgumentByVal:
</span><span class="cx">     case GetMyArgumentByValOutOfBounds:
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGSpeculativeJIT32_64cpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -5616,6 +5616,8 @@
</span><span class="cx">     case GetMyArgumentByVal:
</span><span class="cx">     case GetMyArgumentByValOutOfBounds:
</span><span class="cx">     case PhantomCreateRest:
</span><ins>+    case PhantomSpread:
+    case PhantomNewArrayWithSpread:
</ins><span class="cx">         DFG_CRASH(m_jit.graph(), node, &quot;unexpected node in DFG backend&quot;);
</span><span class="cx">         break;
</span><span class="cx">     }
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGSpeculativeJIT64cpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -5840,6 +5840,8 @@
</span><span class="cx">     case KillStack:
</span><span class="cx">     case GetStack:
</span><span class="cx">     case PhantomCreateRest:
</span><ins>+    case PhantomSpread:
+    case PhantomNewArrayWithSpread:
</ins><span class="cx">         DFG_CRASH(m_jit.graph(), node, &quot;Unexpected node&quot;);
</span><span class="cx">         break;
</span><span class="cx">     }
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGValidatecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGValidate.cpp (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGValidate.cpp        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/dfg/DFGValidate.cpp        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -693,6 +693,39 @@
</span><span class="cx">                     VALIDATE((node), node-&gt;child1()-&gt;isPhantomAllocation());
</span><span class="cx">                     break;
</span><span class="cx"> 
</span><ins>+                case PhantomSpread:
+                    VALIDATE((node), m_graph.m_form == SSA);
+                    // We currently only support PhantomSpread over PhantomCreateRest.
+                    VALIDATE((node), node-&gt;child1()-&gt;op() == PhantomCreateRest);
+                    break;
+
+                case PhantomNewArrayWithSpread: {
+                    VALIDATE((node), m_graph.m_form == SSA);
+                    BitVector* bitVector = node-&gt;bitVector();
+                    for (unsigned i = 0; i &lt; node-&gt;numChildren(); i++) {
+                        Node* child = m_graph.varArgChild(node, i).node();
+                        if (bitVector-&gt;get(i)) {
+                            // We currently only support PhantomSpread over PhantomCreateRest.
+                            VALIDATE((node), child-&gt;op() == PhantomSpread);
+                        } else
+                            VALIDATE((node), !child-&gt;isPhantomAllocation());
+                    }
+                    break;
+                }
+
+                case NewArrayWithSpread: {
+                    BitVector* bitVector = node-&gt;bitVector();
+                    for (unsigned i = 0; i &lt; node-&gt;numChildren(); i++) {
+                        Node* child = m_graph.varArgChild(node, i).node();
+                        if (child-&gt;isPhantomAllocation()) {
+                            VALIDATE((node), bitVector-&gt;get(i));
+                            VALIDATE((node), m_graph.m_form == SSA);
+                            VALIDATE((node), child-&gt;op() == PhantomSpread);
+                        }
+                    }
+                    break;
+                }
+
</ins><span class="cx">                 default:
</span><span class="cx">                     m_graph.doToChildren(
</span><span class="cx">                         node,
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLCapabilitiescpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -235,6 +235,8 @@
</span><span class="cx">     case MaterializeCreateActivation:
</span><span class="cx">     case PhantomDirectArguments:
</span><span class="cx">     case PhantomCreateRest:
</span><ins>+    case PhantomSpread:
+    case PhantomNewArrayWithSpread:
</ins><span class="cx">     case PhantomClonedArguments:
</span><span class="cx">     case GetMyArgumentByVal:
</span><span class="cx">     case GetMyArgumentByValOutOfBounds:
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLLowerDFGToB3cpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -134,6 +134,7 @@
</span><span class="cx">         , m_ftlState(state)
</span><span class="cx">         , m_out(state)
</span><span class="cx">         , m_proc(*state.proc)
</span><ins>+        , m_availabilityCalculator(m_graph)
</ins><span class="cx">         , m_state(state.graph)
</span><span class="cx">         , m_interpreter(state.graph, m_state)
</span><span class="cx">     {
</span><span class="lines">@@ -1089,6 +1090,8 @@
</span><span class="cx">         case PhantomCreateActivation:
</span><span class="cx">         case PhantomDirectArguments:
</span><span class="cx">         case PhantomCreateRest:
</span><ins>+        case PhantomSpread:
+        case PhantomNewArrayWithSpread:
</ins><span class="cx">         case PhantomClonedArguments:
</span><span class="cx">         case PutHint:
</span><span class="cx">         case BottomValue:
</span><span class="lines">@@ -4331,6 +4334,8 @@
</span><span class="cx">         if (m_graph.isWatchingHavingABadTimeWatchpoint(m_node)) {
</span><span class="cx">             unsigned startLength = 0;
</span><span class="cx">             BitVector* bitVector = m_node-&gt;bitVector();
</span><ins>+            HashMap&lt;InlineCallFrame*, LValue, WTF::DefaultHash&lt;InlineCallFrame*&gt;::Hash, WTF::NullableHashTraits&lt;InlineCallFrame*&gt;&gt; cachedSpreadLengths;
+
</ins><span class="cx">             for (unsigned i = 0; i &lt; m_node-&gt;numChildren(); ++i) {
</span><span class="cx">                 if (!bitVector-&gt;get(i))
</span><span class="cx">                     ++startLength;
</span><span class="lines">@@ -4341,8 +4346,18 @@
</span><span class="cx">             for (unsigned i = 0; i &lt; m_node-&gt;numChildren(); ++i) {
</span><span class="cx">                 if (bitVector-&gt;get(i)) {
</span><span class="cx">                     Edge use = m_graph.varArgChild(m_node, i);
</span><del>-                    LValue fixedArray = lowCell(use);
-                    length = m_out.add(length, m_out.load32(fixedArray, m_heaps.JSFixedArray_size));
</del><ins>+                    if (use-&gt;op() == PhantomSpread) {
+                        RELEASE_ASSERT(use-&gt;child1()-&gt;op() == PhantomCreateRest);
+                        InlineCallFrame* inlineCallFrame = use-&gt;child1()-&gt;origin.semantic.inlineCallFrame;
+                        unsigned numberOfArgumentsToSkip = use-&gt;child1()-&gt;numberOfArgumentsToSkip();
+                        LValue spreadLength = cachedSpreadLengths.ensure(inlineCallFrame, [&amp;] () {
+                            return getSpreadLengthFromInlineCallFrame(inlineCallFrame, numberOfArgumentsToSkip);
+                        }).iterator-&gt;value;
+                        length = m_out.add(length, spreadLength);
+                    } else {
+                        LValue fixedArray = lowCell(use);
+                        length = m_out.add(length, m_out.load32(fixedArray, m_heaps.JSFixedArray_size));
+                    }
</ins><span class="cx">                 }
</span><span class="cx">             }
</span><span class="cx"> 
</span><span class="lines">@@ -4355,42 +4370,84 @@
</span><span class="cx">             for (unsigned i = 0; i &lt; m_node-&gt;numChildren(); ++i) {
</span><span class="cx">                 Edge use = m_graph.varArgChild(m_node, i);
</span><span class="cx">                 if (bitVector-&gt;get(i)) {
</span><del>-                    LBasicBlock loopStart = m_out.newBlock();
-                    LBasicBlock continuation = m_out.newBlock();
</del><ins>+                    if (use-&gt;op() == PhantomSpread) {
+                        RELEASE_ASSERT(use-&gt;child1()-&gt;op() == PhantomCreateRest);
+                        InlineCallFrame* inlineCallFrame = use-&gt;child1()-&gt;origin.semantic.inlineCallFrame;
+                        unsigned numberOfArgumentsToSkip = use-&gt;child1()-&gt;numberOfArgumentsToSkip();
</ins><span class="cx"> 
</span><del>-                    LValue fixedArray = lowCell(use);
</del><ins>+                        LValue length = m_out.zeroExtPtr(cachedSpreadLengths.get(inlineCallFrame));
+                        LValue sourceStart = getArgumentsStart(inlineCallFrame, numberOfArgumentsToSkip);
</ins><span class="cx"> 
</span><del>-                    ValueFromBlock fixedIndexStart = m_out.anchor(m_out.constIntPtr(0));
-                    ValueFromBlock arrayIndexStart = m_out.anchor(index);
-                    ValueFromBlock arrayIndexStartForFinish = m_out.anchor(index);
</del><ins>+                        LBasicBlock loopStart = m_out.newBlock();
+                        LBasicBlock continuation = m_out.newBlock();
</ins><span class="cx"> 
</span><del>-                    LValue fixedArraySize = m_out.zeroExtPtr(m_out.load32(fixedArray, m_heaps.JSFixedArray_size));
</del><ins>+                        ValueFromBlock loadIndexStart = m_out.anchor(m_out.constIntPtr(0));
+                        ValueFromBlock arrayIndexStart = m_out.anchor(index);
+                        ValueFromBlock arrayIndexStartForFinish = m_out.anchor(index);
</ins><span class="cx"> 
</span><del>-                    m_out.branch(
-                        m_out.isZero64(fixedArraySize),
-                        unsure(continuation), unsure(loopStart));
</del><ins>+                        m_out.branch(
+                            m_out.isZero64(length),
+                            unsure(continuation), unsure(loopStart));
</ins><span class="cx"> 
</span><del>-                    LBasicBlock lastNext = m_out.appendTo(loopStart, continuation);
</del><ins>+                        LBasicBlock lastNext = m_out.appendTo(loopStart, continuation);
</ins><span class="cx"> 
</span><del>-                    LValue arrayIndex = m_out.phi(pointerType(), arrayIndexStart);
-                    LValue fixedArrayIndex = m_out.phi(pointerType(), fixedIndexStart);
</del><ins>+                        LValue arrayIndex = m_out.phi(pointerType(), arrayIndexStart);
+                        LValue loadIndex = m_out.phi(pointerType(), loadIndexStart);
</ins><span class="cx"> 
</span><del>-                    LValue item = m_out.load64(m_out.baseIndex(m_heaps.JSFixedArray_buffer, fixedArray, fixedArrayIndex));
-                    m_out.store64(item, m_out.baseIndex(m_heaps.indexedContiguousProperties, storage, arrayIndex));
</del><ins>+                        LValue item = m_out.load64(m_out.baseIndex(m_heaps.variables, sourceStart, loadIndex));
+                        m_out.store64(item, m_out.baseIndex(m_heaps.indexedContiguousProperties, storage, arrayIndex));
</ins><span class="cx"> 
</span><del>-                    LValue nextArrayIndex = m_out.add(arrayIndex, m_out.constIntPtr(1));
-                    LValue nextFixedArrayIndex = m_out.add(fixedArrayIndex, m_out.constIntPtr(1));
-                    ValueFromBlock arrayIndexLoopForFinish = m_out.anchor(nextArrayIndex);
</del><ins>+                        LValue nextArrayIndex = m_out.add(arrayIndex, m_out.constIntPtr(1));
+                        LValue nextLoadIndex = m_out.add(loadIndex, m_out.constIntPtr(1));
+                        ValueFromBlock arrayIndexLoopForFinish = m_out.anchor(nextArrayIndex);
</ins><span class="cx"> 
</span><del>-                    m_out.addIncomingToPhi(fixedArrayIndex, m_out.anchor(nextFixedArrayIndex));
-                    m_out.addIncomingToPhi(arrayIndex, m_out.anchor(nextArrayIndex));
</del><ins>+                        m_out.addIncomingToPhi(loadIndex, m_out.anchor(nextLoadIndex));
+                        m_out.addIncomingToPhi(arrayIndex, m_out.anchor(nextArrayIndex));
</ins><span class="cx"> 
</span><del>-                    m_out.branch(
-                        m_out.below(nextFixedArrayIndex, fixedArraySize),
-                        unsure(loopStart), unsure(continuation));
</del><ins>+                        m_out.branch(
+                            m_out.below(nextLoadIndex, length),
+                            unsure(loopStart), unsure(continuation));
</ins><span class="cx"> 
</span><del>-                    m_out.appendTo(continuation, lastNext);
-                    index = m_out.phi(pointerType(), arrayIndexStartForFinish, arrayIndexLoopForFinish);
</del><ins>+                        m_out.appendTo(continuation, lastNext);
+                        index = m_out.phi(pointerType(), arrayIndexStartForFinish, arrayIndexLoopForFinish);
+                    } else {
+                        LBasicBlock loopStart = m_out.newBlock();
+                        LBasicBlock continuation = m_out.newBlock();
+
+                        LValue fixedArray = lowCell(use);
+
+                        ValueFromBlock fixedIndexStart = m_out.anchor(m_out.constIntPtr(0));
+                        ValueFromBlock arrayIndexStart = m_out.anchor(index);
+                        ValueFromBlock arrayIndexStartForFinish = m_out.anchor(index);
+
+                        LValue fixedArraySize = m_out.zeroExtPtr(m_out.load32(fixedArray, m_heaps.JSFixedArray_size));
+
+                        m_out.branch(
+                            m_out.isZero64(fixedArraySize),
+                            unsure(continuation), unsure(loopStart));
+
+                        LBasicBlock lastNext = m_out.appendTo(loopStart, continuation);
+
+                        LValue arrayIndex = m_out.phi(pointerType(), arrayIndexStart);
+                        LValue fixedArrayIndex = m_out.phi(pointerType(), fixedIndexStart);
+
+                        LValue item = m_out.load64(m_out.baseIndex(m_heaps.JSFixedArray_buffer, fixedArray, fixedArrayIndex));
+                        m_out.store64(item, m_out.baseIndex(m_heaps.indexedContiguousProperties, storage, arrayIndex));
+
+                        LValue nextArrayIndex = m_out.add(arrayIndex, m_out.constIntPtr(1));
+                        LValue nextFixedArrayIndex = m_out.add(fixedArrayIndex, m_out.constIntPtr(1));
+                        ValueFromBlock arrayIndexLoopForFinish = m_out.anchor(nextArrayIndex);
+
+                        m_out.addIncomingToPhi(fixedArrayIndex, m_out.anchor(nextFixedArrayIndex));
+                        m_out.addIncomingToPhi(arrayIndex, m_out.anchor(nextArrayIndex));
+
+                        m_out.branch(
+                            m_out.below(nextFixedArrayIndex, fixedArraySize),
+                            unsure(loopStart), unsure(continuation));
+
+                        m_out.appendTo(continuation, lastNext);
+                        index = m_out.phi(pointerType(), arrayIndexStartForFinish, arrayIndexLoopForFinish);
+                    }
</ins><span class="cx">                 } else {
</span><span class="cx">                     IndexedAbstractHeap&amp; heap = m_heaps.indexedContiguousProperties;
</span><span class="cx">                     LValue item = lowJSValue(use);
</span><span class="lines">@@ -4428,6 +4485,12 @@
</span><span class="cx"> 
</span><span class="cx">     void compileSpread()
</span><span class="cx">     {
</span><ins>+        // It would be trivial to support this, but for now, we never create
+        // IR that would necessitate this. The reason is that Spread is only
+        // consumed by NewArrayWithSpread and never anything else. Also, any
+        // Spread(PhantomCreateRest) will turn into PhantomSpread(PhantomCreateRest).
+        RELEASE_ASSERT(m_node-&gt;child1()-&gt;op() != PhantomCreateRest); 
+
</ins><span class="cx">         LValue argument = lowCell(m_node-&gt;child1());
</span><span class="cx"> 
</span><span class="cx">         LValue result;
</span><span class="lines">@@ -6131,6 +6194,272 @@
</span><span class="cx">             });
</span><span class="cx">     }
</span><span class="cx">     
</span><ins>+    void compileCallOrConstructVarargsSpread()
+    {
+        Node* node = m_node;
+        LValue jsCallee = lowJSValue(m_node-&gt;child1());
+        LValue thisArg = lowJSValue(m_node-&gt;child2());
+
+        RELEASE_ASSERT(node-&gt;child3()-&gt;op() == PhantomNewArrayWithSpread);
+        Node* arrayWithSpread = node-&gt;child3().node();
+        BitVector* bitVector = arrayWithSpread-&gt;bitVector();
+        unsigned numNonSpreadParameters = 0;
+        Vector&lt;LValue, 2&gt; spreadLengths;
+        Vector&lt;LValue, 8&gt; patchpointArguments;
+        HashMap&lt;InlineCallFrame*, LValue, WTF::DefaultHash&lt;InlineCallFrame*&gt;::Hash, WTF::NullableHashTraits&lt;InlineCallFrame*&gt;&gt; cachedSpreadLengths;
+
+        for (unsigned i = 0; i &lt; arrayWithSpread-&gt;numChildren(); i++) {
+            if (bitVector-&gt;get(i)) {
+                Node* spread = m_graph.varArgChild(arrayWithSpread, i).node();
+                RELEASE_ASSERT(spread-&gt;op() == PhantomSpread);
+                RELEASE_ASSERT(spread-&gt;child1()-&gt;op() == PhantomCreateRest);
+                InlineCallFrame* inlineCallFrame = spread-&gt;child1()-&gt;origin.semantic.inlineCallFrame;
+                unsigned numberOfArgumentsToSkip = spread-&gt;child1()-&gt;numberOfArgumentsToSkip();
+                LValue length = cachedSpreadLengths.ensure(inlineCallFrame, [&amp;] () {
+                    return m_out.zeroExtPtr(getSpreadLengthFromInlineCallFrame(inlineCallFrame, numberOfArgumentsToSkip));
+                }).iterator-&gt;value;
+                patchpointArguments.append(length);
+                spreadLengths.append(length);
+            } else {
+                ++numNonSpreadParameters;
+                LValue argument = lowJSValue(m_graph.varArgChild(arrayWithSpread, i));
+                patchpointArguments.append(argument);
+            }
+        }
+
+        LValue argumentCountIncludingThis = m_out.constIntPtr(numNonSpreadParameters + 1);
+        for (LValue length : spreadLengths)
+            argumentCountIncludingThis = m_out.add(length, argumentCountIncludingThis);
+        
+        PatchpointValue* patchpoint = m_out.patchpoint(Int64);
+
+        patchpoint-&gt;append(jsCallee, ValueRep::reg(GPRInfo::regT0));
+        patchpoint-&gt;append(thisArg, ValueRep::WarmAny);
+        patchpoint-&gt;append(argumentCountIncludingThis, ValueRep::WarmAny);
+        patchpoint-&gt;appendVectorWithRep(patchpointArguments, ValueRep::WarmAny);
+        patchpoint-&gt;append(m_tagMask, ValueRep::reg(GPRInfo::tagMaskRegister));
+        patchpoint-&gt;append(m_tagTypeNumber, ValueRep::reg(GPRInfo::tagTypeNumberRegister));
+
+        RefPtr&lt;PatchpointExceptionHandle&gt; exceptionHandle = preparePatchpointForExceptions(patchpoint);
+
+        patchpoint-&gt;clobber(RegisterSet::macroScratchRegisters());
+        patchpoint-&gt;clobber(RegisterSet::volatileRegistersForJSCall()); // No inputs will be in a volatile register.
+        patchpoint-&gt;resultConstraint = ValueRep::reg(GPRInfo::returnValueGPR);
+
+        patchpoint-&gt;numGPScratchRegisters = 0;
+
+        // This is the minimum amount of call arg area stack space that all JS-&gt;JS calls always have.
+        unsigned minimumJSCallAreaSize =
+            sizeof(CallerFrameAndPC) +
+            WTF::roundUpToMultipleOf(stackAlignmentBytes(), 5 * sizeof(EncodedJSValue));
+
+        m_proc.requestCallArgAreaSizeInBytes(minimumJSCallAreaSize);
+        
+        CodeOrigin codeOrigin = codeOriginDescriptionOfCallSite();
+        State* state = &amp;m_ftlState;
+        patchpoint-&gt;setGenerator(
+            [=] (CCallHelpers&amp; jit, const StackmapGenerationParams&amp; params) {
+                AllowMacroScratchRegisterUsage allowScratch(jit);
+                CallSiteIndex callSiteIndex =
+                    state-&gt;jitCode-&gt;common.addUniqueCallSiteIndex(codeOrigin);
+
+                Box&lt;CCallHelpers::JumpList&gt; exceptions =
+                    exceptionHandle-&gt;scheduleExitCreation(params)-&gt;jumps(jit);
+
+                exceptionHandle-&gt;scheduleExitCreationForUnwind(params, callSiteIndex);
+
+                jit.store32(
+                    CCallHelpers::TrustedImm32(callSiteIndex.bits()),
+                    CCallHelpers::tagFor(VirtualRegister(CallFrameSlot::argumentCount)));
+
+                CallLinkInfo* callLinkInfo = jit.codeBlock()-&gt;addCallLinkInfo();
+
+                RegisterSet usedRegisters = RegisterSet::allRegisters();
+                usedRegisters.exclude(RegisterSet::volatileRegistersForJSCall());
+                GPRReg calleeGPR = params[1].gpr();
+                usedRegisters.set(calleeGPR);
+
+                ScratchRegisterAllocator allocator(usedRegisters);
+                GPRReg scratchGPR1 = allocator.allocateScratchGPR();
+                GPRReg scratchGPR2 = allocator.allocateScratchGPR();
+                GPRReg scratchGPR3 = allocator.allocateScratchGPR();
+                GPRReg scratchGPR4 = allocator.allocateScratchGPR();
+                RELEASE_ASSERT(!allocator.numberOfReusedRegisters());
+
+                auto getValueFromRep = [&amp;] (B3::ValueRep rep, GPRReg result) {
+                    ASSERT(!usedRegisters.get(result));
+
+                    if (rep.isConstant()) {
+                        jit.move(CCallHelpers::Imm64(rep.value()), result);
+                        return;
+                    }
+
+                    // Note: in this function, we only request 64 bit values.
+                    if (rep.isStack()) {
+                        jit.load64(
+                            CCallHelpers::Address(GPRInfo::callFrameRegister, rep.offsetFromFP()),
+                            result);
+                        return;
+                    }
+
+                    RELEASE_ASSERT(rep.isGPR());
+                    ASSERT(usedRegisters.get(rep.gpr()));
+                    jit.move(rep.gpr(), result);
+                };
+
+                auto callWithExceptionCheck = [&amp;] (void* callee) {
+                    jit.move(CCallHelpers::TrustedImmPtr(callee), GPRInfo::nonPreservedNonArgumentGPR);
+                    jit.call(GPRInfo::nonPreservedNonArgumentGPR);
+                    exceptions-&gt;append(jit.emitExceptionCheck(AssemblyHelpers::NormalExceptionCheck, AssemblyHelpers::FarJumpWidth));
+                };
+
+                auto adjustStack = [&amp;] (GPRReg amount) {
+                    jit.addPtr(CCallHelpers::TrustedImm32(sizeof(CallerFrameAndPC)), amount, CCallHelpers::stackPointerRegister);
+                };
+
+                CCallHelpers::JumpList slowCase;
+                unsigned originalStackHeight = params.proc().frameSize();
+
+                {
+                    unsigned numUsedSlots = WTF::roundUpToMultipleOf(stackAlignmentRegisters(), originalStackHeight / sizeof(EncodedJSValue));
+                    B3::ValueRep argumentCountIncludingThisRep = params[3];
+                    getValueFromRep(argumentCountIncludingThisRep, scratchGPR2);
+                    slowCase.append(jit.branch32(CCallHelpers::Above, scratchGPR2, CCallHelpers::TrustedImm32(JSC::maxArguments + 1)));
+                    
+                    jit.move(scratchGPR2, scratchGPR1);
+                    jit.addPtr(CCallHelpers::TrustedImmPtr(static_cast&lt;size_t&gt;(numUsedSlots + CallFrame::headerSizeInRegisters)), scratchGPR1);
+                    // scratchGPR1 now has the required frame size in Register units
+                    // Round scratchGPR1 to next multiple of stackAlignmentRegisters()
+                    jit.addPtr(CCallHelpers::TrustedImm32(stackAlignmentRegisters() - 1), scratchGPR1);
+                    jit.andPtr(CCallHelpers::TrustedImm32(~(stackAlignmentRegisters() - 1)), scratchGPR1);
+                    jit.negPtr(scratchGPR1);
+                    jit.lshiftPtr(CCallHelpers::Imm32(3), scratchGPR1);
+                    jit.addPtr(GPRInfo::callFrameRegister, scratchGPR1);
+
+                    jit.store32(scratchGPR2, CCallHelpers::Address(scratchGPR1, CallFrameSlot::argumentCount * static_cast&lt;int&gt;(sizeof(Register)) + PayloadOffset));
+
+                    int storeOffset = CallFrame::thisArgumentOffset() * static_cast&lt;int&gt;(sizeof(Register));
+
+                    for (unsigned i = arrayWithSpread-&gt;numChildren(); i--; ) {
+                        unsigned paramsOffset = 4;
+
+                        if (bitVector-&gt;get(i)) {
+                            Node* spread = state-&gt;graph.varArgChild(arrayWithSpread, i).node();
+                            RELEASE_ASSERT(spread-&gt;op() == PhantomSpread);
+                            RELEASE_ASSERT(spread-&gt;child1()-&gt;op() == PhantomCreateRest);
+                            InlineCallFrame* inlineCallFrame = spread-&gt;child1()-&gt;origin.semantic.inlineCallFrame;
+
+                            unsigned numberOfArgumentsToSkip = spread-&gt;child1()-&gt;numberOfArgumentsToSkip();
+
+                            B3::ValueRep numArgumentsToCopy = params[paramsOffset + i];
+                            getValueFromRep(numArgumentsToCopy, scratchGPR3);
+                            int loadOffset = (AssemblyHelpers::argumentsStart(inlineCallFrame).offset() + numberOfArgumentsToSkip) * static_cast&lt;int&gt;(sizeof(Register));
+
+                            auto done = jit.branchTestPtr(MacroAssembler::Zero, scratchGPR3);
+                            auto loopStart = jit.label();
+                            jit.subPtr(CCallHelpers::TrustedImmPtr(static_cast&lt;size_t&gt;(1)), scratchGPR3);
+                            jit.subPtr(CCallHelpers::TrustedImmPtr(static_cast&lt;size_t&gt;(1)), scratchGPR2);
+                            jit.load64(CCallHelpers::BaseIndex(GPRInfo::callFrameRegister, scratchGPR3, CCallHelpers::TimesEight, loadOffset), scratchGPR4);
+                            jit.store64(scratchGPR4,
+                                CCallHelpers::BaseIndex(scratchGPR1, scratchGPR2, CCallHelpers::TimesEight, storeOffset));
+                            jit.branchTestPtr(CCallHelpers::NonZero, scratchGPR3).linkTo(loopStart, &amp;jit);
+                            done.link(&amp;jit);
+                        } else {
+                            jit.subPtr(CCallHelpers::TrustedImmPtr(static_cast&lt;size_t&gt;(1)), scratchGPR2);
+                            getValueFromRep(params[paramsOffset + i], scratchGPR3);
+                            jit.store64(scratchGPR3,
+                                CCallHelpers::BaseIndex(scratchGPR1, scratchGPR2, CCallHelpers::TimesEight, storeOffset));
+                        }
+                    }
+                }
+
+                {
+                    CCallHelpers::Jump dontThrow = jit.jump();
+                    slowCase.link(&amp;jit);
+                    jit.setupArgumentsExecState();
+                    callWithExceptionCheck(bitwise_cast&lt;void*&gt;(operationThrowStackOverflowForVarargs));
+                    jit.abortWithReason(DFGVarargsThrowingPathDidNotThrow);
+                    
+                    dontThrow.link(&amp;jit);
+                }
+
+                adjustStack(scratchGPR1);
+                
+                ASSERT(calleeGPR == GPRInfo::regT0);
+                jit.store64(calleeGPR, CCallHelpers::calleeFrameSlot(CallFrameSlot::callee));
+                getValueFromRep(params[2], scratchGPR3);
+                jit.store64(scratchGPR3, CCallHelpers::calleeArgumentSlot(0));
+                
+                CallLinkInfo::CallType callType;
+                if (node-&gt;op() == ConstructVarargs || node-&gt;op() == ConstructForwardVarargs)
+                    callType = CallLinkInfo::ConstructVarargs;
+                else if (node-&gt;op() == TailCallVarargs || node-&gt;op() == TailCallForwardVarargs)
+                    callType = CallLinkInfo::TailCallVarargs;
+                else
+                    callType = CallLinkInfo::CallVarargs;
+                
+                bool isTailCall = CallLinkInfo::callModeFor(callType) == CallMode::Tail;
+                
+                CCallHelpers::DataLabelPtr targetToCheck;
+                CCallHelpers::Jump slowPath = jit.branchPtrWithPatch(
+                    CCallHelpers::NotEqual, GPRInfo::regT0, targetToCheck,
+                    CCallHelpers::TrustedImmPtr(nullptr));
+                
+                CCallHelpers::Call fastCall;
+                CCallHelpers::Jump done;
+                
+                if (isTailCall) {
+                    jit.emitRestoreCalleeSaves();
+                    jit.prepareForTailCallSlow();
+                    fastCall = jit.nearTailCall();
+                } else {
+                    fastCall = jit.nearCall();
+                    done = jit.jump();
+                }
+                
+                slowPath.link(&amp;jit);
+
+                if (isTailCall)
+                    jit.emitRestoreCalleeSaves();
+                ASSERT(!usedRegisters.get(GPRInfo::regT2));
+                jit.move(CCallHelpers::TrustedImmPtr(callLinkInfo), GPRInfo::regT2);
+                CCallHelpers::Call slowCall = jit.nearCall();
+                
+                if (isTailCall)
+                    jit.abortWithReason(JITDidReturnFromTailCall);
+                else
+                    done.link(&amp;jit);
+                
+                callLinkInfo-&gt;setUpCall(callType, node-&gt;origin.semantic, GPRInfo::regT0);
+
+                jit.addPtr(
+                    CCallHelpers::TrustedImm32(-originalStackHeight),
+                    GPRInfo::callFrameRegister, CCallHelpers::stackPointerRegister);
+                
+                jit.addLinkTask(
+                    [=] (LinkBuffer&amp; linkBuffer) {
+                        MacroAssemblerCodePtr linkCall =
+                            linkBuffer.vm().getCTIStub(linkCallThunkGenerator).code();
+                        linkBuffer.link(slowCall, FunctionPtr(linkCall.executableAddress()));
+                        
+                        callLinkInfo-&gt;setCallLocations(
+                            CodeLocationLabel(linkBuffer.locationOfNearCall(slowCall)),
+                            CodeLocationLabel(linkBuffer.locationOf(targetToCheck)),
+                            linkBuffer.locationOfNearCall(fastCall));
+                    });
+            });
+
+        switch (node-&gt;op()) {
+        case TailCallForwardVarargs:
+            m_out.unreachable();
+            break;
+
+        default:
+            setJSValue(patchpoint);
+            break;
+        }
+    }
+
</ins><span class="cx">     void compileCallOrConstructVarargs()
</span><span class="cx">     {
</span><span class="cx">         Node* node = m_node;
</span><span class="lines">@@ -6157,6 +6486,12 @@
</span><span class="cx">             DFG_CRASH(m_graph, node, &quot;bad node type&quot;);
</span><span class="cx">             break;
</span><span class="cx">         }
</span><ins>+
+        if (forwarding &amp;&amp; m_node-&gt;child3() &amp;&amp; m_node-&gt;child3()-&gt;op() == PhantomNewArrayWithSpread) {
+            compileCallOrConstructVarargsSpread();
+            return;
+        }
+
</ins><span class="cx">         
</span><span class="cx">         PatchpointValue* patchpoint = m_out.patchpoint(Int64);
</span><span class="cx"> 
</span><span class="lines">@@ -6530,6 +6865,11 @@
</span><span class="cx">     
</span><span class="cx">     void compileForwardVarargs()
</span><span class="cx">     {
</span><ins>+        if (m_node-&gt;child1() &amp;&amp; m_node-&gt;child1()-&gt;op() == PhantomNewArrayWithSpread) {
+            compileForwardVarargsWithSpread();
+            return;
+        }
+
</ins><span class="cx">         LoadVarargsData* data = m_node-&gt;loadVarargsData();
</span><span class="cx">         InlineCallFrame* inlineCallFrame;
</span><span class="cx">         if (m_node-&gt;child1())
</span><span class="lines">@@ -6618,6 +6958,135 @@
</span><span class="cx">         m_out.appendTo(continuation, lastNext);
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    LValue getSpreadLengthFromInlineCallFrame(InlineCallFrame* inlineCallFrame, unsigned numberOfArgumentsToSkip)
+    {
+        ArgumentsLength argumentsLength = getArgumentsLength(inlineCallFrame);
+        if (argumentsLength.isKnown) {
+            unsigned knownLength = argumentsLength.known;
+            if (knownLength &gt;= numberOfArgumentsToSkip)
+                knownLength = knownLength - numberOfArgumentsToSkip;
+            else
+                knownLength = 0;
+            return m_out.constInt32(knownLength);
+        }
+
+
+        // We need to perform the same logical operation as the code above, but through dynamic operations.
+        if (!numberOfArgumentsToSkip)
+            return argumentsLength.value;
+
+        LBasicBlock isLarger = m_out.newBlock();
+        LBasicBlock continuation = m_out.newBlock();
+
+        ValueFromBlock smallerOrEqualLengthResult = m_out.anchor(m_out.constInt32(0));
+        m_out.branch(
+            m_out.above(argumentsLength.value, m_out.constInt32(numberOfArgumentsToSkip)), unsure(isLarger), unsure(continuation));
+        LBasicBlock lastNext = m_out.appendTo(isLarger, continuation);
+        ValueFromBlock largerLengthResult = m_out.anchor(m_out.sub(argumentsLength.value, m_out.constInt32(numberOfArgumentsToSkip)));
+        m_out.jump(continuation);
+
+        m_out.appendTo(continuation, lastNext);
+        return m_out.phi(Int32, smallerOrEqualLengthResult, largerLengthResult);
+    }
+
+    void compileForwardVarargsWithSpread()
+    {
+        HashMap&lt;InlineCallFrame*, LValue, WTF::DefaultHash&lt;InlineCallFrame*&gt;::Hash, WTF::NullableHashTraits&lt;InlineCallFrame*&gt;&gt; cachedSpreadLengths;
+
+        Node* arrayWithSpread = m_node-&gt;child1().node();
+        RELEASE_ASSERT(arrayWithSpread-&gt;op() == PhantomNewArrayWithSpread);
+        BitVector* bitVector = arrayWithSpread-&gt;bitVector();
+
+        unsigned numberOfStaticArguments = 0;
+        Vector&lt;LValue, 2&gt; spreadLengths;
+        for (unsigned i = 0; i &lt; arrayWithSpread-&gt;numChildren(); i++) {
+            if (bitVector-&gt;get(i)) {
+                Node* child = m_graph.varArgChild(arrayWithSpread, i).node();
+                ASSERT(child-&gt;op() == PhantomSpread);
+                ASSERT(child-&gt;child1()-&gt;op() == PhantomCreateRest);
+                InlineCallFrame* inlineCallFrame = child-&gt;child1()-&gt;origin.semantic.inlineCallFrame;
+                LValue length = cachedSpreadLengths.ensure(inlineCallFrame, [&amp;] () {
+                    return getSpreadLengthFromInlineCallFrame(inlineCallFrame, child-&gt;child1()-&gt;numberOfArgumentsToSkip());
+                }).iterator-&gt;value;
+                spreadLengths.append(length);
+            } else
+                ++numberOfStaticArguments;
+        }
+
+        LValue lengthIncludingThis = m_out.constInt32(1 + numberOfStaticArguments);
+        for (LValue length : spreadLengths)
+            lengthIncludingThis = m_out.add(lengthIncludingThis, length);
+
+        LoadVarargsData* data = m_node-&gt;loadVarargsData();
+        speculate(
+            VarargsOverflow, noValue(), nullptr,
+            m_out.above(lengthIncludingThis, m_out.constInt32(data-&gt;limit)));
+        
+        m_out.store32(lengthIncludingThis, payloadFor(data-&gt;machineCount));
+
+        LValue targetStart = addressFor(data-&gt;machineStart).value();
+        LValue storeIndex = m_out.constIntPtr(0);
+        for (unsigned i = 0; i &lt; arrayWithSpread-&gt;numChildren(); i++) {
+            if (bitVector-&gt;get(i)) {
+                Node* child = m_graph.varArgChild(arrayWithSpread, i).node();
+                RELEASE_ASSERT(child-&gt;op() == PhantomSpread);
+                RELEASE_ASSERT(child-&gt;child1()-&gt;op() == PhantomCreateRest);
+                InlineCallFrame* inlineCallFrame = child-&gt;child1()-&gt;origin.semantic.inlineCallFrame;
+
+                LValue sourceStart = getArgumentsStart(inlineCallFrame, child-&gt;child1()-&gt;numberOfArgumentsToSkip());
+                LValue spreadLength = m_out.zeroExtPtr(cachedSpreadLengths.get(inlineCallFrame));
+
+                LBasicBlock loop = m_out.newBlock();
+                LBasicBlock continuation = m_out.newBlock();
+                ValueFromBlock startLoadIndex = m_out.anchor(m_out.constIntPtr(0));
+                ValueFromBlock startStoreIndex = m_out.anchor(storeIndex);
+                ValueFromBlock startStoreIndexForEnd = m_out.anchor(storeIndex);
+
+                m_out.branch(m_out.isZero64(spreadLength), unsure(continuation), unsure(loop));
+
+                LBasicBlock lastNext = m_out.appendTo(loop, continuation);
+                LValue loopStoreIndex = m_out.phi(Int64, startStoreIndex);
+                LValue loadIndex = m_out.phi(Int64, startLoadIndex);
+                LValue value = m_out.load64(
+                    m_out.baseIndex(m_heaps.variables, sourceStart, loadIndex));
+                m_out.store64(value, m_out.baseIndex(m_heaps.variables, targetStart, loopStoreIndex));
+                LValue nextLoadIndex = m_out.add(m_out.constIntPtr(1), loadIndex);
+                m_out.addIncomingToPhi(loadIndex, m_out.anchor(nextLoadIndex));
+                LValue nextStoreIndex = m_out.add(m_out.constIntPtr(1), loopStoreIndex);
+                m_out.addIncomingToPhi(loopStoreIndex, m_out.anchor(nextStoreIndex));
+                ValueFromBlock loopStoreIndexForEnd = m_out.anchor(nextStoreIndex);
+                m_out.branch(m_out.below(nextLoadIndex, spreadLength), unsure(loop), unsure(continuation));
+
+                m_out.appendTo(continuation, lastNext);
+                storeIndex = m_out.phi(Int64, startStoreIndexForEnd, loopStoreIndexForEnd);
+            } else {
+                LValue value = lowJSValue(m_graph.varArgChild(arrayWithSpread, i));
+                m_out.store64(value, m_out.baseIndex(m_heaps.variables, targetStart, storeIndex));
+                storeIndex = m_out.add(m_out.constIntPtr(1), storeIndex);
+            }
+        }
+
+        LBasicBlock undefinedLoop = m_out.newBlock();
+        LBasicBlock continuation = m_out.newBlock();
+
+        ValueFromBlock startStoreIndex = m_out.anchor(storeIndex);
+        LValue loopBoundValue = m_out.constIntPtr(data-&gt;mandatoryMinimum);
+        m_out.branch(m_out.below(storeIndex, loopBoundValue),
+            unsure(undefinedLoop), unsure(continuation));
+
+        LBasicBlock lastNext = m_out.appendTo(undefinedLoop, continuation);
+        LValue loopStoreIndex = m_out.phi(Int64, startStoreIndex);
+        m_out.store64(
+            m_out.constInt64(JSValue::encode(jsUndefined())),
+            m_out.baseIndex(m_heaps.variables, targetStart, loopStoreIndex));
+        LValue nextIndex = m_out.add(loopStoreIndex, m_out.constIntPtr(1));
+        m_out.addIncomingToPhi(loopStoreIndex, m_out.anchor(nextIndex));
+        m_out.branch(
+            m_out.below(nextIndex, loopBoundValue), unsure(undefinedLoop), unsure(continuation));
+
+        m_out.appendTo(continuation, lastNext);
+    }
+
</ins><span class="cx">     void compileJump()
</span><span class="cx">     {
</span><span class="cx">         m_out.jump(lowBlock(m_node-&gt;targetBlock()));
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLOperationscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLOperations.cpp (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLOperations.cpp        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/ftl/FTLOperations.cpp        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -35,6 +35,7 @@
</span><span class="cx"> #include &quot;InlineCallFrame.h&quot;
</span><span class="cx"> #include &quot;JSAsyncFunction.h&quot;
</span><span class="cx"> #include &quot;JSCInlines.h&quot;
</span><ins>+#include &quot;JSFixedArray.h&quot;
</ins><span class="cx"> #include &quot;JSGeneratorFunction.h&quot;
</span><span class="cx"> #include &quot;JSLexicalEnvironment.h&quot;
</span><span class="cx"> 
</span><span class="lines">@@ -86,6 +87,8 @@
</span><span class="cx">     case PhantomDirectArguments:
</span><span class="cx">     case PhantomClonedArguments:
</span><span class="cx">     case PhantomCreateRest:
</span><ins>+    case PhantomSpread:
+    case PhantomNewArrayWithSpread:
</ins><span class="cx">         // Those are completely handled by operationMaterializeObjectInOSR
</span><span class="cx">         break;
</span><span class="cx"> 
</span><span class="lines">@@ -393,14 +396,102 @@
</span><span class="cx">                 ASSERT(found);
</span><span class="cx">             }
</span><span class="cx"> #endif
</span><del>-
</del><span class="cx">             return array;
</span><span class="cx">         }
</span><ins>+
</ins><span class="cx">         default:
</span><span class="cx">             RELEASE_ASSERT_NOT_REACHED();
</span><span class="cx">             return nullptr;
</span><span class="cx">         }
</span><span class="cx">     }
</span><ins>+
+    case PhantomSpread: {
+        JSArray* array = nullptr;
+        for (unsigned i = materialization-&gt;properties().size(); i--;) {
+            const ExitPropertyValue&amp; property = materialization-&gt;properties()[i];
+            if (property.location().kind() == SpreadPLoc) {
+                array = jsCast&lt;JSArray*&gt;(JSValue::decode(values[i]));
+                break;
+            }
+        }
+        RELEASE_ASSERT(array);
+
+        // Note: it is sound for JSFixedArray::createFromArray to call getDirectIndex here
+        // because we're guaranteed we won't be calling any getters. The reason for this is
+        // that we only support PhantomSpread over CreateRest, which is an array we create.
+        // Any attempts to put a getter on any indices on the rest array will escape the array.
+        JSFixedArray* fixedArray = JSFixedArray::createFromArray(exec, vm, array);
+        return fixedArray;
+    }
+
+    case PhantomNewArrayWithSpread: {
+        CodeBlock* codeBlock = baselineCodeBlockForOriginAndBaselineCodeBlock(
+            materialization-&gt;origin(), exec-&gt;codeBlock());
+        JSGlobalObject* globalObject = codeBlock-&gt;globalObject();
+        Structure* structure = globalObject-&gt;arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous);
+
+        unsigned arraySize = 0;
+        unsigned numProperties = 0;
+        for (unsigned i = materialization-&gt;properties().size(); i--;) {
+            const ExitPropertyValue&amp; property = materialization-&gt;properties()[i];
+            if (property.location().kind() == NewArrayWithSpreadArgumentPLoc) {
+                ++numProperties;
+                JSValue value = JSValue::decode(values[i]);
+                if (JSFixedArray* fixedArray = jsDynamicCast&lt;JSFixedArray*&gt;(value))
+                    arraySize += fixedArray-&gt;size();
+                else
+                    arraySize += 1;
+            }
+        }
+
+        JSArray* result = JSArray::tryCreateUninitialized(vm, structure, arraySize);
+        RELEASE_ASSERT(result);
+
+#if !ASSERT_DISABLED
+        // Ensure we see indices for everything in the range: [0, numProperties)
+        for (unsigned i = 0; i &lt; numProperties; ++i) {
+            bool found = false;
+            for (unsigned j = 0; j &lt; materialization-&gt;properties().size(); ++j) {
+                const ExitPropertyValue&amp; property = materialization-&gt;properties()[j];
+                if (property.location().kind() == NewArrayWithSpreadArgumentPLoc &amp;&amp; property.location().info() == i) {
+                    found = true;
+                    break;
+                }
+            }
+            ASSERT(found);
+        }
+#endif
+
+        Vector&lt;JSValue, 8&gt; arguments;
+        arguments.grow(numProperties);
+
+        for (unsigned i = materialization-&gt;properties().size(); i--;) {
+            const ExitPropertyValue&amp; property = materialization-&gt;properties()[i];
+            if (property.location().kind() == NewArrayWithSpreadArgumentPLoc) {
+                JSValue value = JSValue::decode(values[i]);
+                RELEASE_ASSERT(property.location().info() &lt; numProperties);
+                arguments[property.location().info()] = value;
+            }
+        }
+
+        unsigned arrayIndex = 0;
+        for (JSValue value : arguments) {
+            if (JSFixedArray* fixedArray = jsDynamicCast&lt;JSFixedArray*&gt;(value)) {
+                for (unsigned i = 0; i &lt; fixedArray-&gt;size(); i++) {
+                    ASSERT(fixedArray-&gt;get(i));
+                    result-&gt;initializeIndex(vm, arrayIndex, fixedArray-&gt;get(i));
+                    ++arrayIndex;
+                }
+            } else {
+                // We are not spreading.
+                result-&gt;initializeIndex(vm, arrayIndex, value);
+                ++arrayIndex;
+            }
+        }
+
+        return result;
+    }
+
</ins><span class="cx">         
</span><span class="cx">     default:
</span><span class="cx">         RELEASE_ASSERT_NOT_REACHED();
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorejitSetupVarargsFramecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/jit/SetupVarargsFrame.cpp (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/jit/SetupVarargsFrame.cpp        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/jit/SetupVarargsFrame.cpp        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -78,7 +78,7 @@
</span><span class="cx">         jit.sub32(CCallHelpers::TrustedImm32(firstVarArgOffset), scratchGPR1);
</span><span class="cx">         endVarArgs.link(&amp;jit);
</span><span class="cx">     }
</span><del>-    slowCase.append(jit.branch32(CCallHelpers::Above, scratchGPR1, CCallHelpers::TrustedImm32(maxArguments + 1)));
</del><ins>+    slowCase.append(jit.branch32(CCallHelpers::Above, scratchGPR1, CCallHelpers::TrustedImm32(JSC::maxArguments + 1)));
</ins><span class="cx">     
</span><span class="cx">     emitSetVarargsFrame(jit, scratchGPR1, true, numUsedSlotsGPR, scratchGPR2);
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorejsccpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/jsc.cpp (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/jsc.cpp        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/jsc.cpp        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -990,6 +990,8 @@
</span><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionSamplingProfilerStackTraces(ExecState*);
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+static EncodedJSValue JSC_HOST_CALL functionMaxArguments(ExecState*);
+
</ins><span class="cx"> #if ENABLE(WEBASSEMBLY)
</span><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionTestWasmModuleFunctions(ExecState*);
</span><span class="cx"> #endif
</span><span class="lines">@@ -1242,6 +1244,8 @@
</span><span class="cx">         addFunction(vm, &quot;samplingProfilerStackTraces&quot;, functionSamplingProfilerStackTraces, 0);
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+        addFunction(vm, &quot;maxArguments&quot;, functionMaxArguments, 0);
+
</ins><span class="cx"> #if ENABLE(WEBASSEMBLY)
</span><span class="cx">         addFunction(vm, &quot;testWasmModuleFunctions&quot;, functionTestWasmModuleFunctions, 0);
</span><span class="cx"> #endif
</span><span class="lines">@@ -2484,6 +2488,11 @@
</span><span class="cx"> }
</span><span class="cx"> #endif // ENABLE(SAMPLING_PROFILER)
</span><span class="cx"> 
</span><ins>+EncodedJSValue JSC_HOST_CALL functionMaxArguments(ExecState*)
+{
+    return JSValue::encode(jsNumber(JSC::maxArguments));
+}
+
</ins><span class="cx"> #if ENABLE(WEBASSEMBLY)
</span><span class="cx"> 
</span><span class="cx"> static JSValue box(ExecState* exec, VM&amp; vm, JSValue wasmValue)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSFixedArrayh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSFixedArray.h (209120 => 209121)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSFixedArray.h        2016-11-30 04:54:04 UTC (rev 209120)
+++ trunk/Source/JavaScriptCore/runtime/JSFixedArray.h        2016-11-30 06:24:44 UTC (rev 209121)
</span><span class="lines">@@ -79,7 +79,11 @@
</span><span class="cx">                 // We may still call into this function when !globalObject-&gt;isArrayIteratorProtocolFastAndNonObservable(),
</span><span class="cx">                 // however, if we do that, we ensure we're calling in with an array with all self properties between
</span><span class="cx">                 // [0, length).
</span><del>-                ASSERT(array-&gt;globalObject()-&gt;isArrayIteratorProtocolFastAndNonObservable());
</del><ins>+                //
+                // We may also call into this during OSR exit to materialize a phantom fixed array.
+                // We may be creating a fixed array during OSR exit even after the iterator protocol changed.
+                // But, when the phantom would have logically been created, the protocol hadn't been
+                // changed. Therefore, it is sound to assume empty indices are jsUndefined().
</ins><span class="cx">                 value = jsUndefined();
</span><span class="cx">             }
</span><span class="cx">             RETURN_IF_EXCEPTION(throwScope, nullptr);
</span></span></pre>
</div>
</div>

</body>
</html>