<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[171052] branches/ftlopt/Source/JavaScriptCore</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/171052">171052</a></dd>
<dt>Author</dt> <dd>fpizlo@apple.com</dd>
<dt>Date</dt> <dd>2014-07-13 10:26:35 -0700 (Sun, 13 Jul 2014)</dd>
</dl>
<h3>Log Message</h3>
<pre>Unreviewed, revert unintended change in <a href="http://trac.webkit.org/projects/webkit/changeset/171051">r171051</a>.
* dfg/DFGCSEPhase.cpp:</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#branchesftloptSourceJavaScriptCoreChangeLog">branches/ftlopt/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#branchesftloptSourceJavaScriptCoredfgDFGCSEPhasecpp">branches/ftlopt/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="branchesftloptSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: branches/ftlopt/Source/JavaScriptCore/ChangeLog (171051 => 171052)</h4>
<pre class="diff"><span>
<span class="info">--- branches/ftlopt/Source/JavaScriptCore/ChangeLog        2014-07-13 17:18:00 UTC (rev 171051)
+++ branches/ftlopt/Source/JavaScriptCore/ChangeLog        2014-07-13 17:26:35 UTC (rev 171052)
</span><span class="lines">@@ -1,3 +1,9 @@
</span><ins>+2014-07-13 Filip Pizlo <fpizlo@apple.com>
+
+ Unreviewed, revert unintended change in r171051.
+
+ * dfg/DFGCSEPhase.cpp:
+
</ins><span class="cx"> 2014-07-08 Filip Pizlo <fpizlo@apple.com>
</span><span class="cx">
</span><span class="cx"> [ftlopt] Move Flush(SetLocal) store elimination to StrengthReductionPhase
</span></span></pre></div>
<a id="branchesftloptSourceJavaScriptCoredfgDFGCSEPhasecpp"></a>
<div class="modfile"><h4>Modified: branches/ftlopt/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp (171051 => 171052)</h4>
<pre class="diff"><span>
<span class="info">--- branches/ftlopt/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp        2014-07-13 17:18:00 UTC (rev 171051)
+++ branches/ftlopt/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp        2014-07-13 17:26:35 UTC (rev 171052)
</span><span class="lines">@@ -29,7 +29,6 @@
</span><span class="cx"> #if ENABLE(DFG_JIT)
</span><span class="cx">
</span><span class="cx"> #include "DFGAbstractHeap.h"
</span><del>-#include "DFGClobberSet.h"
</del><span class="cx"> #include "DFGClobberize.h"
</span><span class="cx"> #include "DFGEdgeUsesStructure.h"
</span><span class="cx"> #include "DFGGraph.h"
</span><span class="lines">@@ -40,479 +39,984 @@
</span><span class="cx">
</span><span class="cx"> namespace JSC { namespace DFG {
</span><span class="cx">
</span><del>-// This file contains two CSE implementations: local (LCSE) and global (GCSE). LCSE typically runs
-// when we're in DFG mode, i.e. we want to compile quickly. LCSE contains a lot of optimizations
-// for compile time. GCSE, on the other hand, is fairly straight-forward. It will find more
-// optimization opportunities by virtue of being global.
-
-namespace {
-
-const bool verbose = false;
-
-class ClobberFilter {
</del><ins>+class CSEPhase : public Phase {
</ins><span class="cx"> public:
</span><del>- ClobberFilter(AbstractHeap heap)
- : m_heap(heap)
</del><ins>+ CSEPhase(Graph& graph)
+ : Phase(graph, "common subexpression elimination")
</ins><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx">
</span><del>- bool operator()(const ImpureMap::KeyValuePairType& pair)
</del><ins>+ bool run()
</ins><span class="cx"> {
</span><del>- return m_heap.overlaps(pair.key.heap());
</del><ins>+ ASSERT(m_graph.m_fixpointState != BeforeFixpoint);
+
+ m_changed = false;
+
+ m_graph.clearReplacements();
+
+ 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;
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> private:
</span><del>- AbstractHeap m_heap;
-};
-
-inline void clobber(ImpureMap& map, AbstractHeap heap)
-{
- ClobberFilter filter(heap);
- map.removeIf(filter);
-}
-
-class LCSEPhase : public Phase {
-public:
- LCSEPhase(Graph& graph)
- : Phase(graph, "local common subexpression elimination")
- , m_smallBlock(graph)
- , m_largeBlock(graph)
</del><ins>+
+ unsigned endIndexForPureCSE()
</ins><span class="cx"> {
</span><ins>+ unsigned result = m_lastSeen[m_currentNode->op()];
+ if (result == UINT_MAX)
+ result = 0;
+ else
+ result++;
+ ASSERT(result <= m_indexInBlock);
+ return result;
</ins><span class="cx"> }
</span><del>-
- bool run()
</del><ins>+
+ Node* pureCSE(Node* node)
</ins><span class="cx"> {
</span><del>- ASSERT(m_graph.m_fixpointState == FixpointNotConverged);
- ASSERT(m_graph.m_form == ThreadedCPS || m_graph.m_form == LoadStore);
</del><ins>+ Edge child1 = node->child1().sanitized();
+ Edge child2 = node->child2().sanitized();
+ Edge child3 = node->child3().sanitized();
</ins><span class="cx">
</span><del>- bool changed = false;
-
- m_graph.clearReplacements();
-
- for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) {
- BasicBlock* block = m_graph.block(blockIndex);
- if (!block)
</del><ins>+ for (unsigned i = endIndexForPureCSE(); i--;) {
+ Node* otherNode = m_currentBlock->at(i);
+ if (otherNode == child1 || otherNode == child2 || otherNode == child3)
+ break;
+
+ if (node->op() != otherNode->op())
</ins><span class="cx"> continue;
</span><span class="cx">
</span><del>- if (block->size() <= SmallMaps::capacity)
- changed |= m_smallBlock.run(block);
- else
- changed |= m_largeBlock.run(block);
</del><ins>+ Edge otherChild = otherNode->child1().sanitized();
+ if (!otherChild)
+ return otherNode;
+ if (otherChild != child1)
+ continue;
+
+ otherChild = otherNode->child2().sanitized();
+ if (!otherChild)
+ return otherNode;
+ if (otherChild != child2)
+ continue;
+
+ otherChild = otherNode->child3().sanitized();
+ if (!otherChild)
+ return otherNode;
+ if (otherChild != child3)
+ continue;
+
+ return otherNode;
</ins><span class="cx"> }
</span><del>-
- return changed;
</del><ins>+ return 0;
</ins><span class="cx"> }
</span><span class="cx">
</span><del>-private:
- class SmallMaps {
- public:
- static const unsigned capacity = 20;
-
- SmallMaps()
- : m_pureLength(0)
- , m_impureLength(0)
- {
</del><ins>+ Node* constantCSE(Node* node)
+ {
+ for (unsigned i = endIndexForPureCSE(); i--;) {
+ Node* otherNode = m_currentBlock->at(i);
+ if (otherNode->op() != node->op())
+ continue;
+
+ if (otherNode->constant() != node->constant())
+ continue;
+
+ return otherNode;
</ins><span class="cx"> }
</span><ins>+ return 0;
+ }
</ins><span class="cx">
</span><del>- void clear()
- {
- m_pureLength = 0;
- m_impureLength = 0;
</del><ins>+ Node* constantStoragePointerCSE(Node* node)
+ {
+ for (unsigned i = endIndexForPureCSE(); i--;) {
+ Node* otherNode = m_currentBlock->at(i);
+ if (otherNode->op() != ConstantStoragePointer)
+ continue;
+
+ if (otherNode->storagePointer() != node->storagePointer())
+ continue;
+
+ return otherNode;
</ins><span class="cx"> }
</span><ins>+ return 0;
+ }
</ins><span class="cx">
</span><del>- void write(AbstractHeap heap)
- {
- for (unsigned i = 0; i < m_impureLength; ++i) {
- if (heap.overlaps(m_impureMap[i].key.heap()))
- m_impureMap[i--] = m_impureMap[--m_impureLength];
</del><ins>+ Node* getCalleeLoadElimination()
+ {
+ for (unsigned i = m_indexInBlock; i--;) {
+ Node* node = m_currentBlock->at(i);
+ switch (node->op()) {
+ case GetCallee:
+ return node;
+ default:
+ break;
</ins><span class="cx"> }
</span><span class="cx"> }
</span><ins>+ return 0;
+ }
</ins><span class="cx">
</span><del>- Node* addPure(PureValue value, Node* node)
- {
- for (unsigned i = m_pureLength; i--;) {
- if (m_pureMap[i].key == value)
- return m_pureMap[i].value;
</del><ins>+ Node* getArrayLengthElimination(Node* array)
+ {
+ for (unsigned i = m_indexInBlock; i--;) {
+ Node* node = m_currentBlock->at(i);
+ switch (node->op()) {
+ case GetArrayLength:
+ if (node->child1() == array)
+ return node;
+ break;
+
+ case PutByValDirect:
+ case PutByVal:
+ if (!m_graph.byValIsPure(node))
+ return 0;
+ if (node->arrayMode().mayStoreToHole())
+ return 0;
+ break;
+
+ default:
+ if (m_graph.clobbersWorld(node))
+ return 0;
</ins><span class="cx"> }
</span><del>-
- ASSERT(m_pureLength < capacity);
- m_pureMap[m_pureLength++] = WTF::KeyValuePair<PureValue, Node*>(value, node);
- return nullptr;
</del><span class="cx"> }
</span><ins>+ return 0;
+ }
</ins><span class="cx">
</span><del>- Node* addImpure(HeapLocation location, Node* node)
- {
- for (unsigned i = m_impureLength; i--;) {
- if (m_impureMap[i].key == location)
- return m_impureMap[i].value;
</del><ins>+ Node* globalVarLoadElimination(WriteBarrier<Unknown>* registerPointer)
+ {
+ for (unsigned i = m_indexInBlock; i--;) {
+ Node* node = m_currentBlock->at(i);
+ switch (node->op()) {
+ case GetGlobalVar:
+ if (node->registerPointer() == registerPointer)
+ return node;
+ break;
+ case PutGlobalVar:
+ if (node->registerPointer() == registerPointer)
+ return node->child1().node();
+ break;
+ default:
+ break;
</ins><span class="cx"> }
</span><del>-
- ASSERT(m_impureLength < capacity);
- m_impureMap[m_impureLength++] = WTF::KeyValuePair<HeapLocation, Node*>(location, node);
- return nullptr;
</del><ins>+ if (m_graph.clobbersWorld(node))
+ break;
</ins><span class="cx"> }
</span><ins>+ return 0;
+ }
</ins><span class="cx">
</span><del>- private:
- WTF::KeyValuePair<PureValue, Node*> m_pureMap[capacity];
- WTF::KeyValuePair<HeapLocation, Node*> m_impureMap[capacity];
- unsigned m_pureLength;
- unsigned m_impureLength;
- };
-
- class LargeMaps {
- public:
- LargeMaps()
- {
</del><ins>+ Node* scopedVarLoadElimination(Node* registers, int varNumber)
+ {
+ for (unsigned i = m_indexInBlock; i--;) {
+ Node* node = m_currentBlock->at(i);
+ switch (node->op()) {
+ case GetClosureVar: {
+ if (node->child1() == registers && node->varNumber() == varNumber)
+ return node;
+ break;
+ }
+ case PutClosureVar: {
+ if (node->varNumber() != varNumber)
+ break;
+ if (node->child2() == registers)
+ return node->child3().node();
+ return 0;
+ }
+ case SetLocal: {
+ VariableAccessData* variableAccessData = node->variableAccessData();
+ if (variableAccessData->isCaptured()
+ && variableAccessData->local() == static_cast<VirtualRegister>(varNumber))
+ return 0;
+ break;
+ }
+ default:
+ break;
+ }
+ if (m_graph.clobbersWorld(node))
+ break;
</ins><span class="cx"> }
</span><ins>+ return 0;
+ }
</ins><span class="cx">
</span><del>- void clear()
- {
- m_pureMap.clear();
- m_impureMap.clear();
</del><ins>+ bool varInjectionWatchpointElimination()
+ {
+ for (unsigned i = m_indexInBlock; i--;) {
+ Node* node = m_currentBlock->at(i);
+ if (node->op() == VarInjectionWatchpoint)
+ return true;
+ if (m_graph.clobbersWorld(node))
+ break;
</ins><span class="cx"> }
</span><ins>+ return false;
+ }
</ins><span class="cx">
</span><del>- void write(AbstractHeap heap)
- {
- clobber(m_impureMap, heap);
</del><ins>+ Node* getByValLoadElimination(Node* child1, Node* child2, ArrayMode arrayMode)
+ {
+ for (unsigned i = m_indexInBlock; i--;) {
+ Node* node = m_currentBlock->at(i);
+ if (node == child1 || node == child2)
+ break;
+
+ switch (node->op()) {
+ case GetByVal:
+ if (!m_graph.byValIsPure(node))
+ return 0;
+ 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;
+ // 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
+ // different type than the GetByVal, then we know that they won't clobber each other.
+ // ... except of course for typed arrays, where all typed arrays clobber all other
+ // typed arrays! An Int32Array can alias a Float64Array for example, and so on.
+ return 0;
+ }
+ default:
+ if (m_graph.clobbersWorld(node))
+ return 0;
+ break;
+ }
</ins><span class="cx"> }
</span><del>-
- Node* addPure(PureValue value, Node* node)
- {
- auto result = m_pureMap.add(value, node);
- if (result.isNewEntry)
- return nullptr;
- return result.iterator->value;
</del><ins>+ return 0;
+ }
+
+ bool checkFunctionElimination(FrozenValue* function, Node* child1)
+ {
+ for (unsigned i = endIndexForPureCSE(); i--;) {
+ Node* node = m_currentBlock->at(i);
+ if (node == child1)
+ break;
+
+ if (node->op() == CheckFunction && node->child1() == child1 && node->function() == function)
+ return true;
</ins><span class="cx"> }
</span><ins>+ return false;
+ }
</ins><span class="cx">
</span><del>- Node* addImpure(HeapLocation location, Node* node)
- {
- auto result = m_impureMap.add(location, node);
- if (result.isNewEntry)
- return nullptr;
- return result.iterator->value;
</del><ins>+ bool checkExecutableElimination(ExecutableBase* executable, Node* child1)
+ {
+ for (unsigned i = endIndexForPureCSE(); i--;) {
+ Node* node = m_currentBlock->at(i);
+ if (node == child1)
+ break;
+
+ if (node->op() == CheckExecutable && node->child1() == child1 && node->executable() == executable)
+ return true;
</ins><span class="cx"> }
</span><ins>+ return false;
+ }
</ins><span class="cx">
</span><del>- private:
- HashMap<PureValue, Node*> m_pureMap;
- HashMap<HeapLocation, Node*> m_impureMap;
- };
</del><ins>+ bool checkStructureElimination(const StructureSet& structureSet, Node* child1)
+ {
+ for (unsigned i = m_indexInBlock; i--;) {
+ Node* node = m_currentBlock->at(i);
+ if (node == child1)
+ break;
</ins><span class="cx">
</span><del>- template<typename Maps>
- class BlockCSE {
- public:
- BlockCSE(Graph& graph)
- : m_graph(graph)
- {
</del><ins>+ switch (node->op()) {
+ case CheckStructure:
+ if (node->child1() == child1
+ && structureSet.isSupersetOf(node->structureSet()))
+ return true;
+ break;
+
+ case PutStructure:
+ if (node->child1() == child1
+ && structureSet.contains(node->transition()->next))
+ return true;
+ if (structureSet.contains(node->transition()->previous))
+ return false;
+ break;
+
+ 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)) {
+ // If PutByVal speculates that it's accessing an array with an
+ // integer index, then it's impossible for it to cause a structure
+ // change.
+ break;
+ }
+ return false;
+
+ case Arrayify:
+ case ArrayifyToStructure:
+ // We could check if the arrayification could affect our structures.
+ // But that seems like it would take Effort.
+ return false;
+
+ default:
+ if (m_graph.clobbersWorld(node))
+ return false;
+ break;
+ }
</ins><span class="cx"> }
</span><ins>+ return false;
+ }
</ins><span class="cx">
</span><del>- bool run(BasicBlock* block)
- {
- m_maps.clear();
- m_changed = false;
-
- for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
- m_node = block->at(nodeIndex);
- m_graph.performSubstitution(m_node);
-
- if (m_node->op() == Identity) {
- m_node->replaceWith(m_node->child1().node());
- m_changed = true;
- } else
- clobberize(m_graph, m_node, *this);
</del><ins>+ bool structureTransitionWatchpointElimination(Structure* structure, Node* child1)
+ {
+ for (unsigned i = m_indexInBlock; i--;) {
+ Node* node = m_currentBlock->at(i);
+ if (node == child1)
+ break;
+
+ switch (node->op()) {
+ case CheckStructure:
+ if (node->child1() == child1
+ && node->structureSet().isSubsetOf(StructureSet(structure)))
+ return true;
+ break;
+
+ case PutStructure:
+ ASSERT(node->transition()->previous != structure);
+ break;
+
+ 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)) {
+ // If PutByVal speculates that it's accessing an array with an
+ // integer index, then it's impossible for it to cause a structure
+ // change.
+ break;
+ }
+ return false;
+
+ case Arrayify:
+ case ArrayifyToStructure:
+ // We could check if the arrayification could affect our structures.
+ // But that seems like it would take Effort.
+ return false;
+
+ default:
+ if (m_graph.clobbersWorld(node))
+ return false;
+ break;
</ins><span class="cx"> }
</span><del>-
- return m_changed;
</del><span class="cx"> }
</span><ins>+ return false;
+ }
</ins><span class="cx">
</span><del>- void read(AbstractHeap) { }
-
- void write(AbstractHeap heap)
- {
- m_maps.write(heap);
</del><ins>+ Node* getByOffsetLoadElimination(unsigned identifierNumber, Node* base)
+ {
+ for (unsigned i = m_indexInBlock; i--;) {
+ Node* node = m_currentBlock->at(i);
+ if (node == base)
+ break;
+
+ switch (node->op()) {
+ case GetByOffset:
+ 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->child2() == base) // Must be same property storage.
+ return node->child3().node();
+ return 0;
+ }
+ break;
+
+ 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)) {
+ // If PutByVal speculates that it's accessing an array with an
+ // integer index, then it's impossible for it to cause a structure
+ // change.
+ break;
+ }
+ return 0;
+
+ default:
+ if (m_graph.clobbersWorld(node))
+ return 0;
+ break;
+ }
</ins><span class="cx"> }
</span><ins>+ return 0;
+ }
</ins><span class="cx">
</span><del>- void def(PureValue value)
- {
- Node* match = m_maps.addPure(value, m_node);
- if (!match)
- return;
</del><ins>+ Node* getGetterSetterByOffsetLoadElimination(unsigned identifierNumber, Node* base)
+ {
+ for (unsigned i = m_indexInBlock; i--;) {
+ Node* node = m_currentBlock->at(i);
+ if (node == base)
+ break;
</ins><span class="cx">
</span><del>- m_node->replaceWith(match);
- m_changed = true;
- }
-
- void def(HeapLocation location, Node* value)
- {
- Node* match = m_maps.addImpure(location, value);
- if (!match)
- return;
-
- if (m_node->op() == GetLocal) {
- // For uncaptured locals, usually the CPS rethreading phase does this. But it's OK
- // for us to mess with locals - regardless of their capturedness - so long as:
- //
- // - We dethread the graph. Any changes we make may invalidate the assumptions of
- // our CPS form, particularly if this GetLocal is linked to the variablesAtTail.
- //
- // - We don't introduce a Phantom for the child of the GetLocal. This wouldn't be
- // totally wrong but it would pessimize the code. Just because there is a
- // GetLocal doesn't mean that the child was live. Simply rerouting the all uses
- // of this GetLocal will preserve the live-at-exit information just fine.
- //
- // We accomplish the latter by just clearing the child; then the Phantom that we
- // introduce won't have children and so it will eventually just be deleted.
-
- m_node->child1() = Edge();
- m_graph.dethread();
</del><ins>+ switch (node->op()) {
+ case GetGetterSetterByOffset:
+ if (node->child2() == base
+ && m_graph.m_storageAccessData[node->storageAccessDataIndex()].identifierNumber == identifierNumber)
+ return node;
+ break;
+
+ case PutByValDirect:
+ case PutByVal:
+ case PutByValAlias:
+ if (m_graph.byValIsPure(node)) {
+ // If PutByVal speculates that it's accessing an array with an
+ // integer index, then it's impossible for it to cause a structure
+ // change.
+ break;
+ }
+ return 0;
+
+ default:
+ if (m_graph.clobbersWorld(node))
+ return 0;
+ break;
</ins><span class="cx"> }
</span><del>-
- m_node->replaceWith(match);
- m_changed = true;
</del><span class="cx"> }
</span><ins>+ return 0;
+ }
</ins><span class="cx">
</span><del>- private:
- Graph& m_graph;
-
- bool m_changed;
- Node* m_node;
-
- Maps m_maps;
- };
</del><ins>+ Node* getPropertyStorageLoadElimination(Node* child1)
+ {
+ for (unsigned i = m_indexInBlock; i--;) {
+ Node* node = m_currentBlock->at(i);
+ if (node == child1)
+ break;
</ins><span class="cx">
</span><del>- BlockCSE<SmallMaps> m_smallBlock;
- BlockCSE<LargeMaps> m_largeBlock;
-};
</del><ins>+ switch (node->op()) {
+ case GetButterfly:
+ if (node->child1() == child1)
+ return node;
+ break;
</ins><span class="cx">
</span><del>-class GCSEPhase : public Phase {
-public:
- GCSEPhase(Graph& graph)
- : Phase(graph, "global common subexpression elimination")
- {
</del><ins>+ case AllocatePropertyStorage:
+ case ReallocatePropertyStorage:
+ // If we can cheaply prove this is a change to our object's storage, we
+ // can optimize and use its result.
+ if (node->child1() == child1)
+ return node;
+ // Otherwise, we currently can't prove that this doesn't change our object's
+ // storage, so we conservatively assume that it may change the storage
+ // pointer of any object, including ours.
+ return 0;
+
+ case PutByValDirect:
+ case PutByVal:
+ case PutByValAlias:
+ if (m_graph.byValIsPure(node)) {
+ // If PutByVal speculates that it's accessing an array with an
+ // integer index, then it's impossible for it to cause a structure
+ // change.
+ break;
+ }
+ return 0;
+
+ case Arrayify:
+ case ArrayifyToStructure:
+ // We could check if the arrayification could affect our butterfly.
+ // 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;
+ }
+ }
+ return 0;
</ins><span class="cx"> }
</span><span class="cx">
</span><del>- bool run()
</del><ins>+ bool checkArrayElimination(Node* child1, ArrayMode arrayMode)
</ins><span class="cx"> {
</span><del>- ASSERT(m_graph.m_fixpointState == FixpointNotConverged);
- ASSERT(m_graph.m_form == SSA);
-
- m_graph.clearReplacements();
- m_graph.initializeNodeOwners();
- m_graph.m_dominators.computeIfNecessary(m_graph);
-
- Vector<BasicBlock*> preOrder;
- m_graph.getBlocksInPreOrder(preOrder);
-
- m_impureDataMap.resize(m_graph.numBlocks());
-
- // First figure out what gets clobbered by blocks. Node that this uses the preOrder list
- // for convenience only.
- for (unsigned i = preOrder.size(); i--;) {
- m_block = preOrder[i];
- m_impureData = &m_impureDataMap[m_block->index];
- for (unsigned nodeIndex = m_block->size(); nodeIndex--;)
- addWrites(m_graph, m_block->at(nodeIndex), m_impureData->writes);
- }
-
- // Based on my experience doing this before, what follows might have to be made iterative.
- // But it feels like, right now, this works as a single pass because we only turn a node
- // into a value or heap location after we have performed substitutions on that node's
- // children.
-
- for (unsigned i = 0; i < preOrder.size(); ++i) {
- m_block = preOrder[i];
- m_impureData = &m_impureDataMap[m_block->index];
- m_writesSoFar.clear();
-
- if (verbose)
- dataLog("Processing block ", *m_block, ":\n");
</del><ins>+ for (unsigned i = m_indexInBlock; i--;) {
+ Node* node = m_currentBlock->at(i);
+ if (node == child1)
+ break;
</ins><span class="cx">
</span><del>- for (unsigned nodeIndex = 0; nodeIndex < m_block->size(); ++nodeIndex) {
- m_node = m_block->at(nodeIndex);
- if (verbose)
- dataLog(" Looking at node ", m_node, ":\n");
</del><ins>+ switch (node->op()) {
+ case CheckArray:
+ if (node->child1() == child1 && node->arrayMode() == arrayMode)
+ return true;
+ break;
</ins><span class="cx">
</span><del>- m_graph.performSubstitution(m_node);
</del><ins>+ case Arrayify:
+ case ArrayifyToStructure:
+ // We could check if the arrayification could affect our array.
+ // But that seems like it would take Effort.
+ return false;
</ins><span class="cx">
</span><del>- if (m_node->op() == Identity) {
- m_node->replaceWith(m_node->child1().node());
- m_changed = true;
- } else
- clobberize(m_graph, m_node, *this);
</del><ins>+ default:
+ if (m_graph.clobbersWorld(node))
+ return false;
+ break;
</ins><span class="cx"> }
</span><del>-
- m_impureData->didVisit = true;
</del><span class="cx"> }
</span><del>-
- return m_changed;
</del><ins>+ return false;
</ins><span class="cx"> }
</span><span class="cx">
</span><del>- void read(AbstractHeap) { }
</del><ins>+ Node* getIndexedPropertyStorageLoadElimination(Node* child1, ArrayMode arrayMode)
+ {
+ for (unsigned i = m_indexInBlock; i--;) {
+ Node* node = m_currentBlock->at(i);
+ if (node == child1)
+ break;
+
+ switch (node->op()) {
+ case GetIndexedPropertyStorage: {
+ if (node->child1() == child1 && node->arrayMode() == arrayMode)
+ return node;
+ break;
+ }
+
+ default:
+ if (m_graph.clobbersWorld(node))
+ return 0;
+ break;
+ }
+ }
+ return 0;
+ }
</ins><span class="cx">
</span><del>- void write(AbstractHeap heap)
</del><ins>+ Node* getInternalFieldLoadElimination(NodeType op, Node* child1)
</ins><span class="cx"> {
</span><del>- clobber(m_impureData->availableAtTail, heap);
- m_writesSoFar.add(heap);
</del><ins>+ for (unsigned i = m_indexInBlock; i--;) {
+ Node* node = m_currentBlock->at(i);
+ if (node == child1)
+ break;
+
+ if (node->op() == op && node->child1() == child1)
+ return node;
+
+ if (m_graph.clobbersWorld(node))
+ return 0;
+ }
+ return 0;
</ins><span class="cx"> }
</span><span class="cx">
</span><del>- void def(PureValue value)
</del><ins>+ Node* getMyScopeLoadElimination()
</ins><span class="cx"> {
</span><del>- auto result = m_pureValues.add(value, Vector<Node*>());
- if (result.isNewEntry) {
- result.iterator->value.append(m_node);
- return;
</del><ins>+ for (unsigned i = m_indexInBlock; i--;) {
+ Node* node = m_currentBlock->at(i);
+ switch (node->op()) {
+ case CreateActivation:
+ // This may cause us to return a different scope.
+ return 0;
+ case GetMyScope:
+ return node;
+ default:
+ break;
+ }
</ins><span class="cx"> }
</span><ins>+ return 0;
+ }
+
+ Node* getLocalLoadElimination(VirtualRegister local, Node*& relevantLocalOp, bool careAboutClobbering)
+ {
+ relevantLocalOp = 0;
</ins><span class="cx">
</span><del>- for (unsigned i = result.iterator->value.size(); i--;) {
- Node* candidate = result.iterator->value[i];
- if (m_graph.m_dominators.dominates(candidate->owner, m_block)) {
- m_node->replaceWith(candidate);
- m_changed = true;
- return;
</del><ins>+ for (unsigned i = m_indexInBlock; i--;) {
+ Node* node = m_currentBlock->at(i);
+ switch (node->op()) {
+ case GetLocal:
+ if (node->local() == local) {
+ relevantLocalOp = node;
+ return node;
+ }
+ break;
+
+ case GetLocalUnlinked:
+ if (node->unlinkedLocal() == local) {
+ relevantLocalOp = node;
+ return node;
+ }
+ break;
+
+ case SetLocal:
+ if (node->local() == local) {
+ relevantLocalOp = node;
+ return node->child1().node();
+ }
+ break;
+
+ case GetClosureVar:
+ case PutClosureVar:
+ if (static_cast<VirtualRegister>(node->varNumber()) == local)
+ return 0;
+ break;
+
+ default:
+ if (careAboutClobbering && m_graph.clobbersWorld(node))
+ return 0;
+ break;
</ins><span class="cx"> }
</span><span class="cx"> }
</span><del>-
- result.iterator->value.append(m_node);
</del><ins>+ return 0;
</ins><span class="cx"> }
</span><span class="cx">
</span><del>- Node* findReplacement(HeapLocation location)
</del><ins>+ bool invalidationPointElimination()
</ins><span class="cx"> {
</span><del>- Node* match = m_impureData->availableAtTail.get(location);
- if (match) {
- if (verbose)
- dataLog(" Found local match: ", match, "\n");
- return match;
</del><ins>+ 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;
</ins><span class="cx"> }
</span><ins>+ return false;
+ }
+
+ bool setReplacement(Node* replacement)
+ {
+ if (!replacement)
+ return false;
</ins><span class="cx">
</span><del>- if (m_writesSoFar.overlaps(location.heap())) {
- if (verbose)
- dataLog(" Not looking globally because of local clobber: ", m_writesSoFar, "\n");
- return nullptr;
- }
</del><ins>+ m_currentNode->convertToPhantom();
</ins><span class="cx">
</span><del>- // This perfoms a backward search to find some possible def() that matches our heap
- // location. As soon as it finds a possible def(), it assumes that this one must be the
- // match and completes the search by halting at this block. As soon as it finds a write()
- // that overlaps the location's heap, it stops, assuming that there is no possible match.
</del><ins>+ // At this point we will eliminate all references to this node.
+ m_currentNode->replacement = replacement;
</ins><span class="cx">
</span><del>- Vector<BasicBlock*, 8> worklist;
- Vector<BasicBlock*, 8> seenList;
- BitVector seen;
</del><ins>+ m_changed = true;
</ins><span class="cx">
</span><del>- for (unsigned i = m_block->predecessors.size(); i--;) {
- BasicBlock* predecessor = m_block->predecessors[i];
- if (!seen.get(predecessor->index)) {
- worklist.append(predecessor);
- seen.set(predecessor->index);
- }
- }
</del><ins>+ return true;
+ }
+
+ void eliminate()
+ {
+ ASSERT(m_currentNode->mustGenerate());
+ m_currentNode->convertToPhantom();
</ins><span class="cx">
</span><del>- while (!worklist.isEmpty()) {
- BasicBlock* block = worklist.takeLast();
- seenList.append(block);
</del><ins>+ m_changed = true;
+ }
+
+ void eliminate(Node* node, NodeType phantomType = Phantom)
+ {
+ if (!node)
+ return;
+ ASSERT(node->mustGenerate());
+ node->setOpAndDefaultFlags(phantomType);
+
+ m_changed = true;
+ }
+
+ void performNodeCSE(Node* node)
+ {
+ m_graph.performSubstitution(node);
+
+ switch (node->op()) {
+
+ case Identity:
+ setReplacement(node->child1().node());
+ break;
</ins><span class="cx">
</span><del>- if (verbose)
- dataLog(" Searching in block ", *block, "\n");
- ImpureBlockData& data = m_impureDataMap[block->index];
</del><ins>+ // Handle the pure nodes. These nodes never have any side-effects.
+ case BitAnd:
+ case BitOr:
+ case BitXor:
+ case BitRShift:
+ case BitLShift:
+ case BitURShift:
+ case ArithAbs:
+ case ArithMin:
+ case ArithMax:
+ case ArithSqrt:
+ case ArithFRound:
+ case ArithSin:
+ case ArithCos:
+ case StringCharAt:
+ case StringCharCodeAt:
+ case IsUndefined:
+ case IsBoolean:
+ case IsNumber:
+ case IsString:
+ case IsObject:
+ case IsFunction:
+ case LogicalNot:
+ case SkipTopScope:
+ case SkipScope:
+ case GetClosureRegisters:
+ case GetScope:
+ case TypeOf:
+ case CompareEqConstant:
+ case ValueToInt32:
+ case MakeRope:
+ case DoubleRep:
+ case ValueRep:
+ case Int52Rep:
+ setReplacement(pureCSE(node));
+ break;
</ins><span class="cx">
</span><del>- if (m_graph.m_dominators.dominates(block, m_block) && block != m_block) {
- if (verbose)
- dataLog(" It strictly dominates.\n");
- DFG_ASSERT(m_graph, m_node, data.didVisit);
- DFG_ASSERT(m_graph, m_node, !match);
- match = data.availableAtTail.get(location);
- if (verbose)
- dataLog(" Availability: ", match, "\n");
- if (match) {
- // Don't examine the predecessors of a match. At this point we just want to
- // establish that other blocks on the path from here to there don't clobber
- // the location we're interested in.
- continue;
- }
</del><ins>+ case ArithAdd:
+ case ArithSub:
+ case ArithNegate:
+ case ArithMul:
+ case ArithDiv:
+ case ArithMod:
+ case UInt32ToNumber:
+ case DoubleAsInt32: {
+ Node* candidate = pureCSE(node);
+ if (!candidate)
+ break;
+ if (!subsumes(candidate->arithMode(), node->arithMode())) {
+ if (!subsumes(node->arithMode(), candidate->arithMode()))
+ break;
+ candidate->setArithMode(node->arithMode());
</ins><span class="cx"> }
</span><ins>+ setReplacement(candidate);
+ break;
+ }
</ins><span class="cx">
</span><del>- if (verbose)
- dataLog(" Dealing with write set ", data.writes, "\n");
- if (data.writes.overlaps(location.heap())) {
- if (verbose)
- dataLog(" Clobbered.\n");
- return nullptr;
</del><ins>+ case GetCallee:
+ setReplacement(getCalleeLoadElimination());
+ break;
+
+ case GetLocal: {
+ VariableAccessData* variableAccessData = node->variableAccessData();
+ if (!variableAccessData->isCaptured())
+ break;
+ Node* relevantLocalOp;
+ Node* possibleReplacement = getLocalLoadElimination(variableAccessData->local(), relevantLocalOp, variableAccessData->isCaptured());
+ if (!relevantLocalOp)
+ break;
+ if (relevantLocalOp->op() != GetLocalUnlinked
+ && relevantLocalOp->variableAccessData() != variableAccessData)
+ break;
+ Node* phi = node->child1().node();
+ if (!setReplacement(possibleReplacement))
+ break;
+
+ m_graph.dethread();
+
+ // If we replace a GetLocal with a GetLocalUnlinked, then turn the GetLocalUnlinked
+ // into a GetLocal.
+ if (relevantLocalOp->op() == GetLocalUnlinked)
+ relevantLocalOp->convertToGetLocal(variableAccessData, phi);
+
+ m_changed = true;
+ break;
+ }
+
+ case GetLocalUnlinked: {
+ Node* relevantLocalOpIgnored;
+ setReplacement(getLocalLoadElimination(node->unlinkedLocal(), relevantLocalOpIgnored, true));
+ break;
+ }
+
+ case JSConstant:
+ case DoubleConstant:
+ case Int52Constant:
+ // This is strange, but necessary. Some phases will convert nodes to constants,
+ // which may result in duplicated constants. We use CSE to clean this up.
+ setReplacement(constantCSE(node));
+ break;
+
+ case ConstantStoragePointer:
+ setReplacement(constantStoragePointerCSE(node));
+ break;
+
+ case GetArrayLength:
+ setReplacement(getArrayLengthElimination(node->child1().node()));
+ break;
+
+ case GetMyScope:
+ 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 CompareLess:
+ case CompareLessEq:
+ case CompareGreater:
+ case CompareGreaterEq:
+ case CompareEq: {
+ if (m_graph.isPredictedNumerical(node)) {
+ Node* replacement = pureCSE(node);
+ if (replacement && m_graph.isPredictedNumerical(replacement))
+ setReplacement(replacement);
</ins><span class="cx"> }
</span><ins>+ break;
+ }
</ins><span class="cx">
</span><del>- for (unsigned i = block->predecessors.size(); i--;) {
- BasicBlock* predecessor = block->predecessors[i];
- if (!seen.get(predecessor->index)) {
- worklist.append(predecessor);
- seen.set(predecessor->index);
- }
</del><ins>+ // Finally handle heap accesses. These are not quite pure, but we can still
+ // optimize them provided that some subtle conditions are met.
+ case GetGlobalVar:
+ setReplacement(globalVarLoadElimination(node->registerPointer()));
+ break;
+
+ case GetClosureVar: {
+ setReplacement(scopedVarLoadElimination(node->child1().node(), node->varNumber()));
+ break;
+ }
+
+ case VarInjectionWatchpoint:
+ if (varInjectionWatchpointElimination())
+ eliminate();
+ break;
+
+ case GetByVal:
+ if (m_graph.byValIsPure(node))
+ setReplacement(getByValLoadElimination(node->child1().node(), node->child2().node(), node->arrayMode()));
+ break;
+
+ case PutByValDirect:
+ case PutByVal: {
+ 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->arrayMode());
+ if (!replacement)
+ break;
+ node->setOp(PutByValAlias);
</ins><span class="cx"> }
</span><ins>+ break;
</ins><span class="cx"> }
</span><ins>+
+ case CheckStructure:
+ if (checkStructureElimination(node->structureSet(), node->child1().node()))
+ eliminate();
+ break;
+
+ case CheckFunction:
+ if (checkFunctionElimination(node->function(), node->child1().node()))
+ eliminate();
+ break;
+
+ case CheckExecutable:
+ if (checkExecutableElimination(node->executable(), node->child1().node()))
+ eliminate();
+ break;
+
+ case CheckArray:
+ if (checkArrayElimination(node->child1().node(), node->arrayMode()))
+ eliminate();
+ break;
+
+ case GetIndexedPropertyStorage: {
+ setReplacement(getIndexedPropertyStorageLoadElimination(node->child1().node(), node->arrayMode()));
+ break;
+ }
+
+ case GetTypedArrayByteOffset:
+ case GetGetter:
+ case GetSetter: {
+ setReplacement(getInternalFieldLoadElimination(node->op(), node->child1().node()));
+ break;
+ }
</ins><span class="cx">
</span><del>- // Cache the results for next time. We cache them both for this block and for all of our
- // predecessors, since even though we've already visited our predecessors, our predecessors
- // probably have successors other than us.
- for (BasicBlock* block : seenList)
- m_impureDataMap[block->index].availableAtTail.add(location, match);
- m_impureData->availableAtTail.add(location, match);
</del><ins>+ case GetButterfly:
+ setReplacement(getPropertyStorageLoadElimination(node->child1().node()));
+ break;
+
+ case GetByOffset:
+ setReplacement(getByOffsetLoadElimination(m_graph.m_storageAccessData[node->storageAccessDataIndex()].identifierNumber, node->child2().node()));
+ break;
+
+ case GetGetterSetterByOffset:
+ setReplacement(getGetterSetterByOffsetLoadElimination(m_graph.m_storageAccessData[node->storageAccessDataIndex()].identifierNumber, node->child2().node()));
+ break;
+
+ case MultiGetByOffset:
+ setReplacement(getByOffsetLoadElimination(node->multiGetByOffsetData().identifierNumber, node->child1().node()));
+ break;
+
+ case InvalidationPoint:
+ if (invalidationPointElimination())
+ eliminate();
+ break;
+
+ default:
+ // do nothing.
+ break;
+ }
</ins><span class="cx">
</span><del>- return match;
</del><ins>+ m_lastSeen[node->op()] = m_indexInBlock;
</ins><span class="cx"> }
</span><span class="cx">
</span><del>- void def(HeapLocation location, Node* value)
</del><ins>+ void performBlockCSE(BasicBlock* block)
</ins><span class="cx"> {
</span><del>- if (verbose)
- dataLog(" Got heap location def: ", location, " -> ", value, "\n");
</del><ins>+ if (!block)
+ return;
+ if (!block->isReachable)
+ return;
</ins><span class="cx">
</span><del>- Node* match = findReplacement(location);
</del><ins>+ m_currentBlock = block;
+ for (unsigned i = 0; i < LastNodeType; ++i)
+ m_lastSeen[i] = UINT_MAX;
</ins><span class="cx">
</span><del>- if (verbose)
- dataLog(" Got match: ", match, "\n");
-
- if (!match) {
- auto result = m_impureData->availableAtTail.add(location, value);
- ASSERT_UNUSED(result, result.isNewEntry);
- return;
</del><ins>+ for (m_indexInBlock = 0; m_indexInBlock < block->size(); ++m_indexInBlock) {
+ m_currentNode = block->at(m_indexInBlock);
+ performNodeCSE(m_currentNode);
</ins><span class="cx"> }
</span><del>-
- m_node->replaceWith(match);
- m_changed = true;
</del><span class="cx"> }
</span><span class="cx">
</span><del>- struct ImpureBlockData {
- ImpureBlockData()
- : didVisit(false)
- {
- }
-
- ClobberSet writes;
- ImpureMap availableAtTail;
- bool didVisit;
- };
-
- PureMultiMap m_pureValues;
- Vector<ImpureBlockData> m_impureDataMap;
-
- BasicBlock* m_block;
- Node* m_node;
- ImpureBlockData* m_impureData;
- ClobberSet m_writesSoFar;
-
- bool m_changed;
</del><ins>+ BasicBlock* m_currentBlock;
+ Node* m_currentNode;
+ unsigned m_indexInBlock;
+ std::array<unsigned, LastNodeType> m_lastSeen;
+ bool m_changed; // Only tracks changes that have a substantive effect on other optimizations.
</ins><span class="cx"> };
</span><span class="cx">
</span><del>-} // anonymous namespace
-
-bool performLCSE(Graph& graph)
</del><ins>+bool performCSE(Graph& graph)
</ins><span class="cx"> {
</span><del>- SamplingRegion samplingRegion("DFG LCSE Phase");
- return runPhase<LCSEPhase>(graph);
</del><ins>+ SamplingRegion samplingRegion("DFG CSE Phase");
+ return runPhase<CSEPhase>(graph);
</ins><span class="cx"> }
</span><span class="cx">
</span><del>-bool performGCSE(Graph& graph)
-{
- SamplingRegion samplingRegion("DFG GCSE Phase");
- return runPhase<GCSEPhase>(graph);
-}
-
</del><span class="cx"> } } // namespace JSC::DFG
</span><span class="cx">
</span><span class="cx"> #endif // ENABLE(DFG_JIT)
</span><ins>+
+
</ins></span></pre>
</div>
</div>
</body>
</html>