Title: [239324] trunk
Revision
239324
Author
[email protected]
Date
2018-12-17 22:54:49 -0800 (Mon, 17 Dec 2018)

Log Message

[JSC] Optimize Object.keys by caching own keys results in StructureRareData
https://bugs.webkit.org/show_bug.cgi?id=190047

Reviewed by Saam Barati.

JSTests:

* stress/object-keys-cached-zero.js: Added.
(shouldBe):
(test):
* stress/object-keys-changed-attribute.js: Added.
(shouldBe):
(test):
* stress/object-keys-changed-index.js: Added.
(shouldBe):
(test):
* stress/object-keys-changed.js: Added.
(shouldBe):
(test):
* stress/object-keys-indexed-non-cache.js: Added.
(shouldBe):
(test):
* stress/object-keys-overrides-get-property-names.js: Added.
(shouldBe):
(test):
(noInline):

Source/_javascript_Core:

Object.keys is one of the most frequently used function in web-tooling-benchmarks (WTB).
Object.keys is dominant in lebab of WTB, and frequently called in babel and others.
Since our Structure knows the shape of JSObject, we can cache the result of Object.keys
in Structure (StructureRareData) as we cache JSPropertyNameEnumerator in StructureRareData.

This patch caches the result of Object.keys in StructureRareData. The cached array is created
as JSImmutableButterfly. And Object.keys creates CoW from this data. Currently, the lifetime
strategy of this JSImmutableButterfly is the same to cached JSPropertyNameEnumerator. It is
referenced from Structure, and collected when Structure is collected.

This improves several benchmarks in SixSpeed.

                                baseline                  patched

    object-assign.es5      350.1710+-3.6303     ^    226.0368+-4.7558        ^ definitely 1.5492x faster
    for-of-object.es6      269.1941+-3.3430     ^    127.9317+-2.3875        ^ definitely 2.1042x faster

And it improves WTB lebab by 11.8%.

    Before: lebab:  6.10 runs/s
    After:  lebab:  6.82 runs/s

* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNode.cpp:
(JSC::DFG::Node::convertToNewArrayBuffer):
* dfg/DFGNode.h:
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileObjectKeys):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLAbstractHeapRepository.h:
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileObjectKeys):
* runtime/Butterfly.h:
(JSC::ContiguousData::Data::setStartingValue):
* runtime/Intrinsic.cpp:
(JSC::intrinsicName):
* runtime/Intrinsic.h:
* runtime/JSImmutableButterfly.h:
(JSC::JSImmutableButterfly::JSImmutableButterfly):
We set JSEmpty to the underlying butterfly storage if indexing type is Contiguous.
Otherwise, JSImmutableButterfly is half-baked one until all the storage is filled with some meaningful values, it leads to crash
if half-baked JSImmutableButterfly is exposed to GC.
* runtime/ObjectConstructor.cpp:
(JSC::ownPropertyKeys):
* runtime/Structure.cpp:
(JSC::Structure::canCachePropertyNameEnumerator const):
* runtime/Structure.h:
* runtime/StructureInlines.h:
(JSC::Structure::setCachedOwnKeys):
(JSC::Structure::cachedOwnKeys const):
(JSC::Structure::cachedOwnKeysIgnoringSentinel const):
(JSC::Structure::canCacheOwnKeys const):
* runtime/StructureRareData.cpp:
(JSC::StructureRareData::visitChildren):
(JSC::StructureRareData::cachedPropertyNameEnumerator const): Deleted.
(JSC::StructureRareData::setCachedPropertyNameEnumerator): Deleted.
* runtime/StructureRareData.h:
* runtime/StructureRareDataInlines.h:
(JSC::StructureRareData::cachedPropertyNameEnumerator const):
(JSC::StructureRareData::setCachedPropertyNameEnumerator):
(JSC::StructureRareData::cachedOwnKeys const):
(JSC::StructureRareData::cachedOwnKeysIgnoringSentinel const):
(JSC::StructureRareData::cachedOwnKeysConcurrently const):
(JSC::StructureRareData::setCachedOwnKeys):
(JSC::StructureRareData::previousID const): Deleted.
* runtime/VM.cpp:
(JSC::VM::VM):

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (239323 => 239324)


--- trunk/JSTests/ChangeLog	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/JSTests/ChangeLog	2018-12-18 06:54:49 UTC (rev 239324)
@@ -1,3 +1,30 @@
+2018-12-16  Yusuke Suzuki  <[email protected]>
+
+        [JSC] Optimize Object.keys by caching own keys results in StructureRareData
+        https://bugs.webkit.org/show_bug.cgi?id=190047
+
+        Reviewed by Saam Barati.
+
+        * stress/object-keys-cached-zero.js: Added.
+        (shouldBe):
+        (test):
+        * stress/object-keys-changed-attribute.js: Added.
+        (shouldBe):
+        (test):
+        * stress/object-keys-changed-index.js: Added.
+        (shouldBe):
+        (test):
+        * stress/object-keys-changed.js: Added.
+        (shouldBe):
+        (test):
+        * stress/object-keys-indexed-non-cache.js: Added.
+        (shouldBe):
+        (test):
+        * stress/object-keys-overrides-get-property-names.js: Added.
+        (shouldBe):
+        (test):
+        (noInline):
+
 2018-12-17  Mark Lam  <[email protected]>
 
         SamplingProfiler's isValidFramePointer() should reject address at stack origin.

Added: trunk/JSTests/stress/object-keys-cached-zero.js (0 => 239324)


--- trunk/JSTests/stress/object-keys-cached-zero.js	                        (rev 0)
+++ trunk/JSTests/stress/object-keys-cached-zero.js	2018-12-18 06:54:49 UTC (rev 239324)
@@ -0,0 +1,21 @@
+function shouldBe(actual, expected)
+{
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test(object)
+{
+    return Object.keys(object);
+}
+noInline(test);
+
+var object = {};
+for (var i = 0; i < 1e6; ++i) {
+    var result = test(object);
+    shouldBe(result.length, 0);
+    shouldBe(result[0], undefined);
+    result[0] = i;
+    shouldBe(result.length, 1);
+    shouldBe(result[0], i);
+}

Added: trunk/JSTests/stress/object-keys-changed-attribute.js (0 => 239324)


--- trunk/JSTests/stress/object-keys-changed-attribute.js	                        (rev 0)
+++ trunk/JSTests/stress/object-keys-changed-attribute.js	2018-12-18 06:54:49 UTC (rev 239324)
@@ -0,0 +1,28 @@
+function shouldBe(actual, expected)
+{
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test(object)
+{
+    return Object.keys(object);
+}
+noInline(test);
+
+var object = { Cocoa: 42 };
+for (var i = 0; i < 1e6; ++i) {
+    var result = test(object);
+    shouldBe(result.length, 1);
+    shouldBe(result[0], 'Cocoa');
+}
+
+Reflect.defineProperty(object, 'Cocoa', {
+    enumerable: false
+});
+
+for (var i = 0; i < 1e6; ++i) {
+    var result = test(object);
+    shouldBe(result.length, 0);
+    shouldBe(result[0], undefined);
+}

Added: trunk/JSTests/stress/object-keys-changed-index.js (0 => 239324)


--- trunk/JSTests/stress/object-keys-changed-index.js	                        (rev 0)
+++ trunk/JSTests/stress/object-keys-changed-index.js	2018-12-18 06:54:49 UTC (rev 239324)
@@ -0,0 +1,28 @@
+function shouldBe(actual, expected)
+{
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test(object)
+{
+    return Object.keys(object);
+}
+noInline(test);
+
+var object = {};
+for (var i = 0; i < 1e6; ++i) {
+    var result = test(object);
+    shouldBe(result.length, 0);
+    shouldBe(result[0], undefined);
+    result[0] = i;
+    shouldBe(result.length, 1);
+    shouldBe(result[0], i);
+}
+
+object[0] = 42;
+for (var i = 0; i < 1e6; ++i) {
+    var result = test(object);
+    shouldBe(result.length, 1);
+    shouldBe(result[0], '0');
+}

Added: trunk/JSTests/stress/object-keys-changed.js (0 => 239324)


--- trunk/JSTests/stress/object-keys-changed.js	                        (rev 0)
+++ trunk/JSTests/stress/object-keys-changed.js	2018-12-18 06:54:49 UTC (rev 239324)
@@ -0,0 +1,28 @@
+function shouldBe(actual, expected)
+{
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test(object)
+{
+    return Object.keys(object);
+}
+noInline(test);
+
+var object = {};
+for (var i = 0; i < 1e6; ++i) {
+    var result = test(object);
+    shouldBe(result.length, 0);
+    shouldBe(result[0], undefined);
+    result[0] = i;
+    shouldBe(result.length, 1);
+    shouldBe(result[0], i);
+}
+
+object.Cocoa = 42;
+for (var i = 0; i < 1e6; ++i) {
+    var result = test(object);
+    shouldBe(result.length, 1);
+    shouldBe(result[0], 'Cocoa');
+}

Added: trunk/JSTests/stress/object-keys-indexed-non-cache.js (0 => 239324)


--- trunk/JSTests/stress/object-keys-indexed-non-cache.js	                        (rev 0)
+++ trunk/JSTests/stress/object-keys-indexed-non-cache.js	2018-12-18 06:54:49 UTC (rev 239324)
@@ -0,0 +1,25 @@
+function shouldBe(actual, expected)
+{
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test(object)
+{
+    return Object.keys(object);
+}
+noInline(test);
+
+var object = {0: 42};
+for (var i = 0; i < 1e3; ++i) {
+    var result = test(object);
+    shouldBe(result.length, 1);
+    shouldBe(result[0], '0');
+}
+object[1] = 44;
+for (var i = 0; i < 1e3; ++i) {
+    var result = test(object);
+    shouldBe(result.length, 2);
+    shouldBe(result[0], '0');
+    shouldBe(result[1], '1');
+}

Added: trunk/JSTests/stress/object-keys-overrides-get-property-names.js (0 => 239324)


--- trunk/JSTests/stress/object-keys-overrides-get-property-names.js	                        (rev 0)
+++ trunk/JSTests/stress/object-keys-overrides-get-property-names.js	2018-12-18 06:54:49 UTC (rev 239324)
@@ -0,0 +1,57 @@
+function shouldBe(actual, expected)
+{
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test(object)
+{
+    return Object.keys(object);
+}
+noInline(test);
+
+{
+    let object = new String("Cocoa");
+    for (let i = 0; i < 1e3; ++i) {
+        let result = test(object);
+        shouldBe(result.length, 5);
+        shouldBe(result[0], '0');
+        shouldBe(result[1], '1');
+        shouldBe(result[2], '2');
+        shouldBe(result[3], '3');
+        shouldBe(result[4], '4');
+    }
+
+    object.Cocoa = 42;
+    let result = test(object);
+    shouldBe(result.length, 6);
+    shouldBe(result[0], '0');
+    shouldBe(result[1], '1');
+    shouldBe(result[2], '2');
+    shouldBe(result[3], '3');
+    shouldBe(result[4], '4');
+    shouldBe(result[5], 'Cocoa');
+}
+
+{
+    let object = new String("Cocoa");
+    for (let i = 0; i < 1e3; ++i) {
+        let result = test(object);
+        shouldBe(result.length, 5);
+        shouldBe(result[0], '0');
+        shouldBe(result[1], '1');
+        shouldBe(result[2], '2');
+        shouldBe(result[3], '3');
+        shouldBe(result[4], '4');
+    }
+
+    object[8] = 42;
+    let result = test(object);
+    shouldBe(result.length, 6);
+    shouldBe(result[0], '0');
+    shouldBe(result[1], '1');
+    shouldBe(result[2], '2');
+    shouldBe(result[3], '3');
+    shouldBe(result[4], '4');
+    shouldBe(result[5], '8');
+}

Modified: trunk/Source/_javascript_Core/ChangeLog (239323 => 239324)


--- trunk/Source/_javascript_Core/ChangeLog	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/ChangeLog	2018-12-18 06:54:49 UTC (rev 239324)
@@ -1,3 +1,102 @@
+2018-12-16  Yusuke Suzuki  <[email protected]>
+
+        [JSC] Optimize Object.keys by caching own keys results in StructureRareData
+        https://bugs.webkit.org/show_bug.cgi?id=190047
+
+        Reviewed by Saam Barati.
+
+        Object.keys is one of the most frequently used function in web-tooling-benchmarks (WTB).
+        Object.keys is dominant in lebab of WTB, and frequently called in babel and others.
+        Since our Structure knows the shape of JSObject, we can cache the result of Object.keys
+        in Structure (StructureRareData) as we cache JSPropertyNameEnumerator in StructureRareData.
+
+        This patch caches the result of Object.keys in StructureRareData. The cached array is created
+        as JSImmutableButterfly. And Object.keys creates CoW from this data. Currently, the lifetime
+        strategy of this JSImmutableButterfly is the same to cached JSPropertyNameEnumerator. It is
+        referenced from Structure, and collected when Structure is collected.
+
+        This improves several benchmarks in SixSpeed.
+
+                                        baseline                  patched
+
+            object-assign.es5      350.1710+-3.6303     ^    226.0368+-4.7558        ^ definitely 1.5492x faster
+            for-of-object.es6      269.1941+-3.3430     ^    127.9317+-2.3875        ^ definitely 2.1042x faster
+
+        And it improves WTB lebab by 11.8%.
+
+            Before: lebab:  6.10 runs/s
+            After:  lebab:  6.82 runs/s
+
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGConstantFoldingPhase.cpp:
+        (JSC::DFG::ConstantFoldingPhase::foldConstants):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGNode.cpp:
+        (JSC::DFG::Node::convertToNewArrayBuffer):
+        * dfg/DFGNode.h:
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileObjectKeys):
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * ftl/FTLAbstractHeapRepository.h:
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileObjectKeys):
+        * runtime/Butterfly.h:
+        (JSC::ContiguousData::Data::setStartingValue):
+        * runtime/Intrinsic.cpp:
+        (JSC::intrinsicName):
+        * runtime/Intrinsic.h:
+        * runtime/JSImmutableButterfly.h:
+        (JSC::JSImmutableButterfly::JSImmutableButterfly):
+        We set JSEmpty to the underlying butterfly storage if indexing type is Contiguous.
+        Otherwise, JSImmutableButterfly is half-baked one until all the storage is filled with some meaningful values, it leads to crash
+        if half-baked JSImmutableButterfly is exposed to GC.
+        * runtime/ObjectConstructor.cpp:
+        (JSC::ownPropertyKeys):
+        * runtime/Structure.cpp:
+        (JSC::Structure::canCachePropertyNameEnumerator const):
+        * runtime/Structure.h:
+        * runtime/StructureInlines.h:
+        (JSC::Structure::setCachedOwnKeys):
+        (JSC::Structure::cachedOwnKeys const):
+        (JSC::Structure::cachedOwnKeysIgnoringSentinel const):
+        (JSC::Structure::canCacheOwnKeys const):
+        * runtime/StructureRareData.cpp:
+        (JSC::StructureRareData::visitChildren):
+        (JSC::StructureRareData::cachedPropertyNameEnumerator const): Deleted.
+        (JSC::StructureRareData::setCachedPropertyNameEnumerator): Deleted.
+        * runtime/StructureRareData.h:
+        * runtime/StructureRareDataInlines.h:
+        (JSC::StructureRareData::cachedPropertyNameEnumerator const):
+        (JSC::StructureRareData::setCachedPropertyNameEnumerator):
+        (JSC::StructureRareData::cachedOwnKeys const):
+        (JSC::StructureRareData::cachedOwnKeysIgnoringSentinel const):
+        (JSC::StructureRareData::cachedOwnKeysConcurrently const):
+        (JSC::StructureRareData::setCachedOwnKeys):
+        (JSC::StructureRareData::previousID const): Deleted.
+        * runtime/VM.cpp:
+        (JSC::VM::VM):
+
 2018-12-17  Mark Lam  <[email protected]>
 
         SamplingProfiler's isValidFramePointer() should reject address at stack origin.

Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (239323 => 239324)


--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2018-12-18 06:54:49 UTC (rev 239324)
@@ -43,6 +43,7 @@
 #include "Operations.h"
 #include "PutByIdStatus.h"
 #include "StringObject.h"
+#include "StructureRareDataInlines.h"
 #include <wtf/BooleanLattice.h>
 #include <wtf/CheckedArithmetic.h>
 
@@ -2580,6 +2581,29 @@
         break;
     }
 
+    case ObjectKeys: {
+        if (node->child1().useKind() == ObjectUse) {
+            auto& structureSet = forNode(node->child1()).m_structure;
+            if (structureSet.isFinite() && structureSet.size() == 1) {
+                RegisteredStructure structure = structureSet.onlyStructure();
+                if (auto* rareData = structure->rareDataConcurrently()) {
+                    if (auto* immutableButterfly = rareData->cachedOwnKeysConcurrently()) {
+                        if (m_graph.isWatchingHavingABadTimeWatchpoint(node)) {
+                            m_state.setFoundConstants(true);
+                            didFoldClobberWorld();
+                            setTypeForNode(node, SpecArray);
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        clobberWorld();
+        setTypeForNode(node, SpecArray);
+        break;
+    }
+
     case ToObject:
     case CallObjectConstructor: {
         AbstractValue& source = forNode(node->child1());

Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (239323 => 239324)


--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2018-12-18 06:54:49 UTC (rev 239324)
@@ -2692,6 +2692,15 @@
         return true;
     }
 
+    case ObjectKeysIntrinsic: {
+        if (argumentCountIncludingThis < 2)
+            return false;
+
+        insertChecks();
+        set(result, addToGraph(ObjectKeys, get(virtualRegisterForArgument(1, registerOffset))));
+        return true;
+    }
+
     case ReflectGetPrototypeOfIntrinsic: {
         if (argumentCountIncludingThis != 2)
             return false;

Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (239323 => 239324)


--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2018-12-18 06:54:49 UTC (rev 239324)
@@ -668,6 +668,7 @@
     case CreateThis:
     case InstanceOf:
     case StringValueOf:
+    case ObjectKeys:
         read(World);
         write(Heap);
         return;
@@ -1529,7 +1530,6 @@
         }
     }
 
-
     case NewObject:
     case NewRegexp:
     case NewSymbol:

Modified: trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp (239323 => 239324)


--- trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp	2018-12-18 06:54:49 UTC (rev 239324)
@@ -766,6 +766,25 @@
                 break;
             }
 
+            case ObjectKeys: {
+                if (node->child1().useKind() == ObjectUse) {
+                    auto& structureSet = m_state.forNode(node->child1()).m_structure;
+                    if (structureSet.isFinite() && structureSet.size() == 1) {
+                        RegisteredStructure structure = structureSet.onlyStructure();
+                        if (auto* rareData = structure->rareDataConcurrently()) {
+                            if (auto* immutableButterfly = rareData->cachedOwnKeysConcurrently()) {
+                                if (m_graph.isWatchingHavingABadTimeWatchpoint(node)) {
+                                    node->convertToNewArrayBuffer(m_graph.freeze(immutableButterfly));
+                                    changed = true;
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                }
+                break;
+            }
+
             case ToNumber: {
                 if (m_state.forNode(node->child1()).m_type & ~SpecBytecodeNumber)
                     break;

Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (239323 => 239324)


--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp	2018-12-18 06:54:49 UTC (rev 239324)
@@ -339,6 +339,7 @@
     case ToThis:
     case CreateThis:
     case ObjectCreate:
+    case ObjectKeys:
     case AllocatePropertyStorage:
     case ReallocatePropertyStorage:
     case Arrayify:

Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (239323 => 239324)


--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2018-12-18 06:54:49 UTC (rev 239324)
@@ -1595,6 +1595,14 @@
             break;
         }
 
+        case ObjectKeys: {
+            if (node->child1()->shouldSpeculateObject()) {
+                watchHavingABadTime(node);
+                fixEdge<ObjectUse>(node->child1());
+            }
+            break;
+        }
+
         case CheckStringIdent: {
             fixEdge<StringIdentUse>(node->child1());
             break;

Modified: trunk/Source/_javascript_Core/dfg/DFGNode.cpp (239323 => 239324)


--- trunk/Source/_javascript_Core/dfg/DFGNode.cpp	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.cpp	2018-12-18 06:54:49 UTC (rev 239324)
@@ -31,6 +31,7 @@
 #include "DFGGraph.h"
 #include "DFGPromotedHeapLocation.h"
 #include "JSCInlines.h"
+#include "JSImmutableButterfly.h"
 
 namespace JSC { namespace DFG {
 
@@ -223,6 +224,17 @@
     children.reset();
 }
 
+void Node::convertToNewArrayBuffer(FrozenValue* immutableButterfly)
+{
+    setOpAndDefaultFlags(NewArrayBuffer);
+    NewArrayBufferData data { };
+    data.indexingMode = immutableButterfly->cast<JSImmutableButterfly*>()->indexingMode();
+    data.vectorLengthHint = immutableButterfly->cast<JSImmutableButterfly*>()->toButterfly()->vectorLength();
+    children.reset();
+    m_opInfo = immutableButterfly;
+    m_opInfo2 = data.asQuadWord;
+}
+
 void Node::convertToDirectCall(FrozenValue* executable)
 {
     NodeType newOp = LastNodeType;

Modified: trunk/Source/_javascript_Core/dfg/DFGNode.h (239323 => 239324)


--- trunk/Source/_javascript_Core/dfg/DFGNode.h	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.h	2018-12-18 06:54:49 UTC (rev 239324)
@@ -761,6 +761,8 @@
         m_opInfo = structure;
         m_opInfo2 = OpInfoWrapper();
     }
+
+    void convertToNewArrayBuffer(FrozenValue* immutableButterfly);
     
     void convertToDirectCall(FrozenValue*);
 

Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (239323 => 239324)


--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2018-12-18 06:54:49 UTC (rev 239324)
@@ -265,6 +265,7 @@
     macro(ParseInt, NodeMustGenerate | NodeResultJS) \
     macro(GetPrototypeOf, NodeMustGenerate | NodeResultJS) \
     macro(ObjectCreate, NodeMustGenerate | NodeResultJS) \
+    macro(ObjectKeys, NodeMustGenerate | NodeResultJS) \
     \
     /* Atomics object functions. */\
     macro(AtomicsAdd, NodeResultJS | NodeMustGenerate | NodeHasVarArgs) \

Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.cpp (239323 => 239324)


--- trunk/Source/_javascript_Core/dfg/DFGOperations.cpp	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.cpp	2018-12-18 06:54:49 UTC (rev 239324)
@@ -248,6 +248,25 @@
     return JSValue::encode(JSValue::decode(encodedOp).toThis(exec, StrictMode));
 }
 
+JSArray* JIT_OPERATION operationObjectKeys(ExecState* exec, EncodedJSValue encodedObject)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    JSObject* object = JSValue::decode(encodedObject).toObject(exec);
+    RETURN_IF_EXCEPTION(scope, nullptr);
+    scope.release();
+    return ownPropertyKeys(exec, object, PropertyNameMode::Strings, DontEnumPropertiesMode::Exclude);
+}
+
+JSArray* JIT_OPERATION operationObjectKeysObject(ExecState* exec, JSObject* object)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    return ownPropertyKeys(exec, object, PropertyNameMode::Strings, DontEnumPropertiesMode::Exclude);
+}
+
 JSCell* JIT_OPERATION operationObjectCreate(ExecState* exec, EncodedJSValue encodedPrototype)
 {
     VM& vm = exec->vm();

Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.h (239323 => 239324)


--- trunk/Source/_javascript_Core/dfg/DFGOperations.h	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.h	2018-12-18 06:54:49 UTC (rev 239324)
@@ -43,6 +43,8 @@
 // These routines provide callbacks out to C++ implementations of operations too complex to JIT.
 JSCell* JIT_OPERATION operationCallObjectConstructor(ExecState*, JSGlobalObject*, EncodedJSValue encodedTarget) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationToObject(ExecState*, JSGlobalObject*, EncodedJSValue encodedTarget, UniquedStringImpl*) WTF_INTERNAL;
+JSArray* JIT_OPERATION operationObjectKeys(ExecState*, EncodedJSValue) WTF_INTERNAL;
+JSArray* JIT_OPERATION operationObjectKeysObject(ExecState*, JSObject*) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationObjectCreate(ExecState*, EncodedJSValue) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationObjectCreateObject(ExecState*, JSObject*) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationCreateThis(ExecState*, JSObject* constructor, uint32_t inlineCapacity) WTF_INTERNAL;

Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (239323 => 239324)


--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2018-12-18 06:54:49 UTC (rev 239324)
@@ -984,7 +984,8 @@
         case NewArray:
         case NewArrayWithSize:
         case CreateRest:
-        case NewArrayBuffer: {
+        case NewArrayBuffer:
+        case ObjectKeys: {
             setPrediction(SpecArray);
             break;
         }

Modified: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (239323 => 239324)


--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h	2018-12-18 06:54:49 UTC (rev 239324)
@@ -178,6 +178,7 @@
     case ToThis:
     case CreateThis:
     case ObjectCreate:
+    case ObjectKeys:
     case GetCallee:
     case SetCallee:
     case GetArgumentCountIncludingThis:

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (239323 => 239324)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2018-12-18 06:54:49 UTC (rev 239324)
@@ -12365,6 +12365,94 @@
     jsValueResult(tempRegs, node);
 }
 
+void SpeculativeJIT::compileObjectKeys(Node* node)
+{
+    switch (node->child1().useKind()) {
+    case ObjectUse: {
+        if (m_graph.isWatchingHavingABadTimeWatchpoint(node)) {
+            SpeculateCellOperand object(this, node->child1());
+            GPRTemporary structure(this);
+            GPRTemporary scratch(this);
+            GPRTemporary scratch2(this);
+            GPRTemporary scratch3(this);
+            GPRTemporary result(this);
+
+            GPRReg objectGPR = object.gpr();
+            GPRReg structureGPR = structure.gpr();
+            GPRReg scratchGPR = scratch.gpr();
+            GPRReg scratch2GPR = scratch2.gpr();
+            GPRReg scratch3GPR = scratch3.gpr();
+            GPRReg resultGPR = result.gpr();
+
+            speculateObject(node->child1(), objectGPR);
+
+            CCallHelpers::JumpList slowCases;
+            m_jit.emitLoadStructure(*m_jit.vm(), objectGPR, structureGPR, scratchGPR);
+            m_jit.loadPtr(CCallHelpers::Address(structureGPR, Structure::previousOrRareDataOffset()), scratchGPR);
+
+            slowCases.append(m_jit.branchTestPtr(CCallHelpers::Zero, scratchGPR));
+            slowCases.append(m_jit.branch32(CCallHelpers::Equal, CCallHelpers::Address(scratchGPR, JSCell::structureIDOffset()), TrustedImm32(bitwise_cast<int32_t>(m_jit.vm()->structureStructure->structureID()))));
+
+            m_jit.loadPtr(CCallHelpers::Address(scratchGPR, StructureRareData::offsetOfCachedOwnKeys()), scratchGPR);
+
+            ASSERT(bitwise_cast<uintptr_t>(StructureRareData::cachedOwnKeysSentinel()) == 1);
+            slowCases.append(m_jit.branchPtr(CCallHelpers::BelowOrEqual, scratchGPR, TrustedImmPtr(bitwise_cast<void*>(StructureRareData::cachedOwnKeysSentinel()))));
+
+            MacroAssembler::JumpList slowButArrayBufferCases;
+
+            JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic);
+            RegisteredStructure arrayStructure = m_jit.graph().registerStructure(globalObject->arrayStructureForIndexingTypeDuringAllocation(CopyOnWriteArrayWithContiguous));
+
+            m_jit.move(scratchGPR, scratch3GPR);
+            m_jit.addPtr(TrustedImmPtr(JSImmutableButterfly::offsetOfData()), scratchGPR);
+
+            emitAllocateJSObject<JSArray>(resultGPR, TrustedImmPtr(arrayStructure), scratchGPR, structureGPR, scratch2GPR, slowButArrayBufferCases);
+
+            addSlowPathGenerator(slowPathCall(slowButArrayBufferCases, this, operationNewArrayBuffer, resultGPR, arrayStructure, scratch3GPR));
+
+            addSlowPathGenerator(slowPathCall(slowCases, this, operationObjectKeysObject, resultGPR, objectGPR));
+
+            cellResult(resultGPR, node);
+            break;
+        }
+
+        SpeculateCellOperand object(this, node->child1());
+
+        GPRReg objectGPR = object.gpr();
+
+        speculateObject(node->child1(), objectGPR);
+
+        flushRegisters();
+        GPRFlushedCallResult result(this);
+        GPRReg resultGPR = result.gpr();
+        callOperation(operationObjectKeysObject, resultGPR, objectGPR);
+        m_jit.exceptionCheck();
+
+        cellResult(resultGPR, node);
+        break;
+    }
+
+    case UntypedUse: {
+        JSValueOperand object(this, node->child1());
+
+        JSValueRegs objectRegs = object.jsValueRegs();
+
+        flushRegisters();
+        GPRFlushedCallResult result(this);
+        GPRReg resultGPR = result.gpr();
+        callOperation(operationObjectKeys, resultGPR, objectRegs);
+        m_jit.exceptionCheck();
+
+        cellResult(resultGPR, node);
+        break;
+    }
+
+    default:
+        RELEASE_ASSERT_NOT_REACHED();
+        break;
+    }
+}
+
 void SpeculativeJIT::compileObjectCreate(Node* node)
 {
     switch (node->child1().useKind()) {

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h (239323 => 239324)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2018-12-18 06:54:49 UTC (rev 239324)
@@ -1479,6 +1479,7 @@
     void compileNewArrayWithSize(Node*);
     void compileNewTypedArray(Node*);
     void compileToThis(Node*);
+    void compileObjectKeys(Node*);
     void compileObjectCreate(Node*);
     void compileCreateThis(Node*);
     void compileNewObject(Node*);

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (239323 => 239324)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2018-12-18 06:54:49 UTC (rev 239324)
@@ -3167,6 +3167,11 @@
         break;
     }
 
+    case ObjectKeys: {
+        compileObjectKeys(node);
+        break;
+    }
+
     case CreateThis: {
         compileCreateThis(node);
         break;

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (239323 => 239324)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2018-12-18 06:54:49 UTC (rev 239324)
@@ -3409,6 +3409,11 @@
         break;
     }
 
+    case ObjectKeys: {
+        compileObjectKeys(node);
+        break;
+    }
+
     case CreateThis: {
         compileCreateThis(node);
         break;

Modified: trunk/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.h (239323 => 239324)


--- trunk/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.h	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.h	2018-12-18 06:54:49 UTC (rev 239324)
@@ -116,8 +116,10 @@
     macro(Structure_globalObject, Structure::globalObjectOffset()) \
     macro(Structure_indexingModeIncludingHistory, Structure::indexingModeIncludingHistoryOffset()) \
     macro(Structure_inlineCapacity, Structure::inlineCapacityOffset()) \
+    macro(Structure_previousOrRareData, Structure::previousOrRareDataOffset()) \
     macro(Structure_prototype, Structure::prototypeOffset()) \
     macro(Structure_structureID, Structure::structureIDOffset()) \
+    macro(StructureRareData_cachedOwnKeys, StructureRareData::offsetOfCachedOwnKeys()) \
     macro(HashMapImpl_capacity, HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfCapacity()) \
     macro(HashMapImpl_buffer,  HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfBuffer()) \
     macro(HashMapImpl_head,  HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfHead()) \

Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (239323 => 239324)


--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2018-12-18 06:54:49 UTC (rev 239324)
@@ -201,6 +201,7 @@
     case CallObjectConstructor:
     case CallStringConstructor:
     case ObjectCreate:
+    case ObjectKeys:
     case MakeRope:
     case NewArrayWithSize:
     case TryGetById:

Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (239323 => 239324)


--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2018-12-18 06:54:49 UTC (rev 239324)
@@ -866,6 +866,9 @@
         case ObjectCreate:
             compileObjectCreate();
             break;
+        case ObjectKeys:
+            compileObjectKeys();
+            break;
         case NewObject:
             compileNewObject();
             break;
@@ -5497,6 +5500,72 @@
         setInt32(m_out.phi(Int32, zeroLengthResult, nonZeroLengthResult));
     }
 
+    void compileObjectKeys()
+    {
+        switch (m_node->child1().useKind()) {
+        case ObjectUse: {
+            if (m_graph.isWatchingHavingABadTimeWatchpoint(m_node)) {
+                LBasicBlock notNullCase = m_out.newBlock();
+                LBasicBlock rareDataCase = m_out.newBlock();
+                LBasicBlock useCacheCase = m_out.newBlock();
+                LBasicBlock slowButArrayBufferCase = m_out.newBlock();
+                LBasicBlock slowCase = m_out.newBlock();
+                LBasicBlock continuation = m_out.newBlock();
+
+                LValue object = lowObject(m_node->child1());
+                LValue structure = loadStructure(object);
+                LValue previousOrRareData = m_out.loadPtr(structure, m_heaps.Structure_previousOrRareData);
+                m_out.branch(m_out.notNull(previousOrRareData), unsure(notNullCase), unsure(slowCase));
+
+                LBasicBlock lastNext = m_out.appendTo(notNullCase, rareDataCase);
+                m_out.branch(
+                    m_out.notEqual(m_out.load32(previousOrRareData, m_heaps.JSCell_structureID), m_out.constInt32(m_graph.m_vm.structureStructure->structureID())),
+                    unsure(rareDataCase), unsure(slowCase));
+
+                m_out.appendTo(rareDataCase, useCacheCase);
+                ASSERT(bitwise_cast<uintptr_t>(StructureRareData::cachedOwnKeysSentinel()) == 1);
+                LValue cachedOwnKeys = m_out.loadPtr(previousOrRareData, m_heaps.StructureRareData_cachedOwnKeys);
+                m_out.branch(m_out.belowOrEqual(cachedOwnKeys, m_out.constIntPtr(bitwise_cast<void*>(StructureRareData::cachedOwnKeysSentinel()))), unsure(slowCase), unsure(useCacheCase));
+
+                m_out.appendTo(useCacheCase, slowButArrayBufferCase);
+                JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
+                RegisteredStructure arrayStructure = m_graph.registerStructure(globalObject->arrayStructureForIndexingTypeDuringAllocation(CopyOnWriteArrayWithContiguous));
+                LValue fastArray = allocateObject<JSArray>(arrayStructure, m_out.addPtr(cachedOwnKeys, JSImmutableButterfly::offsetOfData()), slowButArrayBufferCase);
+                ValueFromBlock fastResult = m_out.anchor(fastArray);
+                m_out.jump(continuation);
+
+                m_out.appendTo(slowButArrayBufferCase, slowCase);
+                LValue slowArray = vmCall(Int64, m_out.operation(operationNewArrayBuffer), m_callFrame, weakStructure(arrayStructure), cachedOwnKeys);
+                ValueFromBlock slowButArrayBufferResult = m_out.anchor(slowArray);
+                m_out.jump(continuation);
+
+                m_out.appendTo(slowCase, continuation);
+                VM& vm = this->vm();
+                LValue slowResultValue = lazySlowPath(
+                    [=, &vm] (const Vector<Location>& locations) -> RefPtr<LazySlowPath::Generator> {
+                        return createLazyCallGenerator(vm,
+                            operationObjectKeysObject, locations[0].directGPR(), locations[1].directGPR());
+                    },
+                    object);
+                ValueFromBlock slowResult = m_out.anchor(slowResultValue);
+                m_out.jump(continuation);
+
+                m_out.appendTo(continuation, lastNext);
+                setJSValue(m_out.phi(pointerType(), fastResult, slowButArrayBufferResult, slowResult));
+                break;
+            }
+            setJSValue(vmCall(Int64, m_out.operation(operationObjectKeysObject), m_callFrame, lowObject(m_node->child1())));
+            break;
+        }
+        case UntypedUse:
+            setJSValue(vmCall(Int64, m_out.operation(operationObjectKeys), m_callFrame, lowJSValue(m_node->child1())));
+            break;
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+            break;
+        }
+    }
+
     void compileObjectCreate()
     {
         switch (m_node->child1().useKind()) {

Modified: trunk/Source/_javascript_Core/runtime/Butterfly.h (239323 => 239324)


--- trunk/Source/_javascript_Core/runtime/Butterfly.h	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/runtime/Butterfly.h	2018-12-18 06:54:49 UTC (rev 239324)
@@ -85,6 +85,11 @@
             m_data.setWithoutWriteBarrier(value);
         }
 
+        void setStartingValue(JSValue value)
+        {
+            m_data.setStartingValue(value);
+        }
+
         void clear()
         {
             ASSERT(m_isWritable);

Modified: trunk/Source/_javascript_Core/runtime/Intrinsic.cpp (239323 => 239324)


--- trunk/Source/_javascript_Core/runtime/Intrinsic.cpp	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/runtime/Intrinsic.cpp	2018-12-18 06:54:49 UTC (rev 239324)
@@ -119,6 +119,8 @@
         return "ObjectGetPrototypeOfIntrinsic";
     case ObjectIsIntrinsic:
         return "ObjectIsIntrinsic";
+    case ObjectKeysIntrinsic:
+        return "ObjectKeysIntrinsic";
     case ReflectGetPrototypeOfIntrinsic:
         return "ReflectGetPrototypeOfIntrinsic";
     case StringPrototypeValueOfIntrinsic:

Modified: trunk/Source/_javascript_Core/runtime/Intrinsic.h (239323 => 239324)


--- trunk/Source/_javascript_Core/runtime/Intrinsic.h	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/runtime/Intrinsic.h	2018-12-18 06:54:49 UTC (rev 239324)
@@ -72,6 +72,7 @@
     ObjectCreateIntrinsic,
     ObjectGetPrototypeOfIntrinsic,
     ObjectIsIntrinsic,
+    ObjectKeysIntrinsic,
     ReflectGetPrototypeOfIntrinsic,
     StringPrototypeValueOfIntrinsic,
     StringPrototypeReplaceIntrinsic,

Modified: trunk/Source/_javascript_Core/runtime/JSImmutableButterfly.h (239323 => 239324)


--- trunk/Source/_javascript_Core/runtime/JSImmutableButterfly.h	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/runtime/JSImmutableButterfly.h	2018-12-18 06:54:49 UTC (rev 239324)
@@ -120,6 +120,10 @@
     {
         m_header.setVectorLength(length);
         m_header.setPublicLength(length);
+        if (hasContiguous(indexingType())) {
+            for (unsigned index = 0; index < length; ++index)
+                toButterfly()->contiguous().at(this, index).setStartingValue(JSValue());
+        }
     }
 
     IndexingHeader m_header;

Modified: trunk/Source/_javascript_Core/runtime/ObjectConstructor.cpp (239323 => 239324)


--- trunk/Source/_javascript_Core/runtime/ObjectConstructor.cpp	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/runtime/ObjectConstructor.cpp	2018-12-18 06:54:49 UTC (rev 239324)
@@ -30,6 +30,7 @@
 #include "JSFunction.h"
 #include "JSGlobalObject.h"
 #include "JSGlobalObjectFunctions.h"
+#include "JSImmutableButterfly.h"
 #include "Lookup.h"
 #include "ObjectPrototype.h"
 #include "PropertyDescriptor.h"
@@ -73,7 +74,7 @@
   getOwnPropertyDescriptors objectConstructorGetOwnPropertyDescriptors  DontEnum|Function 1
   getOwnPropertyNames       objectConstructorGetOwnPropertyNames        DontEnum|Function 1
   getOwnPropertySymbols     objectConstructorGetOwnPropertySymbols      DontEnum|Function 1
-  keys                      objectConstructorKeys                       DontEnum|Function 1
+  keys                      objectConstructorKeys                       DontEnum|Function 1 ObjectKeysIntrinsic
   defineProperty            objectConstructorDefineProperty             DontEnum|Function 3
   defineProperties          objectConstructorDefineProperties           DontEnum|Function 2
   create                    objectConstructorCreate                     DontEnum|Function 2 ObjectCreateIntrinsic
@@ -271,7 +272,6 @@
     RELEASE_AND_RETURN(scope, JSValue::encode(ownPropertyKeys(exec, object, PropertyNameMode::Symbols, DontEnumPropertiesMode::Include)));
 }
 
-// FIXME: Use the enumeration cache.
 EncodedJSValue JSC_HOST_CALL objectConstructorKeys(ExecState* exec)
 {
     VM& vm = exec->vm();
@@ -892,11 +892,23 @@
     return JSValue::encode(jsBoolean(sameValue(exec, exec->argument(0), exec->argument(1))));
 }
 
-// FIXME: Use the enumeration cache.
 JSArray* ownPropertyKeys(ExecState* exec, JSObject* object, PropertyNameMode propertyNameMode, DontEnumPropertiesMode dontEnumPropertiesMode)
 {
     VM& vm = exec->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
+
+    auto* globalObject = exec->lexicalGlobalObject();
+    bool isObjectKeys = propertyNameMode == PropertyNameMode::Strings && dontEnumPropertiesMode == DontEnumPropertiesMode::Exclude;
+    // We attempt to look up own property keys cache in Object.keys case.
+    if (isObjectKeys) {
+        if (LIKELY(!globalObject->isHavingABadTime())) {
+            if (auto* immutableButterfly = object->structure(vm)->cachedOwnKeys()) {
+                Structure* arrayStructure = globalObject->originalArrayStructureForIndexingType(immutableButterfly->indexingMode());
+                return JSArray::createWithButterfly(vm, nullptr, arrayStructure, immutableButterfly->toButterfly());
+            }
+        }
+    }
+
     PropertyNameArray properties(&vm, propertyNameMode, PrivateSymbolMode::Exclude);
     object->methodTable(vm)->getOwnPropertyNames(object, exec, properties, EnumerationMode(dontEnumPropertiesMode));
     RETURN_IF_EXCEPTION(scope, nullptr);
@@ -918,8 +930,30 @@
     if (propertyNameMode != PropertyNameMode::StringsAndSymbols) {
         ASSERT(propertyNameMode == PropertyNameMode::Strings || propertyNameMode == PropertyNameMode::Symbols);
         if (!mustFilterProperty && properties.size() < MIN_SPARSE_ARRAY_INDEX) {
-            auto* globalObject = exec->lexicalGlobalObject();
             if (LIKELY(!globalObject->isHavingABadTime())) {
+                if (isObjectKeys) {
+                    Structure* structure = object->structure(vm);
+                    if (structure->canCacheOwnKeys()) {
+                        auto* cachedButterfly = structure->cachedOwnKeysIgnoringSentinel();
+                        if (cachedButterfly == StructureRareData::cachedOwnKeysSentinel()) {
+                            size_t numProperties = properties.size();
+                            auto* newButterfly = JSImmutableButterfly::create(vm, CopyOnWriteArrayWithContiguous, numProperties);
+                            for (size_t i = 0; i < numProperties; i++) {
+                                const auto& identifier = properties[i];
+                                ASSERT(!identifier.isSymbol());
+                                newButterfly->setIndex(vm, i, jsOwnedString(&vm, identifier.string()));
+                            }
+
+                            structure->setCachedOwnKeys(vm, newButterfly);
+                            Structure* arrayStructure = globalObject->originalArrayStructureForIndexingType(newButterfly->indexingMode());
+                            return JSArray::createWithButterfly(vm, nullptr, arrayStructure, newButterfly->toButterfly());
+                        }
+
+                        if (cachedButterfly == nullptr)
+                            structure->setCachedOwnKeys(vm, StructureRareData::cachedOwnKeysSentinel());
+                    }
+                }
+
                 size_t numProperties = properties.size();
                 JSArray* keys = JSArray::create(vm, globalObject->originalArrayStructureForIndexingType(ArrayWithContiguous), numProperties);
                 WriteBarrier<Unknown>* buffer = keys->butterfly()->contiguous().data();

Modified: trunk/Source/_javascript_Core/runtime/Structure.cpp (239323 => 239324)


--- trunk/Source/_javascript_Core/runtime/Structure.cpp	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/runtime/Structure.cpp	2018-12-18 06:54:49 UTC (rev 239324)
@@ -1252,17 +1252,7 @@
 
 bool Structure::canCachePropertyNameEnumerator() const
 {
-    auto canCache = [] (const Structure* structure) {
-        if (structure->isDictionary())
-            return false;
-        if (hasIndexedProperties(structure->indexingType()))
-            return false;
-        if (structure->typeInfo().overridesGetPropertyNames())
-            return false;
-        return true;
-    };
-
-    if (!canCache(this))
+    if (!this->canCacheOwnKeys())
         return false;
 
     StructureChain* structureChain = m_cachedPrototypeChain.get();
@@ -1271,7 +1261,7 @@
     while (true) {
         if (!structure->get())
             return true;
-        if (!canCache(structure->get()))
+        if (!structure->get()->canCacheOwnKeys())
             return false;
         structure++;
     }

Modified: trunk/Source/_javascript_Core/runtime/Structure.h (239323 => 239324)


--- trunk/Source/_javascript_Core/runtime/Structure.h	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/runtime/Structure.h	2018-12-18 06:54:49 UTC (rev 239324)
@@ -38,7 +38,6 @@
 #include "PutPropertySlot.h"
 #include "StructureIDBlob.h"
 #include "StructureRareData.h"
-#include "StructureRareDataInlines.h"
 #include "StructureTransitionTable.h"
 #include "JSTypeInfo.h"
 #include "Watchpoint.h"
@@ -326,6 +325,14 @@
         return static_cast<const StructureRareData*>(m_previousOrRareData.get());
     }
 
+    const StructureRareData* rareDataConcurrently() const
+    {
+        JSCell* cell = m_previousOrRareData.get();
+        if (isRareData(cell))
+            return static_cast<StructureRareData*>(cell);
+        return nullptr;
+    }
+
     StructureRareData* ensureRareData(VM& vm)
     {
         if (!hasRareData())
@@ -472,6 +479,11 @@
     bool canCachePropertyNameEnumerator() const;
     bool canAccessPropertiesQuicklyForEnumeration() const;
 
+    void setCachedOwnKeys(VM&, JSImmutableButterfly*);
+    JSImmutableButterfly* cachedOwnKeys() const;
+    JSImmutableButterfly* cachedOwnKeysIgnoringSentinel() const;
+    bool canCacheOwnKeys() const;
+
     void getPropertyNamesFromStructure(VM&, PropertyNameArray&, EnumerationMode);
 
     JSString* objectToStringValue()
@@ -520,6 +532,11 @@
         return OBJECT_OFFSETOF(Structure, m_inlineCapacity);
     }
 
+    static ptrdiff_t previousOrRareDataOffset()
+    {
+        return OBJECT_OFFSETOF(Structure, m_previousOrRareData);
+    }
+
     static Structure* createStructure(VM&);
         
     bool transitionWatchpointSetHasBeenInvalidated() const

Modified: trunk/Source/_javascript_Core/runtime/StructureInlines.h (239323 => 239324)


--- trunk/Source/_javascript_Core/runtime/StructureInlines.h	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/runtime/StructureInlines.h	2018-12-18 06:54:49 UTC (rev 239324)
@@ -219,6 +219,36 @@
     return false;
 }
 
+inline void Structure::setCachedOwnKeys(VM& vm, JSImmutableButterfly* ownKeys)
+{
+    ensureRareData(vm)->setCachedOwnKeys(vm, ownKeys);
+}
+
+inline JSImmutableButterfly* Structure::cachedOwnKeys() const
+{
+    if (!hasRareData())
+        return nullptr;
+    return rareData()->cachedOwnKeys();
+}
+
+inline JSImmutableButterfly* Structure::cachedOwnKeysIgnoringSentinel() const
+{
+    if (!hasRareData())
+        return nullptr;
+    return rareData()->cachedOwnKeysIgnoringSentinel();
+}
+
+inline bool Structure::canCacheOwnKeys() const
+{
+    if (isDictionary())
+        return false;
+    if (hasIndexedProperties(indexingType()))
+        return false;
+    if (typeInfo().overridesGetPropertyNames())
+        return false;
+    return true;
+}
+
 ALWAYS_INLINE JSValue prototypeForLookupPrimitiveImpl(JSGlobalObject* globalObject, const Structure* structure)
 {
     ASSERT(!structure->isObject());

Modified: trunk/Source/_javascript_Core/runtime/StructureRareData.cpp (239323 => 239324)


--- trunk/Source/_javascript_Core/runtime/StructureRareData.cpp	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/runtime/StructureRareData.cpp	2018-12-18 06:54:49 UTC (rev 239324)
@@ -27,6 +27,7 @@
 #include "StructureRareData.h"
 
 #include "AdaptiveInferredPropertyValueWatchpointBase.h"
+#include "JSImmutableButterfly.h"
 #include "JSPropertyNameEnumerator.h"
 #include "JSString.h"
 #include "JSCInlines.h"
@@ -70,18 +71,10 @@
     visitor.append(thisObject->m_previous);
     visitor.append(thisObject->m_objectToStringValue);
     visitor.append(thisObject->m_cachedPropertyNameEnumerator);
+    if (thisObject->m_cachedOwnKeys.unvalidatedGet() != cachedOwnKeysSentinel())
+        visitor.append(thisObject->m_cachedOwnKeys);
 }
 
-JSPropertyNameEnumerator* StructureRareData::cachedPropertyNameEnumerator() const
-{
-    return m_cachedPropertyNameEnumerator.get();
-}
-
-void StructureRareData::setCachedPropertyNameEnumerator(VM& vm, JSPropertyNameEnumerator* enumerator)
-{
-    m_cachedPropertyNameEnumerator.set(vm, this, enumerator);
-}
-
 // ----------- Object.prototype.toString() helper watchpoint classes -----------
 
 class ObjectToStringAdaptiveInferredPropertyValueWatchpoint : public AdaptiveInferredPropertyValueWatchpointBase {

Modified: trunk/Source/_javascript_Core/runtime/StructureRareData.h (239323 => 239324)


--- trunk/Source/_javascript_Core/runtime/StructureRareData.h	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/runtime/StructureRareData.h	2018-12-18 06:54:49 UTC (rev 239324)
@@ -58,7 +58,10 @@
 
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype);
 
-    Structure* previousID() const;
+    Structure* previousID() const
+    {
+        return m_previous.get();
+    }
     void setPreviousID(VM&, Structure*);
     void clearPreviousID();
 
@@ -68,11 +71,23 @@
     JSPropertyNameEnumerator* cachedPropertyNameEnumerator() const;
     void setCachedPropertyNameEnumerator(VM&, JSPropertyNameEnumerator*);
 
+    JSImmutableButterfly* cachedOwnKeys() const;
+    JSImmutableButterfly* cachedOwnKeysIgnoringSentinel() const;
+    JSImmutableButterfly* cachedOwnKeysConcurrently() const;
+    void setCachedOwnKeys(VM&, JSImmutableButterfly*);
+
     Box<InlineWatchpointSet> copySharedPolyProtoWatchpoint() const { return m_polyProtoWatchpoint; }
     const Box<InlineWatchpointSet>& sharedPolyProtoWatchpoint() const { return m_polyProtoWatchpoint; }
     void setSharedPolyProtoWatchpoint(Box<InlineWatchpointSet>&& sharedPolyProtoWatchpoint) { m_polyProtoWatchpoint = WTFMove(sharedPolyProtoWatchpoint); }
     bool hasSharedPolyProtoWatchpoint() const { return static_cast<bool>(m_polyProtoWatchpoint); }
 
+    static JSImmutableButterfly* cachedOwnKeysSentinel() { return bitwise_cast<JSImmutableButterfly*>(static_cast<uintptr_t>(1)); }
+
+    static ptrdiff_t offsetOfCachedOwnKeys()
+    {
+        return OBJECT_OFFSETOF(StructureRareData, m_cachedOwnKeys);
+    }
+
     DECLARE_EXPORT_INFO;
 
 private:
@@ -86,7 +101,10 @@
 
     WriteBarrier<Structure> m_previous;
     WriteBarrier<JSString> m_objectToStringValue;
+    // FIXME: We should have some story for clearing these property names caches in GC.
+    // https://bugs.webkit.org/show_bug.cgi?id=192659
     WriteBarrier<JSPropertyNameEnumerator> m_cachedPropertyNameEnumerator;
+    WriteBarrier<JSImmutableButterfly> m_cachedOwnKeys;
 
     typedef HashMap<PropertyOffset, RefPtr<WatchpointSet>, WTF::IntHash<PropertyOffset>, WTF::UnsignedWithZeroKeyHashTraits<PropertyOffset>> PropertyWatchpointMap;
     std::unique_ptr<PropertyWatchpointMap> m_replacementWatchpointSets;

Modified: trunk/Source/_javascript_Core/runtime/StructureRareDataInlines.h (239323 => 239324)


--- trunk/Source/_javascript_Core/runtime/StructureRareDataInlines.h	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/runtime/StructureRareDataInlines.h	2018-12-18 06:54:49 UTC (rev 239324)
@@ -25,16 +25,13 @@
 
 #pragma once
 
+#include "JSImmutableButterfly.h"
+#include "JSPropertyNameEnumerator.h"
 #include "JSString.h"
 #include "StructureRareData.h"
 
 namespace JSC {
 
-inline Structure* StructureRareData::previousID() const
-{
-    return m_previous.get();
-}
-
 inline void StructureRareData::setPreviousID(VM& vm, Structure* structure)
 {
     m_previous.set(vm, this, structure);
@@ -50,4 +47,48 @@
     return m_objectToStringValue.get();
 }
 
+inline JSPropertyNameEnumerator* StructureRareData::cachedPropertyNameEnumerator() const
+{
+    return m_cachedPropertyNameEnumerator.get();
+}
+
+inline void StructureRareData::setCachedPropertyNameEnumerator(VM& vm, JSPropertyNameEnumerator* enumerator)
+{
+    m_cachedPropertyNameEnumerator.set(vm, this, enumerator);
+}
+
+inline JSImmutableButterfly* StructureRareData::cachedOwnKeys() const
+{
+    ASSERT(!isCompilationThread());
+    auto* butterfly = m_cachedOwnKeys.unvalidatedGet();
+    if (butterfly == cachedOwnKeysSentinel())
+        return nullptr;
+    return butterfly;
+}
+
+inline JSImmutableButterfly* StructureRareData::cachedOwnKeysIgnoringSentinel() const
+{
+    ASSERT(!isCompilationThread());
+    return m_cachedOwnKeys.unvalidatedGet();
+}
+
+inline JSImmutableButterfly* StructureRareData::cachedOwnKeysConcurrently() const
+{
+    auto* butterfly = m_cachedOwnKeys.unvalidatedGet();
+    if (butterfly == cachedOwnKeysSentinel())
+        return nullptr;
+    return butterfly;
+}
+
+inline void StructureRareData::setCachedOwnKeys(VM& vm, JSImmutableButterfly* butterfly)
+{
+    if (butterfly == cachedOwnKeysSentinel()) {
+        m_cachedOwnKeys.setWithoutWriteBarrier(butterfly);
+        return;
+    }
+
+    WTF::storeStoreFence();
+    m_cachedOwnKeys.set(vm, this, butterfly);
+}
+
 } // namespace JSC

Modified: trunk/Source/_javascript_Core/runtime/VM.cpp (239323 => 239324)


--- trunk/Source/_javascript_Core/runtime/VM.cpp	2018-12-18 06:37:28 UTC (rev 239323)
+++ trunk/Source/_javascript_Core/runtime/VM.cpp	2018-12-18 06:54:49 UTC (rev 239324)
@@ -434,6 +434,7 @@
     exceptionStructure.set(*this, Exception::createStructure(*this, 0, jsNull()));
     promiseDeferredStructure.set(*this, JSPromiseDeferred::createStructure(*this, 0, jsNull()));
     internalPromiseDeferredStructure.set(*this, JSInternalPromiseDeferred::createStructure(*this, 0, jsNull()));
+    nativeStdFunctionCellStructure.set(*this, NativeStdFunctionCell::createStructure(*this, 0, jsNull()));
     programCodeBlockStructure.set(*this, ProgramCodeBlock::createStructure(*this, 0, jsNull()));
     moduleProgramCodeBlockStructure.set(*this, ModuleProgramCodeBlock::createStructure(*this, 0, jsNull()));
     evalCodeBlockStructure.set(*this, EvalCodeBlock::createStructure(*this, 0, jsNull()));
@@ -448,7 +449,6 @@
     sentinelSetBucket.set(*this, JSSet::BucketType::createSentinel(*this));
     sentinelMapBucket.set(*this, JSMap::BucketType::createSentinel(*this));
 
-    nativeStdFunctionCellStructure.set(*this, NativeStdFunctionCell::createStructure(*this, 0, jsNull()));
     smallStrings.initializeCommonStrings(*this);
 
     Thread::current().setCurrentAtomicStringTable(existingEntryAtomicStringTable);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to