Title: [205064] trunk
Revision
205064
Author
[email protected]
Date
2016-08-26 17:36:15 -0700 (Fri, 26 Aug 2016)

Log Message

[JSC] Implement CompareStrictEq(String, Untyped) in FTL
https://bugs.webkit.org/show_bug.cgi?id=161229

Reviewed by Geoffrey Garen.

JSTests:

* stress/compare-strict-eq-on-various-types.js: Added.

Source/_javascript_Core:

Add (String, Untyped) uses to FTL CompareStrictEq.
This was the last use type not implemented, the node is fully
supported by FTL after this patch.

* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):
(JSC::FTL::DFG::LowerDFGToB3::compileStringToUntypedStrictEquality):

(JSC::FTL::DFG::LowerDFGToB3::nonSpeculativeCompare):
Remove the type checks when possible.

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (205063 => 205064)


--- trunk/JSTests/ChangeLog	2016-08-27 00:33:29 UTC (rev 205063)
+++ trunk/JSTests/ChangeLog	2016-08-27 00:36:15 UTC (rev 205064)
@@ -1,3 +1,12 @@
+2016-08-26  Benjamin Poulain  <[email protected]>
+
+        [JSC] Implement CompareStrictEq(String, Untyped) in FTL
+        https://bugs.webkit.org/show_bug.cgi?id=161229
+
+        Reviewed by Geoffrey Garen.
+
+        * stress/compare-strict-eq-on-various-types.js: Added.
+
 2016-08-26  Yusuke Suzuki  <[email protected]>
 
         [ES6] newPromiseCapabilities should check the given argument is constructor

Added: trunk/JSTests/stress/compare-strict-eq-on-various-types.js (0 => 205064)


--- trunk/JSTests/stress/compare-strict-eq-on-various-types.js	                        (rev 0)
+++ trunk/JSTests/stress/compare-strict-eq-on-various-types.js	2016-08-27 00:36:15 UTC (rev 205064)
@@ -0,0 +1,240 @@
+"use strict";
+
+function opaqueKitString() {
+    return "Kit";
+}
+noInline(opaqueKitString);
+
+let someObject = new Object;
+let validInputTestCases = [
+    "undefined",
+    "Symbol(\"WebKit\")",
+    "null",
+    "true",
+    "false",
+    "0",
+    "{ valueOf: () => { return Math.E; } }",
+    "-0",
+    "42",
+    "-42",
+    "Math.PI",
+    "NaN",
+    "\"WebKit\"",
+    "\"Web\" + opaqueKitString()",
+    "new String(\"WebKit\")",
+    "someObject",
+    "validInputTestCases",
+];
+
+let leftCases = validInputTestCases.map((element) => { return eval("(" + element + ")"); });
+let rightCases = validInputTestCases.map((element) => { return eval("(" + element + ")"); });
+
+// Baseline results.
+let expectedEqualResults = [];
+let expectedNotEqualResults = [];
+for (let testCaseInputLeft of leftCases) {
+    let equalResultRow = [];
+    let notEqualResultRow = [];
+    for (let testCaseInputRight of rightCases) {
+        equalResultRow.push(testCaseInputLeft === testCaseInputRight);
+        notEqualResultRow.push(testCaseInputLeft !== testCaseInputRight);
+    }
+    expectedEqualResults.push(equalResultRow);
+    expectedNotEqualResults.push(notEqualResultRow);
+}
+
+function isIdentical(result, expected)
+{
+    if (expected === expected) {
+        if (result !== expected)
+            return false;
+        if (!expected && 1 / expected === -Infinity && 1 / result !== -Infinity)
+            return false;
+
+        return true;
+    }
+    return result !== result;
+}
+
+
+// Very polymorphic compare.
+function opaqueStrictEqualAllTypes(left, right) {
+    return left === right;
+}
+noInline(opaqueStrictEqualAllTypes);
+noOSRExitFuzzing(opaqueStrictEqualAllTypes);
+
+function opaqueStrictNotEqualAllTypes(left, right) {
+    return left !== right;
+}
+noInline(opaqueStrictNotEqualAllTypes);
+noOSRExitFuzzing(opaqueStrictNotEqualAllTypes);
+
+function testAllTypesCall() {
+    for (let i = 0; i < 1e3; ++i) {
+        for (let leftCaseIndex = 0; leftCaseIndex < leftCases.length; ++leftCaseIndex) {
+            for (let rightCaseIndex = 0; rightCaseIndex < rightCases.length; ++rightCaseIndex) {
+                let strictEqualOutput = opaqueStrictEqualAllTypes(leftCases[leftCaseIndex], rightCases[rightCaseIndex]);
+                if (!isIdentical(strictEqualOutput, expectedEqualResults[leftCaseIndex][rightCaseIndex])) {
+                    throw "Failed testAllTypesCall for equality. Left = " +
+                        leftCases[leftCaseIndex] +
+                        ", Right = " +
+                        rightCases[rightCaseIndex] +
+                        ", Result = " +
+                        strictEqualOutput;
+                }
+
+                let strictNotEqualOutput = opaqueStrictNotEqualAllTypes(leftCases[leftCaseIndex], rightCases[rightCaseIndex]);
+                if (!isIdentical(strictNotEqualOutput, expectedNotEqualResults[leftCaseIndex][rightCaseIndex])) {
+                    throw "Failed testAllTypesCall for !equality. Left = " +
+                        leftCases[leftCaseIndex] +
+                        ", Right = " +
+                        rightCases[rightCaseIndex] +
+                        ", Result = " +
+                        strictEqualOutput;
+                }
+            }
+        }
+    }
+    if (numberOfDFGCompiles(opaqueStrictEqualAllTypes) > 2)
+        throw "opaqueStrictEqualAllTypes() should have been quickly compiled as fully polymorphic.";
+    if (opaqueStrictNotEqualAllTypes(opaqueStrictEqualAllTypes) > 2)
+        throw "opaqueStrictEqualAllTypes() should have been quickly compiled as fully polymorphic.";
+}
+testAllTypesCall();
+
+
+// Comparing String to every type.
+function opaqueStrictEqualStringToAllTypes(left, right) {
+    return left === right;
+}
+noInline(opaqueStrictEqualStringToAllTypes);
+noOSRExitFuzzing(opaqueStrictEqualStringToAllTypes);
+
+function opaqueStrictEqualAllTypesToString(left, right) {
+    return left === right;
+}
+noInline(opaqueStrictEqualAllTypesToString);
+noOSRExitFuzzing(opaqueStrictEqualAllTypesToString);
+
+function opaqueStrictNotEqualStringToAllTypes(left, right) {
+    return left !== right;
+}
+noInline(opaqueStrictNotEqualStringToAllTypes);
+noOSRExitFuzzing(opaqueStrictNotEqualStringToAllTypes);
+
+function opaqueStrictNotEqualAllTypesToString(left, right) {
+    return left !== right;
+}
+noInline(opaqueStrictNotEqualAllTypesToString);
+noOSRExitFuzzing(opaqueStrictNotEqualAllTypesToString);
+
+function testStringToAllCompare() {
+    const leftStringIndex = leftCases.indexOf("WebKit");
+    for (let i = 0; i < 1e3; ++i) {
+        for (let rightCaseIndex = 0; rightCaseIndex < rightCases.length; ++rightCaseIndex) {
+            let rightCase = rightCases[rightCaseIndex];
+            let strictEqualOutput = opaqueStrictEqualStringToAllTypes("Web" + opaqueKitString(), rightCase);
+            if (!isIdentical(strictEqualOutput, expectedEqualResults[leftStringIndex][rightCaseIndex])) {
+                throw "Failed opaqueStrictEqualStringToAllTypes() with right = " + rightCase;
+            }
+            let strictNotEqualOutput = opaqueStrictNotEqualStringToAllTypes("Web" + opaqueKitString(), rightCase);
+            if (!isIdentical(strictNotEqualOutput, expectedNotEqualResults[leftStringIndex][rightCaseIndex])) {
+                throw "Failed opaqueStrictNotEqualStringToAllTypes() with right = " + rightCase;
+            }
+        }
+    }
+
+    const rightStringIndex = leftCases.lastIndexOf("WebKit");
+    for (let i = 0; i < 1e3; ++i) {
+        for (let leftCaseIndex = 0; leftCaseIndex < leftCases.length; ++leftCaseIndex) {
+            let leftCase = leftCases[leftCaseIndex];
+            let strictEqualOutput = opaqueStrictEqualAllTypesToString(leftCase, "Web" + opaqueKitString());
+            if (!isIdentical(strictEqualOutput, expectedEqualResults[leftCaseIndex][rightStringIndex])) {
+                throw "Failed opaqueStrictEqualAllTypesToString() with left = " + leftCase;
+            }
+            let strictNotEqualOutput = opaqueStrictNotEqualAllTypesToString(leftCase, "Web" + opaqueKitString());
+            if (!isIdentical(strictNotEqualOutput, expectedNotEqualResults[leftCaseIndex][rightStringIndex])) {
+                throw "Failed opaqueStrictNotEqualAllTypesToString() with left = " + leftCase;
+            }
+        }
+    }
+
+    if (numberOfDFGCompiles(opaqueStrictEqualStringToAllTypes) > 2)
+        throw "opaqueStrictEqualStringToAllTypes() should quickly converge its types.";
+    if (numberOfDFGCompiles(opaqueStrictEqualAllTypesToString) > 2)
+        throw "opaqueStrictEqualAllTypesToString() should quickly converge its types.";
+    if (numberOfDFGCompiles(opaqueStrictNotEqualStringToAllTypes) > 2)
+        throw "opaqueStrictNotEqualStringToAllTypes() should quickly converge its types.";
+    if (numberOfDFGCompiles(opaqueStrictNotEqualAllTypesToString) > 2)
+        throw "opaqueStrictNotEqualAllTypesToString() should quickly converge its types.";
+}
+testStringToAllCompare();
+
+
+// Compare one type to all the others.
+function compareOneTypeToAll() {
+    for (let leftCaseIndex = 0; leftCaseIndex < validInputTestCases.length; ++leftCaseIndex) {
+        let leftCase = validInputTestCases[leftCaseIndex];
+        eval(`
+            function opaqueStrictEqualOneTypeToAll(left, right) {
+                return left === right;
+            }
+            noInline(opaqueStrictEqualOneTypeToAll);
+            noOSRExitFuzzing(opaqueStrictEqualOneTypeToAll);
+
+            function opaqueStrictNotEqualOneTypeToAll(left, right) {
+                return left !== right;
+            }
+            noInline(opaqueStrictNotEqualOneTypeToAll);
+            noOSRExitFuzzing(opaqueStrictNotEqualOneTypeToAll);
+
+            for (let i = 0; i < 1e3; ++i) {
+                for (let rightCaseIndex = 0; rightCaseIndex < rightCases.length; ++rightCaseIndex) {
+                    let strictEqualOutput = opaqueStrictEqualOneTypeToAll(${leftCase}, rightCases[rightCaseIndex]);
+                    if (!isIdentical(strictEqualOutput, expectedEqualResults[${leftCaseIndex}][rightCaseIndex])) {
+                        throw "Failed opaqueStrictEqualOneTypeToAll() with left case = " + ${leftCase} + ", right case = " + rightCases[rightCaseIndex];
+                    }
+                    let strictNotEqualOutput = opaqueStrictNotEqualOneTypeToAll(${leftCase}, rightCases[rightCaseIndex]);
+                    if (!isIdentical(strictNotEqualOutput, expectedNotEqualResults[${leftCaseIndex}][rightCaseIndex])) {
+                        throw "Failed opaqueStrictNotEqualOneTypeToAll() with left case = " + ${leftCase} + ", right case = " + rightCases[rightCaseIndex];
+                    }
+                }
+            }
+             `);
+    }
+}
+compareOneTypeToAll();
+
+function compareAllTypesToOne() {
+    for (let rightCaseIndex = 0; rightCaseIndex < validInputTestCases.length; ++rightCaseIndex) {
+        let rightCase = validInputTestCases[rightCaseIndex];
+        eval(`
+            function opaqueStrictEqualAllToOne(left, right) {
+                return left === right;
+            }
+            noInline(opaqueStrictEqualAllToOne);
+            noOSRExitFuzzing(opaqueStrictEqualAllToOne);
+
+            function opaqueStrictNotEqualAllToOne(left, right) {
+                return left !== right;
+            }
+            noInline(opaqueStrictNotEqualAllToOne);
+            noOSRExitFuzzing(opaqueStrictNotEqualAllToOne);
+
+            for (let i = 0; i < 1e3; ++i) {
+                for (let leftCaseIndex = 0; leftCaseIndex < leftCases.length; ++leftCaseIndex) {
+                    let strictEqualOutput = opaqueStrictEqualAllToOne(leftCases[leftCaseIndex], ${rightCase});
+                    if (!isIdentical(strictEqualOutput, expectedEqualResults[leftCaseIndex][${rightCaseIndex}])) {
+                        throw "Failed opaqueStrictEqualAllToOne() with left case = " + leftCases[leftCaseIndex] + ", right case = " + ${rightCase};
+                    }
+                    let strictNotEqualOutput = opaqueStrictNotEqualAllToOne(leftCases[leftCaseIndex], ${rightCase});
+                    if (!isIdentical(strictNotEqualOutput, expectedNotEqualResults[leftCaseIndex][${rightCaseIndex}])) {
+                        throw "Failed opaqueStrictNotEqualAllToOne() with left case = " + leftCases[leftCaseIndex] + ", right case = " + ${rightCase};
+                    }
+                }
+            }
+             `);
+    }
+}
+compareAllTypesToOne();

Modified: trunk/Source/_javascript_Core/ChangeLog (205063 => 205064)


--- trunk/Source/_javascript_Core/ChangeLog	2016-08-27 00:33:29 UTC (rev 205063)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-08-27 00:36:15 UTC (rev 205064)
@@ -1,3 +1,23 @@
+2016-08-26  Benjamin Poulain  <[email protected]>
+
+        [JSC] Implement CompareStrictEq(String, Untyped) in FTL
+        https://bugs.webkit.org/show_bug.cgi?id=161229
+
+        Reviewed by Geoffrey Garen.
+
+        Add (String, Untyped) uses to FTL CompareStrictEq.
+        This was the last use type not implemented, the node is fully
+        supported by FTL after this patch.
+
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):
+        (JSC::FTL::DFG::LowerDFGToB3::compileStringToUntypedStrictEquality):
+
+        (JSC::FTL::DFG::LowerDFGToB3::nonSpeculativeCompare):
+        Remove the type checks when possible.
+
 2016-08-26  Johan K. Jensen  <[email protected]>
 
         Web Inspector: Frontend should have access to Resource Timing information

Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (205063 => 205064)


--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2016-08-27 00:33:29 UTC (rev 205063)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2016-08-27 00:36:15 UTC (rev 205064)
@@ -246,6 +246,7 @@
     case GetDynamicVar:
     case PutDynamicVar:
     case CompareEqPtr:
+    case CompareStrictEq:
         // These are OK.
         break;
 
@@ -388,42 +389,6 @@
         if (node->child1().useKind() == OtherUse || node->child2().useKind() == OtherUse)
             break;
         return CannotCompile;
-    case CompareStrictEq:
-        if (node->isBinaryUseKind(Int32Use))
-            break;
-        if (node->isBinaryUseKind(Int52RepUse))
-            break;
-        if (node->isBinaryUseKind(DoubleRepUse))
-            break;
-        if (node->isBinaryUseKind(StringIdentUse))
-            break;
-        if (node->isBinaryUseKind(StringUse))
-            break;
-        if (node->isBinaryUseKind(ObjectUse, UntypedUse))
-            break;
-        if (node->isBinaryUseKind(UntypedUse, ObjectUse))
-            break;
-        if (node->isBinaryUseKind(ObjectUse))
-            break;
-        if (node->isBinaryUseKind(BooleanUse))
-            break;
-        if (node->isBinaryUseKind(UntypedUse))
-            break;
-        if (node->isBinaryUseKind(SymbolUse))
-            break;
-        if (node->isBinaryUseKind(SymbolUse, UntypedUse))
-            break;
-        if (node->isBinaryUseKind(UntypedUse, SymbolUse))
-            break;
-        if (node->isBinaryUseKind(MiscUse, UntypedUse))
-            break;
-        if (node->isBinaryUseKind(UntypedUse, MiscUse))
-            break;
-        if (node->isBinaryUseKind(StringIdentUse, NotStringVarUse))
-            break;
-        if (node->isBinaryUseKind(NotStringVarUse, StringIdentUse))
-            break;
-        return CannotCompile;
     case CompareLess:
     case CompareLessEq:
     case CompareGreater:

Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (205063 => 205064)


--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2016-08-27 00:33:29 UTC (rev 205063)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2016-08-27 00:36:15 UTC (rev 205064)
@@ -5102,15 +5102,6 @@
             return;
         }
 
-        if (m_node->isBinaryUseKind(UntypedUse)) {
-            nonSpeculativeCompare(
-                [&] (LValue left, LValue right) {
-                    return m_out.equal(left, right);
-                },
-                operationCompareStrictEq);
-            return;
-        }
-
         if (m_node->isBinaryUseKind(SymbolUse)) {
             LValue leftSymbol = lowSymbol(m_node->child1());
             LValue rightSymbol = lowSymbol(m_node->child2());
@@ -5175,10 +5166,63 @@
             setBoolean(m_out.phi(Int32, notCellResult, notStringResult, isStringResult));
             return;
         }
-        
-        DFG_CRASH(m_graph, m_node, "Bad use kinds");
+
+        if (m_node->isBinaryUseKind(StringUse, UntypedUse)) {
+            compileStringToUntypedStrictEquality(m_node->child1(), m_node->child2());
+            return;
+        }
+        if (m_node->isBinaryUseKind(UntypedUse, StringUse)) {
+            compileStringToUntypedStrictEquality(m_node->child2(), m_node->child1());
+            return;
+        }
+
+        DFG_ASSERT(m_graph, m_node, m_node->isBinaryUseKind(UntypedUse));
+        nonSpeculativeCompare(
+            [&] (LValue left, LValue right) {
+                return m_out.equal(left, right);
+            },
+            operationCompareStrictEq);
     }
-    
+
+    void compileStringToUntypedStrictEquality(Edge stringEdge, Edge untypedEdge)
+    {
+        ASSERT(stringEdge.useKind() == StringUse);
+        ASSERT(untypedEdge.useKind() == UntypedUse);
+
+        LValue leftString = lowCell(stringEdge);
+        LValue rightValue = lowJSValue(untypedEdge);
+        SpeculatedType rightValueType = provenType(untypedEdge);
+
+        // Verify left is string.
+        speculateString(stringEdge, leftString);
+
+        LBasicBlock testUntypedEdgeIsCell = m_out.newBlock();
+        LBasicBlock testUntypedEdgeIsString = m_out.newBlock();
+        LBasicBlock testStringEquality = m_out.newBlock();
+        LBasicBlock continuation = m_out.newBlock();
+
+        // Given left is string. If the value are strictly equal, rightValue has to be the same string.
+        ValueFromBlock fastTrue = m_out.anchor(m_out.booleanTrue);
+        m_out.branch(m_out.equal(leftString, rightValue), unsure(continuation), unsure(testUntypedEdgeIsCell));
+
+        LBasicBlock lastNext = m_out.appendTo(testUntypedEdgeIsCell, testUntypedEdgeIsString);
+        ValueFromBlock fastFalse = m_out.anchor(m_out.booleanFalse);
+        m_out.branch(isNotCell(rightValue, rightValueType), unsure(continuation), unsure(testUntypedEdgeIsString));
+
+        // Check if the untyped edge is a string.
+        m_out.appendTo(testUntypedEdgeIsString, testStringEquality);
+        m_out.branch(isNotString(rightValue, rightValueType), unsure(continuation), unsure(testStringEquality));
+
+        // Full String compare.
+        m_out.appendTo(testStringEquality, continuation);
+        ValueFromBlock slowResult = m_out.anchor(stringsEqual(leftString, rightValue));
+        m_out.jump(continuation);
+
+        // Continuation.
+        m_out.appendTo(continuation, lastNext);
+        setBoolean(m_out.phi(Int32, fastTrue, fastFalse, slowResult));
+    }
+
     void compileCompareEqPtr()
     {
         setBoolean(
@@ -8142,10 +8186,10 @@
         LBasicBlock slowPath = m_out.newBlock();
         LBasicBlock continuation = m_out.newBlock();
         
-        m_out.branch(isNotInt32(left), rarely(slowPath), usually(leftIsInt));
+        m_out.branch(isNotInt32(left, provenType(m_node->child1())), rarely(slowPath), usually(leftIsInt));
         
         LBasicBlock lastNext = m_out.appendTo(leftIsInt, fastPath);
-        m_out.branch(isNotInt32(right), rarely(slowPath), usually(fastPath));
+        m_out.branch(isNotInt32(right, provenType(m_node->child2())), rarely(slowPath), usually(fastPath));
         
         m_out.appendTo(fastPath, slowPath);
         ValueFromBlock fastResult = m_out.anchor(intFunctor(unboxInt32(left), unboxInt32(right)));
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to