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