Title: [261712] trunk
Revision
261712
Author
[email protected]
Date
2020-05-14 15:01:50 -0700 (Thu, 14 May 2020)

Log Message

GetArrayLength should be "blessed" during Fixup phase in the DFG
https://bugs.webkit.org/show_bug.cgi?id=211540

Reviewed by Saam Barati.

JSTests:

* stress/get-array-length-node-should-be-blessed-in-fixup.js: Added.
(foo):

Source/_javascript_Core:

If we got an ArrayMode during bytecode parsing for-of that expects
to be configured during Fixup, then right now we will crash on
GetArrayLength. This fixes GetArrayLength to properly call
blessArrayOperation and fixes clobberize to know that
GetArrayLength could have a ForceExit ArrayMode briefly before
being cleaned up.

When blessing GetArrayLength we can now produce CheckArrays that
have an AnyTypedArray ArrayMode::Type. So this patch expands
CheckArray to properly handle that. To help with this we expand
branchIfType to have a starting JSType and an optional ending
JSType. Additionally, to prevent extra checks AI has been taught
to fold more ArrayModes so we should almost always avoid new
runtime checks.

Lastly, make sure that Undecided Arrays don't fall back to generic
because GetArrayLength can't be converted to...
GetArrayLenth. Also, GetArrayLength would previously pass it's own
speculation for the speculation of the index, which logically
doesn't make sense. So this patch adds a new constant, which is
SpecInt32Only, that can be passed if a DFG node doesn't have an
index.

* assembler/testmasm.cpp:
(JSC::testBranchIfType):
(JSC::testBranchIfNotType):
(JSC::run):
* dfg/DFGArrayMode.cpp:
(JSC::DFG::canBecomeGetArrayLength):
* dfg/DFGArrayMode.h:
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::blessArrayOperation):
(JSC::DFG::FixupPhase::attemptToMakeGetArrayLength):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::checkArray):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::isArrayTypeForCheckArray):
* jit/AssemblyHelpers.h:
(JSC::AssemblyHelpers::branchIfType):
(JSC::AssemblyHelpers::branchIfNotType):
* runtime/JSType.h:

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (261711 => 261712)


--- trunk/JSTests/ChangeLog	2020-05-14 21:50:41 UTC (rev 261711)
+++ trunk/JSTests/ChangeLog	2020-05-14 22:01:50 UTC (rev 261712)
@@ -1,3 +1,13 @@
+2020-05-14  Keith Miller  <[email protected]>
+
+        GetArrayLength should be "blessed" during Fixup phase in the DFG
+        https://bugs.webkit.org/show_bug.cgi?id=211540
+
+        Reviewed by Saam Barati.
+
+        * stress/get-array-length-node-should-be-blessed-in-fixup.js: Added.
+        (foo):
+
 2020-05-13  Keith Miller  <[email protected]>
 
         iteration bytecodes need to handle osr exiting from inlined getter frames

Added: trunk/JSTests/stress/get-array-length-node-should-be-blessed-in-fixup.js (0 => 261712)


--- trunk/JSTests/stress/get-array-length-node-should-be-blessed-in-fixup.js	                        (rev 0)
+++ trunk/JSTests/stress/get-array-length-node-should-be-blessed-in-fixup.js	2020-05-14 22:01:50 UTC (rev 261712)
@@ -0,0 +1,8 @@
+function foo() {
+  for (let i=0; i<10000; i++) {}
+  for (const q of Array.prototype) {}
+}
+
+for (let i=0; i<1000; i++) {
+  foo();
+}

Modified: trunk/Source/_javascript_Core/ChangeLog (261711 => 261712)


--- trunk/Source/_javascript_Core/ChangeLog	2020-05-14 21:50:41 UTC (rev 261711)
+++ trunk/Source/_javascript_Core/ChangeLog	2020-05-14 22:01:50 UTC (rev 261712)
@@ -1,3 +1,55 @@
+2020-05-14  Keith Miller  <[email protected]>
+
+        GetArrayLength should be "blessed" during Fixup phase in the DFG
+        https://bugs.webkit.org/show_bug.cgi?id=211540
+
+        Reviewed by Saam Barati.
+
+        If we got an ArrayMode during bytecode parsing for-of that expects
+        to be configured during Fixup, then right now we will crash on
+        GetArrayLength. This fixes GetArrayLength to properly call
+        blessArrayOperation and fixes clobberize to know that
+        GetArrayLength could have a ForceExit ArrayMode briefly before
+        being cleaned up.
+
+        When blessing GetArrayLength we can now produce CheckArrays that
+        have an AnyTypedArray ArrayMode::Type. So this patch expands
+        CheckArray to properly handle that. To help with this we expand
+        branchIfType to have a starting JSType and an optional ending
+        JSType. Additionally, to prevent extra checks AI has been taught
+        to fold more ArrayModes so we should almost always avoid new
+        runtime checks.
+
+        Lastly, make sure that Undecided Arrays don't fall back to generic
+        because GetArrayLength can't be converted to...
+        GetArrayLenth. Also, GetArrayLength would previously pass it's own
+        speculation for the speculation of the index, which logically
+        doesn't make sense. So this patch adds a new constant, which is
+        SpecInt32Only, that can be passed if a DFG node doesn't have an
+        index.
+
+        * assembler/testmasm.cpp:
+        (JSC::testBranchIfType):
+        (JSC::testBranchIfNotType):
+        (JSC::run):
+        * dfg/DFGArrayMode.cpp:
+        (JSC::DFG::canBecomeGetArrayLength):
+        * dfg/DFGArrayMode.h:
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        (JSC::DFG::FixupPhase::blessArrayOperation):
+        (JSC::DFG::FixupPhase::attemptToMakeGetArrayLength):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::checkArray):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::isArrayTypeForCheckArray):
+        * jit/AssemblyHelpers.h:
+        (JSC::AssemblyHelpers::branchIfType):
+        (JSC::AssemblyHelpers::branchIfNotType):
+        * runtime/JSType.h:
+
 2020-05-13  Keith Miller  <[email protected]>
 
         iteration bytecodes need to handle osr exiting from inlined getter frames

Modified: trunk/Source/_javascript_Core/assembler/testmasm.cpp (261711 => 261712)


--- trunk/Source/_javascript_Core/assembler/testmasm.cpp	2020-05-14 21:50:41 UTC (rev 261711)
+++ trunk/Source/_javascript_Core/assembler/testmasm.cpp	2020-05-14 22:01:50 UTC (rev 261712)
@@ -2161,6 +2161,74 @@
 #endif
 }
 
+static void testBranchIfType()
+{
+    using JSC::JSType;
+    struct CellLike {
+        uint32_t structureID;
+        uint8_t indexingType;
+        JSType type;
+    };
+    CHECK_EQ(JSCell::typeInfoTypeOffset(), OBJECT_OFFSETOF(CellLike, type));
+
+    auto isType = compile([] (CCallHelpers& jit) {
+        emitFunctionPrologue(jit);
+        auto isType = jit.branchIfType(GPRInfo::argumentGPR0, JSType(FirstTypedArrayType), JSType(LastTypedArrayTypeExcludingDataView));
+        jit.move(CCallHelpers::TrustedImm32(false), GPRInfo::returnValueGPR);
+        auto done = jit.jump();
+        isType.link(&jit);
+        jit.move(CCallHelpers::TrustedImm32(true), GPRInfo::returnValueGPR);
+        done.link(&jit);
+        emitFunctionEpilogue(jit);
+        jit.ret();
+    });
+
+    CellLike cell;
+    for (unsigned i = JSC::FirstTypedArrayType; i <= JSC::LastTypedArrayTypeExcludingDataView; ++i) {
+        cell.type = JSType(i);
+        CHECK_EQ(invoke<bool>(isType, &cell), true);
+    }
+
+    cell.type = JSType(LastTypedArrayType);
+    CHECK_EQ(invoke<bool>(isType, &cell), false);
+    cell.type = JSType(FirstTypedArrayType - 1);
+    CHECK_EQ(invoke<bool>(isType, &cell), false);
+}
+
+static void testBranchIfNotType()
+{
+    using JSC::JSType;
+    struct CellLike {
+        uint32_t structureID;
+        uint8_t indexingType;
+        JSType type;
+    };
+    CHECK_EQ(JSCell::typeInfoTypeOffset(), OBJECT_OFFSETOF(CellLike, type));
+
+    auto isNotType = compile([] (CCallHelpers& jit) {
+        emitFunctionPrologue(jit);
+        auto isNotType = jit.branchIfNotType(GPRInfo::argumentGPR0, JSType(FirstTypedArrayType), JSType(LastTypedArrayTypeExcludingDataView));
+        jit.move(CCallHelpers::TrustedImm32(false), GPRInfo::returnValueGPR);
+        auto done = jit.jump();
+        isNotType.link(&jit);
+        jit.move(CCallHelpers::TrustedImm32(true), GPRInfo::returnValueGPR);
+        done.link(&jit);
+        emitFunctionEpilogue(jit);
+        jit.ret();
+    });
+
+    CellLike cell;
+    for (unsigned i = JSC::FirstTypedArrayType; i <= JSC::LastTypedArrayTypeExcludingDataView; ++i) {
+        cell.type = JSType(i);
+        CHECK_EQ(invoke<bool>(isNotType, &cell), false);
+    }
+
+    cell.type = JSType(LastTypedArrayType);
+    CHECK_EQ(invoke<bool>(isNotType, &cell), true);
+    cell.type = JSType(FirstTypedArrayType - 1);
+    CHECK_EQ(invoke<bool>(isNotType, &cell), true);
+}
+
 #define RUN(test) do {                          \
         if (!shouldRun(#test))                  \
             break;                              \
@@ -2283,6 +2351,9 @@
 
     RUN(testCagePreservesPACFailureBit());
 
+    RUN(testBranchIfType());
+    RUN(testBranchIfNotType());
+
     RUN(testOrImmMem());
 
     if (tasks.isEmpty())

Modified: trunk/Source/_javascript_Core/dfg/DFGArrayMode.cpp (261711 => 261712)


--- trunk/Source/_javascript_Core/dfg/DFGArrayMode.cpp	2020-05-14 21:50:41 UTC (rev 261711)
+++ trunk/Source/_javascript_Core/dfg/DFGArrayMode.cpp	2020-05-14 22:01:50 UTC (rev 261712)
@@ -185,6 +185,8 @@
 
 static bool canBecomeGetArrayLength(Graph& graph, Node* node)
 {
+    if (node->op() == GetArrayLength)
+        return true;
     if (node->op() != GetById)
         return false;
     auto uid = node->cacheableIdentifier().uid();

Modified: trunk/Source/_javascript_Core/dfg/DFGArrayMode.h (261711 => 261712)


--- trunk/Source/_javascript_Core/dfg/DFGArrayMode.h	2020-05-14 21:50:41 UTC (rev 261711)
+++ trunk/Source/_javascript_Core/dfg/DFGArrayMode.h	2020-05-14 22:01:50 UTC (rev 261712)
@@ -237,6 +237,7 @@
         return ArrayMode(type, arrayClass(), speculation(), conversion, action());
     }
     
+    static constexpr SpeculatedType unusedIndexSpeculatedType = SpecInt32Only;
     ArrayMode refine(Graph&, Node*, SpeculatedType base, SpeculatedType index, SpeculatedType value = SpecNone) const;
     
     bool alreadyChecked(Graph&, Node*, const AbstractValue&) const;

Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (261711 => 261712)


--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2020-05-14 21:50:41 UTC (rev 261711)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2020-05-14 22:01:50 UTC (rev 261712)
@@ -1353,6 +1353,11 @@
             def(HeapLocation(ArrayLengthLoc, MiscFields, node->child1()), LazyNode(node));
             return;
 
+        case Array::ForceExit: {
+            write(SideState);
+            return;
+        }
+
         default:
             ASSERT(mode.isSomeTypedArrayView());
             read(MiscFields);

Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (261711 => 261712)


--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2020-05-14 21:50:41 UTC (rev 261711)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2020-05-14 22:01:50 UTC (rev 261712)
@@ -2021,6 +2021,14 @@
         }
 
         case GetArrayLength: {
+            ArrayMode arrayMode = node->arrayMode().refine(m_graph, node, node->child1()->prediction(), ArrayMode::unusedIndexSpeculatedType);
+            // We don't know how to handle generic and we only emit this in the Parser when we have checked the value is an Array/TypedArray.
+            if (arrayMode.type() == Array::Generic)
+                arrayMode = arrayMode.withType(Array::ForceExit);
+            ASSERT(arrayMode.isSpecific() || arrayMode.type() == Array::ForceExit);
+            node->setArrayMode(arrayMode);
+            blessArrayOperation(node->child1(), Edge(), node->child2(), lengthNeedsStorage);
+
             fixEdge<KnownCellUse>(node->child1());
             break;
         }
@@ -3519,7 +3527,7 @@
         }
     }
     
-    void blessArrayOperation(Edge base, Edge index, Edge& storageChild)
+    void blessArrayOperation(Edge base, Edge index, Edge& storageChild, bool (*storageCheck)(const ArrayMode&) = canCSEStorage)
     {
         Node* node = m_currentNode;
         
@@ -3539,7 +3547,7 @@
             return;
             
         default: {
-            Node* storage = checkArray(node->arrayMode(), node->origin, base.node(), index.node());
+            Node* storage = checkArray(node->arrayMode(), node->origin, base.node(), index.node(), storageCheck);
             if (!storage)
                 return;
             
@@ -3828,7 +3836,7 @@
         }
             
         arrayMode = arrayMode.refine(
-            m_graph, node, node->child1()->prediction(), node->prediction());
+            m_graph, node, node->child1()->prediction(), ArrayMode::unusedIndexSpeculatedType);
             
         if (arrayMode.type() == Array::Generic) {
             // Check if the input is something that we can't get array length for, but for which we

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (261711 => 261712)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2020-05-14 21:50:41 UTC (rev 261711)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2020-05-14 22:01:50 UTC (rev 261712)
@@ -832,13 +832,14 @@
 
 void SpeculativeJIT::checkArray(Node* node)
 {
-    ASSERT(node->arrayMode().isSpecific());
-    ASSERT(!node->arrayMode().doesConversion());
+    ArrayMode arrayMode = node->arrayMode();
+    ASSERT(arrayMode.isSpecific());
+    ASSERT(!arrayMode.doesConversion());
     
     SpeculateCellOperand base(this, node->child1());
     GPRReg baseReg = base.gpr();
     
-    if (node->arrayMode().alreadyChecked(m_jit.graph(), node, m_state.forNode(node->child1()))) {
+    if (arrayMode.alreadyChecked(m_jit.graph(), node, m_state.forNode(node->child1()))) {
         // We can purge Empty check completely in this case of CheckArrayOrEmpty since CellUse only accepts SpecCell | SpecEmpty.
         ASSERT(typeFilterFor(node->child1().useKind()) & SpecEmpty);
         noResult(m_currentNode);
@@ -847,7 +848,7 @@
 
     Optional<GPRTemporary> temp;
     Optional<GPRReg> tempGPR;
-    switch (node->arrayMode().type()) {
+    switch (arrayMode.type()) {
     case Array::Int32:
     case Array::Double:
     case Array::Contiguous:
@@ -871,8 +872,7 @@
     }
 #endif
 
-    switch (node->arrayMode().type()) {
-    case Array::AnyTypedArray:
+    switch (arrayMode.type()) {
     case Array::String:
         RELEASE_ASSERT_NOT_REACHED(); // Should have been a Phantom(String:)
         return;
@@ -885,7 +885,7 @@
         m_jit.load8(MacroAssembler::Address(baseReg, JSCell::indexingTypeAndMiscOffset()), tempGPR.value());
         speculationCheck(
             BadIndexingType, JSValueSource::unboxedCell(baseReg), nullptr,
-            jumpSlowForUnwantedArrayMode(tempGPR.value(), node->arrayMode()));
+            jumpSlowForUnwantedArrayMode(tempGPR.value(), arrayMode));
         break;
     }
     case Array::DirectArguments:
@@ -894,12 +894,16 @@
     case Array::ScopedArguments:
         speculateCellTypeWithoutTypeFiltering(node->child1(), baseReg, ScopedArgumentsType);
         break;
-    default:
-        speculateCellTypeWithoutTypeFiltering(
-            node->child1(), baseReg,
-            typeForTypedArrayType(node->arrayMode().typedArrayType()));
+    default: {
+        DFG_ASSERT(m_graph, node, arrayMode.isSomeTypedArrayView());
+
+        if (arrayMode.type() == Array::AnyTypedArray)
+            speculationCheck(BadType, JSValueSource::unboxedCell(baseReg), nullptr, m_jit.branchIfNotType(baseReg, JSType(FirstTypedArrayType), JSType(LastTypedArrayTypeExcludingDataView)));
+        else
+            speculateCellTypeWithoutTypeFiltering(node->child1(), baseReg, typeForTypedArrayType(arrayMode.typedArrayType()));
         break;
     }
+    }
 
     if (isEmpty.isSet())
         isEmpty.link(&m_jit);

Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (261711 => 261712)


--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2020-05-14 21:50:41 UTC (rev 261711)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2020-05-14 22:01:50 UTC (rev 261712)
@@ -17959,11 +17959,15 @@
                 m_out.load8ZeroExt32(cell, m_heaps.JSCell_typeInfoType),
                 m_out.constInt32(ScopedArgumentsType));
             
-        default:
+        default: {
+            DFG_ASSERT(m_graph, m_node, arrayMode.isSomeTypedArrayView());
+            if (arrayMode.type() == Array::AnyTypedArray)
+                return isTypedArrayView(cell);
             return m_out.equal(
                 m_out.load8ZeroExt32(cell, m_heaps.JSCell_typeInfoType),
                 m_out.constInt32(typeForTypedArrayType(arrayMode.typedArrayType())));
         }
+        }
     }
     
     LValue isFunction(LValue cell, SpeculatedType type = SpecFullTop)

Modified: trunk/Source/_javascript_Core/jit/AssemblyHelpers.h (261711 => 261712)


--- trunk/Source/_javascript_Core/jit/AssemblyHelpers.h	2020-05-14 21:50:41 UTC (rev 261711)
+++ trunk/Source/_javascript_Core/jit/AssemblyHelpers.h	2020-05-14 22:01:50 UTC (rev 261712)
@@ -980,14 +980,31 @@
             Below, Address(cellGPR, JSCell::typeInfoTypeOffset()), TrustedImm32(ObjectType));
     }
     
-    Jump branchIfType(GPRReg cellGPR, JSType type)
+    // Note that first and last are inclusive.
+    Jump branchIfType(GPRReg cellGPR, JSType first, Optional<JSType> last = WTF::nullopt)
     {
-        return branch8(Equal, Address(cellGPR, JSCell::typeInfoTypeOffset()), TrustedImm32(type));
+        if (last && *last != first) {
+            ASSERT(*last > first);
+            GPRReg scratch = scratchRegister();
+            load8(Address(cellGPR, JSCell::typeInfoTypeOffset()), scratch);
+            sub32(TrustedImm32(first), scratch);
+            return branch32(BelowOrEqual, scratch, TrustedImm32(*last - first));
+        }
+
+        return branch8(Equal, Address(cellGPR, JSCell::typeInfoTypeOffset()), TrustedImm32(first));
     }
-    
-    Jump branchIfNotType(GPRReg cellGPR, JSType type)
+
+    Jump branchIfNotType(GPRReg cellGPR, JSType first, Optional<JSType> last = WTF::nullopt)
     {
-        return branch8(NotEqual, Address(cellGPR, JSCell::typeInfoTypeOffset()), TrustedImm32(type));
+        if (last && *last != first) {
+            ASSERT(*last > first);
+            GPRReg scratch = scratchRegister();
+            load8(Address(cellGPR, JSCell::typeInfoTypeOffset()), scratch);
+            sub32(TrustedImm32(first), scratch);
+            return branch32(Above, scratch, TrustedImm32(*last - first));
+        }
+
+        return branch8(NotEqual, Address(cellGPR, JSCell::typeInfoTypeOffset()), TrustedImm32(first));
     }
 
     // FIXME: rename these to make it clear that they require their input to be a cell.

Modified: trunk/Source/_javascript_Core/runtime/JSType.h (261711 => 261712)


--- trunk/Source/_javascript_Core/runtime/JSType.h	2020-05-14 21:50:41 UTC (rev 261711)
+++ trunk/Source/_javascript_Core/runtime/JSType.h	2020-05-14 22:01:50 UTC (rev 261712)
@@ -132,6 +132,7 @@
 
 static constexpr uint32_t FirstTypedArrayType = Int8ArrayType;
 static constexpr uint32_t LastTypedArrayType = DataViewType;
+static constexpr uint32_t LastTypedArrayTypeExcludingDataView = LastTypedArrayType - 1;
 
 // LastObjectType should be MaxJSType (not LastJSCObjectType) since embedders can add their extended object types after the enums listed in JSType.
 static constexpr uint32_t FirstObjectType = ObjectType;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to