blob: 2c5e696addc23a3d208817e136ffbe0800c9a2ad [file] [log] [blame]
/*
* Copyright (C) 2012 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>
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 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) {
NodeIndex nodeIndex = block->at(indexInBlock);
Node& node = m_graph[nodeIndex];
if (!node.shouldGenerate())
continue;
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 = m_graph[node.child1()];
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(node.child1());
// 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 (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: {
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());
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) {
NodeIndex nodeIndex = block->at(indexInBlock);
Node& node = m_graph[nodeIndex];
if (!node.shouldGenerate())
continue;
if (node.op() != SetLocal)
continue;
Node& source = m_graph[node.child1()];
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<NodeIndex> insertionSet;
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) {
NodeIndex nodeIndex = block->at(indexInBlock);
Node& node = m_graph[nodeIndex];
if (!node.shouldGenerate())
continue;
switch (node.op()) {
case SetLocal: {
Node& source = m_graph[node.child1()];
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());
changed |= variableAccessData->mergeIsArgumentsAlias(true);
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(m_graph[node.child1()]))
break;
m_graph.deref(node.child1());
node.setOpAndDefaultFlags(Phantom);
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(m_graph[node.child1()]))
break;
m_graph.deref(node.child1());
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(m_graph[node.child1()]))
break;
m_graph.deref(node.child1());
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.
Node check(CheckArgumentsNotCreated, codeOrigin);
check.ref();
NodeIndex checkIndex = m_graph.size();
m_graph.append(check);
insertionSet.append(indexInBlock, checkIndex);
m_graph.convertToConstant(
nodeIndex, 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 (!m_graph[node.child1()].hasConstant())
break;
JSValue value = m_graph[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.
Node check(CheckArgumentsNotCreated, node.codeOrigin);
check.ref();
Node phantom(Phantom, node.codeOrigin);
phantom.ref();
phantom.children = node.children;
node.convertToGetLocalUnlinked(
static_cast<VirtualRegister>(
node.codeOrigin.inlineCallFrame->stackOffset +
m_graph.baselineCodeBlockFor(node.codeOrigin)->argumentIndexAfterCapture(index)));
NodeIndex checkNodeIndex = m_graph.size();
m_graph.append(check);
insertionSet.append(indexInBlock, checkNodeIndex);
NodeIndex phantomNodeIndex = m_graph.size();
m_graph.append(phantom);
insertionSet.append(indexInBlock, phantomNodeIndex);
changed = true;
break;
}
case TearOffArguments: {
if (m_createsArguments.contains(node.codeOrigin.inlineCallFrame))
continue;
node.setOpAndDefaultFlags(Nop);
m_graph.clearAndDerefChild1(node);
m_graph.clearAndDerefChild2(node);
node.setRefCount(0);
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) {
NodeIndex nodeIndex = block->at(indexInBlock);
Node& node = m_graph[nodeIndex];
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;
if (node.shouldGenerate()) {
Node phantom(Phantom, node.codeOrigin);
phantom.children = node.children;
phantom.ref();
NodeIndex phantomNodeIndex = m_graph.size();
m_graph.append(phantom);
insertionSet.append(indexInBlock, phantomNodeIndex);
}
node.setOpAndDefaultFlags(PhantomArguments);
node.children.reset();
changed = true;
}
insertionSet.execute(*block);
}
if (changed)
m_graph.collectGarbage();
return changed;
}
private:
HashSet<InlineCallFrame*,
DefaultHash<InlineCallFrame*>::Hash,
NullableHashTraits<InlineCallFrame*> > m_createsArguments;
HashMap<VariableAccessData*, ArgumentsAliasingData,
DefaultHash<VariableAccessData*>::Hash,
NullableHashTraits<VariableAccessData*> > m_argumentsAliasing;
void observeBadArgumentsUse(Edge edge)
{
if (!edge)
return;
Node& child = m_graph[edge];
switch (child.op()) {
case CreateArguments: {
m_createsArguments.add(child.codeOrigin.inlineCallFrame);
break;
}
case GetLocal: {
int argumentsRegister = m_graph.uncheckedArgumentsRegisterFor(child.codeOrigin);
if (argumentsRegister != InvalidVirtualRegister
&& (child.local() == argumentsRegister
|| child.local() == unmodifiedArgumentsRegister(argumentsRegister))) {
m_createsArguments.add(child.codeOrigin.inlineCallFrame);
break;
}
VariableAccessData* variableAccessData = child.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));
}
void observeProperArgumentsUse(Node& node, Edge edge)
{
Node& child = m_graph[edge];
if (child.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 (child.op() == CreateArguments
&& node.codeOrigin.inlineCallFrame
!= child.codeOrigin.inlineCallFrame)
m_createsArguments.add(child.codeOrigin.inlineCallFrame);
return;
}
VariableAccessData* variableAccessData = child.variableAccessData();
if (child.local() == m_graph.uncheckedArgumentsRegisterFor(child.codeOrigin)
&& node.codeOrigin.inlineCallFrame != child.codeOrigin.inlineCallFrame) {
m_createsArguments.add(child.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;
Node& child = m_graph[edge];
switch (child.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 = child.variableAccessData();
bool isDeadArgumentsRegister =
variableAccessData->local() ==
m_graph.uncheckedArgumentsRegisterFor(child.codeOrigin)
&& !m_createsArguments.contains(child.codeOrigin.inlineCallFrame);
bool isAliasedArgumentsRegister =
!variableAccessData->isCaptured()
&& m_argumentsAliasing.find(variableAccessData)->value.isValid()
&& !m_createsArguments.contains(child.codeOrigin.inlineCallFrame);
if (!isDeadArgumentsRegister && !isAliasedArgumentsRegister)
break;
m_graph.deref(edge);
node.children.removeEdgeFromBag(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(child.codeOrigin.inlineCallFrame))
break;
m_graph.deref(edge);
node.children.removeEdgeFromBag(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)