<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[206134] trunk</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/206134">206134</a></dd>
<dt>Author</dt> <dd>commit-queue@webkit.org</dd>
<dt>Date</dt> <dd>2016-09-19 17:48:39 -0700 (Mon, 19 Sep 2016)</dd>
</dl>
<h3>Log Message</h3>
<pre>[JSC] Make the rounding-related nodes support any type
https://bugs.webkit.org/show_bug.cgi?id=161895
Patch by Benjamin Poulain <bpoulain@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/JavaScriptCore:
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):</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkJSTestsChangeLog">trunk/JSTests/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGAbstractInterpreterInlinesh">trunk/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGByteCodeParsercpp">trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGClobberizeh">trunk/Source/JavaScriptCore/dfg/DFGClobberize.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGFixupPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGNodeTypeh">trunk/Source/JavaScriptCore/dfg/DFGNodeType.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGOperationscpp">trunk/Source/JavaScriptCore/dfg/DFGOperations.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGOperationsh">trunk/Source/JavaScriptCore/dfg/DFGOperations.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGPredictionPropagationPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGSpeculativeJITcpp">trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLLowerDFGToB3cpp">trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorejsccpp">trunk/Source/JavaScriptCore/jsc.cpp</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li><a href="#trunkJSTestsstressarithceilonvarioustypesjs">trunk/JSTests/stress/arith-ceil-on-various-types.js</a></li>
<li><a href="#trunkJSTestsstressarithflooronvarioustypesjs">trunk/JSTests/stress/arith-floor-on-various-types.js</a></li>
<li><a href="#trunkJSTestsstressarithroundonvarioustypesjs">trunk/JSTests/stress/arith-round-on-various-types.js</a></li>
<li><a href="#trunkJSTestsstressarithtrunconvarioustypesjs">trunk/JSTests/stress/arith-trunc-on-various-types.js</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkJSTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/JSTests/ChangeLog (206133 => 206134)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/ChangeLog        2016-09-19 23:51:00 UTC (rev 206133)
+++ trunk/JSTests/ChangeLog        2016-09-20 00:48:39 UTC (rev 206134)
</span><span class="lines">@@ -1,3 +1,15 @@
</span><ins>+2016-09-19 Benjamin Poulain <bpoulain@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.
+
</ins><span class="cx"> 2016-09-18 Yusuke Suzuki <utatane.tea@gmail.com>
</span><span class="cx">
</span><span class="cx"> [JSC] Do not need to use defineProperty to define methods for object literals
</span></span></pre></div>
<a id="trunkJSTestsstressarithceilonvarioustypesjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/arith-ceil-on-various-types.js (0 => 206134)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -0,0 +1,306 @@
</span><ins>+"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();
</ins></span></pre></div>
<a id="trunkJSTestsstressarithflooronvarioustypesjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/arith-floor-on-various-types.js (0 => 206134)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -0,0 +1,306 @@
</span><ins>+"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();
</ins></span></pre></div>
<a id="trunkJSTestsstressarithroundonvarioustypesjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/arith-round-on-various-types.js (0 => 206134)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -0,0 +1,306 @@
</span><ins>+"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();
</ins></span></pre></div>
<a id="trunkJSTestsstressarithtrunconvarioustypesjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/arith-trunc-on-various-types.js (0 => 206134)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -0,0 +1,306 @@
</span><ins>+"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();
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (206133 => 206134)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2016-09-19 23:51:00 UTC (rev 206133)
+++ trunk/Source/JavaScriptCore/ChangeLog        2016-09-20 00:48:39 UTC (rev 206134)
</span><span class="lines">@@ -1,3 +1,44 @@
</span><ins>+2016-09-19 Benjamin Poulain <bpoulain@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):
+
</ins><span class="cx"> 2016-09-19 Yusuke Suzuki <utatane.tea@gmail.com>
</span><span class="cx">
</span><span class="cx"> Unreviewed, build fix for Win64
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGAbstractInterpreterInlinesh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h (206133 => 206134)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h        2016-09-19 23:51:00 UTC (rev 206133)
+++ trunk/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h        2016-09-20 00:48:39 UTC (rev 206134)
</span><span class="lines">@@ -905,19 +905,23 @@
</span><span class="cx"> case ArithCeil:
</span><span class="cx"> case ArithTrunc: {
</span><span class="cx"> JSValue operand = forNode(node->child1()).value();
</span><del>- if (operand && operand.isNumber()) {
</del><ins>+ if (Optional<double> number = operand.toNumberFromPrimitive()) {
</ins><span class="cx"> double roundedValue = 0;
</span><span class="cx"> if (node->op() == ArithRound)
</span><del>- roundedValue = jsRound(operand.asNumber());
</del><ins>+ roundedValue = jsRound(*number);
</ins><span class="cx"> else if (node->op() == ArithFloor)
</span><del>- roundedValue = floor(operand.asNumber());
</del><ins>+ roundedValue = floor(*number);
</ins><span class="cx"> else if (node->op() == ArithCeil)
</span><del>- roundedValue = ceil(operand.asNumber());
</del><ins>+ roundedValue = ceil(*number);
</ins><span class="cx"> else {
</span><span class="cx"> ASSERT(node->op() == ArithTrunc);
</span><del>- roundedValue = trunc(operand.asNumber());
</del><ins>+ roundedValue = trunc(*number);
</ins><span class="cx"> }
</span><span class="cx">
</span><ins>+ if (node->child1().useKind() == UntypedUse) {
+ setConstant(node, jsNumber(roundedValue));
+ break;
+ }
</ins><span class="cx"> if (producesInteger(node->arithRoundingMode())) {
</span><span class="cx"> int32_t roundedValueAsInt32 = static_cast<int32_t>(roundedValue);
</span><span class="cx"> if (roundedValueAsInt32 == roundedValue) {
</span><span class="lines">@@ -936,10 +940,15 @@
</span><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx"> }
</span><del>- if (producesInteger(node->arithRoundingMode()))
- forNode(node).setType(SpecInt32Only);
- else
- forNode(node).setType(typeOfDoubleRounding(forNode(node->child1()).m_type));
</del><ins>+ 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);
+ }
</ins><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGByteCodeParsercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp (206133 => 206134)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp        2016-09-19 23:51:00 UTC (rev 206133)
+++ trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp        2016-09-20 00:48:39 UTC (rev 206134)
</span><span class="lines">@@ -2441,25 +2441,22 @@
</span><span class="cx"> set(VirtualRegister(resultOperand), addToGraph(JSConstant, OpInfo(m_constantNaN)));
</span><span class="cx"> return true;
</span><span class="cx"> }
</span><del>- 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;
</del><ins>+ 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;
</ins><span class="cx"> }
</span><del>- return false;
</del><ins>+ Node* roundNode = addToGraph(op, OpInfo(0), OpInfo(prediction), operand);
+ set(VirtualRegister(resultOperand), roundNode);
+ return true;
</ins><span class="cx"> }
</span><span class="cx"> case IMulIntrinsic: {
</span><span class="cx"> if (argumentCountIncludingThis != 3)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGClobberizeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGClobberize.h (206133 => 206134)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGClobberize.h        2016-09-19 23:51:00 UTC (rev 206133)
+++ trunk/Source/JavaScriptCore/dfg/DFGClobberize.h        2016-09-20 00:48:39 UTC (rev 206134)
</span><span class="lines">@@ -359,7 +359,12 @@
</span><span class="cx"> case ArithFloor:
</span><span class="cx"> case ArithCeil:
</span><span class="cx"> case ArithTrunc:
</span><del>- def(PureValue(node, static_cast<uintptr_t>(node->arithRoundingMode())));
</del><ins>+ if (node->child1().useKind() == DoubleRepUse)
+ def(PureValue(node, static_cast<uintptr_t>(node->arithRoundingMode())));
+ else {
+ read(World);
+ write(Heap);
+ }
</ins><span class="cx"> return;
</span><span class="cx">
</span><span class="cx"> case CheckCell:
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGFixupPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp (206133 => 206134)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp        2016-09-19 23:51:00 UTC (rev 206133)
+++ trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp        2016-09-20 00:48:39 UTC (rev 206134)
</span><span class="lines">@@ -376,24 +376,28 @@
</span><span class="cx"> case ArithFloor:
</span><span class="cx"> case ArithCeil:
</span><span class="cx"> case ArithTrunc: {
</span><del>- if (m_graph.unaryArithShouldSpeculateInt32(node, FixupPass)) {
</del><ins>+ if (node->child1()->shouldSpeculateInt32OrBoolean() && m_graph.roundShouldSpeculateInt32(node, FixupPass)) {
</ins><span class="cx"> fixIntOrBooleanEdge(node->child1());
</span><span class="cx"> insertCheck<Int32Use>(m_indexInBlock, node->child1().node());
</span><span class="cx"> node->convertToIdentity();
</span><span class="cx"> break;
</span><span class="cx"> }
</span><del>- fixDoubleOrBooleanEdge(node->child1());
</del><ins>+ if (node->child1()->shouldSpeculateNotCell()) {
+ fixDoubleOrBooleanEdge(node->child1());
</ins><span class="cx">
</span><del>- 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);
- }
</del><ins>+ 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());
</ins><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGNodeTypeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGNodeType.h (206133 => 206134)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGNodeType.h        2016-09-19 23:51:00 UTC (rev 206133)
+++ trunk/Source/JavaScriptCore/dfg/DFGNodeType.h        2016-09-20 00:48:39 UTC (rev 206134)
</span><span class="lines">@@ -155,10 +155,10 @@
</span><span class="cx"> macro(ArithFRound, NodeResultDouble | NodeMustGenerate) \
</span><span class="cx"> macro(ArithPow, NodeResultDouble) \
</span><span class="cx"> macro(ArithRandom, NodeResultDouble | NodeMustGenerate) \
</span><del>- macro(ArithRound, NodeResultNumber) \
- macro(ArithFloor, NodeResultNumber) \
- macro(ArithCeil, NodeResultNumber) \
- macro(ArithTrunc, NodeResultNumber) \
</del><ins>+ macro(ArithRound, NodeResultNumber | NodeMustGenerate) \
+ macro(ArithFloor, NodeResultNumber | NodeMustGenerate) \
+ macro(ArithCeil, NodeResultNumber | NodeMustGenerate) \
+ macro(ArithTrunc, NodeResultNumber | NodeMustGenerate) \
</ins><span class="cx"> macro(ArithSqrt, NodeResultDouble | NodeMustGenerate) \
</span><span class="cx"> macro(ArithSin, NodeResultDouble | NodeMustGenerate) \
</span><span class="cx"> macro(ArithCos, NodeResultDouble | NodeMustGenerate) \
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGOperationscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGOperations.cpp (206133 => 206134)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGOperations.cpp        2016-09-19 23:51:00 UTC (rev 206133)
+++ trunk/Source/JavaScriptCore/dfg/DFGOperations.cpp        2016-09-20 00:48:39 UTC (rev 206134)
</span><span class="lines">@@ -442,6 +442,58 @@
</span><span class="cx"> return tan(a);
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+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));
+}
+
</ins><span class="cx"> static ALWAYS_INLINE EncodedJSValue getByVal(ExecState* exec, JSCell* base, uint32_t index)
</span><span class="cx"> {
</span><span class="cx"> VM& vm = exec->vm();
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGOperationsh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGOperations.h (206133 => 206134)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGOperations.h        2016-09-19 23:51:00 UTC (rev 206133)
+++ trunk/Source/JavaScriptCore/dfg/DFGOperations.h        2016-09-20 00:48:39 UTC (rev 206134)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> /*
</span><del>- * Copyright (C) 2011, 2013-2015 Apple Inc. All rights reserved.
</del><ins>+ * Copyright (C) 2011, 2013-2016 Apple Inc. All rights reserved.
</ins><span class="cx"> *
</span><span class="cx"> * Redistribution and use in source and binary forms, with or without
</span><span class="cx"> * modification, are permitted provided that the following conditions
</span><span class="lines">@@ -61,6 +61,10 @@
</span><span class="cx"> double JIT_OPERATION operationArithLog(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
</span><span class="cx"> double JIT_OPERATION operationArithSin(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
</span><span class="cx"> double JIT_OPERATION operationArithSqrt(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
</span><ins>+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;
</ins><span class="cx"> EncodedJSValue JIT_OPERATION operationGetByVal(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedProperty) WTF_INTERNAL;
</span><span class="cx"> EncodedJSValue JIT_OPERATION operationGetByValCell(ExecState*, JSCell*, EncodedJSValue encodedProperty) WTF_INTERNAL;
</span><span class="cx"> EncodedJSValue JIT_OPERATION operationGetByValArrayInt(ExecState*, JSArray*, int32_t) WTF_INTERNAL;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGPredictionPropagationPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp (206133 => 206134)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp        2016-09-19 23:51:00 UTC (rev 206133)
+++ trunk/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp        2016-09-20 00:48:39 UTC (rev 206134)
</span><span class="lines">@@ -318,17 +318,6 @@
</span><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span><del>- 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;
- }
-
</del><span class="cx"> case ArithAbs: {
</span><span class="cx"> SpeculatedType childPrediction = node->child1()->prediction();
</span><span class="cx"> if (isInt32OrBooleanSpeculation(childPrediction)
</span><span class="lines">@@ -776,6 +765,18 @@
</span><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+ 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;
+ }
+
</ins><span class="cx"> case ArithRandom: {
</span><span class="cx"> setPrediction(SpecDoubleReal);
</span><span class="cx"> break;
</span><span class="lines">@@ -948,10 +949,6 @@
</span><span class="cx"> case ArithMul:
</span><span class="cx"> case ArithDiv:
</span><span class="cx"> case ArithMod:
</span><del>- case ArithRound:
- case ArithFloor:
- case ArithCeil:
- case ArithTrunc:
</del><span class="cx"> case ArithAbs:
</span><span class="cx"> case GetByVal:
</span><span class="cx"> case ToThis:
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGSpeculativeJITcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp (206133 => 206134)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp        2016-09-19 23:51:00 UTC (rev 206133)
+++ trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp        2016-09-20 00:48:39 UTC (rev 206134)
</span><span class="lines">@@ -4915,101 +4915,129 @@
</span><span class="cx">
</span><span class="cx"> void SpeculativeJIT::compileArithRounding(Node* node)
</span><span class="cx"> {
</span><del>- ASSERT(node->child1().useKind() == DoubleRepUse);
</del><ins>+ if (node->child1().useKind() == DoubleRepUse) {
+ SpeculateDoubleOperand value(this, node->child1());
+ FPRReg valueFPR = value.fpr();
</ins><span class="cx">
</span><del>- SpeculateDoubleOperand value(this, node->child1());
- FPRReg valueFPR = value.fpr();
</del><ins>+ 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);
</ins><span class="cx">
</span><del>- 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);
</del><ins>+ int32Result(resultGPR, node);
+ } else
+ doubleResult(resultFPR, node);
+ };
</ins><span class="cx">
</span><del>- int32Result(resultGPR, node);
- } else
- doubleResult(resultFPR, node);
- };
</del><ins>+ 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);
</ins><span class="cx">
</span><del>- 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);
</del><ins>+ FPRTemporary scratch(this);
+ FPRReg scratchFPR = scratch.fpr();
+ static const double halfConstant = 0.5;
+ m_jit.loadDouble(MacroAssembler::TrustedImmPtr(&halfConstant), scratchFPR);
</ins><span class="cx">
</span><del>- FPRTemporary scratch(this);
- FPRReg scratchFPR = scratch.fpr();
- static const double halfConstant = 0.5;
- m_jit.loadDouble(MacroAssembler::TrustedImmPtr(&halfConstant), scratchFPR);
</del><ins>+ 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;
+ }
</ins><span class="cx">
</span><del>- 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);
</del><ins>+ case ArithFloor: {
+ FPRTemporary rounded(this);
+ FPRReg resultFPR = rounded.fpr();
+ m_jit.floorDouble(valueFPR, resultFPR);
+ setResult(resultFPR);
+ return;
</ins><span class="cx"> }
</span><del>- setResult(resultFPR);
- return;
- }
</del><span class="cx">
</span><del>- case ArithFloor: {
- FPRTemporary rounded(this);
- FPRReg resultFPR = rounded.fpr();
- m_jit.floorDouble(valueFPR, resultFPR);
- setResult(resultFPR);
- return;
- }
</del><ins>+ case ArithCeil: {
+ FPRTemporary rounded(this);
+ FPRReg resultFPR = rounded.fpr();
+ m_jit.ceilDouble(valueFPR, resultFPR);
+ setResult(resultFPR);
+ return;
+ }
</ins><span class="cx">
</span><del>- case ArithCeil: {
- FPRTemporary rounded(this);
- FPRReg resultFPR = rounded.fpr();
- m_jit.ceilDouble(valueFPR, resultFPR);
- setResult(resultFPR);
- return;
- }
</del><ins>+ case ArithTrunc: {
+ FPRTemporary rounded(this);
+ FPRReg resultFPR = rounded.fpr();
+ m_jit.roundTowardZeroDouble(valueFPR, resultFPR);
+ setResult(resultFPR);
+ return;
+ }
</ins><span class="cx">
</span><del>- case ArithTrunc: {
- FPRTemporary rounded(this);
- FPRReg resultFPR = rounded.fpr();
- m_jit.roundTowardZeroDouble(valueFPR, resultFPR);
</del><ins>+ 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);
+ }
</ins><span class="cx"> setResult(resultFPR);
</span><del>- return;
</del><span class="cx"> }
</span><ins>+ return;
+ }
</ins><span class="cx">
</span><del>- 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);
</del><ins>+ 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;
</ins><span class="cx"> }
</span><ins>+ callOperation(operation, resultRegs, argumentRegs);
+ m_jit.exceptionCheck();
+ jsValueResult(resultRegs, node);
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> void SpeculativeJIT::compileArithSin(Node* node)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLLowerDFGToB3cpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp (206133 => 206134)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp        2016-09-19 23:51:00 UTC (rev 206133)
+++ trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp        2016-09-20 00:48:39 UTC (rev 206134)
</span><span class="lines">@@ -2260,67 +2260,91 @@
</span><span class="cx">
</span><span class="cx"> void compileArithRound()
</span><span class="cx"> {
</span><del>- LValue result = nullptr;
</del><ins>+ 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();
</ins><span class="cx">
</span><del>- 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();
</del><ins>+ LValue value = lowDouble(m_node->child1());
+ LValue integerValue = m_out.doubleCeil(value);
+ ValueFromBlock integerValueResult = m_out.anchor(integerValue);
</ins><span class="cx">
</span><del>- LValue value = lowDouble(m_node->child1());
- LValue integerValue = m_out.doubleCeil(value);
- ValueFromBlock integerValueResult = m_out.anchor(integerValue);
</del><ins>+ LValue realPart = m_out.doubleSub(integerValue, value);
</ins><span class="cx">
</span><del>- LValue realPart = m_out.doubleSub(integerValue, value);
</del><ins>+ m_out.branch(m_out.doubleGreaterThanOrUnordered(realPart, m_out.constDouble(0.5)), unsure(realPartIsMoreThanHalf), unsure(continuation));
</ins><span class="cx">
</span><del>- m_out.branch(m_out.doubleGreaterThanOrUnordered(realPart, m_out.constDouble(0.5)), unsure(realPartIsMoreThanHalf), unsure(continuation));
</del><ins>+ 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);
</ins><span class="cx">
</span><del>- 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);
</del><ins>+ result = m_out.phi(Double, integerValueResult, integerValueRoundedDownResult);
+ }
</ins><span class="cx">
</span><del>- result = m_out.phi(Double, integerValueResult, integerValueRoundedDownResult);
</del><ins>+ if (producesInteger(m_node->arithRoundingMode())) {
+ LValue integerValue = convertDoubleToInt32(result, shouldCheckNegativeZero(m_node->arithRoundingMode()));
+ setInt32(integerValue);
+ } else
+ setDouble(result);
+ return;
</ins><span class="cx"> }
</span><span class="cx">
</span><del>- if (producesInteger(m_node->arithRoundingMode())) {
- LValue integerValue = convertDoubleToInt32(result, shouldCheckNegativeZero(m_node->arithRoundingMode()));
- setInt32(integerValue);
- } else
- setDouble(result);
</del><ins>+ 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));
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> void compileArithFloor()
</span><span class="cx"> {
</span><del>- 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);
</del><ins>+ 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));
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> void compileArithCeil()
</span><span class="cx"> {
</span><del>- 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);
</del><ins>+ 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));
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> void compileArithTrunc()
</span><span class="cx"> {
</span><del>- 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);
</del><ins>+ 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));
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> void compileArithSqrt()
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorejsccpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/jsc.cpp (206133 => 206134)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/jsc.cpp        2016-09-19 23:51:00 UTC (rev 206133)
+++ trunk/Source/JavaScriptCore/jsc.cpp        2016-09-20 00:48:39 UTC (rev 206134)
</span><span class="lines">@@ -52,6 +52,7 @@
</span><span class="cx"> #include "JSTypedArrays.h"
</span><span class="cx"> #include "JSWASMModule.h"
</span><span class="cx"> #include "LLIntData.h"
</span><ins>+#include "ObjectConstructor.h"
</ins><span class="cx"> #include "ParserError.h"
</span><span class="cx"> #include "ProfilerDatabase.h"
</span><span class="cx"> #include "SamplingProfiler.h"
</span><span class="lines">@@ -68,6 +69,7 @@
</span><span class="cx"> #include <stdlib.h>
</span><span class="cx"> #include <string.h>
</span><span class="cx"> #include <thread>
</span><ins>+#include <type_traits>
</ins><span class="cx"> #include <wtf/CurrentTime.h>
</span><span class="cx"> #include <wtf/MainThread.h>
</span><span class="cx"> #include <wtf/StringPrintStream.h>
</span><span class="lines">@@ -612,6 +614,7 @@
</span><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionNoOSRExitFuzzing(ExecState*);
</span><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionOptimizeNextInvocation(ExecState*);
</span><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionNumberOfDFGCompiles(ExecState*);
</span><ins>+static EncodedJSValue JSC_HOST_CALL functionJSCOptions(ExecState*);
</ins><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionReoptimizationRetryCount(ExecState*);
</span><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionTransferArrayBuffer(ExecState*);
</span><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionFailNextNewCodeBlock(ExecState*);
</span><span class="lines">@@ -817,6 +820,7 @@
</span><span class="cx"> addFunction(vm, "noFTL", functionNoFTL, 1);
</span><span class="cx"> addFunction(vm, "noOSRExitFuzzing", functionNoOSRExitFuzzing, 1);
</span><span class="cx"> addFunction(vm, "numberOfDFGCompiles", functionNumberOfDFGCompiles, 1);
</span><ins>+ addFunction(vm, "jscOptions", functionJSCOptions, 0);
</ins><span class="cx"> addFunction(vm, "optimizeNextInvocation", functionOptimizeNextInvocation, 1);
</span><span class="cx"> addFunction(vm, "reoptimizationRetryCount", functionReoptimizationRetryCount, 1);
</span><span class="cx"> addFunction(vm, "transferArrayBuffer", functionTransferArrayBuffer, 1);
</span><span class="lines">@@ -1715,6 +1719,25 @@
</span><span class="cx"> return JSValue::encode(numberOfDFGCompiles(exec));
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+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);
+}
+
</ins><span class="cx"> EncodedJSValue JSC_HOST_CALL functionReoptimizationRetryCount(ExecState* exec)
</span><span class="cx"> {
</span><span class="cx"> if (exec->argumentCount() < 1)
</span></span></pre>
</div>
</div>
</body>
</html>