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)