[JSC] Optimize more cases of something-compared-to-null/undefined
authorbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 19 Aug 2015 04:09:12 +0000 (04:09 +0000)
committerbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 19 Aug 2015 04:09:12 +0000 (04:09 +0000)
https://bugs.webkit.org/show_bug.cgi?id=148157

Patch by Benjamin Poulain <bpoulain@apple.com> on 2015-08-18
Reviewed by Geoffrey Garen and Filip Pizlo.

Source/JavaScriptCore:

CompareEq is fairly trivial if you assert one of the operands is either
null or undefined. Under those conditions, the only way to have "true"
is to have the other operand be null/undefined or have an object
that masquerades to undefined.

JSC already had a fast path in CompareEqConstant.
With this patch, I generalize this fast path to more cases and try
to eliminate the checks whenever possible.

CompareEq now does the job of CompareEqConstant. If any operand can
be proved to be undefined/other, its edge is set to OtherUse. Whenever
any edge is OtherUse, we generate the fast code we had for CompareEqConstant.

The AbstractInterpreter has additional checks to reduce the node to a constant
whenever possible.

There are two additional changes in this patch:
-The Fixup Phase tries to set edges to OtherUse early. This is done correctly
 in ConstantFoldingPhase but setting it up early helps the phases relying
 on Clobberize.
-The codegen for CompareEqConstant was improved. The reason is the comparison
 for ObjectOrOther could be faster just because the codegen was better.

* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize): Deleted.
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC): Deleted.
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNode.h:
(JSC::DFG::Node::isUndefinedOrNullConstant):
* dfg/DFGNodeType.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate): Deleted.
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute): Deleted.
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compilePeepHoleBranch):
(JSC::DFG::SpeculativeJIT::compare):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::isKnownNotOther):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::nonSpeculativeNonPeepholeCompareNullOrUndefined):
(JSC::DFG::SpeculativeJIT::nonSpeculativePeepholeBranchNullOrUndefined):
(JSC::DFG::SpeculativeJIT::nonSpeculativeNonPeepholeCompareNull): Deleted.
(JSC::DFG::SpeculativeJIT::nonSpeculativePeepholeBranchNull): Deleted.
(JSC::DFG::SpeculativeJIT::nonSpeculativeCompareNull): Deleted.
(JSC::DFG::SpeculativeJIT::compile): Deleted.
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::nonSpeculativeNonPeepholeCompareNullOrUndefined):
(JSC::DFG::SpeculativeJIT::nonSpeculativePeepholeBranchNullOrUndefined):
(JSC::DFG::SpeculativeJIT::nonSpeculativeNonPeepholeCompareNull): Deleted.
(JSC::DFG::SpeculativeJIT::nonSpeculativePeepholeBranchNull): Deleted.
(JSC::DFG::SpeculativeJIT::nonSpeculativeCompareNull): Deleted.
(JSC::DFG::SpeculativeJIT::compile): Deleted.
* dfg/DFGValidate.cpp:
(JSC::DFG::Validate::validate): Deleted.
* dfg/DFGWatchpointCollectionPhase.cpp:
(JSC::DFG::WatchpointCollectionPhase::handle):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::DFG::LowerDFGToLLVM::compileCompareEq):
(JSC::FTL::DFG::LowerDFGToLLVM::compileNode): Deleted.
(JSC::FTL::DFG::LowerDFGToLLVM::compileCompareEqConstant): Deleted.
* tests/stress/compare-eq-on-null-and-undefined-non-peephole.js: Added.
(string_appeared_here.useForMath):
(testUseForMath):
* tests/stress/compare-eq-on-null-and-undefined-optimized-in-constant-folding.js: Added.
(string_appeared_here.unreachableCodeTest):
(inlinedCompareToNull):
(inlinedComparedToUndefined):
(warmupInlineFunctions):
(testInlineFunctions):
* tests/stress/compare-eq-on-null-and-undefined.js: Added.
(string_appeared_here.compareConstants):
(opaqueNull):
(opaqueUndefined):
(compareConstantsAndDynamicValues):
(compareDynamicValues):
(compareDynamicValueToItself):
(arrayTesting):
(opaqueCompare1):
(testNullComparatorUpdate):
(opaqueCompare2):
(testUndefinedComparatorUpdate):
(opaqueCompare3):
(testNullAndUndefinedComparatorUpdate):

LayoutTests:

* js/dom/document-all-watchpoint-covers-eliminated-compare-eq-expected.txt: Added.
* js/dom/document-all-watchpoint-covers-eliminated-compare-eq.html: Added.
* js/dom/script-tests/document-all-watchpoint-covers-eliminated-compare-eq.js: Added.
(compareFunction):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@188624 268f45cc-cd09-0410-ab3c-d52691b4dbfc

26 files changed:
LayoutTests/ChangeLog
LayoutTests/js/dom/document-all-watchpoint-covers-eliminated-compare-eq-expected.txt [new file with mode: 0644]
LayoutTests/js/dom/document-all-watchpoint-covers-eliminated-compare-eq.html [new file with mode: 0644]
LayoutTests/js/dom/script-tests/document-all-watchpoint-covers-eliminated-compare-eq.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/dfg/DFGValidate.cpp
Source/JavaScriptCore/dfg/DFGWatchpointCollectionPhase.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
Source/JavaScriptCore/tests/stress/compare-eq-on-null-and-undefined-non-peephole.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/compare-eq-on-null-and-undefined-optimized-in-constant-folding.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/compare-eq-on-null-and-undefined.js [new file with mode: 0644]

index 26e76b0..76947d9 100644 (file)
@@ -1,3 +1,15 @@
+2015-08-18  Benjamin Poulain  <bpoulain@apple.com>
+
+        [JSC] Optimize more cases of something-compared-to-null/undefined
+        https://bugs.webkit.org/show_bug.cgi?id=148157
+
+        Reviewed by Geoffrey Garen and Filip Pizlo.
+
+        * js/dom/document-all-watchpoint-covers-eliminated-compare-eq-expected.txt: Added.
+        * js/dom/document-all-watchpoint-covers-eliminated-compare-eq.html: Added.
+        * js/dom/script-tests/document-all-watchpoint-covers-eliminated-compare-eq.js: Added.
+        (compareFunction):
+
 2015-08-18  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         Attempt to fix the failing search-padding-cancel-results-buttons.html test by making
diff --git a/LayoutTests/js/dom/document-all-watchpoint-covers-eliminated-compare-eq-expected.txt b/LayoutTests/js/dom/document-all-watchpoint-covers-eliminated-compare-eq-expected.txt
new file mode 100644 (file)
index 0000000..cb95c98
--- /dev/null
@@ -0,0 +1,12 @@
+Test to make sure that document.all works correctly with elminated CompareEq in DFG.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS documentAllCompare.isNull is true
+PASS documentAllCompare.isUndefined is true
+PASS documentAllCompare.length is 13
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/dom/document-all-watchpoint-covers-eliminated-compare-eq.html b/LayoutTests/js/dom/document-all-watchpoint-covers-eliminated-compare-eq.html
new file mode 100644 (file)
index 0000000..7c4371a
--- /dev/null
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="script-tests/document-all-watchpoint-covers-eliminated-compare-eq.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/dom/script-tests/document-all-watchpoint-covers-eliminated-compare-eq.js b/LayoutTests/js/dom/script-tests/document-all-watchpoint-covers-eliminated-compare-eq.js
new file mode 100644 (file)
index 0000000..f5db6f2
--- /dev/null
@@ -0,0 +1,49 @@
+description("Test to make sure that document.all works correctly with elminated CompareEq in DFG.");
+
+function compareFunction(a)
+{
+    var length = a.length;
+
+    var aIsNull = (a == null) || (null == a);
+    var aIsUndefined = (a == undefined) || (undefined == a);
+
+    if (a == null || undefined == a)
+        return { isNull: aIsNull, isUndefined: aIsUndefined, length: length };
+    else
+        return { isNull: aIsNull, isUndefined: aIsUndefined };
+}
+
+// Warmup with sane objects.
+for (let i = 0; i < 1e4; ++i) {
+    let result = compareFunction({ length: 5});
+    if (result.isNull || result.isUndefined)
+        debug("Failed warmup with compareFunction({ length: 5}).");
+
+    let object = new Object;
+    object.length = 1;
+    result = compareFunction(object);
+    if (result.isNull || result.isUndefined)
+        debug("Failed warmup with compareFunction(object).");
+}
+
+let documentAll = document.all;
+var documentAllCompare = compareFunction(documentAll);
+shouldBeTrue("documentAllCompare.isNull");
+shouldBeTrue("documentAllCompare.isUndefined");
+shouldBe("documentAllCompare.length", "13");
+
+for (let i = 0; i < 1e3; ++i) {
+    let result = compareFunction({ length: 5});
+    if (result.isNull || result.isUndefined)
+        debug("Failed tail with compareFunction({ length: 5}).");
+
+    result = compareFunction(documentAll);
+    if (!result.isNull || !result.isUndefined)
+        debug("Failed tail with compareFunction(documentAll).");
+
+    let object = new Object;
+    object.length = 1;
+    result = compareFunction(object);
+    if (result.isNull || result.isUndefined)
+        debug("Failed tail with compareFunction(object).");
+}
\ No newline at end of file
index e777d4a..1e6580a 100644 (file)
@@ -1,3 +1,105 @@
+2015-08-18  Benjamin Poulain  <bpoulain@apple.com>
+
+        [JSC] Optimize more cases of something-compared-to-null/undefined
+        https://bugs.webkit.org/show_bug.cgi?id=148157
+
+        Reviewed by Geoffrey Garen and Filip Pizlo.
+
+        CompareEq is fairly trivial if you assert one of the operands is either
+        null or undefined. Under those conditions, the only way to have "true"
+        is to have the other operand be null/undefined or have an object
+        that masquerades to undefined.
+
+        JSC already had a fast path in CompareEqConstant.
+        With this patch, I generalize this fast path to more cases and try
+        to eliminate the checks whenever possible.
+
+        CompareEq now does the job of CompareEqConstant. If any operand can
+        be proved to be undefined/other, its edge is set to OtherUse. Whenever
+        any edge is OtherUse, we generate the fast code we had for CompareEqConstant.
+
+        The AbstractInterpreter has additional checks to reduce the node to a constant
+        whenever possible.
+
+        There are two additional changes in this patch:
+        -The Fixup Phase tries to set edges to OtherUse early. This is done correctly
+         in ConstantFoldingPhase but setting it up early helps the phases relying
+         on Clobberize.
+        -The codegen for CompareEqConstant was improved. The reason is the comparison
+         for ObjectOrOther could be faster just because the codegen was better.
+
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize): Deleted.
+        * dfg/DFGConstantFoldingPhase.cpp:
+        (JSC::DFG::ConstantFoldingPhase::foldConstants):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC): Deleted.
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::isUndefinedOrNullConstant):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        (JSC::DFG::PredictionPropagationPhase::propagate): Deleted.
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute): Deleted.
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compilePeepHoleBranch):
+        (JSC::DFG::SpeculativeJIT::compare):
+        * dfg/DFGSpeculativeJIT.h:
+        (JSC::DFG::SpeculativeJIT::isKnownNotOther):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::nonSpeculativeNonPeepholeCompareNullOrUndefined):
+        (JSC::DFG::SpeculativeJIT::nonSpeculativePeepholeBranchNullOrUndefined):
+        (JSC::DFG::SpeculativeJIT::nonSpeculativeNonPeepholeCompareNull): Deleted.
+        (JSC::DFG::SpeculativeJIT::nonSpeculativePeepholeBranchNull): Deleted.
+        (JSC::DFG::SpeculativeJIT::nonSpeculativeCompareNull): Deleted.
+        (JSC::DFG::SpeculativeJIT::compile): Deleted.
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::nonSpeculativeNonPeepholeCompareNullOrUndefined):
+        (JSC::DFG::SpeculativeJIT::nonSpeculativePeepholeBranchNullOrUndefined):
+        (JSC::DFG::SpeculativeJIT::nonSpeculativeNonPeepholeCompareNull): Deleted.
+        (JSC::DFG::SpeculativeJIT::nonSpeculativePeepholeBranchNull): Deleted.
+        (JSC::DFG::SpeculativeJIT::nonSpeculativeCompareNull): Deleted.
+        (JSC::DFG::SpeculativeJIT::compile): Deleted.
+        * dfg/DFGValidate.cpp:
+        (JSC::DFG::Validate::validate): Deleted.
+        * dfg/DFGWatchpointCollectionPhase.cpp:
+        (JSC::DFG::WatchpointCollectionPhase::handle):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToLLVM.cpp:
+        (JSC::FTL::DFG::LowerDFGToLLVM::compileCompareEq):
+        (JSC::FTL::DFG::LowerDFGToLLVM::compileNode): Deleted.
+        (JSC::FTL::DFG::LowerDFGToLLVM::compileCompareEqConstant): Deleted.
+        * tests/stress/compare-eq-on-null-and-undefined-non-peephole.js: Added.
+        (string_appeared_here.useForMath):
+        (testUseForMath):
+        * tests/stress/compare-eq-on-null-and-undefined-optimized-in-constant-folding.js: Added.
+        (string_appeared_here.unreachableCodeTest):
+        (inlinedCompareToNull):
+        (inlinedComparedToUndefined):
+        (warmupInlineFunctions):
+        (testInlineFunctions):
+        * tests/stress/compare-eq-on-null-and-undefined.js: Added.
+        (string_appeared_here.compareConstants):
+        (opaqueNull):
+        (opaqueUndefined):
+        (compareConstantsAndDynamicValues):
+        (compareDynamicValues):
+        (compareDynamicValueToItself):
+        (arrayTesting):
+        (opaqueCompare1):
+        (testNullComparatorUpdate):
+        (opaqueCompare2):
+        (testUndefinedComparatorUpdate):
+        (opaqueCompare3):
+        (testNullAndUndefinedComparatorUpdate):
+
 2015-08-18  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         Introduce non-user-observable Promise functions to use Promises internally
index 1bd8f69..5e2a60b 100644 (file)
@@ -1139,8 +1139,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
     case CompareLessEq:
     case CompareGreater:
     case CompareGreaterEq:
-    case CompareEq:
-    case CompareEqConstant: {
+    case CompareEq: {
         JSValue leftConst = forNode(node->child1()).value();
         JSValue rightConst = forNode(node->child2()).value();
         if (leftConst && rightConst) {
@@ -1180,13 +1179,40 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             }
         }
         
-        if (node->op() == CompareEqConstant || node->op() == CompareEq) {
+        if (node->op() == CompareEq) {
             SpeculatedType leftType = forNode(node->child1()).m_type;
             SpeculatedType rightType = forNode(node->child2()).m_type;
             if (!valuesCouldBeEqual(leftType, rightType)) {
                 setConstant(node, jsBoolean(false));
                 break;
             }
+
+            if (leftType == SpecOther)
+                std::swap(leftType, rightType);
+            if (rightType == SpecOther) {
+                // Undefined and Null are always equal when compared to eachother.
+                if (!(leftType & ~SpecOther)) {
+                    setConstant(node, jsBoolean(true));
+                    break;
+                }
+
+                // Any other type compared to Null or Undefined is always false
+                // as long as the MasqueradesAsUndefined watchpoint is valid.
+                //
+                // MasqueradesAsUndefined only matters for SpecObjectOther, other
+                // cases are always "false".
+                if (!(leftType & (SpecObjectOther | SpecOther))) {
+                    setConstant(node, jsBoolean(false));
+                    break;
+                }
+
+                if (!(leftType & SpecOther) && m_graph.masqueradesAsUndefinedWatchpointIsStillValid(node->origin.semantic)) {
+                    JSGlobalObject* globalObject = m_graph.globalObjectFor(node->origin.semantic);
+                    m_graph.watchpoints().addLazily(globalObject->masqueradesAsUndefinedWatchpoint());
+                    setConstant(node, jsBoolean(false));
+                    break;
+                }
+            }
         }
         
         if (node->child1() == node->child2()) {
@@ -1206,7 +1232,6 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 case CompareLessEq:
                 case CompareGreaterEq:
                 case CompareEq:
-                case CompareEqConstant:
                     setConstant(node, jsBoolean(true));
                     break;
                 default:
index 53e1bef..9dcc747 100644 (file)
@@ -3356,7 +3356,8 @@ bool ByteCodeParser::parseBlock(unsigned limit)
 
         case op_eq_null: {
             Node* value = get(VirtualRegister(currentInstruction[2].u.operand));
-            set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(CompareEqConstant, value, addToGraph(JSConstant, OpInfo(m_constantNull))));
+            Node* nullConstant = addToGraph(JSConstant, OpInfo(m_constantNull));
+            set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(CompareEq, value, nullConstant));
             NEXT_OPCODE(op_eq_null);
         }
 
@@ -3376,7 +3377,8 @@ bool ByteCodeParser::parseBlock(unsigned limit)
 
         case op_neq_null: {
             Node* value = get(VirtualRegister(currentInstruction[2].u.operand));
-            set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(LogicalNot, addToGraph(CompareEqConstant, value, addToGraph(JSConstant, OpInfo(m_constantNull)))));
+            Node* nullConstant = addToGraph(JSConstant, OpInfo(m_constantNull));
+            set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(LogicalNot, addToGraph(CompareEq, value, nullConstant)));
             NEXT_OPCODE(op_neq_null);
         }
 
@@ -3523,7 +3525,8 @@ bool ByteCodeParser::parseBlock(unsigned limit)
         case op_jeq_null: {
             unsigned relativeOffset = currentInstruction[2].u.operand;
             Node* value = get(VirtualRegister(currentInstruction[1].u.operand));
-            Node* condition = addToGraph(CompareEqConstant, value, addToGraph(JSConstant, OpInfo(m_constantNull)));
+            Node* nullConstant = addToGraph(JSConstant, OpInfo(m_constantNull));
+            Node* condition = addToGraph(CompareEq, value, nullConstant);
             addToGraph(Branch, OpInfo(branchData(m_currentIndex + relativeOffset, m_currentIndex + OPCODE_LENGTH(op_jeq_null))), condition);
             LAST_OPCODE(op_jeq_null);
         }
@@ -3531,7 +3534,8 @@ bool ByteCodeParser::parseBlock(unsigned limit)
         case op_jneq_null: {
             unsigned relativeOffset = currentInstruction[2].u.operand;
             Node* value = get(VirtualRegister(currentInstruction[1].u.operand));
-            Node* condition = addToGraph(CompareEqConstant, value, addToGraph(JSConstant, OpInfo(m_constantNull)));
+            Node* nullConstant = addToGraph(JSConstant, OpInfo(m_constantNull));
+            Node* condition = addToGraph(CompareEq, value, nullConstant);
             addToGraph(Branch, OpInfo(branchData(m_currentIndex + OPCODE_LENGTH(op_jneq_null), m_currentIndex + relativeOffset)), condition);
             LAST_OPCODE(op_jneq_null);
         }
index 7727a9e..790d655 100644 (file)
@@ -143,7 +143,6 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     case SkipScope:
     case StringCharCodeAt:
     case StringFromCharCode:
-    case CompareEqConstant:
     case CompareStrictEq:
     case IsUndefined:
     case IsBoolean:
index 3d85caa..d351e57 100644 (file)
@@ -97,6 +97,14 @@ private:
                     node->child1().setUseKind(BooleanUse);
                 break;
             }
+
+            case CompareEq: {
+                if (!m_interpreter.needsTypeCheck(node->child1(), SpecOther))
+                    node->child1().setUseKind(OtherUse);
+                if (!m_interpreter.needsTypeCheck(node->child2(), SpecOther))
+                    node->child2().setUseKind(OtherUse);
+                break;
+            }
                 
             case CheckStructure:
             case ArrayifyToStructure: {
index 3547332..e1e409f 100644 (file)
@@ -116,7 +116,6 @@ bool doesGC(Graph& graph, Node* node)
     case CompareGreater:
     case CompareGreaterEq:
     case CompareEq:
-    case CompareEqConstant:
     case CompareStrictEq:
     case Call:
     case Construct:
index 0ab3938..98b009e 100644 (file)
@@ -368,10 +368,6 @@ private:
                 fixEdge<StringUse>(node->child1());
             break;
         }
-            
-        case CompareEqConstant: {
-            break;
-        }
 
         case CompareEq:
         case CompareLess:
@@ -424,6 +420,32 @@ private:
                 node->clearFlags(NodeMustGenerate);
                 break;
             }
+
+            // If either child can be proved to be Null or Undefined, comparing them is greatly simplified.
+            bool oneArgumentIsUsedAsSpecOther = false;
+            if (node->child1()->isUndefinedOrNullConstant()) {
+                fixEdge<OtherUse>(node->child1());
+                oneArgumentIsUsedAsSpecOther = true;
+            } else if (node->child1()->shouldSpeculateOther()) {
+                m_insertionSet.insertNode(m_indexInBlock, SpecNone, Check, node->origin,
+                    Edge(node->child1().node(), OtherUse));
+                fixEdge<OtherUse>(node->child1());
+                oneArgumentIsUsedAsSpecOther = true;
+            }
+            if (node->child2()->isUndefinedOrNullConstant()) {
+                fixEdge<OtherUse>(node->child2());
+                oneArgumentIsUsedAsSpecOther = true;
+            } else if (node->child2()->shouldSpeculateOther()) {
+                m_insertionSet.insertNode(m_indexInBlock, SpecNone, Check, node->origin,
+                    Edge(node->child2().node(), OtherUse));
+                fixEdge<OtherUse>(node->child2());
+                oneArgumentIsUsedAsSpecOther = true;
+            }
+            if (oneArgumentIsUsedAsSpecOther) {
+                node->clearFlags(NodeMustGenerate);
+                break;
+            }
+
             if (node->child1()->shouldSpeculateObject() && node->child2()->shouldSpeculateObjectOrOther()) {
                 fixEdge<ObjectUse>(node->child1());
                 fixEdge<ObjectOrOtherUse>(node->child2());
@@ -436,6 +458,7 @@ private:
                 node->clearFlags(NodeMustGenerate);
                 break;
             }
+
             break;
         }
             
index 306327a..80033b0 100644 (file)
@@ -690,7 +690,12 @@ struct Node {
     {
         return constant()->value().asBoolean();
     }
-     
+
+    bool isUndefinedOrNullConstant()
+    {
+        return isConstant() && constant()->value().isUndefinedOrNull();
+    }
+
     bool isCellConstant()
     {
         return isConstant() && constant()->value() && constant()->value().isCell();
index 74aa984..0775c68 100644 (file)
@@ -231,7 +231,6 @@ namespace JSC { namespace DFG {
     macro(CompareGreater, NodeResultBoolean | NodeMustGenerate) \
     macro(CompareGreaterEq, NodeResultBoolean | NodeMustGenerate) \
     macro(CompareEq, NodeResultBoolean | NodeMustGenerate) \
-    macro(CompareEqConstant, NodeResultBoolean) \
     macro(CompareStrictEq, NodeResultBoolean) \
     \
     /* Calls. */\
index 355735f..00e4e86 100644 (file)
@@ -371,7 +371,6 @@ private:
         case CompareGreater:
         case CompareGreaterEq:
         case CompareEq:
-        case CompareEqConstant:
         case CompareStrictEq:
         case InstanceOf:
         case IsUndefined:
index f7787f4..b1fc5fb 100644 (file)
@@ -198,7 +198,6 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case CompareGreater:
     case CompareGreaterEq:
     case CompareEq:
-    case CompareEqConstant:
     case CompareStrictEq:
     case Call:
     case Construct:
index aca4820..71fa605 100644 (file)
@@ -1369,6 +1369,10 @@ bool SpeculativeJIT::compilePeepHoleBranch(Node* node, MacroAssembler::Relationa
                 compilePeepHoleObjectToObjectOrOtherEquality(node->child1(), node->child2(), branchNode);
             else if (node->isBinaryUseKind(ObjectOrOtherUse, ObjectUse))
                 compilePeepHoleObjectToObjectOrOtherEquality(node->child2(), node->child1(), branchNode);
+            else if (!needsTypeCheck(node->child1(), SpecOther))
+                nonSpeculativePeepholeBranchNullOrUndefined(node->child2(), branchNode);
+            else if (!needsTypeCheck(node->child2(), SpecOther))
+                nonSpeculativePeepholeBranchNullOrUndefined(node->child1(), branchNode);
             else {
                 nonSpeculativePeepholeBranch(node, branchNode, condition, operation);
                 return true;
@@ -3900,8 +3904,18 @@ bool SpeculativeJIT::compare(Node* node, MacroAssembler::RelationalCondition con
             compileObjectToObjectOrOtherEquality(node->child2(), node->child1());
             return false;
         }
+
+        if (!needsTypeCheck(node->child1(), SpecOther)) {
+            nonSpeculativeNonPeepholeCompareNullOrUndefined(node->child2());
+            return false;
+        }
+
+        if (!needsTypeCheck(node->child2(), SpecOther)) {
+            nonSpeculativeNonPeepholeCompareNullOrUndefined(node->child1());
+            return false;
+        }
     }
-    
+
     nonSpeculativeNonPeepholeCompare(node, condition, operation);
     return false;
 }
index b2bbe62..b2008a1 100644 (file)
@@ -556,6 +556,7 @@ public:
     bool isKnownNotInteger(Node* node) { return !(m_state.forNode(node).m_type & SpecInt32); }
     bool isKnownNotNumber(Node* node) { return !(m_state.forNode(node).m_type & SpecFullNumber); }
     bool isKnownNotCell(Node* node) { return !(m_state.forNode(node).m_type & SpecCell); }
+    bool isKnownNotOther(Node* node) { return !(m_state.forNode(node).m_type & SpecOther); }
     
     UniquedStringImpl* identifierUID(unsigned index)
     {
@@ -701,9 +702,8 @@ public:
     
     void compileBaseValueStoreBarrier(Edge& baseEdge, Edge& valueEdge);
 
-    void nonSpeculativeNonPeepholeCompareNull(Edge operand, bool invert = false);
-    void nonSpeculativePeepholeBranchNull(Edge operand, Node* branchNode, bool invert = false);
-    bool nonSpeculativeCompareNull(Node*, Edge operand, bool invert = false);
+    void nonSpeculativeNonPeepholeCompareNullOrUndefined(Edge operand);
+    void nonSpeculativePeepholeBranchNullOrUndefined(Edge operand, Node* branchNode);
     
     void nonSpeculativePeepholeBranch(Node*, Node* branchNode, MacroAssembler::RelationalCondition, S_JITOperation_EJJ helperFunction);
     void nonSpeculativeNonPeepholeCompare(Node*, MacroAssembler::RelationalCondition, S_JITOperation_EJJ helperFunction);
index 7512096..bba76c5 100644 (file)
@@ -234,9 +234,9 @@ void SpeculativeJIT::cachedPutById(CodeOrigin codeOrigin, GPRReg basePayloadGPR,
     addSlowPathGenerator(WTF::move(slowPath));
 }
 
-void SpeculativeJIT::nonSpeculativeNonPeepholeCompareNull(Edge operand, bool invert)
+void SpeculativeJIT::nonSpeculativeNonPeepholeCompareNullOrUndefined(Edge operand)
 {
-    JSValueOperand arg(this, operand);
+    JSValueOperand arg(this, operand, ManualOperandSpeculation);
     GPRReg argTagGPR = arg.tagGPR();
     GPRReg argPayloadGPR = arg.payloadGPR();
 
@@ -249,7 +249,7 @@ void SpeculativeJIT::nonSpeculativeNonPeepholeCompareNull(Edge operand, bool inv
         if (!isKnownCell(operand.node()))
             notCell = m_jit.branchIfNotCell(arg.jsValueRegs());
         
-        m_jit.move(invert ? TrustedImm32(1) : TrustedImm32(0), resultPayloadGPR);
+        m_jit.move(TrustedImm32(0), resultPayloadGPR);
         notMasqueradesAsUndefined = m_jit.jump();
     } else {
         GPRTemporary localGlobalObject(this);
@@ -263,7 +263,7 @@ void SpeculativeJIT::nonSpeculativeNonPeepholeCompareNull(Edge operand, bool inv
             JITCompiler::Address(argPayloadGPR, JSCell::typeInfoFlagsOffset()), 
             JITCompiler::TrustedImm32(MasqueradesAsUndefined));
         
-        m_jit.move(invert ? TrustedImm32(1) : TrustedImm32(0), resultPayloadGPR);
+        m_jit.move(TrustedImm32(0), resultPayloadGPR);
         notMasqueradesAsUndefined = m_jit.jump();
 
         isMasqueradesAsUndefined.link(&m_jit);
@@ -272,7 +272,7 @@ void SpeculativeJIT::nonSpeculativeNonPeepholeCompareNull(Edge operand, bool inv
         m_jit.move(JITCompiler::TrustedImmPtr(m_jit.graph().globalObjectFor(m_currentNode->origin.semantic)), localGlobalObjectGPR);
         m_jit.loadPtr(JITCompiler::Address(argPayloadGPR, JSCell::structureIDOffset()), resultPayloadGPR);
         m_jit.loadPtr(JITCompiler::Address(resultPayloadGPR, Structure::globalObjectOffset()), remoteGlobalObjectGPR);
-        m_jit.compare32(invert ? JITCompiler::NotEqual : JITCompiler::Equal, localGlobalObjectGPR, remoteGlobalObjectGPR, resultPayloadGPR);
+        m_jit.compare32(JITCompiler::Equal, localGlobalObjectGPR, remoteGlobalObjectGPR, resultPayloadGPR);
     }
  
     if (!isKnownCell(operand.node())) {
@@ -282,7 +282,7 @@ void SpeculativeJIT::nonSpeculativeNonPeepholeCompareNull(Edge operand, bool inv
         // null or undefined?
         COMPILE_ASSERT((JSValue::UndefinedTag | 1) == JSValue::NullTag, UndefinedTag_OR_1_EQUALS_NullTag);
         m_jit.or32(TrustedImm32(1), argTagGPR, resultPayloadGPR);
-        m_jit.compare32(invert ? JITCompiler::NotEqual : JITCompiler::Equal, resultPayloadGPR, TrustedImm32(JSValue::NullTag), resultPayloadGPR);
+        m_jit.compare32(JITCompiler::Equal, resultPayloadGPR, TrustedImm32(JSValue::NullTag), resultPayloadGPR);
 
         done.link(&m_jit);
     }
@@ -292,11 +292,12 @@ void SpeculativeJIT::nonSpeculativeNonPeepholeCompareNull(Edge operand, bool inv
     booleanResult(resultPayloadGPR, m_currentNode);
 }
 
-void SpeculativeJIT::nonSpeculativePeepholeBranchNull(Edge operand, Node* branchNode, bool invert)
+void SpeculativeJIT::nonSpeculativePeepholeBranchNullOrUndefined(Edge operand, Node* branchNode)
 {
     BasicBlock* taken = branchNode->branchData()->taken.block;
     BasicBlock* notTaken = branchNode->branchData()->notTaken.block;
-    
+
+    bool invert = false;
     if (taken == nextBlock()) {
         invert = !invert;
         BasicBlock* tmp = taken;
@@ -304,7 +305,7 @@ void SpeculativeJIT::nonSpeculativePeepholeBranchNull(Edge operand, Node* branch
         notTaken = tmp;
     }
 
-    JSValueOperand arg(this, operand);
+    JSValueOperand arg(this, operand, ManualOperandSpeculation);
     GPRReg argTagGPR = arg.tagGPR();
     GPRReg argPayloadGPR = arg.payloadGPR();
     
@@ -351,29 +352,6 @@ void SpeculativeJIT::nonSpeculativePeepholeBranchNull(Edge operand, Node* branch
     jump(notTaken);
 }
 
-bool SpeculativeJIT::nonSpeculativeCompareNull(Node* node, Edge operand, bool invert)
-{
-    unsigned branchIndexInBlock = detectPeepHoleBranch();
-    if (branchIndexInBlock != UINT_MAX) {
-        Node* branchNode = m_block->at(branchIndexInBlock);
-
-        ASSERT(node->adjustedRefCount() == 1);
-        
-        nonSpeculativePeepholeBranchNull(operand, branchNode, invert);
-    
-        use(node->child1());
-        use(node->child2());
-        m_indexInBlock = branchIndexInBlock;
-        m_currentNode = branchNode;
-        
-        return true;
-    }
-    
-    nonSpeculativeNonPeepholeCompareNull(operand, invert);
-    
-    return false;
-}
-
 void SpeculativeJIT::nonSpeculativePeepholeBranch(Node* node, Node* branchNode, MacroAssembler::RelationalCondition cond, S_JITOperation_EJJ helperFunction)
 {
     BasicBlock* taken = branchNode->branchData()->taken.block;
@@ -2278,12 +2256,6 @@ void SpeculativeJIT::compile(Node* node)
         if (compare(node, JITCompiler::GreaterThanOrEqual, JITCompiler::DoubleGreaterThanOrEqual, operationCompareGreaterEq))
             return;
         break;
-        
-    case CompareEqConstant:
-        ASSERT(node->child2()->asJSValue().isNull());
-        if (nonSpeculativeCompareNull(node, node->child1()))
-            return;
-        break;
 
     case CompareEq:
         if (compare(node, JITCompiler::Equal, JITCompiler::DoubleEqual, operationCompareEq))
index aff7314..1398c60 100644 (file)
@@ -192,146 +192,118 @@ void SpeculativeJIT::cachedPutById(CodeOrigin codeOrigin, GPRReg baseGPR, GPRReg
     addSlowPathGenerator(WTF::move(slowPath));
 }
 
-void SpeculativeJIT::nonSpeculativeNonPeepholeCompareNull(Edge operand, bool invert)
+void SpeculativeJIT::nonSpeculativeNonPeepholeCompareNullOrUndefined(Edge operand)
 {
-    JSValueOperand arg(this, operand);
+    ASSERT_WITH_MESSAGE(!masqueradesAsUndefinedWatchpointIsStillValid() || !isKnownCell(operand.node()), "The Compare should have been eliminated, it is known to be always false.");
+
+    JSValueOperand arg(this, operand, ManualOperandSpeculation);
     GPRReg argGPR = arg.gpr();
     
-    GPRTemporary result(this, Reuse, arg);
+    GPRTemporary result(this);
     GPRReg resultGPR = result.gpr();
-    
-    JITCompiler::Jump notCell;
-    
-    JITCompiler::Jump notMasqueradesAsUndefined;
-    if (masqueradesAsUndefinedWatchpointIsStillValid()) {
-        if (!isKnownCell(operand.node()))
-            notCell = m_jit.branchIfNotCell(JSValueRegs(argGPR));
 
-        m_jit.move(invert ? TrustedImm32(1) : TrustedImm32(0), resultGPR);
-        notMasqueradesAsUndefined = m_jit.jump();
+    m_jit.move(TrustedImm32(0), resultGPR);
+
+    JITCompiler::JumpList done;
+    if (masqueradesAsUndefinedWatchpointIsStillValid()) {
+        if (!isKnownNotCell(operand.node()))
+            done.append(m_jit.branchIfCell(JSValueRegs(argGPR)));
     } else {
         GPRTemporary localGlobalObject(this);
         GPRTemporary remoteGlobalObject(this);
         GPRTemporary scratch(this);
 
+        JITCompiler::Jump notCell;
         if (!isKnownCell(operand.node()))
             notCell = m_jit.branchIfNotCell(JSValueRegs(argGPR));
         
-        JITCompiler::Jump isMasqueradesAsUndefined = m_jit.branchTest8(
-            JITCompiler::NonZero, 
+        JITCompiler::Jump isNotMasqueradesAsUndefined = m_jit.branchTest8(
+            JITCompiler::Zero,
             JITCompiler::Address(argGPR, JSCell::typeInfoFlagsOffset()), 
             JITCompiler::TrustedImm32(MasqueradesAsUndefined));
+        done.append(isNotMasqueradesAsUndefined);
 
-        m_jit.move(invert ? TrustedImm32(1) : TrustedImm32(0), resultGPR);
-        notMasqueradesAsUndefined = m_jit.jump();
-
-        isMasqueradesAsUndefined.link(&m_jit);
         GPRReg localGlobalObjectGPR = localGlobalObject.gpr();
         GPRReg remoteGlobalObjectGPR = remoteGlobalObject.gpr();
         m_jit.move(JITCompiler::TrustedImmPtr(m_jit.graph().globalObjectFor(m_currentNode->origin.semantic)), localGlobalObjectGPR);
         m_jit.emitLoadStructure(argGPR, resultGPR, scratch.gpr());
         m_jit.loadPtr(JITCompiler::Address(resultGPR, Structure::globalObjectOffset()), remoteGlobalObjectGPR);
-        m_jit.comparePtr(invert ? JITCompiler::NotEqual : JITCompiler::Equal, localGlobalObjectGPR, remoteGlobalObjectGPR, resultGPR);
+        m_jit.comparePtr(JITCompiler::Equal, localGlobalObjectGPR, remoteGlobalObjectGPR, resultGPR);
+        done.append(m_jit.jump());
+        if (!isKnownCell(operand.node()))
+            notCell.link(&m_jit);
     }
  
-    if (!isKnownCell(operand.node())) {
-        JITCompiler::Jump done = m_jit.jump();
-        
-        notCell.link(&m_jit);
-        
+    if (!isKnownNotOther(operand.node())) {
         m_jit.move(argGPR, resultGPR);
         m_jit.and64(JITCompiler::TrustedImm32(~TagBitUndefined), resultGPR);
-        m_jit.compare64(invert ? JITCompiler::NotEqual : JITCompiler::Equal, resultGPR, JITCompiler::TrustedImm32(ValueNull), resultGPR);
-        
-        done.link(&m_jit);
+        m_jit.compare64(JITCompiler::Equal, resultGPR, JITCompiler::TrustedImm32(ValueNull), resultGPR);
     }
-   
-    notMasqueradesAsUndefined.link(&m_jit);
+
+    done.link(&m_jit);
  
     m_jit.or32(TrustedImm32(ValueFalse), resultGPR);
     jsValueResult(resultGPR, m_currentNode, DataFormatJSBoolean);
 }
 
-void SpeculativeJIT::nonSpeculativePeepholeBranchNull(Edge operand, Node* branchNode, bool invert)
+void SpeculativeJIT::nonSpeculativePeepholeBranchNullOrUndefined(Edge operand, Node* branchNode)
 {
+    ASSERT_WITH_MESSAGE(!masqueradesAsUndefinedWatchpointIsStillValid() || !isKnownCell(operand.node()), "The Compare should have been eliminated, it is known to be always false.");
+
     BasicBlock* taken = branchNode->branchData()->taken.block;
     BasicBlock* notTaken = branchNode->branchData()->notTaken.block;
-    
-    if (taken == nextBlock()) {
-        invert = !invert;
-        BasicBlock* tmp = taken;
-        taken = notTaken;
-        notTaken = tmp;
-    }
 
-    JSValueOperand arg(this, operand);
+    JSValueOperand arg(this, operand, ManualOperandSpeculation);
     GPRReg argGPR = arg.gpr();
     
     GPRTemporary result(this, Reuse, arg);
     GPRReg resultGPR = result.gpr();
-    
-    JITCompiler::Jump notCell;
-    
+
+    // First, handle the case where "operand" is a cell.
     if (masqueradesAsUndefinedWatchpointIsStillValid()) {
-        if (!isKnownCell(operand.node()))
-            notCell = m_jit.branchIfNotCell(JSValueRegs(argGPR));
-        
-        jump(invert ? taken : notTaken, ForceJump);
+        if (!isKnownNotCell(operand.node())) {
+            JITCompiler::Jump isCell = m_jit.branchIfCell(JSValueRegs(argGPR));
+            addBranch(isCell, notTaken);
+        }
     } else {
         GPRTemporary localGlobalObject(this);
         GPRTemporary remoteGlobalObject(this);
         GPRTemporary scratch(this);
 
+        JITCompiler::Jump notCell;
         if (!isKnownCell(operand.node()))
             notCell = m_jit.branchIfNotCell(JSValueRegs(argGPR));
         
         branchTest8(JITCompiler::Zero, 
             JITCompiler::Address(argGPR, JSCell::typeInfoFlagsOffset()), 
-            JITCompiler::TrustedImm32(MasqueradesAsUndefined), 
-            invert ? taken : notTaken);
+            JITCompiler::TrustedImm32(MasqueradesAsUndefined), notTaken);
 
         GPRReg localGlobalObjectGPR = localGlobalObject.gpr();
         GPRReg remoteGlobalObjectGPR = remoteGlobalObject.gpr();
         m_jit.move(TrustedImmPtr(m_jit.graph().globalObjectFor(m_currentNode->origin.semantic)), localGlobalObjectGPR);
         m_jit.emitLoadStructure(argGPR, resultGPR, scratch.gpr());
         m_jit.loadPtr(JITCompiler::Address(resultGPR, Structure::globalObjectOffset()), remoteGlobalObjectGPR);
-        branchPtr(JITCompiler::Equal, localGlobalObjectGPR, remoteGlobalObjectGPR, invert ? notTaken : taken);
+        branchPtr(JITCompiler::Equal, localGlobalObjectGPR, remoteGlobalObjectGPR, taken);
+
+        if (!isKnownCell(operand.node())) {
+            jump(notTaken, ForceJump);
+            notCell.link(&m_jit);
+        }
     }
-    if (!isKnownCell(operand.node())) {
-        jump(notTaken, ForceJump);
-        
-        notCell.link(&m_jit);
-        
+
+    if (isKnownNotOther(operand.node()))
+        jump(notTaken);
+    else {
+        JITCompiler::RelationalCondition condition = JITCompiler::Equal;
+        if (taken == nextBlock()) {
+            condition = JITCompiler::NotEqual;
+            std::swap(taken, notTaken);
+        }
         m_jit.move(argGPR, resultGPR);
         m_jit.and64(JITCompiler::TrustedImm32(~TagBitUndefined), resultGPR);
-        branch64(invert ? JITCompiler::NotEqual : JITCompiler::Equal, resultGPR, JITCompiler::TrustedImm64(ValueNull), taken);
-    }
-    
-    jump(notTaken);
-}
-
-bool SpeculativeJIT::nonSpeculativeCompareNull(Node* node, Edge operand, bool invert)
-{
-    unsigned branchIndexInBlock = detectPeepHoleBranch();
-    if (branchIndexInBlock != UINT_MAX) {
-        Node* branchNode = m_block->at(branchIndexInBlock);
-
-        DFG_ASSERT(m_jit.graph(), node, node->adjustedRefCount() == 1);
-        
-        nonSpeculativePeepholeBranchNull(operand, branchNode, invert);
-    
-        use(node->child1());
-        use(node->child2());
-        m_indexInBlock = branchIndexInBlock;
-        m_currentNode = branchNode;
-        
-        return true;
+        branch64(condition, resultGPR, JITCompiler::TrustedImm64(ValueNull), taken);
+        jump(notTaken);
     }
-    
-    nonSpeculativeNonPeepholeCompareNull(operand, invert);
-    
-    return false;
 }
 
 void SpeculativeJIT::nonSpeculativePeepholeBranch(Node* node, Node* branchNode, MacroAssembler::RelationalCondition cond, S_JITOperation_EJJ helperFunction)
@@ -2411,12 +2383,6 @@ void SpeculativeJIT::compile(Node* node)
         if (compare(node, JITCompiler::GreaterThanOrEqual, JITCompiler::DoubleGreaterThanOrEqual, operationCompareGreaterEq))
             return;
         break;
-        
-    case CompareEqConstant:
-        ASSERT(node->child2()->asJSValue().isNull());
-        if (nonSpeculativeCompareNull(node, node->child1()))
-            return;
-        break;
 
     case CompareEq:
         if (compare(node, JITCompiler::Equal, JITCompiler::DoubleEqual, operationCompareEq))
index 6a134d0..eca843b 100644 (file)
@@ -236,7 +236,6 @@ public:
                 case CompareGreater:
                 case CompareGreaterEq:
                 case CompareEq:
-                case CompareEqConstant:
                 case CompareStrictEq:
                     VALIDATE((node), !!node->child1());
                     VALIDATE((node), !!node->child2());
index f924e4a..40caac8 100644 (file)
@@ -73,7 +73,6 @@ private:
     void handle()
     {
         switch (m_node->op()) {
-        case CompareEqConstant:
         case IsUndefined:
             handleMasqueradesAsUndefined();
             break;
@@ -81,7 +80,8 @@ private:
         case CompareEq:
             if (m_node->isBinaryUseKind(ObjectUse)
                 || (m_node->child1().useKind() == ObjectUse && m_node->child2().useKind() == ObjectOrOtherUse)
-                || (m_node->child1().useKind() == ObjectOrOtherUse && m_node->child2().useKind() == ObjectUse))
+                || (m_node->child1().useKind() == ObjectOrOtherUse && m_node->child2().useKind() == ObjectUse)
+                || (m_node->child1().useKind() == OtherUse || m_node->child2().useKind() == OtherUse))
                 handleMasqueradesAsUndefined();
             break;
             
index 7737421..a124e6a 100644 (file)
@@ -96,7 +96,6 @@ inline CapabilityLevel canCompile(Node* node)
     case ArithFRound:
     case ArithNegate:
     case UInt32ToNumber:
-    case CompareEqConstant:
     case Jump:
     case ForceOSRExit:
     case Phi:
@@ -321,6 +320,8 @@ inline CapabilityLevel canCompile(Node* node)
             break;
         if (node->isBinaryUseKind(ObjectOrOtherUse, ObjectUse))
             break;
+        if (node->child1().useKind() == OtherUse || node->child2().useKind() == OtherUse)
+            break;
         return CannotCompile;
     case CompareStrictEq:
         if (node->isBinaryUseKind(Int32Use))
index 8a7d00d..1faacdb 100644 (file)
@@ -701,9 +701,6 @@ private:
         case CompareEq:
             compileCompareEq();
             break;
-        case CompareEqConstant:
-            compileCompareEqConstant();
-            break;
         case CompareStrictEq:
             compileCompareStrictEq();
             break;
@@ -4146,18 +4143,22 @@ private:
             nonSpeculativeCompare(LLVMIntEQ, operationCompareEq);
             return;
         }
-        
+
+        if (m_node->child1().useKind() == OtherUse) {
+            ASSERT(!m_interpreter.needsTypeCheck(m_node->child1(), SpecOther));
+            setBoolean(equalNullOrUndefined(m_node->child2(), AllCellsAreFalse, EqualNullOrUndefined, ManualOperandSpeculation));
+            return;
+        }
+
+        if (m_node->child2().useKind() == OtherUse) {
+            ASSERT(!m_interpreter.needsTypeCheck(m_node->child2(), SpecOther));
+            setBoolean(equalNullOrUndefined(m_node->child1(), AllCellsAreFalse, EqualNullOrUndefined, ManualOperandSpeculation));
+            return;
+        }
+
         DFG_CRASH(m_graph, m_node, "Bad use kinds");
     }
     
-    void compileCompareEqConstant()
-    {
-        ASSERT(m_node->child2()->asJSValue().isNull());
-        setBoolean(
-            equalNullOrUndefined(
-                m_node->child1(), AllCellsAreFalse, EqualNullOrUndefined));
-    }
-    
     void compileCompareStrictEq()
     {
         if (m_node->isBinaryUseKind(Int32Use)) {
diff --git a/Source/JavaScriptCore/tests/stress/compare-eq-on-null-and-undefined-non-peephole.js b/Source/JavaScriptCore/tests/stress/compare-eq-on-null-and-undefined-non-peephole.js
new file mode 100644 (file)
index 0000000..20ff95f
--- /dev/null
@@ -0,0 +1,45 @@
+"use strict"
+
+function useForMath(undefinedArgument, nullArgument, polymorphicArgument) {
+    var a = (null == undefinedArgument) + (undefinedArgument == null) + (undefined == undefinedArgument) + (undefinedArgument == undefined);
+    var b = (null == nullArgument) + (nullArgument == null) + (undefined == nullArgument) + (nullArgument == undefined);
+    var c = (null == polymorphicArgument) + (polymorphicArgument == null) + (undefined == polymorphicArgument) + (polymorphicArgument == undefined);
+    var d = (5 == null) + (null == true) + (undefined == Math.LN2) + ("const" == undefined);
+    var e = (5 == undefinedArgument) + (nullArgument == true) + (nullArgument == Math.LN2) + ("const" == undefinedArgument);
+
+    return a + b - c + d - e;
+}
+noInline(useForMath);
+
+function testUseForMath() {
+    for (let i = 0; i < 1e4; ++i) {
+        var value = useForMath(undefined, null, 5);
+        if (value != 8)
+            throw "Failed useForMath(undefined, null, 5), value = " + value + " with i = " + i;
+
+        var value = useForMath(undefined, null, null);
+        if (value != 4)
+            throw "Failed useForMath(undefined, null, null), value = " + value + " with i = " + i;
+
+        var value = useForMath(undefined, null, undefined);
+        if (value != 4)
+            throw "Failed useForMath(undefined, null, undefined), value = " + value + " with i = " + i;
+
+        var value = useForMath(undefined, null, { foo: "bar" });
+        if (value != 8)
+            throw "Failed useForMath(undefined, null, { foo: \"bar\" }), value = " + value + " with i = " + i;
+
+        var value = useForMath(undefined, null, true);
+        if (value != 8)
+            throw "Failed useForMath(undefined, null, true), value = " + value + " with i = " + i;
+
+        var value = useForMath(undefined, null, [1, 2, 3]);
+        if (value != 8)
+            throw "Failed useForMath(undefined, null, true), value = " + value + " with i = " + i;
+
+        var value = useForMath(undefined, null, "WebKit!");
+        if (value != 8)
+            throw "Failed useForMath(undefined, null, true), value = " + value + " with i = " + i;
+    }
+}
+testUseForMath();
\ No newline at end of file
diff --git a/Source/JavaScriptCore/tests/stress/compare-eq-on-null-and-undefined-optimized-in-constant-folding.js b/Source/JavaScriptCore/tests/stress/compare-eq-on-null-and-undefined-optimized-in-constant-folding.js
new file mode 100644 (file)
index 0000000..b714e0c
--- /dev/null
@@ -0,0 +1,70 @@
+"use strict"
+
+function unreachableCodeTest() {
+    var a;
+
+    var b = null;
+    if (b) {
+        a = 5;
+    }
+    return a == b;
+}
+noInline(unreachableCodeTest);
+
+for (let i = 0; i < 1e4; ++i) {
+    if (!unreachableCodeTest())
+        throw "Failed unreachableCodeTest() with i = " + i;
+}
+
+
+function inlinedCompareToNull(a) {
+    return a == null;
+}
+
+function inlinedComparedToUndefined(a) {
+    return a == undefined;
+}
+
+// Warmup. Litter the profile with every types.
+function warmupInlineFunctions() {
+    let returnValue = 0;
+    for (let i = 0; i < 1e4; ++i) {
+        returnValue += inlinedCompareToNull("foo");
+        returnValue += inlinedCompareToNull(null);
+        returnValue += inlinedCompareToNull(Math);
+        returnValue += inlinedCompareToNull(5);
+        returnValue += inlinedCompareToNull(5.5);
+
+        returnValue += inlinedComparedToUndefined("foo");
+        returnValue += inlinedComparedToUndefined(null);
+        returnValue += inlinedComparedToUndefined(Math);
+        returnValue += inlinedComparedToUndefined(5);
+        returnValue += inlinedComparedToUndefined(5.5);
+    }
+    return returnValue;
+}
+noInline(warmupInlineFunctions);
+warmupInlineFunctions();
+
+function testInlineFunctions(undefinedArg, nullArg) {
+    if (inlinedCompareToNull("foo"))
+        throw "Failed inlinedCompareToNull(\"foo\")";
+
+    if (!inlinedCompareToNull(null))
+        throw "Failed !inlinedCompareToNull(null)";
+
+    if (!inlinedCompareToNull(undefined))
+        throw "Failed !inlinedCompareToNull(undefined)";
+
+    if (!inlinedCompareToNull(undefinedArg))
+        throw "Failed !inlinedCompareToNull(undefinedArg)";
+
+    if (!inlinedCompareToNull(nullArg))
+        throw "Failed !inlinedCompareToNull(nullArg)";
+
+}
+noInline(testInlineFunctions);
+
+for (let i = 0; i < 1e4; ++i) {
+    testInlineFunctions(undefined, null);
+}
\ No newline at end of file
diff --git a/Source/JavaScriptCore/tests/stress/compare-eq-on-null-and-undefined.js b/Source/JavaScriptCore/tests/stress/compare-eq-on-null-and-undefined.js
new file mode 100644 (file)
index 0000000..e4e92c4
--- /dev/null
@@ -0,0 +1,174 @@
+"use strict"
+
+// Trivial cases: everything is monomorphic and super predictable.
+function compareConstants()
+{
+    return (null == null) && (null == undefined) && (undefined == null);
+}
+noInline(compareConstants);
+
+for (let i = 0; i < 1e4; ++i) {
+    if (!compareConstants())
+        throw "Failed to compareConstants().";
+}
+
+
+function opaqueNull() {
+    return null;
+}
+noInline(opaqueNull);
+
+function opaqueUndefined() {
+    return undefined;
+}
+noInline(opaqueUndefined);
+
+function compareConstantsAndDynamicValues()
+{
+    return ((null == opaqueNull())
+        && (opaqueNull() == null)
+        && (undefined == opaqueNull())
+        && (opaqueNull() == undefined)
+        && (null == opaqueUndefined())
+        && (opaqueUndefined() == null)
+        && (undefined == opaqueUndefined())
+        && (opaqueUndefined() == undefined));
+}
+noInline(compareConstantsAndDynamicValues);
+
+for (let i = 1e4; i--;) {
+    if (!compareConstantsAndDynamicValues())
+        throw "Failed compareConstantsAndDynamicValues()";
+}
+
+
+function compareDynamicValues()
+{
+    return ((opaqueNull() == opaqueNull())
+            && (opaqueUndefined() == opaqueUndefined())
+            && (opaqueNull() == opaqueUndefined())
+            && (opaqueUndefined() == opaqueNull()));
+}
+noInline(compareDynamicValues);
+
+for (let i = 0; i < 1e4; ++i) {
+    if (!compareDynamicValues())
+        throw "Failed compareDynamicValues()";
+}
+
+
+function compareDynamicValueToItself()
+{
+    const value1 = opaqueNull();
+    const value2 = opaqueUndefined();
+    return value1 == value1 && value2 == value2;
+}
+noInline(compareDynamicValueToItself);
+
+for (let i = 0; i < 1e4; ++i) {
+    if (!compareDynamicValueToItself())
+        throw "Failed compareDynamicValueToItself()";
+}
+
+
+// The case that interested us in the first place.
+// Accessing an array with undecided shape always return undefined.
+
+function arrayTesting()
+{
+    let returnValue = true;
+
+    const array1 = new Array(2);
+    for (let i = 0; i < 3; ++i) {
+        returnValue = returnValue && (array1[i] == null);
+        returnValue = returnValue && (null == array1[i]);
+        returnValue = returnValue && (array1[i] == undefined);
+        returnValue = returnValue && (undefined == array1[i]);
+    }
+
+    const array2 = new Array(2);
+    for (let i = 0; i < 2; ++i) {
+        returnValue = returnValue && (array2[i] == opaqueNull());
+        returnValue = returnValue && (opaqueNull() == array2[i]);
+        returnValue = returnValue && (array2[i] == opaqueUndefined());
+        returnValue = returnValue && (opaqueUndefined() == array2[i]);
+    }
+
+    const array3 = new Array(2);
+    for (let i = 0; i < 3; ++i) {
+        returnValue = returnValue && (array3[i] == array3[i]);
+        returnValue = returnValue && (array1[i] == array3[i]);
+        returnValue = returnValue && (array3[i] == array1[i]);
+        returnValue = returnValue && (array2[i] == array3[i]);
+        returnValue = returnValue && (array3[i] == array2[i]);
+
+    }
+
+    return returnValue;
+}
+noInline(arrayTesting);
+
+for (let i = 0; i < 1e4; ++i) {
+    if (!arrayTesting())
+        throw "Failed arrayTesting()";
+}
+
+
+// Let's make it polymorphic after optimization. We should fallback to a generic compare operation.
+
+function opaqueCompare1(a, b) {
+    return a == b;
+}
+noInline(opaqueCompare1);
+
+function testNullComparatorUpdate() {
+    for (let i = 0; i < 1e4; ++i) {
+        if (!opaqueCompare1(null, null))
+            throw "Failed opaqueCompare1(null, null)"
+    }
+
+    // Let's change types
+    for (let i = 0; i < 1e4; ++i) {
+        if (opaqueCompare1("foo", null))
+            throw "Failed opaqueCompare1(\"foo\", null)"
+    }
+}
+testNullComparatorUpdate();
+
+function opaqueCompare2(a, b) {
+    return a == b;
+}
+noInline(opaqueCompare2);
+
+function testUndefinedComparatorUpdate() {
+    for (let i = 0; i < 1e4; ++i) {
+        if (!opaqueCompare2(undefined, undefined))
+            throw "Failed opaqueCompare2(undefined, undefined)"
+    }
+
+    // Let's change types
+    for (let i = 0; i < 1e4; ++i) {
+        if (!opaqueCompare2("bar", "bar"))
+            throw "Failed opaqueCompare2(\"bar\", \"bar\")"
+    }
+}
+testUndefinedComparatorUpdate();
+
+function opaqueCompare3(a, b) {
+    return a == b;
+}
+noInline(opaqueCompare3);
+
+function testNullAndUndefinedComparatorUpdate() {
+    for (let i = 0; i < 1e4; ++i) {
+        if (!opaqueCompare3(undefined, null) || !opaqueCompare2(null, undefined))
+            throw "Failed opaqueCompare2(undefined/null, undefined/null)"
+    }
+
+    // Let's change types
+    for (let i = 0; i < 1e4; ++i) {
+        if (opaqueCompare3(undefined, "bar"))
+            throw "Failed opaqueCompare3(undefined, \"bar\")"
+    }
+}
+testNullAndUndefinedComparatorUpdate();