DFGConstantFoldingPhase.cpp [plain text]
#include "config.h"
#include "DFGConstantFoldingPhase.h"
#if ENABLE(DFG_JIT)
#include "DFGAbstractState.h"
#include "DFGBasicBlock.h"
#include "DFGGraph.h"
#include "DFGInsertionSet.h"
#include "DFGPhase.h"
#include "GetByIdStatus.h"
#include "Operations.h"
#include "PutByIdStatus.h"
namespace JSC { namespace DFG {
class ConstantFoldingPhase : public Phase {
public:
ConstantFoldingPhase(Graph& graph)
: Phase(graph, "constant folding")
, m_state(graph)
, m_insertionSet(graph)
{
}
bool run()
{
bool changed = false;
for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
BasicBlock* block = m_graph.m_blocks[blockIndex].get();
if (!block)
continue;
if (!block->cfaDidFinish)
changed |= paintUnreachableCode(blockIndex);
if (block->cfaFoundConstants)
changed |= foldConstants(blockIndex);
}
return changed;
}
private:
bool foldConstants(BlockIndex blockIndex)
{
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
dataLogF("Constant folding considering Block #%u.\n", blockIndex);
#endif
BasicBlock* block = m_graph.m_blocks[blockIndex].get();
bool changed = false;
m_state.beginBasicBlock(block);
for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
if (!m_state.isValid())
break;
Node* node = block->at(indexInBlock);
bool eliminated = false;
switch (node->op()) {
case CheckArgumentsNotCreated: {
if (!isEmptySpeculation(
m_state.variables().operand(
m_graph.argumentsRegisterFor(node->codeOrigin)).m_type))
break;
node->convertToPhantom();
eliminated = true;
break;
}
case CheckStructure:
case ForwardCheckStructure:
case ArrayifyToStructure: {
AbstractValue& value = m_state.forNode(node->child1());
StructureSet set;
if (node->op() == ArrayifyToStructure)
set = node->structure();
else
set = node->structureSet();
if (value.m_currentKnownStructure.isSubsetOf(set)) {
m_state.execute(indexInBlock); node->convertToPhantom();
eliminated = true;
break;
}
StructureAbstractValue& structureValue = value.m_futurePossibleStructure;
if (structureValue.isSubsetOf(set)
&& structureValue.hasSingleton()) {
Structure* structure = structureValue.singleton();
m_state.execute(indexInBlock); node->convertToStructureTransitionWatchpoint(structure);
eliminated = true;
break;
}
break;
}
case CheckArray:
case Arrayify: {
if (!node->arrayMode().alreadyChecked(m_graph, node, m_state.forNode(node->child1())))
break;
node->convertToPhantom();
eliminated = true;
break;
}
case CheckFunction: {
if (m_state.forNode(node->child1()).value() != node->function())
break;
node->convertToPhantom();
eliminated = true;
break;
}
case GetById:
case GetByIdFlush: {
CodeOrigin codeOrigin = node->codeOrigin;
Edge childEdge = node->child1();
Node* child = childEdge.node();
unsigned identifierNumber = node->identifierNumber();
if (childEdge.useKind() != CellUse)
break;
Structure* structure = m_state.forNode(child).bestProvenStructure();
if (!structure)
break;
bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton();
bool needsCellCheck = m_state.forNode(child).m_type & ~SpecCell;
GetByIdStatus status = GetByIdStatus::computeFor(
vm(), structure, codeBlock()->identifier(identifierNumber));
if (!status.isSimple()) {
break;
}
ASSERT(status.structureSet().size() == 1);
ASSERT(status.chain().isEmpty());
ASSERT(status.structureSet().singletonStructure() == structure);
m_state.execute(indexInBlock);
eliminated = true;
if (needsWatchpoint) {
ASSERT(m_state.forNode(child).m_futurePossibleStructure.isSubsetOf(StructureSet(structure)));
m_insertionSet.insertNode(
indexInBlock, SpecNone, StructureTransitionWatchpoint, codeOrigin,
OpInfo(structure), childEdge);
} else if (needsCellCheck) {
m_insertionSet.insertNode(
indexInBlock, SpecNone, Phantom, codeOrigin, childEdge);
}
childEdge.setUseKind(KnownCellUse);
Edge propertyStorage;
if (isInlineOffset(status.offset()))
propertyStorage = childEdge;
else {
propertyStorage = Edge(m_insertionSet.insertNode(
indexInBlock, SpecNone, GetButterfly, codeOrigin, childEdge));
}
node->convertToGetByOffset(m_graph.m_storageAccessData.size(), propertyStorage);
StorageAccessData storageAccessData;
storageAccessData.offset = indexRelativeToBase(status.offset());
storageAccessData.identifierNumber = identifierNumber;
m_graph.m_storageAccessData.append(storageAccessData);
break;
}
case PutById:
case PutByIdDirect: {
CodeOrigin codeOrigin = node->codeOrigin;
Edge childEdge = node->child1();
Node* child = childEdge.node();
unsigned identifierNumber = node->identifierNumber();
ASSERT(childEdge.useKind() == CellUse);
Structure* structure = m_state.forNode(child).bestProvenStructure();
if (!structure)
break;
bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton();
bool needsCellCheck = m_state.forNode(child).m_type & ~SpecCell;
PutByIdStatus status = PutByIdStatus::computeFor(
vm(),
m_graph.globalObjectFor(codeOrigin),
structure,
codeBlock()->identifier(identifierNumber),
node->op() == PutByIdDirect);
if (!status.isSimpleReplace() && !status.isSimpleTransition())
break;
ASSERT(status.oldStructure() == structure);
m_state.execute(indexInBlock);
eliminated = true;
if (needsWatchpoint) {
ASSERT(m_state.forNode(child).m_futurePossibleStructure.isSubsetOf(StructureSet(structure)));
m_insertionSet.insertNode(
indexInBlock, SpecNone, StructureTransitionWatchpoint, codeOrigin,
OpInfo(structure), childEdge);
} else if (needsCellCheck) {
m_insertionSet.insertNode(
indexInBlock, SpecNone, Phantom, codeOrigin, childEdge);
}
childEdge.setUseKind(KnownCellUse);
StructureTransitionData* transitionData = 0;
if (status.isSimpleTransition()) {
transitionData = m_graph.addStructureTransitionData(
StructureTransitionData(structure, status.newStructure()));
if (node->op() == PutById) {
if (!structure->storedPrototype().isNull()) {
addStructureTransitionCheck(
codeOrigin, indexInBlock,
structure->storedPrototype().asCell());
}
for (WriteBarrier<Structure>* it = status.structureChain()->head(); *it; ++it) {
JSValue prototype = (*it)->storedPrototype();
if (prototype.isNull())
continue;
ASSERT(prototype.isCell());
addStructureTransitionCheck(
codeOrigin, indexInBlock, prototype.asCell());
}
}
}
Edge propertyStorage;
if (isInlineOffset(status.offset()))
propertyStorage = childEdge;
else if (status.isSimpleReplace() || structure->outOfLineCapacity() == status.newStructure()->outOfLineCapacity()) {
propertyStorage = Edge(m_insertionSet.insertNode(
indexInBlock, SpecNone, GetButterfly, codeOrigin, childEdge));
} else if (!structure->outOfLineCapacity()) {
ASSERT(status.newStructure()->outOfLineCapacity());
ASSERT(!isInlineOffset(status.offset()));
propertyStorage = Edge(m_insertionSet.insertNode(
indexInBlock, SpecNone, AllocatePropertyStorage,
codeOrigin, OpInfo(transitionData), childEdge));
} else {
ASSERT(structure->outOfLineCapacity());
ASSERT(status.newStructure()->outOfLineCapacity() > structure->outOfLineCapacity());
ASSERT(!isInlineOffset(status.offset()));
propertyStorage = Edge(m_insertionSet.insertNode(
indexInBlock, SpecNone, ReallocatePropertyStorage, codeOrigin,
OpInfo(transitionData), childEdge,
Edge(m_insertionSet.insertNode(
indexInBlock, SpecNone, GetButterfly, codeOrigin, childEdge))));
}
if (status.isSimpleTransition()) {
m_insertionSet.insertNode(
indexInBlock, SpecNone, PutStructure, codeOrigin,
OpInfo(transitionData), childEdge);
}
node->convertToPutByOffset(m_graph.m_storageAccessData.size(), propertyStorage);
StorageAccessData storageAccessData;
storageAccessData.offset = indexRelativeToBase(status.offset());
storageAccessData.identifierNumber = identifierNumber;
m_graph.m_storageAccessData.append(storageAccessData);
break;
}
default:
break;
}
if (eliminated) {
changed = true;
continue;
}
m_state.execute(indexInBlock);
if (!node->shouldGenerate() || m_state.didClobber() || node->hasConstant())
continue;
JSValue value = m_state.forNode(node).value();
if (!value)
continue;
CodeOrigin codeOrigin = node->codeOrigin;
AdjacencyList children = node->children;
if (node->op() == GetLocal) {
if (!node->child1())
continue;
if (m_graph.m_form != LoadStore) {
VariableAccessData* variable = node->variableAccessData();
Node* phi = node->child1().node();
if (phi->op() == Phi
&& block->variablesAtHead.operand(variable->local()) == phi
&& block->variablesAtTail.operand(variable->local()) == node) {
m_graph.convertToConstant(node, value);
Node* phantom = m_insertionSet.insertNode(
indexInBlock, SpecNone, PhantomLocal, codeOrigin,
OpInfo(variable), Edge(phi));
block->variablesAtHead.operand(variable->local()) = phantom;
block->variablesAtTail.operand(variable->local()) = phantom;
changed = true;
continue;
}
m_graph.dethread();
}
} else
ASSERT(!node->hasVariableAccessData());
m_graph.convertToConstant(node, value);
m_insertionSet.insertNode(
indexInBlock, SpecNone, Phantom, codeOrigin, children);
changed = true;
}
m_state.reset();
m_insertionSet.execute(block);
return changed;
}
#if !ASSERT_DISABLED
bool isCapturedAtOrAfter(BasicBlock* block, unsigned indexInBlock, int operand)
{
for (; indexInBlock < block->size(); ++indexInBlock) {
Node* node = block->at(indexInBlock);
if (!node->hasLocal())
continue;
if (node->local() != operand)
continue;
if (node->variableAccessData()->isCaptured())
return true;
}
return false;
}
#endif // !ASSERT_DISABLED
void addStructureTransitionCheck(CodeOrigin codeOrigin, unsigned indexInBlock, JSCell* cell)
{
Node* weakConstant = m_insertionSet.insertNode(
indexInBlock, speculationFromValue(cell), WeakJSConstant, codeOrigin, OpInfo(cell));
if (cell->structure()->transitionWatchpointSetIsStillValid()) {
m_insertionSet.insertNode(
indexInBlock, SpecNone, StructureTransitionWatchpoint, codeOrigin,
OpInfo(cell->structure()), Edge(weakConstant, CellUse));
return;
}
m_insertionSet.insertNode(
indexInBlock, SpecNone, CheckStructure, codeOrigin,
OpInfo(m_graph.addStructureSet(cell->structure())), Edge(weakConstant, CellUse));
}
bool paintUnreachableCode(BlockIndex blockIndex)
{
bool changed = false;
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
dataLogF("Painting unreachable code in Block #%u.\n", blockIndex);
#endif
BasicBlock* block = m_graph.m_blocks[blockIndex].get();
m_state.beginBasicBlock(block);
for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
m_state.execute(indexInBlock);
if (m_state.isValid())
continue;
Node* node = block->at(indexInBlock);
switch (node->op()) {
case Return:
case Unreachable:
case ForceOSRExit:
break;
default:
m_insertionSet.insertNode(
indexInBlock, SpecNone, ForceOSRExit, node->codeOrigin);
changed = true;
break;
}
break;
}
m_state.reset();
m_insertionSet.execute(block);
return changed;
}
AbstractState m_state;
InsertionSet m_insertionSet;
};
bool performConstantFolding(Graph& graph)
{
SamplingRegion samplingRegion("DFG Constant Folding Phase");
return runPhase<ConstantFoldingPhase>(graph);
}
} }
#endif // ENABLE(DFG_JIT)