Title: [198770] trunk/Source/_javascript_Core
Revision
198770
Author
commit-qu...@webkit.org
Date
2016-03-28 17:59:07 -0700 (Mon, 28 Mar 2016)

Log Message

[JSC] ArithSub should not propagate "UsesAsOther"
https://bugs.webkit.org/show_bug.cgi?id=155932

Patch by Benjamin Poulain <bpoul...@apple.com> on 2016-03-28
Reviewed by Mark Lam.

The node ArithSub was backpropagating UsesAsOther.
This causes any GetByVal on a Double Array to have an extra
hole check if it flows into an ArithSub.

The definition of ArithSub (12.8.4.1) has both operands go
through ToNumber(). ToNumber() on "undefined" always produces
NaN. It is safe to ignore the NaN marker from hole when
the DAG flows into ArithSub.

This patch also adds this change and test coverage to ArithAdd.
ArithAdd was not a problem in practice because it is only
generated before Fixup if both operands are known to be numerical.
The change to ArithAdd is there to protect us of the ArithSub-like
problems if we ever improve our support of arithmetic operators.

* dfg/DFGBackwardsPropagationPhase.cpp:
(JSC::DFG::BackwardsPropagationPhase::propagate):
* tests/stress/arith-add-on-double-array-with-holes.js: Added.
(let.testCase.of.testCases.eval.nonObservableHoleOnLhs):
(let.testCase.of.testCases.observableHoleOnLhs):
(let.testCase.of.testCases.nonObservableHoleOnRhs):
(let.testCase.of.testCases.observableHoleOnRhs):
* tests/stress/arith-sub-on-double-array-with-holes.js: Added.
(let.testCase.of.testCases.eval.nonObservableHoleOnLhs):
(let.testCase.of.testCases.observableHoleOnLhs):
(let.testCase.of.testCases.nonObservableHoleOnRhs):
(let.testCase.of.testCases.observableHoleOnRhs):
* tests/stress/value-add-on-double-array-with-holes.js: Added.
(let.testCase.of.testCases.eval.nonObservableHoleOnLhs):
(let.testCase.of.testCases.observableHoleOnLhs):
(let.testCase.of.testCases.nonObservableHoleOnRhs):
(let.testCase.of.testCases.observableHoleOnRhs):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (198769 => 198770)


--- trunk/Source/_javascript_Core/ChangeLog	2016-03-29 00:58:23 UTC (rev 198769)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-03-29 00:59:07 UTC (rev 198770)
@@ -1,3 +1,43 @@
+2016-03-28  Benjamin Poulain  <bpoul...@apple.com>
+
+        [JSC] ArithSub should not propagate "UsesAsOther"
+        https://bugs.webkit.org/show_bug.cgi?id=155932
+
+        Reviewed by Mark Lam.
+
+        The node ArithSub was backpropagating UsesAsOther.
+        This causes any GetByVal on a Double Array to have an extra
+        hole check if it flows into an ArithSub.
+
+        The definition of ArithSub (12.8.4.1) has both operands go
+        through ToNumber(). ToNumber() on "undefined" always produces
+        NaN. It is safe to ignore the NaN marker from hole when
+        the DAG flows into ArithSub.
+
+        This patch also adds this change and test coverage to ArithAdd.
+        ArithAdd was not a problem in practice because it is only
+        generated before Fixup if both operands are known to be numerical.
+        The change to ArithAdd is there to protect us of the ArithSub-like
+        problems if we ever improve our support of arithmetic operators.
+
+        * dfg/DFGBackwardsPropagationPhase.cpp:
+        (JSC::DFG::BackwardsPropagationPhase::propagate):
+        * tests/stress/arith-add-on-double-array-with-holes.js: Added.
+        (let.testCase.of.testCases.eval.nonObservableHoleOnLhs):
+        (let.testCase.of.testCases.observableHoleOnLhs):
+        (let.testCase.of.testCases.nonObservableHoleOnRhs):
+        (let.testCase.of.testCases.observableHoleOnRhs):
+        * tests/stress/arith-sub-on-double-array-with-holes.js: Added.
+        (let.testCase.of.testCases.eval.nonObservableHoleOnLhs):
+        (let.testCase.of.testCases.observableHoleOnLhs):
+        (let.testCase.of.testCases.nonObservableHoleOnRhs):
+        (let.testCase.of.testCases.observableHoleOnRhs):
+        * tests/stress/value-add-on-double-array-with-holes.js: Added.
+        (let.testCase.of.testCases.eval.nonObservableHoleOnLhs):
+        (let.testCase.of.testCases.observableHoleOnLhs):
+        (let.testCase.of.testCases.nonObservableHoleOnRhs):
+        (let.testCase.of.testCases.observableHoleOnRhs):
+
 2016-03-28  Brian Burg  <bb...@apple.com>
 
         Web Inspector: protocol generator should generate C++ string-to-enum helper functions

Modified: trunk/Source/_javascript_Core/dfg/DFGBackwardsPropagationPhase.cpp (198769 => 198770)


--- trunk/Source/_javascript_Core/dfg/DFGBackwardsPropagationPhase.cpp	2016-03-29 00:58:23 UTC (rev 198769)
+++ trunk/Source/_javascript_Core/dfg/DFGBackwardsPropagationPhase.cpp	2016-03-29 00:59:07 UTC (rev 198770)
@@ -248,6 +248,7 @@
         }
 
         case ArithAdd: {
+            flags &= ~NodeBytecodeUsesAsOther;
             if (isNotNegZero(node->child1().node()) || isNotNegZero(node->child2().node()))
                 flags &= ~NodeBytecodeNeedsNegZero;
             if (!isWithinPowerOfTwo<32>(node->child1()) && !isWithinPowerOfTwo<32>(node->child2()))
@@ -268,6 +269,7 @@
         }
 
         case ArithSub: {
+            flags &= ~NodeBytecodeUsesAsOther;
             if (isNotNegZero(node->child1().node()) || isNotPosZero(node->child2().node()))
                 flags &= ~NodeBytecodeNeedsNegZero;
             if (!isWithinPowerOfTwo<32>(node->child1()) && !isWithinPowerOfTwo<32>(node->child2()))

Added: trunk/Source/_javascript_Core/tests/stress/arith-add-on-double-array-with-holes.js (0 => 198770)


--- trunk/Source/_javascript_Core/tests/stress/arith-add-on-double-array-with-holes.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/arith-add-on-double-array-with-holes.js	2016-03-29 00:59:07 UTC (rev 198770)
@@ -0,0 +1,100 @@
+let testCases = [
+    // Numbers
+    ['1', NaN, NaN, 2, 2],
+    ['1.5', NaN, NaN, 1 + 1.5, 1 + 1.5],
+    [NaN, NaN, NaN, NaN, NaN],
+
+    // Strings.
+    ['""', NaN, NaN, 1, 1],
+    ['new String()', NaN, NaN, 1, 1],
+    ['"WebKit!"', NaN, NaN, NaN, NaN],
+
+    // Objects.
+    ['{ }', NaN, NaN, NaN, NaN],
+    ['{ foo: 1 }', NaN, NaN, NaN, NaN],
+    ['{ toString: function() { return ""; } }', NaN, NaN, 1, 1],
+    ['{ toString: function() { return "WebKit"; } }', NaN, NaN, NaN, NaN],
+
+    // Others.
+    ['null', NaN, NaN, 1, 1],
+    ['undefined', NaN, NaN, NaN, NaN]
+];
+
+for (let testCase of testCases) {
+    let otherOperand = testCase[0];
+    let expectedLeftValueWithHole = testCase[1];
+    let expectedRightValueWithHole = testCase[2];
+    let expectedLeftValue = testCase[3];
+    let expectedRightValue = testCase[4];
+    eval(
+        `// Those holes are not observable by arithmetic operation.
+        // The return value is always going to be NaN.
+        function nonObservableHoleOnLhs(array, otherValue) {
+            return Math.min(array[0]) + Math.min(otherValue);
+        }
+        noInline(nonObservableHoleOnLhs);
+
+        function observableHoleOnLhs(array, otherValue) {
+            let value = array[0];
+            return [Math.min(value) + Math.min(otherValue), value];
+        }
+        noInline(observableHoleOnLhs);
+
+        function nonObservableHoleOnRhs(array, otherValue) {
+            return Math.min(otherValue) + Math.min(array[0]);
+        }
+        noInline(nonObservableHoleOnRhs);
+
+        function observableHoleOnRhs(array, otherValue) {
+            let value = array[0];
+            return [Math.min(otherValue) + Math.min(value), value];
+        }
+        noInline(observableHoleOnRhs);
+
+        let testArray = new Array;
+        for (let i = 1; i < 3; ++i) {
+            testArray[i] = i + 0.5
+        }
+
+        let isEqual = function(a, b) {
+            if (a === a) {
+                return a === b;
+            }
+            return b !== b;
+        }
+
+        for (let i = 0; i < 1e4; ++i) {
+            let lhsResult1 = nonObservableHoleOnLhs(testArray, ${otherOperand});
+            if (!isEqual(lhsResult1, ${expectedLeftValueWithHole}))
+                throw "Error on nonObservableHoleOnLhs at i = " + i + " with operand " + ${otherOperand} + " expected " + ${expectedLeftValueWithHole} + " got " + lhsResult1;
+            let lhsResult2 = observableHoleOnLhs(testArray, ${otherOperand});
+            if (!isEqual(lhsResult2[0], ${expectedLeftValueWithHole}) || lhsResult2[1] !== undefined)
+                throw "Error on observableHoleOnLhs at i = " + i;
+
+            let rhsResult1 = nonObservableHoleOnRhs(testArray, ${otherOperand});
+            if (!isEqual(rhsResult1, ${expectedRightValueWithHole}))
+                throw "Error on nonObservableHoleOnRhs at i = " + i + " with operand " + ${otherOperand} + " expected " + ${expectedRightValueWithHole} + " got " + rhsResult1;
+            let rhsResult2 = observableHoleOnRhs(testArray, ${otherOperand});
+            if (!isEqual(rhsResult2[0], ${expectedRightValueWithHole}) || rhsResult2[1] !== undefined)
+                throw "Error on observableHoleOnRhs at i = " + i;
+        }
+
+        // Fill the hole, make sure everything still work correctly.
+        testArray[0] = 1.;
+        for (let i = 0; i < 1e4; ++i) {
+            let lhsResult1 = nonObservableHoleOnLhs(testArray, ${otherOperand});
+            if (!isEqual(lhsResult1, ${expectedLeftValue}))
+                throw "Error on non hole nonObservableHoleOnLhs at i = " + i + " expected " + ${expectedLeftValue} + " got " + lhsResult1;
+            let lhsResult2 = observableHoleOnLhs(testArray, ${otherOperand});
+            if (!isEqual(lhsResult2[0], ${expectedLeftValue}) || lhsResult2[1] !== 1)
+                throw "Error on non hole observableHoleOnLhs at i = " + i + " expected " + ${expectedLeftValue} + " got " + lhsResult2[0];
+
+            let rhsResult1 = nonObservableHoleOnRhs(testArray, ${otherOperand});
+            if (!isEqual(rhsResult1, ${expectedRightValue}))
+                throw "Error on non hole nonObservableHoleOnRhs at i = " + i + " with operand " + ${otherOperand} + " expected " + ${expectedRightValue} + " got " + rhsResult1;
+            let rhsResult2 = observableHoleOnRhs(testArray, ${otherOperand});
+            if (!isEqual(rhsResult2[0], ${expectedRightValue}) || rhsResult2[1] !== 1)
+                throw "Error on non hole observableHoleOnRhs at i = " + i;
+        }`
+    );
+}
\ No newline at end of file

Added: trunk/Source/_javascript_Core/tests/stress/arith-sub-on-double-array-with-holes.js (0 => 198770)


--- trunk/Source/_javascript_Core/tests/stress/arith-sub-on-double-array-with-holes.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/arith-sub-on-double-array-with-holes.js	2016-03-29 00:59:07 UTC (rev 198770)
@@ -0,0 +1,98 @@
+let testCases = [
+    // Numbers
+    ['1', 0, 0],
+    ['1.5', 1 - 1.5, 1.5 - 1],
+    [NaN, NaN, NaN],
+
+    // Strings.
+    ['""', 1, -1],
+    ['new String()', 1, -1],
+    ['"WebKit!"', NaN, NaN],
+
+    // Objects.
+    ['{ }', NaN, NaN],
+    ['{ foo: 1 }', NaN, NaN],
+    ['{ toString: function() { return ""; } }', 1, -1],
+    ['{ toString: function() { return "WebKit"; } }', NaN, NaN],
+
+    // Others.
+    ['null', 1, -1],
+    ['undefined', NaN, NaN]
+];
+
+for (let testCase of testCases) {
+    let otherOperand = testCase[0];
+    let expectedLeftValue = testCase[1];
+    let expectedRightValue = testCase[2];
+    eval(
+        `// Those holes are not observable by arithmetic operation.
+        // The return value is always going to be NaN.
+        function nonObservableHoleOnLhs(array, otherValue) {
+            return array[0] - otherValue;
+        }
+        noInline(nonObservableHoleOnLhs);
+
+        function observableHoleOnLhs(array, otherValue) {
+            let value = array[0];
+            return [value - otherValue, value];
+        }
+        noInline(observableHoleOnLhs);
+
+        function nonObservableHoleOnRhs(array, otherValue) {
+            return otherValue - array[0];
+        }
+        noInline(nonObservableHoleOnRhs);
+
+        function observableHoleOnRhs(array, otherValue) {
+            let value = array[0];
+            return [otherValue - value, value];
+        }
+        noInline(observableHoleOnRhs);
+
+        let testArray = new Array;
+        for (let i = 1; i < 3; ++i) {
+            testArray[i] = i + 0.5
+        }
+
+        for (let i = 0; i < 1e4; ++i) {
+            let lhsResult1 = nonObservableHoleOnLhs(testArray, ${otherOperand});
+            if (lhsResult1 == lhsResult1)
+                throw "Error on nonObservableHoleOnLhs at i = " + i;
+            let lhsResult2 = observableHoleOnLhs(testArray, ${otherOperand});
+            if (lhsResult2[0] == lhsResult2[0] || lhsResult2[1] !== undefined)
+                throw "Error on observableHoleOnLhs at i = " + i;
+
+            let rhsResult1 = nonObservableHoleOnRhs(testArray, ${otherOperand});
+            if (rhsResult1 == rhsResult1)
+                throw "Error on nonObservableHoleOnRhs at i = " + i;
+            let rhsResult2 = observableHoleOnRhs(testArray, ${otherOperand});
+            if (rhsResult2[0] == rhsResult2[0] || rhsResult2[1] !== undefined)
+                throw "Error on observableHoleOnRhs at i = " + i;
+        }
+
+        let isEqual = function(a, b) {
+            if (a === a) {
+                return a === b;
+            }
+            return b !== b;
+        }
+
+        // Fill the hole, make sure everything still work correctly.
+        testArray[0] = 1.;
+        for (let i = 0; i < 1e4; ++i) {
+            let lhsResult1 = nonObservableHoleOnLhs(testArray, ${otherOperand});
+            if (!isEqual(lhsResult1, ${expectedLeftValue}))
+                throw "Error on non hole nonObservableHoleOnLhs at i = " + i + " expected " + ${expectedLeftValue} + " got " + lhsResult1;
+            let lhsResult2 = observableHoleOnLhs(testArray, ${otherOperand});
+            if (!isEqual(lhsResult2[0], ${expectedLeftValue}) || lhsResult2[1] !== 1)
+                throw "Error on non hole observableHoleOnLhs at i = " + i + " expected " + ${expectedLeftValue} + " got " + lhsResult2[0];
+
+            let rhsResult1 = nonObservableHoleOnRhs(testArray, ${otherOperand});
+            if (!isEqual(rhsResult1, ${expectedRightValue}))
+                throw "Error on non hole nonObservableHoleOnRhs at i = " + i + " expected " + ${expectedRightValue} + " got " + rhsResult1;
+            let rhsResult2 = observableHoleOnRhs(testArray, ${otherOperand});
+            if (!isEqual(rhsResult2[0], ${expectedRightValue}) || rhsResult2[1] !== 1)
+                throw "Error on non hole observableHoleOnRhs at i = " + i;
+        }`
+    );
+}
\ No newline at end of file

Added: trunk/Source/_javascript_Core/tests/stress/value-add-on-double-array-with-holes.js (0 => 198770)


--- trunk/Source/_javascript_Core/tests/stress/value-add-on-double-array-with-holes.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/value-add-on-double-array-with-holes.js	2016-03-29 00:59:07 UTC (rev 198770)
@@ -0,0 +1,100 @@
+let testCases = [
+    // Numbers
+    ['1', NaN, NaN, 2, 2],
+    ['1.5', NaN, NaN, 1 + 1.5, 1 + 1.5],
+    [NaN, NaN, NaN, NaN, NaN],
+
+    // Strings.
+    ['""', '"undefined"', '"undefined"', '"1"', '"1"'],
+    ['new String()', '"undefined"', '"undefined"', '"1"', '"1"'],
+    ['"WebKit!"', '"undefinedWebKit!"', '"WebKit!undefined"', '"1WebKit!"', '"WebKit!1"'],
+
+    // Objects.
+    ['{ }', '"undefined[object Object]"', '"[object Object]undefined"', '"1[object Object]"', '"[object Object]1"'],
+    ['{ foo: 1 }', '"undefined[object Object]"', '"[object Object]undefined"', '"1[object Object]"', '"[object Object]1"'],
+    ['{ toString: function() { return ""; } }', '"undefined"', '"undefined"', '"1"', '"1"'],
+    ['{ toString: function() { return "WebKit"; } }', '"undefinedWebKit"', '"WebKitundefined"', '"1WebKit"', '"WebKit1"'],
+
+    // Others.
+    ['null', NaN, NaN, 1, 1],
+    ['undefined', NaN, NaN, NaN, NaN]
+];
+
+for (let testCase of testCases) {
+    let otherOperand = testCase[0];
+    let expectedLeftValueWithHole = testCase[1];
+    let expectedRightValueWithHole = testCase[2];
+    let expectedLeftValue = testCase[3];
+    let expectedRightValue = testCase[4];
+    eval(
+        `// Those holes are not observable by arithmetic operation.
+        // The return value is always going to be NaN.
+        function nonObservableHoleOnLhs(array, otherValue) {
+            return array[0] + otherValue;
+        }
+        noInline(nonObservableHoleOnLhs);
+
+        function observableHoleOnLhs(array, otherValue) {
+            let value = array[0];
+            return [value + otherValue, value];
+        }
+        noInline(observableHoleOnLhs);
+
+        function nonObservableHoleOnRhs(array, otherValue) {
+            return otherValue + array[0];
+        }
+        noInline(nonObservableHoleOnRhs);
+
+        function observableHoleOnRhs(array, otherValue) {
+            let value = array[0];
+            return [otherValue + value, value];
+        }
+        noInline(observableHoleOnRhs);
+
+        let testArray = new Array;
+        for (let i = 1; i < 3; ++i) {
+            testArray[i] = i + 0.5
+        }
+
+        let isEqual = function(a, b) {
+            if (a === a) {
+                return a === b;
+            }
+            return b !== b;
+        }
+
+        for (let i = 0; i < 1e4; ++i) {
+            let lhsResult1 = nonObservableHoleOnLhs(testArray, ${otherOperand});
+            if (!isEqual(lhsResult1, ${expectedLeftValueWithHole}))
+                throw "Error on nonObservableHoleOnLhs at i = " + i + " with operand " + ${otherOperand} + " expected " + ${expectedLeftValueWithHole} + " got " + lhsResult1;
+            let lhsResult2 = observableHoleOnLhs(testArray, ${otherOperand});
+            if (!isEqual(lhsResult2[0], ${expectedLeftValueWithHole}) || lhsResult2[1] !== undefined)
+                throw "Error on observableHoleOnLhs at i = " + i;
+
+            let rhsResult1 = nonObservableHoleOnRhs(testArray, ${otherOperand});
+            if (!isEqual(rhsResult1, ${expectedRightValueWithHole}))
+                throw "Error on nonObservableHoleOnRhs at i = " + i + " with operand " + ${otherOperand} + " expected " + ${expectedRightValueWithHole} + " got " + rhsResult1;
+            let rhsResult2 = observableHoleOnRhs(testArray, ${otherOperand});
+            if (!isEqual(rhsResult2[0], ${expectedRightValueWithHole}) || rhsResult2[1] !== undefined)
+                throw "Error on observableHoleOnRhs at i = " + i;
+        }
+
+        // Fill the hole, make sure everything still work correctly.
+        testArray[0] = 1.;
+        for (let i = 0; i < 1e4; ++i) {
+            let lhsResult1 = nonObservableHoleOnLhs(testArray, ${otherOperand});
+            if (!isEqual(lhsResult1, ${expectedLeftValue}))
+                throw "Error on non hole nonObservableHoleOnLhs at i = " + i + " expected " + ${expectedLeftValue} + " got " + lhsResult1;
+            let lhsResult2 = observableHoleOnLhs(testArray, ${otherOperand});
+            if (!isEqual(lhsResult2[0], ${expectedLeftValue}) || lhsResult2[1] !== 1)
+                throw "Error on non hole observableHoleOnLhs at i = " + i + " expected " + ${expectedLeftValue} + " got " + lhsResult2[0];
+
+            let rhsResult1 = nonObservableHoleOnRhs(testArray, ${otherOperand});
+            if (!isEqual(rhsResult1, ${expectedRightValue}))
+                throw "Error on non hole nonObservableHoleOnRhs at i = " + i + " with operand " + ${otherOperand} + " expected " + ${expectedRightValue} + " got " + rhsResult1;
+            let rhsResult2 = observableHoleOnRhs(testArray, ${otherOperand});
+            if (!isEqual(rhsResult2[0], ${expectedRightValue}) || rhsResult2[1] !== 1)
+                throw "Error on non hole observableHoleOnRhs at i = " + i;
+        }`
+    );
+}
\ No newline at end of file
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to