Fix style issues in DFG Phase classes
[WebKit-https.git] / Source / JavaScriptCore / dfg / DFGCSEPhase.cpp
1 /*
2  * Copyright (C) 2011 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "DFGCSEPhase.h"
28
29 #if ENABLE(DFG_JIT)
30
31 #include "DFGGraph.h"
32 #include "DFGPhase.h"
33
34 namespace JSC { namespace DFG {
35
36 class CSEPhase : public Phase {
37 public:
38     CSEPhase(Graph& graph)
39         : Phase(graph, "common subexpression elimination")
40     {
41         // Replacements are used to implement local common subexpression elimination.
42         m_replacements.resize(m_graph.size());
43         
44         for (unsigned i = 0; i < m_graph.size(); ++i)
45             m_replacements[i] = NoNode;
46         
47         for (unsigned i = 0; i < LastNodeId; ++i)
48             m_lastSeen[i] = NoNode;
49     }
50     
51     void run()
52     {
53         for (unsigned block = 0; block < m_graph.m_blocks.size(); ++block)
54             performBlockCSE(*m_graph.m_blocks[block]);
55     }
56     
57 private:
58     
59     NodeIndex canonicalize(NodeIndex nodeIndex)
60     {
61         if (nodeIndex == NoNode)
62             return NoNode;
63         
64         if (m_graph[nodeIndex].op == ValueToInt32)
65             nodeIndex = m_graph[nodeIndex].child1().index();
66         
67         return nodeIndex;
68     }
69     NodeIndex canonicalize(NodeUse nodeUse)
70     {
71         return canonicalize(nodeUse.indexUnchecked());
72     }
73     
74     // Computes where the search for a candidate for CSE should start. Don't call
75     // this directly; call startIndex() instead as it does logging in debug mode.
76     NodeIndex computeStartIndexForChildren(NodeIndex child1 = NoNode, NodeIndex child2 = NoNode, NodeIndex child3 = NoNode)
77     {
78         const unsigned limit = 300;
79         
80         NodeIndex start = m_start;
81         if (m_compileIndex - start > limit)
82             start = m_compileIndex - limit;
83         
84         ASSERT(start >= m_start);
85         
86         NodeIndex child = canonicalize(child1);
87         if (child == NoNode)
88             return start;
89         
90         if (start < child)
91             start = child;
92         
93         child = canonicalize(child2);
94         if (child == NoNode)
95             return start;
96         
97         if (start < child)
98             start = child;
99         
100         child = canonicalize(child3);
101         if (child == NoNode)
102             return start;
103         
104         if (start < child)
105             start = child;
106         
107         return start;
108     }
109     
110     NodeIndex startIndexForChildren(NodeIndex child1 = NoNode, NodeIndex child2 = NoNode, NodeIndex child3 = NoNode)
111     {
112         NodeIndex result = computeStartIndexForChildren(child1, child2, child3);
113 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
114         dataLog("  lookback %u: ", result);
115 #endif
116         return result;
117     }
118     
119     NodeIndex startIndex()
120     {
121         Node& node = m_graph[m_compileIndex];
122         return startIndexForChildren(
123             node.child1().indexUnchecked(),
124             node.child2().indexUnchecked(),
125             node.child3().indexUnchecked());
126     }
127     
128     NodeIndex endIndexForPureCSE()
129     {
130         NodeIndex result = m_lastSeen[m_graph[m_compileIndex].op & NodeIdMask];
131         if (result == NoNode)
132             result = 0;
133         else
134             result++;
135         ASSERT(result <= m_compileIndex);
136 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
137         dataLog("  limit %u: ", result);
138 #endif
139         return result;
140     }
141     
142     NodeIndex pureCSE(Node& node)
143     {
144         NodeIndex child1 = canonicalize(node.child1());
145         NodeIndex child2 = canonicalize(node.child2());
146         NodeIndex child3 = canonicalize(node.child3());
147         
148         NodeIndex start = startIndex();
149         for (NodeIndex index = endIndexForPureCSE(); index-- > start;) {
150             Node& otherNode = m_graph[index];
151             if (node.op != otherNode.op)
152                 continue;
153             
154             if (node.arithNodeFlagsForCompare() != otherNode.arithNodeFlagsForCompare())
155                 continue;
156             
157             NodeIndex otherChild = canonicalize(otherNode.child1());
158             if (otherChild == NoNode)
159                 return index;
160             if (otherChild != child1)
161                 continue;
162             
163             otherChild = canonicalize(otherNode.child2());
164             if (otherChild == NoNode)
165                 return index;
166             if (otherChild != child2)
167                 continue;
168             
169             otherChild = canonicalize(otherNode.child3());
170             if (otherChild == NoNode)
171                 return index;
172             if (otherChild != child3)
173                 continue;
174             
175             return index;
176         }
177         return NoNode;
178     }
179     
180     bool isPredictedNumerical(Node& node)
181     {
182         PredictedType left = m_graph[node.child1()].prediction();
183         PredictedType right = m_graph[node.child2()].prediction();
184         return isNumberPrediction(left) && isNumberPrediction(right);
185     }
186     
187     bool logicalNotIsPure(Node& node)
188     {
189         PredictedType prediction = m_graph[node.child1()].prediction();
190         return isBooleanPrediction(prediction) || !prediction;
191     }
192     
193     bool byValIsPure(Node& node)
194     {
195         return m_graph[node.child2()].shouldSpeculateInteger()
196             && ((node.op == PutByVal || node.op == PutByValAlias)
197                 ? isActionableMutableArrayPrediction(m_graph[node.child1()].prediction())
198                 : isActionableArrayPrediction(m_graph[node.child1()].prediction()));
199     }
200     
201     bool clobbersWorld(NodeIndex nodeIndex)
202     {
203         Node& node = m_graph[nodeIndex];
204         if (node.op & NodeClobbersWorld)
205             return true;
206         if (!(node.op & NodeMightClobber))
207             return false;
208         switch (node.op) {
209         case ValueAdd:
210         case CompareLess:
211         case CompareLessEq:
212         case CompareGreater:
213         case CompareGreaterEq:
214         case CompareEq:
215             return !isPredictedNumerical(node);
216         case LogicalNot:
217             return !logicalNotIsPure(node);
218         case GetByVal:
219             return !byValIsPure(node);
220         default:
221             ASSERT_NOT_REACHED();
222             return true; // If by some oddity we hit this case in release build it's safer to have CSE assume the worst.
223         }
224     }
225     
226     NodeIndex impureCSE(Node& node)
227     {
228         NodeIndex child1 = canonicalize(node.child1());
229         NodeIndex child2 = canonicalize(node.child2());
230         NodeIndex child3 = canonicalize(node.child3());
231         
232         NodeIndex start = startIndex();
233         for (NodeIndex index = m_compileIndex; index-- > start;) {
234             Node& otherNode = m_graph[index];
235             if (node.op == otherNode.op
236                 && node.arithNodeFlagsForCompare() == otherNode.arithNodeFlagsForCompare()) {
237                 NodeIndex otherChild = canonicalize(otherNode.child1());
238                 if (otherChild == NoNode)
239                     return index;
240                 if (otherChild == child1) {
241                     otherChild = canonicalize(otherNode.child2());
242                     if (otherChild == NoNode)
243                         return index;
244                     if (otherChild == child2) {
245                         otherChild = canonicalize(otherNode.child3());
246                         if (otherChild == NoNode)
247                             return index;
248                         if (otherChild == child3)
249                             return index;
250                     }
251                 }
252             }
253             if (clobbersWorld(index))
254                 break;
255         }
256         return NoNode;
257     }
258     
259     NodeIndex globalVarLoadElimination(unsigned varNumber, JSGlobalObject* globalObject)
260     {
261         NodeIndex start = startIndexForChildren();
262         for (NodeIndex index = m_compileIndex; index-- > start;) {
263             Node& node = m_graph[index];
264             switch (node.op) {
265             case GetGlobalVar:
266                 if (node.varNumber() == varNumber && codeBlock()->globalObjectFor(node.codeOrigin) == globalObject)
267                     return index;
268                 break;
269             case PutGlobalVar:
270                 if (node.varNumber() == varNumber && codeBlock()->globalObjectFor(node.codeOrigin) == globalObject)
271                     return node.child1().index();
272                 break;
273             default:
274                 break;
275             }
276             if (clobbersWorld(index))
277                 break;
278         }
279         return NoNode;
280     }
281     
282     NodeIndex getByValLoadElimination(NodeIndex child1, NodeIndex child2)
283     {
284         NodeIndex start = startIndexForChildren(child1, child2);
285         for (NodeIndex index = m_compileIndex; index-- > start;) {
286             Node& node = m_graph[index];
287             switch (node.op) {
288             case GetByVal:
289                 if (!byValIsPure(node))
290                     return NoNode;
291                 if (node.child1() == child1 && canonicalize(node.child2()) == canonicalize(child2))
292                     return index;
293                 break;
294             case PutByVal:
295             case PutByValAlias:
296                 if (!byValIsPure(node))
297                     return NoNode;
298                 if (node.child1() == child1 && canonicalize(node.child2()) == canonicalize(child2))
299                     return node.child3().index();
300                 // We must assume that the PutByVal will clobber the location we're getting from.
301                 // FIXME: We can do better; if we know that the PutByVal is accessing an array of a
302                 // different type than the GetByVal, then we know that they won't clobber each other.
303                 return NoNode;
304             case PutStructure:
305             case PutByOffset:
306                 // GetByVal currently always speculates that it's accessing an
307                 // array with an integer index, which means that it's impossible
308                 // for a structure change or a put to property storage to affect
309                 // the GetByVal.
310                 break;
311             case ArrayPush:
312                 // A push cannot affect previously existing elements in the array.
313                 break;
314             default:
315                 if (clobbersWorld(index))
316                     return NoNode;
317                 break;
318             }
319         }
320         return NoNode;
321     }
322
323     bool checkFunctionElimination(JSFunction* function, NodeIndex child1)
324     {
325         NodeIndex start = startIndexForChildren(child1);
326         for (NodeIndex index = endIndexForPureCSE(); index-- > start;) {
327             Node& node = m_graph[index];
328             if (node.op == CheckFunction && node.child1() == child1 && node.function() == function)
329                 return true;
330         }
331         return false;
332     }
333
334     bool checkStructureLoadElimination(const StructureSet& structureSet, NodeIndex child1)
335     {
336         NodeIndex start = startIndexForChildren(child1);
337         for (NodeIndex index = m_compileIndex; index-- > start;) {
338             Node& node = m_graph[index];
339             switch (node.op) {
340             case CheckStructure:
341                 if (node.child1() == child1
342                     && structureSet.isSupersetOf(node.structureSet()))
343                     return true;
344                 break;
345                 
346             case PutStructure:
347                 if (node.child1() == child1
348                     && structureSet.contains(node.structureTransitionData().newStructure))
349                     return true;
350                 if (structureSet.contains(node.structureTransitionData().previousStructure))
351                     return false;
352                 break;
353                 
354             case PutByOffset:
355                 // Setting a property cannot change the structure.
356                 break;
357                 
358             case PutByVal:
359             case PutByValAlias:
360                 if (byValIsPure(node)) {
361                     // If PutByVal speculates that it's accessing an array with an
362                     // integer index, then it's impossible for it to cause a structure
363                     // change.
364                     break;
365                 }
366                 return false;
367                 
368             default:
369                 if (clobbersWorld(index))
370                     return false;
371                 break;
372             }
373         }
374         return false;
375     }
376     
377     NodeIndex getByOffsetLoadElimination(unsigned identifierNumber, NodeIndex child1)
378     {
379         NodeIndex start = startIndexForChildren(child1);
380         for (NodeIndex index = m_compileIndex; index-- > start;) {
381             Node& node = m_graph[index];
382             switch (node.op) {
383             case GetByOffset:
384                 if (node.child1() == child1
385                     && m_graph.m_storageAccessData[node.storageAccessDataIndex()].identifierNumber == identifierNumber)
386                     return index;
387                 break;
388                 
389             case PutByOffset:
390                 if (m_graph.m_storageAccessData[node.storageAccessDataIndex()].identifierNumber == identifierNumber) {
391                     if (node.child2() == child1)
392                         return node.child3().index();
393                     return NoNode;
394                 }
395                 break;
396                 
397             case PutStructure:
398                 // Changing the structure cannot change the outcome of a property get.
399                 break;
400                 
401             case PutByVal:
402             case PutByValAlias:
403                 if (byValIsPure(node)) {
404                     // If PutByVal speculates that it's accessing an array with an
405                     // integer index, then it's impossible for it to cause a structure
406                     // change.
407                     break;
408                 }
409                 return NoNode;
410                 
411             default:
412                 if (clobbersWorld(index))
413                     return NoNode;
414                 break;
415             }
416         }
417         return NoNode;
418     }
419     
420     NodeIndex getPropertyStorageLoadElimination(NodeIndex child1)
421     {
422         NodeIndex start = startIndexForChildren(child1);
423         for (NodeIndex index = m_compileIndex; index-- > start;) {
424             Node& node = m_graph[index];
425             switch (node.op) {
426             case GetPropertyStorage:
427                 if (node.child1() == child1)
428                     return index;
429                 break;
430                 
431             case PutByOffset:
432             case PutStructure:
433                 // Changing the structure or putting to the storage cannot
434                 // change the property storage pointer.
435                 break;
436                 
437             case PutByVal:
438             case PutByValAlias:
439                 if (byValIsPure(node)) {
440                     // If PutByVal speculates that it's accessing an array with an
441                     // integer index, then it's impossible for it to cause a structure
442                     // change.
443                     break;
444                 }
445                 return NoNode;
446                 
447             default:
448                 if (clobbersWorld(index))
449                     return NoNode;
450                 break;
451             }
452         }
453         return NoNode;
454     }
455
456     NodeIndex getIndexedPropertyStorageLoadElimination(NodeIndex child1, bool hasIntegerIndexPrediction)
457     {
458         NodeIndex start = startIndexForChildren(child1);
459         for (NodeIndex index = m_compileIndex; index-- > start;) {
460             Node& node = m_graph[index];
461             switch (node.op) {
462             case GetIndexedPropertyStorage: {
463                 PredictedType basePrediction = m_graph[node.child2()].prediction();
464                 bool nodeHasIntegerIndexPrediction = !(!(basePrediction & PredictInt32) && basePrediction);
465                 if (node.child1() == child1 && hasIntegerIndexPrediction == nodeHasIntegerIndexPrediction)
466                     return index;
467                 break;
468             }
469
470             case PutByOffset:
471             case PutStructure:
472                 // Changing the structure or putting to the storage cannot
473                 // change the property storage pointer.
474                 break;
475                 
476             case PutByValAlias:
477                 // PutByValAlias can't change the indexed storage pointer
478                 break;
479                 
480             case PutByVal:
481                 if (isFixedIndexedStorageObjectPrediction(m_graph[node.child1()].prediction()) && byValIsPure(node))
482                     break;
483                 return NoNode;
484
485             default:
486                 if (clobbersWorld(index))
487                     return NoNode;
488                 break;
489             }
490         }
491         return NoNode;
492     }
493     
494     NodeIndex getScopeChainLoadElimination(unsigned depth)
495     {
496         NodeIndex start = startIndexForChildren();
497         for (NodeIndex index = endIndexForPureCSE(); index-- > start;) {
498             Node& node = m_graph[index];
499             if (node.op == GetScopeChain
500                 && node.scopeChainDepth() == depth)
501                 return index;
502         }
503         return NoNode;
504     }
505     
506     void performSubstitution(NodeUse& child, bool addRef = true)
507     {
508         // Check if this operand is actually unused.
509         if (!child)
510             return;
511         
512         // Check if there is any replacement.
513         NodeIndex replacement = m_replacements[child.index()];
514         if (replacement == NoNode)
515             return;
516         
517         child.setIndex(replacement);
518         
519         // There is definitely a replacement. Assert that the replacement does not
520         // have a replacement.
521         ASSERT(m_replacements[child.index()] == NoNode);
522         
523         if (addRef)
524             m_graph[child].ref();
525     }
526     
527     void setReplacement(NodeIndex replacement)
528     {
529         if (replacement == NoNode)
530             return;
531         
532         // Be safe. Don't try to perform replacements if the predictions don't
533         // agree.
534         if (m_graph[m_compileIndex].prediction() != m_graph[replacement].prediction())
535             return;
536         
537 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
538         dataLog("   Replacing @%u -> @%u", m_compileIndex, replacement);
539 #endif
540         
541         Node& node = m_graph[m_compileIndex];
542         node.op = Phantom;
543         node.setRefCount(1);
544         
545         // At this point we will eliminate all references to this node.
546         m_replacements[m_compileIndex] = replacement;
547     }
548     
549     void eliminate()
550     {
551 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
552         dataLog("   Eliminating @%u", m_compileIndex);
553 #endif
554         
555         Node& node = m_graph[m_compileIndex];
556         ASSERT(node.refCount() == 1);
557         ASSERT(node.mustGenerate());
558         node.op = Phantom;
559     }
560     
561     void performNodeCSE(Node& node)
562     {
563         bool shouldGenerate = node.shouldGenerate();
564
565         if (node.op & NodeHasVarArgs) {
566             for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); childIdx++)
567                 performSubstitution(m_graph.m_varArgChildren[childIdx], shouldGenerate);
568         } else {
569             performSubstitution(node.children.child1(), shouldGenerate);
570             performSubstitution(node.children.child2(), shouldGenerate);
571             performSubstitution(node.children.child3(), shouldGenerate);
572         }
573         
574         if (!shouldGenerate)
575             return;
576         
577 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
578         dataLog("   %s @%u: ", Graph::opName(m_graph[m_compileIndex].op), m_compileIndex);
579 #endif
580         
581         // NOTE: there are some nodes that we deliberately don't CSE even though we
582         // probably could, like StrCat and ToPrimitive. That's because there is no
583         // evidence that doing CSE on these nodes would result in a performance
584         // progression. Hence considering these nodes in CSE would just mean that this
585         // code does more work with no win. Of course, we may want to reconsider this,
586         // since StrCat is trivially CSE-able. It's not trivially doable for
587         // ToPrimitive, but we could change that with some speculations if we really
588         // needed to.
589         
590         switch (node.op) {
591         
592         // Handle the pure nodes. These nodes never have any side-effects.
593         case BitAnd:
594         case BitOr:
595         case BitXor:
596         case BitRShift:
597         case BitLShift:
598         case BitURShift:
599         case ArithAdd:
600         case ArithSub:
601         case ArithMul:
602         case ArithMod:
603         case ArithDiv:
604         case ArithAbs:
605         case ArithMin:
606         case ArithMax:
607         case ArithSqrt:
608         case GetByteArrayLength:
609         case GetInt8ArrayLength:
610         case GetInt16ArrayLength:
611         case GetInt32ArrayLength:
612         case GetUint8ArrayLength:
613         case GetUint8ClampedArrayLength:
614         case GetUint16ArrayLength:
615         case GetUint32ArrayLength:
616         case GetFloat32ArrayLength:
617         case GetFloat64ArrayLength:
618         case GetCallee:
619         case GetStringLength:
620         case StringCharAt:
621         case StringCharCodeAt:
622             setReplacement(pureCSE(node));
623             break;
624             
625         case GetArrayLength:
626             setReplacement(impureCSE(node));
627             break;
628             
629         case GetScopeChain:
630             setReplacement(getScopeChainLoadElimination(node.scopeChainDepth()));
631             break;
632
633         // Handle nodes that are conditionally pure: these are pure, and can
634         // be CSE'd, so long as the prediction is the one we want.
635         case ValueAdd:
636         case CompareLess:
637         case CompareLessEq:
638         case CompareGreater:
639         case CompareGreaterEq:
640         case CompareEq: {
641             if (isPredictedNumerical(node)) {
642                 NodeIndex replacementIndex = pureCSE(node);
643                 if (replacementIndex != NoNode && isPredictedNumerical(m_graph[replacementIndex]))
644                     setReplacement(replacementIndex);
645             }
646             break;
647         }
648             
649         case LogicalNot: {
650             if (logicalNotIsPure(node)) {
651                 NodeIndex replacementIndex = pureCSE(node);
652                 if (replacementIndex != NoNode && logicalNotIsPure(m_graph[replacementIndex]))
653                     setReplacement(replacementIndex);
654             }
655             break;
656         }
657             
658         // Finally handle heap accesses. These are not quite pure, but we can still
659         // optimize them provided that some subtle conditions are met.
660         case GetGlobalVar:
661             setReplacement(globalVarLoadElimination(node.varNumber(), codeBlock()->globalObjectFor(node.codeOrigin)));
662             break;
663             
664         case GetByVal:
665             if (byValIsPure(node))
666                 setReplacement(getByValLoadElimination(node.child1().index(), node.child2().index()));
667             break;
668             
669         case PutByVal:
670             if (byValIsPure(node) && getByValLoadElimination(node.child1().index(), node.child2().index()) != NoNode)
671                 node.op = PutByValAlias;
672             break;
673             
674         case CheckStructure:
675             if (checkStructureLoadElimination(node.structureSet(), node.child1().index()))
676                 eliminate();
677             break;
678
679         case CheckFunction:
680             if (checkFunctionElimination(node.function(), node.child1().index()))
681                 eliminate();
682             break;
683                 
684         case GetIndexedPropertyStorage: {
685             PredictedType basePrediction = m_graph[node.child2()].prediction();
686             bool nodeHasIntegerIndexPrediction = !(!(basePrediction & PredictInt32) && basePrediction);
687             setReplacement(getIndexedPropertyStorageLoadElimination(node.child1().index(), nodeHasIntegerIndexPrediction));
688             break;
689         }
690
691         case GetPropertyStorage:
692             setReplacement(getPropertyStorageLoadElimination(node.child1().index()));
693             break;
694
695         case GetByOffset:
696             setReplacement(getByOffsetLoadElimination(m_graph.m_storageAccessData[node.storageAccessDataIndex()].identifierNumber, node.child1().index()));
697             break;
698             
699         default:
700             // do nothing.
701             break;
702         }
703         
704         m_lastSeen[node.op & NodeIdMask] = m_compileIndex;
705 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
706         dataLog("\n");
707 #endif
708     }
709     
710     void performBlockCSE(BasicBlock& block)
711     {
712         m_start = block.begin;
713         NodeIndex end = block.end;
714         for (m_compileIndex = m_start; m_compileIndex < end; ++m_compileIndex)
715             performNodeCSE(m_graph[m_compileIndex]);
716     }
717     
718     NodeIndex m_start;
719     NodeIndex m_compileIndex;
720     Vector<NodeIndex, 16> m_replacements;
721     FixedArray<NodeIndex, LastNodeId> m_lastSeen;
722 };
723
724 void performCSE(Graph& graph)
725 {
726     runPhase<CSEPhase>(graph);
727 }
728
729 } } // namespace JSC::DFG
730
731 #endif // ENABLE(DFG_JIT)
732
733