[JSC] DFG should support relational comparisons of Number and Other
https://bugs.webkit.org/show_bug.cgi?id=156669
Patch by Benjamin Poulain <bpoulain@webkit.org> on 2016-04-16
Reviewed by Darin Adler.
In Sunspider/3d-raytrace, DFG falls back to JSValue in some important
relational compare because profiling sees "undefined" from time to time.
This case is fairly common outside Sunspider too because of out-of-bounds array access.
Unfortunately for us, our fallback for compare is really inefficient.
Fortunately, relational comparison with null/undefined/true/false are trival.
We can just convert both side to Double. That's what this patch adds.
I also extended constant folding for those cases because I noticed
a bunch of "undefined" constant going through DoubleRep at runtime.
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* tests/stress/compare-number-and-other.js: Added.
(opaqueSideEffect):
(let.operator.of.operators.eval.testPolymorphic):
(let.operator.of.operators.let.left.of.typeCases.let.right.of.typeCases.eval.testMonomorphic):
(let.operator.of.operators.let.left.of.typeCases.let.right.of.typeCases.testMonomorphicLeftConstant):
(let.operator.of.operators.let.left.of.typeCases.let.right.of.typeCases.testMonomorphicRightConstant):
(let.operator.of.operators.let.left.of.typeCases.let.right.of.typeCases.i.testPolymorphic):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@199639 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 3b61459..f8c0635 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,34 @@
+2016-04-16 Benjamin Poulain <bpoulain@webkit.org>
+
+ [JSC] DFG should support relational comparisons of Number and Other
+ https://bugs.webkit.org/show_bug.cgi?id=156669
+
+ Reviewed by Darin Adler.
+
+ In Sunspider/3d-raytrace, DFG falls back to JSValue in some important
+ relational compare because profiling sees "undefined" from time to time.
+
+ This case is fairly common outside Sunspider too because of out-of-bounds array access.
+ Unfortunately for us, our fallback for compare is really inefficient.
+
+ Fortunately, relational comparison with null/undefined/true/false are trival.
+ We can just convert both side to Double. That's what this patch adds.
+
+ I also extended constant folding for those cases because I noticed
+ a bunch of "undefined" constant going through DoubleRep at runtime.
+
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * tests/stress/compare-number-and-other.js: Added.
+ (opaqueSideEffect):
+ (let.operator.of.operators.eval.testPolymorphic):
+ (let.operator.of.operators.let.left.of.typeCases.let.right.of.typeCases.eval.testMonomorphic):
+ (let.operator.of.operators.let.left.of.typeCases.let.right.of.typeCases.testMonomorphicLeftConstant):
+ (let.operator.of.operators.let.left.of.typeCases.let.right.of.typeCases.testMonomorphicRightConstant):
+ (let.operator.of.operators.let.left.of.typeCases.let.right.of.typeCases.i.testPolymorphic):
+
2016-04-16 Benjamin Poulain <bpoulain@apple.com>
[JSC] FRound/Negate can produce an impure NaN out of a pure NaN
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
index fb544fc..eedcc24 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
@@ -392,9 +392,23 @@
case DoubleRep: {
JSValue child = forNode(node->child1()).value();
- if (child && child.isNumber()) {
- setConstant(node, jsDoubleNumber(child.asNumber()));
- break;
+ if (child) {
+ if (child.isNumber()) {
+ setConstant(node, jsDoubleNumber(child.asNumber()));
+ break;
+ }
+ if (child.isUndefined()) {
+ setConstant(node, jsDoubleNumber(PNaN));
+ break;
+ }
+ if (child.isNull() || child.isFalse()) {
+ setConstant(node, jsDoubleNumber(0));
+ break;
+ }
+ if (child.isTrue()) {
+ setConstant(node, jsDoubleNumber(1));
+ break;
+ }
}
SpeculatedType type = forNode(node->child1()).m_type;
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index 20dcd0d..d387a2e 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -453,6 +453,18 @@
if (Node::shouldSpeculateNumberOrBoolean(node->child1().node(), node->child2().node())) {
fixDoubleOrBooleanEdge(node->child1());
fixDoubleOrBooleanEdge(node->child2());
+ }
+ if (node->op() != CompareEq
+ && node->child1()->shouldSpeculateNotCell()
+ && node->child2()->shouldSpeculateNotCell()) {
+ if (node->child1()->shouldSpeculateNumberOrBoolean())
+ fixDoubleOrBooleanEdge(node->child1());
+ else
+ fixEdge<DoubleRepUse>(node->child1());
+ if (node->child2()->shouldSpeculateNumberOrBoolean())
+ fixDoubleOrBooleanEdge(node->child2());
+ else
+ fixEdge<DoubleRepUse>(node->child2());
node->clearFlags(NodeMustGenerate);
break;
}
diff --git a/Source/JavaScriptCore/tests/stress/compare-number-and-other.js b/Source/JavaScriptCore/tests/stress/compare-number-and-other.js
new file mode 100644
index 0000000..8546bf3
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/compare-number-and-other.js
@@ -0,0 +1,75 @@
+let typeCases = [
+ "1",
+ "Math.PI",
+ "NaN",
+ "undefined",
+ "null",
+ "true",
+ "false",
+];
+
+let operators = ["<", "<=", ">", ">=", "==", "!=", "===", "!=="];
+
+function opaqueSideEffect()
+{
+}
+noInline(opaqueSideEffect);
+
+let testCaseIndex = 0;
+for (let operator of operators) {
+ eval(`
+ function testPolymorphic(a, b) {
+ if (a ${operator} b) {
+ opaqueSideEffect()
+ return true;
+ }
+ return false;
+ }
+ noInline(testPolymorphic)`);
+
+ for (let left of typeCases) {
+ for (let right of typeCases) {
+ let llintResult = eval(left + operator + right);
+ eval(`
+ function testMonomorphic${testCaseIndex}(a, b) {
+ if (a ${operator} b) {
+ opaqueSideEffect()
+ return true;
+ }
+ return false;
+ }
+ noInline(testMonomorphic${testCaseIndex});
+
+ function testMonomorphicLeftConstant${testCaseIndex}(b) {
+ if (${left} ${operator} b) {
+ opaqueSideEffect()
+ return true;
+ }
+ return false;
+ }
+ noInline(testMonomorphicLeftConstant${testCaseIndex});
+
+ function testMonomorphicRightConstant${testCaseIndex}(a) {
+ if (a ${operator} ${right}) {
+ opaqueSideEffect()
+ return true;
+ }
+ return false;
+ }
+ noInline(testMonomorphicRightConstant${testCaseIndex});
+
+ for (let i = 0; i < 500; ++i) {
+ if (testMonomorphic${testCaseIndex}(${left}, ${right}) != ${llintResult})
+ throw "Failed testMonomorphic${testCaseIndex}(${left}, ${right})";
+ if (testMonomorphicLeftConstant${testCaseIndex}(${right}) != ${llintResult})
+ throw "Failed testMonomorphicLeftConstant${testCaseIndex}(${right})";
+ if (testMonomorphicRightConstant${testCaseIndex}(${left}) != ${llintResult})
+ throw "Failed testMonomorphicLeftConstant${testCaseIndex}(${left})";
+ if (testPolymorphic(${left}, ${right}) !== ${llintResult})
+ throw "Failed polymorphicVersion(${left})";
+ }
+ `);
+ ++testCaseIndex;
+ }
+ }
+}
\ No newline at end of file