DFGTypeCheckHoistingPhase.cpp [plain text]
#include "config.h"
#include "DFGTypeCheckHoistingPhase.h"
#if ENABLE(DFG_JIT)
#include "DFGBasicBlock.h"
#include "DFGGraph.h"
#include "DFGInsertionSet.h"
#include "DFGPhase.h"
#include "DFGVariableAccessDataDump.h"
#include "Operations.h"
#include <wtf/HashMap.h>
namespace JSC { namespace DFG {
enum CheckBallot { VoteOther, VoteStructureCheck };
class TypeCheckHoistingPhase : public Phase {
public:
TypeCheckHoistingPhase(Graph& graph)
: Phase(graph, "structure check hoisting")
{
}
bool run()
{
ASSERT(m_graph.m_form == ThreadedCPS);
for (unsigned i = m_graph.m_variableAccessData.size(); i--;) {
VariableAccessData* variable = &m_graph.m_variableAccessData[i];
if (!variable->isRoot())
continue;
variable->clearVotes();
}
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 CheckStructure:
case StructureTransitionWatchpoint: {
Node* child = node->child1().node();
if (child->op() != GetLocal)
break;
VariableAccessData* variable = child->variableAccessData();
variable->vote(VoteStructureCheck);
if (!shouldConsiderForHoisting(variable))
break;
noticeStructureCheck(variable, node->structureSet());
break;
}
case ForwardCheckStructure:
case ForwardStructureTransitionWatchpoint:
RELEASE_ASSERT_NOT_REACHED();
break;
case GetByOffset:
case PutByOffset:
case PutStructure:
case AllocatePropertyStorage:
case ReallocatePropertyStorage:
case GetButterfly:
case GetByVal:
case PutByVal:
case PutByValAlias:
case GetArrayLength:
case CheckArray:
case GetIndexedPropertyStorage:
case Phantom:
break;
case ArrayifyToStructure:
case Arrayify:
if (node->arrayMode().conversion() == Array::RageConvert) {
Node* child = node->child1().node();
if (child->op() != GetLocal)
break;
VariableAccessData* variable = child->variableAccessData();
variable->vote(VoteOther);
if (!shouldConsiderForHoisting(variable))
break;
noticeStructureCheck(variable, 0);
}
break;
case SetLocal: {
VariableAccessData* variable = node->variableAccessData();
if (!shouldConsiderForHoisting(variable))
break;
Node* source = node->child1().node();
for (unsigned subIndexInBlock = 0; subIndexInBlock < block->size(); ++subIndexInBlock) {
Node* subNode = block->at(subIndexInBlock);
switch (subNode->op()) {
case CheckStructure: {
if (subNode->child1() != source)
break;
noticeStructureCheck(variable, subNode->structureSet());
break;
}
case StructureTransitionWatchpoint: {
if (subNode->child1() != source)
break;
noticeStructureCheck(variable, subNode->structure());
break;
}
default:
break;
}
}
m_graph.voteChildren(node, VoteOther);
break;
}
case GarbageValue:
break;
default:
m_graph.voteChildren(node, VoteOther);
break;
}
}
}
for (unsigned i = m_graph.m_variableAccessData.size(); i--;) {
VariableAccessData* variable = &m_graph.m_variableAccessData[i];
if (!variable->isRoot())
continue;
if (variable->voteRatio() >= Options::structureCheckVoteRatioForHoisting())
continue;
HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable);
if (iter == m_map.end())
continue;
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
dataLog(
"Zeroing the structure to hoist for ", VariableAccessDataDump(m_graph, variable),
" because the ratio is ", variable->voteRatio(), ".\n");
#endif
iter->value.m_structure = 0;
}
for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
BasicBlock* block = m_graph.m_blocks[blockIndex].get();
if (!block)
continue;
ASSERT(block->isReachable);
if (!block->isOSRTarget)
continue;
if (block->bytecodeBegin != m_graph.m_osrEntryBytecodeIndex)
continue;
for (size_t i = 0; i < m_graph.m_mustHandleValues.size(); ++i) {
int operand = m_graph.m_mustHandleValues.operandForIndex(i);
Node* node = block->variablesAtHead.operand(operand);
if (!node)
continue;
VariableAccessData* variable = node->variableAccessData();
HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable);
if (iter == m_map.end())
continue;
if (!iter->value.m_structure)
continue;
JSValue value = m_graph.m_mustHandleValues[i];
if (!value || !value.isCell()) {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
dataLog(
"Zeroing the structure to hoist for ", VariableAccessDataDump(m_graph, variable),
" because the OSR entry value is not a cell: ", value, ".\n");
#endif
iter->value.m_structure = 0;
continue;
}
if (value.asCell()->structure() != iter->value.m_structure) {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
dataLog(
"Zeroing the structure to hoist for ", VariableAccessDataDump(m_graph, variable),
" because the OSR entry value has structure ",
RawPointer(value.asCell()->structure()), " and we wanted ",
RawPointer(iter->value.m_structure), ".\n");
#endif
iter->value.m_structure = 0;
continue;
}
}
}
bool changed = false;
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
for (HashMap<VariableAccessData*, CheckData>::iterator it = m_map.begin();
it != m_map.end(); ++it) {
if (!it->value.m_structure) {
dataLog(
"Not hoisting checks for ", VariableAccessDataDump(m_graph, it->key),
" because of heuristics.\n");
continue;
}
dataLog("Hoisting checks for ", VariableAccessDataDump(m_graph, it->key), "\n");
}
#endif // DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
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 SetArgument: {
ASSERT(!blockIndex);
VariableAccessData* variable = node->variableAccessData();
HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable);
if (iter == m_map.end())
break;
if (!iter->value.m_structure)
break;
CodeOrigin codeOrigin = node->codeOrigin;
Node* getLocal = insertionSet.insertNode(
indexInBlock + 1, variable->prediction(), GetLocal, codeOrigin,
OpInfo(variable), Edge(node));
insertionSet.insertNode(
indexInBlock + 1, SpecNone, CheckStructure, codeOrigin,
OpInfo(m_graph.addStructureSet(iter->value.m_structure)),
Edge(getLocal, CellUse));
if (block->variablesAtTail.operand(variable->local()) == node)
block->variablesAtTail.operand(variable->local()) = getLocal;
m_graph.substituteGetLocal(*block, indexInBlock, variable, getLocal);
changed = true;
break;
}
case SetLocal: {
VariableAccessData* variable = node->variableAccessData();
HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable);
if (iter == m_map.end())
break;
if (!iter->value.m_structure)
break;
CodeOrigin codeOrigin = node->codeOrigin;
Edge child1 = node->child1();
insertionSet.insertNode(
indexInBlock, SpecNone, SetLocal, codeOrigin, OpInfo(variable), child1);
insertionSet.insertNode(
indexInBlock, SpecNone, ForwardCheckStructure, codeOrigin,
OpInfo(m_graph.addStructureSet(iter->value.m_structure)),
Edge(child1.node(), CellUse));
changed = true;
break;
}
default:
break;
}
}
insertionSet.execute(block);
}
return changed;
}
private:
bool shouldConsiderForHoisting(VariableAccessData* variable)
{
if (!variable->shouldUnboxIfPossible())
return false;
if (variable->structureCheckHoistingFailed())
return false;
if (!isCellSpeculation(variable->prediction()))
return false;
return true;
}
void noticeStructureCheck(VariableAccessData* variable, Structure* structure)
{
HashMap<VariableAccessData*, CheckData>::AddResult result =
m_map.add(variable, CheckData(structure));
if (result.isNewEntry)
return;
if (result.iterator->value.m_structure == structure)
return;
result.iterator->value.m_structure = 0;
}
void noticeStructureCheck(VariableAccessData* variable, const StructureSet& set)
{
if (set.size() != 1) {
noticeStructureCheck(variable, 0);
return;
}
noticeStructureCheck(variable, set.singletonStructure());
}
struct CheckData {
Structure* m_structure;
CheckData()
: m_structure(0)
{
}
CheckData(Structure* structure)
: m_structure(structure)
{
}
};
HashMap<VariableAccessData*, CheckData> m_map;
};
bool performTypeCheckHoisting(Graph& graph)
{
SamplingRegion samplingRegion("DFG Type Check Hoisting Phase");
return runPhase<TypeCheckHoistingPhase>(graph);
}
} }
#endif // ENABLE(DFG_JIT)