Title: [206134] trunk
Revision
206134
Author
commit-qu...@webkit.org
Date
2016-09-19 17:48:39 -0700 (Mon, 19 Sep 2016)

Log Message

[JSC] Make the rounding-related nodes support any type
https://bugs.webkit.org/show_bug.cgi?id=161895

Patch by Benjamin Poulain <bpoul...@apple.com> on 2016-09-19
Reviewed by Geoffrey Garen.

JSTests:

* stress/arith-ceil-on-various-types.js: Added.
* stress/arith-floor-on-various-types.js: Added.
* stress/arith-round-on-various-types.js: Added.
* stress/arith-trunc-on-various-types.js: Added.

Source/_javascript_Core:

This patch changes ArithRound, ArithFloor, ArithCeil and ArithTrunc
to support polymorphic input without exiting on entry.

* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
The 4 functions ignore any input past the first argument. It is okay
to use the nodes with the first argument and let the Phantoms keep
the remaining arguments live.

* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
Our fixup had the issue we have seen on previous nodes: unaryArithShouldSpeculateInt32()
prevents us from picking a good type if we do not see any double.

* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
Prediction propagation of those nodes are fully determined
from their flags and results's prediction. They are moved
to the invariant processing.

* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileArithRounding):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileArithRound):
(JSC::FTL::DFG::LowerDFGToB3::compileArithFloor):
(JSC::FTL::DFG::LowerDFGToB3::compileArithCeil):
(JSC::FTL::DFG::LowerDFGToB3::compileArithTrunc):

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (206133 => 206134)


--- trunk/JSTests/ChangeLog	2016-09-19 23:51:00 UTC (rev 206133)
+++ trunk/JSTests/ChangeLog	2016-09-20 00:48:39 UTC (rev 206134)
@@ -1,3 +1,15 @@
+2016-09-19  Benjamin Poulain  <bpoul...@apple.com>
+
+        [JSC] Make the rounding-related nodes support any type
+        https://bugs.webkit.org/show_bug.cgi?id=161895
+
+        Reviewed by Geoffrey Garen.
+
+        * stress/arith-ceil-on-various-types.js: Added.
+        * stress/arith-floor-on-various-types.js: Added.
+        * stress/arith-round-on-various-types.js: Added.
+        * stress/arith-trunc-on-various-types.js: Added.
+
 2016-09-18  Yusuke Suzuki  <utatane....@gmail.com>
 
         [JSC] Do not need to use defineProperty to define methods for object literals

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


--- trunk/JSTests/stress/arith-ceil-on-various-types.js	                        (rev 0)
+++ trunk/JSTests/stress/arith-ceil-on-various-types.js	2016-09-20 00:48:39 UTC (rev 206134)
@@ -0,0 +1,306 @@
+"use strict";
+
+let validInputTestCases = [
+    // input as string, expected result as string.
+    ["undefined", "NaN"],
+    ["null", "0"],
+    ["0", "0"],
+    ["-0.", "-0"],
+    ["0.5", "1"],
+    ["-0.5", "-0"],
+    ["4", "4"],
+    ["42.1", "43"],
+    ["42.5", "43"],
+    ["42.9", "43"],
+    ["-42.1", "-42"],
+    ["-42.5", "-42"],
+    ["-42.9", "-42"],
+    ["Math.PI", "4"],
+    ["Infinity", "Infinity"],
+    ["-Infinity", "-Infinity"],
+    ["NaN", "NaN"],
+    ["\"WebKit\"", "NaN"],
+    ["\"4\"", "4"],
+    ["\"42.5\"", "43"],
+    ["{ valueOf: () => { return 4; } }", "4"],
+    ["{ valueOf: () => { return 0; } }", "0"],
+    ["{ valueOf: () => { return -0; } }", "-0"],
+    ["{ valueOf: () => { return 0.5; } }", "1"],
+    ["{ valueOf: () => { return -0.5; } }", "-0"],
+    ["{ 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"],
+    ["{ valueOf: () => { return (0x7fffffff|0) + 0.5; } }", "2147483648"],
+];
+
+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 Math.ceil() without arguments.
+function opaqueCeilNoArgument() {
+    return Math.ceil();
+}
+noInline(opaqueCeilNoArgument);
+noOSRExitFuzzing(opaqueCeilNoArgument);
+
+function testNoArgument() {
+    for (let i = 0; i < 1e4; ++i) {
+        let output = opaqueCeilNoArgument();
+        if (!isIdentical(output, NaN)) {
+            throw "Failed opaqueCeilNoArgument";
+        }
+    }
+    if (numberOfDFGCompiles(opaqueCeilNoArgument) > 1)
+        throw "The call without arguments should never exit.";
+}
+testNoArgument();
+
+
+// Test Math.ceil() with a very polymorphic input. All test cases are seen at each iteration.
+function opaqueAllTypesCeil(argument) {
+    return Math.ceil(argument);
+}
+noInline(opaqueAllTypesCeil);
+noOSRExitFuzzing(opaqueAllTypesCeil);
+
+function testAllTypesCall() {
+    for (let i = 0; i < 1e3; ++i) {
+        for (let testCaseInput of validInputTypedTestCases) {
+            let output = opaqueAllTypesCeil(testCaseInput[0]);
+            if (!isIdentical(output, testCaseInput[1]))
+                throw "Failed testAllTypesCall for input " + testCaseInput[0] + " expected " + testCaseInput[1] + " got " + output;
+        }
+    }
+    if (numberOfDFGCompiles(opaqueAllTypesCeil) > 3)
+        throw "We should have detected ceil() was polymorphic and generated a generic version.";
+}
+testAllTypesCall();
+
+
+// Polymorphic input but negative zero is not observable.
+function opaqueAllTypesCeilWithoutNegativeZero(argument) {
+    return Math.ceil(argument) + 0;
+}
+noInline(opaqueAllTypesCeilWithoutNegativeZero);
+noOSRExitFuzzing(opaqueAllTypesCeilWithoutNegativeZero);
+
+function testAllTypesWithoutNegativeZeroCall() {
+    for (let i = 0; i < 1e3; ++i) {
+        for (let testCaseInput of validInputTypedTestCases) {
+            let output = opaqueAllTypesCeilWithoutNegativeZero(testCaseInput[0]);
+            if (!isIdentical(output, testCaseInput[1] + 0))
+                throw "Failed testAllTypesWithoutNegativeZeroCall for input " + testCaseInput[0] + " expected " + testCaseInput[1] + " got " + output;
+        }
+    }
+    if (numberOfDFGCompiles(opaqueAllTypesCeil) > 3)
+        throw "We should have detected ceil() was polymorphic and generated a generic version.";
+}
+testAllTypesWithoutNegativeZeroCall();
+
+
+// Test Math.ceil() on a completely typed input. Every call see only one type.
+function testSingleTypeCall() {
+    for (let testCaseInput of validInputTestCases) {
+        eval(`
+            function opaqueCeil(argument) {
+                return Math.ceil(argument);
+            }
+            noInline(opaqueCeil);
+            noOSRExitFuzzing(opaqueCeil);
+
+            for (let i = 0; i < 1e4; ++i) {
+                if (!isIdentical(opaqueCeil(${testCaseInput[0]}), ${testCaseInput[1]})) {
+                    throw "Failed testSingleTypeCall()";
+                }
+            }
+            if (numberOfDFGCompiles(opaqueCeil) > 1)
+                throw "We should have compiled a single ceil 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 Math.ceil() on a completely typed input, but without negative zero.
+function testSingleTypeWithoutNegativeZeroCall() {
+    for (let testCaseInput of validInputTestCases) {
+        eval(`
+            function opaqueCeil(argument) {
+                return Math.ceil(argument) + 0;
+            }
+            noInline(opaqueCeil);
+            noOSRExitFuzzing(opaqueCeil);
+
+            for (let i = 0; i < 1e4; ++i) {
+                if (!isIdentical(opaqueCeil(${testCaseInput[0]}), ${testCaseInput[1]} + 0)) {
+                    throw "Failed testSingleTypeWithoutNegativeZeroCall()";
+                }
+            }
+            if (!checkCompileCountForUselessNegativeZero(opaqueCeil))
+                throw "We should have compiled a single ceil for the expected type.";
+        `);
+    }
+}
+testSingleTypeWithoutNegativeZeroCall();
+
+
+// Test Math.ceil() on constants
+function testConstant() {
+    for (let testCaseInput of validInputTestCases) {
+        eval(`
+            function opaqueCeilOnConstant() {
+                return Math.ceil(${testCaseInput[0]});
+            }
+            noInline(opaqueCeilOnConstant);
+            noOSRExitFuzzing(opaqueCeilOnConstant);
+
+            for (let i = 0; i < 1e4; ++i) {
+                if (!isIdentical(opaqueCeilOnConstant(), ${testCaseInput[1]})) {
+                    throw "Failed testConstant()";
+                }
+            }
+            if (numberOfDFGCompiles(opaqueCeilOnConstant) > 1)
+                throw "We should have compiled a single ceil for the expected type.";
+        `);
+    }
+}
+testConstant();
+
+
+// Verify we call valueOf() exactly once per call.
+function opaqueCeilForSideEffects(argument) {
+    return Math.ceil(argument);
+}
+noInline(opaqueCeilForSideEffects);
+noOSRExitFuzzing(opaqueCeilForSideEffects);
+
+function testSideEffect() {
+    let testObject = {
+        counter: 0,
+        valueOf: function() { ++this.counter; return 16; }
+    };
+    let ceil16 = Math.ceil(16);
+    for (let i = 0; i < 1e4; ++i) {
+        if (opaqueCeilForSideEffects(testObject) !== ceil16)
+            throw "Incorrect result in testSideEffect()";
+    }
+    if (testObject.counter !== 1e4)
+        throw "Failed testSideEffect()";
+    if (numberOfDFGCompiles(opaqueCeilForSideEffects) > 1)
+        throw "opaqueCeilForSideEffects() is predictable, it should only be compiled once.";
+}
+testSideEffect();
+
+
+// Verify ceil() is not subject to CSE if the argument has side effects.
+function opaqueCeilForCSE(argument) {
+    return Math.ceil(argument) + Math.ceil(argument) + Math.ceil(argument);
+}
+noInline(opaqueCeilForCSE);
+noOSRExitFuzzing(opaqueCeilForCSE);
+
+function testCSE() {
+    let testObject = {
+        counter: 0,
+        valueOf: function() { ++this.counter; return 16; }
+    };
+    let ceil16 = Math.ceil(16);
+    let threeCeil16 = ceil16 + ceil16 + ceil16;
+    for (let i = 0; i < 1e4; ++i) {
+        if (opaqueCeilForCSE(testObject) !== threeCeil16)
+            throw "Incorrect result in testCSE()";
+    }
+    if (testObject.counter !== 3e4)
+        throw "Failed testCSE()";
+    if (numberOfDFGCompiles(opaqueCeilForCSE) > 1)
+        throw "opaqueCeilForCSE() is predictable, it should only be compiled once.";
+}
+testCSE();
+
+
+// Verify ceil() is not subject to DCE if the argument has side effects.
+function opaqueCeilForDCE(argument) {
+    Math.ceil(argument);
+}
+noInline(opaqueCeilForDCE);
+noOSRExitFuzzing(opaqueCeilForDCE);
+
+function testDCE() {
+    let testObject = {
+        counter: 0,
+        valueOf: function() { ++this.counter; return 16; }
+    };
+    for (let i = 0; i < 1e4; ++i) {
+        opaqueCeilForDCE(testObject);
+    }
+    if (testObject.counter !== 1e4)
+        throw "Failed testDCE()";
+    if (numberOfDFGCompiles(opaqueCeilForDCE) > 1)
+        throw "opaqueCeilForDCE() is predictable, it should only be compiled once.";
+}
+testDCE();
+
+
+// Test exceptions in the argument.
+function testException() {
+    let counter = 0;
+    function opaqueCeilWithException(argument) {
+        let result = Math.ceil(argument);
+        ++counter;
+        return result;
+    }
+    noInline(opaqueCeilWithException);
+
+    let testObject = { valueOf: () => {  return 64; } };
+    let ceil64 = Math.ceil(64);
+
+    // Warm up without exception.
+    for (let i = 0; i < 1e3; ++i) {
+        if (opaqueCeilWithException(testObject) !== ceil64)
+            throw "Incorrect result in opaqueCeilWithException()";
+    }
+
+    let testThrowObject = { valueOf: () => { throw testObject; return 64; } };
+
+    for (let i = 0; i < 1e2; ++i) {
+        try {
+            if (opaqueCeilWithException(testThrowObject) !== 8)
+                throw "This code should not be reached!!";
+        } catch (e) {
+            if (e !== testObject) {
+                throw "Wrong object thrown from opaqueCeilWithException."
+            }
+        }
+    }
+
+    if (counter !== 1e3) {
+        throw "Invalid count in testException()";
+    }
+}
+testException();

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


--- trunk/JSTests/stress/arith-floor-on-various-types.js	                        (rev 0)
+++ trunk/JSTests/stress/arith-floor-on-various-types.js	2016-09-20 00:48:39 UTC (rev 206134)
@@ -0,0 +1,306 @@
+"use strict";
+
+let validInputTestCases = [
+    // input as string, expected result as string.
+    ["undefined", "NaN"],
+    ["null", "0"],
+    ["0", "0"],
+    ["-0.", "-0"],
+    ["0.5", "0"],
+    ["-0.5", "-1"],
+    ["4", "4"],
+    ["42.1", "42"],
+    ["42.5", "42"],
+    ["42.9", "42"],
+    ["-42.1", "-43"],
+    ["-42.5", "-43"],
+    ["-42.9", "-43"],
+    ["Math.PI", "3"],
+    ["Infinity", "Infinity"],
+    ["-Infinity", "-Infinity"],
+    ["NaN", "NaN"],
+    ["\"WebKit\"", "NaN"],
+    ["\"4\"", "4"],
+    ["\"42.5\"", "42"],
+    ["{ valueOf: () => { return 4; } }", "4"],
+    ["{ valueOf: () => { return 0; } }", "0"],
+    ["{ valueOf: () => { return -0; } }", "-0"],
+    ["{ valueOf: () => { return 0.5; } }", "0"],
+    ["{ valueOf: () => { return -0.5; } }", "-1"],
+    ["{ 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; } }", "-2147483649"],
+    ["{ valueOf: () => { return (0x7fffffff|0) + 0.5; } }", "2147483647"],
+];
+
+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 Math.floor() without arguments.
+function opaqueFloorNoArgument() {
+    return Math.floor();
+}
+noInline(opaqueFloorNoArgument);
+noOSRExitFuzzing(opaqueFloorNoArgument);
+
+function testNoArgument() {
+    for (let i = 0; i < 1e4; ++i) {
+        let output = opaqueFloorNoArgument();
+        if (!isIdentical(output, NaN)) {
+            throw "Failed opaqueFloorNoArgument";
+        }
+    }
+    if (numberOfDFGCompiles(opaqueFloorNoArgument) > 1)
+        throw "The call without arguments should never exit.";
+}
+testNoArgument();
+
+
+// Test Math.floor() with a very polymorphic input. All test cases are seen at each iteration.
+function opaqueAllTypesFloor(argument) {
+    return Math.floor(argument);
+}
+noInline(opaqueAllTypesFloor);
+noOSRExitFuzzing(opaqueAllTypesFloor);
+
+function testAllTypesCall() {
+    for (let i = 0; i < 1e3; ++i) {
+        for (let testCaseInput of validInputTypedTestCases) {
+            let output = opaqueAllTypesFloor(testCaseInput[0]);
+            if (!isIdentical(output, testCaseInput[1]))
+                throw "Failed testAllTypesCall for input " + testCaseInput[0] + " expected " + testCaseInput[1] + " got " + output;
+        }
+    }
+    if (numberOfDFGCompiles(opaqueAllTypesFloor) > 3)
+        throw "We should have detected floor() was polymorphic and generated a generic version.";
+}
+testAllTypesCall();
+
+
+// Polymorphic input but negative zero is not observable.
+function opaqueAllTypesFloorWithoutNegativeZero(argument) {
+    return Math.floor(argument) + 0;
+}
+noInline(opaqueAllTypesFloorWithoutNegativeZero);
+noOSRExitFuzzing(opaqueAllTypesFloorWithoutNegativeZero);
+
+function testAllTypesWithoutNegativeZeroCall() {
+    for (let i = 0; i < 1e3; ++i) {
+        for (let testCaseInput of validInputTypedTestCases) {
+            let output = opaqueAllTypesFloorWithoutNegativeZero(testCaseInput[0]);
+            if (!isIdentical(output, testCaseInput[1] + 0))
+                throw "Failed testAllTypesWithoutNegativeZeroCall for input " + testCaseInput[0] + " expected " + testCaseInput[1] + " got " + output;
+        }
+    }
+    if (numberOfDFGCompiles(opaqueAllTypesFloor) > 3)
+        throw "We should have detected floor() was polymorphic and generated a generic version.";
+}
+testAllTypesWithoutNegativeZeroCall();
+
+
+// Test Math.floor() on a completely typed input. Every call see only one type.
+function testSingleTypeCall() {
+    for (let testCaseInput of validInputTestCases) {
+        eval(`
+            function opaqueFloor(argument) {
+                return Math.floor(argument);
+            }
+            noInline(opaqueFloor);
+            noOSRExitFuzzing(opaqueFloor);
+
+            for (let i = 0; i < 1e4; ++i) {
+                if (!isIdentical(opaqueFloor(${testCaseInput[0]}), ${testCaseInput[1]})) {
+                    throw "Failed testSingleTypeCall()";
+                }
+            }
+            if (numberOfDFGCompiles(opaqueFloor) > 1)
+                throw "We should have compiled a single floor 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 Math.floor() on a completely typed input, but without negative zero.
+function testSingleTypeWithoutNegativeZeroCall() {
+    for (let testCaseInput of validInputTestCases) {
+        eval(`
+            function opaqueFloor(argument) {
+                return Math.floor(argument) + 0;
+            }
+            noInline(opaqueFloor);
+            noOSRExitFuzzing(opaqueFloor);
+
+            for (let i = 0; i < 1e4; ++i) {
+                if (!isIdentical(opaqueFloor(${testCaseInput[0]}), ${testCaseInput[1]} + 0)) {
+                    throw "Failed testSingleTypeWithoutNegativeZeroCall()";
+                }
+            }
+            if (!checkCompileCountForUselessNegativeZero(opaqueFloor))
+                throw "We should have compiled a single floor for the expected type.";
+        `);
+    }
+}
+testSingleTypeWithoutNegativeZeroCall();
+
+
+// Test Math.floor() on constants
+function testConstant() {
+    for (let testCaseInput of validInputTestCases) {
+        eval(`
+            function opaqueFloorOnConstant() {
+                return Math.floor(${testCaseInput[0]});
+            }
+            noInline(opaqueFloorOnConstant);
+            noOSRExitFuzzing(opaqueFloorOnConstant);
+
+            for (let i = 0; i < 1e4; ++i) {
+                if (!isIdentical(opaqueFloorOnConstant(), ${testCaseInput[1]})) {
+                    throw "Failed testConstant()";
+                }
+            }
+            if (numberOfDFGCompiles(opaqueFloorOnConstant) > 1)
+                throw "We should have compiled a single floor for the expected type.";
+        `);
+    }
+}
+testConstant();
+
+
+// Verify we call valueOf() exactly once per call.
+function opaqueFloorForSideEffects(argument) {
+    return Math.floor(argument);
+}
+noInline(opaqueFloorForSideEffects);
+noOSRExitFuzzing(opaqueFloorForSideEffects);
+
+function testSideEffect() {
+    let testObject = {
+        counter: 0,
+        valueOf: function() { ++this.counter; return 16; }
+    };
+    let floor16 = Math.floor(16);
+    for (let i = 0; i < 1e4; ++i) {
+        if (opaqueFloorForSideEffects(testObject) !== floor16)
+            throw "Incorrect result in testSideEffect()";
+    }
+    if (testObject.counter !== 1e4)
+        throw "Failed testSideEffect()";
+    if (numberOfDFGCompiles(opaqueFloorForSideEffects) > 1)
+        throw "opaqueFloorForSideEffects() is predictable, it should only be compiled once.";
+}
+testSideEffect();
+
+
+// Verify floor() is not subject to CSE if the argument has side effects.
+function opaqueFloorForCSE(argument) {
+    return Math.floor(argument) + Math.floor(argument) + Math.floor(argument);
+}
+noInline(opaqueFloorForCSE);
+noOSRExitFuzzing(opaqueFloorForCSE);
+
+function testCSE() {
+    let testObject = {
+        counter: 0,
+        valueOf: function() { ++this.counter; return 16; }
+    };
+    let floor16 = Math.floor(16);
+    let threeFloor16 = floor16 + floor16 + floor16;
+    for (let i = 0; i < 1e4; ++i) {
+        if (opaqueFloorForCSE(testObject) !== threeFloor16)
+            throw "Incorrect result in testCSE()";
+    }
+    if (testObject.counter !== 3e4)
+        throw "Failed testCSE()";
+    if (numberOfDFGCompiles(opaqueFloorForCSE) > 1)
+        throw "opaqueFloorForCSE() is predictable, it should only be compiled once.";
+}
+testCSE();
+
+
+// Verify floor() is not subject to DCE if the argument has side effects.
+function opaqueFloorForDCE(argument) {
+    Math.floor(argument);
+}
+noInline(opaqueFloorForDCE);
+noOSRExitFuzzing(opaqueFloorForDCE);
+
+function testDCE() {
+    let testObject = {
+        counter: 0,
+        valueOf: function() { ++this.counter; return 16; }
+    };
+    for (let i = 0; i < 1e4; ++i) {
+        opaqueFloorForDCE(testObject);
+    }
+    if (testObject.counter !== 1e4)
+        throw "Failed testDCE()";
+    if (numberOfDFGCompiles(opaqueFloorForDCE) > 1)
+        throw "opaqueFloorForDCE() is predictable, it should only be compiled once.";
+}
+testDCE();
+
+
+// Test exceptions in the argument.
+function testException() {
+    let counter = 0;
+    function opaqueFloorWithException(argument) {
+        let result = Math.floor(argument);
+        ++counter;
+        return result;
+    }
+    noInline(opaqueFloorWithException);
+
+    let testObject = { valueOf: () => {  return 64; } };
+    let floor64 = Math.floor(64);
+
+    // Warm up without exception.
+    for (let i = 0; i < 1e3; ++i) {
+        if (opaqueFloorWithException(testObject) !== floor64)
+            throw "Incorrect result in opaqueFloorWithException()";
+    }
+
+    let testThrowObject = { valueOf: () => { throw testObject; return 64; } };
+
+    for (let i = 0; i < 1e2; ++i) {
+        try {
+            if (opaqueFloorWithException(testThrowObject) !== 8)
+                throw "This code should not be reached!!";
+        } catch (e) {
+            if (e !== testObject) {
+                throw "Wrong object thrown from opaqueFloorWithException."
+            }
+        }
+    }
+
+    if (counter !== 1e3) {
+        throw "Invalid count in testException()";
+    }
+}
+testException();

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


--- trunk/JSTests/stress/arith-round-on-various-types.js	                        (rev 0)
+++ trunk/JSTests/stress/arith-round-on-various-types.js	2016-09-20 00:48:39 UTC (rev 206134)
@@ -0,0 +1,306 @@
+"use strict";
+
+let validInputTestCases = [
+    // input as string, expected result as string.
+    ["undefined", "NaN"],
+    ["null", "0"],
+    ["0", "0"],
+    ["-0.", "-0"],
+    ["0.5", "1"],
+    ["-0.5", "-0"],
+    ["4", "4"],
+    ["42.1", "42"],
+    ["42.5", "43"],
+    ["42.9", "43"],
+    ["-42.1", "-42"],
+    ["-42.5", "-42"],
+    ["-42.9", "-43"],
+    ["Math.PI", "3"],
+    ["Infinity", "Infinity"],
+    ["-Infinity", "-Infinity"],
+    ["NaN", "NaN"],
+    ["\"WebKit\"", "NaN"],
+    ["\"4\"", "4"],
+    ["\"42.5\"", "43"],
+    ["{ valueOf: () => { return 4; } }", "4"],
+    ["{ valueOf: () => { return 0; } }", "0"],
+    ["{ valueOf: () => { return -0; } }", "-0"],
+    ["{ valueOf: () => { return 0.5; } }", "1"],
+    ["{ valueOf: () => { return -0.5; } }", "-0"],
+    ["{ 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"],
+    ["{ valueOf: () => { return (0x7fffffff|0) + 0.5; } }", "2147483648"],
+];
+
+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 Math.round() without arguments.
+function opaqueRoundNoArgument() {
+    return Math.round();
+}
+noInline(opaqueRoundNoArgument);
+noOSRExitFuzzing(opaqueRoundNoArgument);
+
+function testNoArgument() {
+    for (let i = 0; i < 1e4; ++i) {
+        let output = opaqueRoundNoArgument();
+        if (!isIdentical(output, NaN)) {
+            throw "Failed opaqueRoundNoArgument";
+        }
+    }
+    if (numberOfDFGCompiles(opaqueRoundNoArgument) > 1)
+        throw "The call without arguments should never exit.";
+}
+testNoArgument();
+
+
+// Test Math.round() with a very polymorphic input. All test cases are seen at each iteration.
+function opaqueAllTypesRound(argument) {
+    return Math.round(argument);
+}
+noInline(opaqueAllTypesRound);
+noOSRExitFuzzing(opaqueAllTypesRound);
+
+function testAllTypesCall() {
+    for (let i = 0; i < 1e3; ++i) {
+        for (let testCaseInput of validInputTypedTestCases) {
+            let output = opaqueAllTypesRound(testCaseInput[0]);
+            if (!isIdentical(output, testCaseInput[1]))
+                throw "Failed testAllTypesCall for input " + testCaseInput[0] + " expected " + testCaseInput[1] + " got " + output;
+        }
+    }
+    if (numberOfDFGCompiles(opaqueAllTypesRound) > 3)
+        throw "We should have detected round() was polymorphic and generated a generic version.";
+}
+testAllTypesCall();
+
+
+// Polymorphic input but negative zero is not observable.
+function opaqueAllTypesRoundWithoutNegativeZero(argument) {
+    return Math.round(argument) + 0;
+}
+noInline(opaqueAllTypesRoundWithoutNegativeZero);
+noOSRExitFuzzing(opaqueAllTypesRoundWithoutNegativeZero);
+
+function testAllTypesWithoutNegativeZeroCall() {
+    for (let i = 0; i < 1e3; ++i) {
+        for (let testCaseInput of validInputTypedTestCases) {
+            let output = opaqueAllTypesRoundWithoutNegativeZero(testCaseInput[0]);
+            if (!isIdentical(output, testCaseInput[1] + 0))
+                throw "Failed testAllTypesWithoutNegativeZeroCall for input " + testCaseInput[0] + " expected " + testCaseInput[1] + " got " + output;
+        }
+    }
+    if (numberOfDFGCompiles(opaqueAllTypesRound) > 3)
+        throw "We should have detected round() was polymorphic and generated a generic version.";
+}
+testAllTypesWithoutNegativeZeroCall();
+
+
+// Test Math.round() on a completely typed input. Every call see only one type.
+function testSingleTypeCall() {
+    for (let testCaseInput of validInputTestCases) {
+        eval(`
+            function opaqueRound(argument) {
+                return Math.round(argument);
+            }
+            noInline(opaqueRound);
+            noOSRExitFuzzing(opaqueRound);
+
+            for (let i = 0; i < 1e4; ++i) {
+                if (!isIdentical(opaqueRound(${testCaseInput[0]}), ${testCaseInput[1]})) {
+                    throw "Failed testSingleTypeCall()";
+                }
+            }
+            if (numberOfDFGCompiles(opaqueRound) > 1)
+                throw "We should have compiled a single round 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 Math.round() on a completely typed input, but without negative zero.
+function testSingleTypeWithoutNegativeZeroCall() {
+    for (let testCaseInput of validInputTestCases) {
+        eval(`
+            function opaqueRound(argument) {
+                return Math.round(argument) + 0;
+            }
+            noInline(opaqueRound);
+            noOSRExitFuzzing(opaqueRound);
+
+            for (let i = 0; i < 1e4; ++i) {
+                if (!isIdentical(opaqueRound(${testCaseInput[0]}), ${testCaseInput[1]} + 0)) {
+                    throw "Failed testSingleTypeWithoutNegativeZeroCall()";
+                }
+            }
+            if (!checkCompileCountForUselessNegativeZero(opaqueRound))
+                throw "We should have compiled a single round for the expected type.";
+        `);
+    }
+}
+testSingleTypeWithoutNegativeZeroCall();
+
+
+// Test Math.round() on constants
+function testConstant() {
+    for (let testCaseInput of validInputTestCases) {
+        eval(`
+            function opaqueRoundOnConstant() {
+                return Math.round(${testCaseInput[0]});
+            }
+            noInline(opaqueRoundOnConstant);
+            noOSRExitFuzzing(opaqueRoundOnConstant);
+
+            for (let i = 0; i < 1e4; ++i) {
+                if (!isIdentical(opaqueRoundOnConstant(), ${testCaseInput[1]})) {
+                    throw "Failed testConstant()";
+                }
+            }
+            if (numberOfDFGCompiles(opaqueRoundOnConstant) > 1)
+                throw "We should have compiled a single round for the expected type.";
+        `);
+    }
+}
+testConstant();
+
+
+// Verify we call valueOf() exactly once per call.
+function opaqueRoundForSideEffects(argument) {
+    return Math.round(argument);
+}
+noInline(opaqueRoundForSideEffects);
+noOSRExitFuzzing(opaqueRoundForSideEffects);
+
+function testSideEffect() {
+    let testObject = {
+        counter: 0,
+        valueOf: function() { ++this.counter; return 16; }
+    };
+    let round16 = Math.round(16);
+    for (let i = 0; i < 1e4; ++i) {
+        if (opaqueRoundForSideEffects(testObject) !== round16)
+            throw "Incorrect result in testSideEffect()";
+    }
+    if (testObject.counter !== 1e4)
+        throw "Failed testSideEffect()";
+    if (numberOfDFGCompiles(opaqueRoundForSideEffects) > 1)
+        throw "opaqueRoundForSideEffects() is predictable, it should only be compiled once.";
+}
+testSideEffect();
+
+
+// Verify round() is not subject to CSE if the argument has side effects.
+function opaqueRoundForCSE(argument) {
+    return Math.round(argument) + Math.round(argument) + Math.round(argument);
+}
+noInline(opaqueRoundForCSE);
+noOSRExitFuzzing(opaqueRoundForCSE);
+
+function testCSE() {
+    let testObject = {
+        counter: 0,
+        valueOf: function() { ++this.counter; return 16; }
+    };
+    let round16 = Math.round(16);
+    let threeRound16 = round16 + round16 + round16;
+    for (let i = 0; i < 1e4; ++i) {
+        if (opaqueRoundForCSE(testObject) !== threeRound16)
+            throw "Incorrect result in testCSE()";
+    }
+    if (testObject.counter !== 3e4)
+        throw "Failed testCSE()";
+    if (numberOfDFGCompiles(opaqueRoundForCSE) > 1)
+        throw "opaqueRoundForCSE() is predictable, it should only be compiled once.";
+}
+testCSE();
+
+
+// Verify round() is not subject to DCE if the argument has side effects.
+function opaqueRoundForDCE(argument) {
+    Math.round(argument);
+}
+noInline(opaqueRoundForDCE);
+noOSRExitFuzzing(opaqueRoundForDCE);
+
+function testDCE() {
+    let testObject = {
+        counter: 0,
+        valueOf: function() { ++this.counter; return 16; }
+    };
+    for (let i = 0; i < 1e4; ++i) {
+        opaqueRoundForDCE(testObject);
+    }
+    if (testObject.counter !== 1e4)
+        throw "Failed testDCE()";
+    if (numberOfDFGCompiles(opaqueRoundForDCE) > 1)
+        throw "opaqueRoundForDCE() is predictable, it should only be compiled once.";
+}
+testDCE();
+
+
+// Test exceptions in the argument.
+function testException() {
+    let counter = 0;
+    function opaqueRoundWithException(argument) {
+        let result = Math.round(argument);
+        ++counter;
+        return result;
+    }
+    noInline(opaqueRoundWithException);
+
+    let testObject = { valueOf: () => {  return 64; } };
+    let round64 = Math.round(64);
+
+    // Warm up without exception.
+    for (let i = 0; i < 1e3; ++i) {
+        if (opaqueRoundWithException(testObject) !== round64)
+            throw "Incorrect result in opaqueRoundWithException()";
+    }
+
+    let testThrowObject = { valueOf: () => { throw testObject; return 64; } };
+
+    for (let i = 0; i < 1e2; ++i) {
+        try {
+            if (opaqueRoundWithException(testThrowObject) !== 8)
+                throw "This code should not be reached!!";
+        } catch (e) {
+            if (e !== testObject) {
+                throw "Wrong object thrown from opaqueRoundWithException."
+            }
+        }
+    }
+
+    if (counter !== 1e3) {
+        throw "Invalid count in testException()";
+    }
+}
+testException();

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


--- trunk/JSTests/stress/arith-trunc-on-various-types.js	                        (rev 0)
+++ trunk/JSTests/stress/arith-trunc-on-various-types.js	2016-09-20 00:48:39 UTC (rev 206134)
@@ -0,0 +1,306 @@
+"use strict";
+
+let validInputTestCases = [
+    // input as string, expected result as string.
+    ["undefined", "NaN"],
+    ["null", "0"],
+    ["0", "0"],
+    ["-0.", "-0"],
+    ["0.5", "0"],
+    ["-0.5", "-0"],
+    ["4", "4"],
+    ["42.1", "42"],
+    ["42.5", "42"],
+    ["42.9", "42"],
+    ["-42.1", "-42"],
+    ["-42.5", "-42"],
+    ["-42.9", "-42"],
+    ["Math.PI", "3"],
+    ["Infinity", "Infinity"],
+    ["-Infinity", "-Infinity"],
+    ["NaN", "NaN"],
+    ["\"WebKit\"", "NaN"],
+    ["\"4\"", "4"],
+    ["\"42.5\"", "42"],
+    ["{ valueOf: () => { return 4; } }", "4"],
+    ["{ valueOf: () => { return 0; } }", "0"],
+    ["{ valueOf: () => { return -0; } }", "-0"],
+    ["{ valueOf: () => { return 0.5; } }", "0"],
+    ["{ valueOf: () => { return -0.5; } }", "-0"],
+    ["{ 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"],
+    ["{ valueOf: () => { return (0x7fffffff|0) + 0.5; } }", "2147483647"],
+];
+
+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 Math.trunc() without arguments.
+function opaqueTruncNoArgument() {
+    return Math.trunc();
+}
+noInline(opaqueTruncNoArgument);
+noOSRExitFuzzing(opaqueTruncNoArgument);
+
+function testNoArgument() {
+    for (let i = 0; i < 1e4; ++i) {
+        let output = opaqueTruncNoArgument();
+        if (!isIdentical(output, NaN)) {
+            throw "Failed opaqueTruncNoArgument";
+        }
+    }
+    if (numberOfDFGCompiles(opaqueTruncNoArgument) > 1)
+        throw "The call without arguments should never exit.";
+}
+testNoArgument();
+
+
+// Test Math.trunc() with a very polymorphic input. All test cases are seen at each iteration.
+function opaqueAllTypesTrunc(argument) {
+    return Math.trunc(argument);
+}
+noInline(opaqueAllTypesTrunc);
+noOSRExitFuzzing(opaqueAllTypesTrunc);
+
+function testAllTypesCall() {
+    for (let i = 0; i < 1e3; ++i) {
+        for (let testCaseInput of validInputTypedTestCases) {
+            let output = opaqueAllTypesTrunc(testCaseInput[0]);
+            if (!isIdentical(output, testCaseInput[1]))
+                throw "Failed testAllTypesCall for input " + testCaseInput[0] + " expected " + testCaseInput[1] + " got " + output;
+        }
+    }
+    if (numberOfDFGCompiles(opaqueAllTypesTrunc) > 3)
+        throw "We should have detected trunc() was polymorphic and generated a generic version.";
+}
+testAllTypesCall();
+
+
+// Polymorphic input but negative zero is not observable.
+function opaqueAllTypesTruncWithoutNegativeZero(argument) {
+    return Math.trunc(argument) + 0;
+}
+noInline(opaqueAllTypesTruncWithoutNegativeZero);
+noOSRExitFuzzing(opaqueAllTypesTruncWithoutNegativeZero);
+
+function testAllTypesWithoutNegativeZeroCall() {
+    for (let i = 0; i < 1e3; ++i) {
+        for (let testCaseInput of validInputTypedTestCases) {
+            let output = opaqueAllTypesTruncWithoutNegativeZero(testCaseInput[0]);
+            if (!isIdentical(output, testCaseInput[1] + 0))
+                throw "Failed testAllTypesWithoutNegativeZeroCall for input " + testCaseInput[0] + " expected " + testCaseInput[1] + " got " + output;
+        }
+    }
+    if (numberOfDFGCompiles(opaqueAllTypesTrunc) > 3)
+        throw "We should have detected trunc() was polymorphic and generated a generic version.";
+}
+testAllTypesWithoutNegativeZeroCall();
+
+
+// Test Math.trunc() on a completely typed input. Every call see only one type.
+function testSingleTypeCall() {
+    for (let testCaseInput of validInputTestCases) {
+        eval(`
+            function opaqueTrunc(argument) {
+                return Math.trunc(argument);
+            }
+            noInline(opaqueTrunc);
+            noOSRExitFuzzing(opaqueTrunc);
+
+            for (let i = 0; i < 1e4; ++i) {
+                if (!isIdentical(opaqueTrunc(${testCaseInput[0]}), ${testCaseInput[1]})) {
+                    throw "Failed testSingleTypeCall()";
+                }
+            }
+            if (numberOfDFGCompiles(opaqueTrunc) > 1)
+                throw "Failed testSingleTypeCall(). We should have compiled a single trunc 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 Math.trunc() on a completely typed input, but without negative zero.
+function testSingleTypeWithoutNegativeZeroCall() {
+    for (let testCaseInput of validInputTestCases) {
+        eval(`
+            function opaqueTrunc(argument) {
+                return Math.trunc(argument) + 0;
+            }
+            noInline(opaqueTrunc);
+            noOSRExitFuzzing(opaqueTrunc);
+
+            for (let i = 0; i < 1e4; ++i) {
+                if (!isIdentical(opaqueTrunc(${testCaseInput[0]}), ${testCaseInput[1]} + 0)) {
+                    throw "Failed testSingleTypeWithoutNegativeZeroCall()";
+                }
+            }
+            if (!checkCompileCountForUselessNegativeZero(opaqueTrunc))
+                throw "Failed testSingleTypeWithoutNegativeZeroCall(). We should have compiled a single trunc for the expected type.";
+        `);
+    }
+}
+testSingleTypeWithoutNegativeZeroCall();
+
+
+// Test Math.trunc() on constants
+function testConstant() {
+    for (let testCaseInput of validInputTestCases) {
+        eval(`
+            function opaqueTruncOnConstant() {
+                return Math.trunc(${testCaseInput[0]});
+            }
+            noInline(opaqueTruncOnConstant);
+            noOSRExitFuzzing(opaqueTruncOnConstant);
+
+            for (let i = 0; i < 1e4; ++i) {
+                if (!isIdentical(opaqueTruncOnConstant(), ${testCaseInput[1]})) {
+                    throw "Failed testConstant()";
+                }
+            }
+            if (numberOfDFGCompiles(opaqueTruncOnConstant) > 1)
+                throw "Failed testConstant(). We should have compiled a single trunc for the expected type.";
+        `);
+    }
+}
+testConstant();
+
+
+// Verify we call valueOf() exactly once per call.
+function opaqueTruncForSideEffects(argument) {
+    return Math.trunc(argument);
+}
+noInline(opaqueTruncForSideEffects);
+noOSRExitFuzzing(opaqueTruncForSideEffects);
+
+function testSideEffect() {
+    let testObject = {
+        counter: 0,
+        valueOf: function() { ++this.counter; return 16; }
+    };
+    let trunc16 = Math.trunc(16);
+    for (let i = 0; i < 1e4; ++i) {
+        if (opaqueTruncForSideEffects(testObject) !== trunc16)
+            throw "Incorrect result in testSideEffect()";
+    }
+    if (testObject.counter !== 1e4)
+        throw "Failed testSideEffect()";
+    if (numberOfDFGCompiles(opaqueTruncForSideEffects) > 1)
+        throw "opaqueTruncForSideEffects() is predictable, it should only be compiled once.";
+}
+testSideEffect();
+
+
+// Verify trunc() is not subject to CSE if the argument has side effects.
+function opaqueTruncForCSE(argument) {
+    return Math.trunc(argument) + Math.trunc(argument) + Math.trunc(argument);
+}
+noInline(opaqueTruncForCSE);
+noOSRExitFuzzing(opaqueTruncForCSE);
+
+function testCSE() {
+    let testObject = {
+        counter: 0,
+        valueOf: function() { ++this.counter; return 16; }
+    };
+    let trunc16 = Math.trunc(16);
+    let threeTrunc16 = trunc16 + trunc16 + trunc16;
+    for (let i = 0; i < 1e4; ++i) {
+        if (opaqueTruncForCSE(testObject) !== threeTrunc16)
+            throw "Incorrect result in testCSE()";
+    }
+    if (testObject.counter !== 3e4)
+        throw "Failed testCSE()";
+    if (numberOfDFGCompiles(opaqueTruncForCSE) > 1)
+        throw "opaqueTruncForCSE() is predictable, it should only be compiled once.";
+}
+testCSE();
+
+
+// Verify trunc() is not subject to DCE if the argument has side effects.
+function opaqueTruncForDCE(argument) {
+    Math.trunc(argument);
+}
+noInline(opaqueTruncForDCE);
+noOSRExitFuzzing(opaqueTruncForDCE);
+
+function testDCE() {
+    let testObject = {
+        counter: 0,
+        valueOf: function() { ++this.counter; return 16; }
+    };
+    for (let i = 0; i < 1e4; ++i) {
+        opaqueTruncForDCE(testObject);
+    }
+    if (testObject.counter !== 1e4)
+        throw "Failed testDCE()";
+    if (numberOfDFGCompiles(opaqueTruncForDCE) > 1)
+        throw "opaqueTruncForDCE() is predictable, it should only be compiled once.";
+}
+testDCE();
+
+
+// Test exceptions in the argument.
+function testException() {
+    let counter = 0;
+    function opaqueTruncWithException(argument) {
+        let result = Math.trunc(argument);
+        ++counter;
+        return result;
+    }
+    noInline(opaqueTruncWithException);
+
+    let testObject = { valueOf: () => {  return 64; } };
+    let trunc64 = Math.trunc(64);
+
+    // Warm up without exception.
+    for (let i = 0; i < 1e3; ++i) {
+        if (opaqueTruncWithException(testObject) !== trunc64)
+            throw "Incorrect result in opaqueTruncWithException()";
+    }
+
+    let testThrowObject = { valueOf: () => { throw testObject; return 64; } };
+
+    for (let i = 0; i < 1e2; ++i) {
+        try {
+            if (opaqueTruncWithException(testThrowObject) !== 8)
+                throw "This code should not be reached!!";
+        } catch (e) {
+            if (e !== testObject) {
+                throw "Wrong object thrown from opaqueTruncWithException."
+            }
+        }
+    }
+
+    if (counter !== 1e3) {
+        throw "Invalid count in testException()";
+    }
+}
+testException();

Modified: trunk/Source/_javascript_Core/ChangeLog (206133 => 206134)


--- trunk/Source/_javascript_Core/ChangeLog	2016-09-19 23:51:00 UTC (rev 206133)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-09-20 00:48:39 UTC (rev 206134)
@@ -1,3 +1,44 @@
+2016-09-19  Benjamin Poulain  <bpoul...@apple.com>
+
+        [JSC] Make the rounding-related nodes support any type
+        https://bugs.webkit.org/show_bug.cgi?id=161895
+
+        Reviewed by Geoffrey Garen.
+
+        This patch changes ArithRound, ArithFloor, ArithCeil and ArithTrunc
+        to support polymorphic input without exiting on entry.
+
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+        The 4 functions ignore any input past the first argument. It is okay
+        to use the nodes with the first argument and let the Phantoms keep
+        the remaining arguments live.
+
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        Our fixup had the issue we have seen on previous nodes: unaryArithShouldSpeculateInt32()
+        prevents us from picking a good type if we do not see any double.
+
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        Prediction propagation of those nodes are fully determined
+        from their flags and results's prediction. They are moved
+        to the invariant processing.
+
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileArithRounding):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileArithRound):
+        (JSC::FTL::DFG::LowerDFGToB3::compileArithFloor):
+        (JSC::FTL::DFG::LowerDFGToB3::compileArithCeil):
+        (JSC::FTL::DFG::LowerDFGToB3::compileArithTrunc):
+
 2016-09-19  Yusuke Suzuki  <utatane....@gmail.com>
 
         Unreviewed, build fix for Win64

Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (206133 => 206134)


--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2016-09-19 23:51:00 UTC (rev 206133)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2016-09-20 00:48:39 UTC (rev 206134)
@@ -905,19 +905,23 @@
     case ArithCeil:
     case ArithTrunc: {
         JSValue operand = forNode(node->child1()).value();
-        if (operand && operand.isNumber()) {
+        if (Optional<double> number = operand.toNumberFromPrimitive()) {
             double roundedValue = 0;
             if (node->op() == ArithRound)
-                roundedValue = jsRound(operand.asNumber());
+                roundedValue = jsRound(*number);
             else if (node->op() == ArithFloor)
-                roundedValue = floor(operand.asNumber());
+                roundedValue = floor(*number);
             else if (node->op() == ArithCeil)
-                roundedValue = ceil(operand.asNumber());
+                roundedValue = ceil(*number);
             else {
                 ASSERT(node->op() == ArithTrunc);
-                roundedValue = trunc(operand.asNumber());
+                roundedValue = trunc(*number);
             }
 
+            if (node->child1().useKind() == UntypedUse) {
+                setConstant(node, jsNumber(roundedValue));
+                break;
+            }
             if (producesInteger(node->arithRoundingMode())) {
                 int32_t roundedValueAsInt32 = static_cast<int32_t>(roundedValue);
                 if (roundedValueAsInt32 == roundedValue) {
@@ -936,10 +940,15 @@
                 break;
             }
         }
-        if (producesInteger(node->arithRoundingMode()))
-            forNode(node).setType(SpecInt32Only);
-        else
-            forNode(node).setType(typeOfDoubleRounding(forNode(node->child1()).m_type));
+        if (node->child1().useKind() == DoubleRepUse) {
+            if (producesInteger(node->arithRoundingMode()))
+                forNode(node).setType(SpecInt32Only);
+            else if (node->child1().useKind() == DoubleRepUse)
+                forNode(node).setType(typeOfDoubleRounding(forNode(node->child1()).m_type));
+        } else {
+            DFG_ASSERT(m_graph, node, node->child1().useKind() == UntypedUse);
+            forNode(node).setType(SpecFullNumber);
+        }
         break;
     }
             

Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (206133 => 206134)


--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2016-09-19 23:51:00 UTC (rev 206133)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2016-09-20 00:48:39 UTC (rev 206134)
@@ -2441,25 +2441,22 @@
             set(VirtualRegister(resultOperand), addToGraph(JSConstant, OpInfo(m_constantNaN)));
             return true;
         }
-        if (argumentCountIncludingThis == 2) {
-            insertChecks();
-            Node* operand = get(virtualRegisterForArgument(1, registerOffset));
-            NodeType op;
-            if (intrinsic == RoundIntrinsic)
-                op = ArithRound;
-            else if (intrinsic == FloorIntrinsic)
-                op = ArithFloor;
-            else if (intrinsic == CeilIntrinsic)
-                op = ArithCeil;
-            else {
-                ASSERT(intrinsic == TruncIntrinsic);
-                op = ArithTrunc;
-            }
-            Node* roundNode = addToGraph(op, OpInfo(0), OpInfo(prediction), operand);
-            set(VirtualRegister(resultOperand), roundNode);
-            return true;
+        insertChecks();
+        Node* operand = get(virtualRegisterForArgument(1, registerOffset));
+        NodeType op;
+        if (intrinsic == RoundIntrinsic)
+            op = ArithRound;
+        else if (intrinsic == FloorIntrinsic)
+            op = ArithFloor;
+        else if (intrinsic == CeilIntrinsic)
+            op = ArithCeil;
+        else {
+            ASSERT(intrinsic == TruncIntrinsic);
+            op = ArithTrunc;
         }
-        return false;
+        Node* roundNode = addToGraph(op, OpInfo(0), OpInfo(prediction), operand);
+        set(VirtualRegister(resultOperand), roundNode);
+        return true;
     }
     case IMulIntrinsic: {
         if (argumentCountIncludingThis != 3)

Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (206133 => 206134)


--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2016-09-19 23:51:00 UTC (rev 206133)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2016-09-20 00:48:39 UTC (rev 206134)
@@ -359,7 +359,12 @@
     case ArithFloor:
     case ArithCeil:
     case ArithTrunc:
-        def(PureValue(node, static_cast<uintptr_t>(node->arithRoundingMode())));
+        if (node->child1().useKind() == DoubleRepUse)
+            def(PureValue(node, static_cast<uintptr_t>(node->arithRoundingMode())));
+        else {
+            read(World);
+            write(Heap);
+        }
         return;
 
     case CheckCell:

Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (206133 => 206134)


--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2016-09-19 23:51:00 UTC (rev 206133)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2016-09-20 00:48:39 UTC (rev 206134)
@@ -376,24 +376,28 @@
         case ArithFloor:
         case ArithCeil:
         case ArithTrunc: {
-            if (m_graph.unaryArithShouldSpeculateInt32(node, FixupPass)) {
+            if (node->child1()->shouldSpeculateInt32OrBoolean() && m_graph.roundShouldSpeculateInt32(node, FixupPass)) {
                 fixIntOrBooleanEdge(node->child1());
                 insertCheck<Int32Use>(m_indexInBlock, node->child1().node());
                 node->convertToIdentity();
                 break;
             }
-            fixDoubleOrBooleanEdge(node->child1());
+            if (node->child1()->shouldSpeculateNotCell()) {
+                fixDoubleOrBooleanEdge(node->child1());
 
-            if (isInt32OrBooleanSpeculation(node->getHeapPrediction()) && m_graph.roundShouldSpeculateInt32(node, FixupPass)) {
-                node->setResult(NodeResultInt32);
-                if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags()))
-                    node->setArithRoundingMode(Arith::RoundingMode::Int32);
-                else
-                    node->setArithRoundingMode(Arith::RoundingMode::Int32WithNegativeZeroCheck);
-            } else {
-                node->setResult(NodeResultDouble);
-                node->setArithRoundingMode(Arith::RoundingMode::Double);
-            }
+                if (isInt32OrBooleanSpeculation(node->getHeapPrediction()) && m_graph.roundShouldSpeculateInt32(node, FixupPass)) {
+                    node->setResult(NodeResultInt32);
+                    if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags()))
+                        node->setArithRoundingMode(Arith::RoundingMode::Int32);
+                    else
+                        node->setArithRoundingMode(Arith::RoundingMode::Int32WithNegativeZeroCheck);
+                } else {
+                    node->setResult(NodeResultDouble);
+                    node->setArithRoundingMode(Arith::RoundingMode::Double);
+                }
+                node->clearFlags(NodeMustGenerate);
+            } else
+                fixEdge<UntypedUse>(node->child1());
             break;
         }
 

Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (206133 => 206134)


--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2016-09-19 23:51:00 UTC (rev 206133)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2016-09-20 00:48:39 UTC (rev 206134)
@@ -155,10 +155,10 @@
     macro(ArithFRound, NodeResultDouble | NodeMustGenerate) \
     macro(ArithPow, NodeResultDouble) \
     macro(ArithRandom, NodeResultDouble | NodeMustGenerate) \
-    macro(ArithRound, NodeResultNumber) \
-    macro(ArithFloor, NodeResultNumber) \
-    macro(ArithCeil, NodeResultNumber) \
-    macro(ArithTrunc, NodeResultNumber) \
+    macro(ArithRound, NodeResultNumber | NodeMustGenerate) \
+    macro(ArithFloor, NodeResultNumber | NodeMustGenerate) \
+    macro(ArithCeil, NodeResultNumber | NodeMustGenerate) \
+    macro(ArithTrunc, NodeResultNumber | NodeMustGenerate) \
     macro(ArithSqrt, NodeResultDouble | NodeMustGenerate) \
     macro(ArithSin, NodeResultDouble | NodeMustGenerate) \
     macro(ArithCos, NodeResultDouble | NodeMustGenerate) \

Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.cpp (206133 => 206134)


--- trunk/Source/_javascript_Core/dfg/DFGOperations.cpp	2016-09-19 23:51:00 UTC (rev 206133)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.cpp	2016-09-20 00:48:39 UTC (rev 206134)
@@ -442,6 +442,58 @@
     return tan(a);
 }
 
+EncodedJSValue JIT_OPERATION operationArithRound(ExecState* exec, EncodedJSValue encodedArgument)
+{
+    VM* vm = &exec->vm();
+    NativeCallFrameTracer tracer(vm, exec);
+    auto scope = DECLARE_THROW_SCOPE(*vm);
+
+    JSValue argument = JSValue::decode(encodedArgument);
+    double valueOfArgument = argument.toNumber(exec);
+    if (UNLIKELY(scope.exception()))
+        return JSValue::encode(JSValue());
+    return JSValue::encode(jsNumber(jsRound(valueOfArgument)));
+}
+
+EncodedJSValue JIT_OPERATION operationArithFloor(ExecState* exec, EncodedJSValue encodedArgument)
+{
+    VM* vm = &exec->vm();
+    NativeCallFrameTracer tracer(vm, exec);
+    auto scope = DECLARE_THROW_SCOPE(*vm);
+
+    JSValue argument = JSValue::decode(encodedArgument);
+    double valueOfArgument = argument.toNumber(exec);
+    if (UNLIKELY(scope.exception()))
+        return JSValue::encode(JSValue());
+    return JSValue::encode(jsNumber(floor(valueOfArgument)));
+}
+
+EncodedJSValue JIT_OPERATION operationArithCeil(ExecState* exec, EncodedJSValue encodedArgument)
+{
+    VM* vm = &exec->vm();
+    NativeCallFrameTracer tracer(vm, exec);
+    auto scope = DECLARE_THROW_SCOPE(*vm);
+
+    JSValue argument = JSValue::decode(encodedArgument);
+    double valueOfArgument = argument.toNumber(exec);
+    if (UNLIKELY(scope.exception()))
+        return JSValue::encode(JSValue());
+    return JSValue::encode(jsNumber(ceil(valueOfArgument)));
+}
+
+EncodedJSValue JIT_OPERATION operationArithTrunc(ExecState* exec, EncodedJSValue encodedArgument)
+{
+    VM* vm = &exec->vm();
+    NativeCallFrameTracer tracer(vm, exec);
+    auto scope = DECLARE_THROW_SCOPE(*vm);
+
+    JSValue argument = JSValue::decode(encodedArgument);
+    double truncatedValueOfArgument = argument.toIntegerPreserveNaN(exec);
+    if (UNLIKELY(scope.exception()))
+        return JSValue::encode(JSValue());
+    return JSValue::encode(jsNumber(truncatedValueOfArgument));
+}
+
 static ALWAYS_INLINE EncodedJSValue getByVal(ExecState* exec, JSCell* base, uint32_t index)
 {
     VM& vm = exec->vm();

Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.h (206133 => 206134)


--- trunk/Source/_javascript_Core/dfg/DFGOperations.h	2016-09-19 23:51:00 UTC (rev 206133)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.h	2016-09-20 00:48:39 UTC (rev 206134)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, 2013-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2013-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -61,6 +61,10 @@
 double JIT_OPERATION operationArithLog(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
 double JIT_OPERATION operationArithSin(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
 double JIT_OPERATION operationArithSqrt(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationArithRound(ExecState*, EncodedJSValue) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationArithFloor(ExecState*, EncodedJSValue) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationArithCeil(ExecState*, EncodedJSValue) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationArithTrunc(ExecState*, EncodedJSValue) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByVal(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedProperty) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByValCell(ExecState*, JSCell*, EncodedJSValue encodedProperty) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByValArrayInt(ExecState*, JSArray*, int32_t) WTF_INTERNAL;

Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (206133 => 206134)


--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2016-09-19 23:51:00 UTC (rev 206133)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2016-09-20 00:48:39 UTC (rev 206134)
@@ -318,17 +318,6 @@
             break;
         }
 
-        case ArithRound:
-        case ArithFloor:
-        case ArithCeil:
-        case ArithTrunc: {
-            if (isInt32OrBooleanSpeculation(node->getHeapPrediction()) && m_graph.roundShouldSpeculateInt32(node, m_pass))
-                changed |= setPrediction(SpecInt32Only);
-            else
-                changed |= setPrediction(SpecBytecodeDouble);
-            break;
-        }
-
         case ArithAbs: {
             SpeculatedType childPrediction = node->child1()->prediction();
             if (isInt32OrBooleanSpeculation(childPrediction)
@@ -776,6 +765,18 @@
             break;
         }
 
+        case ArithRound:
+        case ArithFloor:
+        case ArithCeil:
+        case ArithTrunc: {
+            if (isInt32OrBooleanSpeculation(m_currentNode->getHeapPrediction())
+                && m_graph.roundShouldSpeculateInt32(m_currentNode, m_pass))
+                setPrediction(SpecInt32Only);
+            else
+                setPrediction(SpecBytecodeDouble);
+            break;
+        }
+
         case ArithRandom: {
             setPrediction(SpecDoubleReal);
             break;
@@ -948,10 +949,6 @@
         case ArithMul:
         case ArithDiv:
         case ArithMod:
-        case ArithRound:
-        case ArithFloor:
-        case ArithCeil:
-        case ArithTrunc:
         case ArithAbs:
         case GetByVal:
         case ToThis:

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (206133 => 206134)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2016-09-19 23:51:00 UTC (rev 206133)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2016-09-20 00:48:39 UTC (rev 206134)
@@ -4915,101 +4915,129 @@
 
 void SpeculativeJIT::compileArithRounding(Node* node)
 {
-    ASSERT(node->child1().useKind() == DoubleRepUse);
+    if (node->child1().useKind() == DoubleRepUse) {
+        SpeculateDoubleOperand value(this, node->child1());
+        FPRReg valueFPR = value.fpr();
 
-    SpeculateDoubleOperand value(this, node->child1());
-    FPRReg valueFPR = value.fpr();
+        auto setResult = [&] (FPRReg resultFPR) {
+            if (producesInteger(node->arithRoundingMode())) {
+                GPRTemporary roundedResultAsInt32(this);
+                FPRTemporary scratch(this);
+                FPRReg scratchFPR = scratch.fpr();
+                GPRReg resultGPR = roundedResultAsInt32.gpr();
+                JITCompiler::JumpList failureCases;
+                m_jit.branchConvertDoubleToInt32(resultFPR, resultGPR, failureCases, scratchFPR, shouldCheckNegativeZero(node->arithRoundingMode()));
+                speculationCheck(Overflow, JSValueRegs(), node, failureCases);
 
-    auto setResult = [&] (FPRReg resultFPR) {
-        if (producesInteger(node->arithRoundingMode())) {
-            GPRTemporary roundedResultAsInt32(this);
-            FPRTemporary scratch(this);
-            FPRReg scratchFPR = scratch.fpr();
-            GPRReg resultGPR = roundedResultAsInt32.gpr();
-            JITCompiler::JumpList failureCases;
-            m_jit.branchConvertDoubleToInt32(resultFPR, resultGPR, failureCases, scratchFPR, shouldCheckNegativeZero(node->arithRoundingMode()));
-            speculationCheck(Overflow, JSValueRegs(), node, failureCases);
+                int32Result(resultGPR, node);
+            } else
+                doubleResult(resultFPR, node);
+        };
 
-            int32Result(resultGPR, node);
-        } else
-            doubleResult(resultFPR, node);
-    };
+        if (m_jit.supportsFloatingPointRounding()) {
+            switch (node->op()) {
+            case ArithRound: {
+                FPRTemporary result(this);
+                FPRReg resultFPR = result.fpr();
+                if (producesInteger(node->arithRoundingMode()) && !shouldCheckNegativeZero(node->arithRoundingMode())) {
+                    static const double halfConstant = 0.5;
+                    m_jit.loadDouble(MacroAssembler::TrustedImmPtr(&halfConstant), resultFPR);
+                    m_jit.addDouble(valueFPR, resultFPR);
+                    m_jit.floorDouble(resultFPR, resultFPR);
+                } else {
+                    m_jit.ceilDouble(valueFPR, resultFPR);
+                    FPRTemporary realPart(this);
+                    FPRReg realPartFPR = realPart.fpr();
+                    m_jit.subDouble(resultFPR, valueFPR, realPartFPR);
 
-    if (m_jit.supportsFloatingPointRounding()) {
-        switch (node->op()) {
-        case ArithRound: {
-            FPRTemporary result(this);
-            FPRReg resultFPR = result.fpr();
-            if (producesInteger(node->arithRoundingMode()) && !shouldCheckNegativeZero(node->arithRoundingMode())) {
-                static const double halfConstant = 0.5;
-                m_jit.loadDouble(MacroAssembler::TrustedImmPtr(&halfConstant), resultFPR);
-                m_jit.addDouble(valueFPR, resultFPR);
-                m_jit.floorDouble(resultFPR, resultFPR);
-            } else {
-                m_jit.ceilDouble(valueFPR, resultFPR);
-                FPRTemporary realPart(this);
-                FPRReg realPartFPR = realPart.fpr();
-                m_jit.subDouble(resultFPR, valueFPR, realPartFPR);
+                    FPRTemporary scratch(this);
+                    FPRReg scratchFPR = scratch.fpr();
+                    static const double halfConstant = 0.5;
+                    m_jit.loadDouble(MacroAssembler::TrustedImmPtr(&halfConstant), scratchFPR);
 
-                FPRTemporary scratch(this);
-                FPRReg scratchFPR = scratch.fpr();
-                static const double halfConstant = 0.5;
-                m_jit.loadDouble(MacroAssembler::TrustedImmPtr(&halfConstant), scratchFPR);
+                    JITCompiler::Jump shouldUseCeiled = m_jit.branchDouble(JITCompiler::DoubleLessThanOrEqual, realPartFPR, scratchFPR);
+                    static const double _oneConstant_ = -1.0;
+                    m_jit.loadDouble(MacroAssembler::TrustedImmPtr(&oneConstant), scratchFPR);
+                    m_jit.addDouble(scratchFPR, resultFPR);
+                    shouldUseCeiled.link(&m_jit);
+                }
+                setResult(resultFPR);
+                return;
+            }
 
-                JITCompiler::Jump shouldUseCeiled = m_jit.branchDouble(JITCompiler::DoubleLessThanOrEqual, realPartFPR, scratchFPR);
-                static const double _oneConstant_ = -1.0;
-                m_jit.loadDouble(MacroAssembler::TrustedImmPtr(&oneConstant), scratchFPR);
-                m_jit.addDouble(scratchFPR, resultFPR);
-                shouldUseCeiled.link(&m_jit);
+            case ArithFloor: {
+                FPRTemporary rounded(this);
+                FPRReg resultFPR = rounded.fpr();
+                m_jit.floorDouble(valueFPR, resultFPR);
+                setResult(resultFPR);
+                return;
             }
-            setResult(resultFPR);
-            return;
-        }
 
-        case ArithFloor: {
-            FPRTemporary rounded(this);
-            FPRReg resultFPR = rounded.fpr();
-            m_jit.floorDouble(valueFPR, resultFPR);
-            setResult(resultFPR);
-            return;
-        }
+            case ArithCeil: {
+                FPRTemporary rounded(this);
+                FPRReg resultFPR = rounded.fpr();
+                m_jit.ceilDouble(valueFPR, resultFPR);
+                setResult(resultFPR);
+                return;
+            }
 
-        case ArithCeil: {
-            FPRTemporary rounded(this);
-            FPRReg resultFPR = rounded.fpr();
-            m_jit.ceilDouble(valueFPR, resultFPR);
-            setResult(resultFPR);
-            return;
-        }
+            case ArithTrunc: {
+                FPRTemporary rounded(this);
+                FPRReg resultFPR = rounded.fpr();
+                m_jit.roundTowardZeroDouble(valueFPR, resultFPR);
+                setResult(resultFPR);
+                return;
+            }
 
-        case ArithTrunc: {
-            FPRTemporary rounded(this);
-            FPRReg resultFPR = rounded.fpr();
-            m_jit.roundTowardZeroDouble(valueFPR, resultFPR);
+            default:
+                RELEASE_ASSERT_NOT_REACHED();
+            }
+        } else {
+            flushRegisters();
+            FPRResult roundedResultAsDouble(this);
+            FPRReg resultFPR = roundedResultAsDouble.fpr();
+            if (node->op() == ArithRound)
+                callOperation(jsRound, resultFPR, valueFPR);
+            else if (node->op() == ArithFloor)
+                callOperation(floor, resultFPR, valueFPR);
+            else if (node->op() == ArithCeil)
+                callOperation(ceil, resultFPR, valueFPR);
+            else {
+                ASSERT(node->op() == ArithTrunc);
+                callOperation(trunc, resultFPR, valueFPR);
+            }
             setResult(resultFPR);
-            return;
         }
+        return;
+    }
 
-        default:
-            RELEASE_ASSERT_NOT_REACHED();
-        }
-    } else {
-        flushRegisters();
-        FPRResult roundedResultAsDouble(this);
-        FPRReg resultFPR = roundedResultAsDouble.fpr();
-        if (node->op() == ArithRound)
-            callOperation(jsRound, resultFPR, valueFPR);
-        else if (node->op() == ArithFloor)
-            callOperation(floor, resultFPR, valueFPR);
-        else if (node->op() == ArithCeil)
-            callOperation(ceil, resultFPR, valueFPR);
-        else {
-            ASSERT(node->op() == ArithTrunc);
-            callOperation(trunc, resultFPR, valueFPR);
-        }
-        m_jit.exceptionCheck();
-        setResult(resultFPR);
+    DFG_ASSERT(m_jit.graph(), node, node->child1().useKind() == UntypedUse);
+
+    JSValueOperand argument(this, node->child1());
+    JSValueRegs argumentRegs = argument.jsValueRegs();
+#if USE(JSVALUE64)
+    GPRTemporary result(this);
+    JSValueRegs resultRegs = JSValueRegs(result.gpr());
+#else
+    GPRTemporary resultTag(this);
+    GPRTemporary resultPayload(this);
+    JSValueRegs resultRegs = JSValueRegs(resultPayload.gpr(), resultTag.gpr());
+#endif
+    flushRegisters();
+    J_JITOperation_EJ operation = nullptr;
+    if (node->op() == ArithRound)
+        operation = operationArithRound;
+    else if (node->op() == ArithFloor)
+        operation = operationArithFloor;
+    else if (node->op() == ArithCeil)
+        operation = operationArithCeil;
+    else {
+        ASSERT(node->op() == ArithTrunc);
+        operation = operationArithTrunc;
     }
+    callOperation(operation, resultRegs, argumentRegs);
+    m_jit.exceptionCheck();
+    jsValueResult(resultRegs, node);
 }
 
 void SpeculativeJIT::compileArithSin(Node* node)

Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (206133 => 206134)


--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2016-09-19 23:51:00 UTC (rev 206133)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2016-09-20 00:48:39 UTC (rev 206134)
@@ -2260,67 +2260,91 @@
 
     void compileArithRound()
     {
-        LValue result = nullptr;
+        if (m_node->child1().useKind() == DoubleRepUse) {
+            LValue result = nullptr;
+            if (producesInteger(m_node->arithRoundingMode()) && !shouldCheckNegativeZero(m_node->arithRoundingMode())) {
+                LValue value = lowDouble(m_node->child1());
+                result = m_out.doubleFloor(m_out.doubleAdd(value, m_out.constDouble(0.5)));
+            } else {
+                LBasicBlock realPartIsMoreThanHalf = m_out.newBlock();
+                LBasicBlock continuation = m_out.newBlock();
 
-        if (producesInteger(m_node->arithRoundingMode()) && !shouldCheckNegativeZero(m_node->arithRoundingMode())) {
-            LValue value = lowDouble(m_node->child1());
-            result = m_out.doubleFloor(m_out.doubleAdd(value, m_out.constDouble(0.5)));
-        } else {
-            LBasicBlock realPartIsMoreThanHalf = m_out.newBlock();
-            LBasicBlock continuation = m_out.newBlock();
+                LValue value = lowDouble(m_node->child1());
+                LValue integerValue = m_out.doubleCeil(value);
+                ValueFromBlock integerValueResult = m_out.anchor(integerValue);
 
-            LValue value = lowDouble(m_node->child1());
-            LValue integerValue = m_out.doubleCeil(value);
-            ValueFromBlock integerValueResult = m_out.anchor(integerValue);
+                LValue realPart = m_out.doubleSub(integerValue, value);
 
-            LValue realPart = m_out.doubleSub(integerValue, value);
+                m_out.branch(m_out.doubleGreaterThanOrUnordered(realPart, m_out.constDouble(0.5)), unsure(realPartIsMoreThanHalf), unsure(continuation));
 
-            m_out.branch(m_out.doubleGreaterThanOrUnordered(realPart, m_out.constDouble(0.5)), unsure(realPartIsMoreThanHalf), unsure(continuation));
+                LBasicBlock lastNext = m_out.appendTo(realPartIsMoreThanHalf, continuation);
+                LValue integerValueRoundedDown = m_out.doubleSub(integerValue, m_out.constDouble(1));
+                ValueFromBlock integerValueRoundedDownResult = m_out.anchor(integerValueRoundedDown);
+                m_out.jump(continuation);
+                m_out.appendTo(continuation, lastNext);
 
-            LBasicBlock lastNext = m_out.appendTo(realPartIsMoreThanHalf, continuation);
-            LValue integerValueRoundedDown = m_out.doubleSub(integerValue, m_out.constDouble(1));
-            ValueFromBlock integerValueRoundedDownResult = m_out.anchor(integerValueRoundedDown);
-            m_out.jump(continuation);
-            m_out.appendTo(continuation, lastNext);
+                result = m_out.phi(Double, integerValueResult, integerValueRoundedDownResult);
+            }
 
-            result = m_out.phi(Double, integerValueResult, integerValueRoundedDownResult);
+            if (producesInteger(m_node->arithRoundingMode())) {
+                LValue integerValue = convertDoubleToInt32(result, shouldCheckNegativeZero(m_node->arithRoundingMode()));
+                setInt32(integerValue);
+            } else
+                setDouble(result);
+            return;
         }
 
-        if (producesInteger(m_node->arithRoundingMode())) {
-            LValue integerValue = convertDoubleToInt32(result, shouldCheckNegativeZero(m_node->arithRoundingMode()));
-            setInt32(integerValue);
-        } else
-            setDouble(result);
+        DFG_ASSERT(m_graph, m_node, m_node->child1().useKind() == UntypedUse);
+        LValue argument = lowJSValue(m_node->child1());
+        setJSValue(vmCall(Int64, m_out.operation(operationArithRound), m_callFrame, argument));
     }
 
     void compileArithFloor()
     {
-        LValue value = lowDouble(m_node->child1());
-        LValue integerValue = m_out.doubleFloor(value);
-        if (producesInteger(m_node->arithRoundingMode()))
-            setInt32(convertDoubleToInt32(integerValue, shouldCheckNegativeZero(m_node->arithRoundingMode())));
-        else
-            setDouble(integerValue);
+        if (m_node->child1().useKind() == DoubleRepUse) {
+            LValue value = lowDouble(m_node->child1());
+            LValue integerValue = m_out.doubleFloor(value);
+            if (producesInteger(m_node->arithRoundingMode()))
+                setInt32(convertDoubleToInt32(integerValue, shouldCheckNegativeZero(m_node->arithRoundingMode())));
+            else
+                setDouble(integerValue);
+            return;
+        }
+        DFG_ASSERT(m_graph, m_node, m_node->child1().useKind() == UntypedUse);
+        LValue argument = lowJSValue(m_node->child1());
+        setJSValue(vmCall(Int64, m_out.operation(operationArithFloor), m_callFrame, argument));
     }
 
     void compileArithCeil()
     {
-        LValue value = lowDouble(m_node->child1());
-        LValue integerValue = m_out.doubleCeil(value);
-        if (producesInteger(m_node->arithRoundingMode()))
-            setInt32(convertDoubleToInt32(integerValue, shouldCheckNegativeZero(m_node->arithRoundingMode())));
-        else
-            setDouble(integerValue);
+        if (m_node->child1().useKind() == DoubleRepUse) {
+            LValue value = lowDouble(m_node->child1());
+            LValue integerValue = m_out.doubleCeil(value);
+            if (producesInteger(m_node->arithRoundingMode()))
+                setInt32(convertDoubleToInt32(integerValue, shouldCheckNegativeZero(m_node->arithRoundingMode())));
+            else
+                setDouble(integerValue);
+            return;
+        }
+        DFG_ASSERT(m_graph, m_node, m_node->child1().useKind() == UntypedUse);
+        LValue argument = lowJSValue(m_node->child1());
+        setJSValue(vmCall(Int64, m_out.operation(operationArithCeil), m_callFrame, argument));
     }
 
     void compileArithTrunc()
     {
-        LValue value = lowDouble(m_node->child1());
-        LValue result = m_out.doubleTrunc(value);
-        if (producesInteger(m_node->arithRoundingMode()))
-            setInt32(convertDoubleToInt32(result, shouldCheckNegativeZero(m_node->arithRoundingMode())));
-        else
-            setDouble(result);
+        if (m_node->child1().useKind() == DoubleRepUse) {
+            LValue value = lowDouble(m_node->child1());
+            LValue result = m_out.doubleTrunc(value);
+            if (producesInteger(m_node->arithRoundingMode()))
+                setInt32(convertDoubleToInt32(result, shouldCheckNegativeZero(m_node->arithRoundingMode())));
+            else
+                setDouble(result);
+            return;
+        }
+        DFG_ASSERT(m_graph, m_node, m_node->child1().useKind() == UntypedUse);
+        LValue argument = lowJSValue(m_node->child1());
+        setJSValue(vmCall(Int64, m_out.operation(operationArithTrunc), m_callFrame, argument));
     }
 
     void compileArithSqrt()

Modified: trunk/Source/_javascript_Core/jsc.cpp (206133 => 206134)


--- trunk/Source/_javascript_Core/jsc.cpp	2016-09-19 23:51:00 UTC (rev 206133)
+++ trunk/Source/_javascript_Core/jsc.cpp	2016-09-20 00:48:39 UTC (rev 206134)
@@ -52,6 +52,7 @@
 #include "JSTypedArrays.h"
 #include "JSWASMModule.h"
 #include "LLIntData.h"
+#include "ObjectConstructor.h"
 #include "ParserError.h"
 #include "ProfilerDatabase.h"
 #include "SamplingProfiler.h"
@@ -68,6 +69,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <thread>
+#include <type_traits>
 #include <wtf/CurrentTime.h>
 #include <wtf/MainThread.h>
 #include <wtf/StringPrintStream.h>
@@ -612,6 +614,7 @@
 static EncodedJSValue JSC_HOST_CALL functionNoOSRExitFuzzing(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionOptimizeNextInvocation(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionNumberOfDFGCompiles(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionJSCOptions(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionReoptimizationRetryCount(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionTransferArrayBuffer(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionFailNextNewCodeBlock(ExecState*);
@@ -817,6 +820,7 @@
         addFunction(vm, "noFTL", functionNoFTL, 1);
         addFunction(vm, "noOSRExitFuzzing", functionNoOSRExitFuzzing, 1);
         addFunction(vm, "numberOfDFGCompiles", functionNumberOfDFGCompiles, 1);
+        addFunction(vm, "jscOptions", functionJSCOptions, 0);
         addFunction(vm, "optimizeNextInvocation", functionOptimizeNextInvocation, 1);
         addFunction(vm, "reoptimizationRetryCount", functionReoptimizationRetryCount, 1);
         addFunction(vm, "transferArrayBuffer", functionTransferArrayBuffer, 1);
@@ -1715,6 +1719,25 @@
     return JSValue::encode(numberOfDFGCompiles(exec));
 }
 
+template<typename ValueType>
+typename std::enable_if<!std::is_fundamental<ValueType>::value>::type addOption(VM&, JSObject*, Identifier, ValueType) { }
+
+template<typename ValueType>
+typename std::enable_if<std::is_fundamental<ValueType>::value>::type addOption(VM& vm, JSObject* optionsObject, Identifier identifier, ValueType value)
+{
+    optionsObject->putDirect(vm, identifier, JSValue(value));
+}
+
+EncodedJSValue JSC_HOST_CALL functionJSCOptions(ExecState* exec)
+{
+    JSObject* optionsObject = constructEmptyObject(exec);
+#define FOR_EACH_OPTION(type_, name_, defaultValue_, availability_, description_) \
+    addOption(exec->vm(), optionsObject, Identifier::fromString(exec, #name_), Options::name_());
+    JSC_OPTIONS(FOR_EACH_OPTION)
+#undef FOR_EACH_OPTION
+    return JSValue::encode(optionsObject);
+}
+
 EncodedJSValue JSC_HOST_CALL functionReoptimizationRetryCount(ExecState* exec)
 {
     if (exec->argumentCount() < 1)
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to