/*
- * Copyright (C) 2011, 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2012, 2013, 2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
#if ENABLE(DFG_JIT)
+#include "DFGAbstractHeap.h"
+#include "DFGClobberize.h"
+#include "DFGEdgeUsesStructure.h"
#include "DFGGraph.h"
#include "DFGPhase.h"
-#include "JSCellInlines.h"
+#include "JSCInlines.h"
+#include <array>
#include <wtf/FastBitVector.h>
namespace JSC { namespace DFG {
bool run()
{
- ASSERT((cseMode == NormalCSE) == (m_graph.m_fixpointState == FixpointNotConverged));
ASSERT(m_graph.m_fixpointState != BeforeFixpoint);
m_changed = false;
- for (unsigned block = 0; block < m_graph.m_blocks.size(); ++block)
- performBlockCSE(m_graph.m_blocks[block].get());
+ m_graph.clearReplacements();
+
+ for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) {
+ BasicBlock* block = m_graph.block(blockIndex);
+ if (!block)
+ continue;
+
+ // All Phis need to already be marked as relevant to OSR.
+ if (!ASSERT_DISABLED) {
+ for (unsigned i = 0; i < block->phis.size(); ++i)
+ ASSERT(block->phis[i]->flags() & NodeRelevantToOSR);
+ }
+
+ for (unsigned i = block->size(); i--;) {
+ Node* node = block->at(i);
+
+ switch (node->op()) {
+ case SetLocal:
+ case GetLocal: // FIXME: The GetLocal case is only necessary until we do https://bugs.webkit.org/show_bug.cgi?id=106707.
+ node->mergeFlags(NodeRelevantToOSR);
+ break;
+ default:
+ node->clearFlags(NodeRelevantToOSR);
+ break;
+ }
+ }
+ }
+
+ for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) {
+ BasicBlock* block = m_graph.block(blockIndex);
+ if (!block)
+ continue;
+
+ for (unsigned i = block->size(); i--;) {
+ Node* node = block->at(i);
+ if (!node->containsMovHint())
+ continue;
+
+ ASSERT(node->op() != ZombieHint);
+ node->child1()->mergeFlags(NodeRelevantToOSR);
+ }
+ }
+
+ if (m_graph.m_form == SSA) {
+ Vector<BasicBlock*> depthFirst;
+ m_graph.getBlocksInDepthFirstOrder(depthFirst);
+ for (unsigned i = 0; i < depthFirst.size(); ++i)
+ performBlockCSE(depthFirst[i]);
+ } else {
+ for (unsigned blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex)
+ performBlockCSE(m_graph.block(blockIndex));
+ }
return m_changed;
}
private:
- Node* canonicalize(Node* node)
- {
- if (!node)
- return 0;
-
- if (node->op() == ValueToInt32)
- node = node->child1().node();
-
- return node;
- }
- Node* canonicalize(Edge edge)
- {
- return canonicalize(edge.node());
- }
-
unsigned endIndexForPureCSE()
{
unsigned result = m_lastSeen[m_currentNode->op()];
else
result++;
ASSERT(result <= m_indexInBlock);
-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLogF(" limit %u: ", result);
-#endif
return result;
}
Node* pureCSE(Node* node)
{
- Node* child1 = canonicalize(node->child1());
- Node* child2 = canonicalize(node->child2());
- Node* child3 = canonicalize(node->child3());
+ Edge child1 = node->child1().sanitized();
+ Edge child2 = node->child2().sanitized();
+ Edge child3 = node->child3().sanitized();
for (unsigned i = endIndexForPureCSE(); i--;) {
Node* otherNode = m_currentBlock->at(i);
if (node->op() != otherNode->op())
continue;
- if (node->arithNodeFlags() != otherNode->arithNodeFlags())
- continue;
-
- Node* otherChild = canonicalize(otherNode->child1());
+ Edge otherChild = otherNode->child1().sanitized();
if (!otherChild)
return otherNode;
if (otherChild != child1)
continue;
- otherChild = canonicalize(otherNode->child2());
+ otherChild = otherNode->child2().sanitized();
if (!otherChild)
return otherNode;
if (otherChild != child2)
continue;
- otherChild = canonicalize(otherNode->child3());
+ otherChild = otherNode->child3().sanitized();
if (!otherChild)
return otherNode;
if (otherChild != child3)
return 0;
}
- Node* int32ToDoubleCSE(Node* node)
+ Node* constantCSE(Node* node)
{
- for (unsigned i = m_indexInBlock; i--;) {
+ for (unsigned i = endIndexForPureCSE(); i--;) {
Node* otherNode = m_currentBlock->at(i);
- if (otherNode == node->child1())
- return 0;
- switch (otherNode->op()) {
- case Int32ToDouble:
- case ForwardInt32ToDouble:
- if (otherNode->child1() == node->child1())
- return otherNode;
- break;
- default:
- break;
- }
+ if (otherNode->op() != node->op())
+ continue;
+
+ if (otherNode->constantNumber() != node->constantNumber())
+ continue;
+
+ return otherNode;
}
return 0;
}
- Node* constantCSE(Node* node)
+ Node* weakConstantCSE(Node* node)
{
for (unsigned i = endIndexForPureCSE(); i--;) {
Node* otherNode = m_currentBlock->at(i);
- if (otherNode->op() != JSConstant)
+ if (otherNode->op() != WeakJSConstant)
continue;
- if (otherNode->constantNumber() != node->constantNumber())
+ if (otherNode->weakConstant() != node->weakConstant())
continue;
return otherNode;
return 0;
}
- Node* weakConstantCSE(Node* node)
+ Node* constantStoragePointerCSE(Node* node)
{
for (unsigned i = endIndexForPureCSE(); i--;) {
Node* otherNode = m_currentBlock->at(i);
- if (otherNode->op() != WeakJSConstant)
+ if (otherNode->op() != ConstantStoragePointer)
continue;
- if (otherNode->weakConstant() != node->weakConstant())
+ if (otherNode->storagePointer() != node->storagePointer())
continue;
return otherNode;
return 0;
}
- Node* getCalleeLoadElimination(InlineCallFrame* inlineCallFrame)
+ Node* getCalleeLoadElimination()
{
for (unsigned i = m_indexInBlock; i--;) {
Node* node = m_currentBlock->at(i);
- if (node->codeOrigin.inlineCallFrame != inlineCallFrame)
- continue;
switch (node->op()) {
case GetCallee:
return node;
- case SetCallee:
- return node->child1().node();
default:
break;
}
return node;
break;
+ case PutByValDirect:
case PutByVal:
if (!m_graph.byValIsPure(node))
return 0;
return 0;
}
- Node* scopedVarLoadElimination(Node* registers, unsigned varNumber)
+ Node* scopedVarLoadElimination(Node* registers, int varNumber)
{
for (unsigned i = m_indexInBlock; i--;) {
Node* node = m_currentBlock->at(i);
switch (node->op()) {
- case GetScopedVar: {
+ case GetClosureVar: {
if (node->child1() == registers && node->varNumber() == varNumber)
return node;
break;
}
- case PutScopedVar: {
+ case PutClosureVar: {
if (node->varNumber() != varNumber)
break;
if (node->child2() == registers)
return 0;
}
- bool globalVarWatchpointElimination(WriteBarrier<Unknown>* registerPointer)
+ bool varInjectionWatchpointElimination()
{
for (unsigned i = m_indexInBlock; i--;) {
Node* node = m_currentBlock->at(i);
- switch (node->op()) {
- case GlobalVarWatchpoint:
- if (node->registerPointer() == registerPointer)
- return true;
- break;
- case PutGlobalVar:
- if (node->registerPointer() == registerPointer)
- return false;
- break;
- default:
- break;
- }
+ if (node->op() == VarInjectionWatchpoint)
+ return true;
if (m_graph.clobbersWorld(node))
break;
}
Node* node = m_currentBlock->at(i);
switch (node->op()) {
case PutGlobalVar:
- case PutGlobalVarCheck:
if (node->registerPointer() == registerPointer)
return node;
break;
return 0;
}
- Node* scopedVarStoreElimination(Node* scope, Node* registers, unsigned varNumber)
+ Node* scopedVarStoreElimination(Node* scope, Node* registers, int varNumber)
{
for (unsigned i = m_indexInBlock; i--;) {
Node* node = m_currentBlock->at(i);
switch (node->op()) {
- case PutScopedVar: {
+ case PutClosureVar: {
if (node->varNumber() != varNumber)
break;
if (node->child1() == scope && node->child2() == registers)
return 0;
}
- case GetScopedVar: {
+ case GetClosureVar: {
// Let's be conservative.
if (node->varNumber() == varNumber)
return 0;
break;
}
- case GetLocal: {
+ case GetLocal:
+ case SetLocal: {
VariableAccessData* variableAccessData = node->variableAccessData();
if (variableAccessData->isCaptured()
&& variableAccessData->local() == static_cast<VirtualRegister>(varNumber))
return 0;
}
- Node* getByValLoadElimination(Node* child1, Node* child2)
+ Node* getByValLoadElimination(Node* child1, Node* child2, ArrayMode arrayMode)
{
for (unsigned i = m_indexInBlock; i--;) {
Node* node = m_currentBlock->at(i);
- if (node == child1 || node == canonicalize(child2))
+ if (node == child1 || node == child2)
break;
switch (node->op()) {
case GetByVal:
if (!m_graph.byValIsPure(node))
return 0;
- if (node->child1() == child1 && canonicalize(node->child2()) == canonicalize(child2))
+ if (node->child1() == child1
+ && node->child2() == child2
+ && node->arrayMode().type() == arrayMode.type())
return node;
break;
+
+ case PutByValDirect:
case PutByVal:
case PutByValAlias: {
if (!m_graph.byValIsPure(node))
return 0;
- if (m_graph.varArgChild(node, 0) == child1 && canonicalize(m_graph.varArgChild(node, 1)) == canonicalize(child2))
+ // Typed arrays
+ if (arrayMode.typedArrayType() != NotTypedArray)
+ return 0;
+ if (m_graph.varArgChild(node, 0) == child1
+ && m_graph.varArgChild(node, 1) == child2
+ && node->arrayMode().type() == arrayMode.type())
return m_graph.varArgChild(node, 2).node();
// We must assume that the PutByVal will clobber the location we're getting from.
// FIXME: We can do better; if we know that the PutByVal is accessing an array of a
// typed arrays! An Int32Array can alias a Float64Array for example, and so on.
return 0;
}
- case PutStructure:
- case PutByOffset:
- // GetByVal currently always speculates that it's accessing an
- // array with an integer index, which means that it's impossible
- // for a structure change or a put to property storage to affect
- // the GetByVal.
- break;
default:
if (m_graph.clobbersWorld(node))
return 0;
switch (node->op()) {
case CheckStructure:
- case ForwardCheckStructure:
if (node->child1() == child1
&& structureSet.isSupersetOf(node->structureSet()))
return true;
break;
case StructureTransitionWatchpoint:
- case ForwardStructureTransitionWatchpoint:
if (node->child1() == child1
&& structureSet.contains(node->structure()))
return true;
// Setting a property cannot change the structure.
break;
+ case MultiPutByOffset:
+ if (node->multiPutByOffsetData().writesStructures())
+ return false;
+ break;
+
+ case PutByValDirect:
case PutByVal:
case PutByValAlias:
if (m_graph.byValIsPure(node)) {
switch (node->op()) {
case CheckStructure:
- case ForwardCheckStructure:
if (node->child1() == child1
&& node->structureSet().containsOnly(structure))
return true;
case PutByOffset:
// Setting a property cannot change the structure.
break;
+
+ case MultiPutByOffset:
+ if (node->multiPutByOffsetData().writesStructures())
+ return false;
+ break;
+ case PutByValDirect:
case PutByVal:
case PutByValAlias:
if (m_graph.byValIsPure(node)) {
return false;
case StructureTransitionWatchpoint:
- case ForwardStructureTransitionWatchpoint:
if (node->structure() == structure && node->child1() == child1)
return true;
break;
break;
switch (node->op()) {
case CheckStructure:
- case ForwardCheckStructure:
return 0;
case PhantomPutStructure:
case ToString:
case NewStringObject:
case MakeRope:
+ case NewTypedArray:
+ case MultiPutByOffset:
+ return 0;
+
+ // This either exits, causes a GC (lazy string allocation), or clobbers
+ // the world. The chances of it not doing any of those things are so
+ // slim that we might as well not even try to reason about it.
+ case GetByVal:
return 0;
case GetIndexedPropertyStorage:
}
if (m_graph.clobbersWorld(node) || node->canExit())
return 0;
+ if (edgesUseStructure(m_graph, node))
+ return 0;
}
return 0;
}
- Node* getByOffsetLoadElimination(unsigned identifierNumber, Node* child1)
+ Node* getByOffsetLoadElimination(unsigned identifierNumber, Node* base)
{
for (unsigned i = m_indexInBlock; i--;) {
Node* node = m_currentBlock->at(i);
- if (node == child1)
+ if (node == base)
break;
switch (node->op()) {
case GetByOffset:
- if (node->child1() == child1
+ if (node->child2() == base
&& m_graph.m_storageAccessData[node->storageAccessDataIndex()].identifierNumber == identifierNumber)
return node;
break;
+ case MultiGetByOffset:
+ if (node->child1() == base
+ && node->multiGetByOffsetData().identifierNumber == identifierNumber)
+ return node;
+ break;
+
case PutByOffset:
if (m_graph.m_storageAccessData[node->storageAccessDataIndex()].identifierNumber == identifierNumber) {
- if (node->child1() == child1) // Must be same property storage.
+ if (node->child2() == base) // Must be same property storage.
return node->child3().node();
return 0;
}
break;
- case PutStructure:
- // Changing the structure cannot change the outcome of a property get.
+ case MultiPutByOffset:
+ if (node->multiPutByOffsetData().identifierNumber == identifierNumber) {
+ if (node->child1() == base)
+ return node->child2().node();
+ return 0;
+ }
break;
-
+
+ case PutByValDirect:
case PutByVal:
case PutByValAlias:
if (m_graph.byValIsPure(node)) {
}
break;
+ case MultiPutByOffset:
+ if (node->multiPutByOffsetData().identifierNumber == identifierNumber)
+ return 0;
+ break;
+
+ case PutByValDirect:
case PutByVal:
case PutByValAlias:
case GetByVal:
// storage, so we conservatively assume that it may change the storage
// pointer of any object, including ours.
return 0;
-
- case PutByOffset:
- case PutStructure:
- // Changing the structure or putting to the storage cannot
- // change the property storage pointer.
- break;
-
+
+ case PutByValDirect:
case PutByVal:
case PutByValAlias:
if (m_graph.byValIsPure(node)) {
// But that seems like it would take Effort.
return 0;
+ case MultiPutByOffset:
+ if (node->multiPutByOffsetData().reallocatesStorage())
+ return 0;
+ break;
+
default:
if (m_graph.clobbersWorld(node))
return 0;
break;
switch (node->op()) {
- case PutByOffset:
- case PutStructure:
- // Changing the structure or putting to the storage cannot
- // change the property storage pointer.
- break;
-
case CheckArray:
if (node->child1() == child1 && node->arrayMode() == arrayMode)
return true;
break;
}
- case PutByOffset:
- case PutStructure:
- // Changing the structure or putting to the storage cannot
- // change the property storage pointer.
+ default:
+ if (m_graph.clobbersWorld(node))
+ return 0;
break;
-
+ }
+ }
+ return 0;
+ }
+
+ Node* getTypedArrayByteOffsetLoadElimination(Node* child1)
+ {
+ for (unsigned i = m_indexInBlock; i--;) {
+ Node* node = m_currentBlock->at(i);
+ if (node == child1)
+ break;
+
+ switch (node->op()) {
+ case GetTypedArrayByteOffset: {
+ if (node->child1() == child1)
+ return node;
+ break;
+ }
+
default:
if (m_graph.clobbersWorld(node))
return 0;
return 0;
}
- Node* getMyScopeLoadElimination(InlineCallFrame* inlineCallFrame)
+ Node* getMyScopeLoadElimination()
{
for (unsigned i = m_indexInBlock; i--;) {
Node* node = m_currentBlock->at(i);
- if (node->codeOrigin.inlineCallFrame != inlineCallFrame)
- continue;
switch (node->op()) {
case CreateActivation:
// This may cause us to return a different scope.
return 0;
case GetMyScope:
return node;
- case SetMyScope:
- return node->child1().node();
default:
break;
}
}
break;
- case PutScopedVar:
+ case GetClosureVar:
+ case PutClosureVar:
if (static_cast<VirtualRegister>(node->varNumber()) == local)
return 0;
break;
return 0;
}
- struct SetLocalStoreEliminationResult {
- SetLocalStoreEliminationResult()
- : mayBeAccessed(false)
- , mayExit(false)
- , mayClobberWorld(false)
- {
- }
-
- bool mayBeAccessed;
- bool mayExit;
- bool mayClobberWorld;
- };
- SetLocalStoreEliminationResult setLocalStoreElimination(
- VirtualRegister local, Node* expectedNode)
+ bool uncapturedSetLocalStoreElimination(VirtualRegister local, Node* expectedNode)
{
- SetLocalStoreEliminationResult result;
for (unsigned i = m_indexInBlock; i--;) {
Node* node = m_currentBlock->at(i);
switch (node->op()) {
case GetLocal:
case Flush:
if (node->local() == local)
- result.mayBeAccessed = true;
+ return false;
break;
case GetLocalUnlinked:
if (node->unlinkedLocal() == local)
- result.mayBeAccessed = true;
+ return false;
break;
case SetLocal: {
if (node->local() != local)
break;
if (node != expectedNode)
- result.mayBeAccessed = true;
- return result;
+ return false;
+ return true;
}
- case GetScopedVar:
+ case GetClosureVar:
+ case PutClosureVar:
if (static_cast<VirtualRegister>(node->varNumber()) == local)
- result.mayBeAccessed = true;
+ return false;
break;
case GetMyScope:
case SkipTopScope:
- if (m_graph.uncheckedActivationRegisterFor(node->codeOrigin) == local)
- result.mayBeAccessed = true;
+ if (node->origin.semantic.inlineCallFrame)
+ break;
+ if (m_graph.uncheckedActivationRegister() == local)
+ return false;
break;
case CheckArgumentsNotCreated:
case GetMyArgumentsLength:
case GetMyArgumentsLengthSafe:
- if (m_graph.uncheckedArgumentsRegisterFor(node->codeOrigin) == local)
- result.mayBeAccessed = true;
+ if (m_graph.uncheckedArgumentsRegisterFor(node->origin.semantic) == local)
+ return false;
break;
case GetMyArgumentByVal:
case GetMyArgumentByValSafe:
- result.mayBeAccessed = true;
- break;
+ return false;
case GetByVal:
// If this is accessing arguments then it's potentially accessing locals.
if (node->arrayMode().type() == Array::Arguments)
- result.mayBeAccessed = true;
+ return false;
break;
case CreateArguments:
// are live. We could be clever here and check if the local qualifies as an
// argument register. But that seems like it would buy us very little since
// any kind of tear offs are rare to begin with.
- result.mayBeAccessed = true;
- break;
+ return false;
default:
break;
}
- result.mayExit |= node->canExit();
- result.mayClobberWorld |= m_graph.clobbersWorld(node);
+ if (m_graph.clobbersWorld(node))
+ return false;
}
RELEASE_ASSERT_NOT_REACHED();
- // Be safe in release mode.
- result.mayBeAccessed = true;
- return result;
+ return false;
+ }
+
+ bool capturedSetLocalStoreElimination(VirtualRegister local, Node* expectedNode)
+ {
+ for (unsigned i = m_indexInBlock; i--;) {
+ Node* node = m_currentBlock->at(i);
+ switch (node->op()) {
+ case GetLocal:
+ case Flush:
+ if (node->local() == local)
+ return false;
+ break;
+
+ case GetLocalUnlinked:
+ if (node->unlinkedLocal() == local)
+ return false;
+ break;
+
+ case SetLocal: {
+ if (node->local() != local)
+ break;
+ if (node != expectedNode)
+ return false;
+ return true;
+ }
+
+ case Phantom:
+ case Check:
+ case HardPhantom:
+ case MovHint:
+ case JSConstant:
+ case DoubleConstant:
+ case Int52Constant:
+ break;
+
+ default:
+ return false;
+ }
+ }
+ RELEASE_ASSERT_NOT_REACHED();
+ return false;
+ }
+
+ bool setLocalStoreElimination(VariableAccessData* variableAccessData, Node* expectedNode)
+ {
+ if (variableAccessData->isCaptured())
+ return capturedSetLocalStoreElimination(variableAccessData->local(), expectedNode);
+ return uncapturedSetLocalStoreElimination(variableAccessData->local(), expectedNode);
+ }
+
+ bool invalidationPointElimination()
+ {
+ for (unsigned i = m_indexInBlock; i--;) {
+ Node* node = m_currentBlock->at(i);
+ if (node->op() == InvalidationPoint)
+ return true;
+ if (writesOverlap(m_graph, node, Watchpoint_fire))
+ break;
+ }
+ return false;
}
void eliminateIrrelevantPhantomChildren(Node* node)
if (edge->flags() & NodeRelevantToOSR)
continue;
-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Eliminating edge @", m_currentNode->index(), " -> @", edge->index());
-#endif
node->children.removeEdge(i--);
m_changed = true;
}
if (!replacement)
return false;
-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLogF(" Replacing @%u -> @%u", m_currentNode->index(), replacement->index());
-#endif
+ if (!ASSERT_DISABLED
+ && canonicalResultRepresentation(m_currentNode->result()) != canonicalResultRepresentation(replacement->result())) {
+ startCrashing();
+ dataLog("CSE attempting to replace a node with a mismatched result: ", m_currentNode, " with ", replacement, "\n");
+ dataLog("\n");
+ m_graph.dump();
+ RELEASE_ASSERT_NOT_REACHED();
+ }
m_currentNode->convertToPhantom();
eliminateIrrelevantPhantomChildren(m_currentNode);
// At this point we will eliminate all references to this node.
- m_currentNode->replacement = replacement;
+ m_currentNode->misc.replacement = replacement;
m_changed = true;
void eliminate()
{
-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLogF(" Eliminating @%u", m_currentNode->index());
-#endif
-
ASSERT(m_currentNode->mustGenerate());
m_currentNode->convertToPhantom();
eliminateIrrelevantPhantomChildren(m_currentNode);
if (!node)
return;
ASSERT(node->mustGenerate());
- node->setOpAndDefaultNonExitFlags(phantomType);
+ node->setOpAndDefaultFlags(phantomType);
if (phantomType == Phantom)
eliminateIrrelevantPhantomChildren(node);
if (cseMode == NormalCSE)
m_graph.performSubstitution(node);
- if (node->op() == SetLocal)
- node->child1()->mergeFlags(NodeRelevantToOSR);
-
-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLogF(" %s @%u: ", Graph::opName(node->op()), node->index());
-#endif
-
- // NOTE: there are some nodes that we deliberately don't CSE even though we
- // probably could, like MakeRope and ToPrimitive. That's because there is no
- // evidence that doing CSE on these nodes would result in a performance
- // progression. Hence considering these nodes in CSE would just mean that this
- // code does more work with no win. Of course, we may want to reconsider this,
- // since MakeRope is trivially CSE-able. It's not trivially doable for
- // ToPrimitive, but we could change that with some speculations if we really
- // needed to.
-
switch (node->op()) {
case Identity:
case BitRShift:
case BitLShift:
case BitURShift:
- case ArithAdd:
- case ArithSub:
- case ArithNegate:
- case ArithMul:
- case ArithIMul:
- case ArithMod:
- case ArithDiv:
case ArithAbs:
case ArithMin:
case ArithMax:
case ArithSqrt:
+ case ArithFRound:
+ case ArithSin:
+ case ArithCos:
case StringCharAt:
case StringCharCodeAt:
case IsUndefined:
case IsString:
case IsObject:
case IsFunction:
- case DoubleAsInt32:
case LogicalNot:
case SkipTopScope:
case SkipScope:
- case GetScopeRegisters:
+ case GetClosureRegisters:
case GetScope:
case TypeOf:
case CompareEqConstant:
case ValueToInt32:
+ case MakeRope:
+ case DoubleRep:
+ case ValueRep:
+ case Int52Rep:
+ case BooleanToNumber:
if (cseMode == StoreElimination)
break;
setReplacement(pureCSE(node));
break;
- case Int32ToDouble:
- case ForwardInt32ToDouble:
+ case ArithAdd:
+ case ArithSub:
+ case ArithNegate:
+ case ArithMul:
+ case ArithDiv:
+ case ArithMod:
+ case UInt32ToNumber:
+ case DoubleAsInt32: {
if (cseMode == StoreElimination)
break;
- setReplacement(int32ToDoubleCSE(node));
+ Node* candidate = pureCSE(node);
+ if (!candidate)
+ break;
+ if (!subsumes(candidate->arithMode(), node->arithMode())) {
+ if (!subsumes(node->arithMode(), candidate->arithMode()))
+ break;
+ candidate->setArithMode(node->arithMode());
+ }
+ setReplacement(candidate);
break;
+ }
case GetCallee:
if (cseMode == StoreElimination)
break;
- setReplacement(getCalleeLoadElimination(node->codeOrigin.inlineCallFrame));
+ setReplacement(getCalleeLoadElimination());
break;
case GetLocal: {
}
case Flush: {
+ ASSERT(m_graph.m_form != SSA);
VariableAccessData* variableAccessData = node->variableAccessData();
- VirtualRegister local = variableAccessData->local();
+ if (!node->child1()) {
+ // FIXME: It's silly that we punt on flush-eliminating here. We don't really
+ // need child1 to figure out what's going on.
+ // https://bugs.webkit.org/show_bug.cgi?id=130521
+ break;
+ }
Node* replacement = node->child1().node();
if (replacement->op() != SetLocal)
break;
if (cseMode == NormalCSE) {
// Need to be conservative at this time; if the SetLocal has any chance of performing
// any speculations then we cannot do anything.
- if (variableAccessData->isCaptured()) {
- // Captured SetLocals never speculate and hence never exit.
- } else {
- if (variableAccessData->shouldUseDoubleFormat())
- break;
- SpeculatedType prediction = variableAccessData->argumentAwarePrediction();
- if (isInt32Speculation(prediction))
- break;
- if (isArraySpeculation(prediction))
- break;
- if (isBooleanSpeculation(prediction))
- break;
- }
+ FlushFormat format = variableAccessData->flushFormat();
+ ASSERT(format != DeadFlush);
+ if (format != FlushedJSValue)
+ break;
} else {
if (replacement->canExit())
break;
}
- SetLocalStoreEliminationResult result =
- setLocalStoreElimination(local, replacement);
- if (result.mayBeAccessed || result.mayClobberWorld)
+ if (!setLocalStoreElimination(variableAccessData, replacement))
break;
ASSERT(replacement->op() == SetLocal);
- // FIXME: Investigate using mayExit as a further optimization.
node->convertToPhantom();
Node* dataNode = replacement->child1().node();
ASSERT(dataNode->hasResult());
- m_graph.clearAndDerefChild1(node);
- node->children.child1() = Edge(dataNode);
+ node->child1() = dataNode->defaultEdge();
m_graph.dethread();
m_changed = true;
break;
}
case JSConstant:
+ case DoubleConstant:
+ case Int52Constant:
if (cseMode == StoreElimination)
break;
// This is strange, but necessary. Some phases will convert nodes to constants,
setReplacement(weakConstantCSE(node));
break;
+ case ConstantStoragePointer:
+ if (cseMode == StoreElimination)
+ break;
+ setReplacement(constantStoragePointerCSE(node));
+ break;
+
case GetArrayLength:
if (cseMode == StoreElimination)
break;
case GetMyScope:
if (cseMode == StoreElimination)
break;
- setReplacement(getMyScopeLoadElimination(node->codeOrigin.inlineCallFrame));
+ setReplacement(getMyScopeLoadElimination());
break;
// Handle nodes that are conditionally pure: these are pure, and can
// be CSE'd, so long as the prediction is the one we want.
- case ValueAdd:
case CompareLess:
case CompareLessEq:
case CompareGreater:
setReplacement(globalVarLoadElimination(node->registerPointer()));
break;
- case GetScopedVar: {
+ case GetClosureVar: {
if (cseMode == StoreElimination)
break;
setReplacement(scopedVarLoadElimination(node->child1().node(), node->varNumber()));
break;
}
- case GlobalVarWatchpoint:
+ case VarInjectionWatchpoint:
if (cseMode == StoreElimination)
break;
- if (globalVarWatchpointElimination(node->registerPointer()))
+ if (varInjectionWatchpointElimination())
eliminate();
break;
case PutGlobalVar:
- case PutGlobalVarCheck:
if (cseMode == NormalCSE)
break;
eliminate(globalVarStoreElimination(node->registerPointer()));
break;
- case PutScopedVar: {
+ case PutClosureVar: {
if (cseMode == NormalCSE)
break;
eliminate(scopedVarStoreElimination(node->child1().node(), node->child2().node(), node->varNumber()));
if (cseMode == StoreElimination)
break;
if (m_graph.byValIsPure(node))
- setReplacement(getByValLoadElimination(node->child1().node(), node->child2().node()));
+ setReplacement(getByValLoadElimination(node->child1().node(), node->child2().node(), node->arrayMode()));
break;
-
+
+ case PutByValDirect:
case PutByVal: {
if (cseMode == StoreElimination)
break;
Edge child1 = m_graph.varArgChild(node, 0);
Edge child2 = m_graph.varArgChild(node, 1);
if (node->arrayMode().canCSEStorage()) {
- Node* replacement = getByValLoadElimination(child1.node(), child2.node());
+ Node* replacement = getByValLoadElimination(child1.node(), child2.node(), node->arrayMode());
if (!replacement)
break;
node->setOp(PutByValAlias);
}
case CheckStructure:
- case ForwardCheckStructure:
if (cseMode == StoreElimination)
break;
if (checkStructureElimination(node->structureSet(), node->child1().node()))
break;
case StructureTransitionWatchpoint:
- case ForwardStructureTransitionWatchpoint:
if (cseMode == StoreElimination)
break;
if (structureTransitionWatchpointElimination(node->structure(), node->child1().node()))
setReplacement(getIndexedPropertyStorageLoadElimination(node->child1().node(), node->arrayMode()));
break;
}
+
+ case GetTypedArrayByteOffset: {
+ if (cseMode == StoreElimination)
+ break;
+ setReplacement(getTypedArrayByteOffsetLoadElimination(node->child1().node()));
+ break;
+ }
case GetButterfly:
if (cseMode == StoreElimination)
case GetByOffset:
if (cseMode == StoreElimination)
break;
- setReplacement(getByOffsetLoadElimination(m_graph.m_storageAccessData[node->storageAccessDataIndex()].identifierNumber, node->child1().node()));
+ setReplacement(getByOffsetLoadElimination(m_graph.m_storageAccessData[node->storageAccessDataIndex()].identifierNumber, node->child2().node()));
+ break;
+
+ case MultiGetByOffset:
+ if (cseMode == StoreElimination)
+ break;
+ setReplacement(getByOffsetLoadElimination(node->multiGetByOffsetData().identifierNumber, node->child1().node()));
break;
case PutByOffset:
eliminate(putByOffsetStoreElimination(m_graph.m_storageAccessData[node->storageAccessDataIndex()].identifierNumber, node->child1().node()));
break;
+ case InvalidationPoint:
+ if (invalidationPointElimination())
+ eliminate();
+ break;
+
case Phantom:
// FIXME: we ought to remove Phantom's that have no children.
-
+ // NB. It would be incorrect to do this for HardPhantom. In fact, the whole point
+ // of HardPhantom is that we *don't* do this for HardPhantoms, since they signify
+ // a more strict kind of liveness than the Phantom bytecode liveness.
eliminateIrrelevantPhantomChildren(node);
break;
}
m_lastSeen[node->op()] = m_indexInBlock;
-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLogF("\n");
-#endif
}
void performBlockCSE(BasicBlock* block)
for (unsigned i = 0; i < LastNodeType; ++i)
m_lastSeen[i] = UINT_MAX;
- // All Phis need to already be marked as relevant to OSR, and have their
- // replacements cleared, so we don't get confused while doing substitutions on
- // GetLocal's.
- for (unsigned i = 0; i < block->phis.size(); ++i) {
- ASSERT(block->phis[i]->flags() & NodeRelevantToOSR);
- block->phis[i]->replacement = 0;
- }
-
- // Make all of my SetLocal and GetLocal nodes relevant to OSR, and do some other
- // necessary bookkeeping.
- for (unsigned i = 0; i < block->size(); ++i) {
- Node* node = block->at(i);
-
- node->replacement = 0;
-
- switch (node->op()) {
- case SetLocal:
- case GetLocal: // FIXME: The GetLocal case is only necessary until we do https://bugs.webkit.org/show_bug.cgi?id=106707.
- node->mergeFlags(NodeRelevantToOSR);
- break;
- default:
- node->clearFlags(NodeRelevantToOSR);
- break;
- }
- }
-
for (m_indexInBlock = 0; m_indexInBlock < block->size(); ++m_indexInBlock) {
m_currentNode = block->at(m_indexInBlock);
performNodeCSE(m_currentNode);
if (!ASSERT_DISABLED && cseMode == StoreElimination) {
// Nobody should have replacements set.
for (unsigned i = 0; i < block->size(); ++i)
- ASSERT(!block->at(i)->replacement);
+ ASSERT(!block->at(i)->misc.replacement);
}
}
BasicBlock* m_currentBlock;
Node* m_currentNode;
unsigned m_indexInBlock;
- FixedArray<unsigned, LastNodeType> m_lastSeen;
+ std::array<unsigned, LastNodeType> m_lastSeen;
bool m_changed; // Only tracks changes that have a substantive effect on other optimizations.
};
bool performCSE(Graph& graph)
{
SamplingRegion samplingRegion("DFG CSE Phase");
- return runPhase<CSEPhase<NormalCSE> >(graph);
+ return runPhase<CSEPhase<NormalCSE>>(graph);
}
bool performStoreElimination(Graph& graph)
{
SamplingRegion samplingRegion("DFG Store Elimination Phase");
- return runPhase<CSEPhase<StoreElimination> >(graph);
+ return runPhase<CSEPhase<StoreElimination>>(graph);
}
} } // namespace JSC::DFG