summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp')
-rw-r--r--Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp868
1 files changed, 0 insertions, 868 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp b/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp
deleted file mode 100644
index ec7515eec..000000000
--- a/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp
+++ /dev/null
@@ -1,868 +0,0 @@
-/*
- * 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 "Operations.h"
-#include <wtf/HashSet.h>
-#include <wtf/HashMap.h>
-
-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--;)
- pruneObviousArgumentCreations(&codeBlock()->inlineCallFrames()[i]);
- pruneObviousArgumentCreations(0); // the machine call frame.
-
- // 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 pruneObviousArgumentCreations(InlineCallFrame* inlineCallFrame)
- {
- ScriptExecutable* executable = jsCast<ScriptExecutable*>(m_graph.executableFor(inlineCallFrame));
- if (m_graph.m_executablesWhoseArgumentsEscaped.contains(executable)
- || executable->isStrictMode())
- m_createsArguments.add(inlineCallFrame);
- }
-
- 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)
-
-