<!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>[210476] 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/210476">210476</a></dd>
<dt>Author</dt> <dd>sbarati@apple.com</dd>
<dt>Date</dt> <dd>2017-01-06 23:53:07 -0800 (Fri, 06 Jan 2017)</dd>
</dl>
<h3>Log Message</h3>
<pre>Add a slice intrinsic to the DFG/FTL
https://bugs.webkit.org/show_bug.cgi?id=166707
Reviewed by Filip Pizlo.
JSTests:
* stress/array-slice-intrinsic.js: Added.
(assert):
(shallowEq):
(runTest1):
(runTest2):
* stress/array-slice-jettison-on-constructor-change.js: Added.
(assert):
(runTest1):
(runTest2):
(addRandomProperties):
(runTests):
* stress/array-slice-osr-exit-2.js: Added.
(assert):
(Foo):
(shallowEq):
(runTest1):
(runTest2):
(addRandomProperties):
(runTests):
* stress/array-slice-osr-exit.js: Added.
(assert):
(Foo):
(shallowEq):
(runTest1):
(runTest2):
(addRandomProperties):
(runTests):
Source/JavaScriptCore:
The gist of this patch is to inline Array.prototype.slice
into the DFG/FTL. The implementation in the DFG-backend
and FTLLowerDFGToB3 is just a straight forward implementation
of what the C function is doing. The more interesting bits
of this patch are setting up the proper watchpoints and conditions
in the executing code to prove that its safe to skip all of the
observable JS actions that Array.prototype.slice normally does.
We perform the following proofs:
1. Array.prototype.constructor has not changed (via a watchpoint).
2. That Array.prototype.constructor[Symbol.species] has not changed (via a watchpoint).
3. The global object is not having a bad time.
3. The array that is being sliced has an original array structure.
5. Array.prototype/Object.prototype have not transitioned.
Conditions 1, 2, and 3 are strictly required.
4 is ensuring a couple things:
1. That a "constructor" property hasn't been added to the array
we're slicing since we're supposed to perform a Get(array, "constructor").
2. That we're not slicing an instance of a subclass of Array.
We could relax 4.1 in the future if we find other ways to test if
the incoming array hasn't changed the "constructor" property.
I'm seeing a 5% speedup on crypto-pbkdf2 and often a 1% speedup on
the total benchmark (the results are sometimes noisy).
* bytecode/ExitKind.cpp:
(JSC::exitKindToString):
* bytecode/ExitKind.h:
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNode.h:
(JSC::DFG::Node::hasHeapPrediction):
(JSC::DFG::Node::hasArrayMode):
* dfg/DFGNodeType.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileArraySlice):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileArraySlice):
* jit/AssemblyHelpers.cpp:
(JSC::AssemblyHelpers::emitLoadStructure):
* runtime/ArrayPrototype.cpp:
(JSC::ArrayPrototype::finishCreation):
(JSC::speciesWatchpointIsValid):
(JSC::speciesConstructArray):
(JSC::arrayProtoFuncSlice):
(JSC::arrayProtoPrivateFuncConcatMemcpy):
(JSC::ArrayPrototype::initializeSpeciesWatchpoint):
(JSC::ArrayPrototypeAdaptiveInferredPropertyWatchpoint::handleFire):
(JSC::speciesWatchpointsValid): Deleted.
(JSC::ArrayPrototype::attemptToInitializeSpeciesWatchpoint): Deleted.
* runtime/ArrayPrototype.h:
(JSC::ArrayPrototype::speciesWatchpointStatus): Deleted.
(): Deleted.
* runtime/Intrinsic.h:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::JSGlobalObject):
(JSC::JSGlobalObject::init):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::arraySpeciesWatchpoint):</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="#trunkSourceJavaScriptCoredfgDFGAbstractInterpreterInlinesh">trunk/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGByteCodeParsercpp">trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGClobberizeh">trunk/Source/JavaScriptCore/dfg/DFGClobberize.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGDoesGCcpp">trunk/Source/JavaScriptCore/dfg/DFGDoesGC.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGFixupPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp</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="#trunkSourceJavaScriptCoredfgDFGPredictionPropagationPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGSafeToExecuteh">trunk/Source/JavaScriptCore/dfg/DFGSafeToExecute.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGSpeculativeJITcpp">trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGSpeculativeJITh">trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.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="#trunkSourceJavaScriptCoreftlFTLCapabilitiescpp">trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLLowerDFGToB3cpp">trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorejitAssemblyHelperscpp">trunk/Source/JavaScriptCore/jit/AssemblyHelpers.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeArrayPrototypecpp">trunk/Source/JavaScriptCore/runtime/ArrayPrototype.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeArrayPrototypeh">trunk/Source/JavaScriptCore/runtime/ArrayPrototype.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeIntrinsich">trunk/Source/JavaScriptCore/runtime/Intrinsic.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSGlobalObjectcpp">trunk/Source/JavaScriptCore/runtime/JSGlobalObject.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSGlobalObjecth">trunk/Source/JavaScriptCore/runtime/JSGlobalObject.h</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li><a href="#trunkJSTestsstressarraysliceintrinsicjs">trunk/JSTests/stress/array-slice-intrinsic.js</a></li>
<li><a href="#trunkJSTestsstressarrayslicejettisononconstructorchangejs">trunk/JSTests/stress/array-slice-jettison-on-constructor-change.js</a></li>
<li><a href="#trunkJSTestsstressarraysliceosrexit2js">trunk/JSTests/stress/array-slice-osr-exit-2.js</a></li>
<li><a href="#trunkJSTestsstressarraysliceosrexitjs">trunk/JSTests/stress/array-slice-osr-exit.js</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkJSTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/JSTests/ChangeLog (210475 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/ChangeLog        2017-01-07 07:42:19 UTC (rev 210475)
+++ trunk/JSTests/ChangeLog        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -1,3 +1,38 @@
</span><ins>+2017-01-06 Saam Barati <sbarati@apple.com>
+
+ Add a slice intrinsic to the DFG/FTL
+ https://bugs.webkit.org/show_bug.cgi?id=166707
+
+ Reviewed by Filip Pizlo.
+
+ * stress/array-slice-intrinsic.js: Added.
+ (assert):
+ (shallowEq):
+ (runTest1):
+ (runTest2):
+ * stress/array-slice-jettison-on-constructor-change.js: Added.
+ (assert):
+ (runTest1):
+ (runTest2):
+ (addRandomProperties):
+ (runTests):
+ * stress/array-slice-osr-exit-2.js: Added.
+ (assert):
+ (Foo):
+ (shallowEq):
+ (runTest1):
+ (runTest2):
+ (addRandomProperties):
+ (runTests):
+ * stress/array-slice-osr-exit.js: Added.
+ (assert):
+ (Foo):
+ (shallowEq):
+ (runTest1):
+ (runTest2):
+ (addRandomProperties):
+ (runTests):
+
</ins><span class="cx"> 2017-01-06 Michael Saboff <msaboff@apple.com>
</span><span class="cx">
</span><span class="cx"> @putByValDirect in Array.of and Array.from overwrites non-writable/configurable properties
</span></span></pre></div>
<a id="trunkJSTestsstressarraysliceintrinsicjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/array-slice-intrinsic.js (0 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/stress/array-slice-intrinsic.js         (rev 0)
+++ trunk/JSTests/stress/array-slice-intrinsic.js        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -0,0 +1,49 @@
</span><ins>+function assert(b) {
+ if (!b)
+ throw new Error("Bad")
+}
+noInline(assert);
+
+function shallowEq(a, b) {
+ assert(a.length === b.length);
+ for (let i = 0; i < a.length; i++)
+ assert(a[i] === b[i]);
+}
+noInline(shallowEq);
+
+let tests = [
+ [[1,2,3,4,5], [1,2,3,4,5], 0, 5],
+ [[1,2,3,4,5], [1,2,3,4,5], 0],
+ [[1,2,3,4,5], [4], -2, -1],
+ [[1,2,3,4,5], [5], -1],
+ [[1,2,3,4,5], [5], -1, 5],
+ [[1,2,3,4,5], [], -10, -20],
+ [[1,2,3,4,5], [], -20, -10],
+ [[1,2,3,4,5], [], 6, 4],
+ [[1,2,3,4,5], [], 3, 2],
+ [[1,2,3,4,5], [4,5], 3, 10],
+ [[1,2,3,4,5], [3,4,5], 2, 10],
+ [[1,2,3,4,5], [1,2,3,4,5], -10, 10],
+ [[1,2,3,4,5], [1,2,3,4,5], -5, 10],
+ [[1,2,3,4,5], [2,3,4,5], -4, 10],
+];
+
+function runTest1(a, b) {
+ return a.slice(b);
+}
+noInline(runTest1);
+
+function runTest2(a, b, c) {
+ return a.slice(b, c);
+}
+noInline(runTest2);
+
+for (let i = 0; i < 10000; i++) {
+ for (let [input, output, ...args] of tests) {
+ assert(args.length === 1 || args.length === 2);
+ if (args.length === 1)
+ shallowEq(runTest1(input, args[0]), output);
+ else
+ shallowEq(runTest2(input, args[0], args[1]), output);
+ }
+}
</ins></span></pre></div>
<a id="trunkJSTestsstressarrayslicejettisononconstructorchangejs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/array-slice-jettison-on-constructor-change.js (0 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/stress/array-slice-jettison-on-constructor-change.js         (rev 0)
+++ trunk/JSTests/stress/array-slice-jettison-on-constructor-change.js        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -0,0 +1,72 @@
</span><ins>+function assert(b) {
+ if (!b)
+ throw new Error("Bad")
+}
+noInline(assert);
+
+let shouldBeNewConstructor = false;
+const newConstructor = {};
+
+function shallowEq(a, b) {
+ assert(a.length === b.length);
+ if (shouldBeNewConstructor)
+ assert(b.constructor === newConstructor);
+ for (let i = 0; i < a.length; i++)
+ assert(a[i] === b[i]);
+}
+noInline(shallowEq);
+
+let tests = [
+ [[1,2,3,4,5], [1,2,3,4,5], 0, 5],
+ [[1,2,3,4,5], [1,2,3,4,5], 0],
+ [[1,2,3,4,5], [4], -2, -1],
+ [[1,2,3,4,5], [5], -1],
+ [[1,2,3,4,5], [5], -1, 5],
+ [[1,2,3,4,5], [], -10, -20],
+ [[1,2,3,4,5], [], -20, -10],
+ [[1,2,3,4,5], [], 6, 4],
+ [[1,2,3,4,5], [], 3, 2],
+ [[1,2,3,4,5], [4,5], 3, 10],
+ [[1,2,3,4,5], [3,4,5], 2, 10],
+ [[1,2,3,4,5], [1,2,3,4,5], -10, 10],
+ [[1,2,3,4,5], [1,2,3,4,5], -5, 10],
+ [[1,2,3,4,5], [2,3,4,5], -4, 10],
+];
+
+function runTest1(a, b) {
+ let result = a.slice(b);
+ return result;
+}
+noInline(runTest1);
+
+function runTest2(a, b, c) {
+ let result = a.slice(b, c);
+ return result;
+}
+noInline(runTest2);
+
+function addRandomProperties(input) {
+ for (let i = 0; i < 4; i++) {
+ input["prop" + i + ((Math.random() * 100000) | 0)] = i;
+ }
+}
+noInline(addRandomProperties);
+
+function runTests() {
+ for (let i = 0; i < 10000; i++) {
+ for (let [input, output, ...args] of tests) {
+ addRandomProperties(input);
+ assert(args.length === 1 || args.length === 2);
+ if (args.length === 1)
+ shallowEq(runTest1(input, args[0]), output);
+ else
+ shallowEq(runTest2(input, args[0], args[1]), output);
+ }
+ }
+}
+
+runTests();
+
+Array.prototype.constructor = newConstructor;
+shouldBeNewConstructor = true;
+runTests();
</ins></span></pre></div>
<a id="trunkJSTestsstressarraysliceosrexit2js"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/array-slice-osr-exit-2.js (0 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/stress/array-slice-osr-exit-2.js         (rev 0)
+++ trunk/JSTests/stress/array-slice-osr-exit-2.js        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -0,0 +1,76 @@
</span><ins>+function assert(b) {
+ if (!b)
+ throw new Error("Bad")
+}
+noInline(assert);
+
+class Foo extends Array {
+ constructor(...args) {
+ super(...args);
+ }
+};
+function shallowEq(a, b) {
+ assert(a.length === b.length);
+ for (let i = 0; i < a.length; i++)
+ assert(a[i] === b[i]);
+}
+noInline(shallowEq);
+
+let tests = [
+ [[1,2,3,4,5], [1,2,3,4,5], 0, 5],
+ [[1,2,3,4,5], [1,2,3,4,5], 0],
+ [[1,2,3,4,5], [4], -2, -1],
+ [[1,2,3,4,5], [5], -1],
+ [[1,2,3,4,5], [5], -1, 5],
+ [[1,2,3,4,5], [], -10, -20],
+ [[1,2,3,4,5], [], -20, -10],
+ [[1,2,3,4,5], [], 6, 4],
+ [[1,2,3,4,5], [], 3, 2],
+ [[1,2,3,4,5], [4,5], 3, 10],
+ [[1,2,3,4,5], [3,4,5], 2, 10],
+ [[1,2,3,4,5], [1,2,3,4,5], -10, 10],
+ [[1,2,3,4,5], [1,2,3,4,5], -5, 10],
+ [[1,2,3,4,5], [2,3,4,5], -4, 10],
+];
+
+function runTest1(a, b) {
+ let result = a.slice(b);
+ assert(a instanceof Foo === result instanceof Foo);
+ return result;
+}
+noInline(runTest1);
+
+function runTest2(a, b, c) {
+ let result = a.slice(b, c);
+ assert(a instanceof Foo === result instanceof Foo);
+ return result;
+}
+noInline(runTest2);
+
+function addRandomProperties(input) {
+ for (let i = 0; i < 4; i++) {
+ input["prop" + i + ((Math.random() * 100000) | 0)] = i;
+ }
+}
+noInline(addRandomProperties);
+
+function runTests() {
+ for (let i = 0; i < 10000; i++) {
+ for (let [input, output, ...args] of tests) {
+ addRandomProperties(input);
+ assert(args.length === 1 || args.length === 2);
+ if (args.length === 1)
+ shallowEq(runTest1(input, args[0]), output);
+ else
+ shallowEq(runTest2(input, args[0], args[1]), output);
+ }
+ }
+}
+
+runTests();
+
+tests.push([new Foo(1,2,3,4,5), [1,2,3,4,5], -10, 10]);
+tests.push([new Foo(1,2,3,4,5), [1,2,3,4,5], -5, 10]);
+tests.push([new Foo(1,2,3,4,5), [2,3,4,5], -4, 10]);
+tests.push([new Foo(1,2,3,4,5), [2,3,4,5], -4]);
+runTests();
</ins></span></pre></div>
<a id="trunkJSTestsstressarraysliceosrexitjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/array-slice-osr-exit.js (0 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/stress/array-slice-osr-exit.js         (rev 0)
+++ trunk/JSTests/stress/array-slice-osr-exit.js        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -0,0 +1,74 @@
</span><ins>+function assert(b) {
+ if (!b)
+ throw new Error("Bad")
+}
+noInline(assert);
+
+class Foo extends Array {
+ constructor(...args) {
+ super(...args);
+ }
+};
+function shallowEq(a, b) {
+ assert(a.length === b.length);
+ for (let i = 0; i < a.length; i++)
+ assert(a[i] === b[i]);
+}
+noInline(shallowEq);
+
+let tests = [
+ [[1,2,3,4,5], [1,2,3,4,5], 0, 5],
+ [[1,2,3,4,5], [1,2,3,4,5], 0],
+ [[1,2,3,4,5], [4], -2, -1],
+ [[1,2,3,4,5], [5], -1],
+ [[1,2,3,4,5], [5], -1, 5],
+ [[1,2,3,4,5], [], -10, -20],
+ [[1,2,3,4,5], [], -20, -10],
+ [[1,2,3,4,5], [], 6, 4],
+ [[1,2,3,4,5], [], 3, 2],
+ [[1,2,3,4,5], [4,5], 3, 10],
+ [[1,2,3,4,5], [3,4,5], 2, 10],
+ [[1,2,3,4,5], [1,2,3,4,5], -10, 10],
+ [[1,2,3,4,5], [1,2,3,4,5], -5, 10],
+ [[1,2,3,4,5], [2,3,4,5], -4, 10],
+];
+tests.push([new Foo(1,2,3,4,5), [1,2,3,4,5], -10, 10]);
+tests.push([new Foo(1,2,3,4,5), [1,2,3,4,5], -5, 10]);
+tests.push([new Foo(1,2,3,4,5), [2,3,4,5], -4, 10]);
+tests.push([new Foo(1,2,3,4,5), [2,3,4,5], -4]);
+
+function runTest1(a, b) {
+ let result = a.slice(b);
+ assert(a instanceof Foo === result instanceof Foo);
+ return result;
+}
+noInline(runTest1);
+
+function runTest2(a, b, c) {
+ let result = a.slice(b, c);
+ assert(a instanceof Foo === result instanceof Foo);
+ return result;
+}
+noInline(runTest2);
+
+function addRandomProperties(input) {
+ for (let i = 0; i < 4; i++) {
+ input["prop" + i + ((Math.random() * 100000) | 0)] = i;
+ }
+}
+noInline(addRandomProperties);
+
+function runTests() {
+ for (let i = 0; i < 10000; i++) {
+ for (let [input, output, ...args] of tests) {
+ addRandomProperties(input);
+ assert(args.length === 1 || args.length === 2);
+ if (args.length === 1)
+ shallowEq(runTest1(input, args[0]), output);
+ else
+ shallowEq(runTest2(input, args[0], args[1]), output);
+ }
+ }
+}
+
+runTests();
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (210475 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2017-01-07 07:42:19 UTC (rev 210475)
+++ trunk/Source/JavaScriptCore/ChangeLog        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -1,3 +1,92 @@
</span><ins>+2017-01-06 Saam Barati <sbarati@apple.com>
+
+ Add a slice intrinsic to the DFG/FTL
+ https://bugs.webkit.org/show_bug.cgi?id=166707
+
+ Reviewed by Filip Pizlo.
+
+ The gist of this patch is to inline Array.prototype.slice
+ into the DFG/FTL. The implementation in the DFG-backend
+ and FTLLowerDFGToB3 is just a straight forward implementation
+ of what the C function is doing. The more interesting bits
+ of this patch are setting up the proper watchpoints and conditions
+ in the executing code to prove that its safe to skip all of the
+ observable JS actions that Array.prototype.slice normally does.
+
+ We perform the following proofs:
+ 1. Array.prototype.constructor has not changed (via a watchpoint).
+ 2. That Array.prototype.constructor[Symbol.species] has not changed (via a watchpoint).
+ 3. The global object is not having a bad time.
+ 3. The array that is being sliced has an original array structure.
+ 5. Array.prototype/Object.prototype have not transitioned.
+
+ Conditions 1, 2, and 3 are strictly required.
+
+ 4 is ensuring a couple things:
+ 1. That a "constructor" property hasn't been added to the array
+ we're slicing since we're supposed to perform a Get(array, "constructor").
+ 2. That we're not slicing an instance of a subclass of Array.
+
+ We could relax 4.1 in the future if we find other ways to test if
+ the incoming array hasn't changed the "constructor" property.
+
+ I'm seeing a 5% speedup on crypto-pbkdf2 and often a 1% speedup on
+ the total benchmark (the results are sometimes noisy).
+
+ * bytecode/ExitKind.cpp:
+ (JSC::exitKindToString):
+ * bytecode/ExitKind.h:
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+ * dfg/DFGClobberize.h:
+ (JSC::DFG::clobberize):
+ * dfg/DFGDoesGC.cpp:
+ (JSC::DFG::doesGC):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * dfg/DFGNode.h:
+ (JSC::DFG::Node::hasHeapPrediction):
+ (JSC::DFG::Node::hasArrayMode):
+ * dfg/DFGNodeType.h:
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ * dfg/DFGSafeToExecute.h:
+ (JSC::DFG::safeToExecute):
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::compileArraySlice):
+ * dfg/DFGSpeculativeJIT.h:
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * ftl/FTLCapabilities.cpp:
+ (JSC::FTL::canCompile):
+ * ftl/FTLLowerDFGToB3.cpp:
+ (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+ (JSC::FTL::DFG::LowerDFGToB3::compileArraySlice):
+ * jit/AssemblyHelpers.cpp:
+ (JSC::AssemblyHelpers::emitLoadStructure):
+ * runtime/ArrayPrototype.cpp:
+ (JSC::ArrayPrototype::finishCreation):
+ (JSC::speciesWatchpointIsValid):
+ (JSC::speciesConstructArray):
+ (JSC::arrayProtoFuncSlice):
+ (JSC::arrayProtoPrivateFuncConcatMemcpy):
+ (JSC::ArrayPrototype::initializeSpeciesWatchpoint):
+ (JSC::ArrayPrototypeAdaptiveInferredPropertyWatchpoint::handleFire):
+ (JSC::speciesWatchpointsValid): Deleted.
+ (JSC::ArrayPrototype::attemptToInitializeSpeciesWatchpoint): Deleted.
+ * runtime/ArrayPrototype.h:
+ (JSC::ArrayPrototype::speciesWatchpointStatus): Deleted.
+ (): Deleted.
+ * runtime/Intrinsic.h:
+ * runtime/JSGlobalObject.cpp:
+ (JSC::JSGlobalObject::JSGlobalObject):
+ (JSC::JSGlobalObject::init):
+ * runtime/JSGlobalObject.h:
+ (JSC::JSGlobalObject::arraySpeciesWatchpoint):
+
</ins><span class="cx"> 2017-01-06 Mark Lam <mark.lam@apple.com>
</span><span class="cx">
</span><span class="cx"> The ObjC API's JSVirtualMachine's map tables need to be guarded by a lock.
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGAbstractInterpreterInlinesh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h (210475 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h        2017-01-07 07:42:19 UTC (rev 210475)
+++ trunk/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -1650,6 +1650,25 @@
</span><span class="cx"> clobberWorld(node->origin.semantic, clobberLimit);
</span><span class="cx"> forNode(node).setType(SpecBytecodeNumber);
</span><span class="cx"> break;
</span><ins>+
+ case ArraySlice:
+ IndexingType indexingType;
+ switch (node->arrayMode().type()) {
+ case Array::Double:
+ indexingType = ArrayWithDouble;
+ break;
+ case Array::Int32:
+ indexingType = ArrayWithInt32;
+ break;
+ case Array::Contiguous:
+ indexingType = ArrayWithContiguous;
+ break;
+ default:
+ DFG_CRASH(m_graph, node, "Bad array mode.");
+ }
+
+ forNode(node).set(m_graph, m_graph.globalObjectFor(node->origin.semantic)->arrayStructureForIndexingTypeDuringAllocation(indexingType));
+ break;
</ins><span class="cx">
</span><span class="cx"> case ArrayPop:
</span><span class="cx"> clobberWorld(node->origin.semantic, clobberLimit);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGByteCodeParsercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp (210475 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp        2017-01-07 07:42:19 UTC (rev 210475)
+++ trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -2246,6 +2246,92 @@
</span><span class="cx"> return false;
</span><span class="cx"> }
</span><span class="cx"> }
</span><ins>+
+ case ArraySliceIntrinsic: {
+#if USE(JSVALUE32_64)
+ if (isX86()) {
+ // There aren't enough registers for this to be done easily.
+ return false;
+ }
+#endif
+ if (argumentCountIncludingThis < 2)
+ return false;
+
+ if (m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadIndexingType)
+ || m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadConstantCache)
+ || m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadCache))
+ return false;
+
+ ArrayMode arrayMode = getArrayMode(m_currentInstruction[OPCODE_LENGTH(op_call) - 2].u.arrayProfile);
+ if (!arrayMode.isJSArray())
+ return false;
+
+ if (arrayMode.arrayClass() != Array::OriginalArray)
+ return false;
+
+ switch (arrayMode.type()) {
+ case Array::Double:
+ case Array::Int32:
+ case Array::Contiguous: {
+ JSGlobalObject* globalObject = m_graph.globalObjectFor(currentNodeOrigin().semantic);
+
+ InlineWatchpointSet& objectPrototypeTransition = globalObject->objectPrototype()->structure()->transitionWatchpointSet();
+ InlineWatchpointSet& arrayPrototypeTransition = globalObject->arrayPrototype()->structure()->transitionWatchpointSet();
+
+ // FIXME: We could easily relax the Array/Object.prototype transition as long as we OSR exitted if we saw a hole.
+ if (globalObject->arraySpeciesWatchpoint().isStillValid()
+ && globalObject->havingABadTimeWatchpoint()->isStillValid()
+ && arrayPrototypeTransition.isStillValid()
+ && objectPrototypeTransition.isStillValid()
+ && globalObject->arrayPrototypeChainIsSane()) {
+
+ m_graph.watchpoints().addLazily(globalObject->arraySpeciesWatchpoint());
+ m_graph.watchpoints().addLazily(globalObject->havingABadTimeWatchpoint());
+ m_graph.watchpoints().addLazily(arrayPrototypeTransition);
+ m_graph.watchpoints().addLazily(objectPrototypeTransition);
+
+ insertChecks();
+
+ Node* array = get(virtualRegisterForArgument(0, registerOffset));
+ // We do a few things here to prove that we aren't skipping doing side-effects in an observable way:
+ // 1. We ensure that the "constructor" property hasn't been changed (because the observable
+ // effects of slice require that we perform a Get(array, "constructor") and we can skip
+ // that if we're an original array structure.
+ //
+ // 2. We check that the array we're calling slice on has the same global object as the lexical
+ // global object that this code is running in. This requirement is necessary because we setup the
+ // watchpoints above on the lexical global object. This means that code that calls slice on
+ // arrays produced by other global objects won't get this optimization. We could relax this
+ // requirement in the future by checking that the watchpoint hasn't fired at runtime in the code
+ // we generate instead of registering it as a watchpoint that would invalidate the compilation.
+ //
+ // 3. By proving we're an original array structure, we guarantee that the incoming array
+ // isn't a subclass of Array.
+
+ Structure* structure = arrayMode.originalArrayStructure(m_graph, currentNodeOrigin().semantic);
+ RELEASE_ASSERT(structure);
+ addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(structure)), array);
+
+ addVarArgChild(array);
+ addVarArgChild(get(virtualRegisterForArgument(1, registerOffset))); // Start index.
+ if (argumentCountIncludingThis >= 3)
+ addVarArgChild(get(virtualRegisterForArgument(2, registerOffset))); // End index.
+ addVarArgChild(addToGraph(GetButterfly, array));
+
+ Node* arraySlice = addToGraph(Node::VarArg, ArraySlice, OpInfo(arrayMode.asWord()), OpInfo(prediction));
+ set(VirtualRegister(resultOperand), arraySlice);
+ return true;
+ }
+
+ return false;
+ }
+ default:
+ return false;
+ }
+
+ RELEASE_ASSERT_NOT_REACHED();
+ return false;
+ }
</ins><span class="cx">
</span><span class="cx"> case ArrayPopIntrinsic: {
</span><span class="cx"> if (argumentCountIncludingThis != 1)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGClobberizeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGClobberize.h (210475 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGClobberize.h        2017-01-07 07:42:19 UTC (rev 210475)
+++ trunk/Source/JavaScriptCore/dfg/DFGClobberize.h        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -503,6 +503,29 @@
</span><span class="cx"> read(MiscFields);
</span><span class="cx"> def(HeapLocation(IsFunctionLoc, MiscFields, node->child1()), LazyNode(node));
</span><span class="cx"> return;
</span><ins>+
+ case ArraySlice:
+ read(MiscFields);
+ read(JSCell_indexingType);
+ read(JSCell_structureID);
+ read(JSObject_butterfly);
+ read(Butterfly_publicLength);
+ switch (node->arrayMode().type()) {
+ case Array::Double:
+ read(IndexedDoubleProperties);
+ break;
+ case Array::Int32:
+ read(IndexedInt32Properties);
+ break;
+ case Array::Contiguous:
+ read(IndexedContiguousProperties);
+ break;
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+ read(HeapObjectCount);
+ write(HeapObjectCount);
+ return;
</ins><span class="cx">
</span><span class="cx"> case GetById:
</span><span class="cx"> case GetByIdFlush:
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGDoesGCcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGDoesGC.cpp (210475 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGDoesGC.cpp        2017-01-07 07:42:19 UTC (rev 210475)
+++ trunk/Source/JavaScriptCore/dfg/DFGDoesGC.cpp        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -309,6 +309,7 @@
</span><span class="cx"> case ToLowerCase:
</span><span class="cx"> case CallDOMGetter:
</span><span class="cx"> case CallDOM:
</span><ins>+ case ArraySlice:
</ins><span class="cx"> return true;
</span><span class="cx">
</span><span class="cx"> case MultiPutByOffset:
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGFixupPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp (210475 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp        2017-01-07 07:42:19 UTC (rev 210475)
+++ trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -931,6 +931,14 @@
</span><span class="cx"> fixEdge<KnownCellUse>(node->child1());
</span><span class="cx"> break;
</span><span class="cx"> }
</span><ins>+
+ case ArraySlice: {
+ fixEdge<KnownCellUse>(m_graph.varArgChild(node, 0));
+ fixEdge<Int32Use>(m_graph.varArgChild(node, 1));
+ if (node->numChildren() == 4)
+ fixEdge<Int32Use>(m_graph.varArgChild(node, 2));
+ break;
+ }
</ins><span class="cx">
</span><span class="cx"> case RegExpExec:
</span><span class="cx"> case RegExpTest: {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGNodeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGNode.h (210475 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGNode.h        2017-01-07 07:42:19 UTC (rev 210475)
+++ trunk/Source/JavaScriptCore/dfg/DFGNode.h        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -1472,6 +1472,7 @@
</span><span class="cx"> case GetArgument:
</span><span class="cx"> case ArrayPop:
</span><span class="cx"> case ArrayPush:
</span><ins>+ case ArraySlice:
</ins><span class="cx"> case RegExpExec:
</span><span class="cx"> case RegExpTest:
</span><span class="cx"> case GetGlobalVar:
</span><span class="lines">@@ -1796,6 +1797,7 @@
</span><span class="cx"> case Arrayify:
</span><span class="cx"> case ArrayifyToStructure:
</span><span class="cx"> case ArrayPush:
</span><ins>+ case ArraySlice:
</ins><span class="cx"> case ArrayPop:
</span><span class="cx"> case HasIndexedProperty:
</span><span class="cx"> return true;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGNodeTypeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGNodeType.h (210475 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGNodeType.h        2017-01-07 07:42:19 UTC (rev 210475)
+++ trunk/Source/JavaScriptCore/dfg/DFGNodeType.h        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -248,6 +248,7 @@
</span><span class="cx"> /* Optimizations for array mutation. */\
</span><span class="cx"> macro(ArrayPush, NodeResultJS | NodeMustGenerate) \
</span><span class="cx"> macro(ArrayPop, NodeResultJS | NodeMustGenerate) \
</span><ins>+ macro(ArraySlice, NodeResultJS | NodeMustGenerate | NodeHasVarArgs) \
</ins><span class="cx"> \
</span><span class="cx"> /* Optimizations for regular expression matching. */\
</span><span class="cx"> macro(RegExpExec, NodeResultJS | NodeMustGenerate) \
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGPredictionPropagationPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp (210475 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp        2017-01-07 07:42:19 UTC (rev 210475)
+++ trunk/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -678,6 +678,7 @@
</span><span class="cx">
</span><span class="cx"> case ArrayPop:
</span><span class="cx"> case ArrayPush:
</span><ins>+ case ArraySlice:
</ins><span class="cx"> case RegExpExec:
</span><span class="cx"> case RegExpTest:
</span><span class="cx"> case StringReplace:
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGSafeToExecuteh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGSafeToExecute.h (210475 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGSafeToExecute.h        2017-01-07 07:42:19 UTC (rev 210475)
+++ trunk/Source/JavaScriptCore/dfg/DFGSafeToExecute.h        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -402,6 +402,7 @@
</span><span class="cx"> case GetArrayLength:
</span><span class="cx"> case ArrayPush:
</span><span class="cx"> case ArrayPop:
</span><ins>+ case ArraySlice:
</ins><span class="cx"> case StringCharAt:
</span><span class="cx"> case StringCharCodeAt:
</span><span class="cx"> return node->arrayMode().alreadyChecked(graph, node, state.forNode(node->child1()));
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGSpeculativeJITcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp (210475 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp        2017-01-07 07:42:19 UTC (rev 210475)
+++ trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -7171,6 +7171,127 @@
</span><span class="cx"> int32Result(resultGPR, node);
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+void SpeculativeJIT::compileArraySlice(Node* node)
+{
+ ASSERT(node->op() == ArraySlice);
+
+ JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic);
+
+ GPRTemporary temp(this);
+ StorageOperand storage(this, node->numChildren() == 3 ? m_jit.graph().varArgChild(node, 2) : m_jit.graph().varArgChild(node, 3));
+ GPRTemporary result(this);
+
+ GPRReg storageGPR = storage.gpr();
+ GPRReg resultGPR = result.gpr();
+ GPRReg tempGPR = temp.gpr();
+
+ IndexingType indexingType;
+ switch (node->arrayMode().type()) {
+ case Array::Int32:
+ indexingType = ArrayWithInt32;
+ break;
+ case Array::Contiguous:
+ indexingType = ArrayWithContiguous;
+ break;
+ case Array::Double:
+ indexingType = ArrayWithDouble;
+ break;
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+
+ auto populateIndex = [&] (unsigned childIndex, GPRReg length, GPRReg result) {
+ SpeculateInt32Operand index(this, m_jit.graph().varArgChild(node, childIndex));
+ GPRReg indexGPR = index.gpr();
+ MacroAssembler::JumpList done;
+ auto isPositive = m_jit.branch32(MacroAssembler::GreaterThanOrEqual, indexGPR, TrustedImm32(0));
+ m_jit.move(length, result);
+ done.append(m_jit.branchAdd32(MacroAssembler::PositiveOrZero, indexGPR, result));
+ m_jit.move(TrustedImm32(0), result);
+ done.append(m_jit.jump());
+
+ isPositive.link(&m_jit);
+ m_jit.move(indexGPR, result);
+ done.append(m_jit.branch32(MacroAssembler::BelowOrEqual, result, length));
+ m_jit.move(length, result);
+
+ done.link(&m_jit);
+ };
+
+ {
+ GPRTemporary tempLength(this);
+ GPRReg lengthGPR = tempLength.gpr();
+ m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), lengthGPR);
+
+ if (node->numChildren() == 4)
+ populateIndex(2, lengthGPR, tempGPR);
+ else
+ m_jit.move(lengthGPR, tempGPR);
+
+ GPRTemporary tempStartIndex(this);
+ GPRReg startGPR = tempStartIndex.gpr();
+ populateIndex(1, lengthGPR, startGPR);
+
+ auto tooBig = m_jit.branch32(MacroAssembler::Above, startGPR, tempGPR);
+ m_jit.sub32(startGPR, tempGPR); // the size of the array we'll make.
+ auto done = m_jit.jump();
+
+ tooBig.link(&m_jit);
+ m_jit.move(TrustedImm32(0), tempGPR);
+ done.link(&m_jit);
+ }
+
+ const bool shouldConvertLargeSizeToArrayStorage = false;
+ compileAllocateNewArrayWithSize(globalObject, resultGPR, tempGPR, indexingType, shouldConvertLargeSizeToArrayStorage);
+
+ GPRTemporary temp3(this);
+ GPRTemporary temp4(this);
+ GPRReg tempValue = temp3.gpr();
+ GPRReg loadIndex = temp4.gpr();
+
+ m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), tempValue);
+ if (node->numChildren() == 4)
+ populateIndex(2, tempValue, tempGPR);
+ else
+ m_jit.move(tempValue, tempGPR);
+ populateIndex(1, tempValue, loadIndex);
+
+ GPRTemporary temp5(this);
+ GPRReg storeIndex = temp5.gpr();
+ m_jit.move(TrustedImmPtr(0), storeIndex);
+
+ GPRTemporary temp2(this);
+ GPRReg resultButterfly = temp2.gpr();
+
+ m_jit.loadPtr(MacroAssembler::Address(resultGPR, JSObject::butterflyOffset()), resultButterfly);
+ m_jit.zeroExtend32ToPtr(tempGPR, tempGPR);
+ m_jit.zeroExtend32ToPtr(loadIndex, loadIndex);
+ auto done = m_jit.branchPtr(MacroAssembler::AboveOrEqual, loadIndex, tempGPR);
+
+ auto loop = m_jit.label();
+#if USE(JSVALUE64)
+ m_jit.load64(
+ MacroAssembler::BaseIndex(storageGPR, loadIndex, MacroAssembler::TimesEight), tempValue);
+ m_jit.store64(
+ tempValue, MacroAssembler::BaseIndex(resultButterfly, storeIndex, MacroAssembler::TimesEight));
+#else
+ m_jit.load32(
+ MacroAssembler::BaseIndex(storageGPR, loadIndex, MacroAssembler::TimesEight, PayloadOffset), tempValue);
+ m_jit.store32(
+ tempValue, MacroAssembler::BaseIndex(resultButterfly, storeIndex, MacroAssembler::TimesEight, PayloadOffset));
+ m_jit.load32(
+ MacroAssembler::BaseIndex(storageGPR, loadIndex, MacroAssembler::TimesEight, TagOffset), tempValue);
+ m_jit.store32(
+ tempValue, MacroAssembler::BaseIndex(resultButterfly, storeIndex, MacroAssembler::TimesEight, TagOffset));
+#endif // USE(JSVALUE64)
+ m_jit.addPtr(TrustedImm32(1), loadIndex);
+ m_jit.addPtr(TrustedImm32(1), storeIndex);
+ m_jit.branchPtr(MacroAssembler::Below, loadIndex, tempGPR).linkTo(loop, &m_jit);
+
+ done.link(&m_jit);
+ cellResult(resultGPR, node);
+}
+
</ins><span class="cx"> void SpeculativeJIT::compileNotifyWrite(Node* node)
</span><span class="cx"> {
</span><span class="cx"> WatchpointSet* set = node->watchpointSet();
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGSpeculativeJITh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h (210475 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h        2017-01-07 07:42:19 UTC (rev 210475)
+++ trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -2680,6 +2680,7 @@
</span><span class="cx"> void compileSpread(Node*);
</span><span class="cx"> void compileNewArrayWithSpread(Node*);
</span><span class="cx"> void compileGetRestLength(Node*);
</span><ins>+ void compileArraySlice(Node*);
</ins><span class="cx"> void compileNotifyWrite(Node*);
</span><span class="cx"> bool compileRegExpExec(Node*);
</span><span class="cx"> void compileIsObjectOrNull(Node*);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGSpeculativeJIT32_64cpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp (210475 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp        2017-01-07 07:42:19 UTC (rev 210475)
+++ trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -3550,6 +3550,11 @@
</span><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+ case ArraySlice: {
+ compileArraySlice(node);
+ break;
+ }
+
</ins><span class="cx"> case DFG::Jump: {
</span><span class="cx"> jump(node->targetBlock());
</span><span class="cx"> noResult(node);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGSpeculativeJIT64cpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp (210475 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp        2017-01-07 07:42:19 UTC (rev 210475)
+++ trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -3462,6 +3462,11 @@
</span><span class="cx"> }
</span><span class="cx"> break;
</span><span class="cx"> }
</span><ins>+
+ case ArraySlice: {
+ compileArraySlice(node);
+ break;
+ }
</ins><span class="cx">
</span><span class="cx"> case ArrayPop: {
</span><span class="cx"> ASSERT(node->arrayMode().isJSArray());
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLCapabilitiescpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp (210475 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp        2017-01-07 07:42:19 UTC (rev 210475)
+++ trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -282,6 +282,7 @@
</span><span class="cx"> case CheckDOM:
</span><span class="cx"> case CallDOM:
</span><span class="cx"> case CallDOMGetter:
</span><ins>+ case ArraySlice:
</ins><span class="cx"> // These are OK.
</span><span class="cx"> break;
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLLowerDFGToB3cpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp (210475 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp        2017-01-07 07:42:19 UTC (rev 210475)
+++ trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -706,6 +706,9 @@
</span><span class="cx"> case ArrayPop:
</span><span class="cx"> compileArrayPop();
</span><span class="cx"> break;
</span><ins>+ case ArraySlice:
+ compileArraySlice();
+ break;
</ins><span class="cx"> case CreateActivation:
</span><span class="cx"> compileCreateActivation();
</span><span class="cx"> break;
</span><span class="lines">@@ -3860,6 +3863,83 @@
</span><span class="cx"> return;
</span><span class="cx"> }
</span><span class="cx"> }
</span><ins>+
+ void compileArraySlice()
+ {
+ JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
+
+ LValue sourceStorage = lowStorage(m_node->numChildren() == 3 ? m_graph.varArgChild(m_node, 2) : m_graph.varArgChild(m_node, 3));
+ LValue inputLength = m_out.load32(sourceStorage, m_heaps.Butterfly_publicLength);
+
+ LValue endBoundary;
+ if (m_node->numChildren() == 3)
+ endBoundary = m_out.load32(sourceStorage, m_heaps.Butterfly_publicLength);
+ else {
+ endBoundary = lowInt32(m_graph.varArgChild(m_node, 2));
+ endBoundary = m_out.select(m_out.greaterThanOrEqual(endBoundary, m_out.constInt32(0)),
+ m_out.select(m_out.above(endBoundary, inputLength), inputLength, endBoundary),
+ m_out.select(m_out.lessThan(m_out.add(inputLength, endBoundary), m_out.constInt32(0)), m_out.constInt32(0), m_out.add(inputLength, endBoundary)));
+ }
+
+ LValue startIndex = lowInt32(m_graph.varArgChild(m_node, 1));
+ startIndex = m_out.select(m_out.greaterThanOrEqual(startIndex, m_out.constInt32(0)),
+ m_out.select(m_out.above(startIndex, inputLength), inputLength, startIndex),
+ m_out.select(m_out.lessThan(m_out.add(inputLength, startIndex), m_out.constInt32(0)), m_out.constInt32(0), m_out.add(inputLength, startIndex)));
+
+ LValue resultLength = m_out.select(m_out.below(startIndex, endBoundary),
+ m_out.sub(endBoundary, startIndex),
+ m_out.constInt32(0));
+
+ IndexingType indexingType;
+ switch (m_node->arrayMode().type()) {
+ case Array::Int32:
+ indexingType = ArrayWithInt32;
+ break;
+ case Array::Contiguous:
+ indexingType = ArrayWithContiguous;
+ break;
+ case Array::Double:
+ indexingType = ArrayWithDouble;
+ break;
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+
+ Structure* structure = globalObject->arrayStructureForIndexingTypeDuringAllocation(indexingType);
+ auto arrayResult = allocateJSArray(resultLength, structure, false, false);
+
+ LBasicBlock loop = m_out.newBlock();
+ LBasicBlock continuation = m_out.newBlock();
+
+ resultLength = m_out.zeroExtPtr(resultLength);
+ ValueFromBlock startLoadIndex = m_out.anchor(m_out.zeroExtPtr(startIndex));
+ ValueFromBlock startStoreIndex = m_out.anchor(m_out.constIntPtr(0));
+
+ m_out.branch(
+ m_out.below(m_out.constIntPtr(0), resultLength), unsure(loop), unsure(continuation));
+
+ LBasicBlock lastNext = m_out.appendTo(loop, continuation);
+ LValue storeIndex = m_out.phi(pointerType(), startStoreIndex);
+ LValue loadIndex = m_out.phi(pointerType(), startLoadIndex);
+ IndexedAbstractHeap& heap = m_heaps.forArrayType(m_node->arrayMode().type());
+ if (indexingType == ArrayWithDouble) {
+ LValue value = m_out.loadDouble(m_out.baseIndex(heap, sourceStorage, loadIndex));
+ m_out.storeDouble(value, m_out.baseIndex(heap, arrayResult.butterfly, storeIndex));
+ } else {
+ LValue value = m_out.load64(m_out.baseIndex(heap, sourceStorage, loadIndex));
+ m_out.store64(value, m_out.baseIndex(heap, arrayResult.butterfly, storeIndex));
+ }
+ LValue nextStoreIndex = m_out.add(storeIndex, m_out.constIntPtr(1));
+ m_out.addIncomingToPhi(storeIndex, m_out.anchor(nextStoreIndex));
+ m_out.addIncomingToPhi(loadIndex, m_out.anchor(m_out.add(loadIndex, m_out.constIntPtr(1))));
+ m_out.branch(
+ m_out.below(nextStoreIndex, resultLength), unsure(loop), unsure(continuation));
+
+ m_out.appendTo(continuation, lastNext);
+
+ mutatorFence();
+ setJSValue(arrayResult.array);
+ }
</ins><span class="cx">
</span><span class="cx"> void compileArrayPop()
</span><span class="cx"> {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorejitAssemblyHelperscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/jit/AssemblyHelpers.cpp (210475 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/jit/AssemblyHelpers.cpp        2017-01-07 07:42:19 UTC (rev 210475)
+++ trunk/Source/JavaScriptCore/jit/AssemblyHelpers.cpp        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -447,6 +447,7 @@
</span><span class="cx"> void AssemblyHelpers::emitLoadStructure(RegisterID source, RegisterID dest, RegisterID scratch)
</span><span class="cx"> {
</span><span class="cx"> #if USE(JSVALUE64)
</span><ins>+ ASSERT(dest != scratch);
</ins><span class="cx"> load32(MacroAssembler::Address(source, JSCell::structureIDOffset()), dest);
</span><span class="cx"> loadPtr(vm()->heap.structureIDTable().base(), scratch);
</span><span class="cx"> loadPtr(MacroAssembler::BaseIndex(scratch, dest, MacroAssembler::TimesEight), dest);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeArrayPrototypecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/ArrayPrototype.cpp (210475 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/ArrayPrototype.cpp        2017-01-07 07:42:19 UTC (rev 210475)
+++ trunk/Source/JavaScriptCore/runtime/ArrayPrototype.cpp        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -98,7 +98,7 @@
</span><span class="cx"> JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("reverse", arrayProtoFuncReverse, DontEnum, 0);
</span><span class="cx"> JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().shiftPublicName(), arrayProtoFuncShift, DontEnum, 0);
</span><span class="cx"> JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().shiftPrivateName(), arrayProtoFuncShift, DontEnum | DontDelete | ReadOnly, 0);
</span><del>- JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->slice, arrayProtoFuncSlice, DontEnum, 2);
</del><ins>+ JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->slice, arrayProtoFuncSlice, DontEnum, 2, ArraySliceIntrinsic);
</ins><span class="cx"> JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("sort", arrayPrototypeSortCodeGenerator, DontEnum);
</span><span class="cx"> JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("splice", arrayProtoFuncSplice, DontEnum, 2);
</span><span class="cx"> JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("unshift", arrayProtoFuncUnShift, DontEnum, 1);
</span><span class="lines">@@ -191,21 +191,12 @@
</span><span class="cx"> throwTypeError(exec, scope, ASCIILiteral(ReadonlyPropertyWriteError));
</span><span class="cx"> }
</span><span class="cx">
</span><del>-inline bool speciesWatchpointsValid(ExecState* exec, JSObject* thisObject)
</del><ins>+inline bool speciesWatchpointIsValid(JSObject* thisObject)
</ins><span class="cx"> {
</span><del>- VM& vm = exec->vm();
- auto scope = DECLARE_THROW_SCOPE(vm);
-
</del><span class="cx"> ArrayPrototype* arrayPrototype = thisObject->globalObject()->arrayPrototype();
</span><del>- ArrayPrototype::SpeciesWatchpointStatus status = arrayPrototype->speciesWatchpointStatus();
- if (UNLIKELY(status == ArrayPrototype::SpeciesWatchpointStatus::Uninitialized)) {
- status = arrayPrototype->attemptToInitializeSpeciesWatchpoint(exec);
- RETURN_IF_EXCEPTION(scope, false);
- }
- ASSERT(status != ArrayPrototype::SpeciesWatchpointStatus::Uninitialized);
</del><span class="cx"> return !thisObject->hasCustomProperties()
</span><span class="cx"> && arrayPrototype == thisObject->getPrototypeDirect()
</span><del>- && status == ArrayPrototype::SpeciesWatchpointStatus::Initialized;
</del><ins>+ && arrayPrototype->globalObject()->arraySpeciesWatchpoint().isStillValid();
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> enum class SpeciesConstructResult {
</span><span class="lines">@@ -230,8 +221,7 @@
</span><span class="cx"> if (LIKELY(thisIsArray)) {
</span><span class="cx"> // Fast path in the normal case where the user has not set an own constructor and the Array.prototype.constructor is normal.
</span><span class="cx"> // We need prototype check for subclasses of Array, which are Array objects but have a different prototype by default.
</span><del>- bool isValid = speciesWatchpointsValid(exec, thisObject);
- RETURN_IF_EXCEPTION(scope, exceptionResult());
</del><ins>+ bool isValid = speciesWatchpointIsValid(thisObject);
</ins><span class="cx"> if (LIKELY(isValid))
</span><span class="cx"> return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
</span><span class="cx">
</span><span class="lines">@@ -920,29 +910,29 @@
</span><span class="cx">
</span><span class="cx"> EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec)
</span><span class="cx"> {
</span><del>- // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10
</del><ins>+ // https://tc39.github.io/ecma262/#sec-array.prototype.slice
</ins><span class="cx"> VM& vm = exec->vm();
</span><span class="cx"> auto scope = DECLARE_THROW_SCOPE(vm);
</span><span class="cx"> JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
</span><span class="cx"> ASSERT(!!scope.exception() == !thisObj);
</span><span class="cx"> if (UNLIKELY(!thisObj))
</span><del>- return encodedJSValue();
</del><ins>+ return { };
</ins><span class="cx"> unsigned length = getLength(exec, thisObj);
</span><del>- RETURN_IF_EXCEPTION(scope, encodedJSValue());
</del><ins>+ RETURN_IF_EXCEPTION(scope, { });
</ins><span class="cx">
</span><span class="cx"> unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length);
</span><del>- RETURN_IF_EXCEPTION(scope, encodedJSValue());
</del><ins>+ RETURN_IF_EXCEPTION(scope, { });
</ins><span class="cx"> unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length);
</span><del>- RETURN_IF_EXCEPTION(scope, encodedJSValue());
</del><ins>+ RETURN_IF_EXCEPTION(scope, { });
</ins><span class="cx">
</span><span class="cx"> std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, end - begin);
</span><span class="cx"> // We can only get an exception if we call some user function.
</span><span class="cx"> ASSERT(!!scope.exception() == (speciesResult.first == SpeciesConstructResult::Exception));
</span><span class="cx"> if (UNLIKELY(speciesResult.first == SpeciesConstructResult::Exception))
</span><del>- return encodedJSValue();
</del><ins>+ return { };
</ins><span class="cx">
</span><span class="cx"> bool okToDoFastPath = speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj) && length == getLength(exec, thisObj);
</span><del>- RETURN_IF_EXCEPTION(scope, encodedJSValue());
</del><ins>+ RETURN_IF_EXCEPTION(scope, { });
</ins><span class="cx"> if (LIKELY(okToDoFastPath)) {
</span><span class="cx"> if (JSArray* result = asArray(thisObj)->fastSlice(*exec, begin, end - begin))
</span><span class="cx"> return JSValue::encode(result);
</span><span class="lines">@@ -953,16 +943,16 @@
</span><span class="cx"> result = speciesResult.second;
</span><span class="cx"> else {
</span><span class="cx"> result = constructEmptyArray(exec, nullptr, end - begin);
</span><del>- RETURN_IF_EXCEPTION(scope, encodedJSValue());
</del><ins>+ RETURN_IF_EXCEPTION(scope, { });
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> unsigned n = 0;
</span><span class="cx"> for (unsigned k = begin; k < end; k++, n++) {
</span><span class="cx"> JSValue v = getProperty(exec, thisObj, k);
</span><del>- RETURN_IF_EXCEPTION(scope, encodedJSValue());
</del><ins>+ RETURN_IF_EXCEPTION(scope, { });
</ins><span class="cx"> if (v) {
</span><span class="cx"> result->putDirectIndex(exec, n, v, 0, PutDirectIndexShouldThrow);
</span><del>- RETURN_IF_EXCEPTION(scope, encodedJSValue());
</del><ins>+ RETURN_IF_EXCEPTION(scope, { });
</ins><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx"> scope.release();
</span><span class="lines">@@ -1249,8 +1239,7 @@
</span><span class="cx"> return JSValue::encode(jsNull());
</span><span class="cx">
</span><span class="cx"> // We need to check the species constructor here since checking it in the JS wrapper is too expensive for the non-optimizing tiers.
</span><del>- bool isValid = speciesWatchpointsValid(exec, firstArray);
- ASSERT(!scope.exception() || !isValid);
</del><ins>+ bool isValid = speciesWatchpointIsValid(firstArray);
</ins><span class="cx"> if (UNLIKELY(!isValid))
</span><span class="cx"> return JSValue::encode(jsNull());
</span><span class="cx">
</span><span class="lines">@@ -1342,15 +1331,18 @@
</span><span class="cx"> ArrayPrototype* m_arrayPrototype;
</span><span class="cx"> };
</span><span class="cx">
</span><del>-ArrayPrototype::SpeciesWatchpointStatus ArrayPrototype::attemptToInitializeSpeciesWatchpoint(ExecState* exec)
</del><ins>+void ArrayPrototype::initializeSpeciesWatchpoint(ExecState* exec)
</ins><span class="cx"> {
</span><del>- ASSERT(m_speciesWatchpointStatus == SpeciesWatchpointStatus::Uninitialized);
</del><ins>+ VM& vm = exec->vm();
</ins><span class="cx">
</span><del>- VM& vm = exec->vm();
</del><ins>+ RELEASE_ASSERT(!m_constructorWatchpoint);
+ RELEASE_ASSERT(!m_constructorSpeciesWatchpoint);
+
</ins><span class="cx"> auto scope = DECLARE_THROW_SCOPE(vm);
</span><ins>+ UNUSED_PARAM(scope);
</ins><span class="cx">
</span><span class="cx"> if (verbose)
</span><del>- dataLog("Attempting to initialize Array species watchpoints for Array.prototype: ", pointerDump(this), " with structure: ", pointerDump(this->structure()), "\nand Array: ", pointerDump(this->globalObject()->arrayConstructor()), " with structure: ", pointerDump(this->globalObject()->arrayConstructor()->structure()), "\n");
</del><ins>+ dataLog("Initializing Array species watchpoints for Array.prototype: ", pointerDump(this), " with structure: ", pointerDump(this->structure()), "\nand Array: ", pointerDump(this->globalObject()->arrayConstructor()), " with structure: ", pointerDump(this->globalObject()->arrayConstructor()->structure()), "\n");
</ins><span class="cx"> // First we need to make sure that the Array.prototype.constructor property points to Array
</span><span class="cx"> // and that Array[Symbol.species] is the primordial GetterSetter.
</span><span class="cx">
</span><span class="lines">@@ -1364,12 +1356,11 @@
</span><span class="cx"> ArrayConstructor* arrayConstructor = globalObject->arrayConstructor();
</span><span class="cx">
</span><span class="cx"> PropertySlot constructorSlot(this, PropertySlot::InternalMethodType::VMInquiry);
</span><del>- JSValue(this).get(exec, vm.propertyNames->constructor, constructorSlot);
- if (UNLIKELY(scope.exception())
- || constructorSlot.slotBase() != this
- || !constructorSlot.isCacheableValue()
- || constructorSlot.getValue(exec, vm.propertyNames->constructor) != arrayConstructor)
- return m_speciesWatchpointStatus = SpeciesWatchpointStatus::Fired;
</del><ins>+ this->getOwnPropertySlot(this, exec, vm.propertyNames->constructor, constructorSlot);
+ ASSERT(!scope.exception());
+ ASSERT(constructorSlot.slotBase() == this);
+ ASSERT(constructorSlot.isCacheableValue());
+ RELEASE_ASSERT(constructorSlot.getValue(exec, vm.propertyNames->constructor) == arrayConstructor);
</ins><span class="cx">
</span><span class="cx"> Structure* constructorStructure = arrayConstructor->structure(vm);
</span><span class="cx"> if (constructorStructure->isDictionary())
</span><span class="lines">@@ -1376,12 +1367,11 @@
</span><span class="cx"> constructorStructure = constructorStructure->flattenDictionaryStructure(vm, arrayConstructor);
</span><span class="cx">
</span><span class="cx"> PropertySlot speciesSlot(arrayConstructor, PropertySlot::InternalMethodType::VMInquiry);
</span><del>- JSValue(arrayConstructor).get(exec, vm.propertyNames->speciesSymbol, speciesSlot);
- if (UNLIKELY(scope.exception())
- || speciesSlot.slotBase() != arrayConstructor
- || !speciesSlot.isCacheableGetter()
- || speciesSlot.getterSetter() != globalObject->speciesGetterSetter())
- return m_speciesWatchpointStatus = SpeciesWatchpointStatus::Fired;
</del><ins>+ arrayConstructor->getOwnPropertySlot(arrayConstructor, exec, vm.propertyNames->speciesSymbol, speciesSlot);
+ ASSERT(!scope.exception());
+ ASSERT(speciesSlot.slotBase() == arrayConstructor);
+ ASSERT(speciesSlot.isCacheableGetter());
+ RELEASE_ASSERT(speciesSlot.getterSetter() == globalObject->speciesGetterSetter());
</ins><span class="cx">
</span><span class="cx"> // Now we need to setup the watchpoints to make sure these conditions remain valid.
</span><span class="cx"> prototypeStructure->startWatchingPropertyForReplacements(vm, constructorSlot.cachedOffset());
</span><span class="lines">@@ -1390,8 +1380,8 @@
</span><span class="cx"> ObjectPropertyCondition constructorCondition = ObjectPropertyCondition::equivalence(vm, this, this, vm.propertyNames->constructor.impl(), arrayConstructor);
</span><span class="cx"> ObjectPropertyCondition speciesCondition = ObjectPropertyCondition::equivalence(vm, this, arrayConstructor, vm.propertyNames->speciesSymbol.impl(), globalObject->speciesGetterSetter());
</span><span class="cx">
</span><del>- if (!constructorCondition.isWatchable() || !speciesCondition.isWatchable())
- return m_speciesWatchpointStatus = SpeciesWatchpointStatus::Fired;
</del><ins>+ RELEASE_ASSERT(constructorCondition.isWatchable());
+ RELEASE_ASSERT(speciesCondition.isWatchable());
</ins><span class="cx">
</span><span class="cx"> m_constructorWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(constructorCondition, this);
</span><span class="cx"> m_constructorWatchpoint->install();
</span><span class="lines">@@ -1398,8 +1388,6 @@
</span><span class="cx">
</span><span class="cx"> m_constructorSpeciesWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(speciesCondition, this);
</span><span class="cx"> m_constructorSpeciesWatchpoint->install();
</span><del>-
- return m_speciesWatchpointStatus = SpeciesWatchpointStatus::Initialized;
</del><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> ArrayPrototypeAdaptiveInferredPropertyWatchpoint::ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition& key, ArrayPrototype* prototype)
</span><span class="lines">@@ -1418,7 +1406,8 @@
</span><span class="cx"> if (verbose)
</span><span class="cx"> WTF::dataLog(stringDetail, "\n");
</span><span class="cx">
</span><del>- m_arrayPrototype->m_speciesWatchpointStatus = ArrayPrototype::SpeciesWatchpointStatus::Fired;
</del><ins>+ JSGlobalObject* globalObject = m_arrayPrototype->globalObject();
+ globalObject->arraySpeciesWatchpoint().fireAll(globalObject->vm(), stringDetail);
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> } // namespace JSC
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeArrayPrototypeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/ArrayPrototype.h (210475 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/ArrayPrototype.h        2017-01-07 07:42:19 UTC (rev 210475)
+++ trunk/Source/JavaScriptCore/runtime/ArrayPrototype.h        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -49,8 +49,7 @@
</span><span class="cx"> return Structure::create(vm, globalObject, prototype, TypeInfo(DerivedArrayType, StructureFlags), info(), ArrayClass);
</span><span class="cx"> }
</span><span class="cx">
</span><del>- SpeciesWatchpointStatus speciesWatchpointStatus() const { return m_speciesWatchpointStatus; }
- SpeciesWatchpointStatus attemptToInitializeSpeciesWatchpoint(ExecState*);
</del><ins>+ void initializeSpeciesWatchpoint(ExecState*);
</ins><span class="cx">
</span><span class="cx"> static const bool needsDestruction = false;
</span><span class="cx"> // We don't need destruction since we use a finalizer.
</span><span class="lines">@@ -64,7 +63,6 @@
</span><span class="cx"> friend ArrayPrototypeAdaptiveInferredPropertyWatchpoint;
</span><span class="cx"> std::unique_ptr<ArrayPrototypeAdaptiveInferredPropertyWatchpoint> m_constructorWatchpoint;
</span><span class="cx"> std::unique_ptr<ArrayPrototypeAdaptiveInferredPropertyWatchpoint> m_constructorSpeciesWatchpoint;
</span><del>- SpeciesWatchpointStatus m_speciesWatchpointStatus { SpeciesWatchpointStatus::Uninitialized };
</del><span class="cx"> };
</span><span class="cx">
</span><span class="cx"> EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState*);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeIntrinsich"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/Intrinsic.h (210475 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/Intrinsic.h        2017-01-07 07:42:19 UTC (rev 210475)
+++ trunk/Source/JavaScriptCore/runtime/Intrinsic.h        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -40,6 +40,7 @@
</span><span class="cx"> TanIntrinsic,
</span><span class="cx"> ArrayPushIntrinsic,
</span><span class="cx"> ArrayPopIntrinsic,
</span><ins>+ ArraySliceIntrinsic,
</ins><span class="cx"> CharCodeAtIntrinsic,
</span><span class="cx"> CharAtIntrinsic,
</span><span class="cx"> FromCharCodeIntrinsic,
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSGlobalObjectcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSGlobalObject.cpp (210475 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSGlobalObject.cpp        2017-01-07 07:42:19 UTC (rev 210475)
+++ trunk/Source/JavaScriptCore/runtime/JSGlobalObject.cpp        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -332,6 +332,7 @@
</span><span class="cx"> , m_varInjectionWatchpoint(adoptRef(new WatchpointSet(IsWatched)))
</span><span class="cx"> , m_weakRandom(Options::forceWeakRandomSeed() ? Options::forcedWeakRandomSeed() : static_cast<unsigned>(randomNumber() * (std::numeric_limits<unsigned>::max() + 1.0)))
</span><span class="cx"> , m_arrayIteratorProtocolWatchpoint(IsWatched)
</span><ins>+ , m_arraySpeciesWatchpoint(IsWatched)
</ins><span class="cx"> , m_templateRegistry(vm)
</span><span class="cx"> , m_evalEnabled(true)
</span><span class="cx"> , m_runtimeFlags()
</span><span class="lines">@@ -944,6 +945,8 @@
</span><span class="cx"> m_arrayPrototypeSymbolIteratorWatchpoint = std::make_unique<ArrayIteratorAdaptiveWatchpoint>(condition, this);
</span><span class="cx"> m_arrayPrototypeSymbolIteratorWatchpoint->install();
</span><span class="cx"> }
</span><ins>+
+ this->arrayPrototype()->initializeSpeciesWatchpoint(exec);
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> resetPrototype(vm, getPrototypeDirect());
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSGlobalObjecth"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSGlobalObject.h (210475 => 210476)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSGlobalObject.h        2017-01-07 07:42:19 UTC (rev 210475)
+++ trunk/Source/JavaScriptCore/runtime/JSGlobalObject.h        2017-01-07 07:53:07 UTC (rev 210476)
</span><span class="lines">@@ -399,9 +399,11 @@
</span><span class="cx"> WeakRandom m_weakRandom;
</span><span class="cx">
</span><span class="cx"> InlineWatchpointSet& arrayIteratorProtocolWatchpoint() { return m_arrayIteratorProtocolWatchpoint; }
</span><ins>+ InlineWatchpointSet& arraySpeciesWatchpoint() { return m_arraySpeciesWatchpoint; }
</ins><span class="cx"> // If this hasn't been invalidated, it means the array iterator protocol
</span><span class="cx"> // is not observable to user code yet.
</span><span class="cx"> InlineWatchpointSet m_arrayIteratorProtocolWatchpoint;
</span><ins>+ InlineWatchpointSet m_arraySpeciesWatchpoint;
</ins><span class="cx"> std::unique_ptr<ArrayIteratorAdaptiveWatchpoint> m_arrayPrototypeSymbolIteratorWatchpoint;
</span><span class="cx"> std::unique_ptr<ArrayIteratorAdaptiveWatchpoint> m_arrayIteratorPrototypeNext;
</span><span class="cx">
</span></span></pre>
</div>
</div>
</body>
</html>