Add a slice intrinsic to the DFG/FTL
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 13 Jan 2017 04:03:47 +0000 (04:03 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 13 Jan 2017 04:03:47 +0000 (04:03 +0000)
https://bugs.webkit.org/show_bug.cgi?id=166707
<rdar://problem/29913445>

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.
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:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@210695 268f45cc-cd09-0410-ab3c-d52691b4dbfc

29 files changed:
JSTests/ChangeLog
JSTests/stress/array-slice-intrinsic.js [new file with mode: 0644]
JSTests/stress/array-slice-jettison-on-constructor-change.js [new file with mode: 0644]
JSTests/stress/array-slice-osr-exit-2.js [new file with mode: 0644]
JSTests/stress/array-slice-osr-exit.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGCallArrayAllocatorSlowPathGenerator.h
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/jit/AssemblyHelpers.cpp
Source/JavaScriptCore/runtime/ArrayPrototype.cpp
Source/JavaScriptCore/runtime/ArrayPrototype.h
Source/JavaScriptCore/runtime/Intrinsic.h
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSGlobalObject.h
Source/JavaScriptCore/runtime/Structure.h

index 0a2560d..c2c3743 100644 (file)
@@ -1,3 +1,39 @@
+2017-01-12  Saam Barati  <sbarati@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-11  Ryan Haddad  <ryanhaddad@apple.com>
 
         Mark es6/typed_arrays_correct_prototype_chains.js as failing after r210570.
diff --git a/JSTests/stress/array-slice-intrinsic.js b/JSTests/stress/array-slice-intrinsic.js
new file mode 100644 (file)
index 0000000..562db6d
--- /dev/null
@@ -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);
+    }
+}
diff --git a/JSTests/stress/array-slice-jettison-on-constructor-change.js b/JSTests/stress/array-slice-jettison-on-constructor-change.js
new file mode 100644 (file)
index 0000000..13aa63c
--- /dev/null
@@ -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();
diff --git a/JSTests/stress/array-slice-osr-exit-2.js b/JSTests/stress/array-slice-osr-exit-2.js
new file mode 100644 (file)
index 0000000..15a7f44
--- /dev/null
@@ -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();
diff --git a/JSTests/stress/array-slice-osr-exit.js b/JSTests/stress/array-slice-osr-exit.js
new file mode 100644 (file)
index 0000000..980feac
--- /dev/null
@@ -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();
index 13fb9d9..5507973 100644 (file)
@@ -1,5 +1,107 @@
 2017-01-12  Saam Barati  <sbarati@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-12  Saam Barati  <sbarati@apple.com>
+
         Concurrent GC has a bug where we would detect a race but fail to rescan the object
         https://bugs.webkit.org/show_bug.cgi?id=166960
         <rdar://problem/29983526>
index 2791aea..ad8ec4a 100644 (file)
@@ -1650,6 +1650,20 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         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);
index d452b92..f9778f7 100644 (file)
@@ -2246,6 +2246,94 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, int resultOperand, Intrin
             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)
index fa9f639..53dad50 100644 (file)
@@ -124,6 +124,45 @@ private:
     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)
index 0ced522..619f274 100644 (file)
@@ -503,6 +503,19 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         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:
index e71f2b3..cdc0406 100644 (file)
@@ -309,6 +309,7 @@ bool doesGC(Graph& graph, Node* node)
     case ToLowerCase:
     case CallDOMGetter:
     case CallDOM:
+    case ArraySlice:
         return true;
         
     case MultiPutByOffset:
index ca562b4..1ad7bc1 100644 (file)
@@ -931,6 +931,14 @@ private:
             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: {
index d45e4df..6af4b35 100644 (file)
@@ -248,6 +248,7 @@ namespace JSC { namespace DFG {
     /* 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) \
index 356ef3e..d0f4b47 100644 (file)
@@ -860,6 +860,7 @@ private:
             break;
         }
             
+        case ArraySlice:
         case NewArrayWithSpread:
         case NewArray:
         case NewArrayWithSize:
index f3f7892..7b5af95 100644 (file)
@@ -382,6 +382,13 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     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.
index 5364b59..e98e121 100644 (file)
@@ -7170,6 +7170,182 @@ void SpeculativeJIT::compileGetRestLength(Node* node)
     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();
@@ -9380,6 +9556,21 @@ void SpeculativeJIT::compileDefineAccessorProperty(Node* node)
     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
index b0f0e08..26957d8 100644 (file)
@@ -743,6 +743,8 @@ public:
 
     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 @@ public:
     void compileSpread(Node*);
     void compileNewArrayWithSpread(Node*);
     void compileGetRestLength(Node*);
+    void compileArraySlice(Node*);
     void compileNotifyWrite(Node*);
     bool compileRegExpExec(Node*);
     void compileIsObjectOrNull(Node*);
index 4fa8484..9cde8da 100644 (file)
@@ -3550,6 +3550,11 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case ArraySlice: {
+        compileArraySlice(node);
+        break;
+    }
+
     case DFG::Jump: {
         jump(node->targetBlock());
         noResult(node);
@@ -5666,6 +5671,18 @@ void SpeculativeJIT::compileArithRandom(Node* node)
     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 @@ void SpeculativeJIT::compileAllocateNewArrayWithSize(JSGlobalObject* globalObjec
     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);
             
index e51499d..606064c 100644 (file)
@@ -3462,6 +3462,11 @@ void SpeculativeJIT::compile(Node* node)
         }
         break;
     }
+
+    case ArraySlice: {
+        compileArraySlice(node);
+        break;
+    }
         
     case ArrayPop: {
         ASSERT(node->arrayMode().isJSArray());
@@ -5939,6 +5944,17 @@ void SpeculativeJIT::compileArithRandom(Node* node)
     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 @@ void SpeculativeJIT::compileAllocateNewArrayWithSize(JSGlobalObject* globalObjec
     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);
index 7cf9885..0880698 100644 (file)
@@ -108,6 +108,8 @@ namespace JSC { namespace FTL {
     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()) \
index d42ff4a..4383f12 100644 (file)
@@ -282,6 +282,7 @@ inline CapabilityLevel canCompile(Node* node)
     case CheckDOM:
     case CallDOM:
     case CallDOMGetter:
+    case ArraySlice:
         // These are OK.
         break;
 
index 172ada4..8db38d9 100644 (file)
@@ -706,6 +706,9 @@ private:
         case ArrayPop:
             compileArrayPop();
             break;
+        case ArraySlice:
+            compileArraySlice();
+            break;
         case CreateActivation:
             compileCreateActivation();
             break;
@@ -3860,6 +3863,75 @@ private:
             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()
     {
@@ -4627,11 +4699,10 @@ private:
             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;
         }
@@ -8666,7 +8737,7 @@ private:
 
                 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;
@@ -9422,26 +9493,35 @@ private:
         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;
@@ -10393,37 +10473,69 @@ private:
             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);
@@ -10546,15 +10658,17 @@ private:
         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();
@@ -10577,12 +10691,12 @@ private:
         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));
             }
         }
         
@@ -10615,7 +10729,7 @@ private:
         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);
@@ -10627,7 +10741,7 @@ private:
         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);
@@ -10657,7 +10771,7 @@ private:
         bool shouldInitializeElements = false;
         bool shouldLargeArraySizeCreateArrayStorage = false;
         return allocateJSArray(
-            publicLength, structure, shouldInitializeElements,
+            publicLength, m_out.constIntPtr(structure), m_out.constInt32(structure->indexingType()), shouldInitializeElements,
             shouldLargeArraySizeCreateArrayStorage);
     }
     
index 4b4cd8c..ba017e8 100644 (file)
@@ -447,6 +447,7 @@ void AssemblyHelpers::loadProperty(GPRReg object, GPRReg offset, JSValueRegs res
 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);
index 68edc38..d60bcdf 100644 (file)
@@ -98,7 +98,7 @@ void ArrayPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
     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 @@ static ALWAYS_INLINE void setLength(ExecState* exec, VM& vm, JSObject* obj, unsi
         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 @@ static ALWAYS_INLINE std::pair<SpeciesConstructResult, JSObject*> speciesConstru
     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 arrayProtoFuncShift(ExecState* exec)
 
 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 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec)
         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 @@ EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncConcatMemcpy(ExecState* exec)
         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 @@ private:
     ArrayPrototype* m_arrayPrototype;
 };
 
-ArrayPrototype::SpeciesWatchpointStatus ArrayPrototype::attemptToInitializeSpeciesWatchpoint(ExecState* exec)
+void ArrayPrototype::initializeSpeciesWatchpoint(ExecState* exec)
 {
-    ASSERT(m_speciesWatchpointStatus == SpeciesWatchpointStatus::Uninitialized);
-
     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,24 +1356,22 @@ ArrayPrototype::SpeciesWatchpointStatus ArrayPrototype::attemptToInitializeSpeci
     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())
         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,16 +1380,14 @@ ArrayPrototype::SpeciesWatchpointStatus ArrayPrototype::attemptToInitializeSpeci
     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();
 
     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 @@ void ArrayPrototypeAdaptiveInferredPropertyWatchpoint::handleFire(const FireDeta
     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
index 0f3f9fd..2b0b977 100644 (file)
@@ -49,8 +49,7 @@ public:
         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 @@ private:
     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*);
index e4f216a..adae5ef 100644 (file)
@@ -40,6 +40,7 @@ enum JS_EXPORT_PRIVATE Intrinsic {
     TanIntrinsic,
     ArrayPushIntrinsic,
     ArrayPopIntrinsic,
+    ArraySliceIntrinsic,
     CharCodeAtIntrinsic,
     CharAtIntrinsic,
     FromCharCodeIntrinsic,
index 978238d..265d22c 100644 (file)
@@ -332,6 +332,7 @@ JSGlobalObject::JSGlobalObject(VM& vm, Structure* structure, const GlobalObjectM
     , 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()
@@ -946,6 +947,8 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
             m_arrayPrototypeSymbolIteratorWatchpoint = std::make_unique<ArrayIteratorAdaptiveWatchpoint>(condition, this);
             m_arrayPrototypeSymbolIteratorWatchpoint->install();
         }
+
+        this->arrayPrototype()->initializeSpeciesWatchpoint(exec);
     }
 
     resetPrototype(vm, getPrototypeDirect());
index 76aeb30..5c5c1a5 100644 (file)
@@ -402,9 +402,11 @@ public:
     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;
 
index 087d3be..e424467 100644 (file)
@@ -464,6 +464,11 @@ public:
         return OBJECT_OFFSETOF(Structure, m_propertyTableUnsafe);
     }
 
+    static ptrdiff_t inlineCapacityOffset()
+    {
+        return OBJECT_OFFSETOF(Structure, m_inlineCapacity);
+    }
+
     static Structure* createStructure(VM&);
         
     bool transitionWatchpointSetHasBeenInvalidated() const