Title: [234066] trunk
Revision
234066
Author
[email protected]
Date
2018-07-20 14:25:19 -0700 (Fri, 20 Jul 2018)

Log Message

[DFG] Fold GetByVal if Array is CoW
https://bugs.webkit.org/show_bug.cgi?id=186459

Reviewed by Saam Barati.

JSTests:

* stress/folding-get-by-val-with-immutable-butterfly-out-of-bounds-foldable.js: Added.
(shouldBe):
(test0):
(test1):
(test2):
(test3):
(test4):
(test5):
* stress/folding-get-by-val-with-immutable-butterfly-out-of-bounds.js: Added.
(shouldBe):
(test0):
(test1):
(test2):
(test3):
(test4):
(test5):
* stress/folding-get-by-val-with-immutable-butterfly-with-types.js: Added.
(shouldBe):
(test0):
(test1):
(test2):
(test3):
(test4):
(test5):
* stress/folding-get-by-val-with-immutable-butterfly.js: Added.
(shouldBe):
(checking):
(test):

Source/_javascript_Core:

CoW indexing type means that we now tracks the changes in CoW Array by structure. So DFG has a chance to
fold GetByVal if the given array is CoW. This patch folds GetByVal onto the CoW Array. If the structure
is watched and the butterfly is JSImmutableButterfly, we can load the value from this butterfly.

This can be useful since these CoW arrays are used for a storage for constants. Constant-indexed access
to these constant arrays can be folded into an actual constant by this patch.

                                   baseline                  patched

template_string.es6          4993.9853+-147.5308   ^    824.1685+-44.1839       ^ definitely 6.0594x faster
template_string_tag.es5        67.0822+-2.0100     ^      9.3540+-0.5376        ^ definitely 7.1715x faster

* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (234065 => 234066)


--- trunk/JSTests/ChangeLog	2018-07-20 21:14:15 UTC (rev 234065)
+++ trunk/JSTests/ChangeLog	2018-07-20 21:25:19 UTC (rev 234066)
@@ -1,3 +1,39 @@
+2018-07-20  Yusuke Suzuki  <[email protected]>
+
+        [DFG] Fold GetByVal if Array is CoW
+        https://bugs.webkit.org/show_bug.cgi?id=186459
+
+        Reviewed by Saam Barati.
+
+        * stress/folding-get-by-val-with-immutable-butterfly-out-of-bounds-foldable.js: Added.
+        (shouldBe):
+        (test0):
+        (test1):
+        (test2):
+        (test3):
+        (test4):
+        (test5):
+        * stress/folding-get-by-val-with-immutable-butterfly-out-of-bounds.js: Added.
+        (shouldBe):
+        (test0):
+        (test1):
+        (test2):
+        (test3):
+        (test4):
+        (test5):
+        * stress/folding-get-by-val-with-immutable-butterfly-with-types.js: Added.
+        (shouldBe):
+        (test0):
+        (test1):
+        (test2):
+        (test3):
+        (test4):
+        (test5):
+        * stress/folding-get-by-val-with-immutable-butterfly.js: Added.
+        (shouldBe):
+        (checking):
+        (test):
+
 2018-07-20  Saam Barati  <[email protected]>
 
         CompareEq should be using KnownOtherUse instead of OtherUse

Added: trunk/JSTests/stress/folding-get-by-val-with-immutable-butterfly-out-of-bounds-foldable.js (0 => 234066)


--- trunk/JSTests/stress/folding-get-by-val-with-immutable-butterfly-out-of-bounds-foldable.js	                        (rev 0)
+++ trunk/JSTests/stress/folding-get-by-val-with-immutable-butterfly-out-of-bounds-foldable.js	2018-07-20 21:25:19 UTC (rev 234066)
@@ -0,0 +1,56 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+var array0 = [1, 2, 3, 4, 5];
+var array1 = [1.2, 2.3, 3.4, 4.5, 5.6];
+var array2 = ["Hello", "New", "World", "Cappuccino", "Cocoa"];
+var array3 = [null, null, null, null, null];
+var array4 = [undefined, undefined, undefined, undefined, undefined];
+var array5 = [false, true, false, true, false];
+
+function test0()
+{
+    return array0[5];
+}
+noInline(test0);
+
+function test1()
+{
+    return array1[5];
+}
+noInline(test1);
+
+function test2()
+{
+    return array2[5];
+}
+noInline(test2);
+
+function test3()
+{
+    return array3[5];
+}
+noInline(test3);
+
+function test4()
+{
+    return array4[5];
+}
+noInline(test4);
+
+function test5()
+{
+    return array5[5];
+}
+noInline(test5);
+
+for (var i = 0; i < 1e5; ++i) {
+    shouldBe(test0(), undefined);
+    shouldBe(test1(), undefined);
+    shouldBe(test2(), undefined);
+    shouldBe(test3(), undefined);
+    shouldBe(test4(), undefined);
+    shouldBe(test5(), undefined);
+}

Added: trunk/JSTests/stress/folding-get-by-val-with-immutable-butterfly-out-of-bounds.js (0 => 234066)


--- trunk/JSTests/stress/folding-get-by-val-with-immutable-butterfly-out-of-bounds.js	                        (rev 0)
+++ trunk/JSTests/stress/folding-get-by-val-with-immutable-butterfly-out-of-bounds.js	2018-07-20 21:25:19 UTC (rev 234066)
@@ -0,0 +1,66 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+var array0 = [1, 2, 3, 4, 5];
+var array1 = [1.2, 2.3, 3.4, 4.5, 5.6];
+var array2 = ["Hello", "New", "World", "Cappuccino", "Cocoa"];
+var array3 = [null, null, null, null, null];
+var array4 = [undefined, undefined, undefined, undefined, undefined];
+var array5 = [false, true, false, true, false];
+
+function test0()
+{
+    return array0[5];
+}
+noInline(test0);
+
+function test1()
+{
+    return array1[5];
+}
+noInline(test1);
+
+function test2()
+{
+    return array2[5];
+}
+noInline(test2);
+
+function test3()
+{
+    return array3[5];
+}
+noInline(test3);
+
+function test4()
+{
+    return array4[5];
+}
+noInline(test4);
+
+function test5()
+{
+    return array5[5];
+}
+noInline(test5);
+
+for (var i = 0; i < 1e5; ++i) {
+    shouldBe(test0(), undefined);
+    shouldBe(test1(), undefined);
+    shouldBe(test2(), undefined);
+    shouldBe(test3(), undefined);
+    shouldBe(test4(), undefined);
+    shouldBe(test5(), undefined);
+}
+// Breaking sane chains.
+Array.prototype[5] = 42;
+for (var i = 0; i < 1e5; ++i) {
+    shouldBe(test0(), 42);
+    shouldBe(test1(), 42);
+    shouldBe(test2(), 42);
+    shouldBe(test3(), 42);
+    shouldBe(test4(), 42);
+    shouldBe(test5(), 42);
+}

Added: trunk/JSTests/stress/folding-get-by-val-with-immutable-butterfly-with-types.js (0 => 234066)


--- trunk/JSTests/stress/folding-get-by-val-with-immutable-butterfly-with-types.js	                        (rev 0)
+++ trunk/JSTests/stress/folding-get-by-val-with-immutable-butterfly-with-types.js	2018-07-20 21:25:19 UTC (rev 234066)
@@ -0,0 +1,56 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+var array0 = [1, 2, 3, 4, 5];
+var array1 = [1.2, 2.3, 3.4, 4.5, 5.6];
+var array2 = ["Hello", "New", "World", "Cappuccino", "Cocoa"];
+var array3 = [null, null, null, null, null];
+var array4 = [undefined, undefined, undefined, undefined, undefined];
+var array5 = [false, true, false, true, false];
+
+function test0()
+{
+    return array0[0];
+}
+noInline(test0);
+
+function test1()
+{
+    return array1[0];
+}
+noInline(test1);
+
+function test2()
+{
+    return array2[0];
+}
+noInline(test2);
+
+function test3()
+{
+    return array3[0];
+}
+noInline(test3);
+
+function test4()
+{
+    return array4[0];
+}
+noInline(test4);
+
+function test5()
+{
+    return array5[0];
+}
+noInline(test5);
+
+for (var i = 0; i < 1e6; ++i) {
+    shouldBe(test0(), 1);
+    shouldBe(test1(), 1.2);
+    shouldBe(test2(), "Hello");
+    shouldBe(test3(), null);
+    shouldBe(test4(), undefined);
+    shouldBe(test5(), false);
+}

Added: trunk/JSTests/stress/folding-get-by-val-with-immutable-butterfly.js (0 => 234066)


--- trunk/JSTests/stress/folding-get-by-val-with-immutable-butterfly.js	                        (rev 0)
+++ trunk/JSTests/stress/folding-get-by-val-with-immutable-butterfly.js	2018-07-20 21:25:19 UTC (rev 234066)
@@ -0,0 +1,30 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+var array = [1, 2, 3, 4, 5];
+
+function checking(i)
+{
+    if (i === (1e6 - 1)) {
+        // array[0] = 42;
+        array.ok = 4000;
+    } else if (i === (2e6 - 4000)) {
+        array.hey = 4000;
+    } else if (i === (1e6 * 2)) {
+        array[0] = 42;
+    }
+}
+noInline(checking);
+
+function test(i)
+{
+    checking(i);
+    return array[0] + array[1];
+}
+noInline(test);
+
+for (var i = 0; i < 2e6; ++i)
+    shouldBe(test(i), 3);
+shouldBe(test(2e6), 44);

Modified: trunk/Source/_javascript_Core/ChangeLog (234065 => 234066)


--- trunk/Source/_javascript_Core/ChangeLog	2018-07-20 21:14:15 UTC (rev 234065)
+++ trunk/Source/_javascript_Core/ChangeLog	2018-07-20 21:25:19 UTC (rev 234066)
@@ -1,5 +1,27 @@
 2018-07-20  Yusuke Suzuki  <[email protected]>
 
+        [DFG] Fold GetByVal if Array is CoW
+        https://bugs.webkit.org/show_bug.cgi?id=186459
+
+        Reviewed by Saam Barati.
+
+        CoW indexing type means that we now tracks the changes in CoW Array by structure. So DFG has a chance to
+        fold GetByVal if the given array is CoW. This patch folds GetByVal onto the CoW Array. If the structure
+        is watched and the butterfly is JSImmutableButterfly, we can load the value from this butterfly.
+
+        This can be useful since these CoW arrays are used for a storage for constants. Constant-indexed access
+        to these constant arrays can be folded into an actual constant by this patch.
+
+                                           baseline                  patched
+
+        template_string.es6          4993.9853+-147.5308   ^    824.1685+-44.1839       ^ definitely 6.0594x faster
+        template_string_tag.es5        67.0822+-2.0100     ^      9.3540+-0.5376        ^ definitely 7.1715x faster
+
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+
+2018-07-20  Yusuke Suzuki  <[email protected]>
+
         [JSC] Remove cellLock in JSObject::convertContiguousToArrayStorage
         https://bugs.webkit.org/show_bug.cgi?id=186602
 

Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (234065 => 234066)


--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2018-07-20 21:14:15 UTC (rev 234065)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2018-07-20 21:25:19 UTC (rev 234066)
@@ -28,6 +28,7 @@
 #if ENABLE(DFG_JIT)
 
 #include "ArrayConstructor.h"
+#include "ArrayPrototype.h"
 #include "DFGAbstractInterpreter.h"
 #include "DFGAbstractInterpreterClobberState.h"
 #include "DOMJITGetterSetter.h"
@@ -36,6 +37,7 @@
 #include "GetterSetter.h"
 #include "HashMapImpl.h"
 #include "JITOperations.h"
+#include "JSImmutableButterfly.h"
 #include "MathCommon.h"
 #include "NumberConstructor.h"
 #include "Operations.h"
@@ -1815,6 +1817,88 @@
     case AtomicsStore:
     case AtomicsSub:
     case AtomicsXor: {
+        if (node->op() == GetByVal) {
+            auto foldGetByValOnCoWArray = [&] (Edge& arrayEdge, Edge& indexEdge) {
+                // FIXME: We can expand this for non x86 environments.
+                // https://bugs.webkit.org/show_bug.cgi?id=134641
+                if (!isX86())
+                    return false;
+
+                AbstractValue& arrayValue = forNode(arrayEdge);
+                if (node->arrayMode().arrayClass() != Array::OriginalCopyOnWriteArray)
+                    return false;
+
+                // Check the structure set is finite. This means that this constant's structure is watched and guaranteed the one of this set.
+                // When the structure is changed, this code should be invalidated. This is important since the following code relies on the
+                // constant object's is not changed.
+                if (!arrayValue.m_structure.isFinite())
+                    return false;
+
+                JSValue arrayConstant = arrayValue.value();
+                if (!arrayConstant)
+                    return false;
+
+                JSObject* array = jsDynamicCast<JSObject*>(m_vm, arrayConstant);
+                if (!array)
+                    return false;
+
+                JSValue indexConstant = forNode(indexEdge).value();
+                if (!indexConstant || !indexConstant.isInt32() || indexConstant.asInt32() < 0)
+                    return false;
+                uint32_t index = indexConstant.asUInt32();
+
+                // Check that the early StructureID is not nuked, get the butterfly, and check the late StructureID again.
+                // And we check the indexing mode of the structure. If the indexing mode is CoW, the butterfly is
+                // definitely JSImmutableButterfly.
+                StructureID structureIDEarly = array->structureID();
+                if (isNuked(structureIDEarly))
+                    return false;
+
+                WTF::loadLoadFence();
+                Butterfly* butterfly = array->butterfly();
+
+                WTF::loadLoadFence();
+                StructureID structureIDLate = array->structureID();
+
+                if (structureIDEarly != structureIDLate)
+                    return false;
+
+                if (!isCopyOnWrite(m_vm.getStructure(structureIDLate)->indexingMode()))
+                    return false;
+
+                JSImmutableButterfly* immutableButterfly = JSImmutableButterfly::fromButterfly(butterfly);
+                if (index < immutableButterfly->length()) {
+                    JSValue value = immutableButterfly->get(index);
+                    ASSERT(value);
+                    if (value.isCell())
+                        setConstant(node, *m_graph.freeze(value.asCell()));
+                    else
+                        setConstant(node, value);
+                    return true;
+                }
+
+                if (node->arrayMode().isOutOfBounds()) {
+                    JSGlobalObject* globalObject = m_graph.globalObjectFor(node->origin.semantic);
+                    Structure* arrayPrototypeStructure = globalObject->arrayPrototype()->structure(m_vm);
+                    Structure* objectPrototypeStructure = globalObject->objectPrototype()->structure(m_vm);
+                    if (arrayPrototypeStructure->transitionWatchpointSetIsStillValid()
+                        && objectPrototypeStructure->transitionWatchpointSetIsStillValid()
+                        && globalObject->arrayPrototypeChainIsSane()) {
+                        m_graph.registerAndWatchStructureTransition(arrayPrototypeStructure);
+                        m_graph.registerAndWatchStructureTransition(objectPrototypeStructure);
+                        didFoldClobberWorld();
+                        // Note that Array::Double and Array::Int32 return JSValue if array mode is OutOfBounds.
+                        setConstant(node, jsUndefined());
+                        return true;
+                    }
+                }
+                return false;
+            };
+
+            if (foldGetByValOnCoWArray(m_graph.child(node, 0), m_graph.child(node, 1)))
+                break;
+        }
+
         if (node->op() != GetByVal) {
             unsigned numExtraArgs = numExtraAtomicsArgs(node->op());
             Edge storageEdge = m_graph.child(node, 2 + numExtraArgs);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to