DFGArgumentsSimplificationPhase.cpp [plain text]
#include "config.h"
#include "DFGArgumentsSimplificationPhase.h"
#if ENABLE(DFG_JIT)
#include "DFGBasicBlock.h"
#include "DFGGraph.h"
#include "DFGInsertionSet.h"
#include "DFGPhase.h"
#include "DFGValidate.h"
#include "DFGVariableAccessDataDump.h"
#include "JSCInlines.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;
}
};
}
class ArgumentsSimplificationPhase : public Phase {
public:
ArgumentsSimplificationPhase(Graph& graph)
: Phase(graph, "arguments simplification")
{
}
bool run()
{
if (!m_graph.m_hasArguments)
return false;
bool changed = false;
for (InlineCallFrameSet::iterator iter = m_graph.m_plan.inlineCallFrames->begin(); !!iter; ++iter)
pruneObviousArgumentCreations(*iter);
pruneObviousArgumentCreations(0);
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());
}
for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) {
BasicBlock* block = m_graph.block(blockIndex);
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;
}
}
}
for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) {
BasicBlock* block = m_graph.block(blockIndex);
if (!block)
continue;
for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
Node* node = block->at(indexInBlock);
switch (node->op()) {
case CreateArguments: {
break;
}
case TearOffArguments: {
break;
}
case SetLocal: {
Node* source = node->child1().node();
VariableAccessData* variableAccessData = node->variableAccessData();
VirtualRegister argumentsRegister =
m_graph.uncheckedArgumentsRegisterFor(node->origin.semantic);
if (source->op() != CreateArguments && source->op() != PhantomArguments) {
observeBadArgumentsUse(source);
if (source->op() == JSConstant
&& !source->valueOfJSConstant(codeBlock()))
break;
if (!m_isLive.contains(variableAccessData))
break;
if (argumentsRegister.isValid()
&& (variableAccessData->local() == argumentsRegister
|| variableAccessData->local() == unmodifiedArgumentsRegister(argumentsRegister))) {
m_createsArguments.add(node->origin.semantic.inlineCallFrame);
break;
}
if (variableAccessData->isCaptured())
break;
ArgumentsAliasingData& data =
m_argumentsAliasing.find(variableAccessData)->value;
data.mergeNonArgumentsAssignment();
data.mergeCallContext(node->origin.semantic.inlineCallFrame);
break;
}
if (argumentsRegister.isValid()
&& (variableAccessData->local() == argumentsRegister
|| variableAccessData->local() == unmodifiedArgumentsRegister(argumentsRegister))) {
if (node->origin.semantic.inlineCallFrame == source->origin.semantic.inlineCallFrame)
break;
m_createsArguments.add(source->origin.semantic.inlineCallFrame);
break;
}
if (variableAccessData->isCaptured()) {
m_createsArguments.add(source->origin.semantic.inlineCallFrame);
break;
}
ArgumentsAliasingData& data =
m_argumentsAliasing.find(variableAccessData)->value;
data.mergeArgumentsAssignment();
data.mergeCallContext(node->origin.semantic.inlineCallFrame);
data.mergeCallContext(source->origin.semantic.inlineCallFrame);
break;
}
case GetLocal:
case Phi: {
VariableAccessData* variableAccessData = node->variableAccessData();
if (variableAccessData->isCaptured())
break;
ArgumentsAliasingData& data =
m_argumentsAliasing.find(variableAccessData)->value;
data.mergeCallContext(node->origin.semantic.inlineCallFrame);
break;
}
case Flush: {
VariableAccessData* variableAccessData = node->variableAccessData();
if (variableAccessData->isCaptured())
break;
ArgumentsAliasingData& data =
m_argumentsAliasing.find(variableAccessData)->value;
data.mergeCallContext(node->origin.semantic.inlineCallFrame);
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->origin.semantic.inlineCallFrame);
break;
}
case GetByVal: {
if (node->arrayMode().type() != Array::Arguments) {
observeBadArgumentsUses(node);
break;
}
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:
case HardPhantom:
break;
case CheckStructure:
case StructureTransitionWatchpoint:
case CheckArray:
break;
case MovHint:
break;
default:
observeBadArgumentsUses(node);
break;
}
}
}
for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) {
BasicBlock* block = m_graph.block(blockIndex);
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()) {
continue;
}
ArgumentsAliasingData& data =
m_argumentsAliasing.find(variableAccessData)->value;
if (data.isValid())
continue;
m_createsArguments.add(source->origin.semantic.inlineCallFrame);
}
}
InsertionSet insertionSet(m_graph);
for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) {
BasicBlock* block = m_graph.block(blockIndex);
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->origin.semantic.inlineCallFrame))
break;
VariableAccessData* variableAccessData = node->variableAccessData();
if (variableAccessData->mergeIsArgumentsAlias(true)) {
changed = true;
variableAccessData->predict(SpecEmpty);
}
if (node->child1().useKind() != UntypedUse) {
node->child1().setUseKind(UntypedUse);
changed = true;
}
break;
}
case Flush: {
VariableAccessData* variableAccessData = node->variableAccessData();
if (variableAccessData->isCaptured()
|| !m_argumentsAliasing.find(variableAccessData)->value.isValid()
|| m_createsArguments.contains(node->origin.semantic.inlineCallFrame))
break;
RELEASE_ASSERT_NOT_REACHED();
break;
}
case Phantom:
case HardPhantom: {
for (unsigned i = 0; i < AdjacencyList::Size; ++i)
detypeArgumentsReferencingPhantomChild(node, i);
break;
}
case CheckStructure:
case StructureTransitionWatchpoint:
case CheckArray: {
if (!isOKToOptimize(node->child1().node()))
break;
node->convertToPhantom();
break;
}
case GetByVal: {
if (node->arrayMode().type() != Array::Arguments)
break;
if (!isOKToOptimize(node->child1().node()))
break;
insertionSet.insertNode(
indexInBlock, SpecNone, Phantom, node->origin, node->child1());
node->child1() = node->child2();
node->child2() = Edge();
node->setOpAndDefaultFlags(GetMyArgumentByVal);
changed = true;
--indexInBlock; break;
}
case GetArrayLength: {
if (node->arrayMode().type() != Array::Arguments)
break;
if (!isOKToOptimize(node->child1().node()))
break;
insertionSet.insertNode(
indexInBlock, SpecNone, Phantom, node->origin, node->child1());
node->child1() = Edge();
node->setOpAndDefaultFlags(GetMyArgumentsLength);
changed = true;
--indexInBlock; break;
}
case GetMyArgumentsLength:
case GetMyArgumentsLengthSafe: {
if (m_createsArguments.contains(node->origin.semantic.inlineCallFrame)) {
ASSERT(node->op() == GetMyArgumentsLengthSafe);
break;
}
if (node->op() == GetMyArgumentsLengthSafe) {
node->setOp(GetMyArgumentsLength);
changed = true;
}
NodeOrigin origin = node->origin;
if (!origin.semantic.inlineCallFrame)
break;
insertionSet.insertNode(
indexInBlock, SpecNone, CheckArgumentsNotCreated, origin);
m_graph.convertToConstant(
node, jsNumber(origin.semantic.inlineCallFrame->arguments.size() - 1));
changed = true;
break;
}
case GetMyArgumentByVal:
case GetMyArgumentByValSafe: {
if (m_createsArguments.contains(node->origin.semantic.inlineCallFrame)) {
ASSERT(node->op() == GetMyArgumentByValSafe);
break;
}
if (node->op() == GetMyArgumentByValSafe) {
node->setOp(GetMyArgumentByVal);
changed = true;
}
if (!node->origin.semantic.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->origin.semantic.inlineCallFrame->arguments.size())
break;
NodeOrigin origin = node->origin;
AdjacencyList children = node->children;
node->convertToGetLocalUnlinked(
VirtualRegister(
origin.semantic.inlineCallFrame->stackOffset +
m_graph.baselineCodeBlockFor(origin.semantic)->argumentIndexAfterCapture(index)));
insertionSet.insertNode(
indexInBlock, SpecNone, CheckArgumentsNotCreated, origin);
insertionSet.insertNode(
indexInBlock, SpecNone, Phantom, origin, children);
changed = true;
break;
}
case TearOffArguments: {
if (m_createsArguments.contains(node->origin.semantic.inlineCallFrame))
continue;
node->convertToPhantom();
break;
}
default:
break;
}
}
insertionSet.execute(block);
}
for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) {
BasicBlock* block = m_graph.block(blockIndex);
if (!block)
continue;
for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
Node* node = block->at(indexInBlock);
if (node->op() != CreateArguments)
continue;
if (m_createsArguments.contains(node->origin.semantic.inlineCallFrame))
continue;
insertionSet.insertNode(
indexInBlock, SpecNone, Phantom, node->origin, node->children);
node->setOpAndDefaultFlags(PhantomArguments);
node->children.reset();
changed = true;
}
insertionSet.execute(block);
}
for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) {
BasicBlock* block = m_graph.block(blockIndex);
if (!block)
continue;
for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
Node* node = block->at(indexInBlock);
if (node->op() != Phantom)
continue;
for (unsigned i = 0; i < AdjacencyList::Size; ++i)
detypeArgumentsReferencingPhantomChild(node, i);
}
}
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 = 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->origin.semantic.inlineCallFrame);
break;
}
case GetLocal: {
VirtualRegister argumentsRegister =
m_graph.uncheckedArgumentsRegisterFor(node->origin.semantic);
if (argumentsRegister.isValid()
&& (node->local() == argumentsRegister
|| node->local() == unmodifiedArgumentsRegister(argumentsRegister))) {
m_createsArguments.add(node->origin.semantic.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) {
if (edge->op() == CreateArguments
&& node->origin.semantic.inlineCallFrame
!= edge->origin.semantic.inlineCallFrame)
m_createsArguments.add(edge->origin.semantic.inlineCallFrame);
return;
}
VariableAccessData* variableAccessData = edge->variableAccessData();
if (edge->local() == m_graph.uncheckedArgumentsRegisterFor(edge->origin.semantic)
&& node->origin.semantic.inlineCallFrame != edge->origin.semantic.inlineCallFrame) {
m_createsArguments.add(edge->origin.semantic.inlineCallFrame);
return;
}
if (variableAccessData->isCaptured())
return;
ArgumentsAliasingData& data = m_argumentsAliasing.find(variableAccessData)->value;
data.mergeCallContext(node->origin.semantic.inlineCallFrame);
}
bool isOKToOptimize(Node* source)
{
if (m_createsArguments.contains(source->origin.semantic.inlineCallFrame))
return false;
switch (source->op()) {
case GetLocal: {
VariableAccessData* variableAccessData = source->variableAccessData();
VirtualRegister argumentsRegister =
m_graph.uncheckedArgumentsRegisterFor(source->origin.semantic);
if (!argumentsRegister.isValid())
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 detypeArgumentsReferencingPhantomChild(Node* node, unsigned edgeIndex)
{
Edge edge = node->children.child(edgeIndex);
if (!edge)
return;
switch (edge->op()) {
case GetLocal: {
VariableAccessData* variableAccessData = edge->variableAccessData();
if (!variableAccessData->isArgumentsAlias())
break;
node->children.child(edgeIndex).setUseKind(UntypedUse);
break;
}
case PhantomArguments: {
node->children.child(edgeIndex).setUseKind(UntypedUse);
break;
}
default:
break;
}
}
};
bool performArgumentsSimplification(Graph& graph)
{
SamplingRegion samplingRegion("DFG Arguments Simplification Phase");
return runPhase<ArgumentsSimplificationPhase>(graph);
}
} }
#endif // ENABLE(DFG_JIT)