Title: [279746] branches/safari-612.1.23-branch
Revision
279746
Author
repst...@apple.com
Date
2021-07-08 13:27:16 -0700 (Thu, 08 Jul 2021)

Log Message

Cherry-pick r279604. rdar://problem/80340434

    [JSC] Optimize Object.assign and putDirectInternal
    https://bugs.webkit.org/show_bug.cgi?id=227677

    Reviewed by Filip Pizlo.

    JSTests:

    * microbenchmarks/object-assign-replace.js: Added.
    (test):
    * microbenchmarks/object-assign-transition.js: Added.
    (test):

    Source/_javascript_Core:

    This patch improves Object.assign performance (and this requires putDirectInternal improvement).

    1. Object.assign is handled by DFG / FTL as ObjectAssign node
    2. We found that putDirectInternal is suboptimal. This patch removes several bottlenecks. They are super critical from the measurement of
       microbenchmarks.
        2.1. Creating and destroying DeferredStructureTransitionWatchpointFire is not free. We should do that only when we need it.
        2.2. Before r277620, we are checking m_replacementWatchpointSets's nullptr and that was fast. But after that, we are always
             calling HashMap::get, and it is not inlined. This means that if we have StructureRareData, we are always calling HashMap::get
             even though there is no m_replacementWatchpointSets set. This patch adds HashMap::isNullStorage to avoid this call by inlinely
             check this via `LIKELY(m_replacementWatchpointSets.isNullStorage())`.
        2.3. Structure::addPropertyTransitionToExistingStructure has very cheap fast path and we must inline them to get performance benefit
             for major single-transition case.

    Added microbenchmarks show significant performance improvements. And Speedometer2 shows 0.6 - 1.0% improvement.

                                             ToT                     Patched

        object-assign-transition      105.2539+-0.2970     ^     88.8046+-0.2029        ^ definitely 1.1852x faster
        object-assign-replace         116.1568+-0.0905     ^     75.0673+-0.0658        ^ definitely 1.5474x faster

    * _javascript_Core.xcodeproj/project.pbxproj:
    * bytecode/Watchpoint.cpp:
    (JSC::DeferredWatchpointFire::fireAllSlow):
    (JSC::DeferredWatchpointFire::DeferredWatchpointFire): Deleted.
    (JSC::DeferredWatchpointFire::~DeferredWatchpointFire): Deleted.
    (JSC::DeferredWatchpointFire::fireAll): Deleted.
    * bytecode/Watchpoint.h:
    (JSC::DeferredWatchpointFire::DeferredWatchpointFire):
    (JSC::DeferredWatchpointFire::fireAll):
    (JSC::FireDetail::FireDetail): Deleted.
    (JSC::FireDetail::~FireDetail): Deleted.
    * dfg/DFGAbstractInterpreterInlines.h:
    (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
    * dfg/DFGByteCodeParser.cpp:
    (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
    * dfg/DFGClobberize.h:
    (JSC::DFG::clobberize):
    * dfg/DFGDoesGC.cpp:
    (JSC::DFG::doesGC):
    * dfg/DFGFixupPhase.cpp:
    (JSC::DFG::FixupPhase::fixupNode):
    * dfg/DFGMayExit.cpp:
    * dfg/DFGNodeType.h:
    * dfg/DFGOperations.cpp:
    (JSC::DFG::JSC_DEFINE_JIT_OPERATION):
    * dfg/DFGOperations.h:
    * dfg/DFGPredictionPropagationPhase.cpp:
    * dfg/DFGSafeToExecute.h:
    (JSC::DFG::safeToExecute):
    * dfg/DFGSpeculativeJIT.cpp:
    (JSC::DFG::SpeculativeJIT::compileObjectAssign):
    * dfg/DFGSpeculativeJIT.h:
    * dfg/DFGSpeculativeJIT32_64.cpp:
    (JSC::DFG::SpeculativeJIT::compile):
    * dfg/DFGSpeculativeJIT64.cpp:
    (JSC::DFG::SpeculativeJIT::compile):
    * ftl/FTLCapabilities.cpp:
    (JSC::FTL::canCompile):
    * ftl/FTLLowerDFGToB3.cpp:
    (JSC::FTL::DFG::LowerDFGToB3::compileNode):
    (JSC::FTL::DFG::LowerDFGToB3::compileObjectAssign):
    * runtime/Intrinsic.cpp:
    (JSC::intrinsicName):
    * runtime/Intrinsic.h:
    * runtime/JSObject.cpp:
    (JSC::JSObject::deleteProperty):
    * runtime/JSObjectInlines.h:
    (JSC::JSObject::putDirectInternal):
    * runtime/ObjectConstructor.cpp:
    (JSC::objectAssignGeneric):
    (JSC::JSC_DEFINE_HOST_FUNCTION):
    * runtime/ObjectConstructor.h:
    * runtime/ObjectConstructorInlines.h: Added.
    (JSC::canPerformFastPropertyEnumerationForObjectAssign):
    * runtime/Structure.cpp:
    (JSC::StructureTransitionTable::singleTransition const): Deleted.
    (JSC::StructureTransitionTable::get const): Deleted.
    (JSC::Structure::addPropertyTransitionToExistingStructureImpl): Deleted.
    (JSC::Structure::addPropertyTransitionToExistingStructure): Deleted.
    (JSC::Structure::addPropertyTransitionToExistingStructureConcurrently): Deleted.
    * runtime/Structure.h:
    * runtime/StructureInlines.h:
    (JSC::Structure::didReplaceProperty):
    (JSC::Structure::propertyReplacementWatchpointSet):
    (JSC::Structure::addPropertyTransitionToExistingStructureImpl):
    (JSC::Structure::addPropertyTransitionToExistingStructure):
    (JSC::Structure::addPropertyTransitionToExistingStructureConcurrently):
    (JSC::StructureTransitionTable::singleTransition const):
    (JSC::StructureTransitionTable::get const):

    Source/WTF:

    * wtf/HashMap.h:
    * wtf/HashSet.h:
    * wtf/HashTable.h:
    (WTF::HashTable::isNullStorage const):

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

Modified Paths

Added Paths

Diff

Modified: branches/safari-612.1.23-branch/JSTests/ChangeLog (279745 => 279746)


--- branches/safari-612.1.23-branch/JSTests/ChangeLog	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/JSTests/ChangeLog	2021-07-08 20:27:16 UTC (rev 279746)
@@ -1,3 +1,132 @@
+2021-07-08  Ruben Turcios  <rubent...@apple.com>
+
+        Cherry-pick r279604. rdar://problem/80340434
+
+    [JSC] Optimize Object.assign and putDirectInternal
+    https://bugs.webkit.org/show_bug.cgi?id=227677
+    
+    Reviewed by Filip Pizlo.
+    
+    JSTests:
+    
+    * microbenchmarks/object-assign-replace.js: Added.
+    (test):
+    * microbenchmarks/object-assign-transition.js: Added.
+    (test):
+    
+    Source/_javascript_Core:
+    
+    This patch improves Object.assign performance (and this requires putDirectInternal improvement).
+    
+    1. Object.assign is handled by DFG / FTL as ObjectAssign node
+    2. We found that putDirectInternal is suboptimal. This patch removes several bottlenecks. They are super critical from the measurement of
+       microbenchmarks.
+        2.1. Creating and destroying DeferredStructureTransitionWatchpointFire is not free. We should do that only when we need it.
+        2.2. Before r277620, we are checking m_replacementWatchpointSets's nullptr and that was fast. But after that, we are always
+             calling HashMap::get, and it is not inlined. This means that if we have StructureRareData, we are always calling HashMap::get
+             even though there is no m_replacementWatchpointSets set. This patch adds HashMap::isNullStorage to avoid this call by inlinely
+             check this via `LIKELY(m_replacementWatchpointSets.isNullStorage())`.
+        2.3. Structure::addPropertyTransitionToExistingStructure has very cheap fast path and we must inline them to get performance benefit
+             for major single-transition case.
+    
+    Added microbenchmarks show significant performance improvements. And Speedometer2 shows 0.6 - 1.0% improvement.
+    
+                                             ToT                     Patched
+    
+        object-assign-transition      105.2539+-0.2970     ^     88.8046+-0.2029        ^ definitely 1.1852x faster
+        object-assign-replace         116.1568+-0.0905     ^     75.0673+-0.0658        ^ definitely 1.5474x faster
+    
+    * _javascript_Core.xcodeproj/project.pbxproj:
+    * bytecode/Watchpoint.cpp:
+    (JSC::DeferredWatchpointFire::fireAllSlow):
+    (JSC::DeferredWatchpointFire::DeferredWatchpointFire): Deleted.
+    (JSC::DeferredWatchpointFire::~DeferredWatchpointFire): Deleted.
+    (JSC::DeferredWatchpointFire::fireAll): Deleted.
+    * bytecode/Watchpoint.h:
+    (JSC::DeferredWatchpointFire::DeferredWatchpointFire):
+    (JSC::DeferredWatchpointFire::fireAll):
+    (JSC::FireDetail::FireDetail): Deleted.
+    (JSC::FireDetail::~FireDetail): Deleted.
+    * dfg/DFGAbstractInterpreterInlines.h:
+    (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+    * dfg/DFGByteCodeParser.cpp:
+    (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+    * dfg/DFGClobberize.h:
+    (JSC::DFG::clobberize):
+    * dfg/DFGDoesGC.cpp:
+    (JSC::DFG::doesGC):
+    * dfg/DFGFixupPhase.cpp:
+    (JSC::DFG::FixupPhase::fixupNode):
+    * dfg/DFGMayExit.cpp:
+    * dfg/DFGNodeType.h:
+    * dfg/DFGOperations.cpp:
+    (JSC::DFG::JSC_DEFINE_JIT_OPERATION):
+    * dfg/DFGOperations.h:
+    * dfg/DFGPredictionPropagationPhase.cpp:
+    * dfg/DFGSafeToExecute.h:
+    (JSC::DFG::safeToExecute):
+    * dfg/DFGSpeculativeJIT.cpp:
+    (JSC::DFG::SpeculativeJIT::compileObjectAssign):
+    * dfg/DFGSpeculativeJIT.h:
+    * dfg/DFGSpeculativeJIT32_64.cpp:
+    (JSC::DFG::SpeculativeJIT::compile):
+    * dfg/DFGSpeculativeJIT64.cpp:
+    (JSC::DFG::SpeculativeJIT::compile):
+    * ftl/FTLCapabilities.cpp:
+    (JSC::FTL::canCompile):
+    * ftl/FTLLowerDFGToB3.cpp:
+    (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+    (JSC::FTL::DFG::LowerDFGToB3::compileObjectAssign):
+    * runtime/Intrinsic.cpp:
+    (JSC::intrinsicName):
+    * runtime/Intrinsic.h:
+    * runtime/JSObject.cpp:
+    (JSC::JSObject::deleteProperty):
+    * runtime/JSObjectInlines.h:
+    (JSC::JSObject::putDirectInternal):
+    * runtime/ObjectConstructor.cpp:
+    (JSC::objectAssignGeneric):
+    (JSC::JSC_DEFINE_HOST_FUNCTION):
+    * runtime/ObjectConstructor.h:
+    * runtime/ObjectConstructorInlines.h: Added.
+    (JSC::canPerformFastPropertyEnumerationForObjectAssign):
+    * runtime/Structure.cpp:
+    (JSC::StructureTransitionTable::singleTransition const): Deleted.
+    (JSC::StructureTransitionTable::get const): Deleted.
+    (JSC::Structure::addPropertyTransitionToExistingStructureImpl): Deleted.
+    (JSC::Structure::addPropertyTransitionToExistingStructure): Deleted.
+    (JSC::Structure::addPropertyTransitionToExistingStructureConcurrently): Deleted.
+    * runtime/Structure.h:
+    * runtime/StructureInlines.h:
+    (JSC::Structure::didReplaceProperty):
+    (JSC::Structure::propertyReplacementWatchpointSet):
+    (JSC::Structure::addPropertyTransitionToExistingStructureImpl):
+    (JSC::Structure::addPropertyTransitionToExistingStructure):
+    (JSC::Structure::addPropertyTransitionToExistingStructureConcurrently):
+    (JSC::StructureTransitionTable::singleTransition const):
+    (JSC::StructureTransitionTable::get const):
+    
+    Source/WTF:
+    
+    * wtf/HashMap.h:
+    * wtf/HashSet.h:
+    * wtf/HashTable.h:
+    (WTF::HashTable::isNullStorage const):
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@279604 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2021-07-05  Yusuke Suzuki  <ysuz...@apple.com>
+
+            [JSC] Optimize Object.assign and putDirectInternal
+            https://bugs.webkit.org/show_bug.cgi?id=227677
+
+            Reviewed by Filip Pizlo.
+
+            * microbenchmarks/object-assign-replace.js: Added.
+            (test):
+            * microbenchmarks/object-assign-transition.js: Added.
+            (test):
+
 2021-07-03  Alexey Shvayka  <shvaikal...@gmail.com>
 
         [WebIDL] Rework runtime enabled properties leveraging PropertyCallback

Added: branches/safari-612.1.23-branch/JSTests/microbenchmarks/object-assign-replace.js (0 => 279746)


--- branches/safari-612.1.23-branch/JSTests/microbenchmarks/object-assign-replace.js	                        (rev 0)
+++ branches/safari-612.1.23-branch/JSTests/microbenchmarks/object-assign-replace.js	2021-07-08 20:27:16 UTC (rev 279746)
@@ -0,0 +1,12 @@
+function test(target, source)
+{
+    Object.assign(target, source);
+}
+noInline(test);
+
+var target = {};
+var source = {
+    a:42, b:42, c:42
+};
+for (var i = 0; i < 2e6; ++i)
+    test(target, source);

Added: branches/safari-612.1.23-branch/JSTests/microbenchmarks/object-assign-transition.js (0 => 279746)


--- branches/safari-612.1.23-branch/JSTests/microbenchmarks/object-assign-transition.js	                        (rev 0)
+++ branches/safari-612.1.23-branch/JSTests/microbenchmarks/object-assign-transition.js	2021-07-08 20:27:16 UTC (rev 279746)
@@ -0,0 +1,11 @@
+function test(target, source)
+{
+    Object.assign(target, source);
+}
+noInline(test);
+
+var source = {
+    a:42, b:42, c:42
+};
+for (var i = 0; i < 2e6; ++i)
+    test({}, source);

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/ChangeLog (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/ChangeLog	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/ChangeLog	2021-07-08 20:27:16 UTC (rev 279746)
@@ -1,3 +1,217 @@
+2021-07-08  Ruben Turcios  <rubent...@apple.com>
+
+        Cherry-pick r279604. rdar://problem/80340434
+
+    [JSC] Optimize Object.assign and putDirectInternal
+    https://bugs.webkit.org/show_bug.cgi?id=227677
+    
+    Reviewed by Filip Pizlo.
+    
+    JSTests:
+    
+    * microbenchmarks/object-assign-replace.js: Added.
+    (test):
+    * microbenchmarks/object-assign-transition.js: Added.
+    (test):
+    
+    Source/_javascript_Core:
+    
+    This patch improves Object.assign performance (and this requires putDirectInternal improvement).
+    
+    1. Object.assign is handled by DFG / FTL as ObjectAssign node
+    2. We found that putDirectInternal is suboptimal. This patch removes several bottlenecks. They are super critical from the measurement of
+       microbenchmarks.
+        2.1. Creating and destroying DeferredStructureTransitionWatchpointFire is not free. We should do that only when we need it.
+        2.2. Before r277620, we are checking m_replacementWatchpointSets's nullptr and that was fast. But after that, we are always
+             calling HashMap::get, and it is not inlined. This means that if we have StructureRareData, we are always calling HashMap::get
+             even though there is no m_replacementWatchpointSets set. This patch adds HashMap::isNullStorage to avoid this call by inlinely
+             check this via `LIKELY(m_replacementWatchpointSets.isNullStorage())`.
+        2.3. Structure::addPropertyTransitionToExistingStructure has very cheap fast path and we must inline them to get performance benefit
+             for major single-transition case.
+    
+    Added microbenchmarks show significant performance improvements. And Speedometer2 shows 0.6 - 1.0% improvement.
+    
+                                             ToT                     Patched
+    
+        object-assign-transition      105.2539+-0.2970     ^     88.8046+-0.2029        ^ definitely 1.1852x faster
+        object-assign-replace         116.1568+-0.0905     ^     75.0673+-0.0658        ^ definitely 1.5474x faster
+    
+    * _javascript_Core.xcodeproj/project.pbxproj:
+    * bytecode/Watchpoint.cpp:
+    (JSC::DeferredWatchpointFire::fireAllSlow):
+    (JSC::DeferredWatchpointFire::DeferredWatchpointFire): Deleted.
+    (JSC::DeferredWatchpointFire::~DeferredWatchpointFire): Deleted.
+    (JSC::DeferredWatchpointFire::fireAll): Deleted.
+    * bytecode/Watchpoint.h:
+    (JSC::DeferredWatchpointFire::DeferredWatchpointFire):
+    (JSC::DeferredWatchpointFire::fireAll):
+    (JSC::FireDetail::FireDetail): Deleted.
+    (JSC::FireDetail::~FireDetail): Deleted.
+    * dfg/DFGAbstractInterpreterInlines.h:
+    (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+    * dfg/DFGByteCodeParser.cpp:
+    (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+    * dfg/DFGClobberize.h:
+    (JSC::DFG::clobberize):
+    * dfg/DFGDoesGC.cpp:
+    (JSC::DFG::doesGC):
+    * dfg/DFGFixupPhase.cpp:
+    (JSC::DFG::FixupPhase::fixupNode):
+    * dfg/DFGMayExit.cpp:
+    * dfg/DFGNodeType.h:
+    * dfg/DFGOperations.cpp:
+    (JSC::DFG::JSC_DEFINE_JIT_OPERATION):
+    * dfg/DFGOperations.h:
+    * dfg/DFGPredictionPropagationPhase.cpp:
+    * dfg/DFGSafeToExecute.h:
+    (JSC::DFG::safeToExecute):
+    * dfg/DFGSpeculativeJIT.cpp:
+    (JSC::DFG::SpeculativeJIT::compileObjectAssign):
+    * dfg/DFGSpeculativeJIT.h:
+    * dfg/DFGSpeculativeJIT32_64.cpp:
+    (JSC::DFG::SpeculativeJIT::compile):
+    * dfg/DFGSpeculativeJIT64.cpp:
+    (JSC::DFG::SpeculativeJIT::compile):
+    * ftl/FTLCapabilities.cpp:
+    (JSC::FTL::canCompile):
+    * ftl/FTLLowerDFGToB3.cpp:
+    (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+    (JSC::FTL::DFG::LowerDFGToB3::compileObjectAssign):
+    * runtime/Intrinsic.cpp:
+    (JSC::intrinsicName):
+    * runtime/Intrinsic.h:
+    * runtime/JSObject.cpp:
+    (JSC::JSObject::deleteProperty):
+    * runtime/JSObjectInlines.h:
+    (JSC::JSObject::putDirectInternal):
+    * runtime/ObjectConstructor.cpp:
+    (JSC::objectAssignGeneric):
+    (JSC::JSC_DEFINE_HOST_FUNCTION):
+    * runtime/ObjectConstructor.h:
+    * runtime/ObjectConstructorInlines.h: Added.
+    (JSC::canPerformFastPropertyEnumerationForObjectAssign):
+    * runtime/Structure.cpp:
+    (JSC::StructureTransitionTable::singleTransition const): Deleted.
+    (JSC::StructureTransitionTable::get const): Deleted.
+    (JSC::Structure::addPropertyTransitionToExistingStructureImpl): Deleted.
+    (JSC::Structure::addPropertyTransitionToExistingStructure): Deleted.
+    (JSC::Structure::addPropertyTransitionToExistingStructureConcurrently): Deleted.
+    * runtime/Structure.h:
+    * runtime/StructureInlines.h:
+    (JSC::Structure::didReplaceProperty):
+    (JSC::Structure::propertyReplacementWatchpointSet):
+    (JSC::Structure::addPropertyTransitionToExistingStructureImpl):
+    (JSC::Structure::addPropertyTransitionToExistingStructure):
+    (JSC::Structure::addPropertyTransitionToExistingStructureConcurrently):
+    (JSC::StructureTransitionTable::singleTransition const):
+    (JSC::StructureTransitionTable::get const):
+    
+    Source/WTF:
+    
+    * wtf/HashMap.h:
+    * wtf/HashSet.h:
+    * wtf/HashTable.h:
+    (WTF::HashTable::isNullStorage const):
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@279604 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2021-07-05  Yusuke Suzuki  <ysuz...@apple.com>
+
+            [JSC] Optimize Object.assign and putDirectInternal
+            https://bugs.webkit.org/show_bug.cgi?id=227677
+
+            Reviewed by Filip Pizlo.
+
+            This patch improves Object.assign performance (and this requires putDirectInternal improvement).
+
+            1. Object.assign is handled by DFG / FTL as ObjectAssign node
+            2. We found that putDirectInternal is suboptimal. This patch removes several bottlenecks. They are super critical from the measurement of
+               microbenchmarks.
+                2.1. Creating and destroying DeferredStructureTransitionWatchpointFire is not free. We should do that only when we need it.
+                2.2. Before r277620, we are checking m_replacementWatchpointSets's nullptr and that was fast. But after that, we are always
+                     calling HashMap::get, and it is not inlined. This means that if we have StructureRareData, we are always calling HashMap::get
+                     even though there is no m_replacementWatchpointSets set. This patch adds HashMap::isNullStorage to avoid this call by inlinely
+                     check this via `LIKELY(m_replacementWatchpointSets.isNullStorage())`.
+                2.3. Structure::addPropertyTransitionToExistingStructure has very cheap fast path and we must inline them to get performance benefit
+                     for major single-transition case.
+
+            Added microbenchmarks show significant performance improvements. And Speedometer2 shows 0.6 - 1.0% improvement.
+
+                                                     ToT                     Patched
+
+                object-assign-transition      105.2539+-0.2970     ^     88.8046+-0.2029        ^ definitely 1.1852x faster
+                object-assign-replace         116.1568+-0.0905     ^     75.0673+-0.0658        ^ definitely 1.5474x faster
+
+            * _javascript_Core.xcodeproj/project.pbxproj:
+            * bytecode/Watchpoint.cpp:
+            (JSC::DeferredWatchpointFire::fireAllSlow):
+            (JSC::DeferredWatchpointFire::DeferredWatchpointFire): Deleted.
+            (JSC::DeferredWatchpointFire::~DeferredWatchpointFire): Deleted.
+            (JSC::DeferredWatchpointFire::fireAll): Deleted.
+            * bytecode/Watchpoint.h:
+            (JSC::DeferredWatchpointFire::DeferredWatchpointFire):
+            (JSC::DeferredWatchpointFire::fireAll):
+            (JSC::FireDetail::FireDetail): Deleted.
+            (JSC::FireDetail::~FireDetail): Deleted.
+            * dfg/DFGAbstractInterpreterInlines.h:
+            (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+            * dfg/DFGByteCodeParser.cpp:
+            (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+            * dfg/DFGClobberize.h:
+            (JSC::DFG::clobberize):
+            * dfg/DFGDoesGC.cpp:
+            (JSC::DFG::doesGC):
+            * dfg/DFGFixupPhase.cpp:
+            (JSC::DFG::FixupPhase::fixupNode):
+            * dfg/DFGMayExit.cpp:
+            * dfg/DFGNodeType.h:
+            * dfg/DFGOperations.cpp:
+            (JSC::DFG::JSC_DEFINE_JIT_OPERATION):
+            * dfg/DFGOperations.h:
+            * dfg/DFGPredictionPropagationPhase.cpp:
+            * dfg/DFGSafeToExecute.h:
+            (JSC::DFG::safeToExecute):
+            * dfg/DFGSpeculativeJIT.cpp:
+            (JSC::DFG::SpeculativeJIT::compileObjectAssign):
+            * dfg/DFGSpeculativeJIT.h:
+            * dfg/DFGSpeculativeJIT32_64.cpp:
+            (JSC::DFG::SpeculativeJIT::compile):
+            * dfg/DFGSpeculativeJIT64.cpp:
+            (JSC::DFG::SpeculativeJIT::compile):
+            * ftl/FTLCapabilities.cpp:
+            (JSC::FTL::canCompile):
+            * ftl/FTLLowerDFGToB3.cpp:
+            (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+            (JSC::FTL::DFG::LowerDFGToB3::compileObjectAssign):
+            * runtime/Intrinsic.cpp:
+            (JSC::intrinsicName):
+            * runtime/Intrinsic.h:
+            * runtime/JSObject.cpp:
+            (JSC::JSObject::deleteProperty):
+            * runtime/JSObjectInlines.h:
+            (JSC::JSObject::putDirectInternal):
+            * runtime/ObjectConstructor.cpp:
+            (JSC::objectAssignGeneric):
+            (JSC::JSC_DEFINE_HOST_FUNCTION):
+            * runtime/ObjectConstructor.h:
+            * runtime/ObjectConstructorInlines.h: Added.
+            (JSC::canPerformFastPropertyEnumerationForObjectAssign):
+            * runtime/Structure.cpp:
+            (JSC::StructureTransitionTable::singleTransition const): Deleted.
+            (JSC::StructureTransitionTable::get const): Deleted.
+            (JSC::Structure::addPropertyTransitionToExistingStructureImpl): Deleted.
+            (JSC::Structure::addPropertyTransitionToExistingStructure): Deleted.
+            (JSC::Structure::addPropertyTransitionToExistingStructureConcurrently): Deleted.
+            * runtime/Structure.h:
+            * runtime/StructureInlines.h:
+            (JSC::Structure::didReplaceProperty):
+            (JSC::Structure::propertyReplacementWatchpointSet):
+            (JSC::Structure::addPropertyTransitionToExistingStructureImpl):
+            (JSC::Structure::addPropertyTransitionToExistingStructure):
+            (JSC::Structure::addPropertyTransitionToExistingStructureConcurrently):
+            (JSC::StructureTransitionTable::singleTransition const):
+            (JSC::StructureTransitionTable::get const):
+
 2021-07-04  Robin Morisset  <rmoris...@apple.com>
 
         ActiveScratchBufferScope should take the buffer as argument

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2021-07-08 20:27:16 UTC (rev 279746)
@@ -1887,6 +1887,7 @@
 		E36CC9472086314F0051FFD6 /* WasmCreationMode.h in Headers */ = {isa = PBXBuildFile; fileRef = E36CC9462086314F0051FFD6 /* WasmCreationMode.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		E36EDCE524F0975700E60DA2 /* Concurrency.h in Headers */ = {isa = PBXBuildFile; fileRef = E36EDCE424F0975700E60DA2 /* Concurrency.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		E3711992253FA87F00BA69A0 /* Gate.h in Headers */ = {isa = PBXBuildFile; fileRef = E3711991253FA87E00BA69A0 /* Gate.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		E374166E26912BC700C80789 /* ObjectConstructorInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = E374166D26912BC700C80789 /* ObjectConstructorInlines.h */; };
 		E3750CC82502E87E006A0AAB /* IntlDateTimeFormatInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = E3750CC72502E87E006A0AAB /* IntlDateTimeFormatInlines.h */; };
 		E3794E761B77EB97005543AE /* ModuleAnalyzer.h in Headers */ = {isa = PBXBuildFile; fileRef = E3794E741B77EB97005543AE /* ModuleAnalyzer.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		E383500A2390D93B0036316D /* WasmGlobal.h in Headers */ = {isa = PBXBuildFile; fileRef = E38350092390D9370036316D /* WasmGlobal.h */; };
@@ -5149,6 +5150,7 @@
 		E36CC9462086314F0051FFD6 /* WasmCreationMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmCreationMode.h; sourceTree = "<group>"; };
 		E36EDCE424F0975700E60DA2 /* Concurrency.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Concurrency.h; sourceTree = "<group>"; };
 		E3711991253FA87E00BA69A0 /* Gate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Gate.h; sourceTree = "<group>"; };
+		E374166D26912BC700C80789 /* ObjectConstructorInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectConstructorInlines.h; sourceTree = "<group>"; };
 		E3750CC72502E87E006A0AAB /* IntlDateTimeFormatInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IntlDateTimeFormatInlines.h; sourceTree = "<group>"; };
 		E3794E731B77EB97005543AE /* ModuleAnalyzer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ModuleAnalyzer.cpp; sourceTree = "<group>"; };
 		E3794E741B77EB97005543AE /* ModuleAnalyzer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModuleAnalyzer.h; sourceTree = "<group>"; };
@@ -7797,6 +7799,7 @@
 				142D3938103E4560007DCB52 /* NumericStrings.h */,
 				BC2680C60E16D4E900A06E92 /* ObjectConstructor.cpp */,
 				BC2680C70E16D4E900A06E92 /* ObjectConstructor.h */,
+				E374166D26912BC700C80789 /* ObjectConstructorInlines.h */,
 				FE48E6371EB118AD005D7A96 /* ObjectInitializationScope.cpp */,
 				FE48E6361EB1188F005D7A96 /* ObjectInitializationScope.h */,
 				E3C295DC1ED2CBAA00D3016F /* ObjectPropertyChangeAdaptiveWatchpoint.h */,
@@ -10415,6 +10418,7 @@
 				79AC30FF1F99536400484FD7 /* ObjectAllocationProfileInlines.h in Headers */,
 				BC18C4450E16F5CD00B34460 /* ObjectConstructor.h in Headers */,
 				996B73221BDA08EF00331B84 /* ObjectConstructor.lut.h in Headers */,
+				E374166E26912BC700C80789 /* ObjectConstructorInlines.h in Headers */,
 				FE48E6381EB118D2005D7A96 /* ObjectInitializationScope.h in Headers */,
 				E3C295DD1ED2CBDA00D3016F /* ObjectPropertyChangeAdaptiveWatchpoint.h in Headers */,
 				0FD3E40A1B618B6600C80E1E /* ObjectPropertyCondition.h in Headers */,

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/bytecode/Watchpoint.cpp (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/bytecode/Watchpoint.cpp	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/bytecode/Watchpoint.cpp	2021-07-08 20:27:16 UTC (rev 279746)
@@ -194,22 +194,11 @@
     fat()->deref();
 }
 
-DeferredWatchpointFire::DeferredWatchpointFire(VM& vm)
-    : m_vm(vm)
-    , m_watchpointsToFire(ClearWatchpoint)
+void DeferredWatchpointFire::fireAllSlow()
 {
+    m_watchpointsToFire.fireAll(m_vm, *this);
 }
 
-DeferredWatchpointFire::~DeferredWatchpointFire()
-{
-}
-
-void DeferredWatchpointFire::fireAll()
-{
-    if (m_watchpointsToFire.state() == IsWatched)
-        m_watchpointsToFire.fireAll(m_vm, *this);
-}
-
 void DeferredWatchpointFire::takeWatchpointsToFire(WatchpointSet* watchpointsToFire)
 {
     ASSERT(m_watchpointsToFire.state() == ClearWatchpoint);

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/bytecode/Watchpoint.h (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/bytecode/Watchpoint.h	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/bytecode/Watchpoint.h	2021-07-08 20:27:16 UTC (rev 279746)
@@ -46,14 +46,8 @@
     void* operator new(size_t) = delete;
     
 public:
-    FireDetail()
-    {
-    }
-    
-    virtual ~FireDetail()
-    {
-    }
-    
+    FireDetail() = default;
+    virtual ~FireDetail() = default;
     virtual void dump(PrintStream&) const = 0;
 };
 
@@ -534,14 +528,22 @@
 class DeferredWatchpointFire : public FireDetail {
     WTF_MAKE_NONCOPYABLE(DeferredWatchpointFire);
 public:
-    JS_EXPORT_PRIVATE DeferredWatchpointFire(VM&);
-    JS_EXPORT_PRIVATE ~DeferredWatchpointFire() override;
+    DeferredWatchpointFire(VM& vm)
+        : m_vm(vm)
+        , m_watchpointsToFire(ClearWatchpoint)
+    {
+    }
 
     JS_EXPORT_PRIVATE void takeWatchpointsToFire(WatchpointSet*);
-    JS_EXPORT_PRIVATE void fireAll();
+    void fireAll()
+    {
+        if (m_watchpointsToFire.state() == IsWatched)
+            fireAllSlow();
+    }
 
-    void dump(PrintStream& out) const override = 0;
 private:
+    JS_EXPORT_PRIVATE void fireAllSlow();
+
     VM& m_vm;
     WatchpointSet m_watchpointsToFire;
 };

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2021-07-08 20:27:16 UTC (rev 279746)
@@ -3123,6 +3123,11 @@
         setForNode(node, node->structure());
         break;
 
+    case ObjectAssign: {
+        clobberWorld();
+        break;
+    }
+
     case ObjectCreate: {
         if (JSValue base = forNode(node->child1()).m_value) {
             JSGlobalObject* globalObject = m_graph.globalObjectFor(node->origin.semantic);

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2021-07-08 20:27:16 UTC (rev 279746)
@@ -2930,6 +2930,27 @@
             return true;
         }
 
+        case ObjectAssignIntrinsic: {
+            if (argumentCountIncludingThis != 3)
+                return false;
+
+            insertChecks();
+
+            // ToObject is idempotent if it succeeds. Plus, it is non-observable except for the case that an exception is thrown. And when the exception is thrown,
+            // we exit from DFG / FTL. Plus, we keep ordering of these two ToObject because clobberizing rule says clobberTop. So,
+            // we can say exitOK for each ToObject.
+            unsigned errorStringIndex = UINT32_MAX;
+            Node* target = addToGraph(ToObject, OpInfo(errorStringIndex), OpInfo(SpecNone), get(virtualRegisterForArgumentIncludingThis(1, registerOffset)));
+            m_exitOK = true;
+            addToGraph(ExitOK);
+            Node* source = addToGraph(ToObject, OpInfo(errorStringIndex), OpInfo(SpecNone), get(virtualRegisterForArgumentIncludingThis(2, registerOffset)));
+            m_exitOK = true;
+            addToGraph(ExitOK);
+            addToGraph(ObjectAssign, Edge(target, KnownCellUse), Edge(source, KnownCellUse));
+            setResult(target);
+            return true;
+        }
+
         case ObjectGetPrototypeOfIntrinsic: {
             if (argumentCountIncludingThis < 2)
                 return false;

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGClobberize.h (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGClobberize.h	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGClobberize.h	2021-07-08 20:27:16 UTC (rev 279746)
@@ -1679,6 +1679,11 @@
         return;
     }
 
+    case ObjectAssign: {
+        clobberTop();
+        return;
+    }
+
     case ObjectCreate: {
         switch (node->child1().useKind()) {
         case ObjectUse:

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGDoesGC.cpp (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGDoesGC.cpp	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGDoesGC.cpp	2021-07-08 20:27:16 UTC (rev 279746)
@@ -364,6 +364,7 @@
     case CreatePromise:
     case CreateGenerator:
     case CreateAsyncGenerator:
+    case ObjectAssign:
     case ObjectCreate:
     case ObjectKeys:
     case ObjectGetOwnPropertyNames:

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2021-07-08 20:27:16 UTC (rev 279746)
@@ -2892,6 +2892,7 @@
         case FilterSetPrivateBrandStatus:
         case InvalidationPoint:
         case CreateArgumentsButterfly:
+        case ObjectAssign:
             break;
 #else // not ASSERT_ENABLED
         default:

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGMayExit.cpp (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGMayExit.cpp	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGMayExit.cpp	2021-07-08 20:27:16 UTC (rev 279746)
@@ -135,6 +135,7 @@
     case NewRegexp:
     case ToNumber:
     case ToNumeric:
+    case ToObject:
     case RegExpExecNonGlobalOrSticky:
     case RegExpMatchFastGlobal:
         result = ExitsForExceptions;

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGNodeType.h (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGNodeType.h	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGNodeType.h	2021-07-08 20:27:16 UTC (rev 279746)
@@ -292,6 +292,7 @@
     macro(CheckNotJSCast, NodeMustGenerate) \
     macro(ParseInt, NodeMustGenerate | NodeResultJS) \
     macro(GetPrototypeOf, NodeMustGenerate | NodeResultJS) \
+    macro(ObjectAssign, NodeMustGenerate) \
     macro(ObjectCreate, NodeMustGenerate | NodeResultJS) \
     macro(ObjectKeys, NodeMustGenerate | NodeResultJS) \
     macro(ObjectGetOwnPropertyNames, NodeMustGenerate | NodeResultJS) \

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGOperations.cpp (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGOperations.cpp	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGOperations.cpp	2021-07-08 20:27:16 UTC (rev 279746)
@@ -63,7 +63,7 @@
 #include "JSWeakMapInlines.h"
 #include "JSWeakSet.h"
 #include "NumberConstructor.h"
-#include "ObjectConstructor.h"
+#include "ObjectConstructorInlines.h"
 #include "Operations.h"
 #include "ParseInt.h"
 #include "RegExpGlobalDataInlines.h"
@@ -304,6 +304,68 @@
     return constructEmptyObject(globalObject, prototype);
 }
 
+JSC_DEFINE_JIT_OPERATION(operationObjectAssignObject, void, (JSGlobalObject* globalObject, JSObject* target, JSObject* source))
+{
+    VM& vm = globalObject->vm();
+    CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
+    JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    bool targetCanPerformFastPut = jsDynamicCast<JSFinalObject*>(vm, target) && target->canPerformFastPutInlineExcludingProto(vm) && target->isStructureExtensible(vm);
+
+    if (targetCanPerformFastPut) {
+        Vector<RefPtr<UniquedStringImpl>, 8> properties;
+        MarkedArgumentBuffer values;
+        if (!source->staticPropertiesReified(vm)) {
+            source->reifyAllStaticProperties(globalObject);
+            RETURN_IF_EXCEPTION(scope, void());
+        }
+
+        if (canPerformFastPropertyEnumerationForObjectAssign(source->structure(vm))) {
+            // |source| Structure does not have any getters. And target can perform fast put.
+            // So enumerating properties and putting properties are non observable.
+
+            // FIXME: It doesn't seem like we should have to do this in two phases, but
+            // we're running into crashes where it appears that source is transitioning
+            // under us, and even ends up in a state where it has a null butterfly. My
+            // leading hypothesis here is that we fire some value replacement watchpoint
+            // that ends up transitioning the structure underneath us.
+            // https://bugs.webkit.org/show_bug.cgi?id=187837
+
+            // FIXME: This fast path is very similar to ObjectConstructor' one. But extracting it to a function caused performance
+            // regression in object-assign-replace. Since the code is small and fast path, we keep both.
+
+            // Do not clear since Vector::clear shrinks the backing store.
+            properties.resize(0);
+            values.clear();
+            source->structure(vm)->forEachProperty(vm, [&] (const PropertyMapEntry& entry) -> bool {
+                if (entry.attributes & PropertyAttribute::DontEnum)
+                    return true;
+
+                PropertyName propertyName(entry.key);
+                if (propertyName.isPrivateName())
+                    return true;
+
+                properties.append(entry.key);
+                values.appendWithCrashOnOverflow(source->getDirect(entry.offset));
+
+                return true;
+            });
+
+            for (size_t i = 0; i < properties.size(); ++i) {
+                // FIXME: We could put properties in a batching manner to accelerate Object.assign more.
+                // https://bugs.webkit.org/show_bug.cgi?id=185358
+                PutPropertySlot putPropertySlot(target, true);
+                target->putOwnDataProperty(vm, properties[i].get(), values.at(i), putPropertySlot);
+            }
+            return;
+        }
+    }
+
+    scope.release();
+    objectAssignGeneric(globalObject, vm, target, source);
+}
+
 JSC_DEFINE_JIT_OPERATION(operationCreateThis, JSCell*, (JSGlobalObject* globalObject, JSObject* constructor, uint32_t inlineCapacity))
 {
     VM& vm = globalObject->vm();

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGOperations.h (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGOperations.h	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGOperations.h	2021-07-08 20:27:16 UTC (rev 279746)
@@ -53,6 +53,7 @@
 JSC_DECLARE_JIT_OPERATION(operationObjectGetOwnPropertyNamesObject, JSArray*, (JSGlobalObject*, JSObject*));
 JSC_DECLARE_JIT_OPERATION(operationObjectCreate, JSCell*, (JSGlobalObject*, EncodedJSValue));
 JSC_DECLARE_JIT_OPERATION(operationObjectCreateObject, JSCell*, (JSGlobalObject*, JSObject*));
+JSC_DECLARE_JIT_OPERATION(operationObjectAssignObject, void, (JSGlobalObject*, JSObject*, JSObject*));
 JSC_DECLARE_JIT_OPERATION(operationCreateThis, JSCell*, (JSGlobalObject*, JSObject* constructor, uint32_t inlineCapacity));
 JSC_DECLARE_JIT_OPERATION(operationCreatePromise, JSCell*, (JSGlobalObject*, JSObject* constructor));
 JSC_DECLARE_JIT_OPERATION(operationCreateInternalPromise, JSCell*, (JSGlobalObject*, JSObject* constructor));

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2021-07-08 20:27:16 UTC (rev 279746)
@@ -1459,6 +1459,7 @@
         case ClearCatchLocals:
         case DataViewSet:
         case InvalidationPoint:
+        case ObjectAssign:
             break;
             
         // This gets ignored because it only pretends to produce a value.

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGSafeToExecute.h (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGSafeToExecute.h	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGSafeToExecute.h	2021-07-08 20:27:16 UTC (rev 279746)
@@ -501,6 +501,7 @@
     case CreatePromise:
     case CreateGenerator:
     case CreateAsyncGenerator:
+    case ObjectAssign:
     case ObjectCreate:
     case ObjectKeys:
     case ObjectGetOwnPropertyNames:

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2021-07-08 20:27:16 UTC (rev 279746)
@@ -14049,6 +14049,21 @@
     }
 }
 
+void SpeculativeJIT::compileObjectAssign(Node* node)
+{
+    SpeculateCellOperand target(this, node->child1());
+    SpeculateCellOperand source(this, node->child2());
+
+    GPRReg targetGPR = target.gpr();
+    GPRReg sourceGPR = source.gpr();
+
+    flushRegisters();
+    callOperation(operationObjectAssignObject, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), targetGPR, sourceGPR);
+    m_jit.exceptionCheck();
+
+    noResult(node);
+}
+
 void SpeculativeJIT::compileObjectCreate(Node* node)
 {
     switch (node->child1().useKind()) {

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2021-07-08 20:27:16 UTC (rev 279746)
@@ -1552,6 +1552,7 @@
     void compileNewTypedArray(Node*);
     void compileToThis(Node*);
     void compileObjectKeysOrObjectGetOwnPropertyNames(Node*);
+    void compileObjectAssign(Node*);
     void compileObjectCreate(Node*);
     void compileCreateThis(Node*);
     void compileCreatePromise(Node*);

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2021-07-08 20:27:16 UTC (rev 279746)
@@ -3268,6 +3268,11 @@
         break;
     }
 
+    case ObjectAssign: {
+        compileObjectAssign(node);
+        break;
+    }
+
     case ObjectCreate: {
         compileObjectCreate(node);
         break;

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2021-07-08 20:27:16 UTC (rev 279746)
@@ -3984,6 +3984,11 @@
         break;
     }
 
+    case ObjectAssign: {
+        compileObjectAssign(node);
+        break;
+    }
+
     case ObjectCreate: {
         compileObjectCreate(node);
         break;

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/ftl/FTLCapabilities.cpp (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2021-07-08 20:27:16 UTC (rev 279746)
@@ -222,6 +222,7 @@
     case CallObjectConstructor:
     case CallStringConstructor:
     case CallNumberConstructor:
+    case ObjectAssign:
     case ObjectCreate:
     case ObjectKeys:
     case ObjectGetOwnPropertyNames:

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2021-07-08 20:27:16 UTC (rev 279746)
@@ -1113,6 +1113,9 @@
         case CreateArgumentsButterfly:
             compileCreateArgumentsButterfly();
             break;
+        case ObjectAssign:
+            compileObjectAssign();
+            break;
         case ObjectCreate:
             compileObjectCreate();
             break;
@@ -7076,6 +7079,12 @@
         }
     }
 
+    void compileObjectAssign()
+    {
+        JSGlobalObject* globalObject = m_graph.globalObjectFor(m_origin.semantic);
+        vmCall(Void, operationObjectAssignObject, weakPointer(globalObject), lowCell(m_node->child1()), lowCell(m_node->child2()));
+    }
+
     void compileObjectCreate()
     {
         JSGlobalObject* globalObject = m_graph.globalObjectFor(m_origin.semantic);

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/Intrinsic.cpp (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/Intrinsic.cpp	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/Intrinsic.cpp	2021-07-08 20:27:16 UTC (rev 279746)
@@ -157,6 +157,8 @@
         return "RegExpTestFastIntrinsic";
     case RegExpMatchFastIntrinsic:
         return "RegExpMatchFastIntrinsic";
+    case ObjectAssignIntrinsic:
+        return "ObjectAssignIntrinsic";
     case ObjectCreateIntrinsic:
         return "ObjectCreateIntrinsic";
     case ObjectGetOwnPropertyNamesIntrinsic:

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/Intrinsic.h (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/Intrinsic.h	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/Intrinsic.h	2021-07-08 20:27:16 UTC (rev 279746)
@@ -94,6 +94,7 @@
     RegExpTestIntrinsic,
     RegExpTestFastIntrinsic,
     RegExpMatchFastIntrinsic,
+    ObjectAssignIntrinsic,
     ObjectCreateIntrinsic,
     ObjectGetOwnPropertyNamesIntrinsic,
     ObjectGetPrototypeOfIntrinsic,

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/JSObject.cpp (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/JSObject.cpp	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/JSObject.cpp	2021-07-08 20:27:16 UTC (rev 279746)
@@ -2133,22 +2133,23 @@
             slot.setNonconfigurable();
             return false;
         }
-        DeferredStructureTransitionWatchpointFire deferredWatchpointFire(vm, structure);
 
         PropertyOffset offset = invalidOffset;
-        if (structure->isUncacheableDictionary())
+        if (structure->isUncacheableDictionary()) {
             offset = structure->removePropertyWithoutTransition(vm, propertyName, [] (const GCSafeConcurrentJSLocker&, PropertyOffset, PropertyOffset) { });
-        else {
+            ASSERT(!isValidOffset(structure->get(vm, propertyName, attributes)));
+            if (offset != invalidOffset)
+                thisObject->locationForOffset(offset)->clear();
+        } else {
+            DeferredStructureTransitionWatchpointFire deferredWatchpointFire(vm, structure);
             structure = Structure::removePropertyTransition(vm, structure, propertyName, offset, &deferredWatchpointFire);
             slot.setHit(offset);
             ASSERT(structure->outOfLineCapacity() || !thisObject->structure(vm)->outOfLineCapacity());
             thisObject->setStructure(vm, structure);
+            ASSERT(!isValidOffset(structure->get(vm, propertyName, attributes)));
+            if (offset != invalidOffset)
+                thisObject->locationForOffset(offset)->clear();
         }
-
-        ASSERT(!isValidOffset(structure->get(vm, propertyName, attributes)));
-
-        if (offset != invalidOffset)
-            thisObject->locationForOffset(offset)->clear();
     } else
         slot.setConfigurableMiss();
 

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/JSObjectInlines.h (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/JSObjectInlines.h	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/JSObjectInlines.h	2021-07-08 20:27:16 UTC (rev 279746)
@@ -380,10 +380,6 @@
         return true;
     }
 
-    // We want the structure transition watchpoint to fire after this object has switched structure.
-    // This allows adaptive watchpoints to observe if the new structure is the one we want.
-    DeferredStructureTransitionWatchpointFire deferredWatchpointFire(vm, structure);
-
     unsigned currentAttributes;
     offset = structure->get(vm, propertyName, currentAttributes);
     if (offset != invalidOffset) {
@@ -395,9 +391,12 @@
 
         // FIXME: Check attributes against PropertyAttribute::CustomAccessorOrValue. Changing GetterSetter should work w/o transition.
         // https://bugs.webkit.org/show_bug.cgi?id=214342
-        if (mode == PutModeDefineOwnProperty && (attributes != currentAttributes || (attributes & PropertyAttribute::AccessorOrCustomAccessorOrValue)))
+        if (mode == PutModeDefineOwnProperty && (attributes != currentAttributes || (attributes & PropertyAttribute::AccessorOrCustomAccessorOrValue))) {
+            // We want the structure transition watchpoint to fire after this object has switched structure.
+            // This allows adaptive watchpoints to observe if the new structure is the one we want.
+            DeferredStructureTransitionWatchpointFire deferredWatchpointFire(vm, structure);
             setStructure(vm, Structure::attributeChangeTransition(vm, structure, propertyName, attributes, &deferredWatchpointFire));
-        else
+        } else
             slot.setExistingProperty(this, offset);
 
         return true;
@@ -406,8 +405,10 @@
     if ((mode == PutModePut) && !isStructureExtensible(vm))
         return false;
     
-    newStructure = Structure::addNewPropertyTransition(
-        vm, structure, propertyName, attributes, offset, slot.context(), &deferredWatchpointFire);
+    // We want the structure transition watchpoint to fire after this object has switched structure.
+    // This allows adaptive watchpoints to observe if the new structure is the one we want.
+    DeferredStructureTransitionWatchpointFire deferredWatchpointFire(vm, structure);
+    newStructure = Structure::addNewPropertyTransition(vm, structure, propertyName, attributes, offset, slot.context(), &deferredWatchpointFire);
     
     validateOffset(offset);
     ASSERT(newStructure->isValidOffset(offset));

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/ObjectConstructor.cpp (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/ObjectConstructor.cpp	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/ObjectConstructor.cpp	2021-07-08 20:27:16 UTC (rev 279746)
@@ -25,6 +25,7 @@
 #include "JSArray.h"
 #include "JSCInlines.h"
 #include "JSImmutableButterfly.h"
+#include "ObjectConstructorInlines.h"
 #include "PropertyDescriptor.h"
 #include "PropertyNameArray.h"
 #include "Symbol.h"
@@ -75,7 +76,7 @@
   isFrozen                  objectConstructorIsFrozen                   DontEnum|Function 1
   isExtensible              objectConstructorIsExtensible               DontEnum|Function 1
   is                        objectConstructorIs                         DontEnum|Function 2 ObjectIsIntrinsic
-  assign                    objectConstructorAssign                     DontEnum|Function 2
+  assign                    objectConstructorAssign                     DontEnum|Function 2 ObjectAssignIntrinsic
   values                    objectConstructorValues                     DontEnum|Function 1
   entries                   objectConstructorEntries                    DontEnum|Function 1
   fromEntries               JSBuiltin                                   DontEnum|Function 1
@@ -263,6 +264,43 @@
     RELEASE_AND_RETURN(scope, JSValue::encode(ownPropertyKeys(globalObject, object, PropertyNameMode::Strings, DontEnumPropertiesMode::Exclude, CachedPropertyNamesKind::Keys)));
 }
 
+void objectAssignGeneric(JSGlobalObject* globalObject, VM& vm, JSObject* target, JSObject* source)
+{
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    // [[GetOwnPropertyNames]], [[Get]] etc. could modify target object and invalidate this assumption.
+    // For example, [[Get]] of source object could configure setter to target object. So disable the fast path.
+
+    PropertyNameArray properties(vm, PropertyNameMode::StringsAndSymbols, PrivateSymbolMode::Exclude);
+    source->methodTable(vm)->getOwnPropertyNames(source, globalObject, properties, DontEnumPropertiesMode::Include);
+    RETURN_IF_EXCEPTION(scope, void());
+
+    unsigned numProperties = properties.size();
+    for (unsigned j = 0; j < numProperties; j++) {
+        const auto& propertyName = properties[j];
+        ASSERT(!propertyName.isPrivateName());
+
+        PropertySlot slot(source, PropertySlot::InternalMethodType::GetOwnProperty);
+        bool hasProperty = source->methodTable(vm)->getOwnPropertySlot(source, globalObject, propertyName, slot);
+        RETURN_IF_EXCEPTION(scope, void());
+        if (!hasProperty)
+            continue;
+        if (slot.attributes() & PropertyAttribute::DontEnum)
+            continue;
+
+        JSValue value;
+        if (LIKELY(!slot.isTaintedByOpaqueObject()))
+            value = slot.getValue(globalObject, propertyName);
+        else
+            value = source->get(globalObject, propertyName);
+        RETURN_IF_EXCEPTION(scope, void());
+
+        PutPropertySlot putPropertySlot(target, true);
+        target->putInline(globalObject, propertyName, value, putPropertySlot);
+        RETURN_IF_EXCEPTION(scope, void());
+    }
+}
+
 JSC_DEFINE_HOST_FUNCTION(objectConstructorAssign, (JSGlobalObject* globalObject, CallFrame* callFrame))
 {
     VM& vm = globalObject->vm();
@@ -294,29 +332,6 @@
                 RETURN_IF_EXCEPTION(scope, { });
             }
 
-            auto canPerformFastPropertyEnumerationForObjectAssign = [] (Structure* structure) {
-                if (structure->typeInfo().overridesGetOwnPropertySlot())
-                    return false;
-                if (structure->typeInfo().overridesAnyFormOfGetOwnPropertyNames())
-                    return false;
-                // FIXME: Indexed properties can be handled.
-                // https://bugs.webkit.org/show_bug.cgi?id=185358
-                if (hasIndexedProperties(structure->indexingType()))
-                    return false;
-                if (structure->hasGetterSetterProperties())
-                    return false;
-                if (structure->hasReadOnlyOrGetterSetterPropertiesExcludingProto())
-                    return false;
-                if (structure->hasCustomGetterSetterProperties())
-                    return false;
-                if (structure->isUncacheableDictionary())
-                    return false;
-                // Cannot perform fast [[Put]] to |target| if the property names of the |source| contain "__proto__".
-                if (structure->hasUnderscoreProtoPropertyExcludingOriginalProto())
-                    return false;
-                return true;
-            };
-
             if (canPerformFastPropertyEnumerationForObjectAssign(source->structure(vm))) {
                 // |source| Structure does not have any getters. And target can perform fast put.
                 // So enumerating properties and putting properties are non observable.
@@ -328,6 +343,9 @@
                 // that ends up transitioning the structure underneath us.
                 // https://bugs.webkit.org/show_bug.cgi?id=187837
 
+                // FIXME: This fast path is very similar to DFGOperations' one. But extracting it to a function caused performance
+                // regression in object-assign-replace. Since the code is small and fast path, we keep both.
+
                 // Do not clear since Vector::clear shrinks the backing store.
                 properties.resize(0);
                 values.clear();
@@ -355,38 +373,9 @@
             }
         }
 
-        // [[GetOwnPropertyNames]], [[Get]] etc. could modify target object and invalidate this assumption.
-        // For example, [[Get]] of source object could configure setter to target object. So disable the fast path.
         targetCanPerformFastPut = false;
-
-        PropertyNameArray properties(vm, PropertyNameMode::StringsAndSymbols, PrivateSymbolMode::Exclude);
-        source->methodTable(vm)->getOwnPropertyNames(source, globalObject, properties, DontEnumPropertiesMode::Include);
+        objectAssignGeneric(globalObject, vm, target, source);
         RETURN_IF_EXCEPTION(scope, { });
-
-        unsigned numProperties = properties.size();
-        for (unsigned j = 0; j < numProperties; j++) {
-            const auto& propertyName = properties[j];
-            ASSERT(!propertyName.isPrivateName());
-
-            PropertySlot slot(source, PropertySlot::InternalMethodType::GetOwnProperty);
-            bool hasProperty = source->methodTable(vm)->getOwnPropertySlot(source, globalObject, propertyName, slot);
-            RETURN_IF_EXCEPTION(scope, { });
-            if (!hasProperty)
-                continue;
-            if (slot.attributes() & PropertyAttribute::DontEnum)
-                continue;
-
-            JSValue value;
-            if (LIKELY(!slot.isTaintedByOpaqueObject()))
-                value = slot.getValue(globalObject, propertyName);
-            else
-                value = source->get(globalObject, propertyName);
-            RETURN_IF_EXCEPTION(scope, { });
-
-            PutPropertySlot putPropertySlot(target, true);
-            target->putInline(globalObject, propertyName, value, putPropertySlot);
-            RETURN_IF_EXCEPTION(scope, { });
-        }
     }
     return JSValue::encode(target);
 }

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/ObjectConstructor.h (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/ObjectConstructor.h	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/ObjectConstructor.h	2021-07-08 20:27:16 UTC (rev 279746)
@@ -165,6 +165,7 @@
 JSValue objectConstructorGetOwnPropertyDescriptors(JSGlobalObject*, JSObject*);
 JSArray* ownPropertyKeys(JSGlobalObject*, JSObject*, PropertyNameMode, DontEnumPropertiesMode, std::optional<CachedPropertyNamesKind>);
 bool toPropertyDescriptor(JSGlobalObject*, JSValue, PropertyDescriptor&);
+void objectAssignGeneric(JSGlobalObject*, VM&, JSObject* target, JSObject* source);
 
 JSC_DECLARE_HOST_FUNCTION(objectConstructorIs);
 

Added: branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/ObjectConstructorInlines.h (0 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/ObjectConstructorInlines.h	                        (rev 0)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/ObjectConstructorInlines.h	2021-07-08 20:27:16 UTC (rev 279746)
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "ObjectConstructor.h"
+
+namespace JSC {
+
+ALWAYS_INLINE bool canPerformFastPropertyEnumerationForObjectAssign(Structure* structure)
+{
+    if (structure->typeInfo().overridesGetOwnPropertySlot())
+        return false;
+    if (structure->typeInfo().overridesAnyFormOfGetOwnPropertyNames())
+        return false;
+    // FIXME: Indexed properties can be handled.
+    // https://bugs.webkit.org/show_bug.cgi?id=185358
+    if (hasIndexedProperties(structure->indexingType()))
+        return false;
+    if (structure->hasGetterSetterProperties())
+        return false;
+    if (structure->hasReadOnlyOrGetterSetterPropertiesExcludingProto())
+        return false;
+    if (structure->hasCustomGetterSetterProperties())
+        return false;
+    if (structure->isUncacheableDictionary())
+        return false;
+    // Cannot perform fast [[Put]] to |target| if the property names of the |source| contain "__proto__".
+    if (structure->hasUnderscoreProtoPropertyExcludingOriginalProto())
+        return false;
+    return true;
+}
+
+} // namespace JSC

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/Structure.cpp (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/Structure.cpp	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/Structure.cpp	2021-07-08 20:27:16 UTC (rev 279746)
@@ -61,16 +61,6 @@
     return owner;
 }
 
-inline Structure* StructureTransitionTable::singleTransition() const
-{
-    ASSERT(isUsingSingleSlot());
-    if (WeakImpl* impl = this->weakImpl()) {
-        if (impl->state() == WeakImpl::Live)
-            return jsCast<Structure*>(impl->jsValue().asCell());
-    }
-    return nullptr;
-}
-
 inline void StructureTransitionTable::setSingleTransition(Structure* structure)
 {
     ASSERT(isUsingSingleSlot());
@@ -89,15 +79,6 @@
     return map()->get(StructureTransitionTable::Hash::Key(rep, attributes, transitionKind));
 }
 
-inline Structure* StructureTransitionTable::get(UniquedStringImpl* rep, unsigned attributes, TransitionKind transitionKind) const
-{
-    if (isUsingSingleSlot()) {
-        Structure* transition = singleTransition();
-        return (transition && transition->m_transitionPropertyName == rep && transition->transitionPropertyAttributes() == attributes && transition->transitionKind() == transitionKind) ? transition : nullptr;
-    }
-    return map()->get(StructureTransitionTable::Hash::Key(rep, attributes, transitionKind));
-}
-
 void StructureTransitionTable::add(VM& vm, Structure* structure)
 {
     if (isUsingSingleSlot()) {
@@ -475,37 +456,6 @@
     return table;
 }
 
-Structure* Structure::addPropertyTransitionToExistingStructureImpl(Structure* structure, UniquedStringImpl* uid, unsigned attributes, PropertyOffset& offset)
-{
-    ASSERT(!structure->isDictionary());
-    ASSERT(structure->isObject());
-
-    offset = invalidOffset;
-
-    if (structure->hasBeenDictionary())
-        return nullptr;
-
-    if (Structure* existingTransition = structure->m_transitionTable.get(uid, attributes, TransitionKind::PropertyAddition)) {
-        validateOffset(existingTransition->transitionOffset(), existingTransition->inlineCapacity());
-        offset = existingTransition->transitionOffset();
-        return existingTransition;
-    }
-
-    return nullptr;
-}
-
-Structure* Structure::addPropertyTransitionToExistingStructure(Structure* structure, PropertyName propertyName, unsigned attributes, PropertyOffset& offset)
-{
-    ASSERT(!isCompilationThread());
-    return addPropertyTransitionToExistingStructureImpl(structure, propertyName.uid(), attributes, offset);
-}
-
-Structure* Structure::addPropertyTransitionToExistingStructureConcurrently(Structure* structure, UniquedStringImpl* uid, unsigned attributes, PropertyOffset& offset)
-{
-    ConcurrentJSLocker locker(structure->m_lock);
-    return addPropertyTransitionToExistingStructureImpl(structure, uid, attributes, offset);
-}
-
 bool Structure::holesMustForwardToPrototype(VM& vm, JSObject* base) const
 {
     ASSERT(base->structure(vm) == this);

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/Structure.h (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/Structure.h	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/Structure.h	2021-07-08 20:27:16 UTC (rev 279746)
@@ -55,6 +55,7 @@
 namespace JSC {
 
 class DeferGC;
+class DeferredStructureTransitionWatchpointFire;
 class LLIntOffsetsExtractor;
 class PropertyNameArray;
 class PropertyNameArrayData;
@@ -109,20 +110,6 @@
     const Structure* m_structure;
 };
 
-class DeferredStructureTransitionWatchpointFire final : public DeferredWatchpointFire {
-    WTF_MAKE_NONCOPYABLE(DeferredStructureTransitionWatchpointFire);
-public:
-    JS_EXPORT_PRIVATE DeferredStructureTransitionWatchpointFire(VM&, Structure*);
-    JS_EXPORT_PRIVATE ~DeferredStructureTransitionWatchpointFire() final;
-    
-    void dump(PrintStream& out) const final;
-
-    const Structure* structure() const { return m_structure; }
-
-private:
-    const Structure* m_structure;
-};
-
 class Structure : public JSCell {
     static constexpr uint16_t shortInvalidOffset = std::numeric_limits<uint16_t>::max() - 1;
     static constexpr uint16_t useRareDataFlag = std::numeric_limits<uint16_t>::max();
@@ -192,7 +179,7 @@
     JS_EXPORT_PRIVATE static Structure* addPropertyTransition(VM&, Structure*, PropertyName, unsigned attributes, PropertyOffset&);
     JS_EXPORT_PRIVATE static Structure* addNewPropertyTransition(VM&, Structure*, PropertyName, unsigned attributes, PropertyOffset&, PutPropertySlot::Context = PutPropertySlot::UnknownContext, DeferredStructureTransitionWatchpointFire* = nullptr);
     static Structure* addPropertyTransitionToExistingStructureConcurrently(Structure*, UniquedStringImpl* uid, unsigned attributes, PropertyOffset&);
-    JS_EXPORT_PRIVATE static Structure* addPropertyTransitionToExistingStructure(Structure*, PropertyName, unsigned attributes, PropertyOffset&);
+    static Structure* addPropertyTransitionToExistingStructure(Structure*, PropertyName, unsigned attributes, PropertyOffset&);
     static Structure* removeNewPropertyTransition(VM&, Structure*, PropertyName, PropertyOffset&, DeferredStructureTransitionWatchpointFire* = nullptr);
     static Structure* removePropertyTransition(VM&, Structure*, PropertyName, PropertyOffset&, DeferredStructureTransitionWatchpointFire* = nullptr);
     static Structure* removePropertyTransitionFromExistingStructure(Structure*, PropertyName, PropertyOffset&);

Modified: branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/StructureInlines.h (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/StructureInlines.h	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/_javascript_Core/runtime/StructureInlines.h	2021-07-08 20:27:16 UTC (rev 279746)
@@ -37,6 +37,20 @@
 
 namespace JSC {
 
+class DeferredStructureTransitionWatchpointFire final : public DeferredWatchpointFire {
+    WTF_MAKE_NONCOPYABLE(DeferredStructureTransitionWatchpointFire);
+public:
+    JS_EXPORT_PRIVATE DeferredStructureTransitionWatchpointFire(VM&, Structure*);
+    JS_EXPORT_PRIVATE ~DeferredStructureTransitionWatchpointFire() final;
+
+    void dump(PrintStream&) const final;
+
+    const Structure* structure() const { return m_structure; }
+
+private:
+    const Structure* m_structure;
+};
+
 inline Structure* Structure::create(VM& vm, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo, IndexingType indexingModeIncludingHistory, unsigned inlineCapacity)
 {
     ASSERT(vm.structureStructure);
@@ -358,7 +372,10 @@
 {
     if (LIKELY(!hasRareData()))
         return;
-    WatchpointSet* set = rareData()->m_replacementWatchpointSets.get(offset);
+    auto* rareData = this->rareData();
+    if (LIKELY(rareData->m_replacementWatchpointSets.isNullStorage()))
+        return;
+    WatchpointSet* set = rareData->m_replacementWatchpointSets.get(offset);
     if (LIKELY(!set))
         return;
     set->fireAll(vm(), "Property did get replaced");
@@ -370,7 +387,9 @@
     StructureRareData* rareData = tryRareData();
     if (!rareData)
         return nullptr;
-    return rareData->m_replacementWatchpointSets.get(offset);
+    if (!rareData->m_replacementWatchpointSets.isNullStorage())
+        return rareData->m_replacementWatchpointSets.get(offset);
+    return nullptr;
 }
 
 template<typename DetailsFunc>
@@ -689,4 +708,54 @@
     return nonPropertyTransitionSlow(vm, structure, transitionKind);
 }
 
+inline Structure* Structure::addPropertyTransitionToExistingStructureImpl(Structure* structure, UniquedStringImpl* uid, unsigned attributes, PropertyOffset& offset)
+{
+    ASSERT(!structure->isDictionary());
+    ASSERT(structure->isObject());
+
+    offset = invalidOffset;
+
+    if (structure->hasBeenDictionary())
+        return nullptr;
+
+    if (Structure* existingTransition = structure->m_transitionTable.get(uid, attributes, TransitionKind::PropertyAddition)) {
+        validateOffset(existingTransition->transitionOffset(), existingTransition->inlineCapacity());
+        offset = existingTransition->transitionOffset();
+        return existingTransition;
+    }
+
+    return nullptr;
+}
+
+ALWAYS_INLINE Structure* Structure::addPropertyTransitionToExistingStructure(Structure* structure, PropertyName propertyName, unsigned attributes, PropertyOffset& offset)
+{
+    ASSERT(!isCompilationThread());
+    return addPropertyTransitionToExistingStructureImpl(structure, propertyName.uid(), attributes, offset);
+}
+
+ALWAYS_INLINE Structure* Structure::addPropertyTransitionToExistingStructureConcurrently(Structure* structure, UniquedStringImpl* uid, unsigned attributes, PropertyOffset& offset)
+{
+    ConcurrentJSLocker locker(structure->m_lock);
+    return addPropertyTransitionToExistingStructureImpl(structure, uid, attributes, offset);
+}
+
+inline Structure* StructureTransitionTable::singleTransition() const
+{
+    ASSERT(isUsingSingleSlot());
+    if (WeakImpl* impl = this->weakImpl()) {
+        if (impl->state() == WeakImpl::Live)
+            return jsCast<Structure*>(impl->jsValue().asCell());
+    }
+    return nullptr;
+}
+
+inline Structure* StructureTransitionTable::get(UniquedStringImpl* rep, unsigned attributes, TransitionKind transitionKind) const
+{
+    if (isUsingSingleSlot()) {
+        Structure* transition = singleTransition();
+        return (transition && transition->m_transitionPropertyName == rep && transition->transitionPropertyAttributes() == attributes && transition->transitionKind() == transitionKind) ? transition : nullptr;
+    }
+    return map()->get(StructureTransitionTable::Hash::Key(rep, attributes, transitionKind));
+}
+
 } // namespace JSC

Modified: branches/safari-612.1.23-branch/Source/WTF/ChangeLog (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/WTF/ChangeLog	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/WTF/ChangeLog	2021-07-08 20:27:16 UTC (rev 279746)
@@ -1,3 +1,132 @@
+2021-07-08  Ruben Turcios  <rubent...@apple.com>
+
+        Cherry-pick r279604. rdar://problem/80340434
+
+    [JSC] Optimize Object.assign and putDirectInternal
+    https://bugs.webkit.org/show_bug.cgi?id=227677
+    
+    Reviewed by Filip Pizlo.
+    
+    JSTests:
+    
+    * microbenchmarks/object-assign-replace.js: Added.
+    (test):
+    * microbenchmarks/object-assign-transition.js: Added.
+    (test):
+    
+    Source/_javascript_Core:
+    
+    This patch improves Object.assign performance (and this requires putDirectInternal improvement).
+    
+    1. Object.assign is handled by DFG / FTL as ObjectAssign node
+    2. We found that putDirectInternal is suboptimal. This patch removes several bottlenecks. They are super critical from the measurement of
+       microbenchmarks.
+        2.1. Creating and destroying DeferredStructureTransitionWatchpointFire is not free. We should do that only when we need it.
+        2.2. Before r277620, we are checking m_replacementWatchpointSets's nullptr and that was fast. But after that, we are always
+             calling HashMap::get, and it is not inlined. This means that if we have StructureRareData, we are always calling HashMap::get
+             even though there is no m_replacementWatchpointSets set. This patch adds HashMap::isNullStorage to avoid this call by inlinely
+             check this via `LIKELY(m_replacementWatchpointSets.isNullStorage())`.
+        2.3. Structure::addPropertyTransitionToExistingStructure has very cheap fast path and we must inline them to get performance benefit
+             for major single-transition case.
+    
+    Added microbenchmarks show significant performance improvements. And Speedometer2 shows 0.6 - 1.0% improvement.
+    
+                                             ToT                     Patched
+    
+        object-assign-transition      105.2539+-0.2970     ^     88.8046+-0.2029        ^ definitely 1.1852x faster
+        object-assign-replace         116.1568+-0.0905     ^     75.0673+-0.0658        ^ definitely 1.5474x faster
+    
+    * _javascript_Core.xcodeproj/project.pbxproj:
+    * bytecode/Watchpoint.cpp:
+    (JSC::DeferredWatchpointFire::fireAllSlow):
+    (JSC::DeferredWatchpointFire::DeferredWatchpointFire): Deleted.
+    (JSC::DeferredWatchpointFire::~DeferredWatchpointFire): Deleted.
+    (JSC::DeferredWatchpointFire::fireAll): Deleted.
+    * bytecode/Watchpoint.h:
+    (JSC::DeferredWatchpointFire::DeferredWatchpointFire):
+    (JSC::DeferredWatchpointFire::fireAll):
+    (JSC::FireDetail::FireDetail): Deleted.
+    (JSC::FireDetail::~FireDetail): Deleted.
+    * dfg/DFGAbstractInterpreterInlines.h:
+    (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+    * dfg/DFGByteCodeParser.cpp:
+    (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+    * dfg/DFGClobberize.h:
+    (JSC::DFG::clobberize):
+    * dfg/DFGDoesGC.cpp:
+    (JSC::DFG::doesGC):
+    * dfg/DFGFixupPhase.cpp:
+    (JSC::DFG::FixupPhase::fixupNode):
+    * dfg/DFGMayExit.cpp:
+    * dfg/DFGNodeType.h:
+    * dfg/DFGOperations.cpp:
+    (JSC::DFG::JSC_DEFINE_JIT_OPERATION):
+    * dfg/DFGOperations.h:
+    * dfg/DFGPredictionPropagationPhase.cpp:
+    * dfg/DFGSafeToExecute.h:
+    (JSC::DFG::safeToExecute):
+    * dfg/DFGSpeculativeJIT.cpp:
+    (JSC::DFG::SpeculativeJIT::compileObjectAssign):
+    * dfg/DFGSpeculativeJIT.h:
+    * dfg/DFGSpeculativeJIT32_64.cpp:
+    (JSC::DFG::SpeculativeJIT::compile):
+    * dfg/DFGSpeculativeJIT64.cpp:
+    (JSC::DFG::SpeculativeJIT::compile):
+    * ftl/FTLCapabilities.cpp:
+    (JSC::FTL::canCompile):
+    * ftl/FTLLowerDFGToB3.cpp:
+    (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+    (JSC::FTL::DFG::LowerDFGToB3::compileObjectAssign):
+    * runtime/Intrinsic.cpp:
+    (JSC::intrinsicName):
+    * runtime/Intrinsic.h:
+    * runtime/JSObject.cpp:
+    (JSC::JSObject::deleteProperty):
+    * runtime/JSObjectInlines.h:
+    (JSC::JSObject::putDirectInternal):
+    * runtime/ObjectConstructor.cpp:
+    (JSC::objectAssignGeneric):
+    (JSC::JSC_DEFINE_HOST_FUNCTION):
+    * runtime/ObjectConstructor.h:
+    * runtime/ObjectConstructorInlines.h: Added.
+    (JSC::canPerformFastPropertyEnumerationForObjectAssign):
+    * runtime/Structure.cpp:
+    (JSC::StructureTransitionTable::singleTransition const): Deleted.
+    (JSC::StructureTransitionTable::get const): Deleted.
+    (JSC::Structure::addPropertyTransitionToExistingStructureImpl): Deleted.
+    (JSC::Structure::addPropertyTransitionToExistingStructure): Deleted.
+    (JSC::Structure::addPropertyTransitionToExistingStructureConcurrently): Deleted.
+    * runtime/Structure.h:
+    * runtime/StructureInlines.h:
+    (JSC::Structure::didReplaceProperty):
+    (JSC::Structure::propertyReplacementWatchpointSet):
+    (JSC::Structure::addPropertyTransitionToExistingStructureImpl):
+    (JSC::Structure::addPropertyTransitionToExistingStructure):
+    (JSC::Structure::addPropertyTransitionToExistingStructureConcurrently):
+    (JSC::StructureTransitionTable::singleTransition const):
+    (JSC::StructureTransitionTable::get const):
+    
+    Source/WTF:
+    
+    * wtf/HashMap.h:
+    * wtf/HashSet.h:
+    * wtf/HashTable.h:
+    (WTF::HashTable::isNullStorage const):
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@279604 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2021-07-05  Yusuke Suzuki  <ysuz...@apple.com>
+
+            [JSC] Optimize Object.assign and putDirectInternal
+            https://bugs.webkit.org/show_bug.cgi?id=227677
+
+            Reviewed by Filip Pizlo.
+
+            * wtf/HashMap.h:
+            * wtf/HashSet.h:
+            * wtf/HashTable.h:
+            (WTF::HashTable::isNullStorage const):
+
 2021-07-02  Brent Fulgham  <bfulg...@apple.com>
 
         [macOS] Only use WebGL on Metal by default on safe versions of macOS

Modified: branches/safari-612.1.23-branch/Source/WTF/wtf/HashMap.h (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/WTF/wtf/HashMap.h	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/WTF/wtf/HashMap.h	2021-07-08 20:27:16 UTC (rev 279746)
@@ -137,6 +137,8 @@
     // Same as get(), but aggressively inlined.
     MappedPeekType inlineGet(const KeyType&) const;
 
+    ALWAYS_INLINE bool isNullStorage() const { return m_impl.isNullStorage(); }
+
     // Replaces the value but not the key if the key is already present.
     // Return value includes both an iterator to the key location,
     // and an isNewEntry boolean that's true if a new entry was added.

Modified: branches/safari-612.1.23-branch/Source/WTF/wtf/HashSet.h (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/WTF/wtf/HashSet.h	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/WTF/wtf/HashSet.h	2021-07-08 20:27:16 UTC (rev 279746)
@@ -86,6 +86,7 @@
     template<typename HashTranslator, typename T> iterator find(const T&) const;
     template<typename HashTranslator, typename T> bool contains(const T&) const;
 
+    ALWAYS_INLINE bool isNullStorage() const { return m_impl.isNullStorage(); }
 
     // The return value includes both an iterator to the added value's location,
     // and an isNewEntry bool that indicates if it is a new or existing entry in the set.

Modified: branches/safari-612.1.23-branch/Source/WTF/wtf/HashTable.h (279745 => 279746)


--- branches/safari-612.1.23-branch/Source/WTF/wtf/HashTable.h	2021-07-08 20:13:14 UTC (rev 279745)
+++ branches/safari-612.1.23-branch/Source/WTF/wtf/HashTable.h	2021-07-08 20:27:16 UTC (rev 279746)
@@ -500,6 +500,8 @@
         template<typename HashTranslator, typename T> ValueType* lookup(const T&);
         template<typename HashTranslator, typename T> ValueType* inlineLookup(const T&);
 
+        ALWAYS_INLINE bool isNullStorage() const { return !m_table; }
+
 #if ASSERT_ENABLED
         void checkTableConsistency() const;
 #else
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to