Title: [207369] trunk
Revision
207369
Author
commit-qu...@webkit.org
Date
2016-10-14 19:19:16 -0700 (Fri, 14 Oct 2016)

Log Message

[JSC] op_negate should with any type
https://bugs.webkit.org/show_bug.cgi?id=162587

Patch by Benjamin Poulain <bpoul...@apple.com> on 2016-10-14
Reviewed by Saam Barati.

JSTests:

* stress/arith-abs-to-arith-negate-range-optimizaton.js: Added.
Cover OSR Exits when converting Math.abs() into ArithNegate.

* stress/arith-negate-on-various-types.js: Added.
Cover ArithNegate with all types.

Source/_javascript_Core:

* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
ArithNegate is quite simple. If the input is double, the output
is double. The other cases are set from the LLInt slow case.

* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::makeSafe):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):

* dfg/DFGIntegerRangeOptimizationPhase.cpp:
Tweak a bit the IntegerRangeOptimizationPhase when simplifying
ArithAbs to ArithNegate.
We should not do the conversion if the target nodes OSR Exits
on different input than the source node.

In particular, Checked ArithNegate exits on zero while
ArithAbs has not problem with it.
Unchecked ArithAbs() do not OSR Exit on INT_MIN, ArithNeg
should not either.

* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileArithNegate):
(JSC::DFG::SpeculativeJIT::compileMathIC):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileMathIC):
(JSC::FTL::DFG::LowerDFGToB3::compileArithNegate):

* jit/JITNegGenerator.cpp:
(JSC::JITNegGenerator::generateFastPath):
* jit/JITOperations.cpp:
Add result profiling in baseline to have types we can use
in DFG and FTL.

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (207368 => 207369)


--- trunk/JSTests/ChangeLog	2016-10-15 01:48:36 UTC (rev 207368)
+++ trunk/JSTests/ChangeLog	2016-10-15 02:19:16 UTC (rev 207369)
@@ -1,3 +1,16 @@
+2016-10-14  Benjamin Poulain  <bpoul...@apple.com>
+
+        [JSC] op_negate should with any type
+        https://bugs.webkit.org/show_bug.cgi?id=162587
+
+        Reviewed by Saam Barati.
+
+        * stress/arith-abs-to-arith-negate-range-optimizaton.js: Added.
+        Cover OSR Exits when converting Math.abs() into ArithNegate.
+
+        * stress/arith-negate-on-various-types.js: Added.
+        Cover ArithNegate with all types.
+
 2016-10-14  JF Bastien  <jfbast...@apple.com>
 
         Basic WebAssembly testing

Added: trunk/JSTests/stress/arith-abs-to-arith-negate-range-optimizaton.js (0 => 207369)


--- trunk/JSTests/stress/arith-abs-to-arith-negate-range-optimizaton.js	                        (rev 0)
+++ trunk/JSTests/stress/arith-abs-to-arith-negate-range-optimizaton.js	2016-10-15 02:19:16 UTC (rev 207369)
@@ -0,0 +1,426 @@
+//@ defaultNoEagerRun
+"use strict";
+
+// Checked int_min < value < 0
+function opaqueCheckedBetweenIntMinAndZeroExclusive(arg) {
+    if (arg < 0) {
+        if (arg > (0x80000000|0)) {
+            return Math.abs(arg);
+        }
+    }
+    throw "We should not be here";
+}
+noInline(opaqueCheckedBetweenIntMinAndZeroExclusive);
+
+function testCheckedBetweenIntMinAndZeroExclusive()
+{
+    for (let i = 1; i < 1e5; ++i) {
+        if (opaqueCheckedBetweenIntMinAndZeroExclusive(-i) !== i) {
+            throw "Failed testCheckedBetweenIntMinAndZeroExclusive()";
+        }
+        if (opaqueCheckedBetweenIntMinAndZeroExclusive(-2147483647) !== 2147483647) {
+            throw "Failed testCheckedBetweenIntMinAndZeroExclusive() on -2147483647";
+        }
+    }
+    if (numberOfDFGCompiles(opaqueCheckedBetweenIntMinAndZeroExclusive) > 1) {
+        throw "Failed optimizing testCheckedBetweenIntMinAndZeroExclusive(). None of the tested case need to OSR Exit.";
+    }
+}
+testCheckedBetweenIntMinAndZeroExclusive();
+
+
+// Checked int_min < value <= 0
+function opaqueCheckedBetweenIntMinExclusiveAndZeroInclusive(arg) {
+    if (arg <= 0) {
+        if (arg > (0x80000000|0)) {
+            return Math.abs(arg);
+        }
+    }
+    throw "We should not be here";
+}
+noInline(opaqueCheckedBetweenIntMinExclusiveAndZeroInclusive);
+
+function testCheckedBetweenIntMinExclusiveAndZeroInclusive()
+{
+    for (let i = 1; i < 1e5; ++i) {
+        if (opaqueCheckedBetweenIntMinExclusiveAndZeroInclusive(-i) !== i) {
+            throw "Failed testCheckedBetweenIntMinExclusiveAndZeroInclusive()";
+        }
+        if (opaqueCheckedBetweenIntMinExclusiveAndZeroInclusive(0) !== 0) {
+            throw "Failed testCheckedBetweenIntMinExclusiveAndZeroInclusive() on 0";
+        }
+        if (opaqueCheckedBetweenIntMinExclusiveAndZeroInclusive(-2147483647) !== 2147483647) {
+            throw "Failed testCheckedBetweenIntMinExclusiveAndZeroInclusive() on -2147483647";
+        }
+    }
+    if (numberOfDFGCompiles(opaqueCheckedBetweenIntMinExclusiveAndZeroInclusive) > 1) {
+        throw "Failed optimizing testCheckedBetweenIntMinExclusiveAndZeroInclusive(). None of the tested case need to OSR Exit.";
+    }
+}
+testCheckedBetweenIntMinExclusiveAndZeroInclusive();
+
+
+// Checked int_min <= value < 0
+function opaqueCheckedBetweenIntMinInclusiveAndZeroExclusive(arg) {
+    if (arg < 0) {
+        if (arg >= (0x80000000|0)) {
+            return Math.abs(arg);
+        }
+    }
+    throw "We should not be here";
+}
+noInline(opaqueCheckedBetweenIntMinInclusiveAndZeroExclusive);
+
+function testCheckedBetweenIntMinInclusiveAndZeroExclusive()
+{
+    for (let i = 1; i < 1e5; ++i) {
+        if (opaqueCheckedBetweenIntMinInclusiveAndZeroExclusive(-i) !== i) {
+            throw "Failed testCheckedBetweenIntMinInclusiveAndZeroExclusive()";
+        }
+        if (opaqueCheckedBetweenIntMinInclusiveAndZeroExclusive(-2147483647) !== 2147483647) {
+            throw "Failed testCheckedBetweenIntMinInclusiveAndZeroExclusive() on -2147483647";
+        }
+    }
+    if (numberOfDFGCompiles(opaqueCheckedBetweenIntMinInclusiveAndZeroExclusive) > 1) {
+        throw "Failed optimizing testCheckedBetweenIntMinInclusiveAndZeroExclusive(). None of the tested case need to OSR Exit.";
+    }
+
+    for (let i = 1; i < 1e5; ++i) {
+        if (opaqueCheckedBetweenIntMinInclusiveAndZeroExclusive(-i) !== i) {
+            throw "Failed testCheckedBetweenIntMinInclusiveAndZeroExclusive()";
+        }
+        let result = opaqueCheckedBetweenIntMinInclusiveAndZeroExclusive(-2147483648);
+        if (result !== 2147483648) {
+            throw "Failed testCheckedBetweenIntMinInclusiveAndZeroExclusive() on -2147483648, got " + result;
+        }
+    }
+
+    if (numberOfDFGCompiles(opaqueCheckedBetweenIntMinInclusiveAndZeroExclusive) > 2) {
+        throw "Math.abs() on IntMin can OSR Exit but we should quickly settle on double.";
+    }
+}
+testCheckedBetweenIntMinInclusiveAndZeroExclusive();
+
+
+// Checked int_min <= value <= 0
+function opaqueCheckedBetweenIntMinAndZeroInclusive(arg) {
+    if (arg <= 0) {
+        if (arg >= (0x80000000|0)) {
+            return Math.abs(arg);
+        }
+    }
+    throw "We should not be here";
+}
+noInline(opaqueCheckedBetweenIntMinAndZeroInclusive);
+
+function testCheckedBetweenIntMinAndZeroInclusive()
+{
+    for (let i = 1; i < 1e5; ++i) {
+        if (opaqueCheckedBetweenIntMinAndZeroInclusive(-i) !== i) {
+            throw "Failed testCheckedBetweenIntMinAndZeroInclusive()";
+        }
+        if (opaqueCheckedBetweenIntMinAndZeroInclusive(0) !== 0) {
+            throw "Failed testCheckedBetweenIntMinAndZeroInclusive()";
+        }
+        if (opaqueCheckedBetweenIntMinAndZeroInclusive(-2147483647) !== 2147483647) {
+            throw "Failed testCheckedBetweenIntMinAndZeroInclusive() on -2147483647";
+        }
+    }
+    if (numberOfDFGCompiles(opaqueCheckedBetweenIntMinAndZeroInclusive) > 1) {
+        throw "Failed optimizing testCheckedBetweenIntMinAndZeroInclusive(). None of the tested case need to OSR Exit.";
+    }
+
+    for (let i = 1; i < 1e5; ++i) {
+        if (opaqueCheckedBetweenIntMinAndZeroInclusive(-i) !== i) {
+            throw "Failed testCheckedBetweenIntMinAndZeroInclusive()";
+        }
+        if (opaqueCheckedBetweenIntMinAndZeroInclusive(0) !== 0) {
+            throw "Failed testCheckedBetweenIntMinAndZeroInclusive()";
+        }
+        if (opaqueCheckedBetweenIntMinAndZeroInclusive(-2147483648) !== 2147483648) {
+            throw "Failed testCheckedBetweenIntMinAndZeroInclusive() on -2147483648";
+        }
+    }
+
+    if (numberOfDFGCompiles(opaqueCheckedBetweenIntMinAndZeroInclusive) > 2) {
+        throw "Math.abs() on IntMin can OSR Exit but we should quickly settle on double.";
+    }
+}
+testCheckedBetweenIntMinAndZeroInclusive();
+
+
+// Unchecked int_min < value < 0
+function opaqueUncheckedBetweenIntMinAndZeroExclusive(arg) {
+    if (arg < 0) {
+        if (arg > (0x80000000|0)) {
+            return Math.abs(arg)|0;
+        }
+    }
+    throw "We should not be here";
+}
+noInline(opaqueUncheckedBetweenIntMinAndZeroExclusive);
+
+function testUncheckedBetweenIntMinAndZeroExclusive()
+{
+    for (let i = 1; i < 1e5; ++i) {
+        if (opaqueUncheckedBetweenIntMinAndZeroExclusive(-i) !== i) {
+            throw "Failed testUncheckedBetweenIntMinAndZeroExclusive()";
+        }
+        if (opaqueUncheckedBetweenIntMinAndZeroExclusive(-2147483647) !== 2147483647) {
+            throw "Failed testUncheckedBetweenIntMinAndZeroExclusive() on -2147483647";
+        }
+    }
+    if (numberOfDFGCompiles(opaqueUncheckedBetweenIntMinAndZeroExclusive) > 1) {
+        throw "Failed optimizing testUncheckedBetweenIntMinAndZeroExclusive(). None of the tested case need to OSR Exit.";
+    }
+}
+testUncheckedBetweenIntMinAndZeroExclusive();
+
+
+// Unchecked int_min < value <= 0
+function opaqueUncheckedBetweenIntMinExclusiveAndZeroInclusive(arg) {
+    if (arg <= 0) {
+        if (arg > (0x80000000|0)) {
+            return Math.abs(arg)|0;
+        }
+    }
+    throw "We should not be here";
+}
+noInline(opaqueUncheckedBetweenIntMinExclusiveAndZeroInclusive);
+
+function testUncheckedBetweenIntMinExclusiveAndZeroInclusive()
+{
+    for (let i = 1; i < 1e5; ++i) {
+        if (opaqueUncheckedBetweenIntMinExclusiveAndZeroInclusive(-i) !== i) {
+            throw "Failed testUncheckedBetweenIntMinExclusiveAndZeroInclusive()";
+        }
+        if (opaqueUncheckedBetweenIntMinExclusiveAndZeroInclusive(0) !== 0) {
+            throw "Failed testUncheckedBetweenIntMinExclusiveAndZeroInclusive() on 0";
+        }
+        if (opaqueUncheckedBetweenIntMinExclusiveAndZeroInclusive(-2147483647) !== 2147483647) {
+            throw "Failed testUncheckedBetweenIntMinExclusiveAndZeroInclusive() on -2147483647";
+        }
+    }
+    if (numberOfDFGCompiles(opaqueUncheckedBetweenIntMinExclusiveAndZeroInclusive) > 1) {
+        throw "Failed optimizing testUncheckedBetweenIntMinExclusiveAndZeroInclusive(). None of the tested case need to OSR Exit.";
+    }
+}
+testUncheckedBetweenIntMinExclusiveAndZeroInclusive();
+
+
+// Unchecked int_min <= value < 0
+function opaqueUncheckedBetweenIntMinInclusiveAndZeroExclusive(arg) {
+    if (arg < 0) {
+        if (arg >= (0x80000000|0)) {
+            return Math.abs(arg)|0;
+        }
+    }
+    throw "We should not be here";
+}
+noInline(opaqueUncheckedBetweenIntMinInclusiveAndZeroExclusive);
+
+function testUncheckedBetweenIntMinInclusiveAndZeroExclusive()
+{
+    for (let i = 1; i < 1e5; ++i) {
+        if (opaqueUncheckedBetweenIntMinInclusiveAndZeroExclusive(-i) !== i) {
+            throw "Failed testUncheckedBetweenIntMinInclusiveAndZeroExclusive()";
+        }
+        if (opaqueUncheckedBetweenIntMinInclusiveAndZeroExclusive(-2147483647) !== 2147483647) {
+            throw "Failed testUncheckedBetweenIntMinInclusiveAndZeroExclusive() on -2147483647";
+        }
+        if (opaqueUncheckedBetweenIntMinInclusiveAndZeroExclusive(-2147483648) !== -2147483648) {
+            throw "Failed testUncheckedBetweenIntMinInclusiveAndZeroExclusive() on -2147483648";
+        }
+    }
+    if (numberOfDFGCompiles(opaqueUncheckedBetweenIntMinInclusiveAndZeroExclusive) > 1) {
+        throw "Failed optimizing testUncheckedBetweenIntMinInclusiveAndZeroExclusive(). None of the tested case need to OSR Exit.";
+    }
+}
+testUncheckedBetweenIntMinInclusiveAndZeroExclusive();
+
+
+// Unchecked int_min <= value <= 0
+function opaqueUncheckedBetweenIntMinAndZeroInclusive(arg) {
+    if (arg <= 0) {
+        if (arg >= (0x80000000|0)) {
+            return Math.abs(arg)|0;
+        }
+    }
+    throw "We should not be here";
+}
+noInline(opaqueUncheckedBetweenIntMinAndZeroInclusive);
+
+function testUncheckedBetweenIntMinAndZeroInclusive()
+{
+    for (let i = 1; i < 1e5; ++i) {
+        if (opaqueUncheckedBetweenIntMinAndZeroInclusive(-i) !== i) {
+            throw "Failed testUncheckedBetweenIntMinAndZeroInclusive()";
+        }
+        if (opaqueUncheckedBetweenIntMinAndZeroInclusive(0) !== 0) {
+            throw "Failed testUncheckedBetweenIntMinAndZeroInclusive()";
+        }
+        if (opaqueUncheckedBetweenIntMinAndZeroInclusive(-2147483647) !== 2147483647) {
+            throw "Failed testUncheckedBetweenIntMinAndZeroInclusive() on -2147483647";
+        }
+        if (opaqueUncheckedBetweenIntMinInclusiveAndZeroExclusive(-2147483648) !== -2147483648) {
+            throw "Failed testUncheckedBetweenIntMinInclusiveAndZeroExclusive() on -2147483648";
+        }
+    }
+    if (numberOfDFGCompiles(opaqueUncheckedBetweenIntMinAndZeroInclusive) > 1) {
+        throw "Failed optimizing testUncheckedBetweenIntMinAndZeroInclusive(). None of the tested case need to OSR Exit.";
+    }
+}
+testUncheckedBetweenIntMinAndZeroInclusive();
+
+
+// Checked value < 0
+function opaqueCheckedLessThanZero(arg) {
+    if (arg < 0) {
+        return Math.abs(arg);
+    }
+    throw "We should not be here";
+}
+noInline(opaqueCheckedLessThanZero);
+
+function testCheckedLessThanZero()
+{
+    for (let i = 1; i < 1e5; ++i) {
+        if (opaqueCheckedLessThanZero(-i) !== i) {
+            throw "Failed testCheckedLessThanZero()";
+        }
+        if (opaqueCheckedLessThanZero(-2147483647) !== 2147483647) {
+            throw "Failed testCheckedLessThanZero() on -2147483647";
+        }
+    }
+    if (numberOfDFGCompiles(opaqueCheckedLessThanZero) > 1) {
+        throw "Failed optimizing testCheckedLessThanZero(). None of the tested case need to OSR Exit.";
+    }
+
+    for (let i = 1; i < 1e5; ++i) {
+        if (opaqueCheckedLessThanZero(-i) !== i) {
+            throw "Failed testCheckedLessThanZero()";
+        }
+        let result = opaqueCheckedLessThanZero(-2147483648);
+        if (result !== 2147483648) {
+            throw "Failed testCheckedLessThanZero() on -2147483648, got " + result;
+        }
+    }
+    if (numberOfDFGCompiles(opaqueCheckedLessThanZero) > 2) {
+        throw "Math.abs() on IntMin can OSR Exit but we should quickly settle on double.";
+    }
+}
+testCheckedLessThanZero();
+
+
+// Checked value <= 0
+function opaqueCheckedLessThanOrEqualZero(arg) {
+    if (arg <= 0) {
+        return Math.abs(arg);
+    }
+    throw "We should not be here";
+}
+noInline(opaqueCheckedLessThanOrEqualZero);
+
+function testCheckedLessThanOrEqualZero()
+{
+    for (let i = 1; i < 1e5; ++i) {
+        if (opaqueCheckedLessThanOrEqualZero(-i) !== i) {
+            throw "Failed testCheckedLessThanOrEqualZero()";
+        }
+        if (opaqueCheckedLessThanOrEqualZero(0) !== 0) {
+            throw "Failed testCheckedLessThanOrEqualZero() on 0";
+        }
+        if (opaqueCheckedLessThanOrEqualZero(-2147483647) !== 2147483647) {
+            throw "Failed testCheckedLessThanOrEqualZero() on -2147483647";
+        }
+    }
+    if (numberOfDFGCompiles(opaqueCheckedLessThanOrEqualZero) > 1) {
+        throw "Failed optimizing testCheckedLessThanOrEqualZero(). None of the tested case need to OSR Exit.";
+    }
+
+    for (let i = 1; i < 1e5; ++i) {
+        if (opaqueCheckedLessThanOrEqualZero(-i) !== i) {
+            throw "Failed testCheckedLessThanOrEqualZero()";
+        }
+        if (opaqueCheckedLessThanOrEqualZero(-2147483648) !== 2147483648) {
+            throw "Failed testCheckedLessThanOrEqualZero() on -2147483648";
+        }
+    }
+    if (numberOfDFGCompiles(opaqueCheckedLessThanOrEqualZero) > 2) {
+        throw "Math.abs() on IntMin can OSR Exit but we should quickly settle on double.";
+    }
+}
+testCheckedLessThanOrEqualZero();
+
+
+// Unchecked value < 0
+function opaqueUncheckedLessThanZero(arg) {
+    if (arg < 0) {
+        return Math.abs(arg)|0;
+    }
+    throw "We should not be here";
+}
+noInline(opaqueUncheckedLessThanZero);
+
+function testUncheckedLessThanZero()
+{
+    for (let i = 1; i < 1e5; ++i) {
+        if (opaqueUncheckedLessThanZero(-i) !== i) {
+            throw "Failed testUncheckedLessThanZero()";
+        }
+        if (opaqueUncheckedLessThanZero(-2147483647) !== 2147483647) {
+            throw "Failed testUncheckedLessThanZero() on -2147483647";
+        }
+        if (opaqueUncheckedLessThanZero(-2147483648) !== -2147483648) {
+            throw "Failed testUncheckedLessThanOrEqualZero() on -2147483648";
+        }
+    }
+    if (numberOfDFGCompiles(opaqueUncheckedLessThanZero) > 1) {
+        throw "Failed optimizing testUncheckedLessThanZero(). None of the tested case need to OSR Exit.";
+    }
+
+    for (let i = 1; i < 1e5; ++i) {
+        if (opaqueUncheckedLessThanZero(-i) !== i) {
+            throw "Failed testUncheckedLessThanZero()";
+        }
+        if (opaqueUncheckedLessThanZero(-2147483648) !== -2147483648) {
+            throw "Failed testUncheckedLessThanZero() on -2147483648";
+        }
+    }
+    if (numberOfDFGCompiles(opaqueUncheckedLessThanZero) > 2) {
+        throw "Math.abs() on IntMin can OSR Exit but we should quickly settle on double.";
+    }
+}
+testUncheckedLessThanZero();
+
+
+// Unchecked value <= 0
+function opaqueUncheckedLessThanOrEqualZero(arg) {
+    if (arg <= 0) {
+        return Math.abs(arg)|0;
+    }
+    throw "We should not be here";
+}
+noInline(opaqueUncheckedLessThanOrEqualZero);
+
+function testUncheckedLessThanOrEqualZero()
+{
+    for (let i = 1; i < 1e5; ++i) {
+        if (opaqueUncheckedLessThanOrEqualZero(-i) !== i) {
+            throw "Failed testUncheckedLessThanOrEqualZero()";
+        }
+        if (opaqueUncheckedLessThanOrEqualZero(0) !== 0) {
+            throw "Failed testUncheckedLessThanOrEqualZero() on 0";
+        }
+        if (opaqueUncheckedLessThanOrEqualZero(-2147483647) !== 2147483647) {
+            throw "Failed testUncheckedLessThanOrEqualZero() on -2147483647";
+        }
+        if (opaqueUncheckedLessThanOrEqualZero(-2147483648) !== -2147483648) {
+            throw "Failed testUncheckedLessThanOrEqualZero() on -2147483648";
+        }
+    }
+    if (numberOfDFGCompiles(opaqueUncheckedLessThanOrEqualZero) > 1) {
+        throw "Failed optimizing testUncheckedLessThanOrEqualZero(). None of the tested case need to OSR Exit.";
+    }
+}
+testUncheckedLessThanOrEqualZero();

Added: trunk/JSTests/stress/arith-negate-on-various-types.js (0 => 207369)


--- trunk/JSTests/stress/arith-negate-on-various-types.js	                        (rev 0)
+++ trunk/JSTests/stress/arith-negate-on-various-types.js	2016-10-15 02:19:16 UTC (rev 207369)
@@ -0,0 +1,283 @@
+//@ defaultNoEagerRun
+"use strict";
+
+let validInputTestCases = [
+    // input as string, expected result as string.
+    ["undefined", "NaN"],
+    ["null", "-0"],
+    ["0", "-0"],
+    ["-0.", "0"],
+    ["0.5", "-0.5"],
+    ["-0.5", "0.5"],
+    ["4", "-4"],
+    ["42.1", "-42.1"],
+    ["42.5", "-42.5"],
+    ["42.9", "-42.9"],
+    ["-42.1", "42.1"],
+    ["-42.5", "42.5"],
+    ["-42.9", "42.9"],
+    ["Math.PI", "-Math.PI"],
+    ["Infinity", "-Infinity"],
+    ["-Infinity", "Infinity"],
+    ["NaN", "NaN"],
+    ["\"WebKit\"", "NaN"],
+    ["\"4\"", "-4"],
+    ["\"42.5\"", "-42.5"],
+    ["{ valueOf: () => { return 4; } }", "-4"],
+    ["{ valueOf: () => { return 0; } }", "-0"],
+    ["{ valueOf: () => { return -0; } }", "0"],
+    ["{ valueOf: () => { return 0.5; } }", "-0.5"],
+    ["{ valueOf: () => { return -0.5; } }", "0.5"],
+    ["{ valueOf: () => { return Number.MIN_SAFE_INTEGER; } }", "9007199254740991"],
+    ["{ valueOf: () => { return Number.MAX_SAFE_INTEGER; } }", "-9007199254740991"],
+    ["{ valueOf: () => { return 0x80000000|0; } }", "2147483648"],
+    ["{ valueOf: () => { return 0x7fffffff|0; } }", "-2147483647"],
+    ["{ valueOf: () => { return (0x80000000|0) - 0.5; } }", "2147483648.5"],
+    ["{ valueOf: () => { return (0x7fffffff|0) + 0.5; } }", "-2147483647.5"],
+];
+
+let validInputTypedTestCases = validInputTestCases.map((element) => { return [eval("(" + element[0] + ")"), eval(element[1])] });
+
+function isIdentical(result, expected)
+{
+    if (expected === expected) {
+        if (result !== expected)
+            return false;
+        if (!expected && (1 / expected) !== (1 / result))
+            return false;
+
+        return true;
+    }
+    return result !== result;
+}
+
+
+// Test negate with a very polymorphic input. All test cases are seen at each iteration.
+function opaqueAllTypesNegate(argument) {
+    return -(argument);
+}
+noInline(opaqueAllTypesNegate);
+noOSRExitFuzzing(opaqueAllTypesNegate);
+
+function testAllTypesCall() {
+    for (let i = 0; i < 1e3; ++i) {
+        for (let testCaseInput of validInputTypedTestCases) {
+            let output = opaqueAllTypesNegate(testCaseInput[0]);
+            if (!isIdentical(output, testCaseInput[1]))
+                throw "Failed testAllTypesCall for input " + testCaseInput[0] + " expected " + testCaseInput[1] + " got " + output;
+        }
+    }
+    if (numberOfDFGCompiles(opaqueAllTypesNegate) > 3)
+        throw "We should have detected negate was polymorphic and generated a generic version.";
+}
+testAllTypesCall();
+
+
+// Polymorphic input but negative zero is not observable.
+function opaqueAllTypesNegateWithoutNegativeZero(argument) {
+    return -(argument) + 0;
+}
+noInline(opaqueAllTypesNegateWithoutNegativeZero);
+noOSRExitFuzzing(opaqueAllTypesNegateWithoutNegativeZero);
+
+function testAllTypesWithoutNegativeZeroCall() {
+    for (let i = 0; i < 1e3; ++i) {
+        for (let testCaseInput of validInputTypedTestCases) {
+            let output = opaqueAllTypesNegateWithoutNegativeZero(testCaseInput[0]);
+            if (!isIdentical(output, testCaseInput[1] + 0))
+                throw "Failed testAllTypesWithoutNegativeZeroCall for input " + testCaseInput[0] + " expected " + testCaseInput[1] + " got " + output;
+        }
+    }
+    if (numberOfDFGCompiles(opaqueAllTypesNegate) > 3)
+        throw "We should have detected negate was polymorphic and generated a generic version.";
+}
+testAllTypesWithoutNegativeZeroCall();
+
+
+// Test negate on a completely typed input. Every call see only one type.
+function testSingleTypeCall() {
+    for (let testCaseInput of validInputTestCases) {
+        eval(`
+            function opaqueNegate(argument) {
+                return -(argument);
+            }
+            noInline(opaqueNegate);
+            noOSRExitFuzzing(opaqueNegate);
+
+            for (let i = 0; i < 1e4; ++i) {
+                if (!isIdentical(opaqueNegate(${testCaseInput[0]}), ${testCaseInput[1]})) {
+                    throw "Failed testSingleTypeCall()";
+                }
+            }
+            if (numberOfDFGCompiles(opaqueNegate) > 1)
+                throw "Failed testSingleTypeCall(). We should have compiled a single negate for the expected type.";
+        `);
+    }
+}
+testSingleTypeCall();
+
+
+function checkCompileCountForUselessNegativeZero(testFunction)
+{
+    if (jscOptions().useMaximalFlushInsertionPhase) {
+        // If we forced a flush after the operation, the negative zero becomes
+        // observable and we may be overly optimistic.
+        return numberOfDFGCompiles(testFunction) <= 2;
+    }
+    return numberOfDFGCompiles(testFunction) <= 1;
+}
+
+
+// Test negate on a completely typed input, but without negative zero.
+function testSingleTypeWithoutNegativeZeroCall() {
+    for (let testCaseInput of validInputTestCases) {
+        eval(`
+            function opaqueNegate(argument) {
+                return -(argument) + 0;
+            }
+            noInline(opaqueNegate);
+            noOSRExitFuzzing(opaqueNegate);
+
+            for (let i = 0; i < 1e4; ++i) {
+                if (!isIdentical(opaqueNegate(${testCaseInput[0]}), ${testCaseInput[1]} + 0)) {
+                    throw "Failed testSingleTypeWithoutNegativeZeroCall()";
+                }
+            }
+            if (!checkCompileCountForUselessNegativeZero(opaqueNegate))
+                throw "Failed testSingleTypeWithoutNegativeZeroCall(). We should have compiled a single negate for the expected type.";
+        `);
+    }
+}
+testSingleTypeWithoutNegativeZeroCall();
+
+
+// Test negate on constants
+function testConstant() {
+    for (let testCaseInput of validInputTestCases) {
+        eval(`
+            function opaqueNegateOnConstant() {
+                return -(${testCaseInput[0]});
+            }
+            noInline(opaqueNegateOnConstant);
+            noOSRExitFuzzing(opaqueNegateOnConstant);
+
+            for (let i = 0; i < 1e4; ++i) {
+                if (!isIdentical(opaqueNegateOnConstant(), ${testCaseInput[1]})) {
+                    throw "Failed testConstant()";
+                }
+            }
+            if (numberOfDFGCompiles(opaqueNegateOnConstant) > 1)
+                throw "Failed testConstant(). We should have compiled a single negate for the expected type.";
+        `);
+    }
+}
+testConstant();
+
+
+// Verify we call valueOf() exactly once per call.
+function opaqueNegateForSideEffects(argument) {
+    return -(argument);
+}
+noInline(opaqueNegateForSideEffects);
+noOSRExitFuzzing(opaqueNegateForSideEffects);
+
+function testSideEffect() {
+    let testObject = {
+        counter: 0,
+        valueOf: function() { ++this.counter; return 16; }
+    };
+    for (let i = 0; i < 1e4; ++i) {
+        if (opaqueNegateForSideEffects(testObject) !== -16)
+            throw "Incorrect result in testSideEffect()";
+    }
+    if (testObject.counter !== 1e4)
+        throw "Failed testSideEffect()";
+    if (numberOfDFGCompiles(opaqueNegateForSideEffects) > 1)
+        throw "opaqueNegateForSideEffects() is predictable, it should only be compiled once.";
+}
+testSideEffect();
+
+
+// Verify negate is not subject to CSE if the argument has side effects.
+function opaqueNegateForCSE(argument) {
+    return -(argument) + -(argument) + -(argument);
+}
+noInline(opaqueNegateForCSE);
+noOSRExitFuzzing(opaqueNegateForCSE);
+
+function testCSE() {
+    let testObject = {
+        counter: 0,
+        valueOf: function() { ++this.counter; return 16; }
+    };
+    for (let i = 0; i < 1e4; ++i) {
+        if (opaqueNegateForCSE(testObject) !== -48)
+            throw "Incorrect result in testCSE()";
+    }
+    if (testObject.counter !== 3e4)
+        throw "Failed testCSE()";
+    if (numberOfDFGCompiles(opaqueNegateForCSE) > 1)
+        throw "opaqueNegateForCSE() is predictable, it should only be compiled once.";
+}
+testCSE();
+
+
+// Verify negate is not subject to DCE if the argument has side effects.
+function opaqueNegateForDCE(argument) {
+    -(argument);
+}
+noInline(opaqueNegateForDCE);
+noOSRExitFuzzing(opaqueNegateForDCE);
+
+function testDCE() {
+    let testObject = {
+        counter: 0,
+        valueOf: function() { ++this.counter; return 16; }
+    };
+    for (let i = 0; i < 1e4; ++i) {
+        opaqueNegateForDCE(testObject);
+    }
+    if (testObject.counter !== 1e4)
+        throw "Failed testDCE()";
+    if (numberOfDFGCompiles(opaqueNegateForDCE) > 1)
+        throw "opaqueNegateForDCE() is predictable, it should only be compiled once.";
+}
+testDCE();
+
+
+// Test exceptions in the argument.
+function testException() {
+    let counter = 0;
+    function opaqueNegateWithException(argument) {
+        let result = -(argument);
+        ++counter;
+        return result;
+    }
+    noInline(opaqueNegateWithException);
+
+    let testObject = { valueOf: () => {  return 64; } };
+
+    // Warm up without exception.
+    for (let i = 0; i < 1e3; ++i) {
+        if (opaqueNegateWithException(testObject) !== -64)
+            throw "Incorrect result in opaqueNegateWithException()";
+    }
+
+    let testThrowObject = { valueOf: () => { throw testObject; return 64; } };
+
+    for (let i = 0; i < 1e2; ++i) {
+        try {
+            if (opaqueNegateWithException(testThrowObject) !== 8)
+                throw "This code should not be reached!!";
+        } catch (e) {
+            if (e !== testObject) {
+                throw "Wrong object thrown from opaqueNegateWithException."
+            }
+        }
+    }
+
+    if (counter !== 1e3) {
+        throw "Invalid count in testException()";
+    }
+}
+testException();

Modified: trunk/Source/_javascript_Core/ChangeLog (207368 => 207369)


--- trunk/Source/_javascript_Core/ChangeLog	2016-10-15 01:48:36 UTC (rev 207368)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-10-15 02:19:16 UTC (rev 207369)
@@ -1,3 +1,49 @@
+2016-10-14  Benjamin Poulain  <bpoul...@apple.com>
+
+        [JSC] op_negate should with any type
+        https://bugs.webkit.org/show_bug.cgi?id=162587
+
+        Reviewed by Saam Barati.
+
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        ArithNegate is quite simple. If the input is double, the output
+        is double. The other cases are set from the LLInt slow case.
+
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::makeSafe):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+
+        * dfg/DFGIntegerRangeOptimizationPhase.cpp:
+        Tweak a bit the IntegerRangeOptimizationPhase when simplifying
+        ArithAbs to ArithNegate.
+        We should not do the conversion if the target nodes OSR Exits
+        on different input than the source node.
+
+        In particular, Checked ArithNegate exits on zero while
+        ArithAbs has not problem with it.
+        Unchecked ArithAbs() do not OSR Exit on INT_MIN, ArithNeg
+        should not either.
+
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileArithNegate):
+        (JSC::DFG::SpeculativeJIT::compileMathIC):
+        * dfg/DFGSpeculativeJIT.h:
+        (JSC::DFG::SpeculativeJIT::callOperation):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileMathIC):
+        (JSC::FTL::DFG::LowerDFGToB3::compileArithNegate):
+
+        * jit/JITNegGenerator.cpp:
+        (JSC::JITNegGenerator::generateFastPath):
+        * jit/JITOperations.cpp:
+        Add result profiling in baseline to have types we can use
+        in DFG and FTL.
+
 2016-10-14  Keith Miller  <keith_mil...@apple.com>
 
         B3 needs a special WasmAddress Opcode

Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (207368 => 207369)


--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2016-10-15 01:48:36 UTC (rev 207368)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2016-10-15 02:19:16 UTC (rev 207369)
@@ -657,7 +657,8 @@
                     forNode(node->child1()).m_type));
             break;
         default:
-            RELEASE_ASSERT_NOT_REACHED();
+            DFG_ASSERT(m_graph, node, node->child1().useKind() == UntypedUse);
+            forNode(node).setType(SpecBytecodeNumber);
             break;
         }
         break;

Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (207368 => 207369)


--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2016-10-15 01:48:36 UTC (rev 207368)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2016-10-15 02:19:16 UTC (rev 207369)
@@ -974,6 +974,17 @@
                         node->mergeFlags(NodeMayHaveNonNumberResult);
                     break;
                 }
+                case ArithNegate: {
+                    ASSERT_WITH_MESSAGE(!arithProfile->didObserveNonNumber(), "op_negate starts with a toNumber() on the argument, it should only produce numbers.");
+
+                    if (arithProfile->lhsObservedType().sawNumber() || arithProfile->didObserveDouble())
+                        node->mergeFlags(NodeMayHaveDoubleResult);
+                    if (arithProfile->didObserveNegZeroDouble() || m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, NegativeZero))
+                        node->mergeFlags(NodeMayNegZeroInBaseline);
+                    if (arithProfile->didObserveInt32Overflow() || m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow))
+                        node->mergeFlags(NodeMayOverflowInt32InBaseline);
+                    break;
+                }
                 
                 default:
                     break;
@@ -991,14 +1002,6 @@
                 node->mergeFlags(NodeMayOverflowInt32InBaseline);
                 break;
                 
-            case ArithNegate:
-                // Currently we can't tell the difference between a negation overflowing
-                // (i.e. -(1 << 31)) or generating negative zero (i.e. -0). If it took slow
-                // path then we assume that it did both of those things.
-                node->mergeFlags(NodeMayOverflowInt32InBaseline);
-                node->mergeFlags(NodeMayNegZeroInBaseline);
-                break;
-                
             default:
                 break;
             }

Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (207368 => 207369)


--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2016-10-15 01:48:36 UTC (rev 207368)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2016-10-15 02:19:16 UTC (rev 207369)
@@ -211,6 +211,17 @@
         }
         return;
 
+    case ArithNegate:
+        if (node->child1().useKind() == Int32Use
+            || node->child1().useKind() == DoubleRepUse
+            || node->child1().useKind() == Int52RepUse)
+            def(PureValue(node));
+        else {
+            read(World);
+            write(Heap);
+        }
+        return;
+
     case IsCellWithType:
         def(PureValue(node, node->queriedType()));
         return;
@@ -334,7 +345,6 @@
         return;
 
     case ArithAdd:
-    case ArithNegate:
     case ArithMod:
     case DoubleAsInt32:
     case UInt32ToNumber:

Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (207368 => 207369)


--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2016-10-15 01:48:36 UTC (rev 207368)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2016-10-15 02:19:16 UTC (rev 207369)
@@ -209,7 +209,7 @@
         }
             
         case ArithNegate: {
-            if (m_graph.unaryArithShouldSpeculateInt32(node, FixupPass)) {
+            if (node->child1()->shouldSpeculateInt32OrBoolean() && node->canSpeculateInt32(FixupPass)) {
                 fixIntOrBooleanEdge(node->child1());
                 if (bytecodeCanTruncateInteger(node->arithNodeFlags()))
                     node->setArithMode(Arith::Unchecked);
@@ -217,6 +217,8 @@
                     node->setArithMode(Arith::CheckOverflow);
                 else
                     node->setArithMode(Arith::CheckOverflowAndNegativeZero);
+                node->setResult(NodeResultInt32);
+                node->clearFlags(NodeMustGenerate);
                 break;
             }
             if (m_graph.unaryArithShouldSpeculateAnyInt(node, FixupPass)) {
@@ -226,10 +228,15 @@
                 else
                     node->setArithMode(Arith::CheckOverflowAndNegativeZero);
                 node->setResult(NodeResultInt52);
+                node->clearFlags(NodeMustGenerate);
                 break;
             }
-            fixDoubleOrBooleanEdge(node->child1());
-            node->setResult(NodeResultDouble);
+            if (node->child1()->shouldSpeculateNotCell()) {
+                fixDoubleOrBooleanEdge(node->child1());
+                node->setResult(NodeResultDouble);
+                node->clearFlags(NodeMustGenerate);
+            } else
+                fixEdge<UntypedUse>(node->child1());
             break;
         }
             

Modified: trunk/Source/_javascript_Core/dfg/DFGIntegerRangeOptimizationPhase.cpp (207368 => 207369)


--- trunk/Source/_javascript_Core/dfg/DFGIntegerRangeOptimizationPhase.cpp	2016-10-15 01:48:36 UTC (rev 207368)
+++ trunk/Source/_javascript_Core/dfg/DFGIntegerRangeOptimizationPhase.cpp	2016-10-15 02:19:16 UTC (rev 207369)
@@ -1252,9 +1252,10 @@
                         changed = true;
                         break;
                     }
-                    if (maxValue <= 0) {
+                    bool absIsUnchecked = !shouldCheckOverflow(node->arithMode());
+                    if (maxValue < 0 || (absIsUnchecked && maxValue <= 0)) {
                         node->convertToArithNegate();
-                        if (minValue > std::numeric_limits<int>::min())
+                        if (absIsUnchecked || minValue > std::numeric_limits<int>::min())
                             node->setArithMode(Arith::Unchecked);
                         changed = true;
                         break;

Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (207368 => 207369)


--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2016-10-15 01:48:36 UTC (rev 207368)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2016-10-15 02:19:16 UTC (rev 207369)
@@ -245,17 +245,23 @@
             break;
         }
 
-        case ArithNegate:
-            if (node->child1()->prediction()) {
-                if (m_graph.unaryArithShouldSpeculateInt32(node, m_pass))
+        case ArithNegate: {
+            SpeculatedType prediction = node->child1()->prediction();
+            if (prediction) {
+                if (isInt32OrBooleanSpeculation(prediction) && node->canSpeculateInt32(m_pass))
                     changed |= mergePrediction(SpecInt32Only);
                 else if (m_graph.unaryArithShouldSpeculateAnyInt(node, m_pass))
                     changed |= mergePrediction(SpecInt52Only);
-                else
+                else if (isBytecodeNumberSpeculation(prediction))
                     changed |= mergePrediction(speculatedDoubleTypeForPrediction(node->child1()->prediction()));
+                else {
+                    changed |= mergePrediction(SpecInt32Only);
+                    if (node->mayHaveDoubleResult())
+                        changed |= mergePrediction(SpecBytecodeDouble);
+                }
             }
             break;
-            
+        }
         case ArithMin:
         case ArithMax: {
             SpeculatedType left = node->child1()->prediction();

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (207368 => 207369)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2016-10-15 01:48:36 UTC (rev 207368)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2016-10-15 02:19:16 UTC (rev 207369)
@@ -4237,11 +4237,106 @@
         return;
     }
         
-    default:
-        RELEASE_ASSERT_NOT_REACHED();
+    default: {
+        ArithProfile* arithProfile = m_jit.graph().baselineCodeBlockFor(node->origin.semantic)->arithProfileForBytecodeOffset(node->origin.semantic.bytecodeIndex);
+        JITNegIC* negIC = m_jit.codeBlock()->addJITNegIC(arithProfile);
+        auto repatchingFunction = operationArithNegateOptimize;
+        auto nonRepatchingFunction = operationArithNegate;
+        bool needsScratchGPRReg = true;
+        compileMathIC(node, negIC, needsScratchGPRReg, repatchingFunction, nonRepatchingFunction);
         return;
     }
+    }
 }
+
+template <typename Generator, typename RepatchingFunction, typename NonRepatchingFunction>
+void SpeculativeJIT::compileMathIC(Node* node, JITUnaryMathIC<Generator>* mathIC, bool needsScratchGPRReg, RepatchingFunction repatchingFunction, NonRepatchingFunction nonRepatchingFunction)
+{
+    GPRReg scratchGPR = InvalidGPRReg;
+    Optional<GPRTemporary> gprScratch;
+    if (needsScratchGPRReg) {
+        gprScratch = GPRTemporary(this);
+        scratchGPR = gprScratch->gpr();
+    }
+    JSValueOperand childOperand(this, node->child1());
+    JSValueRegs childRegs = childOperand.jsValueRegs();
+#if USE(JSVALUE64)
+    GPRTemporary result(this, Reuse, childOperand);
+    JSValueRegs resultRegs(result.gpr());
+#else
+    GPRTemporary resultTag(this);
+    GPRTemporary resultPayload(this);
+    JSValueRegs resultRegs(resultPayload.gpr(), resultTag.gpr());
+#endif
+
+#if ENABLE(MATH_IC_STATS)
+    auto inlineStart = m_jit.label();
+#endif
+
+    Box<MathICGenerationState> icGenerationState = Box<MathICGenerationState>::create();
+    mathIC->m_generator = Generator(resultRegs, childRegs, scratchGPR);
+
+    bool shouldEmitProfiling = false;
+    bool generatedInline = mathIC->generateInline(m_jit, *icGenerationState, shouldEmitProfiling);
+
+    if (generatedInline) {
+        ASSERT(!icGenerationState->slowPathJumps.empty());
+
+        Vector<SilentRegisterSavePlan> savePlans;
+        silentSpillAllRegistersImpl(false, savePlans, resultRegs);
+
+        auto done = m_jit.label();
+
+        addSlowPathGenerator([=, savePlans = WTFMove(savePlans)] () {
+            icGenerationState->slowPathJumps.link(&m_jit);
+            icGenerationState->slowPathStart = m_jit.label();
+#if ENABLE(MATH_IC_STATS)
+            auto slowPathStart = m_jit.label();
+#endif
+
+            silentSpill(savePlans);
+
+            if (icGenerationState->shouldSlowPathRepatch)
+                icGenerationState->slowPathCall = callOperation(bitwise_cast<J_JITOperation_EJMic>(repatchingFunction), resultRegs, childRegs, TrustedImmPtr(mathIC));
+            else
+                icGenerationState->slowPathCall = callOperation(nonRepatchingFunction, resultRegs, childRegs);
+
+            silentFill(savePlans);
+            m_jit.exceptionCheck();
+            m_jit.jump().linkTo(done, &m_jit);
+
+            m_jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
+                mathIC->finalizeInlineCode(*icGenerationState, linkBuffer);
+            });
+
+#if ENABLE(MATH_IC_STATS)
+            auto slowPathEnd = m_jit.label();
+            m_jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
+                size_t size = static_cast<char*>(linkBuffer.locationOf(slowPathEnd).executableAddress()) - static_cast<char*>(linkBuffer.locationOf(slowPathStart).executableAddress());
+                mathIC->m_generatedCodeSize += size;
+            });
+#endif
+
+        });
+    } else {
+        flushRegisters();
+        callOperation(nonRepatchingFunction, resultRegs, childRegs);
+        m_jit.exceptionCheck();
+    }
+
+#if ENABLE(MATH_IC_STATS)
+    auto inlineEnd = m_jit.label();
+    m_jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
+        size_t size = static_cast<char*>(linkBuffer.locationOf(inlineEnd).executableAddress()) - static_cast<char*>(linkBuffer.locationOf(inlineStart).executableAddress());
+        mathIC->m_generatedCodeSize += size;
+    });
+#endif
+
+    jsValueResult(resultRegs, node);
+    return;
+}
+
+
 void SpeculativeJIT::compileArithMul(Node* node)
 {
     switch (node->binaryUseKind()) {

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h (207368 => 207369)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2016-10-15 01:48:36 UTC (rev 207368)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2016-10-15 02:19:16 UTC (rev 207369)
@@ -1312,6 +1312,11 @@
         m_jit.setupArgumentsWithExecState(arg1, arg2, arg3);
         return appendCallSetResult(operation, result);
     }
+    JITCompiler::Call callOperation(J_JITOperation_EJMic operation, JSValueRegs result, JSValueRegs arg, TrustedImmPtr mathIC)
+    {
+        m_jit.setupArgumentsWithExecState(arg.gpr(), mathIC);
+        return appendCallSetResult(operation, result.gpr());
+    }
     JITCompiler::Call callOperation(J_JITOperation_EJJMic operation, JSValueRegs result, JSValueRegs arg1, JSValueRegs arg2, TrustedImmPtr mathIC)
     {
         m_jit.setupArgumentsWithExecState(arg1.gpr(), arg2.gpr(), mathIC);
@@ -1799,7 +1804,11 @@
         m_jit.setupArgumentsWithExecState(arg1, arg2.payloadGPR(), arg2.tagGPR(), arg3);
         return appendCallSetResult(operation, result);
     }
-
+    JITCompiler::Call callOperation(J_JITOperation_EJMic operation, JSValueRegs result, JSValueRegs arg, TrustedImmPtr mathIC)
+    {
+        m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg.payloadGPR(), arg.tagGPR(), mathIC);
+        return appendCallSetResult(operation, result.payloadGPR(), result.tagGPR());
+    }
     JITCompiler::Call callOperation(J_JITOperation_EJJMic operation, JSValueRegs result, JSValueRegs arg1, JSValueRegs arg2, TrustedImmPtr mathIC)
     {
         m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1.payloadGPR(), arg1.tagGPR(), arg2.payloadGPR(), arg2.tagGPR(), mathIC);
@@ -2595,6 +2604,8 @@
 
     template <typename Generator, typename RepatchingFunction, typename NonRepatchingFunction>
     void compileMathIC(Node*, JITBinaryMathIC<Generator>*, bool needsScratchGPRReg, bool needsScratchFPRReg, RepatchingFunction, NonRepatchingFunction);
+    template <typename Generator, typename RepatchingFunction, typename NonRepatchingFunction>
+    void compileMathIC(Node*, JITUnaryMathIC<Generator>*, bool needsScratchGPRReg, RepatchingFunction, NonRepatchingFunction);
 
     void compileArithDoubleUnaryOp(Node*, double (*doubleFunction)(double), double (*operation)(ExecState*, EncodedJSValue));
     void compileValueAdd(Node*);

Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (207368 => 207369)


--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2016-10-15 01:48:36 UTC (rev 207368)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2016-10-15 02:19:16 UTC (rev 207369)
@@ -1580,6 +1580,90 @@
     }
 
     template <typename Generator>
+    void compileMathIC(JITUnaryMathIC<Generator>* mathIC, FunctionPtr repatchingFunction, FunctionPtr nonRepatchingFunction)
+    {
+        Node* node = m_node;
+
+        LValue operand = lowJSValue(node->child1());
+
+        PatchpointValue* patchpoint = m_out.patchpoint(Int64);
+        patchpoint->appendSomeRegister(operand);
+        patchpoint->append(m_tagMask, ValueRep::lateReg(GPRInfo::tagMaskRegister));
+        patchpoint->append(m_tagTypeNumber, ValueRep::lateReg(GPRInfo::tagTypeNumberRegister));
+        RefPtr<PatchpointExceptionHandle> exceptionHandle = preparePatchpointForExceptions(patchpoint);
+        patchpoint->numGPScratchRegisters = 1;
+        patchpoint->clobber(RegisterSet::macroScratchRegisters());
+        State* state = &m_ftlState;
+        patchpoint->setGenerator(
+            [=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
+                AllowMacroScratchRegisterUsage allowScratch(jit);
+
+                Box<CCallHelpers::JumpList> exceptions =
+                    exceptionHandle->scheduleExitCreation(params)->jumps(jit);
+
+#if ENABLE(MATH_IC_STATS)
+                auto inlineStart = jit.label();
+#endif
+
+                Box<MathICGenerationState> mathICGenerationState = Box<MathICGenerationState>::create();
+                mathIC->m_generator = Generator(JSValueRegs(params[0].gpr()), JSValueRegs(params[1].gpr()), params.gpScratch(0));
+
+                bool shouldEmitProfiling = false;
+                bool generatedInline = mathIC->generateInline(jit, *mathICGenerationState, shouldEmitProfiling);
+
+                if (generatedInline) {
+                    ASSERT(!mathICGenerationState->slowPathJumps.empty());
+                    auto done = jit.label();
+                    params.addLatePath([=] (CCallHelpers& jit) {
+                        AllowMacroScratchRegisterUsage allowScratch(jit);
+                        mathICGenerationState->slowPathJumps.link(&jit);
+                        mathICGenerationState->slowPathStart = jit.label();
+#if ENABLE(MATH_IC_STATS)
+                        auto slowPathStart = jit.label();
+#endif
+
+                        if (mathICGenerationState->shouldSlowPathRepatch) {
+                            SlowPathCall call = callOperation(*state, params.unavailableRegisters(), jit, node->origin.semantic, exceptions.get(),
+                                repatchingFunction, params[0].gpr(), params[1].gpr(), CCallHelpers::TrustedImmPtr(mathIC));
+                            mathICGenerationState->slowPathCall = call.call();
+                        } else {
+                            SlowPathCall call = callOperation(*state, params.unavailableRegisters(), jit, node->origin.semantic,
+                                exceptions.get(), nonRepatchingFunction, params[0].gpr(), params[1].gpr());
+                            mathICGenerationState->slowPathCall = call.call();
+                        }
+                        jit.jump().linkTo(done, &jit);
+
+                        jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
+                            mathIC->finalizeInlineCode(*mathICGenerationState, linkBuffer);
+                        });
+
+#if ENABLE(MATH_IC_STATS)
+                        auto slowPathEnd = jit.label();
+                        jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
+                            size_t size = static_cast<char*>(linkBuffer.locationOf(slowPathEnd).executableAddress()) - static_cast<char*>(linkBuffer.locationOf(slowPathStart).executableAddress());
+                            mathIC->m_generatedCodeSize += size;
+                        });
+#endif
+                    });
+                } else {
+                    callOperation(
+                        *state, params.unavailableRegisters(), jit, node->origin.semantic, exceptions.get(),
+                        nonRepatchingFunction, params[0].gpr(), params[1].gpr());
+                }
+
+#if ENABLE(MATH_IC_STATS)
+                auto inlineEnd = jit.label();
+                jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
+                    size_t size = static_cast<char*>(linkBuffer.locationOf(inlineEnd).executableAddress()) - static_cast<char*>(linkBuffer.locationOf(inlineStart).executableAddress());
+                    mathIC->m_generatedCodeSize += size;
+                });
+#endif
+            });
+
+        setJSValue(patchpoint);
+    }
+
+    template <typename Generator>
     void compileMathIC(JITBinaryMathIC<Generator>* mathIC, FunctionPtr repatchingFunction, FunctionPtr nonRepatchingFunction)
     {
         Node* node = m_node;
@@ -2451,7 +2535,12 @@
         }
             
         default:
-            DFG_CRASH(m_graph, m_node, "Bad use kind");
+            DFG_ASSERT(m_graph, m_node, m_node->child1().useKind() == UntypedUse);
+            ArithProfile* arithProfile = m_ftlState.graph.baselineCodeBlockFor(m_node->origin.semantic)->arithProfileForBytecodeOffset(m_node->origin.semantic.bytecodeIndex);
+            JITNegIC* negIC = codeBlock()->addJITNegIC(arithProfile);
+            auto repatchingFunction = operationArithNegateOptimize;
+            auto nonRepatchingFunction = operationArithNegate;
+            compileMathIC(negIC, repatchingFunction, nonRepatchingFunction);
             break;
         }
     }

Modified: trunk/Source/_javascript_Core/jit/JITNegGenerator.cpp (207368 => 207369)


--- trunk/Source/_javascript_Core/jit/JITNegGenerator.cpp	2016-10-15 01:48:36 UTC (rev 207368)
+++ trunk/Source/_javascript_Core/jit/JITNegGenerator.cpp	2016-10-15 02:19:16 UTC (rev 207369)
@@ -82,7 +82,7 @@
     return JITMathICInlineResult::GenerateFullSnippet;
 }
 
-bool JITNegGenerator::generateFastPath(CCallHelpers& jit, CCallHelpers::JumpList& endJumpList, CCallHelpers::JumpList& slowPathJumpList, const ArithProfile*, bool)
+bool JITNegGenerator::generateFastPath(CCallHelpers& jit, CCallHelpers::JumpList& endJumpList, CCallHelpers::JumpList& slowPathJumpList, const ArithProfile* arithProfile, bool shouldEmitProfiling)
 {
     ASSERT(m_scratchGPR != m_src.payloadGPR());
     ASSERT(m_scratchGPR != m_result.payloadGPR());
@@ -115,6 +115,10 @@
 #else
     jit.xor32(CCallHelpers::TrustedImm32(1 << 31), m_result.tagGPR());
 #endif
+    // The flags of ArithNegate are basic in DFG.
+    // We only need to know if we ever produced a number.
+    if (shouldEmitProfiling && arithProfile && !arithProfile->lhsObservedType().sawNumber() && !arithProfile->didObserveDouble())
+        arithProfile->emitSetDouble(jit);
     return true;
 }
 

Modified: trunk/Source/_javascript_Core/jit/JITOperations.cpp (207368 => 207369)


--- trunk/Source/_javascript_Core/jit/JITOperations.cpp	2016-10-15 01:48:36 UTC (rev 207368)
+++ trunk/Source/_javascript_Core/jit/JITOperations.cpp	2016-10-15 02:19:16 UTC (rev 207369)
@@ -2470,7 +2470,10 @@
     double number = operand.toNumber(exec);
     if (UNLIKELY(scope.exception()))
         return JSValue::encode(JSValue());
-    return JSValue::encode(jsNumber(-number));
+
+    JSValue result = jsNumber(-number);
+    arithProfile.observeResult(result);
+    return JSValue::encode(result);
 }
 
 EncodedJSValue JIT_OPERATION operationArithNegate(ExecState* exec, EncodedJSValue operand)
@@ -2504,7 +2507,9 @@
     double number = operand.toNumber(exec);
     if (UNLIKELY(scope.exception()))
         return JSValue::encode(JSValue());
-    return JSValue::encode(jsNumber(-number));
+    JSValue result = jsNumber(-number);
+    arithProfile->observeResult(result);
+    return JSValue::encode(result);
 }
 
 EncodedJSValue JIT_OPERATION operationArithNegateOptimize(ExecState* exec, EncodedJSValue encodedOperand, JITNegIC* negIC)
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to