Title: [282153] branches/safari-612-branch
Revision
282153
Author
repst...@apple.com
Date
2021-09-08 10:22:34 -0700 (Wed, 08 Sep 2021)

Log Message

Cherry-pick r282014. rdar://problem/82877307

    [JSC] Validate JSPropertyNameEnumerator via watchpoints
    https://bugs.webkit.org/show_bug.cgi?id=229846

    Reviewed by Keith Miller.

    JSTests:

    * stress/for-in-cacheable-dictionary.js: Added.
    (shouldBe):
    (collect):
    * stress/for-in-invalidate.js: Added.
    (shouldBe):
    (collect):
    * stress/for-in-uncacheable-dictionary.js: Added.
    (shouldBe):
    (collect):

    Source/_javascript_Core:

    Looked into Elm-TodoMVC sampling profiler data and found that op_get_property_enumerator is taking enough amount of time.
    And Instruments say validating JSPropertyNameEnumerator via traversing StructureChain is costly.
    We are caching JSPropertyNameEnumerator only when we meet the condition: objects in prototype chain can ensure identity of
    property names if structure is not changed. So we can use watchpoint based approach to invalidate JSPropertyNameEnumerator.

    This patch injects structure transition watchpoints if possible. And when watchpoint is fired, we invalidate JSPropertyNameEnumerator
    cached in StructureRareData, as if we are ensuring prototype chain condition for the other property accesses.

    This offers 0.6% improvement in Speedometer2.

    ----------------------------------------------------------------------------------------------------------------------------------
    |               subtest                |     ms      |     ms      |  b / a   | pValue (significance using False Discovery Rate) |
    ----------------------------------------------------------------------------------------------------------------------------------
    | Elm-TodoMVC                          |121.971667   |117.725000   |0.965183  | 0.000000 (significant)                           |
    | VueJS-TodoMVC                        |26.246667    |26.035000    |0.991935  | 0.360614                                         |
    | EmberJS-TodoMVC                      |126.196667   |126.653333   |1.003619  | 0.103138                                         |
    | BackboneJS-TodoMVC                   |48.976667    |48.881667    |0.998060  | 0.474106                                         |
    | Preact-TodoMVC                       |20.118333    |20.115000    |0.999834  | 0.989038                                         |
    | AngularJS-TodoMVC                    |131.545000   |130.706667   |0.993627  | 0.015344 (significant)                           |
    | Vanilla-ES2015-TodoMVC               |63.725000    |63.773333    |1.000758  | 0.706560                                         |
    | Inferno-TodoMVC                      |64.231667    |62.653333    |0.975427  | 0.000000 (significant)                           |
    | Flight-TodoMVC                       |77.223333    |77.690000    |1.006043  | 0.268309                                         |
    | Angular2-TypeScript-TodoMVC          |39.686667    |39.500000    |0.995296  | 0.499678                                         |
    | VanillaJS-TodoMVC                    |52.321667    |51.973333    |0.993342  | 0.077777                                         |
    | jQuery-TodoMVC                       |224.908333   |225.761667   |1.003794  | 0.022136                                         |
    | EmberJS-Debug-TodoMVC                |339.858333   |339.886667   |1.000083  | 0.950320                                         |
    | React-TodoMVC                        |86.545000    |86.070000    |0.994512  | 0.001518 (significant)                           |
    | React-Redux-TodoMVC                  |146.010000   |142.855000   |0.978392  | 0.000000 (significant)                           |
    | Vanilla-ES2015-Babel-Webpack-TodoMVC |61.411667    |61.456667    |1.000733  | 0.631499                                         |
    ----------------------------------------------------------------------------------------------------------------------------------
    a mean = 255.96543
    b mean = 257.53379
    pValue = 0.0000034394
    (Bigger means are better.)
    1.006 times better
    Results ARE significant

    * bytecode/Watchpoint.cpp:
    * bytecode/Watchpoint.h:
    * dfg/DFGSpeculativeJIT.cpp:
    (JSC::DFG::SpeculativeJIT::compileGetPropertyEnumerator):
    * ftl/FTLAbstractHeapRepository.h:
    * ftl/FTLLowerDFGToB3.cpp:
    (JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):
    * runtime/JSPropertyNameEnumerator.h:
    (JSC::propertyNameEnumerator):
    * runtime/StructureRareData.h:
    * runtime/StructureRareDataInlines.h:
    (JSC::StructureRareData::setCachedPropertyNameEnumerator):
    (JSC::StructureChainInvalidationWatchpoint::install):
    (JSC::StructureChainInvalidationWatchpoint::fireInternal):
    (JSC::StructureRareData::tryCachePropertyNameEnumeratorViaWatchpoint):
    (JSC::StructureRareData::invalidateWatchpointBasedValidation):
    * tools/JSDollarVM.cpp:
    (JSC::JSC_DEFINE_HOST_FUNCTION):
    (JSC::JSDollarVM::finishCreation):

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

Modified Paths

Added Paths

Diff

Modified: branches/safari-612-branch/JSTests/ChangeLog (282152 => 282153)


--- branches/safari-612-branch/JSTests/ChangeLog	2021-09-08 17:22:26 UTC (rev 282152)
+++ branches/safari-612-branch/JSTests/ChangeLog	2021-09-08 17:22:34 UTC (rev 282153)
@@ -1,3 +1,102 @@
+2021-09-08  Alan Coon  <alanc...@apple.com>
+
+        Cherry-pick r282014. rdar://problem/82877307
+
+    [JSC] Validate JSPropertyNameEnumerator via watchpoints
+    https://bugs.webkit.org/show_bug.cgi?id=229846
+    
+    Reviewed by Keith Miller.
+    
+    JSTests:
+    
+    * stress/for-in-cacheable-dictionary.js: Added.
+    (shouldBe):
+    (collect):
+    * stress/for-in-invalidate.js: Added.
+    (shouldBe):
+    (collect):
+    * stress/for-in-uncacheable-dictionary.js: Added.
+    (shouldBe):
+    (collect):
+    
+    Source/_javascript_Core:
+    
+    Looked into Elm-TodoMVC sampling profiler data and found that op_get_property_enumerator is taking enough amount of time.
+    And Instruments say validating JSPropertyNameEnumerator via traversing StructureChain is costly.
+    We are caching JSPropertyNameEnumerator only when we meet the condition: objects in prototype chain can ensure identity of
+    property names if structure is not changed. So we can use watchpoint based approach to invalidate JSPropertyNameEnumerator.
+    
+    This patch injects structure transition watchpoints if possible. And when watchpoint is fired, we invalidate JSPropertyNameEnumerator
+    cached in StructureRareData, as if we are ensuring prototype chain condition for the other property accesses.
+    
+    This offers 0.6% improvement in Speedometer2.
+    
+    ----------------------------------------------------------------------------------------------------------------------------------
+    |               subtest                |     ms      |     ms      |  b / a   | pValue (significance using False Discovery Rate) |
+    ----------------------------------------------------------------------------------------------------------------------------------
+    | Elm-TodoMVC                          |121.971667   |117.725000   |0.965183  | 0.000000 (significant)                           |
+    | VueJS-TodoMVC                        |26.246667    |26.035000    |0.991935  | 0.360614                                         |
+    | EmberJS-TodoMVC                      |126.196667   |126.653333   |1.003619  | 0.103138                                         |
+    | BackboneJS-TodoMVC                   |48.976667    |48.881667    |0.998060  | 0.474106                                         |
+    | Preact-TodoMVC                       |20.118333    |20.115000    |0.999834  | 0.989038                                         |
+    | AngularJS-TodoMVC                    |131.545000   |130.706667   |0.993627  | 0.015344 (significant)                           |
+    | Vanilla-ES2015-TodoMVC               |63.725000    |63.773333    |1.000758  | 0.706560                                         |
+    | Inferno-TodoMVC                      |64.231667    |62.653333    |0.975427  | 0.000000 (significant)                           |
+    | Flight-TodoMVC                       |77.223333    |77.690000    |1.006043  | 0.268309                                         |
+    | Angular2-TypeScript-TodoMVC          |39.686667    |39.500000    |0.995296  | 0.499678                                         |
+    | VanillaJS-TodoMVC                    |52.321667    |51.973333    |0.993342  | 0.077777                                         |
+    | jQuery-TodoMVC                       |224.908333   |225.761667   |1.003794  | 0.022136                                         |
+    | EmberJS-Debug-TodoMVC                |339.858333   |339.886667   |1.000083  | 0.950320                                         |
+    | React-TodoMVC                        |86.545000    |86.070000    |0.994512  | 0.001518 (significant)                           |
+    | React-Redux-TodoMVC                  |146.010000   |142.855000   |0.978392  | 0.000000 (significant)                           |
+    | Vanilla-ES2015-Babel-Webpack-TodoMVC |61.411667    |61.456667    |1.000733  | 0.631499                                         |
+    ----------------------------------------------------------------------------------------------------------------------------------
+    a mean = 255.96543
+    b mean = 257.53379
+    pValue = 0.0000034394
+    (Bigger means are better.)
+    1.006 times better
+    Results ARE significant
+    
+    * bytecode/Watchpoint.cpp:
+    * bytecode/Watchpoint.h:
+    * dfg/DFGSpeculativeJIT.cpp:
+    (JSC::DFG::SpeculativeJIT::compileGetPropertyEnumerator):
+    * ftl/FTLAbstractHeapRepository.h:
+    * ftl/FTLLowerDFGToB3.cpp:
+    (JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):
+    * runtime/JSPropertyNameEnumerator.h:
+    (JSC::propertyNameEnumerator):
+    * runtime/StructureRareData.h:
+    * runtime/StructureRareDataInlines.h:
+    (JSC::StructureRareData::setCachedPropertyNameEnumerator):
+    (JSC::StructureChainInvalidationWatchpoint::install):
+    (JSC::StructureChainInvalidationWatchpoint::fireInternal):
+    (JSC::StructureRareData::tryCachePropertyNameEnumeratorViaWatchpoint):
+    (JSC::StructureRareData::invalidateWatchpointBasedValidation):
+    * tools/JSDollarVM.cpp:
+    (JSC::JSC_DEFINE_HOST_FUNCTION):
+    (JSC::JSDollarVM::finishCreation):
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@282014 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2021-09-02  Yusuke Suzuki  <ysuz...@apple.com>
+
+            [JSC] Validate JSPropertyNameEnumerator via watchpoints
+            https://bugs.webkit.org/show_bug.cgi?id=229846
+
+            Reviewed by Keith Miller.
+
+            * stress/for-in-cacheable-dictionary.js: Added.
+            (shouldBe):
+            (collect):
+            * stress/for-in-invalidate.js: Added.
+            (shouldBe):
+            (collect):
+            * stress/for-in-uncacheable-dictionary.js: Added.
+            (shouldBe):
+            (collect):
+
 2021-09-01  Russell Epstein  <repst...@apple.com>
 
         Cherry-pick r281684. rdar://problem/82651474

Added: branches/safari-612-branch/JSTests/stress/for-in-cacheable-dictionary.js (0 => 282153)


--- branches/safari-612-branch/JSTests/stress/for-in-cacheable-dictionary.js	                        (rev 0)
+++ branches/safari-612-branch/JSTests/stress/for-in-cacheable-dictionary.js	2021-09-08 17:22:34 UTC (rev 282153)
@@ -0,0 +1,34 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+let object = {
+    a: 42,
+    b: 44,
+    c: 43,
+    d: 44,
+};
+
+function collect(object)
+{
+    let array = [];
+    for (let key in object)
+        array.push(key);
+    return array;
+}
+
+let array1 = collect(object);
+let array2 = collect(object);
+$vm.toCacheableDictionary(object);
+object.e = 44;
+let array3 = collect(object);
+let array4 = collect(object);
+object.f = 44;
+let array5 = collect(object);
+
+shouldBe(JSON.stringify(array1), `["a","b","c","d"]`);
+shouldBe(JSON.stringify(array2), `["a","b","c","d"]`);
+shouldBe(JSON.stringify(array3), `["a","b","c","d","e"]`);
+shouldBe(JSON.stringify(array4), `["a","b","c","d","e"]`);
+shouldBe(JSON.stringify(array5), `["a","b","c","d","e","f"]`);

Added: branches/safari-612-branch/JSTests/stress/for-in-invalidate.js (0 => 282153)


--- branches/safari-612-branch/JSTests/stress/for-in-invalidate.js	                        (rev 0)
+++ branches/safari-612-branch/JSTests/stress/for-in-invalidate.js	2021-09-08 17:22:34 UTC (rev 282153)
@@ -0,0 +1,48 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+let object = {
+    a: 42,
+    b: 44,
+    c: 43,
+    d: 44,
+};
+
+function collect(object)
+{
+    let array = [];
+    for (let key in object)
+        array.push(key);
+    return array;
+}
+
+let array1 = collect(object);
+let array2 = collect(object);
+object.e = 44;
+let array3 = collect(object);
+let array4 = collect(object);
+object.f = 44;
+let array5 = collect(object);
+let array6 = collect(object);
+let proto = { Hello: 33 };
+object.__proto__ = proto;
+let array7 = collect(object);
+let array8 = collect(object);
+proto.values = 33;
+let array9 = collect(object);
+let array10 = collect(object);
+let array11 = collect(object);
+
+shouldBe(JSON.stringify(array1), `["a","b","c","d"]`);
+shouldBe(JSON.stringify(array2), `["a","b","c","d"]`);
+shouldBe(JSON.stringify(array3), `["a","b","c","d","e"]`);
+shouldBe(JSON.stringify(array4), `["a","b","c","d","e"]`);
+shouldBe(JSON.stringify(array5), `["a","b","c","d","e","f"]`);
+shouldBe(JSON.stringify(array6), `["a","b","c","d","e","f"]`);
+shouldBe(JSON.stringify(array7), `["a","b","c","d","e","f","Hello"]`);
+shouldBe(JSON.stringify(array8), `["a","b","c","d","e","f","Hello"]`);
+shouldBe(JSON.stringify(array9), `["a","b","c","d","e","f","Hello","values"]`);
+shouldBe(JSON.stringify(array10), `["a","b","c","d","e","f","Hello","values"]`);
+shouldBe(JSON.stringify(array11), `["a","b","c","d","e","f","Hello","values"]`);

Added: branches/safari-612-branch/JSTests/stress/for-in-uncacheable-dictionary.js (0 => 282153)


--- branches/safari-612-branch/JSTests/stress/for-in-uncacheable-dictionary.js	                        (rev 0)
+++ branches/safari-612-branch/JSTests/stress/for-in-uncacheable-dictionary.js	2021-09-08 17:22:34 UTC (rev 282153)
@@ -0,0 +1,34 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+let object = {
+    a: 42,
+    b: 44,
+    c: 43,
+    d: 44,
+};
+
+function collect(object)
+{
+    let array = [];
+    for (let key in object)
+        array.push(key);
+    return array;
+}
+
+let array1 = collect(object);
+let array2 = collect(object);
+$vm.toUncacheableDictionary(object);
+object.e = 44;
+let array3 = collect(object);
+let array4 = collect(object);
+object.f = 44;
+let array5 = collect(object);
+
+shouldBe(JSON.stringify(array1), `["a","b","c","d"]`);
+shouldBe(JSON.stringify(array2), `["a","b","c","d"]`);
+shouldBe(JSON.stringify(array3), `["a","b","c","d","e"]`);
+shouldBe(JSON.stringify(array4), `["a","b","c","d","e"]`);
+shouldBe(JSON.stringify(array5), `["a","b","c","d","e","f"]`);

Modified: branches/safari-612-branch/Source/_javascript_Core/ChangeLog (282152 => 282153)


--- branches/safari-612-branch/Source/_javascript_Core/ChangeLog	2021-09-08 17:22:26 UTC (rev 282152)
+++ branches/safari-612-branch/Source/_javascript_Core/ChangeLog	2021-09-08 17:22:34 UTC (rev 282153)
@@ -1,3 +1,149 @@
+2021-09-08  Alan Coon  <alanc...@apple.com>
+
+        Cherry-pick r282014. rdar://problem/82877307
+
+    [JSC] Validate JSPropertyNameEnumerator via watchpoints
+    https://bugs.webkit.org/show_bug.cgi?id=229846
+    
+    Reviewed by Keith Miller.
+    
+    JSTests:
+    
+    * stress/for-in-cacheable-dictionary.js: Added.
+    (shouldBe):
+    (collect):
+    * stress/for-in-invalidate.js: Added.
+    (shouldBe):
+    (collect):
+    * stress/for-in-uncacheable-dictionary.js: Added.
+    (shouldBe):
+    (collect):
+    
+    Source/_javascript_Core:
+    
+    Looked into Elm-TodoMVC sampling profiler data and found that op_get_property_enumerator is taking enough amount of time.
+    And Instruments say validating JSPropertyNameEnumerator via traversing StructureChain is costly.
+    We are caching JSPropertyNameEnumerator only when we meet the condition: objects in prototype chain can ensure identity of
+    property names if structure is not changed. So we can use watchpoint based approach to invalidate JSPropertyNameEnumerator.
+    
+    This patch injects structure transition watchpoints if possible. And when watchpoint is fired, we invalidate JSPropertyNameEnumerator
+    cached in StructureRareData, as if we are ensuring prototype chain condition for the other property accesses.
+    
+    This offers 0.6% improvement in Speedometer2.
+    
+    ----------------------------------------------------------------------------------------------------------------------------------
+    |               subtest                |     ms      |     ms      |  b / a   | pValue (significance using False Discovery Rate) |
+    ----------------------------------------------------------------------------------------------------------------------------------
+    | Elm-TodoMVC                          |121.971667   |117.725000   |0.965183  | 0.000000 (significant)                           |
+    | VueJS-TodoMVC                        |26.246667    |26.035000    |0.991935  | 0.360614                                         |
+    | EmberJS-TodoMVC                      |126.196667   |126.653333   |1.003619  | 0.103138                                         |
+    | BackboneJS-TodoMVC                   |48.976667    |48.881667    |0.998060  | 0.474106                                         |
+    | Preact-TodoMVC                       |20.118333    |20.115000    |0.999834  | 0.989038                                         |
+    | AngularJS-TodoMVC                    |131.545000   |130.706667   |0.993627  | 0.015344 (significant)                           |
+    | Vanilla-ES2015-TodoMVC               |63.725000    |63.773333    |1.000758  | 0.706560                                         |
+    | Inferno-TodoMVC                      |64.231667    |62.653333    |0.975427  | 0.000000 (significant)                           |
+    | Flight-TodoMVC                       |77.223333    |77.690000    |1.006043  | 0.268309                                         |
+    | Angular2-TypeScript-TodoMVC          |39.686667    |39.500000    |0.995296  | 0.499678                                         |
+    | VanillaJS-TodoMVC                    |52.321667    |51.973333    |0.993342  | 0.077777                                         |
+    | jQuery-TodoMVC                       |224.908333   |225.761667   |1.003794  | 0.022136                                         |
+    | EmberJS-Debug-TodoMVC                |339.858333   |339.886667   |1.000083  | 0.950320                                         |
+    | React-TodoMVC                        |86.545000    |86.070000    |0.994512  | 0.001518 (significant)                           |
+    | React-Redux-TodoMVC                  |146.010000   |142.855000   |0.978392  | 0.000000 (significant)                           |
+    | Vanilla-ES2015-Babel-Webpack-TodoMVC |61.411667    |61.456667    |1.000733  | 0.631499                                         |
+    ----------------------------------------------------------------------------------------------------------------------------------
+    a mean = 255.96543
+    b mean = 257.53379
+    pValue = 0.0000034394
+    (Bigger means are better.)
+    1.006 times better
+    Results ARE significant
+    
+    * bytecode/Watchpoint.cpp:
+    * bytecode/Watchpoint.h:
+    * dfg/DFGSpeculativeJIT.cpp:
+    (JSC::DFG::SpeculativeJIT::compileGetPropertyEnumerator):
+    * ftl/FTLAbstractHeapRepository.h:
+    * ftl/FTLLowerDFGToB3.cpp:
+    (JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):
+    * runtime/JSPropertyNameEnumerator.h:
+    (JSC::propertyNameEnumerator):
+    * runtime/StructureRareData.h:
+    * runtime/StructureRareDataInlines.h:
+    (JSC::StructureRareData::setCachedPropertyNameEnumerator):
+    (JSC::StructureChainInvalidationWatchpoint::install):
+    (JSC::StructureChainInvalidationWatchpoint::fireInternal):
+    (JSC::StructureRareData::tryCachePropertyNameEnumeratorViaWatchpoint):
+    (JSC::StructureRareData::invalidateWatchpointBasedValidation):
+    * tools/JSDollarVM.cpp:
+    (JSC::JSC_DEFINE_HOST_FUNCTION):
+    (JSC::JSDollarVM::finishCreation):
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@282014 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2021-09-02  Yusuke Suzuki  <ysuz...@apple.com>
+
+            [JSC] Validate JSPropertyNameEnumerator via watchpoints
+            https://bugs.webkit.org/show_bug.cgi?id=229846
+
+            Reviewed by Keith Miller.
+
+            Looked into Elm-TodoMVC sampling profiler data and found that op_get_property_enumerator is taking enough amount of time.
+            And Instruments say validating JSPropertyNameEnumerator via traversing StructureChain is costly.
+            We are caching JSPropertyNameEnumerator only when we meet the condition: objects in prototype chain can ensure identity of
+            property names if structure is not changed. So we can use watchpoint based approach to invalidate JSPropertyNameEnumerator.
+
+            This patch injects structure transition watchpoints if possible. And when watchpoint is fired, we invalidate JSPropertyNameEnumerator
+            cached in StructureRareData, as if we are ensuring prototype chain condition for the other property accesses.
+
+            This offers 0.6% improvement in Speedometer2.
+
+            ----------------------------------------------------------------------------------------------------------------------------------
+            |               subtest                |     ms      |     ms      |  b / a   | pValue (significance using False Discovery Rate) |
+            ----------------------------------------------------------------------------------------------------------------------------------
+            | Elm-TodoMVC                          |121.971667   |117.725000   |0.965183  | 0.000000 (significant)                           |
+            | VueJS-TodoMVC                        |26.246667    |26.035000    |0.991935  | 0.360614                                         |
+            | EmberJS-TodoMVC                      |126.196667   |126.653333   |1.003619  | 0.103138                                         |
+            | BackboneJS-TodoMVC                   |48.976667    |48.881667    |0.998060  | 0.474106                                         |
+            | Preact-TodoMVC                       |20.118333    |20.115000    |0.999834  | 0.989038                                         |
+            | AngularJS-TodoMVC                    |131.545000   |130.706667   |0.993627  | 0.015344 (significant)                           |
+            | Vanilla-ES2015-TodoMVC               |63.725000    |63.773333    |1.000758  | 0.706560                                         |
+            | Inferno-TodoMVC                      |64.231667    |62.653333    |0.975427  | 0.000000 (significant)                           |
+            | Flight-TodoMVC                       |77.223333    |77.690000    |1.006043  | 0.268309                                         |
+            | Angular2-TypeScript-TodoMVC          |39.686667    |39.500000    |0.995296  | 0.499678                                         |
+            | VanillaJS-TodoMVC                    |52.321667    |51.973333    |0.993342  | 0.077777                                         |
+            | jQuery-TodoMVC                       |224.908333   |225.761667   |1.003794  | 0.022136                                         |
+            | EmberJS-Debug-TodoMVC                |339.858333   |339.886667   |1.000083  | 0.950320                                         |
+            | React-TodoMVC                        |86.545000    |86.070000    |0.994512  | 0.001518 (significant)                           |
+            | React-Redux-TodoMVC                  |146.010000   |142.855000   |0.978392  | 0.000000 (significant)                           |
+            | Vanilla-ES2015-Babel-Webpack-TodoMVC |61.411667    |61.456667    |1.000733  | 0.631499                                         |
+            ----------------------------------------------------------------------------------------------------------------------------------
+            a mean = 255.96543
+            b mean = 257.53379
+            pValue = 0.0000034394
+            (Bigger means are better.)
+            1.006 times better
+            Results ARE significant
+
+            * bytecode/Watchpoint.cpp:
+            * bytecode/Watchpoint.h:
+            * dfg/DFGSpeculativeJIT.cpp:
+            (JSC::DFG::SpeculativeJIT::compileGetPropertyEnumerator):
+            * ftl/FTLAbstractHeapRepository.h:
+            * ftl/FTLLowerDFGToB3.cpp:
+            (JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):
+            * runtime/JSPropertyNameEnumerator.h:
+            (JSC::propertyNameEnumerator):
+            * runtime/StructureRareData.h:
+            * runtime/StructureRareDataInlines.h:
+            (JSC::StructureRareData::setCachedPropertyNameEnumerator):
+            (JSC::StructureChainInvalidationWatchpoint::install):
+            (JSC::StructureChainInvalidationWatchpoint::fireInternal):
+            (JSC::StructureRareData::tryCachePropertyNameEnumeratorViaWatchpoint):
+            (JSC::StructureRareData::invalidateWatchpointBasedValidation):
+            * tools/JSDollarVM.cpp:
+            (JSC::JSC_DEFINE_HOST_FUNCTION):
+            (JSC::JSDollarVM::finishCreation):
+
 2021-09-01  Russell Epstein  <repst...@apple.com>
 
         Cherry-pick r281684. rdar://problem/82651474

Modified: branches/safari-612-branch/Source/_javascript_Core/bytecode/Watchpoint.cpp (282152 => 282153)


--- branches/safari-612-branch/Source/_javascript_Core/bytecode/Watchpoint.cpp	2021-09-08 17:22:26 UTC (rev 282152)
+++ branches/safari-612-branch/Source/_javascript_Core/bytecode/Watchpoint.cpp	2021-09-08 17:22:34 UTC (rev 282153)
@@ -33,6 +33,7 @@
 #include "FunctionRareData.h"
 #include "HeapInlines.h"
 #include "LLIntPrototypeLoadAdaptiveStructureWatchpoint.h"
+#include "StructureRareDataInlines.h"
 #include "StructureStubClearingWatchpoint.h"
 #include "VM.h"
 

Modified: branches/safari-612-branch/Source/_javascript_Core/bytecode/Watchpoint.h (282152 => 282153)


--- branches/safari-612-branch/Source/_javascript_Core/bytecode/Watchpoint.h	2021-09-08 17:22:26 UTC (rev 282152)
+++ branches/safari-612-branch/Source/_javascript_Core/bytecode/Watchpoint.h	2021-09-08 17:22:34 UTC (rev 282153)
@@ -108,7 +108,8 @@
     macro(CodeBlockJettisoning, CodeBlockJettisoningWatchpoint) \
     macro(LLIntPrototypeLoadAdaptiveStructure, LLIntPrototypeLoadAdaptiveStructureWatchpoint) \
     macro(FunctionRareDataAllocationProfileClearing, FunctionRareData::AllocationProfileClearingWatchpoint) \
-    macro(CachedSpecialPropertyAdaptiveStructure, CachedSpecialPropertyAdaptiveStructureWatchpoint)
+    macro(CachedSpecialPropertyAdaptiveStructure, CachedSpecialPropertyAdaptiveStructureWatchpoint) \
+    macro(StructureChainInvalidation, StructureChainInvalidationWatchpoint) \
 
 #if ENABLE(JIT)
 #define JSC_WATCHPOINT_TYPES_WITHOUT_DFG(macro) \

Modified: branches/safari-612-branch/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (282152 => 282153)


--- branches/safari-612-branch/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2021-09-08 17:22:26 UTC (rev 282152)
+++ branches/safari-612-branch/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2021-09-08 17:22:34 UTC (rev 282153)
@@ -8416,7 +8416,7 @@
             Node* enumerator = get(bytecode.m_enumerator);
             Node* mode = get(bytecode.m_mode);
 
-            auto seenModes = OptionSet<JSPropertyNameEnumerator::Mode>::fromRaw(metadata.m_enumeratorMetadata);
+            auto seenModes = OptionSet<JSPropertyNameEnumerator::Flag>::fromRaw(metadata.m_enumeratorMetadata);
 
             if (!seenModes)
                 addToGraph(ForceOSRExit);
@@ -8451,7 +8451,7 @@
             Node* mode = get(bytecode.m_mode);
             Node* enumerator = get(bytecode.m_enumerator);
 
-            auto seenModes = OptionSet<JSPropertyNameEnumerator::Mode>::fromRaw(metadata.m_enumeratorMetadata);
+            auto seenModes = OptionSet<JSPropertyNameEnumerator::Flag>::fromRaw(metadata.m_enumeratorMetadata);
             if (!seenModes)
                 addToGraph(ForceOSRExit);
 

Modified: branches/safari-612-branch/Source/_javascript_Core/dfg/DFGNode.h (282152 => 282153)


--- branches/safari-612-branch/Source/_javascript_Core/dfg/DFGNode.h	2021-09-08 17:22:26 UTC (rev 282152)
+++ branches/safari-612-branch/Source/_javascript_Core/dfg/DFGNode.h	2021-09-08 17:22:34 UTC (rev 282153)
@@ -3267,10 +3267,10 @@
         }
     }
 
-    OptionSet<JSPropertyNameEnumerator::Mode> enumeratorMetadata()
+    OptionSet<JSPropertyNameEnumerator::Flag> enumeratorMetadata()
     {
         ASSERT(hasEnumeratorMetadata());
-        return OptionSet<JSPropertyNameEnumerator::Mode>::fromRaw(m_opInfo2.as<unsigned>());
+        return OptionSet<JSPropertyNameEnumerator::Flag>::fromRaw(m_opInfo2.as<unsigned>());
     }
 
     void dumpChildren(PrintStream& out)

Modified: branches/safari-612-branch/Source/_javascript_Core/dfg/DFGOperations.cpp (282152 => 282153)


--- branches/safari-612-branch/Source/_javascript_Core/dfg/DFGOperations.cpp	2021-09-08 17:22:26 UTC (rev 282152)
+++ branches/safari-612-branch/Source/_javascript_Core/dfg/DFGOperations.cpp	2021-09-08 17:22:34 UTC (rev 282153)
@@ -2462,7 +2462,7 @@
     JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
     auto scope = DECLARE_THROW_SCOPE(vm);
 
-    JSPropertyNameEnumerator::Mode mode = static_cast<JSPropertyNameEnumerator::Mode>(modeNumber);
+    JSPropertyNameEnumerator::Flag mode = static_cast<JSPropertyNameEnumerator::Flag>(modeNumber);
     if (JSValue::decode(baseValue).isUndefinedOrNull()) {
         ASSERT(mode == JSPropertyNameEnumerator::InitMode);
         ASSERT(!index);

Modified: branches/safari-612-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (282152 => 282153)


--- branches/safari-612-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2021-09-08 17:22:26 UTC (rev 282152)
+++ branches/safari-612-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2021-09-08 17:22:34 UTC (rev 282153)
@@ -13509,7 +13509,8 @@
         m_jit.load32(MacroAssembler::Address(baseGPR, JSCell::structureIDOffset()), resultRegs.payloadGPR());
         speculationCheck(BadCache, JSValueSource(), node, m_jit.branch32(MacroAssembler::NotEqual, resultRegs.payloadGPR(), MacroAssembler::Address(enumeratorGPR, JSPropertyNameEnumerator::cachedStructureIDOffset())));
 
-        m_jit.load32(MacroAssembler::Address(enumeratorGPR, JSPropertyNameEnumerator::modeSetOffset()), resultRegs.payloadGPR());
+        m_jit.load32(MacroAssembler::Address(enumeratorGPR, JSPropertyNameEnumerator::flagsOffset()), resultRegs.payloadGPR());
+        m_jit.and32(TrustedImm32(JSPropertyNameEnumerator::enumerationModeMask), resultRegs.payloadGPR());
         speculationCheck(BadCache, JSValueSource(), node, m_jit.branch32(MacroAssembler::NotEqual, TrustedImm32(JSPropertyNameEnumerator::OwnStructureMode), resultRegs.payloadGPR()));
 
         m_jit.move(indexGPR, resultRegs.payloadGPR());
@@ -13943,14 +13944,44 @@
 {
     if (node->child1().useKind() == CellUse) {
         SpeculateCellOperand base(this, node->child1());
+        GPRTemporary scratch1(this);
+        GPRTemporary scratch2(this);
+
         GPRReg baseGPR = base.gpr();
+        GPRReg scratch1GPR = scratch1.gpr();
+        GPRReg scratch2GPR = scratch2.gpr();
 
-        flushRegisters();
-        GPRFlushedCallResult result(this);
-        GPRReg resultGPR = result.gpr();
-        callOperation(operationGetPropertyEnumeratorCell, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseGPR);
+        CCallHelpers::JumpList slowCases;
+
+        // We go to the inlined fast path if the object is UndecidedShape / NoIndexingShape for simplicity.
+        static_assert(!NonArray);
+        static_assert(ArrayClass == 1);
+        static_assert(UndecidedShape == 2);
+        static_assert(ArrayWithUndecided == 3);
+        static_assert(NonArray <= ArrayWithUndecided);
+        static_assert(ArrayClass <= ArrayWithUndecided);
+        static_assert(ArrayWithUndecided <= ArrayWithUndecided);
+        m_jit.load8(CCallHelpers::Address(baseGPR, JSCell::indexingTypeAndMiscOffset()), scratch1GPR);
+        m_jit.and32(CCallHelpers::TrustedImm32(IndexingTypeMask), scratch1GPR);
+        slowCases.append(m_jit.branch32(CCallHelpers::Above, scratch1GPR, CCallHelpers::TrustedImm32(ArrayWithUndecided)));
+        m_jit.emitLoadStructure(vm(), baseGPR, scratch1GPR, scratch2GPR);
+        m_jit.loadPtr(CCallHelpers::Address(scratch1GPR, Structure::previousOrRareDataOffset()), scratch1GPR);
+
+        slowCases.append(m_jit.branchTestPtr(CCallHelpers::Zero, scratch1GPR));
+        slowCases.append(m_jit.branch32(CCallHelpers::Equal, CCallHelpers::Address(scratch1GPR, JSCell::structureIDOffset()), TrustedImm32(bitwise_cast<int32_t>(vm().structureStructure->structureID()))));
+        m_jit.loadPtr(CCallHelpers::Address(scratch1GPR, StructureRareData::offsetOfCachedPropertyNameEnumerator()), scratch1GPR);
+        slowCases.append(m_jit.branchTestPtr(CCallHelpers::Zero, scratch1GPR));
+        slowCases.append(m_jit.branchTest32(CCallHelpers::Zero, CCallHelpers::Address(scratch1GPR, JSPropertyNameEnumerator::flagsOffset()), CCallHelpers::TrustedImm32(JSPropertyNameEnumerator::ValidatedViaWatchpoint)));
+        auto done = m_jit.jump();
+
+        slowCases.link(&m_jit);
+        silentSpillAllRegisters(scratch1GPR, scratch2GPR);
+        callOperation(operationGetPropertyEnumeratorCell, scratch1GPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseGPR);
+        silentFillAllRegisters();
         m_jit.exceptionCheck();
-        cellResult(resultGPR, node);
+
+        done.link(&m_jit);
+        cellResult(scratch1GPR, node);
         return;
     }
 

Modified: branches/safari-612-branch/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.h (282152 => 282153)


--- branches/safari-612-branch/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.h	2021-09-08 17:22:26 UTC (rev 282152)
+++ branches/safari-612-branch/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.h	2021-09-08 17:22:34 UTC (rev 282153)
@@ -110,11 +110,12 @@
     macro(JSObject_butterfly, JSObject::butterflyOffset()) \
     macro(JSPropertyNameEnumerator_cachedInlineCapacity, JSPropertyNameEnumerator::cachedInlineCapacityOffset()) \
     macro(JSPropertyNameEnumerator_cachedPropertyNamesVector, JSPropertyNameEnumerator::cachedPropertyNamesVectorOffset()) \
+    macro(JSPropertyNameEnumerator_cachedPrototypeChain, JSPropertyNameEnumerator::offsetOfCachedPrototypeChain()) \
     macro(JSPropertyNameEnumerator_cachedStructureID, JSPropertyNameEnumerator::cachedStructureIDOffset()) \
     macro(JSPropertyNameEnumerator_endGenericPropertyIndex, JSPropertyNameEnumerator::endGenericPropertyIndexOffset()) \
     macro(JSPropertyNameEnumerator_endStructurePropertyIndex, JSPropertyNameEnumerator::endStructurePropertyIndexOffset()) \
     macro(JSPropertyNameEnumerator_indexLength, JSPropertyNameEnumerator::indexedLengthOffset()) \
-    macro(JSPropertyNameEnumerator_modeSet, JSPropertyNameEnumerator::modeSetOffset()) \
+    macro(JSPropertyNameEnumerator_flags, JSPropertyNameEnumerator::flagsOffset()) \
     macro(JSRopeString_flags, JSRopeString::offsetOfFlags()) \
     macro(JSRopeString_length, JSRopeString::offsetOfLength()) \
     macro(JSRopeString_fiber0, JSRopeString::offsetOfFiber0()) \
@@ -152,6 +153,7 @@
     macro(Structure_structureID, Structure::structureIDOffset()) \
     macro(StructureRareData_cachedKeys, StructureRareData::offsetOfCachedPropertyNames(CachedPropertyNamesKind::Keys)) \
     macro(StructureRareData_cachedGetOwnPropertyNames, StructureRareData::offsetOfCachedPropertyNames(CachedPropertyNamesKind::GetOwnPropertyNames)) \
+    macro(StructureRareData_cachedPropertyNameEnumerator, StructureRareData::offsetOfCachedPropertyNameEnumerator()) \
     macro(HashMapImpl_capacity, HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfCapacity()) \
     macro(HashMapImpl_buffer,  HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfBuffer()) \
     macro(HashMapImpl_head,  HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfHead()) \

Modified: branches/safari-612-branch/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (282152 => 282153)


--- branches/safari-612-branch/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2021-09-08 17:22:26 UTC (rev 282152)
+++ branches/safari-612-branch/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2021-09-08 17:22:34 UTC (rev 282153)
@@ -13220,10 +13220,54 @@
     void compileGetPropertyEnumerator()
     {
         JSGlobalObject* globalObject = m_graph.globalObjectFor(m_origin.semantic);
-        if (m_node->child1().useKind() == CellUse)
-            setJSValue(vmCall(Int64, operationGetPropertyEnumeratorCell, weakPointer(globalObject), lowCell(m_node->child1())));
-        else
-            setJSValue(vmCall(Int64, operationGetPropertyEnumerator, weakPointer(globalObject), lowJSValue(m_node->child1())));
+        if (m_node->child1().useKind() == CellUse) {
+            LBasicBlock checkExistingCase = m_out.newBlock();
+            LBasicBlock notNullCase = m_out.newBlock();
+            LBasicBlock rareDataCase = m_out.newBlock();
+            LBasicBlock validationCase = m_out.newBlock();
+            LBasicBlock genericCase = m_out.newBlock();
+            LBasicBlock continuation = m_out.newBlock();
+
+            LValue cell = lowCell(m_node->child1());
+
+            // We go to the inlined fast path if the object is UndecidedShape / NoIndexingShape for simplicity.
+            static_assert(!NonArray);
+            static_assert(ArrayClass == 1);
+            static_assert(UndecidedShape == 2);
+            static_assert(ArrayWithUndecided == 3);
+            static_assert(NonArray <= ArrayWithUndecided);
+            static_assert(ArrayClass <= ArrayWithUndecided);
+            static_assert(ArrayWithUndecided <= ArrayWithUndecided);
+            LValue indexingType = m_out.bitAnd(m_out.load8ZeroExt32(cell, m_heaps.JSCell_indexingTypeAndMisc), m_out.constInt32(IndexingTypeMask));
+            m_out.branch(m_out.belowOrEqual(indexingType, m_out.constInt32(ArrayWithUndecided)), unsure(checkExistingCase), unsure(genericCase));
+
+            LBasicBlock lastNext = m_out.appendTo(checkExistingCase, notNullCase);
+            LValue structure = loadStructure(cell);
+            LValue previousOrRareData = m_out.loadPtr(structure, m_heaps.Structure_previousOrRareData);
+            m_out.branch(m_out.notNull(previousOrRareData), unsure(notNullCase), unsure(genericCase));
+
+            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(genericCase));
+
+            m_out.appendTo(rareDataCase, validationCase);
+            LValue cached = m_out.loadPtr(previousOrRareData, m_heaps.StructureRareData_cachedPropertyNameEnumerator);
+            m_out.branch(m_out.notNull(cached), unsure(validationCase), unsure(genericCase));
+
+            m_out.appendTo(validationCase, genericCase);
+            ValueFromBlock fastResult = m_out.anchor(cached);
+            m_out.branch(m_out.testNonZero32(m_out.load32(cached, m_heaps.JSPropertyNameEnumerator_flags), m_out.constInt32(JSPropertyNameEnumerator::ValidatedViaWatchpoint)), unsure(continuation), unsure(genericCase));
+
+            m_out.appendTo(genericCase, continuation);
+            ValueFromBlock genericResult = m_out.anchor(vmCall(pointerType(), operationGetPropertyEnumeratorCell, weakPointer(globalObject), cell));
+            m_out.jump(continuation);
+
+            m_out.appendTo(continuation, lastNext);
+            setJSValue(m_out.phi(pointerType(), fastResult, genericResult));
+            return;
+        }
+        setJSValue(vmCall(Int64, operationGetPropertyEnumerator, weakPointer(globalObject), lowJSValue(m_node->child1())));
     }
 
     void compileEnumeratorNextUpdateIndexAndMode()
@@ -13269,7 +13313,7 @@
             LValue base = lowCell(baseEdge);
             speculate(BadCache, noValue(), m_node, m_out.notEqual(m_out.load32(base, m_heaps.JSCell_structureID), m_out.load32(enumerator, m_heaps.JSPropertyNameEnumerator_cachedStructureID)));
 
-            speculate(BadCache, noValue(), m_node, m_out.notEqual(m_out.load32(enumerator, m_heaps.JSPropertyNameEnumerator_modeSet), m_out.constInt32(JSPropertyNameEnumerator::OwnStructureMode)));
+            speculate(BadCache, noValue(), m_node, m_out.notEqual(m_out.bitAnd(m_out.load32(enumerator, m_heaps.JSPropertyNameEnumerator_flags), m_out.constInt32(JSPropertyNameEnumerator::enumerationModeMask)), m_out.constInt32(JSPropertyNameEnumerator::OwnStructureMode)));
 
             LBasicBlock increment = m_out.newBlock();
             LBasicBlock continuation = m_out.newBlock();

Modified: branches/safari-612-branch/Source/_javascript_Core/jit/JITPropertyAccess.cpp (282152 => 282153)


--- branches/safari-612-branch/Source/_javascript_Core/jit/JITPropertyAccess.cpp	2021-09-08 17:22:26 UTC (rev 282152)
+++ branches/safari-612-branch/Source/_javascript_Core/jit/JITPropertyAccess.cpp	2021-09-08 17:22:34 UTC (rev 282153)
@@ -2808,7 +2808,7 @@
     if (metadata.m_enumeratorMetadata == JSPropertyNameEnumerator::OwnStructureMode) {
         GPRReg enumeratorGPR = regT3;
         emitGetVirtualRegister(enumerator, enumeratorGPR);
-        operationCases.append(branchTest32(NonZero, Address(enumeratorGPR, JSPropertyNameEnumerator::modeSetOffset()), TrustedImm32(~JSPropertyNameEnumerator::OwnStructureMode)));
+        operationCases.append(branchTest32(NonZero, Address(enumeratorGPR, JSPropertyNameEnumerator::flagsOffset()), TrustedImm32((~JSPropertyNameEnumerator::OwnStructureMode) & JSPropertyNameEnumerator::enumerationModeMask)));
         emitGetVirtualRegister(base, baseGPR);
         load32(Address(enumeratorGPR, JSPropertyNameEnumerator::cachedStructureIDOffset()), indexGPR);
         operationCases.append(branch32(NotEqual, indexGPR, Address(baseGPR, JSCell::structureIDOffset())));

Modified: branches/safari-612-branch/Source/_javascript_Core/runtime/CommonSlowPaths.cpp (282152 => 282153)


--- branches/safari-612-branch/Source/_javascript_Core/runtime/CommonSlowPaths.cpp	2021-09-08 17:22:26 UTC (rev 282152)
+++ branches/safari-612-branch/Source/_javascript_Core/runtime/CommonSlowPaths.cpp	2021-09-08 17:22:34 UTC (rev 282153)
@@ -985,7 +985,7 @@
     Register& indexRegister = GET(bytecode.m_index);
     Register& nameRegister = GET(bytecode.m_propertyName);
 
-    auto mode = static_cast<JSPropertyNameEnumerator::Mode>(modeRegister.jsValue().asUInt32());
+    auto mode = static_cast<JSPropertyNameEnumerator::Flag>(modeRegister.jsValue().asUInt32());
     uint32_t index = indexRegister.jsValue().asUInt32();
 
     JSPropertyNameEnumerator* enumerator = jsCast<JSPropertyNameEnumerator*>(GET(bytecode.m_enumerator).jsValue());
@@ -1011,7 +1011,7 @@
     auto bytecode = pc->as<OpEnumeratorGetByVal>();
     JSValue baseValue = GET_C(bytecode.m_base).jsValue();
     auto& metadata = bytecode.metadata(codeBlock);
-    auto mode = static_cast<JSPropertyNameEnumerator::Mode>(GET(bytecode.m_mode).jsValue().asUInt32());
+    auto mode = static_cast<JSPropertyNameEnumerator::Flag>(GET(bytecode.m_mode).jsValue().asUInt32());
     metadata.m_enumeratorMetadata |= static_cast<uint8_t>(mode);
 
     JSPropertyNameEnumerator* enumerator = jsCast<JSPropertyNameEnumerator*>(GET(bytecode.m_enumerator).jsValue());
@@ -1054,7 +1054,7 @@
     auto bytecode = pc->as<OpEnumeratorInByVal>();
     JSValue baseValue = GET_C(bytecode.m_base).jsValue();
     auto& metadata = bytecode.metadata(codeBlock);
-    auto mode = static_cast<JSPropertyNameEnumerator::Mode>(GET(bytecode.m_mode).jsValue().asUInt32());
+    auto mode = static_cast<JSPropertyNameEnumerator::Flag>(GET(bytecode.m_mode).jsValue().asUInt32());
     metadata.m_enumeratorMetadata |= static_cast<uint8_t>(mode);
 
     CHECK_EXCEPTION();
@@ -1077,7 +1077,7 @@
     auto bytecode = pc->as<OpEnumeratorHasOwnProperty>();
     JSValue baseValue = GET_C(bytecode.m_base).jsValue();
     auto& metadata = bytecode.metadata(codeBlock);
-    auto mode = static_cast<JSPropertyNameEnumerator::Mode>(GET(bytecode.m_mode).jsValue().asUInt32());
+    auto mode = static_cast<JSPropertyNameEnumerator::Flag>(GET(bytecode.m_mode).jsValue().asUInt32());
     metadata.m_enumeratorMetadata |= static_cast<uint8_t>(mode);
 
     JSPropertyNameEnumerator* enumerator = jsCast<JSPropertyNameEnumerator*>(GET(bytecode.m_enumerator).jsValue());

Modified: branches/safari-612-branch/Source/_javascript_Core/runtime/JSPropertyNameEnumerator.cpp (282152 => 282153)


--- branches/safari-612-branch/Source/_javascript_Core/runtime/JSPropertyNameEnumerator.cpp	2021-09-08 17:22:26 UTC (rev 282152)
+++ branches/safari-612-branch/Source/_javascript_Core/runtime/JSPropertyNameEnumerator.cpp	2021-09-08 17:22:34 UTC (rev 282153)
@@ -57,11 +57,11 @@
     , m_cachedInlineCapacity(structure ? structure->inlineCapacity() : 0)
 {
     if (m_indexedLength)
-        m_modeSet |= JSPropertyNameEnumerator::IndexedMode;
+        m_flags |= JSPropertyNameEnumerator::IndexedMode;
     if (m_endStructurePropertyIndex)
-        m_modeSet |= JSPropertyNameEnumerator::OwnStructureMode;
+        m_flags |= JSPropertyNameEnumerator::OwnStructureMode;
     if (m_endGenericPropertyIndex - m_endStructurePropertyIndex)
-        m_modeSet |= JSPropertyNameEnumerator::GenericMode;
+        m_flags |= JSPropertyNameEnumerator::GenericMode;
 }
 
 void JSPropertyNameEnumerator::finishCreation(VM& vm, RefPtr<PropertyNameArrayData>&& identifiers)
@@ -154,7 +154,7 @@
     }
 }
 
-JSString* JSPropertyNameEnumerator::computeNext(JSGlobalObject* globalObject, JSObject* base, uint32_t& index, Mode& mode, bool shouldAllocateIndexedNameString)
+JSString* JSPropertyNameEnumerator::computeNext(JSGlobalObject* globalObject, JSObject* base, uint32_t& index, Flag& mode, bool shouldAllocateIndexedNameString)
 {
     VM& vm = globalObject->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);

Modified: branches/safari-612-branch/Source/_javascript_Core/runtime/JSPropertyNameEnumerator.h (282152 => 282153)


--- branches/safari-612-branch/Source/_javascript_Core/runtime/JSPropertyNameEnumerator.h	2021-09-08 17:22:26 UTC (rev 282152)
+++ branches/safari-612-branch/Source/_javascript_Core/runtime/JSPropertyNameEnumerator.h	2021-09-08 17:22:34 UTC (rev 282153)
@@ -37,7 +37,7 @@
     using Base = JSCell;
     static constexpr unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
 
-    enum Mode : uint8_t {
+    enum Flag : uint8_t {
         InitMode = 0,
         IndexedMode = 1 << 0,
         OwnStructureMode = 1 << 1,
@@ -44,6 +44,8 @@
         GenericMode = 1 << 2,
         // Profiling Only
         HasSeenOwnStructureModeStructureMismatch = 1 << 3,
+        // JSPropertyNameEnumerator Only
+        ValidatedViaWatchpoint = 1 << 4,
     };
 
     static constexpr uint8_t enumerationModeMask = (GenericMode << 1) - 1;
@@ -72,6 +74,7 @@
 
     StructureChain* cachedPrototypeChain() const { return m_prototypeChain.get(); }
     void setCachedPrototypeChain(VM& vm, StructureChain* prototypeChain) { return m_prototypeChain.set(vm, this, prototypeChain); }
+    static ptrdiff_t offsetOfCachedPrototypeChain() { return OBJECT_OFFSETOF(JSPropertyNameEnumerator, m_prototypeChain); }
 
     Structure* cachedStructure(VM& vm) const
     {
@@ -85,20 +88,29 @@
     uint32_t endGenericPropertyIndex() const { return m_endGenericPropertyIndex; }
     uint32_t cachedInlineCapacity() const { return m_cachedInlineCapacity; }
     uint32_t sizeOfPropertyNames() const { return endGenericPropertyIndex(); }
-    uint32_t modeSet() const { return m_modeSet; }
+    uint32_t flags() const { return m_flags; }
     static ptrdiff_t cachedStructureIDOffset() { return OBJECT_OFFSETOF(JSPropertyNameEnumerator, m_cachedStructureID); }
     static ptrdiff_t indexedLengthOffset() { return OBJECT_OFFSETOF(JSPropertyNameEnumerator, m_indexedLength); }
     static ptrdiff_t endStructurePropertyIndexOffset() { return OBJECT_OFFSETOF(JSPropertyNameEnumerator, m_endStructurePropertyIndex); }
     static ptrdiff_t endGenericPropertyIndexOffset() { return OBJECT_OFFSETOF(JSPropertyNameEnumerator, m_endGenericPropertyIndex); }
     static ptrdiff_t cachedInlineCapacityOffset() { return OBJECT_OFFSETOF(JSPropertyNameEnumerator, m_cachedInlineCapacity); }
-    static ptrdiff_t modeSetOffset() { return OBJECT_OFFSETOF(JSPropertyNameEnumerator, m_modeSet); }
+    static ptrdiff_t flagsOffset() { return OBJECT_OFFSETOF(JSPropertyNameEnumerator, m_flags); }
     static ptrdiff_t cachedPropertyNamesVectorOffset()
     {
         return OBJECT_OFFSETOF(JSPropertyNameEnumerator, m_propertyNames);
     }
 
-    JSString* computeNext(JSGlobalObject*, JSObject* base, uint32_t& currentIndex, Mode&, bool shouldAllocateIndexedNameString = true);
+    bool validatedViaWatchpoint() const { return m_flags & JSPropertyNameEnumerator::ValidatedViaWatchpoint; }
+    void setValidatedViaWatchpoint(bool validatedViaWatchpoint)
+    {
+        if (validatedViaWatchpoint)
+            m_flags |= JSPropertyNameEnumerator::ValidatedViaWatchpoint;
+        else
+            m_flags &= ~JSPropertyNameEnumerator::ValidatedViaWatchpoint;
+    }
 
+    JSString* computeNext(JSGlobalObject*, JSObject* base, uint32_t& currentIndex, Flag&, bool shouldAllocateIndexedNameString = true);
+
     DECLARE_VISIT_CHILDREN;
 
 private:
@@ -114,7 +126,7 @@
     uint32_t m_endStructurePropertyIndex;
     uint32_t m_endGenericPropertyIndex;
     uint32_t m_cachedInlineCapacity;
-    uint32_t m_modeSet { 0 };
+    uint32_t m_flags { 0 };
 };
 
 void getEnumerablePropertyNames(JSGlobalObject*, JSObject*, PropertyNameArray&, uint32_t& indexedLength, uint32_t& structurePropertyCount);
@@ -130,9 +142,10 @@
 
     Structure* structure = base->structure(vm);
     if (!indexedLength
-        && (enumerator = structure->cachedPropertyNameEnumerator())
-        && enumerator->cachedPrototypeChain() == structure->prototypeChain(globalObject, base))
-        return enumerator;
+        && (enumerator = structure->cachedPropertyNameEnumerator())) {
+        if (enumerator->validatedViaWatchpoint() || enumerator->cachedPrototypeChain() == structure->prototypeChain(globalObject, base))
+            return enumerator;
+    }
 
     uint32_t numberStructureProperties = 0;
     PropertyNameArray propertyNames(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude);
@@ -162,6 +175,6 @@
     return enumerator;
 }
 
-using EnumeratorMetadata = std::underlying_type_t<JSPropertyNameEnumerator::Mode>;
+using EnumeratorMetadata = std::underlying_type_t<JSPropertyNameEnumerator::Flag>;
 
 } // namespace JSC

Modified: branches/safari-612-branch/Source/_javascript_Core/runtime/Structure.h (282152 => 282153)


--- branches/safari-612-branch/Source/_javascript_Core/runtime/Structure.h	2021-09-08 17:22:26 UTC (rev 282152)
+++ branches/safari-612-branch/Source/_javascript_Core/runtime/Structure.h	2021-09-08 17:22:34 UTC (rev 282153)
@@ -634,6 +634,11 @@
     {
         return dfgShouldWatchIfPossible() && transitionWatchpointSetIsStillValid();
     }
+
+    bool propertyNameEnumeratorShouldWatch() const
+    {
+        return dfgShouldWatch();
+    }
         
     void addTransitionWatchpoint(Watchpoint* watchpoint) const
     {

Modified: branches/safari-612-branch/Source/_javascript_Core/runtime/StructureRareData.h (282152 => 282153)


--- branches/safari-612-branch/Source/_javascript_Core/runtime/StructureRareData.h	2021-09-08 17:22:26 UTC (rev 282152)
+++ branches/safari-612-branch/Source/_javascript_Core/runtime/StructureRareData.h	2021-09-08 17:22:34 UTC (rev 282153)
@@ -30,11 +30,13 @@
 #include "JSTypeInfo.h"
 #include "PropertyOffset.h"
 #include "PropertySlot.h"
+#include <wtf/FixedVector.h>
 
 namespace JSC {
 
 class JSPropertyNameEnumerator;
 class Structure;
+class StructureChain;
 class CachedSpecialPropertyAdaptiveStructureWatchpoint;
 class CachedSpecialPropertyAdaptiveInferredPropertyValueWatchpoint;
 struct SpecialPropertyCache;
@@ -52,6 +54,9 @@
 };
 static constexpr unsigned numberOfCachedSpecialPropertyKeys = 4;
 
+class StructureRareData;
+class StructureChainInvalidationWatchpoint;
+
 class StructureRareData final : public JSCell {
 public:
     typedef JSCell Base;
@@ -102,10 +107,17 @@
         return OBJECT_OFFSETOF(StructureRareData, m_cachedPropertyNames) + sizeof(WriteBarrier<JSImmutableButterfly>) * static_cast<unsigned>(kind);
     }
 
+    static ptrdiff_t offsetOfCachedPropertyNameEnumerator()
+    {
+        return OBJECT_OFFSETOF(StructureRareData, m_cachedPropertyNameEnumerator);
+    }
+
     DECLARE_EXPORT_INFO;
 
     void finalizeUnconditionally(VM&);
 
+    void invalidateWatchpointBasedValidation();
+
 private:
     friend class Structure;
     friend class CachedSpecialPropertyAdaptiveStructureWatchpoint;
@@ -121,10 +133,13 @@
     bool canCacheSpecialProperty(CachedSpecialPropertyKey);
     void giveUpOnSpecialPropertyCache(CachedSpecialPropertyKey);
 
+    bool tryCachePropertyNameEnumeratorViaWatchpoint(VM&, StructureChain*);
+
     WriteBarrier<Structure> m_previous;
     // 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;
+    FixedVector<StructureChainInvalidationWatchpoint> m_cachedPropertyNameEnumeratorWatchpoints;
     WriteBarrier<JSImmutableButterfly> m_cachedPropertyNames[numberOfCachedPropertyNames] { };
 
     typedef HashMap<PropertyOffset, RefPtr<WatchpointSet>, WTF::IntHash<PropertyOffset>, WTF::UnsignedWithZeroKeyHashTraits<PropertyOffset>> PropertyWatchpointMap;

Modified: branches/safari-612-branch/Source/_javascript_Core/runtime/StructureRareDataInlines.h (282152 => 282153)


--- branches/safari-612-branch/Source/_javascript_Core/runtime/StructureRareDataInlines.h	2021-09-08 17:22:26 UTC (rev 282152)
+++ branches/safari-612-branch/Source/_javascript_Core/runtime/StructureRareDataInlines.h	2021-09-08 17:22:34 UTC (rev 282153)
@@ -28,6 +28,7 @@
 #include "JSImmutableButterfly.h"
 #include "JSPropertyNameEnumerator.h"
 #include "JSString.h"
+#include "StructureChain.h"
 #include "StructureRareData.h"
 
 namespace JSC {
@@ -47,6 +48,21 @@
     SpecialPropertyCacheEntry m_cache[numberOfCachedSpecialPropertyKeys];
 };
 
+class StructureChainInvalidationWatchpoint final : public Watchpoint {
+public:
+    StructureChainInvalidationWatchpoint()
+        : Watchpoint(Watchpoint::Type::StructureChainInvalidation)
+        , m_structureRareData(nullptr)
+    { }
+
+    void install(StructureRareData*, Structure*);
+    void fireInternal(VM&, const FireDetail&);
+
+private:
+    // Own destructor may not be called. Keep members trivially destructible.
+    JSC_WATCHPOINT_FIELD(PackedCellPtr<StructureRareData>, m_structureRareData);
+};
+
 inline void StructureRareData::setPreviousID(VM& vm, Structure* structure)
 {
     m_previous.set(vm, this, structure);
@@ -80,6 +96,9 @@
 inline void StructureRareData::setCachedPropertyNameEnumerator(VM& vm, JSPropertyNameEnumerator* enumerator)
 {
     m_cachedPropertyNameEnumerator.set(vm, this, enumerator);
+    m_cachedPropertyNameEnumeratorWatchpoints = FixedVector<StructureChainInvalidationWatchpoint>();
+    bool validatedViaWatchpoint = tryCachePropertyNameEnumeratorViaWatchpoint(vm, enumerator->cachedPrototypeChain());
+    enumerator->setValidatedViaWatchpoint(validatedViaWatchpoint);
 }
 
 inline JSImmutableButterfly* StructureRareData::cachedPropertyNames(CachedPropertyNamesKind kind) const
@@ -140,4 +159,44 @@
     return cacheSpecialPropertySlow(globalObject, vm, ownStructure, value, key, slot);
 }
 
+inline void StructureChainInvalidationWatchpoint::install(StructureRareData* structureRareData, Structure* structure)
+{
+    m_structureRareData = structureRareData;
+    structure->addTransitionWatchpoint(this);
+}
+
+inline void StructureChainInvalidationWatchpoint::fireInternal(VM&, const FireDetail&)
+{
+    if (!m_structureRareData->isLive())
+        return;
+    m_structureRareData->invalidateWatchpointBasedValidation();
+}
+
+inline bool StructureRareData::tryCachePropertyNameEnumeratorViaWatchpoint(VM& vm, StructureChain* chain)
+{
+    unsigned size = 0;
+    for (auto* current = chain->head(); *current; ++current) {
+        ++size;
+        StructureID structureID = *current;
+        Structure* structure = vm.getStructure(structureID);
+        if (!structure->propertyNameEnumeratorShouldWatch())
+            return false;
+    }
+    m_cachedPropertyNameEnumeratorWatchpoints = FixedVector<StructureChainInvalidationWatchpoint>(size);
+    unsigned index = 0;
+    for (auto* current = chain->head(); *current; ++current) {
+        StructureID structureID = *current;
+        Structure* structure = vm.getStructure(structureID);
+        m_cachedPropertyNameEnumeratorWatchpoints[index].install(this, structure);
+        ++index;
+    }
+    return true;
+}
+
+inline void StructureRareData::invalidateWatchpointBasedValidation()
+{
+    m_cachedPropertyNameEnumerator.clear();
+    m_cachedPropertyNameEnumeratorWatchpoints = FixedVector<StructureChainInvalidationWatchpoint>();
+}
+
 } // namespace JSC

Modified: branches/safari-612-branch/Source/_javascript_Core/tools/JSDollarVM.cpp (282152 => 282153)


--- branches/safari-612-branch/Source/_javascript_Core/tools/JSDollarVM.cpp	2021-09-08 17:22:26 UTC (rev 282152)
+++ branches/safari-612-branch/Source/_javascript_Core/tools/JSDollarVM.cpp	2021-09-08 17:22:34 UTC (rev 282153)
@@ -2118,6 +2118,7 @@
 static JSC_DECLARE_HOST_FUNCTION(functionIsMemoryLimited);
 static JSC_DECLARE_HOST_FUNCTION(functionUseJIT);
 static JSC_DECLARE_HOST_FUNCTION(functionIsGigacageEnabled);
+static JSC_DECLARE_HOST_FUNCTION(functionToCacheableDictionary);
 static JSC_DECLARE_HOST_FUNCTION(functionToUncacheableDictionary);
 static JSC_DECLARE_HOST_FUNCTION(functionIsPrivateSymbol);
 static JSC_DECLARE_HOST_FUNCTION(functionDumpAndResetPasDebugSpectrum);
@@ -3713,6 +3714,20 @@
     return JSValue::encode(jsBoolean(Gigacage::isEnabled()));
 }
 
+JSC_DEFINE_HOST_FUNCTION(functionToCacheableDictionary, (JSGlobalObject* globalObject, CallFrame* callFrame))
+{
+    DollarVMAssertScope assertScope;
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    JSObject* object = jsDynamicCast<JSObject*>(vm, callFrame->argument(0));
+    if (!object)
+        return throwVMTypeError(globalObject, scope, "Expected first argument to be an object"_s);
+    if (!object->structure(vm)->isUncacheableDictionary())
+        object->convertToDictionary(vm);
+    return JSValue::encode(object);
+}
+
 JSC_DEFINE_HOST_FUNCTION(functionToUncacheableDictionary, (JSGlobalObject* globalObject, CallFrame* callFrame))
 {
     DollarVMAssertScope assertScope;
@@ -3946,6 +3961,7 @@
     addFunction(vm, "useJIT", functionUseJIT, 0);
     addFunction(vm, "isGigacageEnabled", functionIsGigacageEnabled, 0);
 
+    addFunction(vm, "toCacheableDictionary", functionToCacheableDictionary, 1);
     addFunction(vm, "toUncacheableDictionary", functionToUncacheableDictionary, 1);
 
     addFunction(vm, "isPrivateSymbol", functionIsPrivateSymbol, 1);
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to