DFG should optimize a==b for a being an object and b being either an object or
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 31 Mar 2012 02:21:35 +0000 (02:21 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 31 Mar 2012 02:21:35 +0000 (02:21 +0000)
null/undefined, and vice versa
https://bugs.webkit.org/show_bug.cgi?id=82656

Reviewed by Oliver Hunt.

Implements additional object equality optimizations for the case that one
operand is predicted to be an easily speculated object (like FinalObject or
Array) and the other is either an easily speculated object or Other, i.e.
Null or Undefined.

2-5% speed-up on V8/raytrace, leading to a sub-1% progression on V8.

I also took the opportunity to clean up the control flow for the speculation
decisions in the various Compare opcodes. And to fix a build bug in SamplingTool.
And to remove debug cruft I stupidly committed in my last patch.

* bytecode/SamplingTool.h:
(SamplingRegion):
* dfg/DFGAbstractState.cpp:
(JSC::DFG::AbstractState::execute):
* dfg/DFGOperations.cpp:
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compilePeepHoleBranch):
(JSC::DFG::SpeculativeJIT::compare):
* dfg/DFGSpeculativeJIT.h:
(SpeculativeJIT):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::nonSpeculativePeepholeBranch):
(JSC::DFG::SpeculativeJIT::compileObjectToObjectOrOtherEquality):
(DFG):
(JSC::DFG::SpeculativeJIT::compilePeepHoleObjectToObjectOrOtherEquality):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::nonSpeculativePeepholeBranch):
(JSC::DFG::SpeculativeJIT::compileObjectToObjectOrOtherEquality):
(DFG):
(JSC::DFG::SpeculativeJIT::compilePeepHoleObjectToObjectOrOtherEquality):

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

Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecode/SamplingTool.h
Source/JavaScriptCore/dfg/DFGAbstractState.cpp
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp

index f53647b..51f9ab9 100644 (file)
@@ -1,3 +1,43 @@
+2012-03-29  Filip Pizlo  <fpizlo@apple.com>
+
+        DFG should optimize a==b for a being an object and b being either an object or
+        null/undefined, and vice versa
+        https://bugs.webkit.org/show_bug.cgi?id=82656
+
+        Reviewed by Oliver Hunt.
+        
+        Implements additional object equality optimizations for the case that one
+        operand is predicted to be an easily speculated object (like FinalObject or
+        Array) and the other is either an easily speculated object or Other, i.e.
+        Null or Undefined.
+        
+        2-5% speed-up on V8/raytrace, leading to a sub-1% progression on V8.
+        
+        I also took the opportunity to clean up the control flow for the speculation
+        decisions in the various Compare opcodes. And to fix a build bug in SamplingTool.
+        And to remove debug cruft I stupidly committed in my last patch.
+        
+        * bytecode/SamplingTool.h:
+        (SamplingRegion):
+        * dfg/DFGAbstractState.cpp:
+        (JSC::DFG::AbstractState::execute):
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compilePeepHoleBranch):
+        (JSC::DFG::SpeculativeJIT::compare):
+        * dfg/DFGSpeculativeJIT.h:
+        (SpeculativeJIT):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::nonSpeculativePeepholeBranch):
+        (JSC::DFG::SpeculativeJIT::compileObjectToObjectOrOtherEquality):
+        (DFG):
+        (JSC::DFG::SpeculativeJIT::compilePeepHoleObjectToObjectOrOtherEquality):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::nonSpeculativePeepholeBranch):
+        (JSC::DFG::SpeculativeJIT::compileObjectToObjectOrOtherEquality):
+        (DFG):
+        (JSC::DFG::SpeculativeJIT::compilePeepHoleObjectToObjectOrOtherEquality):
+
 2012-03-30  David Barr  <davidbarr@chromium.org>
 
         Split up top-level .gitignore and .gitattributes
 2012-03-30  David Barr  <davidbarr@chromium.org>
 
         Split up top-level .gitignore and .gitattributes
index fcb1986..52a6e35 100644 (file)
@@ -124,7 +124,7 @@ namespace JSC {
         
         static void sample();
         
         
         static void sample();
         
-        static void dump();
+        JS_EXPORT_PRIVATE static void dump();
         
     private:
         const char* m_name;
         
     private:
         const char* m_name;
index 2f1e023..15ffcc8 100644 (file)
@@ -409,6 +409,8 @@ bool AbstractState::execute(unsigned indexInBlock)
     case CompareGreater:
     case CompareGreaterEq:
     case CompareEq: {
     case CompareGreater:
     case CompareGreaterEq:
     case CompareEq: {
+        forNode(nodeIndex).set(PredictBoolean);
+        
         Node& left = m_graph[node.child1()];
         Node& right = m_graph[node.child2()];
         PredictedType filter;
         Node& left = m_graph[node.child1()];
         Node& right = m_graph[node.child2()];
         PredictedType filter;
@@ -416,17 +418,45 @@ bool AbstractState::execute(unsigned indexInBlock)
             filter = PredictInt32;
         else if (Node::shouldSpeculateNumber(left, right))
             filter = PredictNumber;
             filter = PredictInt32;
         else if (Node::shouldSpeculateNumber(left, right))
             filter = PredictNumber;
-        else if (node.op() == CompareEq && Node::shouldSpeculateFinalObject(left, right))
-            filter = PredictFinalObject;
-        else if (node.op() == CompareEq && Node::shouldSpeculateArray(left, right))
-            filter = PredictArray;
-        else {
+        else if (node.op() == CompareEq) {
+            if ((m_graph.isConstant(node.child1().index())
+                 && m_graph.valueOfJSConstant(node.child1().index()).isNull())
+                || (m_graph.isConstant(node.child2().index())
+                    && m_graph.valueOfJSConstant(node.child2().index()).isNull())) {
+                // We know that this won't clobber the world. But that's all we know.
+                break;
+            }
+            
+            if (Node::shouldSpeculateFinalObject(left, right))
+                filter = PredictFinalObject;
+            else if (Node::shouldSpeculateArray(left, right))
+                filter = PredictArray;
+            else if (left.shouldSpeculateFinalObject() && right.shouldSpeculateFinalObjectOrOther()) {
+                forNode(node.child1()).filter(PredictFinalObject);
+                forNode(node.child2()).filter(PredictFinalObject | PredictOther);
+                break;
+            } else if (right.shouldSpeculateFinalObject() && left.shouldSpeculateFinalObjectOrOther()) {
+                forNode(node.child1()).filter(PredictFinalObject | PredictOther);
+                forNode(node.child2()).filter(PredictFinalObject);
+                break;
+            } else if (left.shouldSpeculateArray() && right.shouldSpeculateArrayOrOther()) {
+                forNode(node.child1()).filter(PredictFinalObject);
+                forNode(node.child2()).filter(PredictFinalObject | PredictOther);
+                break;
+            } else if (right.shouldSpeculateArray() && left.shouldSpeculateArrayOrOther()) {
+                forNode(node.child1()).filter(PredictFinalObject | PredictOther);
+                forNode(node.child2()).filter(PredictFinalObject);
+                break;
+            } else {
+                filter = PredictTop;
+                clobberStructures(indexInBlock);
+            }
+        } else {
             filter = PredictTop;
             clobberStructures(indexInBlock);
         }
         forNode(node.child1()).filter(filter);
         forNode(node.child2()).filter(filter);
             filter = PredictTop;
             clobberStructures(indexInBlock);
         }
         forNode(node.child1()).filter(filter);
         forNode(node.child2()).filter(filter);
-        forNode(nodeIndex).set(PredictBoolean);
         break;
     }
             
         break;
     }
             
index 98de1c7..5766d31 100644 (file)
@@ -364,9 +364,6 @@ EncodedJSValue DFG_OPERATION operationGetById(ExecState* exec, EncodedJSValue ba
     JSGlobalData* globalData = &exec->globalData();
     NativeCallFrameTracer tracer(globalData, exec);
     
     JSGlobalData* globalData = &exec->globalData();
     NativeCallFrameTracer tracer(globalData, exec);
     
-    if (false)
-        dataLog("getById(%s, %s)\n", JSValue::decode(base).description(), propertyName->ascii().data());
-    
     JSValue baseValue = JSValue::decode(base);
     PropertySlot slot(baseValue);
     return JSValue::encode(baseValue.get(exec, *propertyName, slot));
     JSValue baseValue = JSValue::decode(base);
     PropertySlot slot(baseValue);
     return JSValue::encode(baseValue.get(exec, *propertyName, slot));
@@ -746,28 +743,11 @@ size_t DFG_OPERATION operationCompareGreaterEq(ExecState* exec, EncodedJSValue e
     return jsLessEq<false>(exec, JSValue::decode(encodedOp2), JSValue::decode(encodedOp1));
 }
 
     return jsLessEq<false>(exec, JSValue::decode(encodedOp2), JSValue::decode(encodedOp1));
 }
 
-static const char *classNameSpace(JSValue value)
-{
-    return value.isCell() ? " " : "";
-}
-
-static const char* className(JSValue value)
-{
-    if (!value.isCell())
-        return "";
-    return value.asCell()->classInfo()->className;
-}
-
 size_t DFG_OPERATION operationCompareEq(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
 {
     JSGlobalData* globalData = &exec->globalData();
     NativeCallFrameTracer tracer(globalData, exec);
     
 size_t DFG_OPERATION operationCompareEq(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
 {
     JSGlobalData* globalData = &exec->globalData();
     NativeCallFrameTracer tracer(globalData, exec);
     
-    if (false) {
-        dataLog("compareEq(%s%s%s, ", JSValue::decode(encodedOp1).description(), classNameSpace(JSValue::decode(encodedOp1)), className(JSValue::decode(encodedOp1)));
-        dataLog("%s%s%s)\n", JSValue::decode(encodedOp2).description(), classNameSpace(JSValue::decode(encodedOp2)), className(JSValue::decode(encodedOp2)));
-    }
-        
     return JSValue::equalSlowCaseInline(exec, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2));
 }
 
     return JSValue::equalSlowCaseInline(exec, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2));
 }
 
index 12f9023..38ba9b7 100644 (file)
@@ -857,25 +857,52 @@ bool SpeculativeJIT::compilePeepHoleBranch(Node& node, MacroAssembler::Relationa
         // so can be no intervening nodes to also reference the compare. 
         ASSERT(node.adjustedRefCount() == 1);
 
         // so can be no intervening nodes to also reference the compare. 
         ASSERT(node.adjustedRefCount() == 1);
 
-        if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2()))) {
+        if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2())))
             compilePeepHoleIntegerBranch(node, branchNodeIndex, condition);
             compilePeepHoleIntegerBranch(node, branchNodeIndex, condition);
-            use(node.child1());
-            use(node.child2());
-        } else if (Node::shouldSpeculateNumber(at(node.child1()), at(node.child2()))) {
+        else if (Node::shouldSpeculateNumber(at(node.child1()), at(node.child2())))
             compilePeepHoleDoubleBranch(node, branchNodeIndex, doubleCondition);
             compilePeepHoleDoubleBranch(node, branchNodeIndex, doubleCondition);
-            use(node.child1());
-            use(node.child2());
-        } else if (node.op() == CompareEq && Node::shouldSpeculateFinalObject(at(node.child1()), at(node.child2()))) {
-            compilePeepHoleObjectEquality(node, branchNodeIndex, &JSFinalObject::s_info, isFinalObjectPrediction);
-            use(node.child1());
-            use(node.child2());
-        } else if (node.op() == CompareEq && Node::shouldSpeculateArray(at(node.child1()), at(node.child2()))) {
-            compilePeepHoleObjectEquality(node, branchNodeIndex, &JSArray::s_info, isArrayPrediction);
-            use(node.child1());
-            use(node.child2());
-        } else
+        else if (node.op() == CompareEq) {
+            if (Node::shouldSpeculateFinalObject(
+                    at(node.child1()), at(node.child2()))) {
+                compilePeepHoleObjectEquality(
+                    node, branchNodeIndex, &JSFinalObject::s_info,
+                    isFinalObjectPrediction);
+            } else if (Node::shouldSpeculateArray(
+                           at(node.child1()), at(node.child2()))) {
+                compilePeepHoleObjectEquality(
+                    node, branchNodeIndex, &JSArray::s_info,
+                    isArrayPrediction);
+            } else if (at(node.child1()).shouldSpeculateFinalObject()
+                       && at(node.child2()).shouldSpeculateFinalObjectOrOther()) {
+                compilePeepHoleObjectToObjectOrOtherEquality(
+                    node.child1(), node.child2(), branchNodeIndex,
+                    &JSFinalObject::s_info, isFinalObjectPrediction);
+            } else if (at(node.child1()).shouldSpeculateFinalObjectOrOther()
+                       && at(node.child2()).shouldSpeculateFinalObject()) {
+                compilePeepHoleObjectToObjectOrOtherEquality(
+                    node.child2(), node.child1(), branchNodeIndex,
+                    &JSFinalObject::s_info, isFinalObjectPrediction);
+            } else if (at(node.child1()).shouldSpeculateArray()
+                       && at(node.child2()).shouldSpeculateArrayOrOther()) {
+                compilePeepHoleObjectToObjectOrOtherEquality(
+                    node.child1(), node.child2(), branchNodeIndex,
+                    &JSArray::s_info, isArrayPrediction);
+            } else if (at(node.child1()).shouldSpeculateArrayOrOther()
+                       && at(node.child2()).shouldSpeculateArray()) {
+                compilePeepHoleObjectToObjectOrOtherEquality(
+                    node.child2(), node.child1(), branchNodeIndex,
+                    &JSArray::s_info, isArrayPrediction);
+            } else {
+                nonSpeculativePeepholeBranch(node, branchNodeIndex, condition, operation);
+                return true;
+            }
+        } else {
             nonSpeculativePeepholeBranch(node, branchNodeIndex, condition, operation);
             nonSpeculativePeepholeBranch(node, branchNodeIndex, condition, operation);
+            return true;
+        }
 
 
+        use(node.child1());
+        use(node.child2());
         m_indexInBlock = branchIndexInBlock;
         m_compileIndex = branchNodeIndex;
         return true;
         m_indexInBlock = branchIndexInBlock;
         m_compileIndex = branchNodeIndex;
         return true;
@@ -2719,17 +2746,61 @@ bool SpeculativeJIT::compare(Node& node, MacroAssembler::RelationalCondition con
     if (compilePeepHoleBranch(node, condition, doubleCondition, operation))
         return true;
 
     if (compilePeepHoleBranch(node, condition, doubleCondition, operation))
         return true;
 
-    if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2())))
+    if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2()))) {
         compileIntegerCompare(node, condition);
         compileIntegerCompare(node, condition);
-    else if (Node::shouldSpeculateNumber(at(node.child1()), at(node.child2())))
+        return false;
+    }
+    
+    if (Node::shouldSpeculateNumber(at(node.child1()), at(node.child2()))) {
         compileDoubleCompare(node, doubleCondition);
         compileDoubleCompare(node, doubleCondition);
-    else if (node.op() == CompareEq && Node::shouldSpeculateFinalObject(at(node.child1()), at(node.child2())))
-        compileObjectEquality(node, &JSFinalObject::s_info, isFinalObjectPrediction);
-    else if (node.op() == CompareEq && Node::shouldSpeculateArray(at(node.child1()), at(node.child2())))
-        compileObjectEquality(node, &JSArray::s_info, isArrayPrediction);
-    else
-        nonSpeculativeNonPeepholeCompare(node, condition, operation);
+        return false;
+    }
+    
+    if (node.op() == CompareEq) {
+        if (Node::shouldSpeculateFinalObject(at(node.child1()), at(node.child2()))) {
+            compileObjectEquality(node, &JSFinalObject::s_info, isFinalObjectPrediction);
+            return false;
+        }
+        
+        if (Node::shouldSpeculateArray(at(node.child1()), at(node.child2()))) {
+            compileObjectEquality(node, &JSArray::s_info, isArrayPrediction);
+            return false;
+        }
+        
+        if (at(node.child1()).shouldSpeculateFinalObject()
+            && at(node.child2()).shouldSpeculateFinalObjectOrOther()) {
+            compileObjectToObjectOrOtherEquality(
+                node.child1(), node.child2(), &JSFinalObject::s_info,
+                isFinalObjectPrediction);
+            return false;
+        }
+        
+        if (at(node.child1()).shouldSpeculateFinalObjectOrOther()
+            && at(node.child2()).shouldSpeculateFinalObject()) {
+            compileObjectToObjectOrOtherEquality(
+                node.child2(), node.child1(), &JSFinalObject::s_info,
+                isFinalObjectPrediction);
+            return false;
+        }
+        
+        if (at(node.child1()).shouldSpeculateArray()
+            && at(node.child2()).shouldSpeculateArrayOrOther()) {
+            compileObjectToObjectOrOtherEquality(
+                node.child1(), node.child2(), &JSArray::s_info,
+                isArrayPrediction);
+            return false;
+        }
+        
+        if (at(node.child1()).shouldSpeculateArrayOrOther()
+            && at(node.child2()).shouldSpeculateArray()) {
+            compileObjectToObjectOrOtherEquality(
+                node.child2(), node.child1(), &JSArray::s_info,
+                isArrayPrediction);
+            return false;
+        }
+    }
     
     
+    nonSpeculativeNonPeepholeCompare(node, condition, operation);
     return false;
 }
 
     return false;
 }
 
index d13ed8a..92d22bd 100644 (file)
@@ -1724,7 +1724,11 @@ private:
     void compilePeepHoleIntegerBranch(Node&, NodeIndex branchNodeIndex, JITCompiler::RelationalCondition);
     void compilePeepHoleDoubleBranch(Node&, NodeIndex branchNodeIndex, JITCompiler::DoubleCondition);
     void compilePeepHoleObjectEquality(Node&, NodeIndex branchNodeIndex, const ClassInfo*, PredictionChecker);
     void compilePeepHoleIntegerBranch(Node&, NodeIndex branchNodeIndex, JITCompiler::RelationalCondition);
     void compilePeepHoleDoubleBranch(Node&, NodeIndex branchNodeIndex, JITCompiler::DoubleCondition);
     void compilePeepHoleObjectEquality(Node&, NodeIndex branchNodeIndex, const ClassInfo*, PredictionChecker);
+    void compilePeepHoleObjectToObjectOrOtherEquality(
+        Edge leftChild, Edge rightChild, NodeIndex branchNodeIndex, const ClassInfo*, PredictionChecker);
     void compileObjectEquality(Node&, const ClassInfo*, PredictionChecker);
     void compileObjectEquality(Node&, const ClassInfo*, PredictionChecker);
+    void compileObjectToObjectOrOtherEquality(
+        Edge leftChild, Edge rightChild, const ClassInfo*, PredictionChecker);
     void compileValueAdd(Node&);
     void compileObjectOrOtherLogicalNot(Edge value, const ClassInfo*, bool needSpeculationCheck);
     void compileLogicalNot(Node&);
     void compileValueAdd(Node&);
     void compileObjectOrOtherLogicalNot(Edge value, const ClassInfo*, bool needSpeculationCheck);
     void compileLogicalNot(Node&);
index 7e0b092..9143415 100644 (file)
@@ -739,6 +739,9 @@ void SpeculativeJIT::nonSpeculativePeepholeBranch(Node& node, NodeIndex branchNo
     }
 
     jump(notTaken);
     }
 
     jump(notTaken);
+    
+    m_indexInBlock = m_jit.graph().m_blocks[m_block]->size() - 1;
+    m_compileIndex = branchNodeIndex;
 }
 
 void SpeculativeJIT::nonSpeculativeNonPeepholeCompare(Node& node, MacroAssembler::RelationalCondition cond, S_DFGOperation_EJJ helperFunction)
 }
 
 void SpeculativeJIT::nonSpeculativeNonPeepholeCompare(Node& node, MacroAssembler::RelationalCondition cond, S_DFGOperation_EJJ helperFunction)
@@ -1399,6 +1402,147 @@ void SpeculativeJIT::compileObjectEquality(Node& node, const ClassInfo* classInf
     booleanResult(resultPayloadGPR, m_compileIndex);
 }
 
     booleanResult(resultPayloadGPR, m_compileIndex);
 }
 
+void SpeculativeJIT::compileObjectToObjectOrOtherEquality(
+    Edge leftChild, Edge rightChild,
+    const ClassInfo* classInfo, PredictionChecker predictionCheck)
+{
+    SpeculateCellOperand op1(this, leftChild);
+    JSValueOperand op2(this, rightChild);
+    GPRTemporary result(this);
+    
+    GPRReg op1GPR = op1.gpr();
+    GPRReg op2TagGPR = op2.tagGPR();
+    GPRReg op2PayloadGPR = op2.payloadGPR();
+    GPRReg resultGPR = result.gpr();
+    
+    if (!predictionCheck(m_state.forNode(leftChild).m_type)) {
+        speculationCheck(
+            BadType, JSValueSource::unboxedCell(op1GPR), leftChild.index(),
+            m_jit.branchPtr(
+                MacroAssembler::NotEqual,
+                MacroAssembler::Address(op1GPR, JSCell::classInfoOffset()),
+                MacroAssembler::TrustedImmPtr(classInfo)));
+    }
+    
+    // It seems that most of the time when programs do a == b where b may be either null/undefined
+    // or an object, b is usually an object. Balance the branches to make that case fast.
+    MacroAssembler::Jump rightNotCell =
+        m_jit.branch32(MacroAssembler::NotEqual, op2TagGPR, TrustedImm32(JSValue::CellTag));
+    
+    // We know that within this branch, rightChild must be a cell. If the CFA can tell us that the
+    // proof, when filtered on cell, demonstrates that we have an object of the desired type
+    // (predictionCheck() will test for FinalObject or Array, currently), then we can skip the
+    // speculation.
+    if (!predictionCheck(m_state.forNode(rightChild).m_type & PredictCell)) {
+        speculationCheck(
+            BadType, JSValueRegs(op2TagGPR, op2PayloadGPR), rightChild.index(),
+            m_jit.branchPtr(
+                MacroAssembler::NotEqual,
+                MacroAssembler::Address(op2PayloadGPR, JSCell::classInfoOffset()),
+                MacroAssembler::TrustedImmPtr(classInfo)));
+    }
+    
+    // At this point we know that we can perform a straight-forward equality comparison on pointer
+    // values because both left and right are pointers to objects that have no special equality
+    // protocols.
+    MacroAssembler::Jump falseCase = m_jit.branchPtr(MacroAssembler::NotEqual, op1GPR, op2PayloadGPR);
+    MacroAssembler::Jump trueCase = m_jit.jump();
+    
+    rightNotCell.link(&m_jit);
+    
+    // We know that within this branch, rightChild must not be a cell. Check if that is enough to
+    // prove that it is either null or undefined.
+    if (!isOtherPrediction(m_state.forNode(rightChild).m_type & ~PredictCell)) {
+        m_jit.move(op2TagGPR, resultGPR);
+        m_jit.or32(TrustedImm32(1), resultGPR);
+        
+        speculationCheck(
+            BadType, JSValueRegs(op2TagGPR, op2PayloadGPR), rightChild.index(),
+            m_jit.branch32(
+                MacroAssembler::NotEqual, resultGPR,
+                MacroAssembler::TrustedImm32(JSValue::NullTag)));
+    }
+    
+    falseCase.link(&m_jit);
+    m_jit.move(TrustedImm32(0), resultGPR);
+    MacroAssembler::Jump done = m_jit.jump();
+    trueCase.link(&m_jit);
+    m_jit.move(TrustedImm32(1), resultGPR);
+    done.link(&m_jit);
+    
+    booleanResult(resultGPR, m_compileIndex);
+}
+
+void SpeculativeJIT::compilePeepHoleObjectToObjectOrOtherEquality(
+    Edge leftChild, Edge rightChild, NodeIndex branchNodeIndex,
+    const ClassInfo* classInfo, PredictionChecker predictionCheck)
+{
+    Node& branchNode = at(branchNodeIndex);
+    BlockIndex taken = branchNode.takenBlockIndex();
+    BlockIndex notTaken = branchNode.notTakenBlockIndex();
+    
+    SpeculateCellOperand op1(this, leftChild);
+    JSValueOperand op2(this, rightChild);
+    GPRTemporary result(this);
+    
+    GPRReg op1GPR = op1.gpr();
+    GPRReg op2TagGPR = op2.tagGPR();
+    GPRReg op2PayloadGPR = op2.payloadGPR();
+    GPRReg resultGPR = result.gpr();
+    
+    if (!predictionCheck(m_state.forNode(leftChild).m_type)) {
+        speculationCheck(
+            BadType, JSValueSource::unboxedCell(op1GPR), leftChild.index(),
+            m_jit.branchPtr(
+                MacroAssembler::NotEqual,
+                MacroAssembler::Address(op1GPR, JSCell::classInfoOffset()),
+                MacroAssembler::TrustedImmPtr(classInfo)));
+    }
+    
+    // It seems that most of the time when programs do a == b where b may be either null/undefined
+    // or an object, b is usually an object. Balance the branches to make that case fast.
+    MacroAssembler::Jump rightNotCell =
+        m_jit.branch32(MacroAssembler::NotEqual, op2TagGPR, TrustedImm32(JSValue::CellTag));
+    
+    // We know that within this branch, rightChild must be a cell. If the CFA can tell us that the
+    // proof, when filtered on cell, demonstrates that we have an object of the desired type
+    // (predictionCheck() will test for FinalObject or Array, currently), then we can skip the
+    // speculation.
+    if (!predictionCheck(m_state.forNode(rightChild).m_type & PredictCell)) {
+        speculationCheck(
+            BadType, JSValueRegs(op2TagGPR, op2PayloadGPR), rightChild.index(),
+            m_jit.branchPtr(
+                MacroAssembler::NotEqual,
+                MacroAssembler::Address(op2PayloadGPR, JSCell::classInfoOffset()),
+                MacroAssembler::TrustedImmPtr(classInfo)));
+    }
+    
+    // At this point we know that we can perform a straight-forward equality comparison on pointer
+    // values because both left and right are pointers to objects that have no special equality
+    // protocols.
+    branch32(MacroAssembler::Equal, op1GPR, op2PayloadGPR, taken);
+    
+    // We know that within this branch, rightChild must not be a cell. Check if that is enough to
+    // prove that it is either null or undefined.
+    if (isOtherPrediction(m_state.forNode(rightChild).m_type & ~PredictCell))
+        rightNotCell.link(&m_jit);
+    else {
+        jump(notTaken, ForceJump);
+        
+        rightNotCell.link(&m_jit);
+        m_jit.move(op2TagGPR, resultGPR);
+        m_jit.or32(TrustedImm32(1), resultGPR);
+        
+        speculationCheck(
+            BadType, JSValueRegs(op2TagGPR, op2PayloadGPR), rightChild.index(),
+            m_jit.branch32(
+                MacroAssembler::NotEqual, resultGPR,
+                MacroAssembler::TrustedImm32(JSValue::NullTag)));
+    }
+    
+    jump(notTaken);
+}
+
 void SpeculativeJIT::compileIntegerCompare(Node& node, MacroAssembler::RelationalCondition condition)
 {
     SpeculateIntegerOperand op1(this, node.child1());
 void SpeculativeJIT::compileIntegerCompare(Node& node, MacroAssembler::RelationalCondition condition)
 {
     SpeculateIntegerOperand op1(this, node.child1());
index 9c65551..d743167 100644 (file)
@@ -713,6 +713,9 @@ void SpeculativeJIT::nonSpeculativePeepholeBranch(Node& node, NodeIndex branchNo
     }
 
     jump(notTaken);
     }
 
     jump(notTaken);
+
+    m_indexInBlock = m_jit.graph().m_blocks[m_block]->size() - 1;
+    m_compileIndex = branchNodeIndex;
 }
 
 void SpeculativeJIT::nonSpeculativeNonPeepholeCompare(Node& node, MacroAssembler::RelationalCondition cond, S_DFGOperation_EJJ helperFunction)
 }
 
 void SpeculativeJIT::nonSpeculativeNonPeepholeCompare(Node& node, MacroAssembler::RelationalCondition cond, S_DFGOperation_EJJ helperFunction)
@@ -1485,6 +1488,145 @@ void SpeculativeJIT::compileObjectEquality(Node& node, const ClassInfo* classInf
     jsValueResult(resultGPR, m_compileIndex, DataFormatJSBoolean);
 }
 
     jsValueResult(resultGPR, m_compileIndex, DataFormatJSBoolean);
 }
 
+void SpeculativeJIT::compileObjectToObjectOrOtherEquality(
+    Edge leftChild, Edge rightChild,
+    const ClassInfo* classInfo, PredictionChecker predictionCheck)
+{
+    SpeculateCellOperand op1(this, leftChild);
+    JSValueOperand op2(this, rightChild);
+    GPRTemporary result(this);
+    
+    GPRReg op1GPR = op1.gpr();
+    GPRReg op2GPR = op2.gpr();
+    GPRReg resultGPR = result.gpr();
+    
+    if (!predictionCheck(m_state.forNode(leftChild).m_type)) {
+        speculationCheck(
+            BadType, JSValueRegs(op1GPR), leftChild.index(),
+            m_jit.branchPtr(
+                MacroAssembler::NotEqual,
+                MacroAssembler::Address(op1GPR, JSCell::classInfoOffset()),
+                MacroAssembler::TrustedImmPtr(classInfo)));
+    }
+    
+    // It seems that most of the time when programs do a == b where b may be either null/undefined
+    // or an object, b is usually an object. Balance the branches to make that case fast.
+    MacroAssembler::Jump rightNotCell =
+        m_jit.branchTestPtr(MacroAssembler::NonZero, op2GPR, GPRInfo::tagMaskRegister);
+    
+    // We know that within this branch, rightChild must be a cell. If the CFA can tell us that the
+    // proof, when filtered on cell, demonstrates that we have an object of the desired type
+    // (predictionCheck() will test for FinalObject or Array, currently), then we can skip the
+    // speculation.
+    if (!predictionCheck(m_state.forNode(rightChild).m_type & PredictCell)) {
+        speculationCheck(
+            BadType, JSValueRegs(op2GPR), rightChild.index(),
+            m_jit.branchPtr(
+                MacroAssembler::NotEqual,
+                MacroAssembler::Address(op2GPR, JSCell::classInfoOffset()),
+                MacroAssembler::TrustedImmPtr(classInfo)));
+    }
+    
+    // At this point we know that we can perform a straight-forward equality comparison on pointer
+    // values because both left and right are pointers to objects that have no special equality
+    // protocols.
+    MacroAssembler::Jump falseCase = m_jit.branchPtr(MacroAssembler::NotEqual, op1GPR, op2GPR);
+    MacroAssembler::Jump trueCase = m_jit.jump();
+    
+    rightNotCell.link(&m_jit);
+    
+    // We know that within this branch, rightChild must not be a cell. Check if that is enough to
+    // prove that it is either null or undefined.
+    if (!isOtherPrediction(m_state.forNode(rightChild).m_type & ~PredictCell)) {
+        m_jit.move(op2GPR, resultGPR);
+        m_jit.andPtr(MacroAssembler::TrustedImm32(~TagBitUndefined), resultGPR);
+        
+        speculationCheck(
+            BadType, JSValueRegs(op2GPR), rightChild.index(),
+            m_jit.branchPtr(
+                MacroAssembler::NotEqual, resultGPR,
+                MacroAssembler::TrustedImmPtr(reinterpret_cast<void*>(ValueNull))));
+    }
+    
+    falseCase.link(&m_jit);
+    m_jit.move(TrustedImm32(ValueFalse), resultGPR);
+    MacroAssembler::Jump done = m_jit.jump();
+    trueCase.link(&m_jit);
+    m_jit.move(TrustedImm32(ValueTrue), resultGPR);
+    done.link(&m_jit);
+    
+    jsValueResult(resultGPR, m_compileIndex, DataFormatJSBoolean);
+}
+
+void SpeculativeJIT::compilePeepHoleObjectToObjectOrOtherEquality(
+    Edge leftChild, Edge rightChild, NodeIndex branchNodeIndex,
+    const ClassInfo* classInfo, PredictionChecker predictionCheck)
+{
+    Node& branchNode = at(branchNodeIndex);
+    BlockIndex taken = branchNode.takenBlockIndex();
+    BlockIndex notTaken = branchNode.notTakenBlockIndex();
+    
+    SpeculateCellOperand op1(this, leftChild);
+    JSValueOperand op2(this, rightChild);
+    GPRTemporary result(this);
+    
+    GPRReg op1GPR = op1.gpr();
+    GPRReg op2GPR = op2.gpr();
+    GPRReg resultGPR = result.gpr();
+    
+    if (!predictionCheck(m_state.forNode(leftChild).m_type)) {
+        speculationCheck(
+            BadType, JSValueRegs(op1GPR), leftChild.index(),
+            m_jit.branchPtr(
+                MacroAssembler::NotEqual,
+                MacroAssembler::Address(op1GPR, JSCell::classInfoOffset()),
+                MacroAssembler::TrustedImmPtr(classInfo)));
+    }
+    
+    // It seems that most of the time when programs do a == b where b may be either null/undefined
+    // or an object, b is usually an object. Balance the branches to make that case fast.
+    MacroAssembler::Jump rightNotCell =
+        m_jit.branchTestPtr(MacroAssembler::NonZero, op2GPR, GPRInfo::tagMaskRegister);
+    
+    // We know that within this branch, rightChild must be a cell. If the CFA can tell us that the
+    // proof, when filtered on cell, demonstrates that we have an object of the desired type
+    // (predictionCheck() will test for FinalObject or Array, currently), then we can skip the
+    // speculation.
+    if (!predictionCheck(m_state.forNode(rightChild).m_type & PredictCell)) {
+        speculationCheck(
+            BadType, JSValueRegs(op2GPR), rightChild.index(),
+            m_jit.branchPtr(
+                MacroAssembler::NotEqual,
+                MacroAssembler::Address(op2GPR, JSCell::classInfoOffset()),
+                MacroAssembler::TrustedImmPtr(classInfo)));
+    }
+    
+    // At this point we know that we can perform a straight-forward equality comparison on pointer
+    // values because both left and right are pointers to objects that have no special equality
+    // protocols.
+    branchPtr(MacroAssembler::Equal, op1GPR, op2GPR, taken);
+    
+    // We know that within this branch, rightChild must not be a cell. Check if that is enough to
+    // prove that it is either null or undefined.
+    if (isOtherPrediction(m_state.forNode(rightChild).m_type & ~PredictCell))
+        rightNotCell.link(&m_jit);
+    else {
+        jump(notTaken, ForceJump);
+        
+        rightNotCell.link(&m_jit);
+        m_jit.move(op2GPR, resultGPR);
+        m_jit.andPtr(MacroAssembler::TrustedImm32(~TagBitUndefined), resultGPR);
+        
+        speculationCheck(
+            BadType, JSValueRegs(op2GPR), rightChild.index(),
+            m_jit.branchPtr(
+                MacroAssembler::NotEqual, resultGPR,
+                MacroAssembler::TrustedImmPtr(reinterpret_cast<void*>(ValueNull))));
+    }
+    
+    jump(notTaken);
+}
+
 void SpeculativeJIT::compileIntegerCompare(Node& node, MacroAssembler::RelationalCondition condition)
 {
     SpeculateIntegerOperand op1(this, node.child1());
 void SpeculativeJIT::compileIntegerCompare(Node& node, MacroAssembler::RelationalCondition condition)
 {
     SpeculateIntegerOperand op1(this, node.child1());