Given a PutById or GetById with a proven structure, the DFG should be able to emit a PutByOffset or GetByOffset instead
https://bugs.webkit.org/show_bug.cgi?id=102327

Reviewed by Mark Hahnenberg.

If the profiler tells us that a GetById or PutById may be polymorphic but our
control flow analysis proves that it isn't, we should trust the control flow
analysis over the profiler. This arises in cases where GetById or PutById were
inlined: the inlined function may have been called from other places that led
to polymorphism, but in the current inlined context, there is no polymorphism.

* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dump):
* bytecode/GetByIdStatus.cpp:
(JSC::GetByIdStatus::computeFor):
(JSC):
* bytecode/GetByIdStatus.h:
(JSC::GetByIdStatus::GetByIdStatus):
(GetByIdStatus):
* bytecode/PutByIdStatus.cpp:
(JSC::PutByIdStatus::computeFor):
(JSC):
* bytecode/PutByIdStatus.h:
(JSC):
(JSC::PutByIdStatus::PutByIdStatus):
(PutByIdStatus):
* dfg/DFGAbstractState.cpp:
(JSC::DFG::AbstractState::execute):
* dfg/DFGAbstractValue.h:
(JSC::DFG::AbstractValue::bestProvenStructure):
(AbstractValue):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
(JSC::DFG::ConstantFoldingPhase::addStructureTransitionCheck):
(ConstantFoldingPhase):
* dfg/DFGNode.h:
(JSC::DFG::Node::convertToGetByOffset):
(Node):
(JSC::DFG::Node::convertToPutByOffset):
(JSC::DFG::Node::hasStorageResult):
* runtime/JSGlobalObject.h:
(JSC::Structure::prototypeChain):
(JSC):
(JSC::Structure::isValid):
* runtime/Operations.h:
(JSC::isPrototypeChainNormalized):
(JSC):
* runtime/Structure.h:
(Structure):
(JSC::Structure::transitionDidInvolveSpecificValue):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@135041 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
index 65395fc..92f2f20 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
+++ b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
@@ -30,6 +30,8 @@
 
 #include "CodeBlock.h"
 #include "DFGBasicBlock.h"
+#include "GetByIdStatus.h"
+#include "PutByIdStatus.h"
 
 namespace JSC { namespace DFG {
 
@@ -1409,8 +1411,30 @@
             m_isValid = false;
             break;
         }
-        if (isCellSpeculation(m_graph[node.child1()].prediction()))
+        if (isCellSpeculation(m_graph[node.child1()].prediction())) {
             forNode(node.child1()).filter(SpecCell);
+
+            if (Structure* structure = forNode(node.child1()).bestProvenStructure()) {
+                GetByIdStatus status = GetByIdStatus::computeFor(
+                    m_graph.m_globalData, structure,
+                    m_graph.m_codeBlock->identifier(node.identifierNumber()));
+                if (status.isSimple()) {
+                    // Assert things that we can't handle and that the computeFor() method
+                    // above won't be able to return.
+                    ASSERT(status.structureSet().size() == 1);
+                    ASSERT(status.chain().isEmpty());
+                    
+                    if (status.specificValue())
+                        forNode(nodeIndex).set(status.specificValue());
+                    else
+                        forNode(nodeIndex).makeTop();
+                    forNode(node.child1()).filter(status.structureSet());
+                    
+                    m_foundConstants = true;
+                    break;
+                }
+            }
+        }
         clobberWorld(node.codeOrigin, indexInBlock);
         forNode(nodeIndex).makeTop();
         break;
@@ -1573,15 +1597,24 @@
         break; 
     }
     case GetByOffset:
-        node.setCanExit(!isCellSpeculation(forNode(node.child1()).m_type));
-        forNode(node.child1()).filter(SpecCell);
+        if (!m_graph[node.child1()].hasStorageResult()) {
+            node.setCanExit(!isCellSpeculation(forNode(node.child1()).m_type));
+            forNode(node.child1()).filter(SpecCell);
+        }
         forNode(nodeIndex).makeTop();
         break;
             
-    case PutByOffset:
-        node.setCanExit(!isCellSpeculation(forNode(node.child1()).m_type));
-        forNode(node.child1()).filter(SpecCell);
+    case PutByOffset: {
+        bool canExit = false;
+        if (!m_graph[node.child1()].hasStorageResult()) {
+            canExit |= !isCellSpeculation(forNode(node.child1()).m_type);
+            forNode(node.child1()).filter(SpecCell);
+        }
+        canExit |= !isCellSpeculation(forNode(node.child2()).m_type);
+        forNode(node.child2()).filter(SpecCell);
+        node.setCanExit(canExit);
         break;
+    }
             
     case CheckFunction: {
         JSValue value = forNode(node.child1()).value();
@@ -1603,6 +1636,26 @@
     case PutById:
     case PutByIdDirect:
         node.setCanExit(true);
+        if (Structure* structure = forNode(node.child1()).bestProvenStructure()) {
+            PutByIdStatus status = PutByIdStatus::computeFor(
+                m_graph.m_globalData,
+                m_graph.globalObjectFor(node.codeOrigin),
+                structure,
+                m_graph.m_codeBlock->identifier(node.identifierNumber()),
+                node.op() == PutByIdDirect);
+            if (status.isSimpleReplace()) {
+                forNode(node.child1()).filter(structure);
+                m_foundConstants = true;
+                break;
+            }
+            if (status.isSimpleTransition()) {
+                clobberStructures(indexInBlock);
+                forNode(node.child1()).set(status.newStructure());
+                m_haveStructures = true;
+                m_foundConstants = true;
+                break;
+            }
+        }
         forNode(node.child1()).filter(SpecCell);
         clobberWorld(node.codeOrigin, indexInBlock);
         break;