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);