Title: [278568] trunk
Revision
278568
Author
rmoris...@apple.com
Date
2021-06-07 12:55:30 -0700 (Mon, 07 Jun 2021)

Log Message

Optimize compareStrictEq when neither side is a double and at least one is neither a string nor a BigInt
https://bugs.webkit.org/show_bug.cgi?id=226676

Reviewed by Filip Pizlo.

JSTests:

I made two variants of the already existing poly-stricteq microbenchmarks with different types in the array.
I also tweaked all three so that we more reliably reach the FTL.
Finally I added a stress-test to verify that I did not introduce an OSR exit bug.

* microbenchmarks/poly-stricteq-not-double-nor-string.js: Added.
(foo):
(test):
* microbenchmarks/poly-stricteq-not-double.js: Added.
(foo):
(test):
* microbenchmarks/poly-stricteq.js:
(foo):
(test):
* stress/poly-stricteq-not-double-nor-string-fail.js: Added.
(foo):
(test):

Source/_javascript_Core:

There is exactly one case where x === y must return false despite x and y being JSValues with the same bits:
    NaN === NaN
There are a few cases where x === y must return true despite x and y being JSValues with potentially different bits:
    Double === Int32
    String === String
    HeapBigInt === HeapBigInt
    HeapBigInt === BigInt32 (if they are enabled)
If we don't have a double on either side, at least one side has neither a String nor a HeapBigInt, and BigInt32 are disabled, we can clearly ignore all of these pathological cases.

This optimization was decided based on looking at DFG graphs of Speedometer2; here is a sample of the compareStrictEq(Untyped, Untyped), courtesy of Phil:
    Final|Array|String|Bool, Final|Array|String|Bool
    Array|String|Bool, String|Bool (twice)
    Array|String|Bool, String|Int32 (once in DFG, once in FTL)
!   Array|String|Bool, Array|Bool
!   Final|Other, Final|Other
!   Int32|Other, Int32
    Final|StringIdent, Final|StringIdent (3 times)
    Final|StringIdent|BoolInt32, StringIdent|BoolInt32 (twice)
    String|Bool, String|Bool (4 times)
    DoublePureNaN, String|Bool
!   Other, Function|Other
!   Final|Other, Final|Function|Other (twice)
    Final|String|Bool|Other, Final|String|Bool|Other (3 times, two in the FTL)
    Final|String|Int32, String|Int32 (four times)
    String|Int32|Bool, Function|String|Int32|Bool (twice)
    String|DoublePureNaN, String|Bool (twice)
!   Final|Bool|Other, Final|Function|Other (four times, twice in FTL)
I marked with a ! those for which this optimization should apply.

The only slightly interesting part of this patch is DFG::SpeculativeJIT::speculateNeitherDoubleNorHeapBigIntNorString where I took care to skip every test whose result we can predict from the abstract interpreter.

Results on microbenchmarks:
    poly-stricteq-not-double                  45.5793+-0.5304     ?     46.0306+-0.5621        ?
    poly-stricteq-not-double-nor-string       45.5829+-0.5750     ^     16.9089+-0.3070        ^ definitely 2.6958x faster
    poly-stricteq                             49.9719+-0.6450           48.9855+-0.5227          might be 1.0201x faster

I also measured the amount of code that we generate in the DFG on JetStream2.
The results here are disappointing but still measurable. Before:
    DFG_fast_CompareStrictEq totalBytes: 468425 count: 10951 avg: 42.774632
    DFG_fast_CompareStrictEq totalBytes: 468020 count: 10917 avg: 42.870752
    DFG_fast_CompareStrictEq totalBytes: 467424 count: 10888 avg: 42.930198
After:
    DFG_fast_CompareStrictEq totalBytes: 463946 count: 10917 avg: 42.497573
    DFG_fast_CompareStrictEq totalBytes: 474492 count: 11138 avg: 42.601185
    DFG_fast_CompareStrictEq totalBytes: 467138 count: 10970 avg: 42.583227

* bytecode/SpeculatedType.h:
(JSC::isNeitherDoubleNorHeapBigIntNorStringSpeculation):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupCompareStrictEqAndSameValue):
* dfg/DFGNode.h:
(JSC::DFG::Node::shouldSpeculateNeitherDoubleNorHeapBigIntNorString):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::SafeToExecuteEdge::operator()):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileStrictEq):
(JSC::DFG::SpeculativeJIT::compileNotDoubleNeitherDoubleNorHeapBigIntNorStringStrictEquality):
(JSC::DFG::SpeculativeJIT::compilePeepHoleNotDoubleNeitherDoubleNorHeapBigIntNorStringStrictEquality):
(JSC::DFG::SpeculativeJIT::speculateNotDouble):
(JSC::DFG::SpeculativeJIT::speculateNeitherDoubleNorHeapBigIntNorString):
(JSC::DFG::SpeculativeJIT::speculate):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGUseKind.cpp:
(WTF::printInternal):
* dfg/DFGUseKind.h:
(JSC::DFG::typeFilterFor):
(JSC::DFG::checkMayCrashIfInputIsEmpty):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):
(JSC::FTL::DFG::LowerDFGToB3::speculate):
(JSC::FTL::DFG::LowerDFGToB3::speculateNeitherDoubleNorHeapBigIntNorString):

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (278567 => 278568)


--- trunk/JSTests/ChangeLog	2021-06-07 19:46:05 UTC (rev 278567)
+++ trunk/JSTests/ChangeLog	2021-06-07 19:55:30 UTC (rev 278568)
@@ -1,3 +1,27 @@
+2021-06-07  Robin Morisset  <rmoris...@apple.com>
+
+        Optimize compareStrictEq when neither side is a double and at least one is neither a string nor a BigInt
+        https://bugs.webkit.org/show_bug.cgi?id=226676
+
+        Reviewed by Filip Pizlo.
+
+        I made two variants of the already existing poly-stricteq microbenchmarks with different types in the array.
+        I also tweaked all three so that we more reliably reach the FTL.
+        Finally I added a stress-test to verify that I did not introduce an OSR exit bug.
+
+        * microbenchmarks/poly-stricteq-not-double-nor-string.js: Added.
+        (foo):
+        (test):
+        * microbenchmarks/poly-stricteq-not-double.js: Added.
+        (foo):
+        (test):
+        * microbenchmarks/poly-stricteq.js:
+        (foo):
+        (test):
+        * stress/poly-stricteq-not-double-nor-string-fail.js: Added.
+        (foo):
+        (test):
+
 2021-06-04  Yusuke Suzuki  <ysuz...@apple.com>
 
         [JSC] Private static method should define privateClassBrandIdentifier in class-scope

Added: trunk/JSTests/microbenchmarks/poly-stricteq-not-double-nor-string.js (0 => 278568)


--- trunk/JSTests/microbenchmarks/poly-stricteq-not-double-nor-string.js	                        (rev 0)
+++ trunk/JSTests/microbenchmarks/poly-stricteq-not-double-nor-string.js	2021-06-07 19:55:30 UTC (rev 278568)
@@ -0,0 +1,40 @@
+//@ skip if $model == "Apple Watch Series 3" # added by mark-jsc-stress-test.py
+//@ runNoFTL
+
+// Tests the performance of polymorphic strict equality.
+// It has most kinds of types, but not Doubles. This is relevant because NaN is the only value that returns false when compared to itself.
+// This test does not have strings or big ints either. This is relevant because strings/big ints at different places in memory can compare equal.
+
+var array = [];
+
+for (var i = 0; i < 1000; ++i) {
+    array.push((i%2) == 0);
+    array.push(i);
+    array.push([i]);
+    var o = {};
+    o["a" + i] = i + 1;
+    array.push(o);
+}
+
+var numStrictEqual = 0;
+
+function foo(x, y)
+{
+    if(x === y)
+        numStrictEqual++;
+}
+
+function test()
+{
+    for (var i = 0; i < array.length; ++i) {
+        for (var j = i + 1; j < array.length; ++j) {
+            foo(array[i], array[j]);
+        }
+    }
+
+    if (numStrictEqual != 249500)
+        throw "Incorrect result: " + numStrictEqual;
+}
+noInline(test)
+test();
+

Added: trunk/JSTests/microbenchmarks/poly-stricteq-not-double.js (0 => 278568)


--- trunk/JSTests/microbenchmarks/poly-stricteq-not-double.js	                        (rev 0)
+++ trunk/JSTests/microbenchmarks/poly-stricteq-not-double.js	2021-06-07 19:55:30 UTC (rev 278568)
@@ -0,0 +1,40 @@
+//@ skip if $model == "Apple Watch Series 3" # added by mark-jsc-stress-test.py
+//@ runNoFTL
+
+// Tests the performance of polymorphic strict equality.
+// It has most kinds of types, but not Doubles. This is relevant because NaN is the only value that returns false when compared to itself.
+
+var array = [];
+
+for (var i = 0; i < 1000; ++i) {
+    array.push((i%2) == 0);
+    array.push(i);
+    array.push("" + i);
+    var o = {};
+    o["a" + i] = i + 1;
+    array.push(o);
+}
+
+var numStrictEqual = 0;
+
+function foo(x, y)
+{
+    if(x === y)
+        numStrictEqual++;
+}
+
+function test()
+{
+    for (var i = 0; i < array.length; ++i) {
+        for (var j = i + 1; j < array.length; ++j) {
+            foo(array[i], array[j]);
+        }
+    }
+
+    if (numStrictEqual != 249500)
+        throw "Incorrect result: " + numStrictEqual;
+}
+noInline(test);
+test();
+
+

Modified: trunk/JSTests/microbenchmarks/poly-stricteq.js (278567 => 278568)


--- trunk/JSTests/microbenchmarks/poly-stricteq.js	2021-06-07 19:46:05 UTC (rev 278567)
+++ trunk/JSTests/microbenchmarks/poly-stricteq.js	2021-06-07 19:55:30 UTC (rev 278568)
@@ -6,8 +6,8 @@
 var array = [];
 
 for (var i = 0; i < 1000; ++i) {
-    array.push(i);
-    array.push((i%2) == 0);
+    array.push((i % 2) == 0);
+    array.push(3.14 * i);
     array.push("" + i);
     var o = {};
     o["a" + i] = i + 1;
@@ -15,15 +15,25 @@
 }
 
 var numStrictEqual = 0;
-for (var i = 0; i < array.length; ++i) {
-    for (var j = i + 1; j < array.length; ++j) {
-        if (array[i] === array[j])
-            numStrictEqual++;
-    }
+
+function foo(x, y)
+{
+    if(x === y)
+        numStrictEqual++;
 }
 
-if (numStrictEqual != 249500)
-    throw "Incorrect result: " + numStrictEqual;
+function test()
+{
+    for (var i = 0; i < array.length; ++i) {
+        for (var j = i + 1; j < array.length; ++j) {
+            foo(array[i], array[j]);
+        }
+    }
 
+    if (numStrictEqual != 249500)
+        throw "Incorrect result: " + numStrictEqual;
+}
+noInline(test);
+test();
 
 

Added: trunk/JSTests/stress/poly-stricteq-not-double-nor-string-fail.js (0 => 278568)


--- trunk/JSTests/stress/poly-stricteq-not-double-nor-string-fail.js	                        (rev 0)
+++ trunk/JSTests/stress/poly-stricteq-not-double-nor-string-fail.js	2021-06-07 19:55:30 UTC (rev 278568)
@@ -0,0 +1,43 @@
+//@ skip if $model == "Apple Watch Series 3" # added by mark-jsc-stress-test.py
+
+var array = [];
+
+for (var i = 0; i < 1000; ++i) {
+    array.push((i%2) == 0);
+    array.push(i);
+    array.push([i]);
+    var o = {};
+    o["a" + i] = i + 1;
+    array.push(o);
+}
+
+var numStrictEqual = 0;
+
+function foo(x, y)
+{
+    if(x === y)
+        numStrictEqual++;
+}
+noInline(foo);
+
+function test()
+{
+    for (var i = 0; i < array.length; ++i) {
+        for (var j = i + 1; j < array.length; ++j) {
+            foo(array[i], array[j]);
+        }
+    }
+
+    if (numStrictEqual != 249500)
+        throw "Incorrect result: " + numStrictEqual;
+
+    foo(42, 42.0);
+    foo(NaN, NaN);
+    foo("foobar", "foo" + "bar")
+
+    if (numStrictEqual != 249502)
+        throw "Incorrect result at the end " + numStrictEqual;
+}
+noInline(test)
+test();
+

Modified: trunk/Source/_javascript_Core/ChangeLog (278567 => 278568)


--- trunk/Source/_javascript_Core/ChangeLog	2021-06-07 19:46:05 UTC (rev 278567)
+++ trunk/Source/_javascript_Core/ChangeLog	2021-06-07 19:55:30 UTC (rev 278568)
@@ -1,3 +1,86 @@
+2021-06-07  Robin Morisset  <rmoris...@apple.com>
+
+        Optimize compareStrictEq when neither side is a double and at least one is neither a string nor a BigInt
+        https://bugs.webkit.org/show_bug.cgi?id=226676
+
+        Reviewed by Filip Pizlo.
+
+        There is exactly one case where x === y must return false despite x and y being JSValues with the same bits:
+            NaN === NaN
+        There are a few cases where x === y must return true despite x and y being JSValues with potentially different bits:
+            Double === Int32
+            String === String
+            HeapBigInt === HeapBigInt
+            HeapBigInt === BigInt32 (if they are enabled)
+        If we don't have a double on either side, at least one side has neither a String nor a HeapBigInt, and BigInt32 are disabled, we can clearly ignore all of these pathological cases.
+
+        This optimization was decided based on looking at DFG graphs of Speedometer2; here is a sample of the compareStrictEq(Untyped, Untyped), courtesy of Phil:
+            Final|Array|String|Bool, Final|Array|String|Bool
+            Array|String|Bool, String|Bool (twice)
+            Array|String|Bool, String|Int32 (once in DFG, once in FTL)
+        !   Array|String|Bool, Array|Bool
+        !   Final|Other, Final|Other
+        !   Int32|Other, Int32
+            Final|StringIdent, Final|StringIdent (3 times)
+            Final|StringIdent|BoolInt32, StringIdent|BoolInt32 (twice)
+            String|Bool, String|Bool (4 times)
+            DoublePureNaN, String|Bool
+        !   Other, Function|Other
+        !   Final|Other, Final|Function|Other (twice)
+            Final|String|Bool|Other, Final|String|Bool|Other (3 times, two in the FTL)
+            Final|String|Int32, String|Int32 (four times)
+            String|Int32|Bool, Function|String|Int32|Bool (twice)
+            String|DoublePureNaN, String|Bool (twice)
+        !   Final|Bool|Other, Final|Function|Other (four times, twice in FTL)
+        I marked with a ! those for which this optimization should apply.
+
+        The only slightly interesting part of this patch is DFG::SpeculativeJIT::speculateNeitherDoubleNorHeapBigIntNorString where I took care to skip every test whose result we can predict from the abstract interpreter.
+
+        Results on microbenchmarks:
+            poly-stricteq-not-double                  45.5793+-0.5304     ?     46.0306+-0.5621        ?
+            poly-stricteq-not-double-nor-string       45.5829+-0.5750     ^     16.9089+-0.3070        ^ definitely 2.6958x faster
+            poly-stricteq                             49.9719+-0.6450           48.9855+-0.5227          might be 1.0201x faster
+
+        I also measured the amount of code that we generate in the DFG on JetStream2.
+        The results here are disappointing but still measurable. Before:
+            DFG_fast_CompareStrictEq totalBytes: 468425 count: 10951 avg: 42.774632
+            DFG_fast_CompareStrictEq totalBytes: 468020 count: 10917 avg: 42.870752
+            DFG_fast_CompareStrictEq totalBytes: 467424 count: 10888 avg: 42.930198
+        After:
+            DFG_fast_CompareStrictEq totalBytes: 463946 count: 10917 avg: 42.497573
+            DFG_fast_CompareStrictEq totalBytes: 474492 count: 11138 avg: 42.601185
+            DFG_fast_CompareStrictEq totalBytes: 467138 count: 10970 avg: 42.583227
+
+        * bytecode/SpeculatedType.h:
+        (JSC::isNeitherDoubleNorHeapBigIntNorStringSpeculation):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupCompareStrictEqAndSameValue):
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::shouldSpeculateNeitherDoubleNorHeapBigIntNorString):
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::SafeToExecuteEdge::operator()):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileStrictEq):
+        (JSC::DFG::SpeculativeJIT::compileNotDoubleNeitherDoubleNorHeapBigIntNorStringStrictEquality):
+        (JSC::DFG::SpeculativeJIT::compilePeepHoleNotDoubleNeitherDoubleNorHeapBigIntNorStringStrictEquality):
+        (JSC::DFG::SpeculativeJIT::speculateNotDouble):
+        (JSC::DFG::SpeculativeJIT::speculateNeitherDoubleNorHeapBigIntNorString):
+        (JSC::DFG::SpeculativeJIT::speculate):
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGUseKind.cpp:
+        (WTF::printInternal):
+        * dfg/DFGUseKind.h:
+        (JSC::DFG::typeFilterFor):
+        (JSC::DFG::checkMayCrashIfInputIsEmpty):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):
+        (JSC::FTL::DFG::LowerDFGToB3::speculate):
+        (JSC::FTL::DFG::LowerDFGToB3::speculateNeitherDoubleNorHeapBigIntNorString):
+
 2021-06-07  Tuomas Karkkainen  <tuomas.web...@apple.com>
 
         $vm should have a function for checking if security assertions are enabled similar to $vm.assertEnabled

Modified: trunk/Source/_javascript_Core/bytecode/SpeculatedType.h (278567 => 278568)


--- trunk/Source/_javascript_Core/bytecode/SpeculatedType.h	2021-06-07 19:46:05 UTC (rev 278567)
+++ trunk/Source/_javascript_Core/bytecode/SpeculatedType.h	2021-06-07 19:55:30 UTC (rev 278568)
@@ -444,6 +444,11 @@
     return !(type & SpecFullDouble);
 }
 
+inline bool isNeitherDoubleNorHeapBigIntNorStringSpeculation(SpeculatedType type)
+{
+    return !(type & (SpecFullDouble | SpecHeapBigInt | SpecString));
+}
+
 inline bool isOtherSpeculation(SpeculatedType value)
 {
     return value == SpecOther;

Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (278567 => 278568)


--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp	2021-06-07 19:46:05 UTC (rev 278567)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp	2021-06-07 19:55:30 UTC (rev 278568)
@@ -47,7 +47,7 @@
     //     1. Allocates any objects.
     //     2. Resolves a rope string, which allocates a string.
     //     3. Produces a string (which allocates the string) except when we can prove that
-    //        the string will always be one of the pre-allcoated SmallStrings.
+    //        the string will always be one of the pre-allocated SmallStrings.
     //     4. Triggers a structure transition (which can allocate a new structure)
     //        unless it is a known transition between previously allocated structures
     //        such as between Array types.
@@ -496,7 +496,8 @@
             || node->isBinaryUseKind(ObjectUse, UntypedUse) || node->isBinaryUseKind(UntypedUse, ObjectUse)
             || node->isBinaryUseKind(ObjectUse)
             || node->isBinaryUseKind(MiscUse, UntypedUse) || node->isBinaryUseKind(UntypedUse, MiscUse)
-            || node->isBinaryUseKind(StringIdentUse, NotStringVarUse) || node->isBinaryUseKind(NotStringVarUse, StringIdentUse))
+            || node->isBinaryUseKind(StringIdentUse, NotStringVarUse) || node->isBinaryUseKind(NotStringVarUse, StringIdentUse)
+            || node->isBinaryUseKind(NotDoubleUse, NeitherDoubleNorHeapBigIntNorStringUse) || node->isBinaryUseKind(NotDoubleUse, NeitherDoubleNorHeapBigIntNorStringUse))
             return false;
         return true;
 

Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (278567 => 278568)


--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2021-06-07 19:46:05 UTC (rev 278567)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2021-06-07 19:55:30 UTC (rev 278568)
@@ -4408,6 +4408,23 @@
             node->setOpAndDefaultFlags(CompareStrictEq);
             return;
         }
+#if !USE(BIGINT32)
+        // As long as a BigInt32 and a HeapBigInt can compare equal, it is not sound to replace compareStrictEq by a simple comparison of the JSValue in the following cases.
+        if (node->child1()->shouldSpeculateNeitherDoubleNorHeapBigIntNorString()
+            && node->child2()->shouldSpeculateNotDouble()) {
+            fixEdge<NeitherDoubleNorHeapBigIntNorStringUse>(node->child1());
+            fixEdge<NotDoubleUse>(node->child2());
+            node->setOpAndDefaultFlags(CompareStrictEq);
+            return;
+        }
+        if (node->child1()->shouldSpeculateNotDouble()
+            && node->child2()->shouldSpeculateNeitherDoubleNorHeapBigIntNorString()) {
+            fixEdge<NotDoubleUse>(node->child1());
+            fixEdge<NeitherDoubleNorHeapBigIntNorStringUse>(node->child2());
+            node->setOpAndDefaultFlags(CompareStrictEq);
+            return;
+        }
+#endif
     }
 
     void fixupChecksInBlock(BasicBlock* block)

Modified: trunk/Source/_javascript_Core/dfg/DFGNode.h (278567 => 278568)


--- trunk/Source/_javascript_Core/dfg/DFGNode.h	2021-06-07 19:46:05 UTC (rev 278567)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.h	2021-06-07 19:55:30 UTC (rev 278568)
@@ -2802,6 +2802,11 @@
     {
         return isNotDoubleSpeculation(prediction());
     }
+
+    bool shouldSpeculateNeitherDoubleNorHeapBigIntNorString()
+    {
+        return isNeitherDoubleNorHeapBigIntNorStringSpeculation(prediction());
+    }
     
     bool shouldSpeculateUntypedForArithmetic()
     {

Modified: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (278567 => 278568)


--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h	2021-06-07 19:46:05 UTC (rev 278567)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h	2021-06-07 19:55:30 UTC (rev 278568)
@@ -93,6 +93,7 @@
         case AnyIntUse:
         case DoubleRepAnyIntUse:
         case NotDoubleUse:
+        case NeitherDoubleNorHeapBigIntNorStringUse:
             return;
             
         case KnownInt32Use:

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (278567 => 278568)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2021-06-07 19:46:05 UTC (rev 278567)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2021-06-07 19:55:30 UTC (rev 278568)
@@ -6933,7 +6933,42 @@
         compileSymbolEquality(node);
         return false;
     }
-    
+
+#if !USE(BIGINT32)
+    if (node->isBinaryUseKind(NotDoubleUse, NeitherDoubleNorHeapBigIntNorStringUse)) {
+        Edge notDoubleChild = node->child1();
+        Edge neitherDoubleNorHeapBigIntNorStringChild = node->child2();
+        unsigned branchIndexInBlock = detectPeepHoleBranch();
+        if (branchIndexInBlock != UINT_MAX) {
+            Node* branchNode = m_block->at(branchIndexInBlock);
+            compilePeepHoleNotDoubleNeitherDoubleNorHeapBigIntNorStringStrictEquality(node, branchNode, notDoubleChild, neitherDoubleNorHeapBigIntNorStringChild);
+            use(notDoubleChild);
+            use(neitherDoubleNorHeapBigIntNorStringChild);
+            m_indexInBlock = branchIndexInBlock;
+            m_currentNode = branchNode;
+            return true;
+        }
+        compileNotDoubleNeitherDoubleNorHeapBigIntNorStringStrictEquality(node, notDoubleChild, neitherDoubleNorHeapBigIntNorStringChild);
+        return false;
+    }
+    if (node->isBinaryUseKind(NeitherDoubleNorHeapBigIntNorStringUse, NotDoubleUse)) {
+        Edge neitherDoubleNorHeapBigIntNorStringChild = node->child1();
+        Edge notDoubleChild = node->child2();
+        unsigned branchIndexInBlock = detectPeepHoleBranch();
+        if (branchIndexInBlock != UINT_MAX) {
+            Node* branchNode = m_block->at(branchIndexInBlock);
+            compilePeepHoleNotDoubleNeitherDoubleNorHeapBigIntNorStringStrictEquality(node, branchNode, notDoubleChild, neitherDoubleNorHeapBigIntNorStringChild);
+            use(notDoubleChild);
+            use(neitherDoubleNorHeapBigIntNorStringChild);
+            m_indexInBlock = branchIndexInBlock;
+            m_currentNode = branchNode;
+            return true;
+        }
+        compileNotDoubleNeitherDoubleNorHeapBigIntNorStringStrictEquality(node, notDoubleChild, neitherDoubleNorHeapBigIntNorStringChild);
+        return false;
+    }
+#endif
+
     if (node->isBinaryUseKind(HeapBigIntUse)) {
         compileHeapBigIntEquality(node);
         return false;
@@ -7163,6 +7198,72 @@
     }
 }
 
+void SpeculativeJIT::compileNotDoubleNeitherDoubleNorHeapBigIntNorStringStrictEquality(Node* node, Edge notDoubleChild, Edge neitherDoubleNorHeapBigIntNorStringChild)
+{
+    JSValueOperand left(this, notDoubleChild, ManualOperandSpeculation);
+    JSValueOperand right(this, neitherDoubleNorHeapBigIntNorStringChild, ManualOperandSpeculation);
+
+    GPRTemporary temp(this);
+#if USE(JSVALUE64)
+    GPRTemporary result(this, Reuse, left, right);
+#else
+    GPRTemporary result(this, Reuse, left, PayloadWord);
+#endif
+    JSValueRegs leftRegs = left.jsValueRegs();
+    JSValueRegs rightRegs = right.jsValueRegs();
+    GPRReg tempGPR = temp.gpr();
+    GPRReg resultGPR = result.gpr();
+
+    speculateNotDouble(notDoubleChild, leftRegs, tempGPR);
+    speculateNeitherDoubleNorHeapBigIntNorString(neitherDoubleNorHeapBigIntNorStringChild, rightRegs, tempGPR);
+
+#if USE(JSVALUE64)
+    m_jit.compare64(JITCompiler::Equal, left.gpr(), right.gpr(), result.gpr());
+#else
+    m_jit.move(TrustedImm32(0), result.gpr());
+    JITCompiler::Jump notEqual = m_jit.branch32(JITCompiler::NotEqual, left.tagGPR(), right.tagGPR());
+    m_jit.compare32(JITCompiler::Equal, left.payloadGPR(), right.payloadGPR(), result.gpr());
+    notEqual.link(&m_jit);
+#endif
+    unblessedBooleanResult(resultGPR, node);
+}
+
+void SpeculativeJIT::compilePeepHoleNotDoubleNeitherDoubleNorHeapBigIntNorStringStrictEquality(Node*, Node* branchNode, Edge notDoubleChild, Edge neitherDoubleNorHeapBigIntNorStringChild)
+{
+    JSValueOperand left(this, notDoubleChild, ManualOperandSpeculation);
+    JSValueOperand right(this, neitherDoubleNorHeapBigIntNorStringChild, ManualOperandSpeculation);
+
+    GPRTemporary temp(this);
+    JSValueRegs leftRegs = left.jsValueRegs();
+    JSValueRegs rightRegs = right.jsValueRegs();
+    GPRReg tempGPR = temp.gpr();
+
+    speculateNotDouble(notDoubleChild, leftRegs, tempGPR);
+    speculateNeitherDoubleNorHeapBigIntNorString(neitherDoubleNorHeapBigIntNorStringChild, rightRegs, tempGPR);
+
+    BasicBlock* taken = branchNode->branchData()->taken.block;
+    BasicBlock* notTaken = branchNode->branchData()->notTaken.block;
+
+#if USE(JSVALUE64)
+    if (taken == nextBlock()) {
+        branch64(JITCompiler::NotEqual, left.gpr(), right.gpr(), notTaken);
+        jump(taken);
+    } else {
+        branch64(JITCompiler::Equal, left.gpr(), right.gpr(), taken);
+        jump(notTaken);
+    }
+#else
+    branch32(JITCompiler::NotEqual, left.tagGPR(), right.tagGPR(), notTaken);
+    if (taken == nextBlock()) {
+        branch32(JITCompiler::NotEqual, left.payloadGPR(), right.payloadGPR(), notTaken);
+        jump(taken);
+    } else {
+        branch32(JITCompiler::Equal, left.payloadGPR(), right.payloadGPR(), taken);
+        jump(notTaken);
+    }
+#endif
+}
+
 void SpeculativeJIT::compileStringEquality(
     Node* node, GPRReg leftGPR, GPRReg rightGPR, GPRReg lengthGPR, GPRReg leftTempGPR,
     GPRReg rightTempGPR, GPRReg leftTemp2GPR, GPRReg rightTemp2GPR,
@@ -11432,6 +11533,23 @@
 #endif
 }
 
+void SpeculativeJIT::speculateNotDouble(Edge edge, JSValueRegs regs, GPRReg tempGPR)
+{
+    if (!needsTypeCheck(edge, ~SpecFullDouble))
+        return;
+
+    JITCompiler::Jump done;
+
+    bool mayBeInt32 = needsTypeCheck(edge, ~SpecInt32Only);
+    if (mayBeInt32)
+        done = m_jit.branchIfInt32(regs);
+
+    DFG_TYPE_CHECK(regs, edge, ~SpecFullDouble, m_jit.branchIfNumber(regs, tempGPR));
+
+    if (mayBeInt32)
+        done.link(&m_jit);
+}
+
 void SpeculativeJIT::speculateNotDouble(Edge edge)
 {
     if (!needsTypeCheck(edge, ~SpecFullDouble))
@@ -11442,11 +11560,46 @@
     JSValueRegs regs = operand.jsValueRegs();
     GPRReg tempGPR = temp.gpr();
     
-    JITCompiler::Jump done = m_jit.branchIfInt32(regs);
+    speculateNotDouble(edge, regs, tempGPR);
+}
+
+void SpeculativeJIT::speculateNeitherDoubleNorHeapBigIntNorString(Edge edge, JSValueRegs regs, GPRReg tempGPR)
+{
+    if (!needsTypeCheck(edge, ~(SpecFullDouble | SpecString)))
+        return;
+
+    MacroAssembler::JumpList done;
+
+    bool mayBeInt32 = needsTypeCheck(edge, ~SpecInt32Only);
+    if (mayBeInt32)
+        done.append(m_jit.branchIfInt32(regs));
+
     DFG_TYPE_CHECK(regs, edge, ~SpecFullDouble, m_jit.branchIfNumber(regs, tempGPR));
-    done.link(&m_jit);
+
+    bool mayNotBeCell = needsTypeCheck(edge, SpecCell);
+    if (mayNotBeCell)
+        done.append(m_jit.branchIfNotCell(regs));
+
+    DFG_TYPE_CHECK(regs, edge, ~SpecString, m_jit.branchIfString(regs.payloadGPR()));
+    DFG_TYPE_CHECK(regs, edge, ~SpecHeapBigInt, m_jit.branchIfHeapBigInt(regs.payloadGPR()));
+
+    if (mayBeInt32 || mayNotBeCell)
+        done.link(&m_jit);
 }
 
+void SpeculativeJIT::speculateNeitherDoubleNorHeapBigIntNorString(Edge edge)
+{
+    if (!needsTypeCheck(edge, ~(SpecFullDouble | SpecString)))
+        return;
+
+    JSValueOperand operand(this, edge, ManualOperandSpeculation);
+    GPRTemporary temp(this);
+    JSValueRegs regs = operand.jsValueRegs();
+    GPRReg tempGPR = temp.gpr();
+
+    speculateNeitherDoubleNorHeapBigIntNorString(edge, regs, tempGPR);
+}
+
 void SpeculativeJIT::speculateOther(Edge edge, JSValueRegs regs, GPRReg tempGPR)
 {
     DFG_TYPE_CHECK(regs, edge, SpecOther, m_jit.branchIfNotOther(regs, tempGPR));
@@ -11632,6 +11785,9 @@
     case NotDoubleUse:
         speculateNotDouble(edge);
         break;
+    case NeitherDoubleNorHeapBigIntNorStringUse:
+        speculateNeitherDoubleNorHeapBigIntNorString(edge);
+        break;
     case OtherUse:
         speculateOther(edge);
         break;

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h (278567 => 278568)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2021-06-07 19:46:05 UTC (rev 278567)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2021-06-07 19:55:30 UTC (rev 278568)
@@ -1189,6 +1189,8 @@
     void compileSymbolEquality(Node*);
     void compileHeapBigIntEquality(Node*);
     void compilePeepHoleSymbolEquality(Node*, Node* branchNode);
+    void compileNotDoubleNeitherDoubleNorHeapBigIntNorStringStrictEquality(Node*, Edge notDoubleEdge, Edge neitherDoubleNorHeapBigIntNorStringEdge);
+    void compilePeepHoleNotDoubleNeitherDoubleNorHeapBigIntNorStringStrictEquality(Node*, Node* branchNode, Edge notDoubleEdge, Edge neitherDoubleNorHeapBigIntNorStringEdge);
     void compileSymbolUntypedEquality(Node*, Edge symbolEdge, Edge untypedEdge);
 
     void emitObjectOrOtherBranch(Edge value, BasicBlock* taken, BasicBlock* notTaken);
@@ -1671,7 +1673,10 @@
     void speculateNotCell(Edge, JSValueRegs);
     void speculateNotCell(Edge);
     void speculateNotCellNorBigInt(Edge);
+    void speculateNotDouble(Edge, JSValueRegs, GPRReg temp);
     void speculateNotDouble(Edge);
+    void speculateNeitherDoubleNorHeapBigIntNorString(Edge, JSValueRegs, GPRReg temp);
+    void speculateNeitherDoubleNorHeapBigIntNorString(Edge);
     void speculateOther(Edge, JSValueRegs, GPRReg temp);
     void speculateOther(Edge, JSValueRegs);
     void speculateOther(Edge);

Modified: trunk/Source/_javascript_Core/dfg/DFGUseKind.cpp (278567 => 278568)


--- trunk/Source/_javascript_Core/dfg/DFGUseKind.cpp	2021-06-07 19:46:05 UTC (rev 278567)
+++ trunk/Source/_javascript_Core/dfg/DFGUseKind.cpp	2021-06-07 19:55:30 UTC (rev 278568)
@@ -173,6 +173,9 @@
     case NotDoubleUse:
         out.print("NotDouble");
         return;
+    case NeitherDoubleNorHeapBigIntNorStringUse:
+        out.print("NeitherDoubleNorHeapBigIntNorString");
+        return;
     case KnownOtherUse:
         out.print("KnownOther");
         return;

Modified: trunk/Source/_javascript_Core/dfg/DFGUseKind.h (278567 => 278568)


--- trunk/Source/_javascript_Core/dfg/DFGUseKind.h	2021-06-07 19:46:05 UTC (rev 278567)
+++ trunk/Source/_javascript_Core/dfg/DFGUseKind.h	2021-06-07 19:55:30 UTC (rev 278568)
@@ -82,6 +82,7 @@
     NotCellUse,
     NotCellNorBigIntUse,
     NotDoubleUse,
+    NeitherDoubleNorHeapBigIntNorStringUse,
     KnownOtherUse,
     OtherUse,
     MiscUse,
@@ -190,6 +191,8 @@
         return ~SpecCellCheck & ~SpecBigInt;
     case NotDoubleUse:
         return ~SpecFullDouble;
+    case NeitherDoubleNorHeapBigIntNorStringUse:
+        return ~SpecFullDouble & ~SpecHeapBigInt & ~SpecString;
     case KnownOtherUse:
     case OtherUse:
         return SpecOther;
@@ -309,6 +312,7 @@
     case NotCellUse:
     case NotCellNorBigIntUse:
     case NotDoubleUse:
+    case NeitherDoubleNorHeapBigIntNorStringUse:
         return false;
     default:
         return true;

Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (278567 => 278568)


--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2021-06-07 19:46:05 UTC (rev 278567)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2021-06-07 19:55:30 UTC (rev 278568)
@@ -529,6 +529,7 @@
                 case AnyIntUse:
                 case DoubleRepAnyIntUse:
                 case NotDoubleUse:
+                case NeitherDoubleNorHeapBigIntNorStringUse:
                     // These are OK.
                     break;
                 default:

Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (278567 => 278568)


--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2021-06-07 19:46:05 UTC (rev 278567)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2021-06-07 19:55:30 UTC (rev 278568)
@@ -9502,7 +9502,13 @@
         }
         
         if (m_node->isBinaryUseKind(MiscUse, UntypedUse)
-            || m_node->isBinaryUseKind(UntypedUse, MiscUse)) {
+            || m_node->isBinaryUseKind(UntypedUse, MiscUse)
+#if !USE(BIGINT32)
+            || m_node->isBinaryUseKind(NotDoubleUse, NeitherDoubleNorHeapBigIntNorStringUse)
+            || m_node->isBinaryUseKind(NeitherDoubleNorHeapBigIntNorStringUse, NotDoubleUse)) {
+#else
+            ) {
+#endif
             speculate(m_node->child1());
             speculate(m_node->child2());
             LValue left = lowJSValue(m_node->child1(), ManualOperandSpeculation);
@@ -18406,6 +18412,9 @@
         case NotDoubleUse:
             speculateNotDouble(edge);
             break;
+        case NeitherDoubleNorHeapBigIntNorStringUse:
+            speculateNeitherDoubleNorHeapBigIntNorString(edge);
+            break;
         case OtherUse:
             speculateOther(edge);
             break;
@@ -18469,6 +18478,31 @@
 
         m_out.appendTo(continuation, lastNext);
     }
+
+    void speculateNeitherDoubleNorHeapBigIntNorString(Edge edge)
+    {
+        if (!m_interpreter.needsTypeCheck(edge))
+            return;
+
+        LValue value = lowJSValue(edge, ManualOperandSpeculation);
+
+        LBasicBlock isNotInt32 = m_out.newBlock();
+        LBasicBlock isCellBlock = m_out.newBlock();
+        LBasicBlock continuation = m_out.newBlock();
+
+        m_out.branch(isInt32(value, provenType(edge)), unsure(continuation), unsure(isNotInt32));
+
+        LBasicBlock lastNext = m_out.appendTo(isNotInt32, isCellBlock);
+        FTL_TYPE_CHECK(jsValueValue(value), edge, ~SpecFullDouble, isNumber(value));
+        m_out.branch(isCell(value, provenType(edge)), unsure(isCellBlock), unsure(continuation));
+
+        m_out.appendTo(isCellBlock, continuation);
+        FTL_TYPE_CHECK(jsValueValue(value), edge, ~SpecString, isString(value));
+        FTL_TYPE_CHECK(jsValueValue(value), edge, ~SpecHeapBigInt, isHeapBigInt(value));
+        m_out.jump(continuation);
+
+        m_out.appendTo(continuation, lastNext);
+    }
     
     void speculateCellOrOther(Edge edge)
     {
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to