]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - dfg/DFGArgumentsSimplificationPhase.cpp
JavaScriptCore-1218.tar.gz
[apple/javascriptcore.git] / dfg / DFGArgumentsSimplificationPhase.cpp
diff --git a/dfg/DFGArgumentsSimplificationPhase.cpp b/dfg/DFGArgumentsSimplificationPhase.cpp
new file mode 100644 (file)
index 0000000..06ce701
--- /dev/null
@@ -0,0 +1,866 @@
+/*
+ * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "DFGArgumentsSimplificationPhase.h"
+
+#if ENABLE(DFG_JIT)
+
+#include "DFGAbstractState.h"
+#include "DFGBasicBlock.h"
+#include "DFGGraph.h"
+#include "DFGInsertionSet.h"
+#include "DFGPhase.h"
+#include "DFGValidate.h"
+#include "DFGVariableAccessDataDump.h"
+#include <wtf/HashSet.h>
+#include <wtf/HashMap.h>
+
+#if PLATFORM(IOS)
+#include "Operations.h"
+#endif
+
+namespace JSC { namespace DFG {
+
+namespace {
+
+struct ArgumentsAliasingData {
+    InlineCallFrame* callContext;
+    bool callContextSet;
+    bool multipleCallContexts;
+    
+    bool assignedFromArguments;
+    bool assignedFromManyThings;
+    
+    bool escapes;
+    
+    ArgumentsAliasingData()
+        : callContext(0)
+        , callContextSet(false)
+        , multipleCallContexts(false)
+        , assignedFromArguments(false)
+        , assignedFromManyThings(false)
+        , escapes(false)
+    {
+    }
+    
+    void mergeCallContext(InlineCallFrame* newCallContext)
+    {
+        if (multipleCallContexts)
+            return;
+        
+        if (!callContextSet) {
+            callContext = newCallContext;
+            callContextSet = true;
+            return;
+        }
+        
+        if (callContext == newCallContext)
+            return;
+        
+        multipleCallContexts = true;
+    }
+    
+    bool callContextIsValid()
+    {
+        return callContextSet && !multipleCallContexts;
+    }
+    
+    void mergeArgumentsAssignment()
+    {
+        assignedFromArguments = true;
+    }
+    
+    void mergeNonArgumentsAssignment()
+    {
+        assignedFromManyThings = true;
+    }
+    
+    bool argumentsAssignmentIsValid()
+    {
+        return assignedFromArguments && !assignedFromManyThings;
+    }
+    
+    bool isValid()
+    {
+        return callContextIsValid() && argumentsAssignmentIsValid() && !escapes;
+    }
+};
+
+} // end anonymous namespace
+
+class ArgumentsSimplificationPhase : public Phase {
+public:
+    ArgumentsSimplificationPhase(Graph& graph)
+        : Phase(graph, "arguments simplification")
+    {
+    }
+    
+    bool run()
+    {
+        if (!m_graph.m_hasArguments)
+            return false;
+        
+        bool changed = false;
+        
+        // Record which arguments are known to escape no matter what.
+        for (unsigned i = codeBlock()->inlineCallFrames().size(); i--;) {
+            InlineCallFrame* inlineCallFrame = &codeBlock()->inlineCallFrames()[i];
+            if (m_graph.m_executablesWhoseArgumentsEscaped.contains(
+                    m_graph.executableFor(inlineCallFrame)))
+                m_createsArguments.add(inlineCallFrame);
+        }
+        
+        // Create data for variable access datas that we will want to analyze.
+        for (unsigned i = m_graph.m_variableAccessData.size(); i--;) {
+            VariableAccessData* variableAccessData = &m_graph.m_variableAccessData[i];
+            if (!variableAccessData->isRoot())
+                continue;
+            if (variableAccessData->isCaptured())
+                continue;
+            m_argumentsAliasing.add(variableAccessData, ArgumentsAliasingData());
+        }
+        
+        // Figure out which variables are live, using a conservative approximation of
+        // liveness.
+        for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
+            BasicBlock* block = m_graph.m_blocks[blockIndex].get();
+            if (!block)
+                continue;
+            for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
+                Node* node = block->at(indexInBlock);
+                switch (node->op()) {
+                case GetLocal:
+                case Flush:
+                case PhantomLocal:
+                    m_isLive.add(node->variableAccessData());
+                    break;
+                default:
+                    break;
+                }
+            }
+        }
+        
+        // Figure out which variables alias the arguments and nothing else, and are
+        // used only for GetByVal and GetArrayLength accesses. At the same time,
+        // identify uses of CreateArguments that are not consistent with the arguments
+        // being aliased only to variables that satisfy these constraints.
+        for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
+            BasicBlock* block = m_graph.m_blocks[blockIndex].get();
+            if (!block)
+                continue;
+            for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
+                Node* node = block->at(indexInBlock);
+                switch (node->op()) {
+                case CreateArguments: {
+                    // Ignore this op. If we see a lone CreateArguments then we want to
+                    // completely ignore it because:
+                    // 1) The default would be to see that the child is a GetLocal on the
+                    //    arguments register and conclude that we have an arguments escape.
+                    // 2) The fact that a CreateArguments exists does not mean that it
+                    //    will continue to exist after we're done with this phase. As far
+                    //    as this phase is concerned, a CreateArguments only "exists" if it
+                    //    is used in a manner that necessitates its existance.
+                    break;
+                }
+                    
+                case TearOffArguments: {
+                    // Ignore arguments tear off, because it's only relevant if we actually
+                    // need to create the arguments.
+                    break;
+                }
+                    
+                case SetLocal: {
+                    Node* source = node->child1().node();
+                    VariableAccessData* variableAccessData = node->variableAccessData();
+                    int argumentsRegister =
+                        m_graph.uncheckedArgumentsRegisterFor(node->codeOrigin);
+                    if (source->op() != CreateArguments && source->op() != PhantomArguments) {
+                        // Make sure that the source of the SetLocal knows that if it's
+                        // a variable that we think is aliased to the arguments, then it
+                        // may escape at this point. In future, we could track transitive
+                        // aliasing. But not yet.
+                        observeBadArgumentsUse(source);
+                        
+                        // If this is an assignment to the arguments register, then
+                        // pretend as if the arguments were created. We don't want to
+                        // optimize code that explicitly assigns to the arguments,
+                        // because that seems too ugly.
+                        
+                        // But, before getting rid of CreateArguments, we will have
+                        // an assignment to the arguments registers with JSValue().
+                        // That's because CSE will refuse to get rid of the
+                        // init_lazy_reg since it treats CreateArguments as reading
+                        // local variables. That could be fixed, but it's easier to
+                        // work around this here.
+                        if (source->op() == JSConstant
+                            && !source->valueOfJSConstant(codeBlock()))
+                            break;
+                        
+                        // If the variable is totally dead, then ignore it.
+                        if (!m_isLive.contains(variableAccessData))
+                            break;
+                        
+                        if (argumentsRegister != InvalidVirtualRegister
+                            && (variableAccessData->local() == argumentsRegister
+                                || variableAccessData->local() == unmodifiedArgumentsRegister(argumentsRegister))) {
+                            m_createsArguments.add(node->codeOrigin.inlineCallFrame);
+                            break;
+                        }
+
+                        if (variableAccessData->isCaptured())
+                            break;
+                        
+                        // Make sure that if it's a variable that we think is aliased to
+                        // the arguments, that we know that it might actually not be.
+                        ArgumentsAliasingData& data =
+                            m_argumentsAliasing.find(variableAccessData)->value;
+                        data.mergeNonArgumentsAssignment();
+                        data.mergeCallContext(node->codeOrigin.inlineCallFrame);
+                        break;
+                    }
+                    if (argumentsRegister != InvalidVirtualRegister
+                        && (variableAccessData->local() == argumentsRegister
+                            || variableAccessData->local() == unmodifiedArgumentsRegister(argumentsRegister))) {
+                        if (node->codeOrigin.inlineCallFrame == source->codeOrigin.inlineCallFrame)
+                            break;
+                        m_createsArguments.add(source->codeOrigin.inlineCallFrame);
+                        break;
+                    }
+                    if (variableAccessData->isCaptured()) {
+                        m_createsArguments.add(source->codeOrigin.inlineCallFrame);
+                        break;
+                    }
+                    ArgumentsAliasingData& data =
+                        m_argumentsAliasing.find(variableAccessData)->value;
+                    data.mergeArgumentsAssignment();
+                    // This ensures that the variable's uses are in the same context as
+                    // the arguments it is aliasing.
+                    data.mergeCallContext(node->codeOrigin.inlineCallFrame);
+                    data.mergeCallContext(source->codeOrigin.inlineCallFrame);
+                    break;
+                }
+                    
+                case GetLocal:
+                case Phi: /* FIXME: https://bugs.webkit.org/show_bug.cgi?id=108555 */ {
+                    VariableAccessData* variableAccessData = node->variableAccessData();
+                    if (variableAccessData->isCaptured())
+                        break;
+                    ArgumentsAliasingData& data =
+                        m_argumentsAliasing.find(variableAccessData)->value;
+                    data.mergeCallContext(node->codeOrigin.inlineCallFrame);
+                    break;
+                }
+                    
+                case Flush: {
+                    VariableAccessData* variableAccessData = node->variableAccessData();
+                    if (variableAccessData->isCaptured())
+                        break;
+                    ArgumentsAliasingData& data =
+                        m_argumentsAliasing.find(variableAccessData)->value;
+                    data.mergeCallContext(node->codeOrigin.inlineCallFrame);
+                    
+                    // If a variable is used in a flush then by definition it escapes.
+                    data.escapes = true;
+                    break;
+                }
+                    
+                case SetArgument: {
+                    VariableAccessData* variableAccessData = node->variableAccessData();
+                    if (variableAccessData->isCaptured())
+                        break;
+                    ArgumentsAliasingData& data =
+                        m_argumentsAliasing.find(variableAccessData)->value;
+                    data.mergeNonArgumentsAssignment();
+                    data.mergeCallContext(node->codeOrigin.inlineCallFrame);
+                    break;
+                }
+                    
+                case GetByVal: {
+                    if (node->arrayMode().type() != Array::Arguments) {
+                        observeBadArgumentsUses(node);
+                        break;
+                    }
+
+                    // That's so awful and pretty much impossible since it would
+                    // imply that the arguments were predicted integer, but it's
+                    // good to be defensive and thorough.
+                    observeBadArgumentsUse(node->child2().node());
+                    observeProperArgumentsUse(node, node->child1());
+                    break;
+                }
+                    
+                case GetArrayLength: {
+                    if (node->arrayMode().type() != Array::Arguments) {
+                        observeBadArgumentsUses(node);
+                        break;
+                    }
+                        
+                    observeProperArgumentsUse(node, node->child1());
+                    break;
+                }
+                    
+                case Phantom:
+                    // We don't care about phantom uses, since phantom uses are all about
+                    // just keeping things alive for OSR exit. If something - like the
+                    // CreateArguments - is just being kept alive, then this transformation
+                    // will not break this, since the Phantom will now just keep alive a
+                    // PhantomArguments and OSR exit will still do the right things.
+                    break;
+                    
+                case CheckStructure:
+                case ForwardCheckStructure:
+                case StructureTransitionWatchpoint:
+                case ForwardStructureTransitionWatchpoint:
+                case CheckArray:
+                    // We don't care about these because if we get uses of the relevant
+                    // variable then we can safely get rid of these, too. This of course
+                    // relies on there not being any information transferred by the CFA
+                    // from a CheckStructure on one variable to the information about the
+                    // structures of another variable.
+                    break;
+                    
+                default:
+                    observeBadArgumentsUses(node);
+                    break;
+                }
+            }
+        }
+
+        // Now we know which variables are aliased to arguments. But if any of them are
+        // found to have escaped, or were otherwise invalidated, then we need to mark
+        // the arguments as requiring creation. This is a property of SetLocals to
+        // variables that are neither the correct arguments register nor are marked as
+        // being arguments-aliased.
+        for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
+            BasicBlock* block = m_graph.m_blocks[blockIndex].get();
+            if (!block)
+                continue;
+            for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
+                Node* node = block->at(indexInBlock);
+                if (node->op() != SetLocal)
+                    continue;
+                Node* source = node->child1().node();
+                if (source->op() != CreateArguments)
+                    continue;
+                VariableAccessData* variableAccessData = node->variableAccessData();
+                if (variableAccessData->isCaptured()) {
+                    // The captured case would have already been taken care of in the
+                    // previous pass.
+                    continue;
+                }
+                
+                ArgumentsAliasingData& data =
+                    m_argumentsAliasing.find(variableAccessData)->value;
+                if (data.isValid())
+                    continue;
+                
+                m_createsArguments.add(source->codeOrigin.inlineCallFrame);
+            }
+        }
+        
+#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
+        dataLogF("Arguments aliasing states:\n");
+        for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) {
+            VariableAccessData* variableAccessData = &m_graph.m_variableAccessData[i];
+            if (!variableAccessData->isRoot())
+                continue;
+            dataLog("   r", variableAccessData->local(), "(", VariableAccessDataDump(m_graph, variableAccessData), "): ");
+            if (variableAccessData->isCaptured())
+                dataLogF("Captured");
+            else {
+                ArgumentsAliasingData& data =
+                    m_argumentsAliasing.find(variableAccessData)->value;
+                bool first = true;
+                if (data.callContextIsValid()) {
+                    if (!first)
+                        dataLogF(", ");
+                    dataLogF("Have Call Context: %p", data.callContext);
+                    first = false;
+                    if (!m_createsArguments.contains(data.callContext))
+                        dataLogF(" (Does Not Create Arguments)");
+                }
+                if (data.argumentsAssignmentIsValid()) {
+                    if (!first)
+                        dataLogF(", ");
+                    dataLogF("Arguments Assignment Is Valid");
+                    first = false;
+                }
+                if (!data.escapes) {
+                    if (!first)
+                        dataLogF(", ");
+                    dataLogF("Does Not Escape");
+                    first = false;
+                }
+                if (!first)
+                    dataLogF(", ");
+                if (data.isValid()) {
+                    if (m_createsArguments.contains(data.callContext))
+                        dataLogF("VALID");
+                    else
+                        dataLogF("INVALID (due to argument creation)");
+                } else
+                    dataLogF("INVALID (due to bad variable use)");
+            }
+            dataLogF("\n");
+        }
+#endif
+        
+        InsertionSet insertionSet(m_graph);
+        
+        for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
+            BasicBlock* block = m_graph.m_blocks[blockIndex].get();
+            if (!block)
+                continue;
+            for (unsigned indexInBlock = 0; indexInBlock < block->size(); indexInBlock++) {
+                Node* node = block->at(indexInBlock);
+                switch (node->op()) {
+                case SetLocal: {
+                    Node* source = node->child1().node();
+                    if (source->op() != CreateArguments)
+                        break;
+                    
+                    if (m_createsArguments.contains(source->codeOrigin.inlineCallFrame))
+                        break;
+                    
+                    VariableAccessData* variableAccessData = node->variableAccessData();
+                    
+                    if (m_graph.argumentsRegisterFor(node->codeOrigin) == variableAccessData->local()
+                        || unmodifiedArgumentsRegister(m_graph.argumentsRegisterFor(node->codeOrigin)) == variableAccessData->local())
+                        break;
+
+                    ASSERT(!variableAccessData->isCaptured());
+                    
+                    // If this is a store into a VariableAccessData* that is marked as
+                    // arguments aliasing for an InlineCallFrame* that does not create
+                    // arguments, then flag the VariableAccessData as being an
+                    // arguments-aliased. This'll let the OSR exit machinery do the right
+                    // things. Note also that the SetLocal should become dead as soon as
+                    // we replace all uses of this variable with GetMyArgumentsLength and
+                    // GetMyArgumentByVal.
+                    ASSERT(m_argumentsAliasing.find(variableAccessData)->value.isValid());
+                    if (variableAccessData->mergeIsArgumentsAlias(true)) {
+                        changed = true;
+                        
+                        // Make sure that the variable knows, that it may now hold non-cell values.
+                        variableAccessData->predict(SpecEmpty);
+                    }
+                    
+                    // Make sure that the SetLocal doesn't check that the input is a Cell.
+                    if (node->child1().useKind() != UntypedUse) {
+                        node->child1().setUseKind(UntypedUse);
+                        changed = true;
+                    }
+                    break;
+                }
+                    
+                case PhantomLocal: {
+                    VariableAccessData* variableAccessData = node->variableAccessData();
+                    
+                    if (variableAccessData->isCaptured()
+                        || !m_argumentsAliasing.find(variableAccessData)->value.isValid()
+                        || m_createsArguments.contains(node->codeOrigin.inlineCallFrame))
+                        break;
+                    
+                    // Turn PhantomLocals into just GetLocals. This will preserve the threading
+                    // of the local through to this point, but will allow it to die, causing
+                    // only OSR to know about it.
+
+                    node->setOpAndDefaultFlags(GetLocal);
+                    break;
+                }
+
+                case Flush: {
+                    VariableAccessData* variableAccessData = node->variableAccessData();
+                    
+                    if (variableAccessData->isCaptured()
+                        || !m_argumentsAliasing.find(variableAccessData)->value.isValid()
+                        || m_createsArguments.contains(node->codeOrigin.inlineCallFrame))
+                        break;
+                    
+                    RELEASE_ASSERT_NOT_REACHED();
+                    break;
+                }
+                    
+                case Phantom: {
+                    // It's highly likely that we will have a Phantom referencing either
+                    // CreateArguments, or a local op for the arguments register, or a
+                    // local op for an arguments-aliased variable. In any of those cases,
+                    // we should remove the phantom reference, since:
+                    // 1) Phantoms only exist to aid OSR exit. But arguments simplification
+                    //    has its own OSR exit story, which is to inform OSR exit to reify
+                    //    the arguments as necessary.
+                    // 2) The Phantom may keep the CreateArguments node alive, which is
+                    //    precisely what we don't want.
+                    for (unsigned i = 0; i < AdjacencyList::Size; ++i)
+                        removeArgumentsReferencingPhantomChild(node, i);
+                    break;
+                }
+                    
+                case CheckStructure:
+                case ForwardCheckStructure:
+                case StructureTransitionWatchpoint:
+                case ForwardStructureTransitionWatchpoint:
+                case CheckArray: {
+                    // We can just get rid of this node, if it references a phantom argument.
+                    if (!isOKToOptimize(node->child1().node()))
+                        break;
+                    node->convertToPhantom();
+                    node->children.setChild1(Edge());
+                    break;
+                }
+                    
+                case GetByVal: {
+                    if (node->arrayMode().type() != Array::Arguments)
+                        break;
+
+                    // This can be simplified to GetMyArgumentByVal if we know that
+                    // it satisfies either condition (1) or (2):
+                    // 1) Its first child is a valid ArgumentsAliasingData and the
+                    //    InlineCallFrame* is not marked as creating arguments.
+                    // 2) Its first child is CreateArguments and its InlineCallFrame*
+                    //    is not marked as creating arguments.
+                    
+                    if (!isOKToOptimize(node->child1().node()))
+                        break;
+                    
+                    node->children.child1() = node->children.child2();
+                    node->children.child2() = Edge();
+                    node->setOpAndDefaultFlags(GetMyArgumentByVal);
+                    changed = true;
+                    --indexInBlock; // Force reconsideration of this op now that it's a GetMyArgumentByVal.
+                    break;
+                }
+                    
+                case GetArrayLength: {
+                    if (node->arrayMode().type() != Array::Arguments)
+                        break;
+                    
+                    if (!isOKToOptimize(node->child1().node()))
+                        break;
+                    
+                    node->children.child1() = Edge();
+                    node->setOpAndDefaultFlags(GetMyArgumentsLength);
+                    changed = true;
+                    --indexInBlock; // Force reconsideration of this op noew that it's a GetMyArgumentsLength.
+                    break;
+                }
+                    
+                case GetMyArgumentsLength:
+                case GetMyArgumentsLengthSafe: {
+                    if (m_createsArguments.contains(node->codeOrigin.inlineCallFrame)) {
+                        ASSERT(node->op() == GetMyArgumentsLengthSafe);
+                        break;
+                    }
+                    if (node->op() == GetMyArgumentsLengthSafe) {
+                        node->setOp(GetMyArgumentsLength);
+                        changed = true;
+                    }
+                    
+                    CodeOrigin codeOrigin = node->codeOrigin;
+                    if (!codeOrigin.inlineCallFrame)
+                        break;
+                    
+                    // We know exactly what this will return. But only after we have checked
+                    // that nobody has escaped our arguments.
+                    insertionSet.insertNode(
+                        indexInBlock, SpecNone, CheckArgumentsNotCreated, codeOrigin);
+                    
+                    m_graph.convertToConstant(
+                        node, jsNumber(codeOrigin.inlineCallFrame->arguments.size() - 1));
+                    changed = true;
+                    break;
+                }
+                    
+                case GetMyArgumentByVal:
+                case GetMyArgumentByValSafe: {
+                    if (m_createsArguments.contains(node->codeOrigin.inlineCallFrame)) {
+                        ASSERT(node->op() == GetMyArgumentByValSafe);
+                        break;
+                    }
+                    if (node->op() == GetMyArgumentByValSafe) {
+                        node->setOp(GetMyArgumentByVal);
+                        changed = true;
+                    }
+                    if (!node->codeOrigin.inlineCallFrame)
+                        break;
+                    if (!node->child1()->hasConstant())
+                        break;
+                    JSValue value = node->child1()->valueOfJSConstant(codeBlock());
+                    if (!value.isInt32())
+                        break;
+                    int32_t index = value.asInt32();
+                    if (index < 0
+                        || static_cast<size_t>(index + 1) >=
+                            node->codeOrigin.inlineCallFrame->arguments.size())
+                        break;
+                    
+                    // We know which argument this is accessing. But only after we have checked
+                    // that nobody has escaped our arguments. We also need to ensure that the
+                    // index is kept alive. That's somewhat pointless since it's a constant, but
+                    // it's important because this is one of those invariants that we like to
+                    // have in the DFG. Note finally that we use the GetLocalUnlinked opcode
+                    // here, since this is being done _after_ the prediction propagation phase
+                    // has run - therefore it makes little sense to link the GetLocal operation
+                    // into the VariableAccessData and Phi graphs.
+
+                    CodeOrigin codeOrigin = node->codeOrigin;
+                    AdjacencyList children = node->children;
+                    
+                    node->convertToGetLocalUnlinked(
+                        static_cast<VirtualRegister>(
+                            node->codeOrigin.inlineCallFrame->stackOffset +
+                            m_graph.baselineCodeBlockFor(node->codeOrigin)->argumentIndexAfterCapture(index)));
+
+                    insertionSet.insertNode(
+                        indexInBlock, SpecNone, CheckArgumentsNotCreated,
+                        codeOrigin);
+                    insertionSet.insertNode(
+                        indexInBlock, SpecNone, Phantom, codeOrigin,
+                        children);
+                    
+                    changed = true;
+                    break;
+                }
+                    
+                case TearOffArguments: {
+                    if (m_createsArguments.contains(node->codeOrigin.inlineCallFrame))
+                        continue;
+                    
+                    node->setOpAndDefaultFlags(Nop);
+                    m_graph.clearAndDerefChild1(node);
+                    m_graph.clearAndDerefChild2(node);
+                    break;
+                }
+                    
+                default:
+                    break;
+                }
+            }
+            insertionSet.execute(block);
+        }
+        
+        for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
+            BasicBlock* block = m_graph.m_blocks[blockIndex].get();
+            if (!block)
+                continue;
+            for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
+                Node* node = block->at(indexInBlock);
+                if (node->op() != CreateArguments)
+                    continue;
+                // If this is a CreateArguments for an InlineCallFrame* that does
+                // not create arguments, then replace it with a PhantomArguments.
+                // PhantomArguments is a non-executing node that just indicates
+                // that the node should be reified as an arguments object on OSR
+                // exit.
+                if (m_createsArguments.contains(node->codeOrigin.inlineCallFrame))
+                    continue;
+                insertionSet.insertNode(
+                    indexInBlock, SpecNone, Phantom, node->codeOrigin, node->children);
+                node->setOpAndDefaultFlags(PhantomArguments);
+                node->children.reset();
+                changed = true;
+            }
+            insertionSet.execute(block);
+        }
+        
+        if (changed) {
+            m_graph.dethread();
+            m_graph.m_form = LoadStore;
+        }
+        
+        return changed;
+    }
+    
+private:
+    HashSet<InlineCallFrame*,
+            DefaultHash<InlineCallFrame*>::Hash,
+            NullableHashTraits<InlineCallFrame*> > m_createsArguments;
+    HashMap<VariableAccessData*, ArgumentsAliasingData,
+            DefaultHash<VariableAccessData*>::Hash,
+            NullableHashTraits<VariableAccessData*> > m_argumentsAliasing;
+    HashSet<VariableAccessData*> m_isLive;
+
+    void observeBadArgumentsUse(Node* node)
+    {
+        if (!node)
+            return;
+        
+        switch (node->op()) {
+        case CreateArguments: {
+            m_createsArguments.add(node->codeOrigin.inlineCallFrame);
+            break;
+        }
+            
+        case GetLocal: {
+            int argumentsRegister = m_graph.uncheckedArgumentsRegisterFor(node->codeOrigin);
+            if (argumentsRegister != InvalidVirtualRegister
+                && (node->local() == argumentsRegister
+                    || node->local() == unmodifiedArgumentsRegister(argumentsRegister))) {
+                m_createsArguments.add(node->codeOrigin.inlineCallFrame);
+                break;
+            }
+            
+            VariableAccessData* variableAccessData = node->variableAccessData();
+            if (variableAccessData->isCaptured())
+                break;
+            
+            ArgumentsAliasingData& data = m_argumentsAliasing.find(variableAccessData)->value;
+            data.escapes = true;
+            break;
+        }
+            
+        default:
+            break;
+        }
+    }
+    
+    void observeBadArgumentsUses(Node* node)
+    {
+        for (unsigned i = m_graph.numChildren(node); i--;)
+            observeBadArgumentsUse(m_graph.child(node, i).node());
+    }
+    
+    void observeProperArgumentsUse(Node* node, Edge edge)
+    {
+        if (edge->op() != GetLocal) {
+            // When can this happen? At least two cases that I can think
+            // of:
+            //
+            // 1) Aliased use of arguments in the same basic block,
+            //    like:
+            //
+            //    var a = arguments;
+            //    var x = arguments[i];
+            //
+            // 2) If we're accessing arguments we got from the heap!
+                            
+            if (edge->op() == CreateArguments
+                && node->codeOrigin.inlineCallFrame
+                    != edge->codeOrigin.inlineCallFrame)
+                m_createsArguments.add(edge->codeOrigin.inlineCallFrame);
+            
+            return;
+        }
+                        
+        VariableAccessData* variableAccessData = edge->variableAccessData();
+        if (edge->local() == m_graph.uncheckedArgumentsRegisterFor(edge->codeOrigin)
+            && node->codeOrigin.inlineCallFrame != edge->codeOrigin.inlineCallFrame) {
+            m_createsArguments.add(edge->codeOrigin.inlineCallFrame);
+            return;
+        }
+
+        if (variableAccessData->isCaptured())
+            return;
+        
+        ArgumentsAliasingData& data = m_argumentsAliasing.find(variableAccessData)->value;
+        data.mergeCallContext(node->codeOrigin.inlineCallFrame);
+    }
+    
+    bool isOKToOptimize(Node* source)
+    {
+        if (m_createsArguments.contains(source->codeOrigin.inlineCallFrame))
+            return false;
+        
+        switch (source->op()) {
+        case GetLocal: {
+            VariableAccessData* variableAccessData = source->variableAccessData();
+            int argumentsRegister = m_graph.uncheckedArgumentsRegisterFor(source->codeOrigin);
+            if (argumentsRegister == InvalidVirtualRegister)
+                break;
+            if (argumentsRegister == variableAccessData->local())
+                return true;
+            if (unmodifiedArgumentsRegister(argumentsRegister) == variableAccessData->local())
+                return true;
+            if (variableAccessData->isCaptured())
+                break;
+            ArgumentsAliasingData& data =
+                m_argumentsAliasing.find(variableAccessData)->value;
+            if (!data.isValid())
+                break;
+                            
+            return true;
+        }
+                            
+        case CreateArguments: {
+            return true;
+        }
+                            
+        default:
+            break;
+        }
+        
+        return false;
+    }
+    
+    void removeArgumentsReferencingPhantomChild(Node* node, unsigned edgeIndex)
+    {
+        Edge edge = node->children.child(edgeIndex);
+        if (!edge)
+            return;
+        
+        switch (edge->op()) {
+        case Phi: // Arises if we had CSE on a GetLocal of the arguments register.
+        case GetLocal: // Arises if we had CSE on an arguments access to a variable aliased to the arguments.
+        case SetLocal: { // Arises if we had CSE on a GetLocal of the arguments register.
+            VariableAccessData* variableAccessData = edge->variableAccessData();
+            bool isDeadArgumentsRegister =
+                variableAccessData->local() ==
+                    m_graph.uncheckedArgumentsRegisterFor(edge->codeOrigin)
+                && !m_createsArguments.contains(edge->codeOrigin.inlineCallFrame);
+            bool isAliasedArgumentsRegister =
+                !variableAccessData->isCaptured()
+                && m_argumentsAliasing.find(variableAccessData)->value.isValid()
+                && !m_createsArguments.contains(edge->codeOrigin.inlineCallFrame);
+            if (!isDeadArgumentsRegister && !isAliasedArgumentsRegister)
+                break;
+            node->children.removeEdge(edgeIndex);
+            break;
+        }
+            
+        case CreateArguments: { // Arises if we CSE two GetLocals to the arguments register and then CSE the second use of the GetLocal to the first.
+            if (m_createsArguments.contains(edge->codeOrigin.inlineCallFrame))
+                break;
+            node->children.removeEdge(edgeIndex);
+            break;
+        }
+            
+        default:
+            break;
+        }
+    }
+};
+
+bool performArgumentsSimplification(Graph& graph)
+{
+    SamplingRegion samplingRegion("DFG Arguments Simplification Phase");
+    return runPhase<ArgumentsSimplificationPhase>(graph);
+}
+
+} } // namespace JSC::DFG
+
+#endif // ENABLE(DFG_JIT)
+
+