]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - dfg/DFGAbstractInterpreterInlines.h
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / dfg / DFGAbstractInterpreterInlines.h
index 0e9419444c672a165d73e9bd9446f34257072177..21ee0e4ac2f1656e9a73b7661fb33c8afb3e38da 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -30,6 +30,9 @@
 
 #include "DFGAbstractInterpreter.h"
 #include "GetByIdStatus.h"
+#include "GetterSetter.h"
+#include "JITOperations.h"
+#include "MathCommon.h"
 #include "Operations.h"
 #include "PutByIdStatus.h"
 #include "StringObject.h"
@@ -42,6 +45,8 @@ AbstractInterpreter<AbstractStateType>::AbstractInterpreter(Graph& graph, Abstra
     , m_graph(graph)
     , m_state(state)
 {
+    if (m_graph.m_form == SSA)
+        m_phiChildren = std::make_unique<PhiChildren>(m_graph);
 }
 
 template<typename AbstractStateType>
@@ -62,11 +67,17 @@ AbstractInterpreter<AbstractStateType>::booleanResult(
     }
 
     // Next check if we can fold because we know that the source is an object or string and does not equal undefined.
-    if (isCellSpeculation(value.m_type)
-        && value.m_currentKnownStructure.hasSingleton()) {
-        Structure* structure = value.m_currentKnownStructure.singleton();
-        if (!structure->masqueradesAsUndefined(m_codeBlock->globalObjectFor(node->origin.semantic))
-            && structure->typeInfo().type() != StringType)
+    if (isCellSpeculation(value.m_type) && !value.m_structure.isTop()) {
+        bool allTrue = true;
+        for (unsigned i = value.m_structure.size(); i--;) {
+            Structure* structure = value.m_structure[i];
+            if (structure->masqueradesAsUndefined(m_codeBlock->globalObjectFor(node->origin.semantic))
+                || structure->typeInfo().type() == StringType) {
+                allTrue = false;
+                break;
+            }
+        }
+        if (allTrue)
             return DefinitelyTrue;
     }
     
@@ -74,22 +85,12 @@ AbstractInterpreter<AbstractStateType>::booleanResult(
 }
 
 template<typename AbstractStateType>
-bool AbstractInterpreter<AbstractStateType>::startExecuting(Node* node)
+void AbstractInterpreter<AbstractStateType>::startExecuting()
 {
     ASSERT(m_state.block());
     ASSERT(m_state.isValid());
     
     m_state.setDidClobber(false);
-    
-    node->setCanExit(false);
-    
-    return node->shouldGenerate();
-}
-
-template<typename AbstractStateType>
-bool AbstractInterpreter<AbstractStateType>::startExecuting(unsigned indexInBlock)
-{
-    return startExecuting(m_state.block()->at(indexInBlock));
 }
 
 template<typename AbstractStateType>
@@ -105,9 +106,12 @@ void AbstractInterpreter<AbstractStateType>::executeEdges(unsigned indexInBlock)
 }
 
 template<typename AbstractStateType>
-void AbstractInterpreter<AbstractStateType>::verifyEdge(Node*, Edge edge)
+void AbstractInterpreter<AbstractStateType>::verifyEdge(Node* node, Edge edge)
 {
-    RELEASE_ASSERT(!(forNode(edge).m_type & ~typeFilterFor(edge.useKind())));
+    if (!(forNode(edge).m_type & ~typeFilterFor(edge.useKind())))
+        return;
+    
+    DFG_CRASH(m_graph, node, toCString("Edge verification error: ", node, "->", edge, " was expected to have type ", SpeculationDump(typeFilterFor(edge.useKind())), " but has type ", SpeculationDump(forNode(edge).m_type), " (", forNode(edge).m_type, ")").data());
 }
 
 template<typename AbstractStateType>
@@ -127,51 +131,39 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
     switch (node->op()) {
     case JSConstant:
     case DoubleConstant:
-    case Int52Constant:
-    case WeakJSConstant:
-    case PhantomArguments: {
-        setBuiltInConstant(node, m_graph.valueOfJSConstant(node));
+    case Int52Constant: {
+        setBuiltInConstant(node, *node->constant());
         break;
     }
         
     case Identity: {
         forNode(node) = forNode(node->child1());
-        break;
-    }
-        
-    case GetArgument: {
-        ASSERT(m_graph.m_form == SSA);
-        VariableAccessData* variable = node->variableAccessData();
-        AbstractValue& value = m_state.variables().operand(variable->local().offset());
-        ASSERT(value.isHeapTop());
-        FiltrationResult result =
-            value.filter(typeFilterFor(useKindFor(variable->flushFormat())));
-        ASSERT_UNUSED(result, result == FiltrationOK);
-        forNode(node) = value;
+        if (forNode(node).value())
+            m_state.setFoundConstants(true);
         break;
     }
         
     case ExtractOSREntryLocal: {
-        if (!(node->unlinkedLocal().isArgument())
-            && m_graph.m_lazyVars.get(node->unlinkedLocal().toLocal())) {
-            // This is kind of pessimistic - we could know in some cases that the
-            // DFG code at the point of the OSR had already initialized the lazy
-            // variable. But maybe this is fine, since we're inserting OSR
-            // entrypoints very early in the pipeline - so any lazy initializations
-            // ought to be hoisted out anyway.
-            forNode(node).makeBytecodeTop();
-        } else
-            forNode(node).makeHeapTop();
+        forNode(node).makeBytecodeTop();
         break;
     }
             
     case GetLocal: {
         VariableAccessData* variableAccessData = node->variableAccessData();
         AbstractValue value = m_state.variables().operand(variableAccessData->local().offset());
-        if (!variableAccessData->isCaptured()) {
-            if (value.isClear())
-                node->setCanExit(true);
-        }
+        // The value in the local should already be checked.
+        DFG_ASSERT(m_graph, node, value.isType(typeFilterFor(variableAccessData->flushFormat())));
+        if (value.value())
+            m_state.setFoundConstants(true);
+        forNode(node) = value;
+        break;
+    }
+        
+    case GetStack: {
+        StackAccessData* data = node->stackAccessData();
+        AbstractValue value = m_state.variables().operand(data->local);
+        // The value in the local should already be checked.
+        DFG_ASSERT(m_graph, node, value.isType(typeFilterFor(data->format)));
         if (value.value())
             m_state.setFoundConstants(true);
         forNode(node) = value;
@@ -187,7 +179,12 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
     }
         
     case SetLocal: {
-        m_state.variables().operand(node->local().offset()) = forNode(node->child1());
+        m_state.variables().operand(node->local()) = forNode(node->child1());
+        break;
+    }
+        
+    case PutStack: {
+        m_state.variables().operand(node->stackAccessData()->local) = forNode(node->child1());
         break;
     }
         
@@ -198,10 +195,31 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         break;
     }
         
+    case KillStack: {
+        // This is just a hint telling us that the OSR state of the local is no longer inside the
+        // flushed data.
+        break;
+    }
+        
     case SetArgument:
-        // Assert that the state of arguments has been set.
-        ASSERT(!m_state.block()->valuesAtHead.operand(node->local()).isClear());
+        // Assert that the state of arguments has been set. SetArgument means that someone set
+        // the argument values out-of-band, and currently this always means setting to a
+        // non-clear value.
+        ASSERT(!m_state.variables().operand(node->local()).isClear());
         break;
+        
+    case LoadVarargs:
+    case ForwardVarargs: {
+        // FIXME: ForwardVarargs should check if the count becomes known, and if it does, it should turn
+        // itself into a straight-line sequence of GetStack/PutStack.
+        // https://bugs.webkit.org/show_bug.cgi?id=143071
+        clobberWorld(node->origin.semantic, clobberLimit);
+        LoadVarargsData* data = node->loadVarargsData();
+        m_state.variables().operand(data->count).setType(SpecInt32);
+        for (unsigned i = data->limit - 1; i--;)
+            m_state.variables().operand(data->start.offset() + i).makeHeapTop();
+        break;
+    }
             
     case BitAnd:
     case BitOr:
@@ -239,6 +257,14 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             }
             break;
         }
+        
+        if (node->op() == BitAnd
+            && (isBoolInt32Speculation(forNode(node->child1()).m_type) ||
+                isBoolInt32Speculation(forNode(node->child2()).m_type))) {
+            forNode(node).setType(SpecBoolInt32);
+            break;
+        }
+        
         forNode(node).setType(SpecInt32);
         break;
     }
@@ -262,7 +288,6 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             }
         }
         forNode(node).setType(SpecInt32);
-        node->setCanExit(true);
         break;
     }
         
@@ -272,7 +297,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             if (concreteValue.isBoolean())
                 setConstant(node, jsNumber(concreteValue.asBoolean()));
             else
-                setConstant(node, concreteValue);
+                setConstant(node, *m_graph.freeze(concreteValue));
             break;
         }
         AbstractValue& value = forNode(node);
@@ -280,7 +305,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         if (node->child1().useKind() == UntypedUse && !(value.m_type & ~SpecBoolean))
             m_state.setFoundConstants(true);
         if (value.m_type & SpecBoolean) {
-            value.merge(SpecInt32);
+            value.merge(SpecBoolInt32);
             value.filter(~SpecBoolean);
         }
         break;
@@ -296,7 +321,6 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 break;
             }
         }
-        node->setCanExit(true);
         forNode(node).setType(SpecInt32);
         break;
     }
@@ -321,6 +345,11 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             }
         }
         
+        if (isBooleanSpeculation(forNode(node->child1()).m_type)) {
+            forNode(node).setType(SpecBoolInt32);
+            break;
+        }
+        
         forNode(node).setType(SpecInt32);
         break;
     }
@@ -331,8 +360,32 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             setConstant(node, jsDoubleNumber(child.asNumber()));
             break;
         }
-        forNode(node).setType(forNode(node->child1()).m_type);
-        forNode(node).fixTypeForRepresentation(node);
+
+        SpeculatedType type = forNode(node->child1()).m_type;
+        switch (node->child1().useKind()) {
+        case NotCellUse: {
+            if (type & SpecOther) {
+                type &= ~SpecOther;
+                type |= SpecDoublePureNaN | SpecBoolInt32; // Null becomes zero, undefined becomes NaN.
+            }
+            if (type & SpecBoolean) {
+                type &= ~SpecBoolean;
+                type |= SpecBoolInt32; // True becomes 1, false becomes 0.
+            }
+            type &= SpecBytecodeNumber;
+            break;
+        }
+
+        case Int52RepUse:
+        case NumberUse:
+        case RealNumberUse:
+            break;
+
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+        }
+        forNode(node).setType(type);
+        forNode(node).fixTypeForRepresentation(m_graph, node);
         break;
     }
         
@@ -354,15 +407,15 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             break;
         }
         
-        forNode(node).setType(forNode(node->child1()).m_type & ~SpecDoubleImpureNaN);
-        forNode(node).fixTypeForRepresentation(node);
+        forNode(node).setType(m_graph, forNode(node->child1()).m_type & ~SpecDoubleImpureNaN);
+        forNode(node).fixTypeForRepresentation(m_graph, node);
         break;
     }
         
     case ValueAdd: {
         ASSERT(node->binaryUseKind() == UntypedUse);
         clobberWorld(node->origin.semantic, clobberLimit);
-        forNode(node).setType(SpecString | SpecBytecodeNumber);
+        forNode(node).setType(m_graph, SpecString | SpecBytecodeNumber);
         break;
     }
         
@@ -383,8 +436,6 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 }
             }
             forNode(node).setType(SpecInt32);
-            if (shouldCheckOverflow(node->arithMode()))
-                node->setCanExit(true);
             break;
         case Int52RepUse:
             if (left && right && left.isMachineInt() && right.isMachineInt()) {
@@ -395,9 +446,6 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 }
             }
             forNode(node).setType(SpecMachineInt);
-            if (!forNode(node->child1()).isType(SpecInt32)
-                || !forNode(node->child2()).isType(SpecInt32))
-                node->setCanExit(true);
             break;
         case DoubleRepUse:
             if (left && right && left.isNumber() && right.isNumber()) {
@@ -414,9 +462,19 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         }
         break;
     }
-        
+
+    case ArithClz32: {
+        JSValue operand = forNode(node->child1()).value();
+        if (operand && operand.isNumber()) {
+            uint32_t value = toUInt32(operand.asNumber());
+            setConstant(node, jsNumber(clz32(value)));
+            break;
+        }
+        forNode(node).setType(SpecInt32);
+        break;
+    }
+
     case MakeRope: {
-        node->setCanExit(true);
         forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get());
         break;
     }
@@ -438,8 +496,6 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 }
             }
             forNode(node).setType(SpecInt32);
-            if (shouldCheckOverflow(node->arithMode()))
-                node->setCanExit(true);
             break;
         case Int52RepUse:
             if (left && right && left.isMachineInt() && right.isMachineInt()) {
@@ -450,9 +506,6 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 }
             }
             forNode(node).setType(SpecMachineInt);
-            if (!forNode(node->child1()).isType(SpecInt32)
-                || !forNode(node->child2()).isType(SpecInt32))
-                node->setCanExit(true);
             break;
         case DoubleRepUse:
             if (left && right && left.isNumber() && right.isNumber()) {
@@ -491,8 +544,6 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 }
             }
             forNode(node).setType(SpecInt32);
-            if (shouldCheckOverflow(node->arithMode()))
-                node->setCanExit(true);
             break;
         case Int52RepUse:
             if (child && child.isMachineInt()) {
@@ -508,10 +559,6 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 }
             }
             forNode(node).setType(SpecMachineInt);
-            if (m_state.forNode(node->child1()).couldBeType(SpecInt52))
-                node->setCanExit(true);
-            if (shouldCheckNegativeZero(node->arithMode()))
-                node->setCanExit(true);
             break;
         case DoubleRepUse:
             if (child && child.isNumber()) {
@@ -549,8 +596,6 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 }
             }
             forNode(node).setType(SpecInt32);
-            if (shouldCheckOverflow(node->arithMode()))
-                node->setCanExit(true);
             break;
         case Int52RepUse:
             if (left && right && left.isMachineInt() && right.isMachineInt()) {
@@ -564,7 +609,6 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 }
             }
             forNode(node).setType(SpecMachineInt);
-            node->setCanExit(true);
             break;
         case DoubleRepUse:
             if (left && right && left.isNumber() && right.isNumber()) {
@@ -600,7 +644,6 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 }
             }
             forNode(node).setType(SpecInt32);
-            node->setCanExit(true);
             break;
         case DoubleRepUse:
             if (left && right && left.isNumber() && right.isNumber()) {
@@ -636,7 +679,6 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 }
             }
             forNode(node).setType(SpecInt32);
-            node->setCanExit(true);
             break;
         case DoubleRepUse:
             if (left && right && left.isNumber() && right.isNumber()) {
@@ -664,7 +706,6 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 break;
             }
             forNode(node).setType(SpecInt32);
-            node->setCanExit(true);
             break;
         case DoubleRepUse:
             if (left && right && left.isNumber() && right.isNumber()) {
@@ -694,7 +735,6 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 break;
             }
             forNode(node).setType(SpecInt32);
-            node->setCanExit(true);
             break;
         case DoubleRepUse:
             if (left && right && left.isNumber() && right.isNumber()) {
@@ -726,11 +766,10 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 }
             }
             forNode(node).setType(SpecInt32);
-            node->setCanExit(true);
             break;
         case DoubleRepUse:
             if (child && child.isNumber()) {
-                setConstant(node, jsDoubleNumber(child.asNumber()));
+                setConstant(node, jsDoubleNumber(fabs(child.asNumber())));
                 break;
             }
             forNode(node).setType(typeOfDoubleAbs(forNode(node->child1()).m_type));
@@ -741,6 +780,54 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         }
         break;
     }
+
+    case ArithPow: {
+        JSValue childY = forNode(node->child2()).value();
+        if (childY && childY.isNumber()) {
+            if (!childY.asNumber()) {
+                setConstant(node, jsDoubleNumber(1));
+                break;
+            }
+
+            JSValue childX = forNode(node->child1()).value();
+            if (childX && childX.isNumber()) {
+                setConstant(node, jsDoubleNumber(operationMathPow(childX.asNumber(), childY.asNumber())));
+                break;
+            }
+        }
+        forNode(node).setType(typeOfDoublePow(forNode(node->child1()).m_type, forNode(node->child2()).m_type));
+        break;
+    }
+
+    case ArithRound: {
+        JSValue operand = forNode(node->child1()).value();
+        if (operand && operand.isNumber()) {
+            double roundedValue = jsRound(operand.asNumber());
+
+            if (producesInteger(node->arithRoundingMode())) {
+                int32_t roundedValueAsInt32 = static_cast<int32_t>(roundedValue);
+                if (roundedValueAsInt32 == roundedValue) {
+                    if (shouldCheckNegativeZero(node->arithRoundingMode())) {
+                        if (roundedValueAsInt32 || !std::signbit(roundedValue)) {
+                            setConstant(node, jsNumber(roundedValueAsInt32));
+                            break;
+                        }
+                    } else {
+                        setConstant(node, jsNumber(roundedValueAsInt32));
+                        break;
+                    }
+                }
+            } else {
+                setConstant(node, jsDoubleNumber(roundedValue));
+                break;
+            }
+        }
+        if (producesInteger(node->arithRoundingMode()))
+            forNode(node).setType(SpecInt32);
+        else
+            forNode(node).setType(typeOfDoubleRounding(forNode(node->child1()).m_type));
+        break;
+    }
             
     case ArithSqrt: {
         JSValue child = forNode(node->child1()).value();
@@ -758,7 +845,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             setConstant(node, jsDoubleNumber(static_cast<float>(child.asNumber())));
             break;
         }
-        forNode(node).setType(typeOfDoubleFRound(forNode(node->child1()).m_type));
+        forNode(node).setType(typeOfDoubleRounding(forNode(node->child1()).m_type));
         break;
     }
         
@@ -781,6 +868,16 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         forNode(node).setType(typeOfDoubleUnaryOp(forNode(node->child1()).m_type));
         break;
     }
+
+    case ArithLog: {
+        JSValue child = forNode(node->child1()).value();
+        if (child && child.isNumber()) {
+            setConstant(node, jsDoubleNumber(log(child.asNumber())));
+            break;
+        }
+        forNode(node).setType(typeOfDoubleUnaryOp(forNode(node->child1()).m_type));
+        break;
+    }
             
     case LogicalNot: {
         switch (booleanResult(node, forNode(node->child1()))) {
@@ -791,20 +888,6 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             setConstant(node, jsBoolean(true));
             break;
         default:
-            switch (node->child1().useKind()) {
-            case BooleanUse:
-            case Int32Use:
-            case DoubleRepUse:
-            case UntypedUse:
-            case StringUse:
-                break;
-            case ObjectOrOtherUse:
-                node->setCanExit(true);
-                break;
-            default:
-                RELEASE_ASSERT_NOT_REACHED();
-                break;
-            }
             forNode(node).setType(SpecBoolean);
             break;
         }
@@ -816,35 +899,59 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
     case IsNumber:
     case IsString:
     case IsObject:
+    case IsObjectOrNull:
     case IsFunction: {
-        node->setCanExit(
-            node->op() == IsUndefined
-            && m_graph.masqueradesAsUndefinedWatchpointIsStillValid(node->origin.semantic));
-        JSValue child = forNode(node->child1()).value();
-        if (child) {
+        AbstractValue child = forNode(node->child1());
+        if (child.value()) {
             bool constantWasSet = true;
             switch (node->op()) {
             case IsUndefined:
                 setConstant(node, jsBoolean(
-                    child.isCell()
-                    ? child.asCell()->structure()->masqueradesAsUndefined(m_codeBlock->globalObjectFor(node->origin.semantic))
-                    : child.isUndefined()));
+                    child.value().isCell()
+                    ? child.value().asCell()->structure()->masqueradesAsUndefined(m_codeBlock->globalObjectFor(node->origin.semantic))
+                    : child.value().isUndefined()));
                 break;
             case IsBoolean:
-                setConstant(node, jsBoolean(child.isBoolean()));
+                setConstant(node, jsBoolean(child.value().isBoolean()));
                 break;
             case IsNumber:
-                setConstant(node, jsBoolean(child.isNumber()));
+                setConstant(node, jsBoolean(child.value().isNumber()));
                 break;
             case IsString:
-                setConstant(node, jsBoolean(isJSString(child)));
+                setConstant(node, jsBoolean(isJSString(child.value())));
                 break;
             case IsObject:
-                if (child.isNull() || !child.isObject()) {
-                    setConstant(node, jsBoolean(child.isNull()));
-                    break;
-                }
-                constantWasSet = false;
+                setConstant(node, jsBoolean(child.value().isObject()));
+                break;
+            case IsObjectOrNull:
+                if (child.value().isObject()) {
+                    JSObject* object = asObject(child.value());
+                    if (object->type() == JSFunctionType)
+                        setConstant(node, jsBoolean(false));
+                    else if (!(object->inlineTypeFlags() & TypeOfShouldCallGetCallData))
+                        setConstant(node, jsBoolean(!child.value().asCell()->structure()->masqueradesAsUndefined(m_codeBlock->globalObjectFor(node->origin.semantic))));
+                    else {
+                        // FIXME: This could just call getCallData.
+                        // https://bugs.webkit.org/show_bug.cgi?id=144457
+                        constantWasSet = false;
+                    }
+                } else
+                    setConstant(node, jsBoolean(child.value().isNull()));
+                break;
+            case IsFunction:
+                if (child.value().isObject()) {
+                    JSObject* object = asObject(child.value());
+                    if (object->type() == JSFunctionType)
+                        setConstant(node, jsBoolean(true));
+                    else if (!(object->inlineTypeFlags() & TypeOfShouldCallGetCallData))
+                        setConstant(node, jsBoolean(false));
+                    else {
+                        // FIXME: This could just call getCallData.
+                        // https://bugs.webkit.org/show_bug.cgi?id=144457
+                        constantWasSet = false;
+                    }
+                } else
+                    setConstant(node, jsBoolean(false));
                 break;
             default:
                 constantWasSet = false;
@@ -853,7 +960,131 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             if (constantWasSet)
                 break;
         }
-
+        
+        // FIXME: This code should really use AbstractValue::isType() and
+        // AbstractValue::couldBeType().
+        // https://bugs.webkit.org/show_bug.cgi?id=146870
+        
+        bool constantWasSet = false;
+        switch (node->op()) {
+        case IsUndefined:
+            // FIXME: Use the masquerades-as-undefined watchpoint thingy.
+            // https://bugs.webkit.org/show_bug.cgi?id=144456
+            
+            if (!(child.m_type & (SpecOther | SpecObjectOther))) {
+                setConstant(node, jsBoolean(false));
+                constantWasSet = true;
+                break;
+            }
+            
+            break;
+        case IsBoolean:
+            if (!(child.m_type & ~SpecBoolean)) {
+                setConstant(node, jsBoolean(true));
+                constantWasSet = true;
+                break;
+            }
+            
+            if (!(child.m_type & SpecBoolean)) {
+                setConstant(node, jsBoolean(false));
+                constantWasSet = true;
+                break;
+            }
+            
+            break;
+        case IsNumber:
+            if (!(child.m_type & ~SpecFullNumber)) {
+                setConstant(node, jsBoolean(true));
+                constantWasSet = true;
+                break;
+            }
+            
+            if (!(child.m_type & SpecFullNumber)) {
+                setConstant(node, jsBoolean(false));
+                constantWasSet = true;
+                break;
+            }
+            
+            break;
+        case IsString:
+            if (!(child.m_type & ~SpecString)) {
+                setConstant(node, jsBoolean(true));
+                constantWasSet = true;
+                break;
+            }
+            
+            if (!(child.m_type & SpecString)) {
+                setConstant(node, jsBoolean(false));
+                constantWasSet = true;
+                break;
+            }
+            
+            break;
+        case IsObject:
+            if (!(child.m_type & ~SpecObject)) {
+                setConstant(node, jsBoolean(true));
+                constantWasSet = true;
+                break;
+            }
+            
+            if (!(child.m_type & SpecObject)) {
+                setConstant(node, jsBoolean(false));
+                constantWasSet = true;
+                break;
+            }
+            
+            break;
+        case IsObjectOrNull:
+            // FIXME: Use the masquerades-as-undefined watchpoint thingy.
+            // https://bugs.webkit.org/show_bug.cgi?id=144456
+            
+            // These expressions are complicated to parse. A helpful way to parse this is that
+            // "!(T & ~S)" means "T is a subset of S". Conversely, "!(T & S)" means "T is a
+            // disjoint set from S". Things like "T - S" means that, provided that S is a
+            // subset of T, it's the "set of all things in T but not in S". Things like "T | S"
+            // mean the "union of T and S".
+            
+            // Is the child's type an object that isn't an other-object (i.e. object that could
+            // have masquaredes-as-undefined traps) and isn't a function?  Then: we should fold
+            // this to true.
+            if (!(child.m_type & ~(SpecObject - SpecObjectOther - SpecFunction))) {
+                setConstant(node, jsBoolean(true));
+                constantWasSet = true;
+                break;
+            }
+            
+            // Is the child's type definitely not either of: an object that isn't a function,
+            // or either undefined or null?  Then: we should fold this to false.  This means
+            // for example that if it's any non-function object, including those that have
+            // masquerades-as-undefined traps, then we don't fold. It also means we won't fold
+            // if it's undefined-or-null, since the type bits don't distinguish between
+            // undefined (which should fold to false) and null (which should fold to true).
+            if (!(child.m_type & ((SpecObject - SpecFunction) | SpecOther))) {
+                setConstant(node, jsBoolean(false));
+                constantWasSet = true;
+                break;
+            }
+            
+            break;
+        case IsFunction:
+            if (!(child.m_type & ~SpecFunction)) {
+                setConstant(node, jsBoolean(true));
+                constantWasSet = true;
+                break;
+            }
+            
+            if (!(child.m_type & (SpecFunction | SpecObjectOther))) {
+                setConstant(node, jsBoolean(false));
+                constantWasSet = true;
+                break;
+            }
+            break;
+        default:
+            break;
+        }
+        if (constantWasSet)
+            break;
+        
         forNode(node).setType(SpecBoolean);
         break;
     }
@@ -864,47 +1095,43 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         AbstractValue& abstractChild = forNode(node->child1());
         if (child) {
             JSValue typeString = jsTypeStringForValue(*vm, m_codeBlock->globalObjectFor(node->origin.semantic), child);
-            setConstant(node, typeString);
+            setConstant(node, *m_graph.freeze(typeString));
             break;
         }
         
         if (isFullNumberSpeculation(abstractChild.m_type)) {
-            setConstant(node, vm->smallStrings.numberString());
+            setConstant(node, *m_graph.freeze(vm->smallStrings.numberString()));
             break;
         }
         
         if (isStringSpeculation(abstractChild.m_type)) {
-            setConstant(node, vm->smallStrings.stringString());
+            setConstant(node, *m_graph.freeze(vm->smallStrings.stringString()));
             break;
         }
-        
-        if (isFinalObjectSpeculation(abstractChild.m_type) || isArraySpeculation(abstractChild.m_type) || isArgumentsSpeculation(abstractChild.m_type)) {
-            setConstant(node, vm->smallStrings.objectString());
+
+        // FIXME: We could use the masquerades-as-undefined watchpoint here.
+        // https://bugs.webkit.org/show_bug.cgi?id=144456
+        if (!(abstractChild.m_type & ~(SpecObject - SpecObjectOther))) {
+            setConstant(node, *m_graph.freeze(vm->smallStrings.objectString()));
             break;
         }
         
         if (isFunctionSpeculation(abstractChild.m_type)) {
-            setConstant(node, vm->smallStrings.functionString());
+            setConstant(node, *m_graph.freeze(vm->smallStrings.functionString()));
             break;
         }
         
         if (isBooleanSpeculation(abstractChild.m_type)) {
-            setConstant(node, vm->smallStrings.booleanString());
+            setConstant(node, *m_graph.freeze(vm->smallStrings.booleanString()));
             break;
         }
 
-        switch (node->child1().useKind()) {
-        case StringUse:
-        case CellUse:
-            node->setCanExit(true);
-            break;
-        case UntypedUse:
-            break;
-        default:
-            RELEASE_ASSERT_NOT_REACHED();
+        if (isSymbolSpeculation(abstractChild.m_type)) {
+            setConstant(node, *m_graph.freeze(vm->smallStrings.symbolString()));
             break;
         }
-        forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get());
+
+        forNode(node).setType(m_graph, SpecStringIdent);
         break;
     }
             
@@ -963,14 +1190,6 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         }
         
         forNode(node).setType(SpecBoolean);
-        
-        // This is overly conservative. But the only thing this prevents is store elimination,
-        // and how likely is it, really, that you'll have redundant stores across a comparison
-        // operation? Comparison operations are typically at the end of basic blocks, so
-        // unless we have global store elimination (super unlikely given how unprofitable that
-        // optimization is to begin with), you aren't going to be wanting to store eliminate
-        // across an equality op.
-        node->setCanExit(true);
         break;
     }
             
@@ -1003,26 +1222,22 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         }
         
         forNode(node).setType(SpecBoolean);
-        node->setCanExit(true); // This is overly conservative.
         break;
     }
         
     case StringCharCodeAt:
-        node->setCanExit(true);
         forNode(node).setType(SpecInt32);
         break;
         
     case StringFromCharCode:
-        forNode(node).setType(SpecString);
+        forNode(node).setType(m_graph, SpecString);
         break;
 
     case StringCharAt:
-        node->setCanExit(true);
         forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get());
         break;
             
     case GetByVal: {
-        node->setCanExit(true);
         switch (node->arrayMode().type()) {
         case Array::SelectUsingPredictions:
         case Array::Unprofiled:
@@ -1053,7 +1268,8 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             } else
                 forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get());
             break;
-        case Array::Arguments:
+        case Array::DirectArguments:
+        case Array::ScopedArguments:
             forNode(node).makeHeapTop();
             break;
         case Array::Int32:
@@ -1121,7 +1337,6 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
     case PutByValDirect:
     case PutByVal:
     case PutByValAlias: {
-        node->setCanExit(true);
         switch (node->arrayMode().modeForPut().type()) {
         case Array::ForceExit:
             m_state.setIsValid(false);
@@ -1153,16 +1368,59 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
     }
             
     case ArrayPush:
-        node->setCanExit(true);
         clobberWorld(node->origin.semantic, clobberLimit);
         forNode(node).setType(SpecBytecodeNumber);
         break;
             
     case ArrayPop:
-        node->setCanExit(true);
         clobberWorld(node->origin.semantic, clobberLimit);
         forNode(node).makeHeapTop();
         break;
+        
+    case GetMyArgumentByVal: {
+        JSValue index = forNode(node->child2()).m_value;
+        InlineCallFrame* inlineCallFrame = node->child1()->origin.semantic.inlineCallFrame;
+
+        if (index && index.isInt32()) {
+            // This pretends to return TOP for accesses that are actually proven out-of-bounds because
+            // that's the conservative thing to do. Otherwise we'd need to write more code to mark such
+            // paths as unreachable, and it's almost certainly not worth the effort.
+            
+            if (inlineCallFrame) {
+                if (index.asUInt32() < inlineCallFrame->arguments.size() - 1) {
+                    forNode(node) = m_state.variables().operand(
+                        virtualRegisterForArgument(index.asInt32() + 1) + inlineCallFrame->stackOffset);
+                    m_state.setFoundConstants(true);
+                    break;
+                }
+            } else {
+                if (index.asUInt32() < m_state.variables().numberOfArguments() - 1) {
+                    forNode(node) = m_state.variables().argument(index.asInt32() + 1);
+                    m_state.setFoundConstants(true);
+                    break;
+                }
+            }
+        }
+        
+        if (inlineCallFrame) {
+            // We have a bound on the types even though it's random access. Take advantage of this.
+            
+            AbstractValue result;
+            for (unsigned i = inlineCallFrame->arguments.size(); i-- > 1;) {
+                result.merge(
+                    m_state.variables().operand(
+                        virtualRegisterForArgument(i) + inlineCallFrame->stackOffset));
+            }
+            
+            if (result.value())
+                m_state.setFoundConstants(true);
+            forNode(node) = result;
+            break;
+        }
+        
+        forNode(node).makeHeapTop();
+        break;
+    }
             
     case RegExpExec:
         forNode(node).makeHeapTop();
@@ -1190,7 +1448,6 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         // constant propagation, but we can do better:
         // We can specialize the source variable's value on each direction of
         // the branch.
-        node->setCanExit(true); // This is overly conservative.
         m_state.setBranchDirection(TakeBoth);
         break;
     }
@@ -1208,7 +1465,6 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
     case Throw:
     case ThrowReferenceError:
         m_state.setIsValid(false);
-        node->setCanExit(true);
         break;
             
     case ToPrimitive: {
@@ -1225,7 +1481,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             break;
         }
         
-        if (!(forNode(node->child1()).m_type & ~(SpecFullNumber | SpecBoolean | SpecString))) {
+        if (!(forNode(node->child1()).m_type & ~(SpecFullNumber | SpecBoolean | SpecString | SpecSymbol))) {
             m_state.setFoundConstants(true);
             forNode(node) = forNode(node->child1());
             break;
@@ -1233,11 +1489,12 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         
         clobberWorld(node->origin.semantic, clobberLimit);
         
-        forNode(node).setType((SpecHeapTop & ~SpecCell) | SpecString);
+        forNode(node).setType(m_graph, (SpecHeapTop & ~SpecCell) | SpecString | SpecSymbol);
         break;
     }
         
-    case ToString: {
+    case ToString:
+    case CallStringConstructor: {
         switch (node->child1().useKind()) {
         case StringObjectUse:
             // This also filters that the StringObject has the primordial StringObject
@@ -1245,10 +1502,8 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             filter(
                 node->child1(),
                 m_graph.globalObjectFor(node->origin.semantic)->stringObjectStructure());
-            node->setCanExit(true); // We could be more precise but it's likely not worth it.
             break;
         case StringOrStringObjectUse:
-            node->setCanExit(true); // We could be more precise but it's likely not worth it.
             break;
         case CellUse:
         case UntypedUse:
@@ -1269,25 +1524,19 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
     }
             
     case NewArray:
-        node->setCanExit(true);
         forNode(node).set(
             m_graph,
             m_graph.globalObjectFor(node->origin.semantic)->arrayStructureForIndexingTypeDuringAllocation(node->indexingType()));
-        m_state.setHaveStructures(true);
         break;
         
     case NewArrayBuffer:
-        node->setCanExit(true);
         forNode(node).set(
             m_graph,
             m_graph.globalObjectFor(node->origin.semantic)->arrayStructureForIndexingTypeDuringAllocation(node->indexingType()));
-        m_state.setHaveStructures(true);
         break;
 
     case NewArrayWithSize:
-        node->setCanExit(true);
-        forNode(node).setType(SpecArray);
-        m_state.setHaveStructures(true);
+        forNode(node).setType(m_graph, SpecArray);
         break;
         
     case NewTypedArray:
@@ -1305,12 +1554,10 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             m_graph,
             m_graph.globalObjectFor(node->origin.semantic)->typedArrayStructure(
                 node->typedArrayType()));
-        m_state.setHaveStructures(true);
         break;
             
     case NewRegexp:
         forNode(node).set(m_graph, m_graph.globalObjectFor(node->origin.semantic)->regExpStructure());
-        m_state.setHaveStructures(true);
         break;
             
     case ToThis: {
@@ -1327,214 +1574,214 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
     }
 
     case CreateThis: {
-        forNode(node).setType(SpecFinalObject);
+        // FIXME: We can fold this to NewObject if the incoming callee is a constant.
+        forNode(node).setType(m_graph, SpecFinalObject);
         break;
     }
         
-    case AllocationProfileWatchpoint:
-        node->setCanExit(true);
-        break;
-
     case NewObject:
         ASSERT(node->structure());
         forNode(node).set(m_graph, node->structure());
-        m_state.setHaveStructures(true);
         break;
         
+    case PhantomNewObject:
+    case PhantomNewFunction:
+    case PhantomCreateActivation:
+    case PhantomDirectArguments:
+    case PhantomClonedArguments:
+    case BottomValue:
+        m_state.setDidClobber(true); // Prevent constant folding.
+        // This claims to return bottom.
+        break;
+        
+    case PutHint:
+        break;
+        
+    case MaterializeNewObject: {
+        StructureSet set;
+        
+        m_phiChildren->forAllTransitiveIncomingValues(
+            m_graph.varArgChild(node, 0).node(),
+            [&] (Node* incoming) {
+                set.add(incoming->castConstant<Structure*>());
+            });
+        
+        forNode(node).set(m_graph, set);
+        break;
+    }
+
     case CreateActivation:
+    case MaterializeCreateActivation:
         forNode(node).set(
             m_graph, m_codeBlock->globalObjectFor(node->origin.semantic)->activationStructure());
-        m_state.setHaveStructures(true);
         break;
         
-    case FunctionReentryWatchpoint:
-    case TypedArrayWatchpoint:
-        break;
-    
-    case CreateArguments:
-        forNode(node) = forNode(node->child1());
-        forNode(node).filter(~SpecEmpty);
-        forNode(node).merge(SpecArguments);
+    case CreateDirectArguments:
+        forNode(node).set(m_graph, m_codeBlock->globalObjectFor(node->origin.semantic)->directArgumentsStructure());
         break;
         
-    case TearOffActivation:
-    case TearOffArguments:
-        // Does nothing that is user-visible.
-        break;
-
-    case CheckArgumentsNotCreated:
-        if (isEmptySpeculation(
-                m_state.variables().operand(
-                    m_graph.argumentsRegisterFor(node->origin.semantic).offset()).m_type))
-            m_state.setFoundConstants(true);
-        else
-            node->setCanExit(true);
-        break;
-        
-    case GetMyArgumentsLength:
-        // We know that this executable does not escape its arguments, so we can optimize
-        // the arguments a bit. Note that this is not sufficient to force constant folding
-        // of GetMyArgumentsLength, because GetMyArgumentsLength is a clobbering operation.
-        // We perform further optimizations on this later on.
-        if (node->origin.semantic.inlineCallFrame) {
-            forNode(node).set(
-                m_graph, jsNumber(node->origin.semantic.inlineCallFrame->arguments.size() - 1));
-        } else
-            forNode(node).setType(SpecInt32);
-        node->setCanExit(
-            !isEmptySpeculation(
-                m_state.variables().operand(
-                    m_graph.argumentsRegisterFor(node->origin.semantic)).m_type));
+    case CreateScopedArguments:
+        forNode(node).set(m_graph, m_codeBlock->globalObjectFor(node->origin.semantic)->scopedArgumentsStructure());
         break;
         
-    case GetMyArgumentsLengthSafe:
-        // This potentially clobbers all structures if the arguments object had a getter
-        // installed on the length property.
-        clobberWorld(node->origin.semantic, clobberLimit);
-        // We currently make no guarantee about what this returns because it does not
-        // speculate that the length property is actually a length.
-        forNode(node).makeHeapTop();
+    case CreateClonedArguments:
+        forNode(node).setType(m_graph, SpecObjectOther);
         break;
         
-    case GetMyArgumentByVal:
-        node->setCanExit(true);
-        // We know that this executable does not escape its arguments, so we can optimize
-        // the arguments a bit. Note that this ends up being further optimized by the
-        // ArgumentsSimplificationPhase.
-        forNode(node).makeHeapTop();
+    case NewFunction:
+        forNode(node).set(
+            m_graph, m_codeBlock->globalObjectFor(node->origin.semantic)->functionStructure());
         break;
         
-    case GetMyArgumentByValSafe:
-        node->setCanExit(true);
-        // This potentially clobbers all structures if the property we're accessing has
-        // a getter. We don't speculate against this.
-        clobberWorld(node->origin.semantic, clobberLimit);
-        // And the result is unknown.
-        forNode(node).makeHeapTop();
+    case GetCallee:
+        if (FunctionExecutable* executable = jsDynamicCast<FunctionExecutable*>(m_codeBlock->ownerExecutable())) {
+            InferredValue* singleton = executable->singletonFunction();
+            if (JSValue value = singleton->inferredValue()) {
+                m_graph.watchpoints().addLazily(singleton);
+                JSFunction* function = jsCast<JSFunction*>(value);
+                setConstant(node, *m_graph.freeze(function));
+                break;
+            }
+        }
+        forNode(node).setType(m_graph, SpecFunction);
         break;
         
-    case NewFunction: {
-        AbstractValue& value = forNode(node);
-        value = forNode(node->child1());
+    case GetArgumentCount:
+        forNode(node).setType(SpecInt32);
+        break;
         
-        if (!(value.m_type & SpecEmpty)) {
-            m_state.setFoundConstants(true);
-            break;
+    case GetGetter: {
+        JSValue base = forNode(node->child1()).m_value;
+        if (base) {
+            GetterSetter* getterSetter = jsCast<GetterSetter*>(base);
+            if (!getterSetter->isGetterNull()) {
+                setConstant(node, *m_graph.freeze(getterSetter->getterConcurrently()));
+                break;
+            }
         }
-
-        value.setType((value.m_type & ~SpecEmpty) | SpecFunction);
+        
+        forNode(node).setType(m_graph, SpecObject);
         break;
     }
-
-    case NewFunctionExpression:
-    case NewFunctionNoCheck:
-        forNode(node).set(
-            m_graph, m_codeBlock->globalObjectFor(node->origin.semantic)->functionStructure());
-        m_state.setHaveStructures(true);
-        break;
         
-    case GetCallee:
-        forNode(node).setType(SpecFunction);
+    case GetSetter: {
+        JSValue base = forNode(node->child1()).m_value;
+        if (base) {
+            GetterSetter* getterSetter = jsCast<GetterSetter*>(base);
+            if (!getterSetter->isSetterNull()) {
+                setConstant(node, *m_graph.freeze(getterSetter->setterConcurrently()));
+                break;
+            }
+        }
+        
+        forNode(node).setType(m_graph, SpecObject);
         break;
+    }
         
-    case GetScope: // FIXME: We could get rid of these if we know that the JSFunction is a constant. https://bugs.webkit.org/show_bug.cgi?id=106202
-    case GetMyScope:
-    case SkipTopScope:
-        forNode(node).setType(SpecObjectOther);
+    case GetScope:
+        if (JSValue base = forNode(node->child1()).m_value) {
+            if (JSFunction* function = jsDynamicCast<JSFunction*>(base)) {
+                setConstant(node, *m_graph.freeze(function->scope()));
+                break;
+            }
+        }
+        forNode(node).setType(m_graph, SpecObjectOther);
         break;
 
     case SkipScope: {
         JSValue child = forNode(node->child1()).value();
         if (child) {
-            setConstant(node, JSValue(jsCast<JSScope*>(child.asCell())->next()));
+            setConstant(node, *m_graph.freeze(JSValue(jsCast<JSScope*>(child.asCell())->next())));
             break;
         }
-        forNode(node).setType(SpecObjectOther);
+        forNode(node).setType(m_graph, SpecObjectOther);
         break;
     }
 
-    case GetClosureRegisters:
-        forNode(node).clear(); // The result is not a JS value.
-        break;
-
     case GetClosureVar:
+        if (JSValue value = m_graph.tryGetConstantClosureVar(forNode(node->child1()), node->scopeOffset())) {
+            setConstant(node, *m_graph.freeze(value));
+            break;
+        }
         forNode(node).makeHeapTop();
         break;
             
     case PutClosureVar:
-        clobberCapturedVars(node->origin.semantic);
+        break;
+        
+    case GetFromArguments:
+        forNode(node).makeHeapTop();
+        break;
+        
+    case PutToArguments:
         break;
             
     case GetById:
-    case GetByIdFlush:
-        node->setCanExit(true);
+    case GetByIdFlush: {
         if (!node->prediction()) {
             m_state.setIsValid(false);
             break;
         }
-        if (isCellSpeculation(node->child1()->prediction())) {
-            if (Structure* structure = forNode(node->child1()).bestProvenStructure()) {
-                GetByIdStatus status = GetByIdStatus::computeFor(
-                    m_graph.m_vm, structure,
-                    m_graph.identifiers()[node->identifierNumber()]);
-                if (status.isSimple() && status.numVariants() == 1) {
-                    // Assert things that we can't handle and that the computeFor() method
-                    // above won't be able to return.
-                    ASSERT(status[0].structureSet().size() == 1);
-                    ASSERT(!status[0].chain());
-                    
-                    if (status[0].specificValue())
-                        setConstant(node, status[0].specificValue());
-                    else
-                        forNode(node).makeHeapTop();
-                    filter(node->child1(), status[0].structureSet());
+        
+        AbstractValue& value = forNode(node->child1());
+        if (!value.m_structure.isTop() && !value.m_structure.isClobbered()
+            && (node->child1().useKind() == CellUse || !(value.m_type & ~SpecCell))) {
+            GetByIdStatus status = GetByIdStatus::computeFor(
+                value.m_structure.set(), m_graph.identifiers()[node->identifierNumber()]);
+            if (status.isSimple()) {
+                // Figure out what the result is going to be - is it TOP, a constant, or maybe
+                // something more subtle?
+                AbstractValue result;
+                for (unsigned i = status.numVariants(); i--;) {
+                    DFG_ASSERT(m_graph, node, !status[i].alternateBase());
+                    JSValue constantResult =
+                        m_graph.tryGetConstantProperty(value, status[i].offset());
+                    if (!constantResult) {
+                        result.makeHeapTop();
+                        break;
+                    }
                     
-                    m_state.setFoundConstants(true);
-                    m_state.setHaveStructures(true);
-                    break;
+                    AbstractValue thisResult;
+                    thisResult.set(
+                        m_graph, *m_graph.freeze(constantResult),
+                        m_state.structureClobberState());
+                    result.merge(thisResult);
                 }
+                if (status.numVariants() == 1 || isFTL(m_graph.m_plan.mode))
+                    m_state.setFoundConstants(true);
+                forNode(node) = result;
+                break;
             }
         }
+
         clobberWorld(node->origin.semantic, clobberLimit);
         forNode(node).makeHeapTop();
         break;
+    }
             
-    case GetArrayLength:
-        node->setCanExit(true); // Lies, but it's true for the common case of JSArray, so it's good enough.
+    case GetArrayLength: {
+        JSArrayBufferView* view = m_graph.tryGetFoldableView(
+            forNode(node->child1()).m_value, node->arrayMode());
+        if (view) {
+            setConstant(node, jsNumber(view->length()));
+            break;
+        }
         forNode(node).setType(SpecInt32);
         break;
-        
-    case CheckExecutable: {
-        // FIXME: We could track executables in AbstractValue, which would allow us to get rid of these checks
-        // more thoroughly. https://bugs.webkit.org/show_bug.cgi?id=106200
-        // FIXME: We could eliminate these entirely if we know the exact value that flows into this.
-        // https://bugs.webkit.org/show_bug.cgi?id=106201
-        node->setCanExit(true);
-        break;
     }
-
+        
     case CheckStructure: {
-        // 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_state.setFoundConstants(true);
-            break;
-        }
-
-        node->setCanExit(true);
-        m_state.setHaveStructures(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.
-        if (value.m_futurePossibleStructure.isSubsetOf(set)
-            && value.m_futurePossibleStructure.hasSingleton()) {
+        
+        // It's interesting that we could have proven that the object has a larger structure set
+        // that includes the set we're testing. In that case we could make the structure check
+        // more efficient. We currently don't.
+        
+        if (value.m_structure.isSubsetOf(set)) {
             m_state.setFoundConstants(true);
-            filter(value, value.m_futurePossibleStructure.singleton());
             break;
         }
 
@@ -1542,21 +1789,59 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         break;
     }
         
-    case StructureTransitionWatchpoint: {
+    case CheckStructureImmediate: {
+        // FIXME: This currently can only reason about one structure at a time.
+        // https://bugs.webkit.org/show_bug.cgi?id=136988
+        
         AbstractValue& value = forNode(node->child1());
-
-        filter(value, node->structure());
-        m_state.setHaveStructures(true);
-        node->setCanExit(true);
+        StructureSet& set = node->structureSet();
+        
+        if (value.value()) {
+            if (Structure* structure = jsDynamicCast<Structure*>(value.value())) {
+                if (set.contains(structure)) {
+                    m_state.setFoundConstants(true);
+                    break;
+                }
+            }
+            m_state.setIsValid(false);
+            break;
+        }
+        
+        if (m_phiChildren) {
+            bool allGood = true;
+            m_phiChildren->forAllTransitiveIncomingValues(
+                node,
+                [&] (Node* incoming) {
+                    if (Structure* structure = incoming->dynamicCastConstant<Structure*>()) {
+                        if (set.contains(structure))
+                            return;
+                    }
+                    allGood = false;
+                });
+            if (allGood) {
+                m_state.setFoundConstants(true);
+                break;
+            }
+        }
+            
+        if (Structure* structure = set.onlyStructure()) {
+            filterByValue(node->child1(), *m_graph.freeze(structure));
+            break;
+        }
+        
+        // Aw shucks, we can't do anything!
         break;
     }
-            
+        
     case PutStructure:
-    case PhantomPutStructure:
-        if (!forNode(node->child1()).m_currentKnownStructure.isClear()) {
-            clobberStructures(clobberLimit);
-            forNode(node->child1()).set(m_graph, node->structureTransitionData().newStructure);
-            m_state.setHaveStructures(true);
+        if (!forNode(node->child1()).m_structure.isClear()) {
+            if (forNode(node->child1()).m_structure.onlyStructure() == node->transition()->next)
+                m_state.setFoundConstants(true);
+            else {
+                observeTransition(
+                    clobberLimit, node->transition()->previous, node->transition()->next);
+                forNode(node->child1()).changeStructure(m_graph, node->transition()->next);
+            }
         }
         break;
     case GetButterfly:
@@ -1569,7 +1854,6 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             m_state.setFoundConstants(true);
             break;
         }
-        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:
             filter(node->child1(), SpecString);
@@ -1580,8 +1864,11 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         case Array::ArrayStorage:
         case Array::SlowPutArrayStorage:
             break;
-        case Array::Arguments:
-            filter(node->child1(), SpecArguments);
+        case Array::DirectArguments:
+            filter(node->child1(), SpecDirectArguments);
+            break;
+        case Array::ScopedArguments:
+            filter(node->child1(), SpecScopedArguments);
             break;
         case Array::Int8Array:
             filter(node->child1(), SpecInt8Array);
@@ -1615,7 +1902,6 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             break;
         }
         filterArrayModes(node->child1(), node->arrayMode().arrayModesThatPassFiltering());
-        m_state.setHaveStructures(true);
         break;
     }
     case Arrayify: {
@@ -1623,72 +1909,143 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             m_state.setFoundConstants(true);
             break;
         }
-        ASSERT(node->arrayMode().conversion() == Array::Convert
-            || node->arrayMode().conversion() == Array::RageConvert);
-        node->setCanExit(true);
+        ASSERT(node->arrayMode().conversion() == Array::Convert);
         clobberStructures(clobberLimit);
         filterArrayModes(node->child1(), node->arrayMode().arrayModesThatPassFiltering());
-        m_state.setHaveStructures(true);
         break;
     }
     case ArrayifyToStructure: {
         AbstractValue& value = forNode(node->child1());
-        StructureSet set = node->structure();
-        if (value.m_futurePossibleStructure.isSubsetOf(set)
-            || value.m_currentKnownStructure.isSubsetOf(set))
+        if (value.m_structure.isSubsetOf(StructureSet(node->structure())))
             m_state.setFoundConstants(true);
-        node->setCanExit(true);
         clobberStructures(clobberLimit);
-        filter(value, set);
-        m_state.setHaveStructures(true);
+        
+        // We have a bunch of options of how to express the abstract set at this point. Let set S
+        // be the set of structures that the value had before clobbering and assume that all of
+        // them are watchable. The new value should be the least expressible upper bound of the
+        // intersection of "values that currently have structure = node->structure()" and "values
+        // that have structure in S plus any structure transition-reachable from S". Assume that
+        // node->structure() is not in S but it is transition-reachable from S. Then we would
+        // like to say that the result is "values that have structure = node->structure() until
+        // we invalidate", but there is no way to express this using the AbstractValue syntax. So
+        // we must choose between:
+        //
+        // 1) "values that currently have structure = node->structure()". This is a valid
+        //    superset of the value that we really want, and it's specific enough to satisfy the
+        //    preconditions of the array access that this is guarding. It's also specific enough
+        //    to allow relevant optimizations in the case that we didn't have a contradiction
+        //    like in this example. Notice that in the abscence of any contradiction, this result
+        //    is precise rather than being a conservative LUB.
+        //
+        // 2) "values that currently hava structure in S plus any structure transition-reachable
+        //    from S". This is also a valid superset of the value that we really want, but it's
+        //    not specific enough to satisfy the preconditions of the array access that this is
+        //    guarding - so playing such shenanigans would preclude us from having assertions on
+        //    the typing preconditions of any array accesses. This would also not be a desirable
+        //    answer in the absence of a contradiction.
+        //
+        // Note that it's tempting to simply say that the resulting value is BOTTOM because of
+        // the contradiction. That would be wrong, since we haven't hit an invalidation point,
+        // yet.
+        value.set(m_graph, node->structure());
+        break;
+    }
+    case GetIndexedPropertyStorage: {
+        JSArrayBufferView* view = m_graph.tryGetFoldableView(
+            forNode(node->child1()).m_value, node->arrayMode());
+        if (view)
+            m_state.setFoundConstants(true);
+        forNode(node).clear();
         break;
     }
-    case GetIndexedPropertyStorage:
     case ConstantStoragePointer: {
         forNode(node).clear();
         break; 
     }
         
     case GetTypedArrayByteOffset: {
+        JSArrayBufferView* view = m_graph.tryGetFoldableView(forNode(node->child1()).m_value);
+        if (view) {
+            setConstant(node, jsNumber(view->byteOffset()));
+            break;
+        }
         forNode(node).setType(SpecInt32);
         break;
     }
         
     case GetByOffset: {
+        StorageAccessData& data = node->storageAccessData();
+        JSValue result = m_graph.tryGetConstantProperty(forNode(node->child2()), data.offset);
+        if (result) {
+            setConstant(node, *m_graph.freeze(result));
+            break;
+        }
+        
         forNode(node).makeHeapTop();
         break;
     }
         
+    case GetGetterSetterByOffset: {
+        StorageAccessData& data = node->storageAccessData();
+        JSValue result = m_graph.tryGetConstantProperty(forNode(node->child2()), data.offset);
+        if (result && jsDynamicCast<GetterSetter*>(result)) {
+            setConstant(node, *m_graph.freeze(result));
+            break;
+        }
+        
+        forNode(node).set(m_graph, m_graph.m_vm.getterSetterStructure.get());
+        break;
+    }
+        
     case MultiGetByOffset: {
-        AbstractValue& value = forNode(node->child1());
-        ASSERT(!(value.m_type & ~SpecCell)); // Edge filtering should have already ensured this.
-
-        if (Structure* structure = value.bestProvenStructure()) {
-            bool done = false;
-            for (unsigned i = node->multiGetByOffsetData().variants.size(); i--;) {
-                const GetByIdVariant& variant = node->multiGetByOffsetData().variants[i];
-                if (!variant.structureSet().contains(structure))
-                    continue;
-                
-                if (variant.chain())
-                    break;
-                
-                filter(value, structure);
-                forNode(node).makeHeapTop();
-                m_state.setFoundConstants(true);
-                done = true;
-                break;
+        // This code will filter the base value in a manner that is possibly different (either more
+        // or less precise) than the way it would be filtered if this was strength-reduced to a
+        // CheckStructure. This is fine. It's legal for different passes over the code to prove
+        // different things about the code, so long as all of them are sound. That even includes
+        // one guy proving that code should never execute (due to a contradiction) and another guy
+        // not finding that contradiction. If someone ever proved that there would be a
+        // contradiction then there must always be a contradiction even if subsequent passes don't
+        // realize it. This is the case here.
+        
+        // Ordinarily you have to be careful with calling setFoundConstants()
+        // because of the effect on compile times, but this node is FTL-only.
+        m_state.setFoundConstants(true);
+        
+        AbstractValue base = forNode(node->child1());
+        StructureSet baseSet;
+        AbstractValue result;
+        for (unsigned i = node->multiGetByOffsetData().variants.size(); i--;) {
+            GetByIdVariant& variant = node->multiGetByOffsetData().variants[i];
+            StructureSet set = variant.structureSet();
+            set.filter(base);
+            if (set.isEmpty())
+                continue;
+            baseSet.merge(set);
+            
+            JSValue baseForLoad;
+            if (variant.alternateBase())
+                baseForLoad = variant.alternateBase();
+            else
+                baseForLoad = base.m_value;
+            JSValue constantResult =
+                m_graph.tryGetConstantProperty(
+                    baseForLoad, variant.baseStructure(), variant.offset());
+            if (!constantResult) {
+                result.makeHeapTop();
+                continue;
             }
-            if (done)
-                break;
+            AbstractValue thisResult;
+            thisResult.set(
+                m_graph,
+                *m_graph.freeze(constantResult),
+                m_state.structureClobberState());
+            result.merge(thisResult);
         }
         
-        StructureSet set;
-        for (unsigned i = node->multiGetByOffsetData().variants.size(); i--;)
-            set.addAll(node->multiGetByOffsetData().variants[i].structureSet());
+        if (forNode(node->child1()).changeStructure(m_graph, baseSet) == Contradiction)
+            m_state.setIsValid(false);
         
-        filter(node->child1(), set);
-        forNode(node).makeHeapTop();
+        forNode(node) = result;
         break;
     }
             
@@ -1697,71 +2054,74 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
     }
         
     case MultiPutByOffset: {
-        AbstractValue& value = forNode(node->child1());
-        ASSERT(!(value.m_type & ~SpecCell)); // Edge filtering should have already ensured this.
-
-        if (Structure* structure = value.bestProvenStructure()) {
-            bool done = false;
-            for (unsigned i = node->multiPutByOffsetData().variants.size(); i--;) {
-                const PutByIdVariant& variant = node->multiPutByOffsetData().variants[i];
-                if (variant.oldStructure() != structure)
-                    continue;
-                
-                if (variant.kind() == PutByIdVariant::Replace) {
-                    filter(node->child1(), structure);
-                    m_state.setFoundConstants(true);
-                    m_state.setHaveStructures(true);
-                    done = true;
-                    break;
-                }
-                
-                ASSERT(variant.kind() == PutByIdVariant::Transition);
-                clobberStructures(clobberLimit);
-                forNode(node->child1()).set(m_graph, variant.newStructure());
-                m_state.setFoundConstants(true);
-                m_state.setHaveStructures(true);
-                done = true;
-                break;
-            }
-            if (done)
-                break;
-        }
+        StructureSet newSet;
+        TransitionVector transitions;
         
-        clobberStructures(clobberLimit);
+        // Ordinarily you have to be careful with calling setFoundConstants()
+        // because of the effect on compile times, but this node is FTL-only.
+        m_state.setFoundConstants(true);
+        
+        AbstractValue base = forNode(node->child1());
         
-        StructureSet newSet;
         for (unsigned i = node->multiPutByOffsetData().variants.size(); i--;) {
             const PutByIdVariant& variant = node->multiPutByOffsetData().variants[i];
-            if (variant.kind() == PutByIdVariant::Replace) {
-                if (value.m_currentKnownStructure.contains(variant.structure()))
-                    newSet.addAll(variant.structure());
+            StructureSet thisSet = variant.oldStructure();
+            thisSet.filter(base);
+            if (thisSet.isEmpty())
                 continue;
+            if (variant.kind() == PutByIdVariant::Transition) {
+                if (thisSet.onlyStructure() != variant.newStructure()) {
+                    transitions.append(
+                        Transition(variant.oldStructureForTransition(), variant.newStructure()));
+                } // else this is really a replace.
+                newSet.add(variant.newStructure());
+            } else {
+                ASSERT(variant.kind() == PutByIdVariant::Replace);
+                newSet.merge(thisSet);
             }
-            ASSERT(variant.kind() == PutByIdVariant::Transition);
-            if (value.m_currentKnownStructure.contains(variant.oldStructure()))
-                newSet.addAll(variant.newStructure());
         }
         
-        // Use filter(value, set) as a way of setting the structure set. This works because
-        // we would have already made the set be TOP before this. Filtering top is another 
-        // way of setting.
-        filter(node->child1(), newSet);
+        observeTransitions(clobberLimit, transitions);
+        if (forNode(node->child1()).changeStructure(m_graph, newSet) == Contradiction)
+            m_state.setIsValid(false);
+        break;
+    }
+        
+    case GetExecutable: {
+        JSValue value = forNode(node->child1()).value();
+        if (value) {
+            JSFunction* function = jsDynamicCast<JSFunction*>(value);
+            if (function) {
+                setConstant(node, *m_graph.freeze(function->executable()));
+                break;
+            }
+        }
+        forNode(node).setType(m_graph, SpecCellOther);
         break;
     }
     
-    case CheckFunction: {
+    case CheckCell: {
         JSValue value = forNode(node->child1()).value();
-        if (value == node->function()) {
+        if (value == node->cellOperand()->value()) {
             m_state.setFoundConstants(true);
             ASSERT(value);
             break;
         }
-        
-        node->setCanExit(true); // Lies! We can do better.
-        filterByValue(node->child1(), node->function());
+        filterByValue(node->child1(), *node->cellOperand());
         break;
     }
+
+    case CheckNotEmpty: {
+        AbstractValue& value = forNode(node->child1());
+        if (!(value.m_type & SpecEmpty)) {
+            m_state.setFoundConstants(true);
+            break;
+        }
         
+        filter(value, ~SpecEmpty);
+        break;
+    }
+
     case CheckInBounds: {
         JSValue left = forNode(node->child1()).value();
         JSValue right = forNode(node->child2()).value();
@@ -1770,82 +2130,140 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             m_state.setFoundConstants(true);
             break;
         }
-        
-        node->setCanExit(true);
         break;
     }
         
     case PutById:
     case PutByIdFlush:
-    case PutByIdDirect:
-        node->setCanExit(true);
-        if (Structure* structure = forNode(node->child1()).bestProvenStructure()) {
+    case PutByIdDirect: {
+        AbstractValue& value = forNode(node->child1());
+        if (!value.m_structure.isTop() && !value.m_structure.isClobbered()) {
             PutByIdStatus status = PutByIdStatus::computeFor(
-                m_graph.m_vm,
                 m_graph.globalObjectFor(node->origin.semantic),
-                structure,
+                value.m_structure.set(),
                 m_graph.identifiers()[node->identifierNumber()],
                 node->op() == PutByIdDirect);
-            if (status.isSimple() && status.numVariants() == 1) {
-                if (status[0].kind() == PutByIdVariant::Replace) {
-                    filter(node->child1(), structure);
-                    m_state.setFoundConstants(true);
-                    m_state.setHaveStructures(true);
-                    break;
+            
+            if (status.isSimple()) {
+                StructureSet newSet;
+                TransitionVector transitions;
+                
+                for (unsigned i = status.numVariants(); i--;) {
+                    const PutByIdVariant& variant = status[i];
+                    if (variant.kind() == PutByIdVariant::Transition) {
+                        transitions.append(
+                            Transition(
+                                variant.oldStructureForTransition(), variant.newStructure()));
+                        m_graph.registerStructure(variant.newStructure());
+                        newSet.add(variant.newStructure());
+                    } else {
+                        ASSERT(variant.kind() == PutByIdVariant::Replace);
+                        newSet.merge(variant.oldStructure());
+                    }
                 }
-                if (status[0].kind() == PutByIdVariant::Transition) {
-                    clobberStructures(clobberLimit);
-                    forNode(node->child1()).set(m_graph, status[0].newStructure());
-                    m_state.setHaveStructures(true);
+                
+                if (status.numVariants() == 1 || isFTL(m_graph.m_plan.mode))
                     m_state.setFoundConstants(true);
-                    break;
-                }
+                
+                observeTransitions(clobberLimit, transitions);
+                if (forNode(node->child1()).changeStructure(m_graph, newSet) == Contradiction)
+                    m_state.setIsValid(false);
+                break;
             }
         }
+        
         clobberWorld(node->origin.semantic, clobberLimit);
         break;
+    }
         
-    case In:
+    case In: {
         // FIXME: We can determine when the property definitely exists based on abstract
         // value information.
         clobberWorld(node->origin.semantic, clobberLimit);
         forNode(node).setType(SpecBoolean);
         break;
+    }
             
+    case GetEnumerableLength: {
+        forNode(node).setType(SpecInt32);
+        break;
+    }
+    case HasGenericProperty: {
+        forNode(node).setType(SpecBoolean);
+        break;
+    }
+    case HasStructureProperty: {
+        forNode(node).setType(SpecBoolean);
+        break;
+    }
+    case HasIndexedProperty: {
+        ArrayMode mode = node->arrayMode();
+        switch (mode.type()) {
+        case Array::Int32:
+        case Array::Double:
+        case Array::Contiguous:
+        case Array::ArrayStorage: {
+            break;
+        }
+        default: {
+            clobberWorld(node->origin.semantic, clobberLimit);
+            break;
+        }
+        }
+        forNode(node).setType(SpecBoolean);
+        break;
+    }
+    case GetDirectPname: {
+        clobberWorld(node->origin.semantic, clobberLimit);
+        forNode(node).makeHeapTop();
+        break;
+    }
+    case GetPropertyEnumerator: {
+        forNode(node).setType(m_graph, SpecCell);
+        break;
+    }
+    case GetEnumeratorStructurePname: {
+        forNode(node).setType(m_graph, SpecString | SpecOther);
+        break;
+    }
+    case GetEnumeratorGenericPname: {
+        forNode(node).setType(m_graph, SpecString | SpecOther);
+        break;
+    }
+    case ToIndexString: {
+        forNode(node).setType(m_graph, SpecString);
+        break;
+    }
+
     case GetGlobalVar:
         forNode(node).makeHeapTop();
         break;
         
-    case VariableWatchpoint:
     case VarInjectionWatchpoint:
-        node->setCanExit(true);
-        break;
-            
     case PutGlobalVar:
     case NotifyWrite:
         break;
             
     case CheckHasInstance:
-        node->setCanExit(true);
         // Sadly, we don't propagate the fact that we've done CheckHasInstance
         break;
             
     case InstanceOf:
-        node->setCanExit(true);
         // Again, sadly, we don't propagate the fact that we've done InstanceOf
         forNode(node).setType(SpecBoolean);
         break;
             
     case Phi:
         RELEASE_ASSERT(m_graph.m_form == SSA);
-        // The state of this node would have already been decided.
+        // The state of this node would have already been decided, but it may have become a
+        // constant, in which case we'd like to know.
+        if (forNode(node).m_value)
+            m_state.setFoundConstants(true);
         break;
         
     case Upsilon: {
         m_state.createValueForNode(node->phi());
-        AbstractValue& value = forNode(node->child1());
-        forNode(node) = value;
-        forNode(node->phi()) = value;
+        forNode(node->phi()) = forNode(node->child1());
         break;
     }
         
@@ -1855,56 +2273,70 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             
     case Call:
     case Construct:
-        node->setCanExit(true);
+    case NativeCall:
+    case NativeConstruct:
+    case CallVarargs:
+    case CallForwardVarargs:
+    case ConstructVarargs:
+    case ConstructForwardVarargs:
         clobberWorld(node->origin.semantic, clobberLimit);
         forNode(node).makeHeapTop();
         break;
 
     case ForceOSRExit:
-        node->setCanExit(true);
+    case CheckBadCell:
         m_state.setIsValid(false);
         break;
         
     case InvalidationPoint:
-        node->setCanExit(true);
+        forAllValues(clobberLimit, AbstractValue::observeInvalidationPointFor);
+        m_state.setStructureClobberState(StructuresAreWatched);
         break;
 
     case CheckWatchdogTimer:
-        node->setCanExit(true);
         break;
 
     case Breakpoint:
     case ProfileWillCall:
     case ProfileDidCall:
+    case ProfileType:
+    case ProfileControlFlow:
     case Phantom:
-    case HardPhantom:
-    case Check:
     case CountExecution:
     case CheckTierUpInLoop:
     case CheckTierUpAtReturn:
         break;
 
-    case StoreBarrier: {
-        filter(node->child1(), SpecCell);
+    case Check: {
+        // Simplify out checks that don't actually do checking.
+        for (unsigned i = 0; i < AdjacencyList::Size; ++i) {
+            Edge edge = node->children.child(i);
+            if (!edge)
+                break;
+            if (edge.isProved() || edge.willNotHaveCheck()) {
+                m_state.setFoundConstants(true);
+                break;
+            }
+        }
         break;
     }
 
-    case StoreBarrierWithNullCheck: {
+    case StoreBarrier: {
+        filter(node->child1(), SpecCell);
         break;
     }
 
     case CheckTierUpAndOSREnter:
+    case CheckTierUpWithNestedTriggerAndOSREnter:
     case LoopHint:
-        // We pretend that it can exit because it may want to get all state.
-        node->setCanExit(true);
+    case ZombieHint:
         break;
 
-    case ZombieHint:
     case Unreachable:
     case LastNodeType:
     case ArithIMul:
     case FiatInt52:
-        RELEASE_ASSERT_NOT_REACHED();
+        DFG_CRASH(m_graph, node, "Unexpected node type");
         break;
     }
     
@@ -1921,9 +2353,8 @@ template<typename AbstractStateType>
 bool AbstractInterpreter<AbstractStateType>::execute(unsigned indexInBlock)
 {
     Node* node = m_state.block()->at(indexInBlock);
-    if (!startExecuting(node))
-        return true;
     
+    startExecuting();
     executeEdges(node);
     return executeEffects(indexInBlock, node);
 }
@@ -1931,79 +2362,99 @@ bool AbstractInterpreter<AbstractStateType>::execute(unsigned indexInBlock)
 template<typename AbstractStateType>
 bool AbstractInterpreter<AbstractStateType>::execute(Node* node)
 {
-    if (!startExecuting(node))
-        return true;
-    
+    startExecuting();
     executeEdges(node);
     return executeEffects(UINT_MAX, node);
 }
 
 template<typename AbstractStateType>
 void AbstractInterpreter<AbstractStateType>::clobberWorld(
-    const CodeOrigin& codeOrigin, unsigned clobberLimit)
+    const CodeOrigin&, unsigned clobberLimit)
 {
-    clobberCapturedVars(codeOrigin);
     clobberStructures(clobberLimit);
 }
 
 template<typename AbstractStateType>
-void AbstractInterpreter<AbstractStateType>::clobberCapturedVars(const CodeOrigin& codeOrigin)
+template<typename Functor>
+void AbstractInterpreter<AbstractStateType>::forAllValues(
+    unsigned clobberLimit, Functor& functor)
 {
-    if (codeOrigin.inlineCallFrame) {
-        const BitVector& capturedVars = codeOrigin.inlineCallFrame->capturedVars;
-        for (size_t i = capturedVars.size(); i--;) {
-            if (!capturedVars.quickGet(i))
-                continue;
-            m_state.variables().local(i).makeHeapTop();
-        }
-    } else {
-        for (size_t i = m_codeBlock->m_numVars; i--;) {
-            if (m_codeBlock->isCaptured(virtualRegisterForLocal(i)))
-                m_state.variables().local(i).makeHeapTop();
-        }
-    }
-
-    for (size_t i = m_state.variables().numberOfArguments(); i--;) {
-        if (m_codeBlock->isCaptured(virtualRegisterForArgument(i)))
-            m_state.variables().argument(i).makeHeapTop();
-    }
-}
-
-template<typename AbstractStateType>
-void AbstractInterpreter<AbstractStateType>::clobberStructures(unsigned clobberLimit)
-{
-    if (!m_state.haveStructures())
-        return;
+    SamplingRegion samplingRegion("DFG AI For All Values");
     if (clobberLimit >= m_state.block()->size())
         clobberLimit = m_state.block()->size();
     else
         clobberLimit++;
     ASSERT(clobberLimit <= m_state.block()->size());
     for (size_t i = clobberLimit; i--;)
-        forNode(m_state.block()->at(i)).clobberStructures();
+        functor(forNode(m_state.block()->at(i)));
     if (m_graph.m_form == SSA) {
         HashSet<Node*>::iterator iter = m_state.block()->ssa->liveAtHead.begin();
         HashSet<Node*>::iterator end = m_state.block()->ssa->liveAtHead.end();
         for (; iter != end; ++iter)
-            forNode(*iter).clobberStructures();
+            functor(forNode(*iter));
     }
     for (size_t i = m_state.variables().numberOfArguments(); i--;)
-        m_state.variables().argument(i).clobberStructures();
+        functor(m_state.variables().argument(i));
     for (size_t i = m_state.variables().numberOfLocals(); i--;)
-        m_state.variables().local(i).clobberStructures();
-    m_state.setHaveStructures(true);
+        functor(m_state.variables().local(i));
+}
+
+template<typename AbstractStateType>
+void AbstractInterpreter<AbstractStateType>::clobberStructures(unsigned clobberLimit)
+{
+    SamplingRegion samplingRegion("DFG AI Clobber Structures");
+    forAllValues(clobberLimit, AbstractValue::clobberStructuresFor);
+    setDidClobber();
+}
+
+template<typename AbstractStateType>
+void AbstractInterpreter<AbstractStateType>::observeTransition(
+    unsigned clobberLimit, Structure* from, Structure* to)
+{
+    AbstractValue::TransitionObserver transitionObserver(from, to);
+    forAllValues(clobberLimit, transitionObserver);
+    
+    ASSERT(!from->dfgShouldWatch()); // We don't need to claim to be in a clobbered state because 'from' was never watchable (during the time we were compiling), hence no constants ever introduced into the DFG IR that ever had a watchable structure would ever have the same structure as from.
+}
+
+template<typename AbstractStateType>
+void AbstractInterpreter<AbstractStateType>::observeTransitions(
+    unsigned clobberLimit, const TransitionVector& vector)
+{
+    AbstractValue::TransitionsObserver transitionsObserver(vector);
+    forAllValues(clobberLimit, transitionsObserver);
+    
+    if (!ASSERT_DISABLED) {
+        // We don't need to claim to be in a clobbered state because none of the Transition::previous structures are watchable.
+        for (unsigned i = vector.size(); i--;)
+            ASSERT(!vector[i].previous->dfgShouldWatch());
+    }
+}
+
+template<typename AbstractStateType>
+void AbstractInterpreter<AbstractStateType>::setDidClobber()
+{
     m_state.setDidClobber(true);
+    m_state.setStructureClobberState(StructuresAreClobbered);
+}
+
+template<typename AbstractStateType>
+void AbstractInterpreter<AbstractStateType>::dump(PrintStream& out) const
+{
+    const_cast<AbstractInterpreter<AbstractStateType>*>(this)->dump(out);
 }
 
 template<typename AbstractStateType>
 void AbstractInterpreter<AbstractStateType>::dump(PrintStream& out)
 {
     CommaPrinter comma(" ");
+    HashSet<Node*> seen;
     if (m_graph.m_form == SSA) {
         HashSet<Node*>::iterator iter = m_state.block()->ssa->liveAtHead.begin();
         HashSet<Node*>::iterator end = m_state.block()->ssa->liveAtHead.end();
         for (; iter != end; ++iter) {
             Node* node = *iter;
+            seen.add(node);
             AbstractValue& value = forNode(node);
             if (value.isClear())
                 continue;
@@ -2012,11 +2463,25 @@ void AbstractInterpreter<AbstractStateType>::dump(PrintStream& out)
     }
     for (size_t i = 0; i < m_state.block()->size(); ++i) {
         Node* node = m_state.block()->at(i);
+        seen.add(node);
         AbstractValue& value = forNode(node);
         if (value.isClear())
             continue;
         out.print(comma, node, ":", value);
     }
+    if (m_graph.m_form == SSA) {
+        HashSet<Node*>::iterator iter = m_state.block()->ssa->liveAtTail.begin();
+        HashSet<Node*>::iterator end = m_state.block()->ssa->liveAtTail.end();
+        for (; iter != end; ++iter) {
+            Node* node = *iter;
+            if (seen.contains(node))
+                continue;
+            AbstractValue& value = forNode(node);
+            if (value.isClear())
+                continue;
+            out.print(comma, node, ":", value);
+        }
+    }
 }
 
 template<typename AbstractStateType>
@@ -2051,7 +2516,7 @@ FiltrationResult AbstractInterpreter<AbstractStateType>::filter(
 
 template<typename AbstractStateType>
 FiltrationResult AbstractInterpreter<AbstractStateType>::filterByValue(
-    AbstractValue& abstractValue, JSValue concreteValue)
+    AbstractValue& abstractValue, FrozenValue concreteValue)
 {
     if (abstractValue.filterByValue(concreteValue) == FiltrationOK)
         return FiltrationOK;