Diff
Modified: branches/safari-603-branch/JSTests/ChangeLog (210864 => 210865)
--- branches/safari-603-branch/JSTests/ChangeLog 2017-01-18 20:29:26 UTC (rev 210864)
+++ branches/safari-603-branch/JSTests/ChangeLog 2017-01-18 20:42:19 UTC (rev 210865)
@@ -1,5 +1,45 @@
2017-01-18 Matthew Hanson <matthew_han...@apple.com>
+ Merge r210695. rdar://problem/29913445
+
+ 2017-01-12 Saam Barati <sbar...@apple.com>
+
+ Add a slice intrinsic to the DFG/FTL
+ https://bugs.webkit.org/show_bug.cgi?id=166707
+ <rdar://problem/29913445>
+
+ 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):
+
+2017-01-18 Matthew Hanson <matthew_han...@apple.com>
+
Merge r210837. rdar://problem/29432371
2017-01-17 Michael Saboff <msab...@apple.com>
Added: branches/safari-603-branch/JSTests/stress/array-slice-intrinsic.js (0 => 210865)
--- branches/safari-603-branch/JSTests/stress/array-slice-intrinsic.js (rev 0)
+++ branches/safari-603-branch/JSTests/stress/array-slice-intrinsic.js 2017-01-18 20:42:19 UTC (rev 210865)
@@ -0,0 +1,49 @@
+function assert(b, ...m) {
+ if (!b)
+ throw new Error("Bad: ", ...m);
+}
+noInline(assert);
+
+function shallowEq(a, b) {
+ assert(a.length === b.length, a, b);
+ for (let i = 0; i < a.length; i++)
+ assert(a[i] === b[i], a, b);
+}
+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);
+ }
+}
Added: branches/safari-603-branch/JSTests/stress/array-slice-jettison-on-constructor-change.js (0 => 210865)
--- branches/safari-603-branch/JSTests/stress/array-slice-jettison-on-constructor-change.js (rev 0)
+++ branches/safari-603-branch/JSTests/stress/array-slice-jettison-on-constructor-change.js 2017-01-18 20:42:19 UTC (rev 210865)
@@ -0,0 +1,72 @@
+function assert(b, ...m) {
+ if (!b)
+ throw new Error("Bad: ", ...m)
+}
+noInline(assert);
+
+let shouldBeNewConstructor = false;
+const newConstructor = {};
+
+function shallowEq(a, b) {
+ assert(a.length === b.length, a, b);
+ if (shouldBeNewConstructor)
+ assert(b.constructor === newConstructor);
+ for (let i = 0; i < a.length; i++)
+ assert(a[i] === b[i], a, b);
+}
+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();
Added: branches/safari-603-branch/JSTests/stress/array-slice-osr-exit-2.js (0 => 210865)
--- branches/safari-603-branch/JSTests/stress/array-slice-osr-exit-2.js (rev 0)
+++ branches/safari-603-branch/JSTests/stress/array-slice-osr-exit-2.js 2017-01-18 20:42:19 UTC (rev 210865)
@@ -0,0 +1,76 @@
+function assert(b, ...m) {
+ if (!b)
+ throw new Error("Bad: ", ...m)
+}
+noInline(assert);
+
+class Foo extends Array {
+ constructor(...args) {
+ super(...args);
+ }
+};
+function shallowEq(a, b) {
+ assert(a.length === b.length, a, b);
+ for (let i = 0; i < a.length; i++)
+ assert(a[i] === b[i], a, b);
+}
+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();
Added: branches/safari-603-branch/JSTests/stress/array-slice-osr-exit.js (0 => 210865)
--- branches/safari-603-branch/JSTests/stress/array-slice-osr-exit.js (rev 0)
+++ branches/safari-603-branch/JSTests/stress/array-slice-osr-exit.js 2017-01-18 20:42:19 UTC (rev 210865)
@@ -0,0 +1,74 @@
+function assert(b, ...m) {
+ if (!b)
+ throw new Error("Bad: ", ...m)
+}
+noInline(assert);
+
+class Foo extends Array {
+ constructor(...args) {
+ super(...args);
+ }
+};
+function shallowEq(a, b) {
+ assert(a.length === b.length, a, b);
+ for (let i = 0; i < a.length; i++)
+ assert(a[i] === b[i], a, b);
+}
+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();
Modified: branches/safari-603-branch/Source/_javascript_Core/ChangeLog (210864 => 210865)
--- branches/safari-603-branch/Source/_javascript_Core/ChangeLog 2017-01-18 20:29:26 UTC (rev 210864)
+++ branches/safari-603-branch/Source/_javascript_Core/ChangeLog 2017-01-18 20:42:19 UTC (rev 210865)
@@ -1,5 +1,111 @@
2017-01-18 Matthew Hanson <matthew_han...@apple.com>
+ Merge r210695. rdar://problem/29913445
+
+ 2017-01-12 Saam Barati <sbar...@apple.com>
+
+ Add a slice intrinsic to the DFG/FTL
+ https://bugs.webkit.org/show_bug.cgi?id=166707
+ <rdar://problem/29913445>
+
+ 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.
+ 4. 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. We
+ would probably use TryGetById to do this.
+
+ I'm seeing a 5% speedup on crypto-pbkdf2 and often a 1% speedup on
+ the total benchmark (the results are sometimes noisy).
+
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+ * dfg/DFGCallArrayAllocatorSlowPathGenerator.h:
+ (JSC::DFG::CallArrayAllocatorWithVariableStructureVariableSizeSlowPathGenerator::CallArrayAllocatorWithVariableStructureVariableSizeSlowPathGenerator):
+ * dfg/DFGClobberize.h:
+ (JSC::DFG::clobberize):
+ * dfg/DFGDoesGC.cpp:
+ (JSC::DFG::doesGC):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * dfg/DFGNodeType.h:
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ * dfg/DFGSafeToExecute.h:
+ (JSC::DFG::safeToExecute):
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::compileArraySlice):
+ (JSC::DFG::SpeculativeJIT::emitAllocateButterfly):
+ * dfg/DFGSpeculativeJIT.h:
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ (JSC::DFG::SpeculativeJIT::emitInitializeButterfly):
+ (JSC::DFG::SpeculativeJIT::compileAllocateNewArrayWithSize):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ (JSC::DFG::SpeculativeJIT::emitInitializeButterfly):
+ (JSC::DFG::SpeculativeJIT::compileAllocateNewArrayWithSize):
+ * ftl/FTLAbstractHeapRepository.h:
+ * ftl/FTLCapabilities.cpp:
+ (JSC::FTL::canCompile):
+ * ftl/FTLLowerDFGToB3.cpp:
+ (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+ (JSC::FTL::DFG::LowerDFGToB3::compileArraySlice):
+ (JSC::FTL::DFG::LowerDFGToB3::compileNewArrayWithSize):
+ (JSC::FTL::DFG::LowerDFGToB3::compileMaterializeNewObject):
+ (JSC::FTL::DFG::LowerDFGToB3::initializeArrayElements):
+ (JSC::FTL::DFG::LowerDFGToB3::storeStructure):
+ (JSC::FTL::DFG::LowerDFGToB3::allocateCell):
+ (JSC::FTL::DFG::LowerDFGToB3::allocateObject):
+ (JSC::FTL::DFG::LowerDFGToB3::allocateJSArray):
+ (JSC::FTL::DFG::LowerDFGToB3::allocateUninitializedContiguousJSArray):
+ * 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):
+ * runtime/Structure.h:
+
+2017-01-18 Matthew Hanson <matthew_han...@apple.com>
+
Merge r210837. rdar://problem/29432371
2017-01-17 Michael Saboff <msab...@apple.com>
Modified: branches/safari-603-branch/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (210864 => 210865)
--- branches/safari-603-branch/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2017-01-18 20:29:26 UTC (rev 210864)
+++ branches/safari-603-branch/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2017-01-18 20:42:19 UTC (rev 210865)
@@ -1650,6 +1650,20 @@
clobberWorld(node->origin.semantic, clobberLimit);
forNode(node).setType(SpecBytecodeNumber);
break;
+
+ case ArraySlice: {
+ JSGlobalObject* globalObject = m_graph.globalObjectFor(node->origin.semantic);
+
+ // FIXME: We could do better here if we prove that the
+ // incoming value has only a single structure.
+ StructureSet structureSet;
+ structureSet.add(globalObject->originalArrayStructureForIndexingType(ArrayWithInt32));
+ structureSet.add(globalObject->originalArrayStructureForIndexingType(ArrayWithContiguous));
+ structureSet.add(globalObject->originalArrayStructureForIndexingType(ArrayWithDouble));
+
+ forNode(node).set(m_graph, structureSet);
+ break;
+ }
case ArrayPop:
clobberWorld(node->origin.semantic, clobberLimit);
Modified: branches/safari-603-branch/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (210864 => 210865)
--- branches/safari-603-branch/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2017-01-18 20:29:26 UTC (rev 210864)
+++ branches/safari-603-branch/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2017-01-18 20:42:19 UTC (rev 210865)
@@ -2246,6 +2246,94 @@
return false;
}
}
+
+ 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, 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. (We can relax this in the future by using
+ // TryGetById and CheckCell).
+ //
+ // 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.
+
+ StructureSet structureSet;
+ structureSet.add(globalObject->originalArrayStructureForIndexingType(ArrayWithInt32));
+ structureSet.add(globalObject->originalArrayStructureForIndexingType(ArrayWithContiguous));
+ structureSet.add(globalObject->originalArrayStructureForIndexingType(ArrayWithDouble));
+ addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(structureSet)), 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(), OpInfo());
+ set(VirtualRegister(resultOperand), arraySlice);
+ return true;
+ }
+
+ return false;
+ }
+ default:
+ return false;
+ }
+
+ RELEASE_ASSERT_NOT_REACHED();
+ return false;
+ }
case ArrayPopIntrinsic: {
if (argumentCountIncludingThis != 1)
Modified: branches/safari-603-branch/Source/_javascript_Core/dfg/DFGCallArrayAllocatorSlowPathGenerator.h (210864 => 210865)
--- branches/safari-603-branch/Source/_javascript_Core/dfg/DFGCallArrayAllocatorSlowPathGenerator.h 2017-01-18 20:29:26 UTC (rev 210864)
+++ branches/safari-603-branch/Source/_javascript_Core/dfg/DFGCallArrayAllocatorSlowPathGenerator.h 2017-01-18 20:42:19 UTC (rev 210865)
@@ -124,6 +124,45 @@
Vector<SilentRegisterSavePlan, 2> m_plans;
};
+class CallArrayAllocatorWithVariableStructureVariableSizeSlowPathGenerator : public JumpingSlowPathGenerator<MacroAssembler::JumpList> {
+public:
+ CallArrayAllocatorWithVariableStructureVariableSizeSlowPathGenerator(
+ MacroAssembler::JumpList from, SpeculativeJIT* jit, P_JITOperation_EStZB function,
+ GPRReg resultGPR, GPRReg structureGPR, GPRReg sizeGPR, GPRReg storageGPR, GPRReg scratchGPR)
+ : JumpingSlowPathGenerator<MacroAssembler::JumpList>(from, jit)
+ , m_function(function)
+ , m_resultGPR(resultGPR)
+ , m_structureGPR(structureGPR)
+ , m_sizeGPR(sizeGPR)
+ , m_storageGPR(storageGPR)
+ , m_scratchGPR(scratchGPR)
+ {
+ jit->silentSpillAllRegistersImpl(false, m_plans, resultGPR, m_scratchGPR);
+ }
+
+protected:
+ void generateInternal(SpeculativeJIT* jit) override
+ {
+ linkFrom(jit);
+ for (unsigned i = 0; i < m_plans.size(); ++i)
+ jit->silentSpill(m_plans[i]);
+ jit->callOperation(m_function, m_resultGPR, m_structureGPR, m_sizeGPR, m_storageGPR);
+ for (unsigned i = m_plans.size(); i--;)
+ jit->silentFill(m_plans[i], m_scratchGPR);
+ jit->m_jit.exceptionCheck();
+ jumpTo(jit);
+ }
+
+private:
+ P_JITOperation_EStZB m_function;
+ GPRReg m_resultGPR;
+ GPRReg m_structureGPR;
+ GPRReg m_sizeGPR;
+ GPRReg m_storageGPR;
+ GPRReg m_scratchGPR;
+ Vector<SilentRegisterSavePlan, 2> m_plans;
+};
+
} } // namespace JSC::DFG
#endif // ENABLE(DFG_JIT)
Modified: branches/safari-603-branch/Source/_javascript_Core/dfg/DFGClobberize.h (210864 => 210865)
--- branches/safari-603-branch/Source/_javascript_Core/dfg/DFGClobberize.h 2017-01-18 20:29:26 UTC (rev 210864)
+++ branches/safari-603-branch/Source/_javascript_Core/dfg/DFGClobberize.h 2017-01-18 20:42:19 UTC (rev 210865)
@@ -503,6 +503,19 @@
read(MiscFields);
def(HeapLocation(IsFunctionLoc, MiscFields, node->child1()), LazyNode(node));
return;
+
+ case ArraySlice:
+ read(MiscFields);
+ read(JSCell_indexingType);
+ read(JSCell_structureID);
+ read(JSObject_butterfly);
+ read(Butterfly_publicLength);
+ read(IndexedDoubleProperties);
+ read(IndexedInt32Properties);
+ read(IndexedContiguousProperties);
+ read(HeapObjectCount);
+ write(HeapObjectCount);
+ return;
case GetById:
case GetByIdFlush:
Modified: branches/safari-603-branch/Source/_javascript_Core/dfg/DFGDoesGC.cpp (210864 => 210865)
--- branches/safari-603-branch/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2017-01-18 20:29:26 UTC (rev 210864)
+++ branches/safari-603-branch/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2017-01-18 20:42:19 UTC (rev 210865)
@@ -309,6 +309,7 @@
case ToLowerCase:
case CallDOMGetter:
case CallDOM:
+ case ArraySlice:
return true;
case MultiPutByOffset:
Modified: branches/safari-603-branch/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (210864 => 210865)
--- branches/safari-603-branch/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2017-01-18 20:29:26 UTC (rev 210864)
+++ branches/safari-603-branch/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2017-01-18 20:42:19 UTC (rev 210865)
@@ -931,6 +931,14 @@
fixEdge<KnownCellUse>(node->child1());
break;
}
+
+ 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;
+ }
case RegExpExec:
case RegExpTest: {
Modified: branches/safari-603-branch/Source/_javascript_Core/dfg/DFGNodeType.h (210864 => 210865)
--- branches/safari-603-branch/Source/_javascript_Core/dfg/DFGNodeType.h 2017-01-18 20:29:26 UTC (rev 210864)
+++ branches/safari-603-branch/Source/_javascript_Core/dfg/DFGNodeType.h 2017-01-18 20:42:19 UTC (rev 210865)
@@ -248,6 +248,7 @@
/* Optimizations for array mutation. */\
macro(ArrayPush, NodeResultJS | NodeMustGenerate) \
macro(ArrayPop, NodeResultJS | NodeMustGenerate) \
+ macro(ArraySlice, NodeResultJS | NodeMustGenerate | NodeHasVarArgs) \
\
/* Optimizations for regular _expression_ matching. */\
macro(RegExpExec, NodeResultJS | NodeMustGenerate) \
Modified: branches/safari-603-branch/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (210864 => 210865)
--- branches/safari-603-branch/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2017-01-18 20:29:26 UTC (rev 210864)
+++ branches/safari-603-branch/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2017-01-18 20:42:19 UTC (rev 210865)
@@ -860,6 +860,7 @@
break;
}
+ case ArraySlice:
case NewArrayWithSpread:
case NewArray:
case NewArrayWithSize:
Modified: branches/safari-603-branch/Source/_javascript_Core/dfg/DFGSafeToExecute.h (210864 => 210865)
--- branches/safari-603-branch/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2017-01-18 20:29:26 UTC (rev 210864)
+++ branches/safari-603-branch/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2017-01-18 20:42:19 UTC (rev 210865)
@@ -382,6 +382,13 @@
case IsNonEmptyMapBucket:
return true;
+ case ArraySlice: {
+ // You could plausibly move this code around as long as you proved the
+ // incoming array base structure is an original array at the hoisted location.
+ // Instead of doing that extra work, we just conservatively return false.
+ return false;
+ }
+
case BottomValue:
// If in doubt, assume that this isn't safe to execute, just because we have no way of
// compiling this node.
Modified: branches/safari-603-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (210864 => 210865)
--- branches/safari-603-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2017-01-18 20:29:26 UTC (rev 210864)
+++ branches/safari-603-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2017-01-18 20:42:19 UTC (rev 210865)
@@ -7169,6 +7169,182 @@
int32Result(resultGPR, node);
}
+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();
+
+ 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);
+ }
+
+
+ GPRTemporary temp3(this);
+ GPRReg tempValue = temp3.gpr();
+ {
+ SpeculateCellOperand cell(this, m_jit.graph().varArgChild(node, 0));
+ m_jit.load8(MacroAssembler::Address(cell.gpr(), JSCell::indexingTypeAndMiscOffset()), tempValue);
+ m_jit.and32(TrustedImm32(AllArrayTypesAndHistory), tempValue);
+ }
+
+ {
+#if USE(JSVALUE64)
+ GPRTemporary emptyValue(this);
+ JSValueRegs emptyValueRegs = JSValueRegs(emptyValue.gpr());
+#else
+ GPRTemporary emptyValuePayload(this);
+ GPRTemporary emptyValueTag(this);
+ JSValueRegs emptyValueRegs(emptyValueTag.gpr(), emptyValuePayload.gpr());
+#endif
+
+ GPRTemporary storage(this);
+ GPRReg storageResultGPR = storage.gpr();
+
+ GPRReg sizeGPR = tempGPR;
+
+ CCallHelpers::JumpList done;
+
+ auto emitMoveEmptyValue = [&] (JSValue v) {
+#if USE(JSVALUE64)
+ m_jit.move(TrustedImm64(JSValue::encode(v)), emptyValueRegs.gpr());
+#else
+ m_jit.move(TrustedImm32(v.tag()), emptyValueRegs.tagGPR());
+ m_jit.move(TrustedImm32(v.payload()), emptyValueRegs.payloadGPR());
+#endif
+ };
+
+ auto isContiguous = m_jit.branch32(MacroAssembler::Equal, tempValue, TrustedImm32(ArrayWithContiguous));
+ auto isInt32 = m_jit.branch32(MacroAssembler::Equal, tempValue, TrustedImm32(ArrayWithInt32));
+ // When we emit an ArraySlice, we dominate the use of the array by a CheckStructure
+ // to ensure the incoming array is one to be one of the original array structures
+ // with one of the following indexing shapes: Int32, Contiguous, Double. Therefore,
+ // we're a double array here.
+ m_jit.move(TrustedImmPtr(globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithDouble)), tempValue);
+ emitMoveEmptyValue(jsNaN());
+ done.append(m_jit.jump());
+
+ isContiguous.link(&m_jit);
+ m_jit.move(TrustedImmPtr(globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous)), tempValue);
+ emitMoveEmptyValue(JSValue());
+ done.append(m_jit.jump());
+
+ isInt32.link(&m_jit);
+ m_jit.move(TrustedImmPtr(globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithInt32)), tempValue);
+ emitMoveEmptyValue(JSValue());
+
+ done.link(&m_jit);
+
+ {
+ GPRTemporary scratch(this);
+ GPRTemporary scratch2(this);
+ GPRReg scratchGPR = scratch.gpr();
+ GPRReg scratch2GPR = scratch2.gpr();
+
+ MacroAssembler::JumpList slowCases;
+ m_jit.move(TrustedImmPtr(0), storageResultGPR);
+
+ emitAllocateButterfly(storageResultGPR, sizeGPR, scratchGPR, scratch2GPR, resultGPR, slowCases);
+ emitInitializeButterfly(storageResultGPR, sizeGPR, emptyValueRegs, scratchGPR);
+ emitAllocateJSObject<JSArray>(resultGPR, tempValue, storageResultGPR, scratchGPR, scratch2GPR, slowCases);
+ m_jit.mutatorFence();
+
+ addSlowPathGenerator(std::make_unique<CallArrayAllocatorWithVariableStructureVariableSizeSlowPathGenerator>(
+ slowCases, this, operationNewArrayWithSize, resultGPR, tempValue, sizeGPR, storageResultGPR, scratchGPR));
+ }
+ }
+
+ GPRTemporary temp4(this);
+ 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);
+}
+
void SpeculativeJIT::compileNotifyWrite(Node* node)
{
WatchpointSet* set = node->watchpointSet();
@@ -9381,6 +9557,21 @@
noResult(node, UseChildrenCalledExplicitly);
}
+void SpeculativeJIT::emitAllocateButterfly(GPRReg storageResultGPR, GPRReg sizeGPR, GPRReg scratch1, GPRReg scratch2, GPRReg scratch3, MacroAssembler::JumpList& slowCases)
+{
+ RELEASE_ASSERT(RegisterSet(storageResultGPR, sizeGPR, scratch1, scratch2, scratch3).numberOfSetGPRs() == 5);
+ ASSERT((1 << 3) == sizeof(JSValue));
+ m_jit.zeroExtend32ToPtr(sizeGPR, scratch1);
+ m_jit.lshift32(TrustedImm32(3), scratch1);
+ m_jit.add32(TrustedImm32(sizeof(IndexingHeader)), scratch1, scratch2);
+ m_jit.emitAllocateVariableSized(
+ storageResultGPR, m_jit.vm()->heap.subspaceForAuxiliaryData(), scratch2, scratch1, scratch3, slowCases);
+ m_jit.addPtr(TrustedImm32(sizeof(IndexingHeader)), storageResultGPR);
+
+ m_jit.store32(sizeGPR, MacroAssembler::Address(storageResultGPR, Butterfly::offsetOfPublicLength()));
+ m_jit.store32(sizeGPR, MacroAssembler::Address(storageResultGPR, Butterfly::offsetOfVectorLength()));
+}
+
} } // namespace JSC::DFG
#endif
Modified: branches/safari-603-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h (210864 => 210865)
--- branches/safari-603-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h 2017-01-18 20:29:26 UTC (rev 210864)
+++ branches/safari-603-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h 2017-01-18 20:42:19 UTC (rev 210865)
@@ -743,6 +743,8 @@
void emitCall(Node*);
+ void emitAllocateButterfly(GPRReg storageGPR, GPRReg sizeGPR, GPRReg scratch1, GPRReg scratch2, GPRReg scratch3, MacroAssembler::JumpList& slowCases);
+ void emitInitializeButterfly(GPRReg storageGPR, GPRReg sizeGPR, JSValueRegs emptyValueRegs, GPRReg scratchGPR);
void compileAllocateNewArrayWithSize(JSGlobalObject*, GPRReg resultGPR, GPRReg sizeGPR, IndexingType, bool shouldConvertLargeSizeToArrayStorage = true);
// Called once a node has completed code generation but prior to setting
@@ -2680,6 +2682,7 @@
void compileSpread(Node*);
void compileNewArrayWithSpread(Node*);
void compileGetRestLength(Node*);
+ void compileArraySlice(Node*);
void compileNotifyWrite(Node*);
bool compileRegExpExec(Node*);
void compileIsObjectOrNull(Node*);
Modified: branches/safari-603-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (210864 => 210865)
--- branches/safari-603-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2017-01-18 20:29:26 UTC (rev 210864)
+++ branches/safari-603-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2017-01-18 20:42:19 UTC (rev 210865)
@@ -3550,6 +3550,11 @@
break;
}
+ case ArraySlice: {
+ compileArraySlice(node);
+ break;
+ }
+
case DFG::Jump: {
jump(node->targetBlock());
noResult(node);
@@ -5666,6 +5671,18 @@
doubleResult(result.fpr(), node);
}
+void SpeculativeJIT::emitInitializeButterfly(GPRReg storageGPR, GPRReg sizeGPR, JSValueRegs emptyValueRegs, GPRReg scratchGPR)
+{
+ m_jit.move(sizeGPR, scratchGPR);
+ MacroAssembler::Jump done = m_jit.branchTest32(MacroAssembler::Zero, scratchGPR);
+ MacroAssembler::Label loop = m_jit.label();
+ m_jit.sub32(TrustedImm32(1), scratchGPR);
+ m_jit.store32(emptyValueRegs.tagGPR(), MacroAssembler::BaseIndex(storageGPR, scratchGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)));
+ m_jit.store32(emptyValueRegs.payloadGPR(), MacroAssembler::BaseIndex(storageGPR, scratchGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload)));
+ m_jit.branchTest32(MacroAssembler::NonZero, scratchGPR).linkTo(loop, &m_jit);
+ done.link(&m_jit);
+}
+
void SpeculativeJIT::compileAllocateNewArrayWithSize(JSGlobalObject* globalObject, GPRReg resultGPR, GPRReg sizeGPR, IndexingType indexingType, bool shouldConvertLargeSizeToArrayStorage)
{
GPRTemporary storage(this);
@@ -5681,34 +5698,21 @@
MacroAssembler::JumpList slowCases;
if (shouldConvertLargeSizeToArrayStorage)
slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, sizeGPR, TrustedImm32(MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH)));
-
- ASSERT((1 << 3) == sizeof(JSValue));
- m_jit.move(sizeGPR, scratchGPR);
- m_jit.lshift32(TrustedImm32(3), scratchGPR);
- m_jit.add32(TrustedImm32(sizeof(IndexingHeader)), scratchGPR, resultGPR);
- m_jit.emitAllocateVariableSized(
- storageGPR, m_jit.vm()->heap.subspaceForAuxiliaryData(), resultGPR, scratchGPR,
- scratch2GPR, slowCases);
- m_jit.addPtr(TrustedImm32(sizeof(IndexingHeader)), storageGPR);
- m_jit.store32(sizeGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()));
- m_jit.store32(sizeGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength()));
-
+ // We can use result as a scratch for this.
+ emitAllocateButterfly(storageGPR, sizeGPR, scratchGPR, scratch2GPR, resultGPR, slowCases);
+
JSValue hole;
if (hasDouble(indexingType))
hole = JSValue(JSValue::EncodeAsDouble, PNaN);
else
hole = JSValue();
+ JSValueRegs emptyValueRegs(scratchGPR, scratch2GPR);
+ m_jit.move(TrustedImm32(hole.tag()), emptyValueRegs.tagGPR());
+ m_jit.move(TrustedImm32(hole.payload()), emptyValueRegs.payloadGPR());
+ // We can use result as a scratch for this.
+ emitInitializeButterfly(storageGPR, sizeGPR, emptyValueRegs, resultGPR);
- m_jit.move(sizeGPR, scratchGPR);
- MacroAssembler::Jump done = m_jit.branchTest32(MacroAssembler::Zero, scratchGPR);
- MacroAssembler::Label loop = m_jit.label();
- m_jit.sub32(TrustedImm32(1), scratchGPR);
- m_jit.store32(TrustedImm32(hole.u.asBits.tag), MacroAssembler::BaseIndex(storageGPR, scratchGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)));
- m_jit.store32(TrustedImm32(hole.u.asBits.payload), MacroAssembler::BaseIndex(storageGPR, scratchGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload)));
- m_jit.branchTest32(MacroAssembler::NonZero, scratchGPR).linkTo(loop, &m_jit);
- done.link(&m_jit);
-
Structure* structure = globalObject->arrayStructureForIndexingTypeDuringAllocation(indexingType);
emitAllocateJSObject<JSArray>(resultGPR, TrustedImmPtr(structure), storageGPR, scratchGPR, scratch2GPR, slowCases);
Modified: branches/safari-603-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (210864 => 210865)
--- branches/safari-603-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2017-01-18 20:29:26 UTC (rev 210864)
+++ branches/safari-603-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2017-01-18 20:42:19 UTC (rev 210865)
@@ -3462,6 +3462,11 @@
}
break;
}
+
+ case ArraySlice: {
+ compileArraySlice(node);
+ break;
+ }
case ArrayPop: {
ASSERT(node->arrayMode().isJSArray());
@@ -5939,6 +5944,17 @@
doubleResult(result.fpr(), node);
}
+void SpeculativeJIT::emitInitializeButterfly(GPRReg storageGPR, GPRReg sizeGPR, JSValueRegs emptyValueRegs, GPRReg scratchGPR)
+{
+ m_jit.zeroExtend32ToPtr(sizeGPR, scratchGPR);
+ MacroAssembler::Jump done = m_jit.branchTest32(MacroAssembler::Zero, scratchGPR);
+ MacroAssembler::Label loop = m_jit.label();
+ m_jit.sub32(TrustedImm32(1), scratchGPR);
+ m_jit.store64(emptyValueRegs.gpr(), MacroAssembler::BaseIndex(storageGPR, scratchGPR, MacroAssembler::TimesEight));
+ m_jit.branchTest32(MacroAssembler::NonZero, scratchGPR).linkTo(loop, &m_jit);
+ done.link(&m_jit);
+}
+
void SpeculativeJIT::compileAllocateNewArrayWithSize(JSGlobalObject* globalObject, GPRReg resultGPR, GPRReg sizeGPR, IndexingType indexingType, bool shouldConvertLargeSizeToArrayStorage)
{
GPRTemporary storage(this);
@@ -5955,30 +5971,15 @@
if (shouldConvertLargeSizeToArrayStorage)
slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, sizeGPR, TrustedImm32(MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH)));
- ASSERT((1 << 3) == sizeof(JSValue));
- m_jit.move(sizeGPR, scratchGPR);
- m_jit.lshift32(TrustedImm32(3), scratchGPR);
- m_jit.add32(TrustedImm32(sizeof(IndexingHeader)), scratchGPR, resultGPR);
- m_jit.emitAllocateVariableSized(
- storageGPR, m_jit.vm()->heap.subspaceForAuxiliaryData(), resultGPR, scratchGPR,
- scratch2GPR, slowCases);
- m_jit.addPtr(TrustedImm32(sizeof(IndexingHeader)), storageGPR);
+ // We can use resultGPR as a scratch right now.
+ emitAllocateButterfly(storageGPR, sizeGPR, resultGPR, scratchGPR, scratch2GPR, slowCases);
- m_jit.store32(sizeGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()));
- m_jit.store32(sizeGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength()));
-
if (hasDouble(indexingType))
m_jit.move(TrustedImm64(bitwise_cast<int64_t>(PNaN)), scratchGPR);
else
m_jit.move(TrustedImm64(JSValue::encode(JSValue())), scratchGPR);
- m_jit.move(sizeGPR, scratch2GPR);
- MacroAssembler::Jump done = m_jit.branchTest32(MacroAssembler::Zero, scratch2GPR);
- MacroAssembler::Label loop = m_jit.label();
- m_jit.sub32(TrustedImm32(1), scratch2GPR);
- m_jit.store64(scratchGPR, MacroAssembler::BaseIndex(storageGPR, scratch2GPR, MacroAssembler::TimesEight));
- m_jit.branchTest32(MacroAssembler::NonZero, scratch2GPR).linkTo(loop, &m_jit);
- done.link(&m_jit);
-
+ emitInitializeButterfly(storageGPR, sizeGPR, JSValueRegs(scratchGPR), scratch2GPR);
+
Structure* structure = globalObject->arrayStructureForIndexingTypeDuringAllocation(indexingType);
emitAllocateJSObject<JSArray>(resultGPR, TrustedImmPtr(structure), storageGPR, scratchGPR, scratch2GPR, slowCases);
Modified: branches/safari-603-branch/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.h (210864 => 210865)
--- branches/safari-603-branch/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.h 2017-01-18 20:29:26 UTC (rev 210864)
+++ branches/safari-603-branch/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.h 2017-01-18 20:42:19 UTC (rev 210865)
@@ -107,6 +107,8 @@
macro(Structure_globalObject, Structure::globalObjectOffset()) \
macro(Structure_prototype, Structure::prototypeOffset()) \
macro(Structure_structureID, Structure::structureIDOffset()) \
+ macro(Structure_inlineCapacity, Structure::inlineCapacityOffset()) \
+ macro(Structure_indexingTypeIncludingHistory, Structure::indexingTypeIncludingHistoryOffset()) \
macro(JSMap_hashMapImpl, JSMap::offsetOfHashMapImpl()) \
macro(JSSet_hashMapImpl, JSSet::offsetOfHashMapImpl()) \
macro(HashMapImpl_capacity, HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfCapacity()) \
Modified: branches/safari-603-branch/Source/_javascript_Core/ftl/FTLCapabilities.cpp (210864 => 210865)
--- branches/safari-603-branch/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2017-01-18 20:29:26 UTC (rev 210864)
+++ branches/safari-603-branch/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2017-01-18 20:42:19 UTC (rev 210865)
@@ -282,6 +282,7 @@
case CheckDOM:
case CallDOM:
case CallDOMGetter:
+ case ArraySlice:
// These are OK.
break;
Modified: branches/safari-603-branch/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (210864 => 210865)
--- branches/safari-603-branch/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2017-01-18 20:29:26 UTC (rev 210864)
+++ branches/safari-603-branch/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2017-01-18 20:42:19 UTC (rev 210865)
@@ -706,6 +706,9 @@
case ArrayPop:
compileArrayPop();
break;
+ case ArraySlice:
+ compileArraySlice();
+ break;
case CreateActivation:
compileCreateActivation();
break;
@@ -3860,6 +3863,75 @@
return;
}
}
+
+ 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));
+
+ ArrayValues arrayResult;
+ {
+ LValue indexingType = m_out.load8ZeroExt32(lowCell(m_graph.varArgChild(m_node, 0)), m_heaps.JSCell_indexingTypeAndMisc);
+ indexingType = m_out.bitAnd(indexingType, m_out.constInt32(AllArrayTypesAndHistory));
+ // When we emit an ArraySlice, we dominate the use of the array by a CheckStructure
+ // to ensure the incoming array is one to be one of the original array structures
+ // with one of the following indexing shapes: Int32, Contiguous, Double.
+ LValue structure = m_out.select(
+ m_out.equal(indexingType, m_out.constInt32(ArrayWithInt32)),
+ m_out.constIntPtr(globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithInt32)),
+ m_out.select(m_out.equal(indexingType, m_out.constInt32(ArrayWithContiguous)),
+ m_out.constIntPtr(globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous)),
+ m_out.constIntPtr(globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithDouble))));
+ arrayResult = allocateJSArray(resultLength, structure, indexingType, 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);
+ LValue value = m_out.load64(m_out.baseIndex(m_heaps.root, sourceStorage, loadIndex, ScaleEight));
+ m_out.store64(value, m_out.baseIndex(m_heaps.root, arrayResult.butterfly, storeIndex, ScaleEight));
+ 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);
+ }
void compileArrayPop()
{
@@ -4626,11 +4698,10 @@
m_node->indexingType());
if (!globalObject->isHavingABadTime() && !hasAnyArrayStorage(m_node->indexingType())) {
+ IndexingType indexingType = m_node->indexingType();
setJSValue(
allocateJSArray(
- publicLength,
- globalObject->arrayStructureForIndexingTypeDuringAllocation(
- m_node->indexingType())).array);
+ publicLength, m_out.constIntPtr(globalObject->arrayStructureForIndexingTypeDuringAllocation(indexingType)), m_out.constInt32(indexingType)).array);
mutatorFence();
return;
}
@@ -8665,7 +8736,7 @@
m_out.store32(publicLength, butterfly, m_heaps.Butterfly_publicLength);
- initializeArrayElements(structure->indexingType(), m_out.int32Zero, vectorLength, butterfly);
+ initializeArrayElements(m_out.constInt32(structure->indexingType()), m_out.int32Zero, vectorLength, butterfly);
HashMap<int32_t, LValue, DefaultHash<int32_t>::Hash, WTF::UnsignedWithZeroKeyHashTraits<int32_t>> indexMap;
Vector<int32_t> indices;
@@ -9421,26 +9492,35 @@
return result;
}
- void initializeArrayElements(IndexingType indexingType, LValue begin, LValue end, LValue butterfly)
+ void initializeArrayElements(LValue indexingType, LValue begin, LValue end, LValue butterfly)
{
- if (hasUndecided(indexingType))
- return;
if (begin == end)
return;
- IndexedAbstractHeap* heap = m_heaps.forIndexingType(indexingType);
- DFG_ASSERT(m_graph, m_node, heap);
-
- LValue hole;
- if (hasDouble(indexingType))
- hole = m_out.constInt64(bitwise_cast<int64_t>(PNaN));
- else
- hole = m_out.constInt64(JSValue::encode(JSValue()));
-
- splatWords(butterfly, begin, end, hole, heap->atAnyIndex());
+ if (indexingType->hasInt32()) {
+ IndexingType rawIndexingType = static_cast<IndexingType>(indexingType->asInt32());
+ if (hasUndecided(rawIndexingType))
+ return;
+ IndexedAbstractHeap* heap = m_heaps.forIndexingType(rawIndexingType);
+ DFG_ASSERT(m_graph, m_node, heap);
+
+ LValue hole;
+ if (hasDouble(rawIndexingType))
+ hole = m_out.constInt64(bitwise_cast<int64_t>(PNaN));
+ else
+ hole = m_out.constInt64(JSValue::encode(JSValue()));
+
+ splatWords(butterfly, begin, end, hole, heap->atAnyIndex());
+ } else {
+ LValue hole = m_out.select(
+ m_out.equal(m_out.bitAnd(indexingType, m_out.constInt32(IndexingShapeMask)), m_out.constInt32(DoubleShape)),
+ m_out.constInt64(bitwise_cast<int64_t>(PNaN)),
+ m_out.constInt64(JSValue::encode(JSValue())));
+ splatWords(butterfly, begin, end, hole, m_heaps.root);
+ }
}
-
+
void splatWords(LValue base, LValue begin, LValue end, LValue value, const AbstractHeap& heap)
{
const uint64_t unrollingLimit = 10;
@@ -10392,37 +10472,69 @@
object, m_heaps.JSCell_usefulBytes);
}
- LValue allocateCell(LValue allocator, Structure* structure, LBasicBlock slowPath)
+ void storeStructure(LValue object, LValue structure)
{
+ if (structure->hasIntPtr()) {
+ storeStructure(object, bitwise_cast<Structure*>(structure->asIntPtr()));
+ return;
+ }
+
+ LValue id = m_out.load32(structure, m_heaps.Structure_structureID);
+ m_out.store32(id, object, m_heaps.JSCell_structureID);
+
+ LValue blob = m_out.load32(structure, m_heaps.Structure_indexingTypeIncludingHistory);
+ m_out.store32(blob, object, m_heaps.JSCell_usefulBytes);
+ }
+
+ template <typename StructureType>
+ LValue allocateCell(LValue allocator, StructureType structure, LBasicBlock slowPath)
+ {
LValue result = allocateHeapCell(allocator, slowPath);
storeStructure(result, structure);
return result;
}
- LValue allocateObject(
- LValue allocator, Structure* structure, LValue butterfly, LBasicBlock slowPath)
+ LValue allocateObject(LValue allocator, Structure* structure, LValue butterfly, LBasicBlock slowPath)
{
+ return allocateObject(allocator, m_out.constIntPtr(structure), butterfly, slowPath);
+ }
+
+ LValue allocateObject(LValue allocator, LValue structure, LValue butterfly, LBasicBlock slowPath)
+ {
LValue result = allocateCell(allocator, structure, slowPath);
- splatWords(
- result,
- m_out.constInt32(JSFinalObject::offsetOfInlineStorage() / 8),
- m_out.constInt32(JSFinalObject::offsetOfInlineStorage() / 8 + structure->inlineCapacity()),
- m_out.int64Zero,
- m_heaps.properties.atAnyNumber());
+ if (structure->hasIntPtr()) {
+ splatWords(
+ result,
+ m_out.constInt32(JSFinalObject::offsetOfInlineStorage() / 8),
+ m_out.constInt32(JSFinalObject::offsetOfInlineStorage() / 8 + bitwise_cast<Structure*>(structure->asIntPtr())->inlineCapacity()),
+ m_out.int64Zero,
+ m_heaps.properties.atAnyNumber());
+ } else {
+ LValue end = m_out.add(
+ m_out.constInt32(JSFinalObject::offsetOfInlineStorage() / 8),
+ m_out.load8ZeroExt32(structure, m_heaps.Structure_inlineCapacity));
+ splatWords(
+ result,
+ m_out.constInt32(JSFinalObject::offsetOfInlineStorage() / 8),
+ end,
+ m_out.int64Zero,
+ m_heaps.properties.atAnyNumber());
+ }
+
m_out.storePtr(butterfly, result, m_heaps.JSObject_butterfly);
return result;
}
- template<typename ClassType>
+ template<typename ClassType, typename StructureType>
LValue allocateObject(
- size_t size, Structure* structure, LValue butterfly, LBasicBlock slowPath)
+ size_t size, StructureType structure, LValue butterfly, LBasicBlock slowPath)
{
MarkedAllocator* allocator = vm().heap.allocatorForObjectOfType<ClassType>(size);
return allocateObject(m_out.constIntPtr(allocator), structure, butterfly, slowPath);
}
- template<typename ClassType>
- LValue allocateObject(Structure* structure, LValue butterfly, LBasicBlock slowPath)
+ template<typename ClassType, typename StructureType>
+ LValue allocateObject(StructureType structure, LValue butterfly, LBasicBlock slowPath)
{
return allocateObject<ClassType>(
ClassType::allocationSize(0), structure, butterfly, slowPath);
@@ -10545,15 +10657,17 @@
LValue butterfly;
};
- ArrayValues allocateJSArray(LValue publicLength, Structure* structure, bool shouldInitializeElements = true, bool shouldLargeArraySizeCreateArrayStorage = true)
+ ArrayValues allocateJSArray(LValue publicLength, LValue structure, LValue indexingType, bool shouldInitializeElements = true, bool shouldLargeArraySizeCreateArrayStorage = true)
{
JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
- IndexingType indexingType = structure->indexingType();
- ASSERT(
- hasUndecided(indexingType)
- || hasInt32(indexingType)
- || hasDouble(indexingType)
- || hasContiguous(indexingType));
+ if (indexingType->hasInt32()) {
+ IndexingType type = static_cast<IndexingType>(indexingType->asInt32());
+ ASSERT_UNUSED(type,
+ hasUndecided(type)
+ || hasInt32(type)
+ || hasDouble(type)
+ || hasContiguous(type));
+ }
LBasicBlock fastCase = m_out.newBlock();
LBasicBlock largeCase = m_out.newBlock();
@@ -10576,12 +10690,12 @@
m_out.appendTo(fastCase, largeCase);
LValue vectorLength = nullptr;
- if (publicLength->hasInt32()) {
+ if (publicLength->hasInt32() && structure->hasIntPtr()) {
unsigned publicLengthConst = static_cast<unsigned>(publicLength->asInt32());
if (publicLengthConst <= MAX_STORAGE_VECTOR_LENGTH) {
vectorLength = m_out.constInt32(
Butterfly::optimalContiguousVectorLength(
- structure->outOfLineCapacity(), publicLengthConst));
+ bitwise_cast<Structure*>(structure->asIntPtr())->outOfLineCapacity(), publicLengthConst));
}
}
@@ -10614,7 +10728,7 @@
ValueFromBlock haveButterfly = m_out.anchor(butterfly);
LValue object = allocateObject<JSArray>(structure, butterfly, failCase);
-
+
ValueFromBlock fastResult = m_out.anchor(object);
ValueFromBlock fastButterfly = m_out.anchor(butterfly);
m_out.jump(continuation);
@@ -10626,7 +10740,7 @@
m_out.jump(slowCase);
m_out.appendTo(failCase, slowCase);
- ValueFromBlock failStructure = m_out.anchor(m_out.constIntPtr(structure));
+ ValueFromBlock failStructure = m_out.anchor(structure);
m_out.jump(slowCase);
m_out.appendTo(slowCase, continuation);
@@ -10656,7 +10770,7 @@
bool shouldInitializeElements = false;
bool shouldLargeArraySizeCreateArrayStorage = false;
return allocateJSArray(
- publicLength, structure, shouldInitializeElements,
+ publicLength, m_out.constIntPtr(structure), m_out.constInt32(structure->indexingType()), shouldInitializeElements,
shouldLargeArraySizeCreateArrayStorage);
}
Modified: branches/safari-603-branch/Source/_javascript_Core/jit/AssemblyHelpers.cpp (210864 => 210865)
--- branches/safari-603-branch/Source/_javascript_Core/jit/AssemblyHelpers.cpp 2017-01-18 20:29:26 UTC (rev 210864)
+++ branches/safari-603-branch/Source/_javascript_Core/jit/AssemblyHelpers.cpp 2017-01-18 20:42:19 UTC (rev 210865)
@@ -447,6 +447,7 @@
void AssemblyHelpers::emitLoadStructure(RegisterID source, RegisterID dest, RegisterID scratch)
{
#if USE(JSVALUE64)
+ ASSERT(dest != scratch);
load32(MacroAssembler::Address(source, JSCell::structureIDOffset()), dest);
loadPtr(vm()->heap.structureIDTable().base(), scratch);
loadPtr(MacroAssembler::BaseIndex(scratch, dest, MacroAssembler::TimesEight), dest);
Modified: branches/safari-603-branch/Source/_javascript_Core/runtime/ArrayPrototype.cpp (210864 => 210865)
--- branches/safari-603-branch/Source/_javascript_Core/runtime/ArrayPrototype.cpp 2017-01-18 20:29:26 UTC (rev 210864)
+++ branches/safari-603-branch/Source/_javascript_Core/runtime/ArrayPrototype.cpp 2017-01-18 20:42:19 UTC (rev 210865)
@@ -98,7 +98,7 @@
JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("reverse", arrayProtoFuncReverse, DontEnum, 0);
JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().shiftPublicName(), arrayProtoFuncShift, DontEnum, 0);
JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().shiftPrivateName(), arrayProtoFuncShift, DontEnum | DontDelete | ReadOnly, 0);
- JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->slice, arrayProtoFuncSlice, DontEnum, 2);
+ JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->slice, arrayProtoFuncSlice, DontEnum, 2, ArraySliceIntrinsic);
JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("sort", arrayPrototypeSortCodeGenerator, DontEnum);
JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("splice", arrayProtoFuncSplice, DontEnum, 2);
JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("unshift", arrayProtoFuncUnShift, DontEnum, 1);
@@ -191,21 +191,12 @@
throwTypeError(exec, scope, ASCIILiteral(ReadonlyPropertyWriteError));
}
-inline bool speciesWatchpointsValid(ExecState* exec, JSObject* thisObject)
+inline bool speciesWatchpointIsValid(JSObject* thisObject)
{
- VM& vm = exec->vm();
- auto scope = DECLARE_THROW_SCOPE(vm);
-
ArrayPrototype* arrayPrototype = thisObject->globalObject()->arrayPrototype();
- 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);
return !thisObject->hasCustomProperties()
&& arrayPrototype == thisObject->getPrototypeDirect()
- && status == ArrayPrototype::SpeciesWatchpointStatus::Initialized;
+ && arrayPrototype->globalObject()->arraySpeciesWatchpoint().isStillValid();
}
enum class SpeciesConstructResult {
@@ -230,8 +221,7 @@
if (LIKELY(thisIsArray)) {
// Fast path in the normal case where the user has not set an own constructor and the Array.prototype.constructor is normal.
// We need prototype check for subclasses of Array, which are Array objects but have a different prototype by default.
- bool isValid = speciesWatchpointsValid(exec, thisObject);
- RETURN_IF_EXCEPTION(scope, exceptionResult());
+ bool isValid = speciesWatchpointIsValid(thisObject);
if (LIKELY(isValid))
return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
@@ -920,29 +910,29 @@
EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec)
{
- // http://developer.netscape.com/docs/manuals/js/client/jsref/array.htm#1193713 or 15.4.4.10
+ // https://tc39.github.io/ecma262/#sec-array.prototype.slice
VM& vm = exec->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
ASSERT(!!scope.exception() == !thisObj);
if (UNLIKELY(!thisObj))
- return encodedJSValue();
+ return { };
unsigned length = getLength(exec, thisObj);
- RETURN_IF_EXCEPTION(scope, encodedJSValue());
+ RETURN_IF_EXCEPTION(scope, { });
unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length);
- RETURN_IF_EXCEPTION(scope, encodedJSValue());
+ RETURN_IF_EXCEPTION(scope, { });
unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length);
- RETURN_IF_EXCEPTION(scope, encodedJSValue());
+ RETURN_IF_EXCEPTION(scope, { });
std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, end - begin);
// We can only get an exception if we call some user function.
ASSERT(!!scope.exception() == (speciesResult.first == SpeciesConstructResult::Exception));
if (UNLIKELY(speciesResult.first == SpeciesConstructResult::Exception))
- return encodedJSValue();
+ return { };
bool okToDoFastPath = speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj) && length == getLength(exec, thisObj);
- RETURN_IF_EXCEPTION(scope, encodedJSValue());
+ RETURN_IF_EXCEPTION(scope, { });
if (LIKELY(okToDoFastPath)) {
if (JSArray* result = asArray(thisObj)->fastSlice(*exec, begin, end - begin))
return JSValue::encode(result);
@@ -953,16 +943,16 @@
result = speciesResult.second;
else {
result = constructEmptyArray(exec, nullptr, end - begin);
- RETURN_IF_EXCEPTION(scope, encodedJSValue());
+ RETURN_IF_EXCEPTION(scope, { });
}
unsigned n = 0;
for (unsigned k = begin; k < end; k++, n++) {
JSValue v = getProperty(exec, thisObj, k);
- RETURN_IF_EXCEPTION(scope, encodedJSValue());
+ RETURN_IF_EXCEPTION(scope, { });
if (v) {
result->putDirectIndex(exec, n, v, 0, PutDirectIndexShouldThrow);
- RETURN_IF_EXCEPTION(scope, encodedJSValue());
+ RETURN_IF_EXCEPTION(scope, { });
}
}
scope.release();
@@ -1249,8 +1239,7 @@
return JSValue::encode(jsNull());
// We need to check the species constructor here since checking it in the JS wrapper is too expensive for the non-optimizing tiers.
- bool isValid = speciesWatchpointsValid(exec, firstArray);
- ASSERT(!scope.exception() || !isValid);
+ bool isValid = speciesWatchpointIsValid(firstArray);
if (UNLIKELY(!isValid))
return JSValue::encode(jsNull());
@@ -1342,15 +1331,18 @@
ArrayPrototype* m_arrayPrototype;
};
-ArrayPrototype::SpeciesWatchpointStatus ArrayPrototype::attemptToInitializeSpeciesWatchpoint(ExecState* exec)
+void ArrayPrototype::initializeSpeciesWatchpoint(ExecState* exec)
{
- ASSERT(m_speciesWatchpointStatus == SpeciesWatchpointStatus::Uninitialized);
+ VM& vm = exec->vm();
- VM& vm = exec->vm();
+ RELEASE_ASSERT(!m_constructorWatchpoint);
+ RELEASE_ASSERT(!m_constructorSpeciesWatchpoint);
+
auto scope = DECLARE_THROW_SCOPE(vm);
+ UNUSED_PARAM(scope);
if (verbose)
- 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");
+ 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");
// First we need to make sure that the Array.prototype.constructor property points to Array
// and that Array[Symbol.species] is the primordial GetterSetter.
@@ -1364,12 +1356,11 @@
ArrayConstructor* arrayConstructor = globalObject->arrayConstructor();
PropertySlot constructorSlot(this, PropertySlot::InternalMethodType::VMInquiry);
- 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;
+ 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);
Structure* constructorStructure = arrayConstructor->structure(vm);
if (constructorStructure->isDictionary())
@@ -1376,12 +1367,11 @@
constructorStructure = constructorStructure->flattenDictionaryStructure(vm, arrayConstructor);
PropertySlot speciesSlot(arrayConstructor, PropertySlot::InternalMethodType::VMInquiry);
- 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;
+ arrayConstructor->getOwnPropertySlot(arrayConstructor, exec, vm.propertyNames->speciesSymbol, speciesSlot);
+ ASSERT(!scope.exception());
+ ASSERT(speciesSlot.slotBase() == arrayConstructor);
+ ASSERT(speciesSlot.isCacheableGetter());
+ RELEASE_ASSERT(speciesSlot.getterSetter() == globalObject->speciesGetterSetter());
// Now we need to setup the watchpoints to make sure these conditions remain valid.
prototypeStructure->startWatchingPropertyForReplacements(vm, constructorSlot.cachedOffset());
@@ -1390,8 +1380,8 @@
ObjectPropertyCondition constructorCondition = ObjectPropertyCondition::equivalence(vm, this, this, vm.propertyNames->constructor.impl(), arrayConstructor);
ObjectPropertyCondition speciesCondition = ObjectPropertyCondition::equivalence(vm, this, arrayConstructor, vm.propertyNames->speciesSymbol.impl(), globalObject->speciesGetterSetter());
- if (!constructorCondition.isWatchable() || !speciesCondition.isWatchable())
- return m_speciesWatchpointStatus = SpeciesWatchpointStatus::Fired;
+ RELEASE_ASSERT(constructorCondition.isWatchable());
+ RELEASE_ASSERT(speciesCondition.isWatchable());
m_constructorWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(constructorCondition, this);
m_constructorWatchpoint->install();
@@ -1398,8 +1388,6 @@
m_constructorSpeciesWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(speciesCondition, this);
m_constructorSpeciesWatchpoint->install();
-
- return m_speciesWatchpointStatus = SpeciesWatchpointStatus::Initialized;
}
ArrayPrototypeAdaptiveInferredPropertyWatchpoint::ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition& key, ArrayPrototype* prototype)
@@ -1418,7 +1406,8 @@
if (verbose)
WTF::dataLog(stringDetail, "\n");
- m_arrayPrototype->m_speciesWatchpointStatus = ArrayPrototype::SpeciesWatchpointStatus::Fired;
+ JSGlobalObject* globalObject = m_arrayPrototype->globalObject();
+ globalObject->arraySpeciesWatchpoint().fireAll(globalObject->vm(), stringDetail);
}
} // namespace JSC
Modified: branches/safari-603-branch/Source/_javascript_Core/runtime/ArrayPrototype.h (210864 => 210865)
--- branches/safari-603-branch/Source/_javascript_Core/runtime/ArrayPrototype.h 2017-01-18 20:29:26 UTC (rev 210864)
+++ branches/safari-603-branch/Source/_javascript_Core/runtime/ArrayPrototype.h 2017-01-18 20:42:19 UTC (rev 210865)
@@ -49,8 +49,7 @@
return Structure::create(vm, globalObject, prototype, TypeInfo(DerivedArrayType, StructureFlags), info(), ArrayClass);
}
- SpeciesWatchpointStatus speciesWatchpointStatus() const { return m_speciesWatchpointStatus; }
- SpeciesWatchpointStatus attemptToInitializeSpeciesWatchpoint(ExecState*);
+ void initializeSpeciesWatchpoint(ExecState*);
static const bool needsDestruction = false;
// We don't need destruction since we use a finalizer.
@@ -64,7 +63,6 @@
friend ArrayPrototypeAdaptiveInferredPropertyWatchpoint;
std::unique_ptr<ArrayPrototypeAdaptiveInferredPropertyWatchpoint> m_constructorWatchpoint;
std::unique_ptr<ArrayPrototypeAdaptiveInferredPropertyWatchpoint> m_constructorSpeciesWatchpoint;
- SpeciesWatchpointStatus m_speciesWatchpointStatus { SpeciesWatchpointStatus::Uninitialized };
};
EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState*);
Modified: branches/safari-603-branch/Source/_javascript_Core/runtime/Intrinsic.h (210864 => 210865)
--- branches/safari-603-branch/Source/_javascript_Core/runtime/Intrinsic.h 2017-01-18 20:29:26 UTC (rev 210864)
+++ branches/safari-603-branch/Source/_javascript_Core/runtime/Intrinsic.h 2017-01-18 20:42:19 UTC (rev 210865)
@@ -40,6 +40,7 @@
TanIntrinsic,
ArrayPushIntrinsic,
ArrayPopIntrinsic,
+ ArraySliceIntrinsic,
CharCodeAtIntrinsic,
CharAtIntrinsic,
FromCharCodeIntrinsic,
Modified: branches/safari-603-branch/Source/_javascript_Core/runtime/JSGlobalObject.cpp (210864 => 210865)
--- branches/safari-603-branch/Source/_javascript_Core/runtime/JSGlobalObject.cpp 2017-01-18 20:29:26 UTC (rev 210864)
+++ branches/safari-603-branch/Source/_javascript_Core/runtime/JSGlobalObject.cpp 2017-01-18 20:42:19 UTC (rev 210865)
@@ -331,6 +331,7 @@
, m_varInjectionWatchpoint(adoptRef(new WatchpointSet(IsWatched)))
, m_weakRandom(Options::forceWeakRandomSeed() ? Options::forcedWeakRandomSeed() : static_cast<unsigned>(randomNumber() * (std::numeric_limits<unsigned>::max() + 1.0)))
, m_arrayIteratorProtocolWatchpoint(IsWatched)
+ , m_arraySpeciesWatchpoint(IsWatched)
, m_templateRegistry(vm)
, m_evalEnabled(true)
, m_runtimeFlags()
@@ -943,6 +944,8 @@
m_arrayPrototypeSymbolIteratorWatchpoint = std::make_unique<ArrayIteratorAdaptiveWatchpoint>(condition, this);
m_arrayPrototypeSymbolIteratorWatchpoint->install();
}
+
+ this->arrayPrototype()->initializeSpeciesWatchpoint(exec);
}
resetPrototype(vm, getPrototypeDirect());
Modified: branches/safari-603-branch/Source/_javascript_Core/runtime/JSGlobalObject.h (210864 => 210865)
--- branches/safari-603-branch/Source/_javascript_Core/runtime/JSGlobalObject.h 2017-01-18 20:29:26 UTC (rev 210864)
+++ branches/safari-603-branch/Source/_javascript_Core/runtime/JSGlobalObject.h 2017-01-18 20:42:19 UTC (rev 210865)
@@ -398,9 +398,11 @@
WeakRandom m_weakRandom;
InlineWatchpointSet& arrayIteratorProtocolWatchpoint() { return m_arrayIteratorProtocolWatchpoint; }
+ InlineWatchpointSet& arraySpeciesWatchpoint() { return m_arraySpeciesWatchpoint; }
// If this hasn't been invalidated, it means the array iterator protocol
// is not observable to user code yet.
InlineWatchpointSet m_arrayIteratorProtocolWatchpoint;
+ InlineWatchpointSet m_arraySpeciesWatchpoint;
std::unique_ptr<ArrayIteratorAdaptiveWatchpoint> m_arrayPrototypeSymbolIteratorWatchpoint;
std::unique_ptr<ArrayIteratorAdaptiveWatchpoint> m_arrayIteratorPrototypeNext;
Modified: branches/safari-603-branch/Source/_javascript_Core/runtime/Structure.h (210864 => 210865)
--- branches/safari-603-branch/Source/_javascript_Core/runtime/Structure.h 2017-01-18 20:29:26 UTC (rev 210864)
+++ branches/safari-603-branch/Source/_javascript_Core/runtime/Structure.h 2017-01-18 20:42:19 UTC (rev 210865)
@@ -464,6 +464,11 @@
return OBJECT_OFFSETOF(Structure, m_propertyTableUnsafe);
}
+ static ptrdiff_t inlineCapacityOffset()
+ {
+ return OBJECT_OFFSETOF(Structure, m_inlineCapacity);
+ }
+
static Structure* createStructure(VM&);
bool transitionWatchpointSetHasBeenInvalidated() const