fourthTier: DFG CFA should know when it hits a contradiction
authoroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 25 Jul 2013 04:02:13 +0000 (04:02 +0000)
committeroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 25 Jul 2013 04:02:13 +0000 (04:02 +0000)
https://bugs.webkit.org/show_bug.cgi?id=117272

Reviewed by Oliver Hunt.

This makes the DFG CFA immediately detect when it hit a contradiction. Previously
we might not know this: for example if we did an int32 type check on a known string;
the code would definitely always exit but the CFA would think that we wouldn't have
even though it would have computed a BOTTOM (i.e. contradictory) value for that
variable.

This requires two other changes:

- CFA must report contradictions as if they are frequent exit sites, since
  contradictory speculations will subsequently get replaced with ForceOSRExit.
  ForceOSRExit cannot itself report profiling data back to the DFG::ExitProfile. So,
  we do this on behalf of the speculation, eagerly, within the CFA. This also has
  the effect of speeding convergence somewhat. We may want to revisit this later;
  for example we might want to instead have the notion of a ForceOSRExit that knows
  the set of speculations that got folded into it.

- This revealed a bug where the CFA was modeling CheckStructure on a node that had
  a known singleton m_futurePossibleStructure set somewhat differently than the
  constant folder. If the CheckStructure was checking a structure set with two or
  more structures in it, it would not filter the abstract value. But the constant
  folder would turn this into a watchpoint on the singleton structure, thereby
  filtering the value. This discrepancy meant that we wouldn't realize the
  contradiction until the backend, and the AbstractState::bail() method asserts that
  we always realize contradictions in the constant folder.

* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/CodeBlock.h:
(JSC::CodeBlock::addFrequentExitSite):
(JSC::CodeBlock::hasExitSite):
(CodeBlock):
* bytecode/DFGExitProfile.cpp:
(JSC::DFG::ExitProfile::add):
(JSC::DFG::ExitProfile::hasExitSite):
(JSC::DFG::QueryableExitProfile::QueryableExitProfile):
(JSC::DFG::QueryableExitProfile::~QueryableExitProfile):
(DFG):
(JSC::DFG::QueryableExitProfile::initialize):
* bytecode/DFGExitProfile.h:
(JSC::DFG::FrequentExitSite::FrequentExitSite):
(ExitProfile):
(JSC::DFG::ExitProfile::hasExitSite):
(QueryableExitProfile):
* bytecode/ExitKind.cpp:
(JSC::exitKindToString):
* dfg/DFGAbstractState.cpp:
(JSC::DFG::AbstractState::AbstractState):
(JSC::DFG::AbstractState::beginBasicBlock):
(JSC::DFG::AbstractState::reset):
(JSC::DFG::AbstractState::startExecuting):
(JSC::DFG::AbstractState::executeEffects):
(JSC::DFG::AbstractState::execute):
(JSC::DFG::AbstractState::filter):
(DFG):
(JSC::DFG::AbstractState::filterArrayModes):
(JSC::DFG::AbstractState::filterByValue):
(JSC::DFG::AbstractState::bail):
* dfg/DFGAbstractState.h:
(AbstractState):
(JSC::DFG::AbstractState::filter):
(JSC::DFG::AbstractState::filterArrayModes):
(JSC::DFG::AbstractState::filterByValue):
(JSC::DFG::AbstractState::filterByType):
* dfg/DFGAbstractValue.cpp:
(JSC::DFG::AbstractValue::filter):
(JSC::DFG::AbstractValue::filterArrayModes):
(DFG):
(JSC::DFG::AbstractValue::filterByValue):
(JSC::DFG::AbstractValue::normalizeClarity):
* dfg/DFGAbstractValue.h:
(AbstractValue):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::InlineStackEntry::InlineStackEntry):
* dfg/DFGCFAPhase.cpp:
(JSC::DFG::CFAPhase::performBlockCFA):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::debugFail):
(JSC::DFG::capabilityLevel):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
(ConstantFoldingPhase):
(JSC::DFG::ConstantFoldingPhase::paintUnreachableCode):
* dfg/DFGFiltrationResult.h: Added.
(DFG):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNodeType.h:
(DFG):
* dfg/DFGOSRExitBase.cpp:
(JSC::DFG::OSRExitBase::considerAddingAsFrequentExitSiteSlow):
* dfg/DFGOSRExitBase.h:
(JSC::DFG::OSRExitBase::considerAddingAsFrequentExitSite):
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::backwardTypeCheck):
(JSC::DFG::SpeculativeJIT::bail):
(DFG):
(JSC::DFG::SpeculativeJIT::compile):
(JSC::DFG::SpeculativeJIT::compileToStringOnCell):
(JSC::DFG::SpeculativeJIT::speculateStringObject):
(JSC::DFG::SpeculativeJIT::speculateStringOrStringObject):
* dfg/DFGSpeculativeJIT.h:
(SpeculativeJIT):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::fillSpeculateIntInternal):
(JSC::DFG::SpeculativeJIT::fillSpeculateDouble):
(JSC::DFG::SpeculativeJIT::fillSpeculateCell):
(JSC::DFG::SpeculativeJIT::fillSpeculateBoolean):
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::fillSpeculateIntInternal):
(JSC::DFG::SpeculativeJIT::fillSpeculateDouble):
(JSC::DFG::SpeculativeJIT::fillSpeculateCell):
(JSC::DFG::SpeculativeJIT::fillSpeculateBoolean):
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::compileNode):
(JSC::FTL::LowerDFGToLLVM::appendTypeCheck):

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

26 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecode/CodeBlock.h
Source/JavaScriptCore/bytecode/DFGExitProfile.cpp
Source/JavaScriptCore/bytecode/DFGExitProfile.h
Source/JavaScriptCore/bytecode/ExitKind.cpp
Source/JavaScriptCore/dfg/DFGAbstractState.cpp
Source/JavaScriptCore/dfg/DFGAbstractState.h
Source/JavaScriptCore/dfg/DFGAbstractValue.cpp
Source/JavaScriptCore/dfg/DFGAbstractValue.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGCFAPhase.cpp
Source/JavaScriptCore/dfg/DFGCapabilities.cpp
Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
Source/JavaScriptCore/dfg/DFGFiltrationResult.h [new file with mode: 0644]
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGGraph.cpp
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOSRExitBase.cpp
Source/JavaScriptCore/dfg/DFGOSRExitBase.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp

index e5e3f9b..b7494fb 100644 (file)
@@ -1,3 +1,131 @@
+2013-06-05  Filip Pizlo  <fpizlo@apple.com>
+
+        fourthTier: DFG CFA should know when it hits a contradiction
+        https://bugs.webkit.org/show_bug.cgi?id=117272
+
+        Reviewed by Oliver Hunt.
+        
+        This makes the DFG CFA immediately detect when it hit a contradiction. Previously
+        we might not know this: for example if we did an int32 type check on a known string;
+        the code would definitely always exit but the CFA would think that we wouldn't have
+        even though it would have computed a BOTTOM (i.e. contradictory) value for that
+        variable.
+        
+        This requires two other changes:
+        
+        - CFA must report contradictions as if they are frequent exit sites, since
+          contradictory speculations will subsequently get replaced with ForceOSRExit.
+          ForceOSRExit cannot itself report profiling data back to the DFG::ExitProfile. So,
+          we do this on behalf of the speculation, eagerly, within the CFA. This also has
+          the effect of speeding convergence somewhat. We may want to revisit this later;
+          for example we might want to instead have the notion of a ForceOSRExit that knows
+          the set of speculations that got folded into it.
+        
+        - This revealed a bug where the CFA was modeling CheckStructure on a node that had
+          a known singleton m_futurePossibleStructure set somewhat differently than the
+          constant folder. If the CheckStructure was checking a structure set with two or
+          more structures in it, it would not filter the abstract value. But the constant
+          folder would turn this into a watchpoint on the singleton structure, thereby
+          filtering the value. This discrepancy meant that we wouldn't realize the
+          contradiction until the backend, and the AbstractState::bail() method asserts that
+          we always realize contradictions in the constant folder.
+
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * bytecode/CodeBlock.h:
+        (JSC::CodeBlock::addFrequentExitSite):
+        (JSC::CodeBlock::hasExitSite):
+        (CodeBlock):
+        * bytecode/DFGExitProfile.cpp:
+        (JSC::DFG::ExitProfile::add):
+        (JSC::DFG::ExitProfile::hasExitSite):
+        (JSC::DFG::QueryableExitProfile::QueryableExitProfile):
+        (JSC::DFG::QueryableExitProfile::~QueryableExitProfile):
+        (DFG):
+        (JSC::DFG::QueryableExitProfile::initialize):
+        * bytecode/DFGExitProfile.h:
+        (JSC::DFG::FrequentExitSite::FrequentExitSite):
+        (ExitProfile):
+        (JSC::DFG::ExitProfile::hasExitSite):
+        (QueryableExitProfile):
+        * bytecode/ExitKind.cpp:
+        (JSC::exitKindToString):
+        * dfg/DFGAbstractState.cpp:
+        (JSC::DFG::AbstractState::AbstractState):
+        (JSC::DFG::AbstractState::beginBasicBlock):
+        (JSC::DFG::AbstractState::reset):
+        (JSC::DFG::AbstractState::startExecuting):
+        (JSC::DFG::AbstractState::executeEffects):
+        (JSC::DFG::AbstractState::execute):
+        (JSC::DFG::AbstractState::filter):
+        (DFG):
+        (JSC::DFG::AbstractState::filterArrayModes):
+        (JSC::DFG::AbstractState::filterByValue):
+        (JSC::DFG::AbstractState::bail):
+        * dfg/DFGAbstractState.h:
+        (AbstractState):
+        (JSC::DFG::AbstractState::filter):
+        (JSC::DFG::AbstractState::filterArrayModes):
+        (JSC::DFG::AbstractState::filterByValue):
+        (JSC::DFG::AbstractState::filterByType):
+        * dfg/DFGAbstractValue.cpp:
+        (JSC::DFG::AbstractValue::filter):
+        (JSC::DFG::AbstractValue::filterArrayModes):
+        (DFG):
+        (JSC::DFG::AbstractValue::filterByValue):
+        (JSC::DFG::AbstractValue::normalizeClarity):
+        * dfg/DFGAbstractValue.h:
+        (AbstractValue):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::InlineStackEntry::InlineStackEntry):
+        * dfg/DFGCFAPhase.cpp:
+        (JSC::DFG::CFAPhase::performBlockCFA):
+        * dfg/DFGCapabilities.cpp:
+        (JSC::DFG::debugFail):
+        (JSC::DFG::capabilityLevel):
+        * dfg/DFGConstantFoldingPhase.cpp:
+        (JSC::DFG::ConstantFoldingPhase::foldConstants):
+        (ConstantFoldingPhase):
+        (JSC::DFG::ConstantFoldingPhase::paintUnreachableCode):
+        * dfg/DFGFiltrationResult.h: Added.
+        (DFG):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGNodeType.h:
+        (DFG):
+        * dfg/DFGOSRExitBase.cpp:
+        (JSC::DFG::OSRExitBase::considerAddingAsFrequentExitSiteSlow):
+        * dfg/DFGOSRExitBase.h:
+        (JSC::DFG::OSRExitBase::considerAddingAsFrequentExitSite):
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        (JSC::DFG::PredictionPropagationPhase::propagate):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::backwardTypeCheck):
+        (JSC::DFG::SpeculativeJIT::bail):
+        (DFG):
+        (JSC::DFG::SpeculativeJIT::compile):
+        (JSC::DFG::SpeculativeJIT::compileToStringOnCell):
+        (JSC::DFG::SpeculativeJIT::speculateStringObject):
+        (JSC::DFG::SpeculativeJIT::speculateStringOrStringObject):
+        * dfg/DFGSpeculativeJIT.h:
+        (SpeculativeJIT):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::fillSpeculateIntInternal):
+        (JSC::DFG::SpeculativeJIT::fillSpeculateDouble):
+        (JSC::DFG::SpeculativeJIT::fillSpeculateCell):
+        (JSC::DFG::SpeculativeJIT::fillSpeculateBoolean):
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::fillSpeculateIntInternal):
+        (JSC::DFG::SpeculativeJIT::fillSpeculateDouble):
+        (JSC::DFG::SpeculativeJIT::fillSpeculateCell):
+        (JSC::DFG::SpeculativeJIT::fillSpeculateBoolean):
+        (JSC::DFG::SpeculativeJIT::compile):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToLLVM.cpp:
+        (JSC::FTL::LowerDFGToLLVM::compileNode):
+        (JSC::FTL::LowerDFGToLLVM::appendTypeCheck):
+
 2013-06-07  Mark Lam  <mark.lam@apple.com>
 
         32-bit CallFrame::Location should use Instruction* for BytecodeLocation, not bytecodeOffset.
index 24040ea..b9656c4 100644 (file)
@@ -622,10 +622,15 @@ public:
     bool addFrequentExitSite(const DFG::FrequentExitSite& site)
     {
         ASSERT(JITCode::isBaselineCode(jitType()));
-        return m_exitProfile.add(site);
+        ConcurrentJITLocker locker(m_lock);
+        return m_exitProfile.add(locker, site);
+    }
+        
+    bool hasExitSite(const DFG::FrequentExitSite& site) const
+    {
+        ConcurrentJITLocker locker(m_lock);
+        return m_exitProfile.hasExitSite(locker, site);
     }
-
-    bool hasExitSite(const DFG::FrequentExitSite& site) const { return m_exitProfile.hasExitSite(site); }
 
     DFG::ExitProfile& exitProfile() { return m_exitProfile; }
 
@@ -907,7 +912,7 @@ public:
     // Another exception to the rules is that the GC can do whatever it wants
     // without holding any locks, because the GC is guaranteed to wait until any
     // concurrent compilation threads finish what they're doing.
-    ConcurrentJITLock m_lock;
+    mutable ConcurrentJITLock m_lock;
     
     bool m_shouldAlwaysBeInlined;
     bool m_allTransitionsHaveBeenMarked; // Initialized and used on every GC.
index d36878f..90ae41e 100644 (file)
@@ -33,7 +33,7 @@ namespace JSC { namespace DFG {
 ExitProfile::ExitProfile() { }
 ExitProfile::~ExitProfile() { }
 
-bool ExitProfile::add(const FrequentExitSite& site)
+bool ExitProfile::add(const ConcurrentJITLocker&, const FrequentExitSite& site)
 {
     // If we've never seen any frequent exits then create the list and put this site
     // into it.
@@ -70,7 +70,7 @@ Vector<FrequentExitSite> ExitProfile::exitSitesFor(unsigned bytecodeIndex)
     return result;
 }
 
-bool ExitProfile::hasExitSite(const FrequentExitSite& site) const
+bool ExitProfile::hasExitSite(const ConcurrentJITLocker&, const FrequentExitSite& site) const
 {
     if (!m_frequentExitSites)
         return false;
@@ -82,7 +82,10 @@ bool ExitProfile::hasExitSite(const FrequentExitSite& site) const
     return false;
 }
 
-QueryableExitProfile::QueryableExitProfile(const ExitProfile& profile)
+QueryableExitProfile::QueryableExitProfile() { }
+QueryableExitProfile::~QueryableExitProfile() { }
+
+void QueryableExitProfile::initialize(const ConcurrentJITLocker&, const ExitProfile& profile)
 {
     if (!profile.m_frequentExitSites)
         return;
@@ -91,6 +94,4 @@ QueryableExitProfile::QueryableExitProfile(const ExitProfile& profile)
         m_frequentExitSites.add(profile.m_frequentExitSites->at(i));
 }
 
-QueryableExitProfile::~QueryableExitProfile() { }
-
 } } // namespace JSC::DFG
index fe7b2f9..f5eb83d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2012, 2013 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -26,6 +26,7 @@
 #ifndef DFGExitProfile_h
 #define DFGExitProfile_h
 
+#include "ConcurrentJITLock.h"
 #include "ExitKind.h"
 #include <wtf/HashSet.h>
 #include <wtf/OwnPtr.h>
@@ -51,7 +52,11 @@ public:
         : m_bytecodeOffset(bytecodeOffset)
         , m_kind(kind)
     {
-        ASSERT(exitKindIsCountable(kind));
+        if (m_kind == ArgumentsEscaped) {
+            // Count this one globally. It doesn't matter where in the code block the arguments excaped;
+            // the fact that they did is not associated with any particular instruction.
+            m_bytecodeOffset = 0;
+        }
     }
     
     // Use this constructor if you wish for the exit site to be counted globally within its
@@ -60,7 +65,6 @@ public:
         : m_bytecodeOffset(0)
         , m_kind(kind)
     {
-        ASSERT(exitKindIsCountable(kind));
     }
     
     bool operator!() const
@@ -127,7 +131,7 @@ public:
     // be called a fixed number of times per recompilation. Recompilation is
     // rare to begin with, and implies doing O(n) operations on the CodeBlock
     // anyway.
-    bool add(const FrequentExitSite&);
+    bool add(const ConcurrentJITLocker&, const FrequentExitSite&);
     
     // Get the frequent exit sites for a bytecode index. This is O(n), and is
     // meant to only be used from debugging/profiling code.
@@ -137,14 +141,14 @@ public:
     // in the compiler. It should be strictly cheaper than building a
     // QueryableExitProfile, if you really expect this to be called infrequently
     // and you believe that there are few exit sites.
-    bool hasExitSite(const FrequentExitSite&) const;
-    bool hasExitSite(ExitKind kind) const
+    bool hasExitSite(const ConcurrentJITLocker&, const FrequentExitSite&) const;
+    bool hasExitSite(const ConcurrentJITLocker& locker, ExitKind kind) const
     {
-        return hasExitSite(FrequentExitSite(kind));
+        return hasExitSite(locker, FrequentExitSite(kind));
     }
-    bool hasExitSite(unsigned bytecodeIndex, ExitKind kind) const
+    bool hasExitSite(const ConcurrentJITLocker& locker, unsigned bytecodeIndex, ExitKind kind) const
     {
-        return hasExitSite(FrequentExitSite(bytecodeIndex, kind));
+        return hasExitSite(locker, FrequentExitSite(bytecodeIndex, kind));
     }
     
 private:
@@ -155,9 +159,11 @@ private:
 
 class QueryableExitProfile {
 public:
-    explicit QueryableExitProfile(const ExitProfile&);
+    QueryableExitProfile();
     ~QueryableExitProfile();
     
+    void initialize(const ConcurrentJITLocker&, const ExitProfile&);
+
     bool hasExitSite(const FrequentExitSite& site) const
     {
         return m_frequentExitSites.find(site) != m_frequentExitSites.end();
index a8d9045..f0809fa 100644 (file)
@@ -70,10 +70,11 @@ const char* exitKindToString(ExitKind kind)
         return "Uncountable";
     case UncountableWatchpoint:
         return "UncountableWatchpoint";
-    default:
-        RELEASE_ASSERT_NOT_REACHED();
-        return "Unknown";
+    case WatchdogTimerFired:
+        return "WatchdogTimerFired";
     }
+    RELEASE_ASSERT_NOT_REACHED();
+    return "Unknown";
 }
 
 bool exitKindIsCountable(ExitKind kind)
index 834d161..a2b1f63 100644 (file)
@@ -42,6 +42,8 @@ AbstractState::AbstractState(Graph& graph)
     , m_graph(graph)
     , m_variables(m_codeBlock->numParameters(), graph.m_localVars)
     , m_block(0)
+    , m_currentNode(0)
+    , m_executionMode(CleanFiltration)
 {
 }
 
@@ -79,6 +81,7 @@ void AbstractState::beginBasicBlock(BasicBlock* basicBlock)
     m_isValid = true;
     m_foundConstants = false;
     m_branchDirection = InvalidBranchDirection;
+    m_currentNode = 0;
 }
 
 void AbstractState::initialize(Graph& graph)
@@ -206,6 +209,7 @@ void AbstractState::reset()
     m_block = 0;
     m_isValid = false;
     m_branchDirection = InvalidBranchDirection;
+    m_currentNode = 0;
 }
 
 AbstractState::BooleanResult AbstractState::booleanResult(Node* node, AbstractValue& value)
@@ -229,7 +233,7 @@ AbstractState::BooleanResult AbstractState::booleanResult(Node* node, AbstractVa
     return UnknownBooleanResult;
 }
 
-bool AbstractState::startExecuting(Node* node)
+bool AbstractState::startExecuting(Node* node, ExecutionMode executionMode)
 {
     ASSERT(m_block);
     ASSERT(m_isValid);
@@ -238,15 +242,20 @@ bool AbstractState::startExecuting(Node* node)
     
     node->setCanExit(false);
     
-    if (!node->shouldGenerate())
+    if (!node->shouldGenerate()) {
+        m_currentNode = 0;
+        m_executionMode = CleanFiltration;
         return false;
+    }
     
+    m_currentNode = node;
+    m_executionMode = executionMode;
     return true;
 }
 
-bool AbstractState::startExecuting(unsigned indexInBlock)
+bool AbstractState::startExecuting(unsigned indexInBlock, ExecutionMode executionMode)
 {
-    return startExecuting(m_block->at(indexInBlock));
+    return startExecuting(m_block->at(indexInBlock), executionMode);
 }
 
 void AbstractState::executeEdges(Node* node)
@@ -740,31 +749,31 @@ bool AbstractState::executeEffects(unsigned indexInBlock, Node* node)
             }
         } else if (isNumberSpeculation(abstractChild.m_type)) {
             if (trySetConstant(node, vm->smallStrings.numberString())) {
-                forNode(node->child1()).filter(SpecNumber);
+                filter(node->child1(), SpecNumber);
                 m_foundConstants = true;
                 break;
             }
         } else if (isStringSpeculation(abstractChild.m_type)) {
             if (trySetConstant(node, vm->smallStrings.stringString())) {
-                forNode(node->child1()).filter(SpecString);
+                filter(node->child1(), SpecString);
                 m_foundConstants = true;
                 break;
             }
         } else if (isFinalObjectSpeculation(abstractChild.m_type) || isArraySpeculation(abstractChild.m_type) || isArgumentsSpeculation(abstractChild.m_type)) {
             if (trySetConstant(node, vm->smallStrings.objectString())) {
-                forNode(node->child1()).filter(SpecFinalObject | SpecArray | SpecArguments);
+                filter(node->child1(), SpecFinalObject | SpecArray | SpecArguments);
                 m_foundConstants = true;
                 break;
             }
         } else if (isFunctionSpeculation(abstractChild.m_type)) {
             if (trySetConstant(node, vm->smallStrings.functionString())) {
-                forNode(node->child1()).filter(SpecFunction);
+                filter(node->child1(), SpecFunction);
                 m_foundConstants = true;
                 break;
             }
         } else if (isBooleanSpeculation(abstractChild.m_type)) {
             if (trySetConstant(node, vm->smallStrings.booleanString())) {
-                forNode(node->child1()).filter(SpecBoolean);
+                filter(node->child1(), SpecBoolean);
                 m_foundConstants = true;
                 break;
             }
@@ -1084,6 +1093,8 @@ bool AbstractState::executeEffects(unsigned indexInBlock, Node* node)
             type |= SpecString;
         }
         destination.setType(type);
+        if (destination.isClear())
+            m_isValid = false;
         break;
     }
         
@@ -1092,8 +1103,10 @@ bool AbstractState::executeEffects(unsigned indexInBlock, Node* node)
         case StringObjectUse:
             // This also filters that the StringObject has the primordial StringObject
             // structure.
-            forNode(node->child1()).filter(
-                m_graph, m_graph.globalObjectFor(node->codeOrigin)->stringObjectStructure());
+            filter(
+                node->child1(),
+                m_graph.globalObjectFor(node->codeOrigin)->stringObjectStructure(),
+                NotStringObject);
             node->setCanExit(true); // We could be more precise but it's likely not worth it.
             break;
         case StringOrStringObjectUse:
@@ -1312,7 +1325,7 @@ bool AbstractState::executeEffects(unsigned indexInBlock, Node* node)
                         forNode(node).set(m_graph, status.specificValue());
                     else
                         forNode(node).makeTop();
-                    forNode(node->child1()).filter(m_graph, status.structureSet());
+                    filter(node->child1(), status.structureSet(), BadCache);
                     
                     m_foundConstants = true;
                     break;
@@ -1342,16 +1355,33 @@ bool AbstractState::executeEffects(unsigned indexInBlock, Node* node)
         // FIXME: We should be able to propagate the structure sets of constants (i.e. prototypes).
         AbstractValue& value = forNode(node->child1());
         ASSERT(!(value.m_type & ~SpecCell)); // Edge filtering should have already ensured this.
+
+        StructureSet& set = node->structureSet();
+
+        if (value.m_currentKnownStructure.isSubsetOf(set)) {
+            m_foundConstants = true;
+            break;
+        }
+
+        ExitKind exitKind;
+        if (node->child1()->op() == WeakJSConstant)
+            exitKind = BadWeakConstantCache;
+        else
+            exitKind = BadCache;
+
+        node->setCanExit(true);
+
         // If this structure check is attempting to prove knowledge already held in
         // the futurePossibleStructure set then the constant folding phase should
         // turn this into a watchpoint instead.
-        StructureSet& set = node->structureSet();
         if (value.m_futurePossibleStructure.isSubsetOf(set)
-            || value.m_currentKnownStructure.isSubsetOf(set))
+            && value.m_futurePossibleStructure.hasSingleton()) {
             m_foundConstants = true;
-        if (!value.m_currentKnownStructure.isSubsetOf(set))
-            node->setCanExit(true);
-        value.filter(m_graph, set);
+            filter(value, value.m_futurePossibleStructure.singleton(), exitKind);
+            break;
+        }
+
+        filter(value, set, exitKind);
         m_haveStructures = true;
         break;
     }
@@ -1360,6 +1390,12 @@ bool AbstractState::executeEffects(unsigned indexInBlock, Node* node)
     case ForwardStructureTransitionWatchpoint: {
         AbstractValue& value = forNode(node->child1());
 
+        ExitKind exitKind;
+        if (node->child1()->op() == WeakJSConstant)
+            exitKind = BadWeakConstantCache;
+        else
+            exitKind = BadCache;
+
         // It's only valid to issue a structure transition watchpoint if we already
         // know that the watchpoint covers a superset of the structures known to
         // belong to the set of future structures that this value may have.
@@ -1370,7 +1406,7 @@ bool AbstractState::executeEffects(unsigned indexInBlock, Node* node)
             value.m_futurePossibleStructure.isSubsetOf(StructureSet(node->structure()))
             || m_graph.watchpoints().shouldAssumeMixedState(node->structure()->transitionWatchpointSet()));
         
-        value.filter(m_graph, node->structure());
+        filter(value, node->structure(), exitKind);
         m_haveStructures = true;
         node->setCanExit(true);
         break;
@@ -1398,7 +1434,7 @@ bool AbstractState::executeEffects(unsigned indexInBlock, Node* node)
         node->setCanExit(true); // Lies, but this is followed by operations (like GetByVal) that always exit, so there is no point in us trying to be clever here.
         switch (node->arrayMode().type()) {
         case Array::String:
-            forNode(node->child1()).filter(SpecString);
+            filter(node->child1(), SpecString);
             break;
         case Array::Int32:
         case Array::Double:
@@ -1407,34 +1443,34 @@ bool AbstractState::executeEffects(unsigned indexInBlock, Node* node)
         case Array::SlowPutArrayStorage:
             break;
         case Array::Arguments:
-            forNode(node->child1()).filter(SpecArguments);
+            filter(node->child1(), SpecArguments);
             break;
         case Array::Int8Array:
-            forNode(node->child1()).filter(SpecInt8Array);
+            filter(node->child1(), SpecInt8Array);
             break;
         case Array::Int16Array:
-            forNode(node->child1()).filter(SpecInt16Array);
+            filter(node->child1(), SpecInt16Array);
             break;
         case Array::Int32Array:
-            forNode(node->child1()).filter(SpecInt32Array);
+            filter(node->child1(), SpecInt32Array);
             break;
         case Array::Uint8Array:
-            forNode(node->child1()).filter(SpecUint8Array);
+            filter(node->child1(), SpecUint8Array);
             break;
         case Array::Uint8ClampedArray:
-            forNode(node->child1()).filter(SpecUint8ClampedArray);
+            filter(node->child1(), SpecUint8ClampedArray);
             break;
         case Array::Uint16Array:
-            forNode(node->child1()).filter(SpecUint16Array);
+            filter(node->child1(), SpecUint16Array);
             break;
         case Array::Uint32Array:
-            forNode(node->child1()).filter(SpecUint32Array);
+            filter(node->child1(), SpecUint32Array);
             break;
         case Array::Float32Array:
-            forNode(node->child1()).filter(SpecFloat32Array);
+            filter(node->child1(), SpecFloat32Array);
             break;
         case Array::Float64Array:
-            forNode(node->child1()).filter(SpecFloat64Array);
+            filter(node->child1(), SpecFloat64Array);
             break;
         default:
             RELEASE_ASSERT_NOT_REACHED();
@@ -1453,7 +1489,7 @@ bool AbstractState::executeEffects(unsigned indexInBlock, Node* node)
             || node->arrayMode().conversion() == Array::RageConvert);
         node->setCanExit(true);
         clobberStructures(indexInBlock);
-        forNode(node->child1()).filterArrayModes(node->arrayMode().arrayModesThatPassFiltering());
+        filterArrayModes(node->child1(), node->arrayMode().arrayModesThatPassFiltering());
         m_haveStructures = true;
         break;
     }
@@ -1465,7 +1501,7 @@ bool AbstractState::executeEffects(unsigned indexInBlock, Node* node)
             m_foundConstants = true;
         node->setCanExit(true);
         clobberStructures(indexInBlock);
-        value.filter(m_graph, set);
+        filter(value, set, BadIndexingType);
         m_haveStructures = true;
         break;
     }
@@ -1491,7 +1527,7 @@ bool AbstractState::executeEffects(unsigned indexInBlock, Node* node)
         }
         
         node->setCanExit(true); // Lies! We can do better.
-        forNode(node->child1()).filterByValue(node->function());
+        filterByValue(node->child1(), node->function(), BadFunction);
         break;
     }
         
@@ -1506,7 +1542,7 @@ bool AbstractState::executeEffects(unsigned indexInBlock, Node* node)
                 m_graph.identifiers()[node->identifierNumber()],
                 node->op() == PutByIdDirect);
             if (status.isSimpleReplace()) {
-                forNode(node->child1()).filter(m_graph, structure);
+                filter(node->child1(), structure, BadCache);
                 m_foundConstants = true;
                 break;
             }
@@ -1567,6 +1603,7 @@ bool AbstractState::executeEffects(unsigned indexInBlock, Node* node)
         break;
 
     case ForceOSRExit:
+    case ForwardForceOSRExit:
         node->setCanExit(true);
         m_isValid = false;
         break;
@@ -1594,10 +1631,10 @@ bool AbstractState::executeEffects(unsigned indexInBlock)
     return executeEffects(indexInBlock, m_block->at(indexInBlock));
 }
 
-bool AbstractState::execute(unsigned indexInBlock)
+bool AbstractState::execute(unsigned indexInBlock, ExecutionMode executionMode)
 {
     Node* node = m_block->at(indexInBlock);
-    if (!startExecuting(node))
+    if (!startExecuting(node, executionMode))
         return true;
     
     executeEdges(node);
@@ -1831,6 +1868,61 @@ void AbstractState::dump(PrintStream& out)
     }
 }
 
+FiltrationResult AbstractState::filter(
+    AbstractValue& value, const StructureSet& set, ExitKind exitKind)
+{
+    if (value.filter(m_graph, set) == FiltrationOK)
+        return FiltrationOK;
+    bail(exitKind);
+    return Contradiction;
+}
+    
+FiltrationResult AbstractState::filterArrayModes(
+    AbstractValue& value, ArrayModes arrayModes, ExitKind exitKind)
+{
+    if (value.filterArrayModes(arrayModes) == FiltrationOK)
+        return FiltrationOK;
+    bail(exitKind);
+    return Contradiction;
+}
+    
+FiltrationResult AbstractState::filter(
+    AbstractValue& value, SpeculatedType type, ExitKind exitKind)
+{
+    if (value.filter(type) == FiltrationOK)
+        return FiltrationOK;
+    bail(exitKind);
+    return Contradiction;
+}
+    
+FiltrationResult AbstractState::filterByValue(
+    AbstractValue& abstractValue, JSValue concreteValue, ExitKind exitKind)
+{
+    if (abstractValue.filterByValue(concreteValue) == FiltrationOK)
+        return FiltrationOK;
+    bail(exitKind);
+    return Contradiction;
+}
+
+void AbstractState::bail(ExitKind exitKind)
+{
+    ASSERT(m_currentNode);
+    m_isValid = false;
+    switch (m_executionMode) {
+    case StillConverging:
+        return;
+    case AfterConvergence: {
+        m_graph.baselineCodeBlockFor(m_currentNode->codeOrigin)->addFrequentExitSite(
+            FrequentExitSite(m_currentNode->codeOrigin.bytecodeIndex, exitKind));
+        return;
+    }
+    case CleanFiltration:
+        RELEASE_ASSERT_NOT_REACHED();
+        return;
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+}
+    
 } } // namespace JSC::DFG
 
 #endif // ENABLE(DFG_JIT)
index eeb97e8..dbc2623 100644 (file)
@@ -93,6 +93,21 @@ public:
         MergeToSuccessors
     };
     
+    enum ExecutionMode {
+        // If we encounter a contradiction, assume that this might just
+        // be because we haven't converged yet.
+        StillConverging,
+        
+        // If we encounter a contradition, assume that this contradiction
+        // is real and report it to the profiling infrastructure as if it
+        // happened at run time.
+        AfterConvergence,
+        
+        // Assume that there cannot be any contradictions other than
+        // ForceOSRExit because we have already cleaned the graph.
+        CleanFiltration
+    };
+    
     AbstractState(Graph&);
     
     ~AbstractState();
@@ -176,12 +191,12 @@ public:
     //
     // This is guaranteed to be equivalent to doing:
     //
-    // if (state.startExecuting(index)) {
+    // if (state.startExecuting(index, executionMode)) {
     //     state.executeEdges(index);
     //     result = state.executeEffects(index);
     // } else
     //     result = true;
-    bool execute(unsigned indexInBlock);
+    bool execute(unsigned indexInBlock, ExecutionMode);
     
     // Indicate the start of execution of the node. It resets any state in the node,
     // that is progressively built up by executeEdges() and executeEffects(). In
@@ -189,8 +204,8 @@ public:
     // startExecuting() and executeEdges()/Effects() whether the last run of the
     // analysis concluded that the node can exit, you should probably set that
     // information aside prior to calling startExecuting().
-    bool startExecuting(Node*);
-    bool startExecuting(unsigned indexInBlock);
+    bool startExecuting(Node*, ExecutionMode);
+    bool startExecuting(unsigned indexInBlock, ExecutionMode);
     
     // Abstractly execute the edges of the given node. This runs filterEdgeByUse()
     // on all edges of the node. You can skip this step, if you have already used
@@ -231,6 +246,38 @@ public:
     
     void dump(PrintStream& out);
     
+    template<typename T>
+    FiltrationResult filter(T node, const StructureSet& set, ExitKind exitKind)
+    {
+        return filter(forNode(node), set, exitKind);
+    }
+    
+    template<typename T>
+    FiltrationResult filterArrayModes(
+        T node, ArrayModes arrayModes, ExitKind exitKind = BadIndexingType)
+    {
+        return filterArrayModes(forNode(node), arrayModes, exitKind);
+    }
+    
+    template<typename T>
+    FiltrationResult filter(T node, SpeculatedType type, ExitKind exitKind = BadType)
+    {
+        return filter(forNode(node), type, exitKind);
+    }
+    
+    template<typename T>
+    FiltrationResult filterByValue(T node, JSValue value, ExitKind exitKind)
+    {
+        return filterByValue(forNode(node), value, exitKind);
+    }
+    
+    FiltrationResult filter(AbstractValue&, const StructureSet&, ExitKind);
+    FiltrationResult filterArrayModes(AbstractValue&, ArrayModes, ExitKind = BadIndexingType);
+    FiltrationResult filter(AbstractValue&, SpeculatedType, ExitKind = BadType);
+    FiltrationResult filterByValue(AbstractValue&, JSValue, ExitKind);
+    
+    void bail(ExitKind);
+    
 private:
     void clobberWorld(const CodeOrigin&, unsigned indexInBlock);
     void clobberCapturedVars(const CodeOrigin&);
@@ -273,7 +320,7 @@ private:
         } else
             edge.setProofStatus(IsProved);
         
-        value.filter(type);
+        filter(value, type);
     }
     
     void verifyEdge(Node*, Edge);
@@ -284,6 +331,9 @@ private:
     
     Operands<AbstractValue> m_variables;
     BasicBlock* m_block;
+    Node* m_currentNode;
+    ExecutionMode m_executionMode;
+    
     bool m_haveStructures;
     bool m_foundConstants;
     
index 8fe485e..fdc088b 100644 (file)
@@ -83,8 +83,11 @@ void AbstractValue::set(Graph& graph, Structure* structure)
     checkConsistency();
 }
 
-void AbstractValue::filter(Graph& graph, const StructureSet& other)
+FiltrationResult AbstractValue::filter(Graph& graph, const StructureSet& other)
 {
+    if (isClear())
+        return FiltrationOK;
+    
     // FIXME: This could be optimized for the common case of m_type not
     // having structures, array modes, or a specific value.
     // https://bugs.webkit.org/show_bug.cgi?id=109663
@@ -105,26 +108,29 @@ void AbstractValue::filter(Graph& graph, const StructureSet& other)
         
     filterArrayModesByType();
     filterValueByType();
-    normalizeClarity();
-    
-    checkConsistency();
+    return normalizeClarity();
 }
 
-void AbstractValue::filterArrayModes(ArrayModes arrayModes)
+FiltrationResult AbstractValue::filterArrayModes(ArrayModes arrayModes)
 {
     ASSERT(arrayModes);
     
+    if (isClear())
+        return FiltrationOK;
+    
     m_type &= SpecCell;
     m_arrayModes &= arrayModes;
-    normalizeClarity();
-    
-    checkConsistency();
+    return normalizeClarity();
 }
 
-void AbstractValue::filter(SpeculatedType type)
+FiltrationResult AbstractValue::filter(SpeculatedType type)
 {
+    if (isClear())
+        return FiltrationOK;
+    
     if (type == SpecTop)
-        return;
+        return isClear() ? Contradiction : FiltrationOK;
+    
     m_type &= type;
     
     // It's possible that prior to this filter() call we had, say, (Final, TOP), and
@@ -135,9 +141,15 @@ void AbstractValue::filter(SpeculatedType type)
     m_futurePossibleStructure.filter(m_type);
     filterArrayModesByType();
     filterValueByType();
-    normalizeClarity();
-    
-    checkConsistency();
+    return normalizeClarity();
+}
+
+FiltrationResult AbstractValue::filterByValue(JSValue value)
+{
+    FiltrationResult result = filter(speculationFromValue(value));
+    if (m_type)
+        m_value = value;
+    return result;
 }
 
 void AbstractValue::setFuturePossibleStructure(Graph& graph, Structure* structure)
@@ -200,13 +212,22 @@ bool AbstractValue::shouldBeClear() const
     return false;
 }
 
-void AbstractValue::normalizeClarity()
+FiltrationResult AbstractValue::normalizeClarity()
 {
     // It's useful to be able to quickly check if an abstract value is clear.
     // This normalizes everything to make that easy.
     
-    if (shouldBeClear())
+    FiltrationResult result;
+    
+    if (shouldBeClear()) {
         clear();
+        result = Contradiction;
+    } else
+        result = FiltrationOK;
+
+    checkConsistency();
+    
+    return result;
 }
 
 void AbstractValue::checkConsistency() const
index 1a367e8..bea723d 100644 (file)
@@ -31,6 +31,7 @@
 #if ENABLE(DFG_JIT)
 
 #include "ArrayProfile.h"
+#include "DFGFiltrationResult.h"
 #include "DFGStructureAbstractValue.h"
 #include "JSCell.h"
 #include "SpeculatedType.h"
@@ -182,18 +183,13 @@ struct AbstractValue {
         checkConsistency();
     }
     
-    void filter(Graph&, const StructureSet&);
+    FiltrationResult filter(Graph&, const StructureSet&);
     
-    void filterArrayModes(ArrayModes arrayModes);
+    FiltrationResult filterArrayModes(ArrayModes arrayModes);
     
-    void filter(SpeculatedType type);
+    FiltrationResult filter(SpeculatedType type);
     
-    void filterByValue(JSValue value)
-    {
-        filter(speculationFromValue(value));
-        if (m_type)
-            m_value = value;
-    }
+    FiltrationResult filterByValue(JSValue value);
     
     bool validateType(JSValue value) const
     {
@@ -364,7 +360,7 @@ private:
     void filterArrayModesByType();
     
     bool shouldBeClear() const;
-    void normalizeClarity();
+    FiltrationResult normalizeClarity();
 };
 
 } } // namespace JSC::DFG
index 3540421..cd9a891 100644 (file)
@@ -3443,7 +3443,6 @@ ByteCodeParser::InlineStackEntry::InlineStackEntry(
     : m_byteCodeParser(byteCodeParser)
     , m_codeBlock(codeBlock)
     , m_profiledBlock(profiledBlock)
-    , m_exitProfile(profiledBlock->exitProfile())
     , m_callsiteBlockHead(callsiteBlockHead)
     , m_returnValue(returnValueVR)
     , m_didReturn(false)
@@ -3453,6 +3452,7 @@ ByteCodeParser::InlineStackEntry::InlineStackEntry(
     {
         ConcurrentJITLocker locker(m_profiledBlock->m_lock);
         m_lazyOperands.initialize(locker, m_profiledBlock->lazyOperandValueProfiles());
+        m_exitProfile.initialize(locker, profiledBlock->exitProfile());
     }
     
     m_argumentPositions.resize(argumentCountIncludingThis);
index be40afe..1cfb10f 100644 (file)
@@ -101,7 +101,7 @@ private:
                 m_state.dump(WTF::dataFile());
                 dataLogF("\n");
             }
-            if (!m_state.execute(i)) {
+            if (!m_state.execute(i, AbstractState::StillConverging)) {
                 if (verbose)
                     dataLogF("         Expect OSR exit.\n");
                 break;
index 6133cc5..6f262dd 100644 (file)
@@ -72,7 +72,7 @@ bool mightInlineFunctionForConstruct(CodeBlock* codeBlock)
 inline void debugFail(CodeBlock* codeBlock, OpcodeID opcodeID, CapabilityLevel result)
 {
     if (Options::verboseCompilation() && !canCompile(result))
-        dataLog("Cannot compile code block ", *codeBlock, " because of opcode %s.\n", opcodeNames[opcodeID]);
+        dataLog("Cannot compile code block ", *codeBlock, " because of opcode ", opcodeNames[opcodeID], "\n");
 }
 
 // Opcode checking.
@@ -261,8 +261,11 @@ CapabilityLevel capabilityLevel(CodeBlock* codeBlock)
         switch (interpreter->getOpcodeID(instructionsBegin[bytecodeOffset].u.opcode)) {
 #define DEFINE_OP(opcode, length) \
         case opcode: { \
-            result = leastUpperBound(result, capabilityLevel(opcode, codeBlock, instructionsBegin + bytecodeOffset)); \
-            debugFail(codeBlock, opcode, result); \
+            CapabilityLevel newResult = leastUpperBound(result, capabilityLevel(opcode, codeBlock, instructionsBegin + bytecodeOffset)); \
+            if (newResult != result) { \
+                debugFail(codeBlock, opcode, newResult); \
+                result = newResult; \
+            } \
             bytecodeOffset += length; \
             break; \
         }
index ce01204..62fe632 100644 (file)
@@ -103,7 +103,7 @@ private:
                 else
                     set = node->structureSet();
                 if (value.m_currentKnownStructure.isSubsetOf(set)) {
-                    m_state.execute(indexInBlock); // Catch the fact that we may filter on cell.
+                    m_state.execute(indexInBlock, AbstractState::CleanFiltration); // Catch the fact that we may filter on cell.
                     node->convertToPhantom();
                     eliminated = true;
                     break;
@@ -112,7 +112,17 @@ private:
                 if (structureValue.isSubsetOf(set)
                     && structureValue.hasSingleton()) {
                     Structure* structure = structureValue.singleton();
-                    m_state.execute(indexInBlock); // Catch the fact that we may filter on cell.
+                    m_state.execute(indexInBlock, AbstractState::CleanFiltration); // Catch the fact that we may filter on cell.
+                    AdjacencyList children = node->children;
+                    children.removeEdge(0);
+                    if (!!children.child1()) {
+                        Node phantom(Phantom, node->codeOrigin, children);
+                        if (node->flags() & NodeExitsForward)
+                            phantom.mergeFlags(NodeExitsForward);
+                        m_insertionSet.insertNode(indexInBlock, SpecNone, phantom);
+                    }
+                    node->children.setChild2(Edge());
+                    node->children.setChild3(Edge());
                     node->convertToStructureTransitionWatchpoint(structure);
                     eliminated = true;
                     break;
@@ -171,7 +181,7 @@ private:
                 // Now before we do anything else, push the CFA forward over the GetById
                 // and make sure we signal to the loop that it should continue and not
                 // do any eliminations.
-                m_state.execute(indexInBlock);
+                m_state.execute(indexInBlock, AbstractState::CleanFiltration);
                 eliminated = true;
                 
                 if (needsWatchpoint) {
@@ -235,7 +245,7 @@ private:
                 // Now before we do anything else, push the CFA forward over the PutById
                 // and make sure we signal to the loop that it should continue and not
                 // do any eliminations.
-                m_state.execute(indexInBlock);
+                m_state.execute(indexInBlock, AbstractState::CleanFiltration);
                 eliminated = true;
                 
                 if (needsWatchpoint) {
@@ -324,7 +334,7 @@ private:
                 continue;
             }
                 
-            m_state.execute(indexInBlock);
+            m_state.execute(indexInBlock, AbstractState::CleanFiltration);
             if (!node->shouldGenerate() || m_state.didClobber() || node->hasConstant())
                 continue;
             JSValue value = m_state.forNode(node).value();
@@ -418,7 +428,7 @@ private:
     // to ensure soundness, we must paint unreachable code as such, by inserting an
     // unconditional ForceOSRExit wherever we find that a node would have always exited.
     // This will only happen in cases where we are making static speculations, or we're
-    // making totally wrong speculations due to imprecision on the prediction propagator.
+    // making totally wrong speculations due to imprecision in the prediction propagator.
     bool paintUnreachableCode(BlockIndex blockIndex)
     {
         bool changed = false;
@@ -430,7 +440,7 @@ private:
         m_state.beginBasicBlock(block);
         
         for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
-            m_state.execute(indexInBlock);
+            m_state.execute(indexInBlock, AbstractState::AfterConvergence);
             if (m_state.isValid())
                 continue;
             
@@ -440,12 +450,15 @@ private:
             case Throw:
             case ThrowReferenceError:
             case ForceOSRExit:
+            case ForwardForceOSRExit:
                 // Do nothing. These nodes will already do the right thing.
                 break;
                 
             default:
                 m_insertionSet.insertNode(
-                    indexInBlock, SpecNone, ForceOSRExit, node->codeOrigin);
+                    indexInBlock, SpecNone,
+                    (node->flags() & NodeExitsForward) ? ForwardForceOSRExit : ForceOSRExit,
+                    node->codeOrigin);
                 changed = true;
                 break;
             }
diff --git a/Source/JavaScriptCore/dfg/DFGFiltrationResult.h b/Source/JavaScriptCore/dfg/DFGFiltrationResult.h
new file mode 100644 (file)
index 0000000..8c80cea
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef DFGFiltrationResult_h
+#define DFGFiltrationResult_h
+
+#include <wtf/Platform.h>
+
+#if ENABLE(DFG_JIT)
+
+namespace JSC { namespace DFG {
+
+enum FiltrationResult {
+    FiltrationOK,
+    Contradiction
+};
+
+} } // namespace JSC::DFG
+
+#endif // ENABLE(DFG_JIT)
+
+#endif // DFGFiltrationResult_h
+
index a81d6c6..71c0d85 100644 (file)
@@ -881,6 +881,7 @@ private:
         case GarbageValue:
         case CountExecution:
         case ForceOSRExit:
+        case ForwardForceOSRExit:
         case CheckWatchdogTimer:
             break;
 #else
index 6837a1d..826defb 100644 (file)
@@ -253,7 +253,7 @@ void Graph::dump(PrintStream& out, const char* prefix, Node* node)
         out.print(" = ", value);
     }
     if (op == WeakJSConstant)
-        out.print(comma, RawPointer(node->weakConstant()));
+        out.print(comma, RawPointer(node->weakConstant()), " (structure: ", RawPointer(node->weakConstant()->structure()), ")");
     if (node->isBranch() || node->isJump())
         out.print(comma, "T:#", node->takenBlockIndex());
     if (node->isBranch())
index f28ebc7..0cb4939 100644 (file)
@@ -267,6 +267,7 @@ namespace JSC { namespace DFG {
     /* this point, but execution does continue in the basic block - just in a */\
     /* different compiler. */\
     macro(ForceOSRExit, NodeMustGenerate) \
+    macro(ForwardForceOSRExit, NodeMustGenerate | NodeExitsForward) \
     \
     /* Checks the watchdog timer. If the timer has fired, we OSR exit to the */ \
     /* baseline JIT to redo the watchdog timer check, and service the timer. */ \
index 58d99c1..b982617 100644 (file)
@@ -37,16 +37,9 @@ namespace JSC { namespace DFG {
 
 bool OSRExitBase::considerAddingAsFrequentExitSiteSlow(CodeBlock* profiledCodeBlock)
 {
-    FrequentExitSite exitSite;
-    
-    if (m_kind == ArgumentsEscaped) {
-        // Count this one globally. It doesn't matter where in the code block the arguments excaped;
-        // the fact that they did is not associated with any particular instruction.
-        exitSite = FrequentExitSite(m_kind);
-    } else
-        exitSite = FrequentExitSite(m_codeOriginForExitProfile.bytecodeIndex, m_kind);
-    
-    return baselineCodeBlockForOriginAndBaselineCodeBlock(m_codeOriginForExitProfile, profiledCodeBlock)->addFrequentExitSite(exitSite);
+    return baselineCodeBlockForOriginAndBaselineCodeBlock(
+        m_codeOriginForExitProfile, profiledCodeBlock)->addFrequentExitSite(
+            FrequentExitSite(m_codeOriginForExitProfile.bytecodeIndex, m_kind));
 }
 
 bool OSRExitBase::doSearchForForwardConversion(
index 7f64861..e054e19 100644 (file)
@@ -58,7 +58,7 @@ struct OSRExitBase {
 
     bool considerAddingAsFrequentExitSite(CodeBlock* profiledCodeBlock)
     {
-        if (!m_count || !exitKindIsCountable(m_kind))
+        if (!m_count)
             return false;
         return considerAddingAsFrequentExitSiteSlow(profiledCodeBlock);
     }
index 5cf4674..479e1ad 100644 (file)
@@ -522,6 +522,7 @@ private:
         case CheckHasInstance:
         case ThrowReferenceError:
         case ForceOSRExit:
+        case ForwardForceOSRExit:
         case SetArgument:
         case CheckStructure:
         case CheckExecutable:
index a172f3e..f48b9b3 100644 (file)
@@ -262,7 +262,7 @@ void SpeculativeJIT::terminateSpeculativeExecution(ExitKind kind, JSValueRegs js
 void SpeculativeJIT::backwardTypeCheck(JSValueSource source, Edge edge, SpeculatedType typesPassedThrough, MacroAssembler::Jump jumpToFail)
 {
     ASSERT(needsTypeCheck(edge, typesPassedThrough));
-    m_state.forNode(edge).filter(typesPassedThrough);
+    m_state.filter(edge, typesPassedThrough);
     backwardSpeculationCheck(BadType, source, edge.node(), jumpToFail);
 }
 
@@ -1591,6 +1591,13 @@ void SpeculativeJIT::compileInlineStart(Node* node)
     }
 }
 
+void SpeculativeJIT::bail()
+{
+    m_compileOkay = true;
+    m_jit.breakpoint();
+    clearGenerationInfo();
+}
+
 void SpeculativeJIT::compile(BasicBlock& block)
 {
     ASSERT(m_compileOkay);
@@ -1602,9 +1609,7 @@ void SpeculativeJIT::compile(BasicBlock& block)
         // Don't generate code for basic blocks that are unreachable according to CFA.
         // But to be sure that nobody has generated a jump to this block, drop in a
         // breakpoint here.
-#if !ASSERT_DISABLED
         m_jit.breakpoint();
-#endif
         return;
     }
 
@@ -1668,10 +1673,12 @@ void SpeculativeJIT::compile(BasicBlock& block)
 
     for (m_indexInBlock = 0; m_indexInBlock < block.size(); ++m_indexInBlock) {
         m_currentNode = block[m_indexInBlock];
-#if !ASSERT_DISABLED
+        
+        // If there was a contradiction then the constant folder ought to have caught it.
+        RELEASE_ASSERT(m_state.isValid());
+        
         m_canExit = m_currentNode->canExit();
-#endif
-        bool shouldExecuteEffects = m_state.startExecuting(m_currentNode);
+        bool shouldExecuteEffects = m_state.startExecuting(m_currentNode, AbstractState::CleanFiltration);
         m_jit.setForNode(m_currentNode);
         m_codeOriginForOSR = m_currentNode->codeOrigin;
         if (!m_currentNode->shouldGenerate()) {
@@ -1733,8 +1740,7 @@ void SpeculativeJIT::compile(BasicBlock& block)
             
             compile(m_currentNode);
             if (!m_compileOkay) {
-                m_compileOkay = true;
-                clearGenerationInfo();
+                bail();
                 return;
             }
             
@@ -4210,7 +4216,7 @@ void SpeculativeJIT::compileToStringOnCell(Node* node)
         GPRReg resultGPR = result.gpr();
         
         speculateStringObject(node->child1(), op1GPR);
-        m_state.forNode(node->child1()).filter(SpecStringObject);
+        m_state.filter(node->child1(), SpecStringObject);
         m_jit.loadPtr(JITCompiler::Address(op1GPR, JSWrapperObject::internalValueCellOffset()), resultGPR);
         cellResult(resultGPR, node);
         break;
@@ -4233,7 +4239,7 @@ void SpeculativeJIT::compileToStringOnCell(Node* node)
         m_jit.move(op1GPR, resultGPR);
         done.link(&m_jit);
         
-        m_state.forNode(node->child1()).filter(SpecString | SpecStringObject);
+        m_state.filter(node->child1(), SpecString | SpecStringObject);
         
         cellResult(resultGPR, node);
         break;
@@ -4454,7 +4460,7 @@ void SpeculativeJIT::speculateStringObject(Edge edge)
         return;
     
     speculateStringObject(edge, gpr);
-    m_state.forNode(edge).filter(SpecStringObject);
+    m_state.filter(edge, SpecStringObject);
 }
 
 void SpeculativeJIT::speculateStringOrStringObject(Edge edge)
@@ -4479,7 +4485,7 @@ void SpeculativeJIT::speculateStringOrStringObject(Edge edge)
     
     isString.link(&m_jit);
     
-    m_state.forNode(edge).filter(SpecString | SpecStringObject);
+    m_state.filter(edge, SpecString | SpecStringObject);
 }
 
 void SpeculativeJIT::speculateNotCell(Edge edge)
index bb2aa10..07a4d7b 100644 (file)
@@ -335,6 +335,7 @@ public:
     
     void compile(Node*);
     void noticeOSRBirth(Node*);
+    void bail();
     void compile(BasicBlock&);
 
     void checkArgumentTypes();
@@ -2202,9 +2203,7 @@ public:
     BlockIndex m_block;
     Node* m_currentNode;
     SpeculationDirection m_speculationDirection;
-#if !ASSERT_DISABLED
     bool m_canExit;
-#endif
     unsigned m_indexInBlock;
     // Virtual and physical register maps.
     Vector<GenerationInfo, 32> m_generationInfo;
index e9c3d2a..6f24af2 100644 (file)
@@ -862,7 +862,7 @@ GPRReg SpeculativeJIT::fillSpeculateIntInternal(Edge edge, DataFormat& returnFor
     AbstractValue& value = m_state.forNode(edge);
     SpeculatedType type = value.m_type;
     ASSERT(edge.useKind() != KnownInt32Use || !(value.m_type & ~SpecInt32));
-    value.filter(SpecInt32);
+    m_state.filter(value, SpecInt32);
     VirtualRegister virtualRegister = edge->virtualRegister();
     GenerationInfo& info = m_generationInfo[virtualRegister];
 
@@ -963,7 +963,7 @@ FPRReg SpeculativeJIT::fillSpeculateDouble(Edge edge)
     AbstractValue& value = m_state.forNode(edge);
     SpeculatedType type = value.m_type;
     ASSERT(edge.useKind() != KnownNumberUse || !(value.m_type & ~SpecNumber));
-    value.filter(SpecNumber);
+    m_state.filter(value, SpecNumber);
     VirtualRegister virtualRegister = edge->virtualRegister();
     GenerationInfo& info = m_generationInfo[virtualRegister];
 
@@ -1100,7 +1100,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(Edge edge)
     AbstractValue& value = m_state.forNode(edge);
     SpeculatedType type = value.m_type;
     ASSERT((edge.useKind() != KnownCellUse && edge.useKind() != KnownStringUse) || !(value.m_type & ~SpecCell));
-    value.filter(SpecCell);
+    m_state.filter(value, SpecCell);
     VirtualRegister virtualRegister = edge->virtualRegister();
     GenerationInfo& info = m_generationInfo[virtualRegister];
 
@@ -1177,7 +1177,7 @@ GPRReg SpeculativeJIT::fillSpeculateBoolean(Edge edge)
 #endif
     AbstractValue& value = m_state.forNode(edge);
     SpeculatedType type = value.m_type;
-    value.filter(SpecBoolean);
+    m_state.filter(value, SpecBoolean);
     VirtualRegister virtualRegister = edge->virtualRegister();
     GenerationInfo& info = m_generationInfo[virtualRegister];
 
@@ -4907,7 +4907,8 @@ void SpeculativeJIT::compile(Node* node)
         CRASH();
         break;
 
-    case ForceOSRExit: {
+    case ForceOSRExit:
+    case ForwardForceOSRExit: {
         terminateSpeculativeExecution(InadequateCoverage, JSValueRegs(), 0);
         break;
     }
index f9f3bf7..324c5cc 100644 (file)
@@ -822,7 +822,7 @@ GPRReg SpeculativeJIT::fillSpeculateIntInternal(Edge edge, DataFormat& returnFor
     AbstractValue& value = m_state.forNode(edge);
     SpeculatedType type = value.m_type;
     ASSERT(edge.useKind() != KnownInt32Use || !(value.m_type & ~SpecInt32));
-    value.filter(SpecInt32);
+    m_state.filter(value, SpecInt32);
     VirtualRegister virtualRegister = edge->virtualRegister();
     GenerationInfo& info = m_generationInfo[virtualRegister];
 
@@ -972,7 +972,7 @@ FPRReg SpeculativeJIT::fillSpeculateDouble(Edge edge)
     AbstractValue& value = m_state.forNode(edge);
     SpeculatedType type = value.m_type;
     ASSERT(edge.useKind() != KnownNumberUse || !(value.m_type & ~SpecNumber));
-    value.filter(SpecNumber);
+    m_state.filter(value, SpecNumber);
     VirtualRegister virtualRegister = edge->virtualRegister();
     GenerationInfo& info = m_generationInfo[virtualRegister];
 
@@ -1128,7 +1128,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(Edge edge)
     AbstractValue& value = m_state.forNode(edge);
     SpeculatedType type = value.m_type;
     ASSERT((edge.useKind() != KnownCellUse && edge.useKind() != KnownStringUse) || !(value.m_type & ~SpecCell));
-    value.filter(SpecCell);
+    m_state.filter(value, SpecCell);
     VirtualRegister virtualRegister = edge->virtualRegister();
     GenerationInfo& info = m_generationInfo[virtualRegister];
 
@@ -1205,7 +1205,7 @@ GPRReg SpeculativeJIT::fillSpeculateBoolean(Edge edge)
 #endif
     AbstractValue& value = m_state.forNode(edge);
     SpeculatedType type = value.m_type;
-    value.filter(SpecBoolean);
+    m_state.filter(value, SpecBoolean);
     VirtualRegister virtualRegister = edge->virtualRegister();
     GenerationInfo& info = m_generationInfo[virtualRegister];
 
@@ -4757,7 +4757,8 @@ void SpeculativeJIT::compile(Node* node)
         CRASH();
         break;
 
-    case ForceOSRExit: {
+    case ForceOSRExit:
+    case ForwardForceOSRExit: {
         terminateSpeculativeExecution(InadequateCoverage, JSValueRegs(), 0);
         break;
     }
index b73604f..482603f 100644 (file)
@@ -78,6 +78,7 @@ inline bool canCompile(Node* node)
     case CompareStrictEqConstant:
     case Jump:
     case ForceOSRExit:
+    case ForwardForceOSRExit:
         // These are OK.
         break;
     case GetArrayLength:
index 2af9b20..129815f 100644 (file)
@@ -265,7 +265,7 @@ private:
         if (verboseCompilationEnabled())
             dataLog("Lowering ", m_node, "\n");
         
-        bool shouldExecuteEffects = m_state.startExecuting(m_node);
+        bool shouldExecuteEffects = m_state.startExecuting(m_node, AbstractState::CleanFiltration);
         
         m_direction = (m_node->flags() & NodeExitsForward) ? ForwardSpeculation : BackwardSpeculation;
         
@@ -418,6 +418,7 @@ private:
             compileReturn();
             break;
         case ForceOSRExit:
+        case ForwardForceOSRExit:
             compileForceOSRExit();
             break;
         default:
@@ -1619,7 +1620,7 @@ private:
             return;
         ASSERT(mayHaveTypeCheck(highValue.useKind()));
         appendOSRExit(BadType, lowValue, highValue.node(), failCondition, direction, recovery);
-        m_state.forNode(highValue).filter(typesPassedThrough);
+        m_state.filter(highValue, typesPassedThrough);
     }
     
     LValue lowInt32(Edge edge, OperandSpeculationMode mode = AutomaticOperandSpeculation)