Strange bug in DFG OSR in JSC
https://bugs.webkit.org/show_bug.cgi?id=109491

Source/JavaScriptCore: 

Reviewed by Mark Hahnenberg.
        
Int32ToDouble was being injected after a side-effecting operation and before a SetLocal. Anytime we
inject something just before a SetLocal we should be aware that the previous operation may have been
a side-effect associated with the current code origin. Hence, we should use a forward exit.
Int32ToDouble does not do forward exits by default.
        
This patch adds a forward-exiting form of Int32ToDouble, for use in SetLocal Int32ToDouble injections.
Changed the CSE and other things to treat these nodes identically, but for the exit strategy to be
distinct (Int32ToDouble -> backward, ForwardInt32ToDouble -> forward). The use of the NodeType for
signaling exit direction is not "great" but it's what we use in other places already (like
ForwardCheckStructure).

* dfg/DFGAbstractState.cpp:
(JSC::DFG::AbstractState::execute):
* dfg/DFGCSEPhase.cpp:
(JSC::DFG::CSEPhase::int32ToDoubleCSE):
(CSEPhase):
(JSC::DFG::CSEPhase::performNodeCSE):
* dfg/DFGCommon.h:
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::fixDoubleEdge):
(JSC::DFG::FixupPhase::injectInt32ToDoubleNode):
* dfg/DFGNode.h:
(JSC::DFG::Node::willHaveCodeGenOrOSR):
* dfg/DFGNodeType.h:
(DFG):
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::convertLastOSRExitToForward):
(JSC::DFG::SpeculativeJIT::compileInt32ToDouble):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGVariableEventStream.cpp:
(JSC::DFG::VariableEventStream::reconstruct):

LayoutTests: 

Reviewed by Mark Hahnenberg.
        
Added one version of the test (dfg-int32-to-double-on-set-local-and-exit) that is based
exactly on Gabor's original test, and another that ought to fail even if I fix other bugs
in the future (see https://bugs.webkit.org/show_bug.cgi?id=109511).

* fast/js/dfg-int32-to-double-on-set-local-and-exit-expected.txt: Added.
* fast/js/dfg-int32-to-double-on-set-local-and-exit.html: Added.
* fast/js/dfg-int32-to-double-on-set-local-and-sometimes-exit-expected.txt: Added.
* fast/js/dfg-int32-to-double-on-set-local-and-sometimes-exit.html: Added.
* fast/js/script-tests/dfg-int32-to-double-on-set-local-and-exit.js: Added.
(checkpoint):
(func1):
(func2):
(func3):
(test):
* fast/js/script-tests/dfg-int32-to-double-on-set-local-and-sometimes-exit.js: Added.
(checkpoint):
(func1):
(func2):
(func3):
(test):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@142544 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index b5e0412..99a3a78 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,5 +1,51 @@
 2013-02-11  Filip Pizlo  <fpizlo@apple.com>
 
+        Strange bug in DFG OSR in JSC
+        https://bugs.webkit.org/show_bug.cgi?id=109491
+
+        Reviewed by Mark Hahnenberg.
+        
+        Int32ToDouble was being injected after a side-effecting operation and before a SetLocal. Anytime we
+        inject something just before a SetLocal we should be aware that the previous operation may have been
+        a side-effect associated with the current code origin. Hence, we should use a forward exit.
+        Int32ToDouble does not do forward exits by default.
+        
+        This patch adds a forward-exiting form of Int32ToDouble, for use in SetLocal Int32ToDouble injections.
+        Changed the CSE and other things to treat these nodes identically, but for the exit strategy to be
+        distinct (Int32ToDouble -> backward, ForwardInt32ToDouble -> forward). The use of the NodeType for
+        signaling exit direction is not "great" but it's what we use in other places already (like
+        ForwardCheckStructure).
+
+        * dfg/DFGAbstractState.cpp:
+        (JSC::DFG::AbstractState::execute):
+        * dfg/DFGCSEPhase.cpp:
+        (JSC::DFG::CSEPhase::int32ToDoubleCSE):
+        (CSEPhase):
+        (JSC::DFG::CSEPhase::performNodeCSE):
+        * dfg/DFGCommon.h:
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        (JSC::DFG::FixupPhase::fixDoubleEdge):
+        (JSC::DFG::FixupPhase::injectInt32ToDoubleNode):
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::willHaveCodeGenOrOSR):
+        * dfg/DFGNodeType.h:
+        (DFG):
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        (JSC::DFG::PredictionPropagationPhase::propagate):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::convertLastOSRExitToForward):
+        (JSC::DFG::SpeculativeJIT::compileInt32ToDouble):
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGVariableEventStream.cpp:
+        (JSC::DFG::VariableEventStream::reconstruct):
+
+2013-02-11  Filip Pizlo  <fpizlo@apple.com>
+
         NonStringCell and Object are practically the same thing for the purpose of speculation
         https://bugs.webkit.org/show_bug.cgi?id=109492
 
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
index cd14e28..17ad2e2 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
+++ b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
@@ -436,7 +436,8 @@
         break;
     }
         
-    case Int32ToDouble: {
+    case Int32ToDouble:
+    case ForwardInt32ToDouble: {
         JSValue child = forNode(node->child1()).value();
         if (child && child.isNumber()
             && trySetConstant(node, JSValue(JSValue::EncodeAsDouble, child.asNumber()))) {
diff --git a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
index 9745ce0..18495b5 100644
--- a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
@@ -126,6 +126,27 @@
         return 0;
     }
     
+    Node* int32ToDoubleCSE(Node* node)
+    {
+        for (unsigned i = m_indexInBlock; i--;) {
+            Node* otherNode = m_currentBlock->at(i);
+            if (otherNode == node->child1())
+                return 0;
+            if (!otherNode->shouldGenerate())
+                continue;
+            switch (otherNode->op()) {
+            case Int32ToDouble:
+            case ForwardInt32ToDouble:
+                if (otherNode->child1() == node->child1())
+                    return otherNode;
+                break;
+            default:
+                break;
+            }
+        }
+        return 0;
+    }
+    
     Node* constantCSE(Node* node)
     {
         for (unsigned i = endIndexForPureCSE(); i--;) {
@@ -1097,7 +1118,6 @@
         case ArithSqrt:
         case StringCharAt:
         case StringCharCodeAt:
-        case Int32ToDouble:
         case IsUndefined:
         case IsBoolean:
         case IsNumber:
@@ -1115,6 +1135,11 @@
             setReplacement(pureCSE(node));
             break;
             
+        case Int32ToDouble:
+        case ForwardInt32ToDouble:
+            setReplacement(int32ToDoubleCSE(node));
+            break;
+            
         case GetCallee:
             setReplacement(getCalleeLoadElimination(node->codeOrigin.inlineCallFrame));
             break;
diff --git a/Source/JavaScriptCore/dfg/DFGCommon.h b/Source/JavaScriptCore/dfg/DFGCommon.h
index 7908b10..6a275cc 100644
--- a/Source/JavaScriptCore/dfg/DFGCommon.h
+++ b/Source/JavaScriptCore/dfg/DFGCommon.h
@@ -236,6 +236,8 @@
     GloballyUnified
 };
 
+enum SpeculationDirection { ForwardSpeculation, BackwardSpeculation };
+
 } } // namespace JSC::DFG
 
 namespace WTF {
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index 0217665..1a93765 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -252,7 +252,7 @@
                 break;
             if (!node->variableAccessData()->shouldUseDoubleFormat())
                 break;
-            fixDoubleEdge(0);
+            fixDoubleEdge(0, ForwardSpeculation);
             break;
         }
             
@@ -508,7 +508,7 @@
         edge = newEdge;
     }
     
-    void fixDoubleEdge(unsigned childIndex)
+    void fixDoubleEdge(unsigned childIndex, SpeculationDirection direction = BackwardSpeculation)
     {
         Node* source = m_currentNode;
         Edge& edge = m_graph.child(source, childIndex);
@@ -518,13 +518,14 @@
             return;
         }
         
-        injectInt32ToDoubleNode(childIndex);
+        injectInt32ToDoubleNode(childIndex, direction);
     }
 
-    void injectInt32ToDoubleNode(unsigned childIndex)
+    void injectInt32ToDoubleNode(unsigned childIndex, SpeculationDirection direction = BackwardSpeculation)
     {
         Node* result = m_insertionSet.insertNode(
-            m_indexInBlock, DontRefChildren, RefNode, SpecDouble, Int32ToDouble,
+            m_indexInBlock, DontRefChildren, RefNode, SpecDouble,
+            direction == BackwardSpeculation ? Int32ToDouble : ForwardInt32ToDouble,
             m_currentNode->codeOrigin, m_graph.child(m_currentNode, childIndex).node());
         
 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h
index b818e8a..28ec073 100644
--- a/Source/JavaScriptCore/dfg/DFGNode.h
+++ b/Source/JavaScriptCore/dfg/DFGNode.h
@@ -943,6 +943,7 @@
         switch (op()) {
         case SetLocal:
         case Int32ToDouble:
+        case ForwardInt32ToDouble:
         case ValueToInt32:
         case UInt32ToNumber:
         case DoubleAsInt32:
diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h
index 726b6f9..d4ae55f 100644
--- a/Source/JavaScriptCore/dfg/DFGNodeType.h
+++ b/Source/JavaScriptCore/dfg/DFGNodeType.h
@@ -93,6 +93,7 @@
     /* Used to cast known integers to doubles, so as to separate the double form */\
     /* of the value from the integer form. */\
     macro(Int32ToDouble, NodeResultNumber) \
+    macro(ForwardInt32ToDouble, NodeResultNumber) \
     /* Used to speculate that a double value is actually an integer. */\
     macro(DoubleAsInt32, NodeResultInt32) \
     /* Used to record places where we must check if a value is a number. */\
diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
index 2b203a2..952fc76 100644
--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
@@ -727,6 +727,7 @@
         case PutByValAlias:
         case GetArrayLength:
         case Int32ToDouble:
+        case ForwardInt32ToDouble:
         case DoubleAsInt32:
         case GetLocalUnlinked:
         case GetMyArgumentsLength:
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
index e0caf75..48247eb 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
@@ -234,7 +234,7 @@
     Node* setLocal = m_jit.graph().m_blocks[m_block]->at(setLocalIndexInBlock);
     bool hadInt32ToDouble = false;
     
-    if (setLocal->op() == Int32ToDouble) {
+    if (setLocal->op() == ForwardInt32ToDouble) {
         setLocal = m_jit.graph().m_blocks[m_block]->at(++setLocalIndexInBlock);
         hadInt32ToDouble = true;
     }
@@ -2395,9 +2395,16 @@
         MacroAssembler::AboveOrEqual, op1GPR, GPRInfo::tagTypeNumberRegister);
     
     if (!isNumberSpeculation(m_state.forNode(node->child1()).m_type)) {
-        speculationCheck(
-            BadType, JSValueRegs(op1GPR), node->child1(),
-            m_jit.branchTest64(MacroAssembler::Zero, op1GPR, GPRInfo::tagTypeNumberRegister));
+        if (node->op() == ForwardInt32ToDouble) {
+            forwardSpeculationCheck(
+                BadType, JSValueRegs(op1GPR), node->child1().node(),
+                m_jit.branchTest64(MacroAssembler::Zero, op1GPR, GPRInfo::tagTypeNumberRegister),
+                ValueRecovery::inGPR(op1GPR, DataFormatJS));
+        } else {
+            speculationCheck(
+                BadType, JSValueRegs(op1GPR), node->child1(),
+                m_jit.branchTest64(MacroAssembler::Zero, op1GPR, GPRInfo::tagTypeNumberRegister));
+        }
     }
     
     m_jit.move(op1GPR, tempGPR);
@@ -2419,9 +2426,16 @@
         MacroAssembler::Equal, op1TagGPR, TrustedImm32(JSValue::Int32Tag));
     
     if (!isNumberSpeculation(m_state.forNode(node->child1()).m_type)) {
-        speculationCheck(
-            BadType, JSValueRegs(op1TagGPR, op1PayloadGPR), node->child1(),
-            m_jit.branch32(MacroAssembler::AboveOrEqual, op1TagGPR, TrustedImm32(JSValue::LowestTag)));
+        if (node->op() == ForwardInt32ToDouble) {
+            forwardSpeculationCheck(
+                BadType, JSValueRegs(op1TagGPR, op1PayloadGPR), node->child1().node(),
+                m_jit.branch32(MacroAssembler::AboveOrEqual, op1TagGPR, TrustedImm32(JSValue::LowestTag)),
+                ValueRecovery::inPair(op1TagGPR, op1PayloadGPR));
+        } else {
+            speculationCheck(
+                BadType, JSValueRegs(op1TagGPR, op1PayloadGPR), node->child1(),
+                m_jit.branch32(MacroAssembler::AboveOrEqual, op1TagGPR, TrustedImm32(JSValue::LowestTag)));
+        }
     }
     
     unboxDouble(op1TagGPR, op1PayloadGPR, resultFPR, tempFPR);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
index 0f053f7..8cc73d6 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
@@ -54,7 +54,6 @@
 class SpeculateBooleanOperand;
 
 enum GeneratedOperandType { GeneratedOperandTypeUnknown, GeneratedOperandInteger, GeneratedOperandDouble, GeneratedOperandJSValue};
-enum SpeculationDirection { ForwardSpeculation, BackwardSpeculation };
 
 // === SpeculativeJIT ===
 //
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
index ddef58d..f76bc53 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
@@ -2372,7 +2372,8 @@
         break;
     }
         
-    case Int32ToDouble: {
+    case Int32ToDouble:
+    case ForwardInt32ToDouble: {
         compileInt32ToDouble(node);
         break;
     }
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
index 59bac16..449fc47 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
@@ -2342,7 +2342,8 @@
         break;
     }
         
-    case Int32ToDouble: {
+    case Int32ToDouble:
+    case ForwardInt32ToDouble: {
         compileInt32ToDouble(node);
         break;
     }
diff --git a/Source/JavaScriptCore/dfg/DFGVariableAccessData.h b/Source/JavaScriptCore/dfg/DFGVariableAccessData.h
index 6d8e897..2128681 100644
--- a/Source/JavaScriptCore/dfg/DFGVariableAccessData.h
+++ b/Source/JavaScriptCore/dfg/DFGVariableAccessData.h
@@ -177,8 +177,12 @@
         
         // If the variable is not a number prediction, then this doesn't
         // make any sense.
-        if (!isNumberSpeculation(prediction()))
+        if (!isNumberSpeculation(prediction())) {
+            // FIXME: we may end up forcing a local in inlined argument position to be a double even
+            // if it is sometimes not even numeric, since this never signals the fact that it doesn't
+            // want doubles. https://bugs.webkit.org/show_bug.cgi?id=109511
             return false;
+        }
         
         // If the variable is predicted to hold only doubles, then it's a
         // no-brainer: it should be formatted as a double.
diff --git a/Source/JavaScriptCore/dfg/DFGVariableEventStream.cpp b/Source/JavaScriptCore/dfg/DFGVariableEventStream.cpp
index ee6a15d..300fe59 100644
--- a/Source/JavaScriptCore/dfg/DFGVariableEventStream.cpp
+++ b/Source/JavaScriptCore/dfg/DFGVariableEventStream.cpp
@@ -243,6 +243,7 @@
                         continue;
                     switch (node->op()) {
                     case Int32ToDouble:
+                    case ForwardInt32ToDouble:
                         int32ToDoubleID = id;
                         break;
                     case ValueToInt32: