]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - dfg/DFGConstantFoldingPhase.cpp
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / dfg / DFGConstantFoldingPhase.cpp
index ad699e7fbc1cb84575d9794f8eda49e78f4e31e3..fd8df4d99418c637d48dfa19a38830a5fec238c4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-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
 
 #if ENABLE(DFG_JIT)
 
-#include "DFGAbstractState.h"
+#include "DFGAbstractInterpreterInlines.h"
+#include "DFGArgumentsUtilities.h"
 #include "DFGBasicBlock.h"
 #include "DFGGraph.h"
+#include "DFGInPlaceAbstractState.h"
 #include "DFGInsertionSet.h"
 #include "DFGPhase.h"
 #include "GetByIdStatus.h"
-#include "Operations.h"
+#include "JSCInlines.h"
 #include "PutByIdStatus.h"
 
 namespace JSC { namespace DFG {
@@ -44,6 +46,7 @@ public:
     ConstantFoldingPhase(Graph& graph)
         : Phase(graph, "constant folding")
         , m_state(graph)
+        , m_interpreter(graph, m_state)
         , m_insertionSet(graph)
     {
     }
@@ -52,26 +55,30 @@ public:
     {
         bool changed = false;
         
-        for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
-            BasicBlock* block = m_graph.m_blocks[blockIndex].get();
+        for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) {
+            BasicBlock* block = m_graph.block(blockIndex);
             if (!block)
                 continue;
-            if (!block->cfaDidFinish)
-                changed |= paintUnreachableCode(blockIndex);
             if (block->cfaFoundConstants)
-                changed |= foldConstants(blockIndex);
+                changed |= foldConstants(block);
         }
         
+        if (changed && m_graph.m_form == SSA) {
+            // It's now possible that we have Upsilons pointed at JSConstants. Fix that.
+            for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) {
+                BasicBlock* block = m_graph.block(blockIndex);
+                if (!block)
+                    continue;
+                fixUpsilons(block);
+            }
+        }
+         
         return changed;
     }
 
 private:
-    bool foldConstants(BlockIndex blockIndex)
+    bool foldConstants(BasicBlock* block)
     {
-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
-        dataLogF("Constant folding considering Block #%u.\n", blockIndex);
-#endif
-        BasicBlock* block = m_graph.m_blocks[blockIndex].get();
         bool changed = false;
         m_state.beginBasicBlock(block);
         for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
@@ -80,21 +87,18 @@ private:
             
             Node* node = block->at(indexInBlock);
 
+            bool alreadyHandled = false;
             bool eliminated = false;
                     
             switch (node->op()) {
-            case CheckArgumentsNotCreated: {
-                if (!isEmptySpeculation(
-                        m_state.variables().operand(
-                            m_graph.argumentsRegisterFor(node->codeOrigin)).m_type))
-                    break;
-                node->convertToPhantom();
-                eliminated = true;
+            case BooleanToNumber: {
+                if (node->child1().useKind() == UntypedUse
+                    && !m_interpreter.needsTypeCheck(node->child1(), SpecBoolean))
+                    node->child1().setUseKind(BooleanUse);
                 break;
             }
-                    
+                
             case CheckStructure:
-            case ForwardCheckStructure:
             case ArrayifyToStructure: {
                 AbstractValue& value = m_state.forNode(node->child1());
                 StructureSet set;
@@ -102,21 +106,71 @@ private:
                     set = node->structure();
                 else
                     set = node->structureSet();
-                if (value.m_currentKnownStructure.isSubsetOf(set)) {
-                    m_state.execute(indexInBlock); // Catch the fact that we may filter on cell.
-                    node->convertToPhantom();
+                if (value.m_structure.isSubsetOf(set)) {
+                    m_interpreter.execute(indexInBlock); // Catch the fact that we may filter on cell.
+                    node->remove();
                     eliminated = true;
                     break;
                 }
-                StructureAbstractValue& structureValue = value.m_futurePossibleStructure;
-                if (structureValue.isSubsetOf(set)
-                    && structureValue.hasSingleton()) {
-                    Structure* structure = structureValue.singleton();
-                    m_state.execute(indexInBlock); // Catch the fact that we may filter on cell.
-                    node->convertToStructureTransitionWatchpoint(structure);
-                    eliminated = true;
+                break;
+            }
+                
+            case GetIndexedPropertyStorage: {
+                JSArrayBufferView* view = m_graph.tryGetFoldableView(
+                    m_state.forNode(node->child1()).m_value, node->arrayMode());
+                if (!view)
+                    break;
+                
+                if (view->mode() == FastTypedArray) {
+                    // FIXME: It would be awesome to be able to fold the property storage for
+                    // these GC-allocated typed arrays. For now it doesn't matter because the
+                    // most common use-cases for constant typed arrays involve large arrays with
+                    // aliased buffer views.
+                    // https://bugs.webkit.org/show_bug.cgi?id=125425
                     break;
                 }
+                
+                m_interpreter.execute(indexInBlock);
+                eliminated = true;
+                
+                m_insertionSet.insertCheck(indexInBlock, node->origin, node->children);
+                node->convertToConstantStoragePointer(view->vector());
+                break;
+            }
+                
+            case CheckStructureImmediate: {
+                AbstractValue& value = m_state.forNode(node->child1());
+                StructureSet& set = node->structureSet();
+                
+                if (value.value()) {
+                    if (Structure* structure = jsDynamicCast<Structure*>(value.value())) {
+                        if (set.contains(structure)) {
+                            m_interpreter.execute(indexInBlock);
+                            node->remove();
+                            eliminated = true;
+                            break;
+                        }
+                    }
+                }
+                
+                if (PhiChildren* phiChildren = m_interpreter.phiChildren()) {
+                    bool allGood = true;
+                    phiChildren->forAllTransitiveIncomingValues(
+                        node,
+                        [&] (Node* incoming) {
+                            if (Structure* structure = incoming->dynamicCastConstant<Structure*>()) {
+                                if (set.contains(structure))
+                                    return;
+                            }
+                            allGood = false;
+                        });
+                    if (allGood) {
+                        m_interpreter.execute(indexInBlock);
+                        node->remove();
+                        eliminated = true;
+                        break;
+                    }
+                }
                 break;
             }
                 
@@ -124,249 +178,349 @@ private:
             case Arrayify: {
                 if (!node->arrayMode().alreadyChecked(m_graph, node, m_state.forNode(node->child1())))
                     break;
-                node->convertToPhantom();
+                node->remove();
                 eliminated = true;
                 break;
             }
                 
-            case CheckFunction: {
-                if (m_state.forNode(node->child1()).value() != node->function())
+            case PutStructure: {
+                if (m_state.forNode(node->child1()).m_structure.onlyStructure() != node->transition()->next)
                     break;
-                node->convertToPhantom();
+                
+                node->remove();
                 eliminated = true;
                 break;
             }
                 
-            case GetById:
-            case GetByIdFlush: {
-                CodeOrigin codeOrigin = node->codeOrigin;
-                Edge childEdge = node->child1();
-                Node* child = childEdge.node();
-                unsigned identifierNumber = node->identifierNumber();
-                
-                if (childEdge.useKind() != CellUse)
+            case CheckCell: {
+                if (m_state.forNode(node->child1()).value() != node->cellOperand()->value())
+                    break;
+                node->remove();
+                eliminated = true;
+                break;
+            }
+
+            case CheckNotEmpty: {
+                if (m_state.forNode(node->child1()).m_type & SpecEmpty)
                     break;
+                node->remove();
+                eliminated = true;
+                break;
+            }
+
+            case CheckInBounds: {
+                JSValue left = m_state.forNode(node->child1()).value();
+                JSValue right = m_state.forNode(node->child2()).value();
+                if (left && right && left.isInt32() && right.isInt32()
+                    && static_cast<uint32_t>(left.asInt32()) < static_cast<uint32_t>(right.asInt32())) {
+                    node->remove();
+                    eliminated = true;
+                    break;
+                }
+                
+                break;
+            }
                 
-                Structure* structure = m_state.forNode(child).bestProvenStructure();
-                if (!structure)
+            case GetMyArgumentByVal: {
+                JSValue index = m_state.forNode(node->child2()).value();
+                if (!index || !index.isInt32())
                     break;
                 
-                bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton();
-                bool needsCellCheck = m_state.forNode(child).m_type & ~SpecCell;
+                Node* arguments = node->child1().node();
+                InlineCallFrame* inlineCallFrame = arguments->origin.semantic.inlineCallFrame;
+                
+                // Don't try to do anything if the index is known to be outside our static bounds. Note
+                // that our static bounds are usually strictly larger than the dynamic bounds. The
+                // exception is something like this, assuming foo() is not inlined:
+                //
+                // function foo() { return arguments[5]; }
+                //
+                // Here the static bound on number of arguments is 0, and we're accessing index 5. We
+                // will not strength-reduce this to GetStack because GetStack is otherwise assumed by the
+                // compiler to access those variables that are statically accounted for; for example if
+                // we emitted a GetStack on arg6 we would have out-of-bounds access crashes anywhere that
+                // uses an Operands<> map. There is not much cost to continuing to use a
+                // GetMyArgumentByVal in such statically-out-of-bounds accesses; we just lose CFA unless
+                // GCSE removes the access entirely.
+                if (inlineCallFrame) {
+                    if (index.asUInt32() >= inlineCallFrame->arguments.size() - 1)
+                        break;
+                } else {
+                    if (index.asUInt32() >= m_state.variables().numberOfArguments() - 1)
+                        break;
+                }
                 
-                GetByIdStatus status = GetByIdStatus::computeFor(
-                    vm(), structure, codeBlock()->identifier(identifierNumber));
+                m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before.
                 
-                if (!status.isSimple()) {
-                    // FIXME: We could handle prototype cases.
-                    // https://bugs.webkit.org/show_bug.cgi?id=110386
-                    break;
+                StackAccessData* data;
+                if (inlineCallFrame) {
+                    data = m_graph.m_stackAccessData.add(
+                        VirtualRegister(
+                            inlineCallFrame->stackOffset +
+                            CallFrame::argumentOffset(index.asInt32())),
+                        FlushedJSValue);
+                } else {
+                    data = m_graph.m_stackAccessData.add(
+                        virtualRegisterForArgument(index.asInt32() + 1), FlushedJSValue);
                 }
                 
-                ASSERT(status.structureSet().size() == 1);
-                ASSERT(status.chain().isEmpty());
-                ASSERT(status.structureSet().singletonStructure() == structure);
+                if (inlineCallFrame && !inlineCallFrame->isVarargs()
+                    && index.asUInt32() < inlineCallFrame->arguments.size() - 1) {
+                    node->convertToGetStack(data);
+                    eliminated = true;
+                    break;
+                }
                 
-                // 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);
+                Node* length = emitCodeToGetArgumentsArrayLength(
+                    m_insertionSet, arguments, indexInBlock, node->origin);
+                m_insertionSet.insertNode(
+                    indexInBlock, SpecNone, CheckInBounds, node->origin,
+                    node->child2(), Edge(length, Int32Use));
+                node->convertToGetStack(data);
                 eliminated = true;
+                break;
+            }
+                
+            case MultiGetByOffset: {
+                Edge baseEdge = node->child1();
+                Node* base = baseEdge.node();
+                MultiGetByOffsetData& data = node->multiGetByOffsetData();
+
+                // First prune the variants, then check if the MultiGetByOffset can be
+                // strength-reduced to a GetByOffset.
                 
-                if (needsWatchpoint) {
-                    ASSERT(m_state.forNode(child).m_futurePossibleStructure.isSubsetOf(StructureSet(structure)));
-                    m_insertionSet.insertNode(
-                        indexInBlock, SpecNone, StructureTransitionWatchpoint, codeOrigin,
-                        OpInfo(structure), childEdge);
-                } else if (needsCellCheck) {
-                    m_insertionSet.insertNode(
-                        indexInBlock, SpecNone, Phantom, codeOrigin, childEdge);
+                AbstractValue baseValue = m_state.forNode(base);
+                
+                m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before.
+                alreadyHandled = true; // Don't allow the default constant folder to do things to this.
+                
+                for (unsigned i = 0; i < data.variants.size(); ++i) {
+                    GetByIdVariant& variant = data.variants[i];
+                    variant.structureSet().filter(baseValue);
+                    if (variant.structureSet().isEmpty()) {
+                        data.variants[i--] = data.variants.last();
+                        data.variants.removeLast();
+                        changed = true;
+                    }
+                }
+                
+                if (data.variants.size() != 1)
+                    break;
+                
+                emitGetByOffset(
+                    indexInBlock, node, baseValue, data.variants[0], data.identifierNumber);
+                changed = true;
+                break;
+            }
+                
+            case MultiPutByOffset: {
+                Edge baseEdge = node->child1();
+                Node* base = baseEdge.node();
+                MultiPutByOffsetData& data = node->multiPutByOffsetData();
+                
+                AbstractValue baseValue = m_state.forNode(base);
+
+                m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before.
+                alreadyHandled = true; // Don't allow the default constant folder to do things to this.
+                
+
+                for (unsigned i = 0; i < data.variants.size(); ++i) {
+                    PutByIdVariant& variant = data.variants[i];
+                    variant.oldStructure().filter(baseValue);
+                    
+                    if (variant.oldStructure().isEmpty()) {
+                        data.variants[i--] = data.variants.last();
+                        data.variants.removeLast();
+                        changed = true;
+                        continue;
+                    }
+                    
+                    if (variant.kind() == PutByIdVariant::Transition
+                        && variant.oldStructure().onlyStructure() == variant.newStructure()) {
+                        variant = PutByIdVariant::replace(
+                            variant.oldStructure(),
+                            variant.offset());
+                        changed = true;
+                    }
                 }
+
+                if (data.variants.size() != 1)
+                    break;
                 
-                childEdge.setUseKind(KnownCellUse);
+                emitPutByOffset(
+                    indexInBlock, node, baseValue, data.variants[0], data.identifierNumber);
+                changed = true;
+                break;
+            }
+        
+            case GetById:
+            case GetByIdFlush: {
+                Edge childEdge = node->child1();
+                Node* child = childEdge.node();
+                unsigned identifierNumber = node->identifierNumber();
                 
-                Edge propertyStorage;
+                AbstractValue baseValue = m_state.forNode(child);
+
+                m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before.
+                alreadyHandled = true; // Don't allow the default constant folder to do things to this.
+
+                if (baseValue.m_structure.isTop() || baseValue.m_structure.isClobbered()
+                    || (node->child1().useKind() == UntypedUse || (baseValue.m_type & ~SpecCell)))
+                    break;
                 
-                if (isInlineOffset(status.offset()))
-                    propertyStorage = childEdge;
-                else {
-                    propertyStorage = Edge(m_insertionSet.insertNode(
-                        indexInBlock, SpecNone, GetButterfly, codeOrigin, childEdge));
+                GetByIdStatus status = GetByIdStatus::computeFor(
+                    baseValue.m_structure.set(), m_graph.identifiers()[identifierNumber]);
+                if (!status.isSimple())
+                    break;
+                
+                for (unsigned i = status.numVariants(); i--;) {
+                    if (!status[i].constantChecks().isEmpty()
+                        || status[i].alternateBase()) {
+                        // FIXME: We could handle prototype cases.
+                        // https://bugs.webkit.org/show_bug.cgi?id=110386
+                        break;
+                    }
                 }
                 
-                node->convertToGetByOffset(m_graph.m_storageAccessData.size(), propertyStorage);
+                if (status.numVariants() == 1) {
+                    emitGetByOffset(indexInBlock, node, baseValue, status[0], identifierNumber);
+                    changed = true;
+                    break;
+                }
+                
+                if (!isFTL(m_graph.m_plan.mode))
+                    break;
                 
-                StorageAccessData storageAccessData;
-                storageAccessData.offset = indexRelativeToBase(status.offset());
-                storageAccessData.identifierNumber = identifierNumber;
-                m_graph.m_storageAccessData.append(storageAccessData);
+                MultiGetByOffsetData* data = m_graph.m_multiGetByOffsetData.add();
+                data->variants = status.variants();
+                data->identifierNumber = identifierNumber;
+                node->convertToMultiGetByOffset(data);
+                changed = true;
                 break;
             }
                 
             case PutById:
-            case PutByIdDirect: {
-                CodeOrigin codeOrigin = node->codeOrigin;
+            case PutByIdDirect:
+            case PutByIdFlush: {
+                NodeOrigin origin = node->origin;
                 Edge childEdge = node->child1();
                 Node* child = childEdge.node();
                 unsigned identifierNumber = node->identifierNumber();
                 
                 ASSERT(childEdge.useKind() == CellUse);
                 
-                Structure* structure = m_state.forNode(child).bestProvenStructure();
-                if (!structure)
+                AbstractValue baseValue = m_state.forNode(child);
+
+                m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before.
+                alreadyHandled = true; // Don't allow the default constant folder to do things to this.
+
+                if (baseValue.m_structure.isTop() || baseValue.m_structure.isClobbered())
                     break;
                 
-                bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton();
-                bool needsCellCheck = m_state.forNode(child).m_type & ~SpecCell;
-                
                 PutByIdStatus status = PutByIdStatus::computeFor(
-                    vm(),
-                    m_graph.globalObjectFor(codeOrigin),
-                    structure,
-                    codeBlock()->identifier(identifierNumber),
+                    m_graph.globalObjectFor(origin.semantic),
+                    baseValue.m_structure.set(),
+                    m_graph.identifiers()[identifierNumber],
                     node->op() == PutByIdDirect);
                 
-                if (!status.isSimpleReplace() && !status.isSimpleTransition())
+                if (!status.isSimple())
                     break;
                 
-                ASSERT(status.oldStructure() == structure);
-                
-                // 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);
-                eliminated = true;
+                ASSERT(status.numVariants());
                 
-                if (needsWatchpoint) {
-                    ASSERT(m_state.forNode(child).m_futurePossibleStructure.isSubsetOf(StructureSet(structure)));
-                    m_insertionSet.insertNode(
-                        indexInBlock, SpecNone, StructureTransitionWatchpoint, codeOrigin,
-                        OpInfo(structure), childEdge);
-                } else if (needsCellCheck) {
-                    m_insertionSet.insertNode(
-                        indexInBlock, SpecNone, Phantom, codeOrigin, childEdge);
-                }
+                if (status.numVariants() > 1 && !isFTL(m_graph.m_plan.mode))
+                    break;
                 
-                childEdge.setUseKind(KnownCellUse);
+                changed = true;
                 
-                StructureTransitionData* transitionData = 0;
-                if (status.isSimpleTransition()) {
-                    transitionData = m_graph.addStructureTransitionData(
-                        StructureTransitionData(structure, status.newStructure()));
-                    
-                    if (node->op() == PutById) {
-                        if (!structure->storedPrototype().isNull()) {
-                            addStructureTransitionCheck(
-                                codeOrigin, indexInBlock,
-                                structure->storedPrototype().asCell());
-                        }
-                        
-                        for (WriteBarrier<Structure>* it = status.structureChain()->head(); *it; ++it) {
-                            JSValue prototype = (*it)->storedPrototype();
-                            if (prototype.isNull())
-                                continue;
-                            ASSERT(prototype.isCell());
-                            addStructureTransitionCheck(
-                                codeOrigin, indexInBlock, prototype.asCell());
-                        }
-                    }
-                }
+                for (unsigned i = status.numVariants(); i--;)
+                    addChecks(origin, indexInBlock, status[i].constantChecks());
                 
-                Edge propertyStorage;
-                
-                if (isInlineOffset(status.offset()))
-                    propertyStorage = childEdge;
-                else if (status.isSimpleReplace() || structure->outOfLineCapacity() == status.newStructure()->outOfLineCapacity()) {
-                    propertyStorage = Edge(m_insertionSet.insertNode(
-                        indexInBlock, SpecNone, GetButterfly, codeOrigin, childEdge));
-                } else if (!structure->outOfLineCapacity()) {
-                    ASSERT(status.newStructure()->outOfLineCapacity());
-                    ASSERT(!isInlineOffset(status.offset()));
-                    propertyStorage = Edge(m_insertionSet.insertNode(
-                        indexInBlock, SpecNone, AllocatePropertyStorage,
-                        codeOrigin, OpInfo(transitionData), childEdge));
-                } else {
-                    ASSERT(structure->outOfLineCapacity());
-                    ASSERT(status.newStructure()->outOfLineCapacity() > structure->outOfLineCapacity());
-                    ASSERT(!isInlineOffset(status.offset()));
-                    
-                    propertyStorage = Edge(m_insertionSet.insertNode(
-                        indexInBlock, SpecNone, ReallocatePropertyStorage, codeOrigin,
-                        OpInfo(transitionData), childEdge,
-                        Edge(m_insertionSet.insertNode(
-                            indexInBlock, SpecNone, GetButterfly, codeOrigin, childEdge))));
+                if (status.numVariants() == 1) {
+                    emitPutByOffset(indexInBlock, node, baseValue, status[0], identifierNumber);
+                    break;
                 }
                 
-                if (status.isSimpleTransition()) {
-                    m_insertionSet.insertNode(
-                        indexInBlock, SpecNone, PutStructure, codeOrigin, 
-                        OpInfo(transitionData), childEdge);
-                }
+                ASSERT(isFTL(m_graph.m_plan.mode));
+
+                MultiPutByOffsetData* data = m_graph.m_multiPutByOffsetData.add();
+                data->variants = status.variants();
+                data->identifierNumber = identifierNumber;
+                node->convertToMultiPutByOffset(data);
+                break;
+            }
+
+            case ToPrimitive: {
+                if (m_state.forNode(node->child1()).m_type & ~(SpecFullNumber | SpecBoolean | SpecString | SpecSymbol))
+                    break;
                 
-                node->convertToPutByOffset(m_graph.m_storageAccessData.size(), propertyStorage);
+                node->convertToIdentity();
+                changed = true;
+                break;
+            }
                 
-                StorageAccessData storageAccessData;
-                storageAccessData.offset = indexRelativeToBase(status.offset());
-                storageAccessData.identifierNumber = identifierNumber;
-                m_graph.m_storageAccessData.append(storageAccessData);
+            case Check: {
+                alreadyHandled = true;
+                m_interpreter.execute(indexInBlock);
+                for (unsigned i = 0; i < AdjacencyList::Size; ++i) {
+                    Edge edge = node->children.child(i);
+                    if (!edge)
+                        break;
+                    if (edge.isProved() || edge.willNotHaveCheck()) {
+                        node->children.removeEdge(i--);
+                        changed = true;
+                    }
+                }
                 break;
             }
                 
             default:
                 break;
             }
-                
+            
             if (eliminated) {
                 changed = true;
                 continue;
             }
                 
-            m_state.execute(indexInBlock);
+            if (alreadyHandled)
+                continue;
+            
+            m_interpreter.execute(indexInBlock);
+            if (!m_state.isValid()) {
+                // If we invalidated then we shouldn't attempt to constant-fold. Here's an
+                // example:
+                //
+                //     c: JSConstant(4.2)
+                //     x: ValueToInt32(Check:Int32:@const)
+                //
+                // It would be correct for an analysis to assume that execution cannot
+                // proceed past @x. Therefore, constant-folding @x could be rather bad. But,
+                // the CFA may report that it found a constant even though it also reported
+                // that everything has been invalidated. This will only happen in a couple of
+                // the constant folding cases; most of them are also separately defensive
+                // about such things.
+                break;
+            }
             if (!node->shouldGenerate() || m_state.didClobber() || node->hasConstant())
                 continue;
-            JSValue value = m_state.forNode(node).value();
-            if (!value)
+            
+            // Interesting fact: this freezing that we do right here may turn an fragile value into
+            // a weak value. See DFGValueStrength.h.
+            FrozenValue* value = m_graph.freeze(m_state.forNode(node).value());
+            if (!*value)
                 continue;
-                
-            CodeOrigin codeOrigin = node->codeOrigin;
-            AdjacencyList children = node->children;
             
             if (node->op() == GetLocal) {
-                // GetLocals without a Phi child are guaranteed dead. We don't have to
-                // do anything about them.
-                if (!node->child1())
-                    continue;
-                
-                if (m_graph.m_form != LoadStore) {
-                    VariableAccessData* variable = node->variableAccessData();
-                    Node* phi = node->child1().node();
-                    if (phi->op() == Phi
-                        && block->variablesAtHead.operand(variable->local()) == phi
-                        && block->variablesAtTail.operand(variable->local()) == node) {
-                        
-                        // Keep the graph threaded for easy cases. This is improves compile
-                        // times. It would be correct to just dethread here.
-                        
-                        m_graph.convertToConstant(node, value);
-                        Node* phantom = m_insertionSet.insertNode(
-                            indexInBlock, SpecNone, PhantomLocal,  codeOrigin,
-                            OpInfo(variable), Edge(phi));
-                        block->variablesAtHead.operand(variable->local()) = phantom;
-                        block->variablesAtTail.operand(variable->local()) = phantom;
-                        
-                        changed = true;
-                        
-                        continue;
-                    }
-                    
-                    m_graph.dethread();
-                }
+                // Need to preserve bytecode liveness in ThreadedCPS form. This wouldn't be necessary
+                // if it wasn't for https://bugs.webkit.org/show_bug.cgi?id=144086.
+                m_insertionSet.insertNode(
+                    indexInBlock, SpecNone, PhantomLocal, node->origin,
+                    OpInfo(node->variableAccessData()));
+                m_graph.dethread();
             } else
-                ASSERT(!node->hasVariableAccessData());
-            
+                m_insertionSet.insertCheck(indexInBlock, node->origin, node->children);
             m_graph.convertToConstant(node, value);
-            m_insertionSet.insertNode(
-                indexInBlock, SpecNone, Phantom, codeOrigin, children);
             
             changed = true;
         }
@@ -375,85 +529,168 @@ private:
         
         return changed;
     }
-    
-#if !ASSERT_DISABLED
-    bool isCapturedAtOrAfter(BasicBlock* block, unsigned indexInBlock, int operand)
+        
+    void emitGetByOffset(unsigned indexInBlock, Node* node, const AbstractValue& baseValue, const GetByIdVariant& variant, unsigned identifierNumber)
     {
-        for (; indexInBlock < block->size(); ++indexInBlock) {
-            Node* node = block->at(indexInBlock);
-            if (!node->hasLocal())
-                continue;
-            if (node->local() != operand)
-                continue;
-            if (node->variableAccessData()->isCaptured())
-                return true;
+        NodeOrigin origin = node->origin;
+        Edge childEdge = node->child1();
+
+        addBaseCheck(indexInBlock, node, baseValue, variant.structureSet());
+        
+        JSValue baseForLoad;
+        if (variant.alternateBase())
+            baseForLoad = variant.alternateBase();
+        else
+            baseForLoad = baseValue.m_value;
+        if (JSValue value = m_graph.tryGetConstantProperty(baseForLoad, variant.baseStructure(), variant.offset())) {
+            m_graph.convertToConstant(node, m_graph.freeze(value));
+            return;
         }
-        return false;
+        
+        if (variant.alternateBase()) {
+            Node* child = m_insertionSet.insertConstant(indexInBlock, origin, variant.alternateBase());
+            childEdge = Edge(child, KnownCellUse);
+        } else
+            childEdge.setUseKind(KnownCellUse);
+        
+        Edge propertyStorage;
+        
+        if (isInlineOffset(variant.offset()))
+            propertyStorage = childEdge;
+        else {
+            propertyStorage = Edge(m_insertionSet.insertNode(
+                indexInBlock, SpecNone, GetButterfly, origin, childEdge));
+        }
+        
+        StorageAccessData& data = *m_graph.m_storageAccessData.add();
+        data.offset = variant.offset();
+        data.identifierNumber = identifierNumber;
+        
+        node->convertToGetByOffset(data, propertyStorage);
     }
-#endif // !ASSERT_DISABLED
-    
-    void addStructureTransitionCheck(CodeOrigin codeOrigin, unsigned indexInBlock, JSCell* cell)
+
+    void emitPutByOffset(unsigned indexInBlock, Node* node, const AbstractValue& baseValue, const PutByIdVariant& variant, unsigned identifierNumber)
     {
-        Node* weakConstant = m_insertionSet.insertNode(
-            indexInBlock, speculationFromValue(cell), WeakJSConstant, codeOrigin, OpInfo(cell));
+        NodeOrigin origin = node->origin;
+        Edge childEdge = node->child1();
         
-        if (cell->structure()->transitionWatchpointSetIsStillValid()) {
+        addBaseCheck(indexInBlock, node, baseValue, variant.oldStructure());
+
+        childEdge.setUseKind(KnownCellUse);
+
+        Transition* transition = 0;
+        if (variant.kind() == PutByIdVariant::Transition) {
+            transition = m_graph.m_transitions.add(
+                variant.oldStructureForTransition(), variant.newStructure());
+        }
+
+        Edge propertyStorage;
+
+        if (isInlineOffset(variant.offset()))
+            propertyStorage = childEdge;
+        else if (!variant.reallocatesStorage()) {
+            propertyStorage = Edge(m_insertionSet.insertNode(
+                indexInBlock, SpecNone, GetButterfly, origin, childEdge));
+        } else if (!variant.oldStructureForTransition()->outOfLineCapacity()) {
+            ASSERT(variant.newStructure()->outOfLineCapacity());
+            ASSERT(!isInlineOffset(variant.offset()));
+            Node* allocatePropertyStorage = m_insertionSet.insertNode(
+                indexInBlock, SpecNone, AllocatePropertyStorage,
+                origin, OpInfo(transition), childEdge);
+            propertyStorage = Edge(allocatePropertyStorage);
+        } else {
+            ASSERT(variant.oldStructureForTransition()->outOfLineCapacity());
+            ASSERT(variant.newStructure()->outOfLineCapacity() > variant.oldStructureForTransition()->outOfLineCapacity());
+            ASSERT(!isInlineOffset(variant.offset()));
+
+            Node* reallocatePropertyStorage = m_insertionSet.insertNode(
+                indexInBlock, SpecNone, ReallocatePropertyStorage, origin,
+                OpInfo(transition), childEdge,
+                Edge(m_insertionSet.insertNode(
+                    indexInBlock, SpecNone, GetButterfly, origin, childEdge)));
+            propertyStorage = Edge(reallocatePropertyStorage);
+        }
+
+        StorageAccessData& data = *m_graph.m_storageAccessData.add();
+        data.offset = variant.offset();
+        data.identifierNumber = identifierNumber;
+        
+        node->convertToPutByOffset(data, propertyStorage);
+
+        if (variant.kind() == PutByIdVariant::Transition) {
+            // FIXME: PutStructure goes last until we fix either
+            // https://bugs.webkit.org/show_bug.cgi?id=142921 or
+            // https://bugs.webkit.org/show_bug.cgi?id=142924.
             m_insertionSet.insertNode(
-                indexInBlock, SpecNone, StructureTransitionWatchpoint, codeOrigin,
-                OpInfo(cell->structure()), Edge(weakConstant, CellUse));
+                indexInBlock + 1, SpecNone, PutStructure, origin, OpInfo(transition), childEdge);
+        }
+    }
+    
+    void addBaseCheck(
+        unsigned indexInBlock, Node* node, const AbstractValue& baseValue, const StructureSet& set)
+    {
+        if (!baseValue.m_structure.isSubsetOf(set)) {
+            // Arises when we prune MultiGetByOffset. We could have a
+            // MultiGetByOffset with a single variant that checks for structure S,
+            // and the input has structures S and T, for example.
+            m_insertionSet.insertNode(
+                indexInBlock, SpecNone, CheckStructure, node->origin,
+                OpInfo(m_graph.addStructureSet(set)), node->child1());
             return;
         }
-
-        m_insertionSet.insertNode(
-            indexInBlock, SpecNone, CheckStructure, codeOrigin,
-            OpInfo(m_graph.addStructureSet(cell->structure())), Edge(weakConstant, CellUse));
+        
+        if (baseValue.m_type & ~SpecCell)
+            m_insertionSet.insertCheck(indexInBlock, node->origin, node->child1());
     }
     
-    // This is necessary because the CFA may reach conclusions about constants based on its
-    // assumption that certain code must exit, but then those constants may lead future
-    // reexecutions of the CFA to believe that the same code will now no longer exit. Thus
-    // 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.
-    bool paintUnreachableCode(BlockIndex blockIndex)
+    void addChecks(
+        NodeOrigin origin, unsigned indexInBlock, const ConstantStructureCheckVector& checks)
     {
-        bool changed = false;
+        for (unsigned i = 0; i < checks.size(); ++i) {
+            addStructureTransitionCheck(
+                origin, indexInBlock, checks[i].constant(), checks[i].structure());
+        }
+    }
+
+    void addStructureTransitionCheck(NodeOrigin origin, unsigned indexInBlock, JSCell* cell, Structure* structure)
+    {
+        if (m_graph.registerStructure(cell->structure()) == StructureRegisteredAndWatched)
+            return;
         
-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
-        dataLogF("Painting unreachable code in Block #%u.\n", blockIndex);
-#endif
-        BasicBlock* block = m_graph.m_blocks[blockIndex].get();
-        m_state.beginBasicBlock(block);
+        m_graph.registerStructure(structure);
+
+        Node* weakConstant = m_insertionSet.insertNode(
+            indexInBlock, speculationFromValue(cell), JSConstant, origin,
+            OpInfo(m_graph.freeze(cell)));
         
-        for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
-            m_state.execute(indexInBlock);
-            if (m_state.isValid())
+        m_insertionSet.insertNode(
+            indexInBlock, SpecNone, CheckStructure, origin,
+            OpInfo(m_graph.addStructureSet(structure)), Edge(weakConstant, CellUse));
+    }
+    
+    void fixUpsilons(BasicBlock* block)
+    {
+        for (unsigned nodeIndex = block->size(); nodeIndex--;) {
+            Node* node = block->at(nodeIndex);
+            if (node->op() != Upsilon)
                 continue;
-            
-            Node* node = block->at(indexInBlock);
-            switch (node->op()) {
-            case Return:
-            case Unreachable:
-            case ForceOSRExit:
-                // Do nothing. These nodes will already do the right thing.
+            switch (node->phi()->op()) {
+            case Phi:
+                break;
+            case JSConstant:
+            case DoubleConstant:
+            case Int52Constant:
+                node->remove();
                 break;
-                
             default:
-                m_insertionSet.insertNode(
-                    indexInBlock, SpecNone, ForceOSRExit, node->codeOrigin);
-                changed = true;
+                DFG_CRASH(m_graph, node, "Bad Upsilon phi() pointer");
                 break;
             }
-            break;
         }
-        m_state.reset();
-        m_insertionSet.execute(block);
-        
-        return changed;
     }
-
-    AbstractState m_state;
+    
+    InPlaceAbstractState m_state;
+    AbstractInterpreter<InPlaceAbstractState> m_interpreter;
     InsertionSet m_insertionSet;
 };