Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (196725 => 196726)
--- trunk/Source/_javascript_Core/ChangeLog 2016-02-17 23:25:20 UTC (rev 196725)
+++ trunk/Source/_javascript_Core/ChangeLog 2016-02-17 23:35:11 UTC (rev 196726)
@@ -1,3 +1,40 @@
+2016-02-17 Benjamin Poulain <[email protected]>
+
+ [JSC] Remove the overflow check on ArithAbs when possible
+ https://bugs.webkit.org/show_bug.cgi?id=154325
+
+ Reviewed by Filip Pizlo.
+
+ This patch adds support for ArithMode for ArithAbs.
+
+ It is useful for kraken tests where Math.abs() is used
+ on values for which the range is known.
+
+ For example, imaging-gaussian-blur has two Math.abs() with
+ integers that are always in a small range around zero.
+ The IntegerRangeOptimizationPhase detects the range correctly
+ so we can just update the ArithMode depending on the input.
+
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * dfg/DFGIntegerRangeOptimizationPhase.cpp:
+ * dfg/DFGNode.h:
+ (JSC::DFG::Node::convertToArithNegate):
+ (JSC::DFG::Node::hasArithMode):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * ftl/FTLLowerDFGToLLVM.cpp:
+ (JSC::FTL::DFG::LowerDFGToLLVM::compileArithAbs):
+ * tests/stress/arith-abs-integer-range-optimization.js: Added.
+ (negativeRange):
+ (negativeRangeIncludingZero):
+ (negativeRangeWithOverflow):
+ (positiveRange):
+ (positiveRangeIncludingZero):
+ (rangeWithoutOverflow):
+ * tests/stress/arith-abs-with-bitwise-or-zero.js: Added.
+ (opaqueAbs):
+
2016-02-17 Chris Dumez <[email protected]>
SES selftest page crashes on nightly r196694
Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (196725 => 196726)
--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2016-02-17 23:25:20 UTC (rev 196725)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2016-02-17 23:35:11 UTC (rev 196726)
@@ -334,6 +334,10 @@
case ArithAbs: {
if (m_graph.unaryArithShouldSpeculateInt32(node, FixupPass)) {
fixIntOrBooleanEdge(node->child1());
+ if (bytecodeCanTruncateInteger(node->arithNodeFlags()))
+ node->setArithMode(Arith::Unchecked);
+ else
+ node->setArithMode(Arith::CheckOverflow);
break;
}
fixDoubleOrBooleanEdge(node->child1());
Modified: trunk/Source/_javascript_Core/dfg/DFGIntegerRangeOptimizationPhase.cpp (196725 => 196726)
--- trunk/Source/_javascript_Core/dfg/DFGIntegerRangeOptimizationPhase.cpp 2016-02-17 23:25:20 UTC (rev 196725)
+++ trunk/Source/_javascript_Core/dfg/DFGIntegerRangeOptimizationPhase.cpp 2016-02-17 23:35:11 UTC (rev 196726)
@@ -1203,6 +1203,43 @@
// optimize by using the relationships before the operation, but we need to
// call executeNode() before we optimize.
switch (node->op()) {
+ case ArithAbs: {
+ if (node->child1().useKind() != Int32Use)
+ break;
+
+ auto iter = m_relationships.find(node->child1().node());
+ if (iter == m_relationships.end())
+ break;
+
+ int minValue = std::numeric_limits<int>::min();
+ int maxValue = std::numeric_limits<int>::max();
+ for (Relationship relationship : iter->value) {
+ minValue = std::max(minValue, relationship.minValueOfLeft());
+ maxValue = std::min(maxValue, relationship.maxValueOfLeft());
+ }
+
+ executeNode(block->at(nodeIndex));
+
+ if (minValue >= 0) {
+ node->convertToIdentityOn(node->child1().node());
+ changed = true;
+ break;
+ }
+ if (maxValue <= 0) {
+ node->convertToArithNegate();
+ if (minValue > std::numeric_limits<int>::min())
+ node->setArithMode(Arith::Unchecked);
+ changed = true;
+ break;
+ }
+ if (minValue > std::numeric_limits<int>::min()) {
+ node->setArithMode(Arith::Unchecked);
+ changed = true;
+ break;
+ }
+
+ break;
+ }
case ArithAdd: {
if (!node->isBinaryUseKind(Int32Use))
break;
@@ -1309,6 +1346,13 @@
setRelationship(Relationship::safeCreate(node->child1().node(), m_zero, Relationship::GreaterThan, -1));
break;
}
+
+ case ArithAbs: {
+ if (node->child1().useKind() != Int32Use)
+ break;
+ setRelationship(Relationship(node, m_zero, Relationship::GreaterThan, -1));
+ break;
+ }
case ArithAdd: {
// We're only interested in int32 additions and we currently only know how to
Modified: trunk/Source/_javascript_Core/dfg/DFGNode.h (196725 => 196726)
--- trunk/Source/_javascript_Core/dfg/DFGNode.h 2016-02-17 23:25:20 UTC (rev 196725)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.h 2016-02-17 23:35:11 UTC (rev 196726)
@@ -650,6 +650,12 @@
child2() = Edge();
m_op = ArithSqrt;
}
+
+ void convertToArithNegate()
+ {
+ ASSERT(m_op == ArithAbs && child1().useKind() == Int32Use);
+ m_op = ArithNegate;
+ }
JSValue asJSValue()
{
@@ -1678,6 +1684,7 @@
bool hasArithMode()
{
switch (op()) {
+ case ArithAbs:
case ArithAdd:
case ArithSub:
case ArithNegate:
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (196725 => 196726)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2016-02-17 23:25:20 UTC (rev 196725)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2016-02-17 23:35:11 UTC (rev 196726)
@@ -2302,7 +2302,8 @@
m_jit.rshift32(result.gpr(), MacroAssembler::TrustedImm32(31), scratch.gpr());
m_jit.add32(scratch.gpr(), result.gpr());
m_jit.xor32(scratch.gpr(), result.gpr());
- speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branch32(MacroAssembler::Equal, result.gpr(), MacroAssembler::TrustedImm32(1 << 31)));
+ if (shouldCheckOverflow(node->arithMode()))
+ speculationCheck(Overflow, JSValueRegs(), 0, m_jit.branch32(MacroAssembler::Equal, result.gpr(), MacroAssembler::TrustedImm32(1 << 31)));
int32Result(result.gpr(), node);
break;
}
Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToLLVM.cpp (196725 => 196726)
--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToLLVM.cpp 2016-02-17 23:25:20 UTC (rev 196725)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToLLVM.cpp 2016-02-17 23:35:11 UTC (rev 196726)
@@ -2124,12 +2124,13 @@
switch (m_node->child1().useKind()) {
case Int32Use: {
LValue value = lowInt32(m_node->child1());
-
+
LValue mask = m_out.aShr(value, m_out.constInt32(31));
LValue result = m_out.bitXor(mask, m_out.add(mask, value));
-
- speculate(Overflow, noValue(), 0, m_out.equal(result, m_out.constInt32(1 << 31)));
-
+
+ if (shouldCheckOverflow(m_node->arithMode()))
+ speculate(Overflow, noValue(), 0, m_out.equal(result, m_out.constInt32(1 << 31)));
+
setInt32(result);
break;
}
Added: trunk/Source/_javascript_Core/tests/stress/arith-abs-integer-range-optimization.js (0 => 196726)
--- trunk/Source/_javascript_Core/tests/stress/arith-abs-integer-range-optimization.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/arith-abs-integer-range-optimization.js 2016-02-17 23:35:11 UTC (rev 196726)
@@ -0,0 +1,139 @@
+function negativeRange(results)
+{
+ for (var i = -1; i > -10; --i) {
+ results[Math.abs(i)] = i;
+ }
+}
+noInline(negativeRange);
+
+for (var i = 0; i < 1e4; ++i) {
+ var results = [];
+ negativeRange(results);
+ if (results.length != 10)
+ throw "Wrong result length: " + results.length;
+ for (var j = 0; j < 10; ++j) {
+ if (j < 1) {
+ if (results[j] !== undefined)
+ throw "Wrong result, results[j] = " + results[j] + " at j = " + j;
+ continue;
+ }
+ if (results[j] !== -j)
+ throw "Wrong result, results[j] = " + results[j] + " at j = " + j;
+ }
+}
+
+function negativeRangeIncludingZero(results)
+{
+ for (var i = 0; i > -10; --i) {
+ results[Math.abs(i)] = i;
+ }
+}
+noInline(negativeRangeIncludingZero);
+
+for (var i = 0; i < 1e4; ++i) {
+ var results = [];
+ negativeRangeIncludingZero(results);
+ if (results.length != 10)
+ throw "Wrong result length: " + results.length;
+ for (var j = 0; j < 10; ++j) {
+ if (results[j] !== -j)
+ throw "Wrong result, results[j] = " + results[j] + " at j = " + j;
+ }
+}
+
+function negativeRangeWithOverflow(results, limit)
+{
+ var i = -2147483648 + 10;
+ do {
+ results.push(Math.abs(i));
+ --i;
+ } while (i !== limit);
+}
+noInline(negativeRangeWithOverflow);
+
+// First, we warm up without overflow.
+for (var i = 0; i < 1e4; ++i) {
+ var results = [];
+ negativeRangeWithOverflow(results, -2147483647);
+ if (results.length != 9)
+ throw "Wrong result length: " + results.length;
+ for (var j = 0; j < 9; ++j) {
+ if (results[j] !== 2147483638 + j)
+ throw "Wrong result, results[j] = " + results[j] + " at j = " + j;
+ }
+}
+
+// Then we overflow.
+for (var i = 0; i < 1e4; ++i) {
+ var results = [];
+ negativeRangeWithOverflow(results, -2147483648);
+ if (results.length != 10)
+ throw "Wrong result length: " + results.length;
+ for (var j = 0; j < 10; ++j) {
+ if (results[j] !== 2147483638 + j)
+ throw "Wrong result, results[j] = " + results[j] + " at j = " + j;
+ }
+}
+
+function positiveRange(results)
+{
+ for (var i = 1; i < 10; ++i) {
+ results[Math.abs(i)] = i;
+ }
+}
+noInline(positiveRange);
+
+for (var i = 0; i < 1e4; ++i) {
+ var results = [];
+ positiveRange(results);
+ if (results.length != 10)
+ throw "Wrong result length: " + results.length;
+ for (var j = 0; j < 10; ++j) {
+ if (j < 1) {
+ if (results[j] !== undefined)
+ throw "Wrong result, results[j] = " + results[j] + " at j = " + j;
+ continue;
+ }
+ if (results[j] !== j)
+ throw "Wrong result, results[j] = " + results[j] + " at j = " + j;
+ }
+}
+
+function positiveRangeIncludingZero(results)
+{
+ for (var i = 0; i < 10; ++i) {
+ results[Math.abs(i)] = i;
+ }
+}
+noInline(positiveRangeIncludingZero);
+
+for (var i = 0; i < 1e4; ++i) {
+ var results = [];
+ positiveRangeIncludingZero(results);
+ if (results.length != 10)
+ throw "Wrong result length: " + results.length;
+ for (var j = 0; j < 10; ++j) {
+ if (results[j] !== j)
+ throw "Wrong result, results[j] = " + results[j] + " at j = " + j;
+ }
+}
+
+function rangeWithoutOverflow(results)
+{
+ for (var i = -10; i < 10; ++i) {
+ results[i] = Math.abs(i);
+ }
+}
+noInline(rangeWithoutOverflow);
+
+for (var i = 0; i < 1e4; ++i) {
+ var results = [];
+ rangeWithoutOverflow(results);
+ if (results.length != 10)
+ throw "Wrong result length: " + results.length;
+ for (var j = -10; j < 10; ++j) {
+ var expected = (j < 0) ? -j : j;
+ if (results[j] !== expected)
+ throw "Wrong result, results[j] = " + results[j] + " at j = " + j;
+ }
+}
Added: trunk/Source/_javascript_Core/tests/stress/arith-abs-with-bitwise-or-zero.js (0 => 196726)
--- trunk/Source/_javascript_Core/tests/stress/arith-abs-with-bitwise-or-zero.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/arith-abs-with-bitwise-or-zero.js 2016-02-17 23:35:11 UTC (rev 196726)
@@ -0,0 +1,54 @@
+function opaqueAbs(value)
+{
+ return Math.abs(value)|0;
+}
+noInline(opaqueAbs);
+
+for (let i = 0; i < 1e4; ++i) {
+ var positiveResult = opaqueAbs(i);
+ if (positiveResult !== i)
+ throw "Incorrect result at i = " + i + " result = " + positiveResult;
+ var negativeResult = opaqueAbs(-i);
+ if (negativeResult !== i)
+ throw "Incorrect result at -i = " + -i + " result = " + negativeResult;
+}
+
+
+var intMax = 2147483647;
+var intMin = 2147483647;
+
+var intMaxResult = opaqueAbs(intMax);
+if (intMaxResult !== intMax)
+ throw "Incorrect result at intMax result = " + intMaxResult;
+var intMaxResult = opaqueAbs(intMin);
+if (intMaxResult !== intMin)
+ throw "Incorrect result at intMax result = " + intMaxResult;
+
+// Numbers around IntMax/IntMin. Numbers outside the bounds are doubles and opaqueAbs()
+// has to OSR Exit to handle them correctly.
+for (let i = intMax - 1e4; i < intMax + 1e4; ++i) {
+ var positiveResult = opaqueAbs(i);
+ if (positiveResult !== (i|0))
+ throw "Incorrect result at i = " + i + " result = " + positiveResult;
+ var negativeResult = opaqueAbs(-i);
+ if (negativeResult !== (i|0))
+ throw "Incorrect result at -i = " + -i + " result = " + negativeResult;
+}
+
+// Edge cases and exits.
+if (opaqueAbs(NaN) !== 0)
+ throw "opaqueAbs(NaN) failed.";
+if (opaqueAbs(Infinity) !== 0)
+ throw "opaqueAbs(Infinity) failed.";
+if (opaqueAbs(-Infinity) !== 0)
+ throw "opaqueAbs(-Infinity) failed.";
+if (opaqueAbs(null) !== 0)
+ throw "opaqueAbs(null) failed.";
+if (opaqueAbs(undefined) !== 0)
+ throw "opaqueAbs(undefined) failed.";
+if (opaqueAbs(true) !== 1)
+ throw "opaqueAbs(true) failed.";
+if (opaqueAbs(false) !== 0)
+ throw "opaqueAbs(false) failed.";
+if (opaqueAbs({foo:"bar"}) !== 0)
+ throw "opaqueAbs({foo:'bar'}) failed.";