Title: [284590] trunk/Source/_javascript_Core
Revision
284590
Author
[email protected]
Date
2021-10-20 23:27:53 -0700 (Wed, 20 Oct 2021)

Log Message

*IsSane API's could take in the Structure's we're consulting, or they can be out parameters, so we don't rely on the CPU's memory ordering
https://bugs.webkit.org/show_bug.cgi?id=231996

Reviewed by Filip Pizlo.

objectPrototypeIsSane, arrayPrototypeChainIsSane, and stringPrototypeChainIsSane reloads structures from prototype objects while the caller
is already getting them and validating them. This introduces a race condition where structure transition happens just before calling these
APIs and we will see different structures which are already validated. This is simply wrong: if we validate one structure, then we should
continue using that and we should put a watchpoint on this structure. We should not reload structures from the prototype again.

We add Concurrently postfix to these functions, and passing structures to these APIs to continue using these structures instead of reloading
it from prototype objects. This eliminate the race condition we had, and this removes the necessity of load-load-fence on watchpoint state
retrieval.

* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGArgumentsEliminationPhase.cpp:
* dfg/DFGArrayMode.cpp:
(JSC::DFG::ArrayMode::refine const):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::setSaneChainIfPossible):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileGetByValOnString):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileStringCharAtImpl):
* runtime/JSGlobalObject.h:
* runtime/JSGlobalObjectInlines.h:
(JSC::JSGlobalObject::objectPrototypeIsSane):
(JSC::JSGlobalObject::arrayPrototypeChainIsSaneConcurrently):
(JSC::JSGlobalObject::stringPrototypeChainIsSaneConcurrently):
(JSC::JSGlobalObject::arrayPrototypeChainIsSane):
(JSC::JSGlobalObject::stringPrototypeChainIsSane):

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (284589 => 284590)


--- trunk/Source/_javascript_Core/ChangeLog	2021-10-21 06:15:51 UTC (rev 284589)
+++ trunk/Source/_javascript_Core/ChangeLog	2021-10-21 06:27:53 UTC (rev 284590)
@@ -1,5 +1,43 @@
 2021-10-20  Yusuke Suzuki  <[email protected]>
 
+        *IsSane API's could take in the Structure's we're consulting, or they can be out parameters, so we don't rely on the CPU's memory ordering
+        https://bugs.webkit.org/show_bug.cgi?id=231996
+
+        Reviewed by Filip Pizlo.
+
+        objectPrototypeIsSane, arrayPrototypeChainIsSane, and stringPrototypeChainIsSane reloads structures from prototype objects while the caller
+        is already getting them and validating them. This introduces a race condition where structure transition happens just before calling these
+        APIs and we will see different structures which are already validated. This is simply wrong: if we validate one structure, then we should
+        continue using that and we should put a watchpoint on this structure. We should not reload structures from the prototype again.
+
+        We add Concurrently postfix to these functions, and passing structures to these APIs to continue using these structures instead of reloading
+        it from prototype objects. This eliminate the race condition we had, and this removes the necessity of load-load-fence on watchpoint state
+        retrieval.
+
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGArgumentsEliminationPhase.cpp:
+        * dfg/DFGArrayMode.cpp:
+        (JSC::DFG::ArrayMode::refine const):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        (JSC::DFG::FixupPhase::setSaneChainIfPossible):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileGetByValOnString):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileStringCharAtImpl):
+        * runtime/JSGlobalObject.h:
+        * runtime/JSGlobalObjectInlines.h:
+        (JSC::JSGlobalObject::objectPrototypeIsSane):
+        (JSC::JSGlobalObject::arrayPrototypeChainIsSaneConcurrently):
+        (JSC::JSGlobalObject::stringPrototypeChainIsSaneConcurrently):
+        (JSC::JSGlobalObject::arrayPrototypeChainIsSane):
+        (JSC::JSGlobalObject::stringPrototypeChainIsSane):
+
+2021-10-20  Yusuke Suzuki  <[email protected]>
+
         [JSC] ArithAbs should care about INT32_MIN
         https://bugs.webkit.org/show_bug.cgi?id=232051
         rdar://84338648

Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (284589 => 284590)


--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2021-10-21 06:15:51 UTC (rev 284589)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2021-10-21 06:27:53 UTC (rev 284590)
@@ -2245,7 +2245,7 @@
                         Structure* objectPrototypeStructure = globalObject->objectPrototype()->structure(m_vm);
                         if (arrayPrototypeStructure->transitionWatchpointSetIsStillValid()
                             && objectPrototypeStructure->transitionWatchpointSetIsStillValid()
-                            && globalObject->arrayPrototypeChainIsSane()) {
+                            && globalObject->arrayPrototypeChainIsSaneConcurrently(arrayPrototypeStructure, objectPrototypeStructure)) {
                             m_graph.registerAndWatchStructureTransition(arrayPrototypeStructure);
                             m_graph.registerAndWatchStructureTransition(objectPrototypeStructure);
                             if (node->arrayMode().type() == Array::Double && node->arrayMode().isOutOfBoundsSaneChain() && !(node->flags() & NodeBytecodeUsesAsOther))

Modified: trunk/Source/_javascript_Core/dfg/DFGArgumentsEliminationPhase.cpp (284589 => 284590)


--- trunk/Source/_javascript_Core/dfg/DFGArgumentsEliminationPhase.cpp	2021-10-21 06:15:51 UTC (rev 284589)
+++ trunk/Source/_javascript_Core/dfg/DFGArgumentsEliminationPhase.cpp	2021-10-21 06:27:53 UTC (rev 284590)
@@ -252,7 +252,7 @@
                 JSGlobalObject* globalObject = m_graph.globalObjectFor(edge->origin.semantic);
                 Structure* objectPrototypeStructure = globalObject->objectPrototype()->structure(m_graph.m_vm);
                 if (objectPrototypeStructure->transitionWatchpointSetIsStillValid()
-                    && globalObject->objectPrototypeIsSane()) {
+                    && globalObject->objectPrototypeIsSaneConcurrently(objectPrototypeStructure)) {
                     m_graph.registerAndWatchStructureTransition(objectPrototypeStructure);
                     break;
                 }
@@ -278,7 +278,7 @@
                     Structure* arrayPrototypeStructure = globalObject->arrayPrototype()->structure(m_graph.m_vm);
                     if (arrayPrototypeStructure->transitionWatchpointSetIsStillValid()
                         && objectPrototypeStructure->transitionWatchpointSetIsStillValid()
-                        && globalObject->arrayPrototypeChainIsSane()) {
+                        && globalObject->arrayPrototypeChainIsSaneConcurrently(arrayPrototypeStructure, objectPrototypeStructure)) {
                         m_graph.registerAndWatchStructureTransition(arrayPrototypeStructure);
                         m_graph.registerAndWatchStructureTransition(objectPrototypeStructure);
                         break;
@@ -285,7 +285,7 @@
                     }
                 } else {
                     if (objectPrototypeStructure->transitionWatchpointSetIsStillValid()
-                        && globalObject->objectPrototypeIsSane()) {
+                        && globalObject->objectPrototypeIsSaneConcurrently(objectPrototypeStructure)) {
                         m_graph.registerAndWatchStructureTransition(objectPrototypeStructure);
                         break;
                     }

Modified: trunk/Source/_javascript_Core/dfg/DFGArrayMode.cpp (284589 => 284590)


--- trunk/Source/_javascript_Core/dfg/DFGArrayMode.cpp	2021-10-21 06:15:51 UTC (rev 284589)
+++ trunk/Source/_javascript_Core/dfg/DFGArrayMode.cpp	2021-10-21 06:27:53 UTC (rev 284590)
@@ -260,10 +260,10 @@
             && !graph.hasExitSite(node->origin.semantic, OutOfBounds)
             && arrayPrototypeStructure->transitionWatchpointSetIsStillValid()
             && objectPrototypeStructure->transitionWatchpointSetIsStillValid()
-            && globalObject->arrayPrototypeChainIsSane()) {
+            && globalObject->arrayPrototypeChainIsSaneConcurrently(arrayPrototypeStructure, objectPrototypeStructure)) {
             graph.registerAndWatchStructureTransition(arrayPrototypeStructure);
             graph.registerAndWatchStructureTransition(objectPrototypeStructure);
-            if (globalObject->arrayPrototypeChainIsSane())
+            if (globalObject->arrayPrototypeChainIsSaneConcurrently(arrayPrototypeStructure, objectPrototypeStructure))
                 return withSpeculation(Array::InBoundsSaneChain);
         }
         return ArrayMode(Array::Generic, action());

Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (284589 => 284590)


--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2021-10-21 06:15:51 UTC (rev 284589)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2021-10-21 06:27:53 UTC (rev 284590)
@@ -2525,7 +2525,7 @@
                     && globalObject->havingABadTimeWatchpoint()->isStillValid()
                     && arrayPrototypeStructure->transitionWatchpointSetIsStillValid()
                     && objectPrototypeStructure->transitionWatchpointSetIsStillValid()
-                    && globalObject->arrayPrototypeChainIsSane()) {
+                    && globalObject->arrayPrototypeChainIsSaneConcurrently(arrayPrototypeStructure, objectPrototypeStructure)) {
 
                     m_graph.watchpoints().addLazily(globalObject->arraySpeciesWatchpointSet());
                     m_graph.watchpoints().addLazily(globalObject->havingABadTimeWatchpoint());
@@ -2616,7 +2616,7 @@
                 // https://bugs.webkit.org/show_bug.cgi?id=173171
                 if (arrayPrototypeStructure->transitionWatchpointSetIsStillValid()
                     && objectPrototypeStructure->transitionWatchpointSetIsStillValid()
-                    && globalObject->arrayPrototypeChainIsSane()) {
+                    && globalObject->arrayPrototypeChainIsSaneConcurrently(arrayPrototypeStructure, objectPrototypeStructure)) {
 
                     m_graph.registerAndWatchStructureTransition(arrayPrototypeStructure);
                     m_graph.registerAndWatchStructureTransition(objectPrototypeStructure);

Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (284589 => 284590)


--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2021-10-21 06:15:51 UTC (rev 284589)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2021-10-21 06:27:53 UTC (rev 284590)
@@ -1679,7 +1679,7 @@
             if (node->child1()->shouldSpeculateArray()
                 && arrayPrototypeStructure->transitionWatchpointSetIsStillValid()
                 && objectPrototypeStructure->transitionWatchpointSetIsStillValid()
-                && globalObject->arrayPrototypeChainIsSane()
+                && globalObject->arrayPrototypeChainIsSaneConcurrently(arrayPrototypeStructure, objectPrototypeStructure)
                 && m_graph.isWatchingArrayIteratorProtocolWatchpoint(node->child1().node())
                 && m_graph.isWatchingHavingABadTimeWatchpoint(node->child1().node())) {
                 m_graph.registerAndWatchStructureTransition(objectPrototypeStructure);
@@ -3783,7 +3783,7 @@
         Structure* objectPrototypeStructure = globalObject->objectPrototype()->structure(vm());
         if (arrayPrototypeStructure->transitionWatchpointSetIsStillValid()
             && objectPrototypeStructure->transitionWatchpointSetIsStillValid()
-            && globalObject->arrayPrototypeChainIsSane()) {
+            && globalObject->arrayPrototypeChainIsSaneConcurrently(arrayPrototypeStructure, objectPrototypeStructure)) {
             m_graph.registerAndWatchStructureTransition(arrayPrototypeStructure);
             m_graph.registerAndWatchStructureTransition(objectPrototypeStructure);
             node->setArrayMode(node->arrayMode().withSpeculation(speculation));

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (284589 => 284590)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2021-10-21 06:15:51 UTC (rev 284589)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2021-10-21 06:27:53 UTC (rev 284590)
@@ -2651,11 +2651,11 @@
         Structure* objectPrototypeStructure = globalObject->objectPrototype()->structure(vm);
         WTF::dependentLoadLoadFence();
 
-        if (globalObject->stringPrototypeChainIsSane()) {
+        if (globalObject->stringPrototypeChainIsSaneConcurrently(stringPrototypeStructure, objectPrototypeStructure)) {
             // FIXME: This could be captured using a Speculation mode that means "out-of-bounds
             // loads return a trivial value". Something like OutOfBoundsSaneChain. This should
             // speculate that we don't take negative out-of-bounds, or better yet, it should rely
-            // on a stringPrototypeChainIsSane() guaranteeing that the prototypes have no negative
+            // on a stringPrototypeChainIsSaneConcurrently() guaranteeing that the prototypes have no negative
             // indexed properties either.
             // https://bugs.webkit.org/show_bug.cgi?id=144668
             m_jit.graph().registerAndWatchStructureTransition(stringPrototypeStructure);

Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (284589 => 284590)


--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2021-10-21 06:15:51 UTC (rev 284589)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2021-10-21 06:27:53 UTC (rev 284590)
@@ -8927,7 +8927,7 @@
             Structure* objectPrototypeStructure = globalObject->objectPrototype()->structure(vm());
             WTF::dependentLoadLoadFence();
 
-            if (globalObject->stringPrototypeChainIsSane()) {
+            if (globalObject->stringPrototypeChainIsSaneConcurrently(stringPrototypeStructure, objectPrototypeStructure)) {
                 // FIXME: This could be captured using a Speculation mode that means
                 // "out-of-bounds loads return a trivial value", something like
                 // OutOfBoundsSaneChain.

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.h (284589 => 284590)


--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.h	2021-10-21 06:15:51 UTC (rev 284589)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.h	2021-10-21 06:27:53 UTC (rev 284590)
@@ -1044,7 +1044,9 @@
         
     void haveABadTime(VM&);
         
-    bool objectPrototypeIsSane();
+    static bool objectPrototypeIsSaneConcurrently(Structure* objectPrototypeStructure);
+    bool arrayPrototypeChainIsSaneConcurrently(Structure* arrayPrototypeStructure, Structure* objectPrototypeStructure);
+    bool stringPrototypeChainIsSaneConcurrently(Structure* stringPrototypeStructure, Structure* objectPrototypeStructure);
     bool arrayPrototypeChainIsSane();
     bool stringPrototypeChainIsSane();
 

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObjectInlines.h (284589 => 284590)


--- trunk/Source/_javascript_Core/runtime/JSGlobalObjectInlines.h	2021-10-21 06:15:51 UTC (rev 284589)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObjectInlines.h	2021-10-21 06:27:53 UTC (rev 284590)
@@ -37,24 +37,45 @@
 
 namespace JSC {
 
-ALWAYS_INLINE bool JSGlobalObject::objectPrototypeIsSane()
+ALWAYS_INLINE bool JSGlobalObject::objectPrototypeIsSaneConcurrently(Structure* objectPrototypeStructure)
 {
-    return !hasIndexedProperties(m_objectPrototype->indexingType())
-        && m_objectPrototype->getPrototypeDirect(vm()).isNull();
+    return !hasIndexedProperties(objectPrototypeStructure->indexingType())
+        && objectPrototypeStructure->hasMonoProto()
+        && objectPrototypeStructure->storedPrototype().isNull();
 }
 
+ALWAYS_INLINE bool JSGlobalObject::arrayPrototypeChainIsSaneConcurrently(Structure* arrayPrototypeStructure, Structure* objectPrototypeStructure)
+{
+    return !hasIndexedProperties(arrayPrototypeStructure->indexingType())
+        && arrayPrototypeStructure->hasMonoProto()
+        && arrayPrototypeStructure->storedPrototype() == objectPrototype()
+        && objectPrototypeIsSaneConcurrently(objectPrototypeStructure);
+}
+
+ALWAYS_INLINE bool JSGlobalObject::stringPrototypeChainIsSaneConcurrently(Structure* stringPrototypeStructure, Structure* objectPrototypeStructure)
+{
+    return !hasIndexedProperties(stringPrototypeStructure->indexingType())
+        && stringPrototypeStructure->hasMonoProto()
+        && stringPrototypeStructure->storedPrototype() == objectPrototype()
+        && objectPrototypeIsSaneConcurrently(objectPrototypeStructure);
+}
+
 ALWAYS_INLINE bool JSGlobalObject::arrayPrototypeChainIsSane()
 {
-    return !hasIndexedProperties(m_arrayPrototype->indexingType())
-        && m_arrayPrototype->getPrototypeDirect(vm()) == m_objectPrototype.get()
-        && objectPrototypeIsSane();
+    VM& vm = this->vm();
+    ASSERT(!isCompilationThread() && !Thread::mayBeGCThread());
+    Structure* arrayPrototypeStructure = arrayPrototype()->structure(vm);
+    Structure* objectPrototypeStructure = objectPrototype()->structure(vm);
+    return arrayPrototypeChainIsSaneConcurrently(arrayPrototypeStructure, objectPrototypeStructure);
 }
 
 ALWAYS_INLINE bool JSGlobalObject::stringPrototypeChainIsSane()
 {
-    return !hasIndexedProperties(m_stringPrototype->indexingType())
-        && m_stringPrototype->getPrototypeDirect(vm()) == m_objectPrototype.get()
-        && objectPrototypeIsSane();
+    VM& vm = this->vm();
+    ASSERT(!isCompilationThread() && !Thread::mayBeGCThread());
+    Structure* stringPrototypeStructure = stringPrototype()->structure(vm);
+    Structure* objectPrototypeStructure = objectPrototype()->structure(vm);
+    return stringPrototypeChainIsSaneConcurrently(stringPrototypeStructure, objectPrototypeStructure);
 }
 
 ALWAYS_INLINE bool JSGlobalObject::isArrayPrototypeIteratorProtocolFastAndNonObservable()
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to