DFG SSA stack accesses shouldn't speak of VariableAccessDatas
[WebKit-https.git] / Source / JavaScriptCore / dfg / DFGConstantFoldingPhase.cpp
1 /*
2  * Copyright (C) 2012, 2013, 2014 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 "DFGConstantFoldingPhase.h"
28
29 #if ENABLE(DFG_JIT)
30
31 #include "DFGAbstractInterpreterInlines.h"
32 #include "DFGBasicBlock.h"
33 #include "DFGGraph.h"
34 #include "DFGInPlaceAbstractState.h"
35 #include "DFGInsertionSet.h"
36 #include "DFGPhase.h"
37 #include "GetByIdStatus.h"
38 #include "JSCInlines.h"
39 #include "PutByIdStatus.h"
40
41 namespace JSC { namespace DFG {
42
43 class ConstantFoldingPhase : public Phase {
44 public:
45     ConstantFoldingPhase(Graph& graph)
46         : Phase(graph, "constant folding")
47         , m_state(graph)
48         , m_interpreter(graph, m_state)
49         , m_insertionSet(graph)
50     {
51     }
52     
53     bool run()
54     {
55         bool changed = false;
56         
57         for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) {
58             BasicBlock* block = m_graph.block(blockIndex);
59             if (!block)
60                 continue;
61             if (block->cfaFoundConstants)
62                 changed |= foldConstants(block);
63         }
64         
65         if (changed && m_graph.m_form == SSA) {
66             // It's now possible that we have Upsilons pointed at JSConstants. Fix that.
67             for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) {
68                 BasicBlock* block = m_graph.block(blockIndex);
69                 if (!block)
70                     continue;
71                 fixUpsilons(block);
72             }
73         }
74          
75         return changed;
76     }
77
78 private:
79     bool foldConstants(BasicBlock* block)
80     {
81         bool changed = false;
82         m_state.beginBasicBlock(block);
83         for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
84             if (!m_state.isValid())
85                 break;
86             
87             Node* node = block->at(indexInBlock);
88
89             bool alreadyHandled = false;
90             bool eliminated = false;
91                     
92             switch (node->op()) {
93             case BooleanToNumber: {
94                 if (node->child1().useKind() == UntypedUse
95                     && !m_interpreter.needsTypeCheck(node->child1(), SpecBoolean))
96                     node->child1().setUseKind(BooleanUse);
97                 break;
98             }
99                 
100             case CheckArgumentsNotCreated: {
101                 if (!isEmptySpeculation(
102                         m_state.variables().operand(
103                             m_graph.argumentsRegisterFor(node->origin.semantic)).m_type))
104                     break;
105                 node->convertToPhantom();
106                 eliminated = true;
107                 break;
108             }
109                     
110             case CheckStructure:
111             case ArrayifyToStructure: {
112                 AbstractValue& value = m_state.forNode(node->child1());
113                 StructureSet set;
114                 if (node->op() == ArrayifyToStructure)
115                     set = node->structure();
116                 else
117                     set = node->structureSet();
118                 if (value.m_structure.isSubsetOf(set)) {
119                     m_interpreter.execute(indexInBlock); // Catch the fact that we may filter on cell.
120                     node->convertToPhantom();
121                     eliminated = true;
122                     break;
123                 }
124                 break;
125             }
126                 
127             case CheckStructureImmediate: {
128                 AbstractValue& value = m_state.forNode(node->child1());
129                 StructureSet& set = node->structureSet();
130                 
131                 if (value.value()) {
132                     if (Structure* structure = jsDynamicCast<Structure*>(value.value())) {
133                         if (set.contains(structure)) {
134                             m_interpreter.execute(indexInBlock);
135                             node->convertToPhantom();
136                             eliminated = true;
137                             break;
138                         }
139                     }
140                 }
141                 
142                 if (PhiChildren* phiChildren = m_interpreter.phiChildren()) {
143                     bool allGood = true;
144                     phiChildren->forAllTransitiveIncomingValues(
145                         node,
146                         [&] (Node* incoming) {
147                             if (Structure* structure = incoming->dynamicCastConstant<Structure*>()) {
148                                 if (set.contains(structure))
149                                     return;
150                             }
151                             allGood = false;
152                         });
153                     if (allGood) {
154                         m_interpreter.execute(indexInBlock);
155                         node->convertToPhantom();
156                         eliminated = true;
157                         break;
158                     }
159                 }
160                 break;
161             }
162                 
163             case CheckArray:
164             case Arrayify: {
165                 if (!node->arrayMode().alreadyChecked(m_graph, node, m_state.forNode(node->child1())))
166                     break;
167                 node->convertToPhantom();
168                 eliminated = true;
169                 break;
170             }
171                 
172             case PutStructure: {
173                 if (m_state.forNode(node->child1()).m_structure.onlyStructure() != node->transition()->next)
174                     break;
175                 
176                 node->convertToPhantom();
177                 eliminated = true;
178                 break;
179             }
180                 
181             case CheckCell: {
182                 if (m_state.forNode(node->child1()).value() != node->cellOperand()->value())
183                     break;
184                 node->convertToPhantom();
185                 eliminated = true;
186                 break;
187             }
188                 
189             case CheckInBounds: {
190                 JSValue left = m_state.forNode(node->child1()).value();
191                 JSValue right = m_state.forNode(node->child2()).value();
192                 if (left && right && left.isInt32() && right.isInt32()
193                     && static_cast<uint32_t>(left.asInt32()) < static_cast<uint32_t>(right.asInt32())) {
194                     node->convertToPhantom();
195                     eliminated = true;
196                     break;
197                 }
198                 
199                 break;
200             }
201                 
202             case MultiGetByOffset: {
203                 Edge baseEdge = node->child1();
204                 Node* base = baseEdge.node();
205                 MultiGetByOffsetData& data = node->multiGetByOffsetData();
206
207                 // First prune the variants, then check if the MultiGetByOffset can be
208                 // strength-reduced to a GetByOffset.
209                 
210                 AbstractValue baseValue = m_state.forNode(base);
211                 
212                 m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before.
213                 alreadyHandled = true; // Don't allow the default constant folder to do things to this.
214                 
215                 for (unsigned i = 0; i < data.variants.size(); ++i) {
216                     GetByIdVariant& variant = data.variants[i];
217                     variant.structureSet().filter(baseValue);
218                     if (variant.structureSet().isEmpty()) {
219                         data.variants[i--] = data.variants.last();
220                         data.variants.removeLast();
221                         changed = true;
222                     }
223                 }
224                 
225                 if (data.variants.size() != 1)
226                     break;
227                 
228                 emitGetByOffset(
229                     indexInBlock, node, baseValue, data.variants[0], data.identifierNumber);
230                 changed = true;
231                 break;
232             }
233                 
234             case MultiPutByOffset: {
235                 Edge baseEdge = node->child1();
236                 Node* base = baseEdge.node();
237                 MultiPutByOffsetData& data = node->multiPutByOffsetData();
238                 
239                 AbstractValue baseValue = m_state.forNode(base);
240
241                 m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before.
242                 alreadyHandled = true; // Don't allow the default constant folder to do things to this.
243                 
244
245                 for (unsigned i = 0; i < data.variants.size(); ++i) {
246                     PutByIdVariant& variant = data.variants[i];
247                     variant.oldStructure().filter(baseValue);
248                     
249                     if (variant.oldStructure().isEmpty()) {
250                         data.variants[i--] = data.variants.last();
251                         data.variants.removeLast();
252                         changed = true;
253                         continue;
254                     }
255                     
256                     if (variant.kind() == PutByIdVariant::Transition
257                         && variant.oldStructure().onlyStructure() == variant.newStructure()) {
258                         variant = PutByIdVariant::replace(
259                             variant.oldStructure(),
260                             variant.offset());
261                         changed = true;
262                     }
263                 }
264
265                 if (data.variants.size() != 1)
266                     break;
267                 
268                 emitPutByOffset(
269                     indexInBlock, node, baseValue, data.variants[0], data.identifierNumber);
270                 changed = true;
271                 break;
272             }
273         
274             case GetById:
275             case GetByIdFlush: {
276                 Edge childEdge = node->child1();
277                 Node* child = childEdge.node();
278                 unsigned identifierNumber = node->identifierNumber();
279                 
280                 AbstractValue baseValue = m_state.forNode(child);
281
282                 m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before.
283                 alreadyHandled = true; // Don't allow the default constant folder to do things to this.
284
285                 if (baseValue.m_structure.isTop() || baseValue.m_structure.isClobbered()
286                     || (node->child1().useKind() == UntypedUse || (baseValue.m_type & ~SpecCell)))
287                     break;
288                 
289                 GetByIdStatus status = GetByIdStatus::computeFor(
290                     baseValue.m_structure.set(), m_graph.identifiers()[identifierNumber]);
291                 if (!status.isSimple())
292                     break;
293                 
294                 for (unsigned i = status.numVariants(); i--;) {
295                     if (!status[i].constantChecks().isEmpty()
296                         || status[i].alternateBase()) {
297                         // FIXME: We could handle prototype cases.
298                         // https://bugs.webkit.org/show_bug.cgi?id=110386
299                         break;
300                     }
301                 }
302                 
303                 if (status.numVariants() == 1) {
304                     emitGetByOffset(indexInBlock, node, baseValue, status[0], identifierNumber);
305                     changed = true;
306                     break;
307                 }
308                 
309                 if (!isFTL(m_graph.m_plan.mode))
310                     break;
311                 
312                 MultiGetByOffsetData* data = m_graph.m_multiGetByOffsetData.add();
313                 data->variants = status.variants();
314                 data->identifierNumber = identifierNumber;
315                 node->convertToMultiGetByOffset(data);
316                 changed = true;
317                 break;
318             }
319                 
320             case PutById:
321             case PutByIdDirect:
322             case PutByIdFlush: {
323                 NodeOrigin origin = node->origin;
324                 Edge childEdge = node->child1();
325                 Node* child = childEdge.node();
326                 unsigned identifierNumber = node->identifierNumber();
327                 
328                 ASSERT(childEdge.useKind() == CellUse);
329                 
330                 AbstractValue baseValue = m_state.forNode(child);
331
332                 m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before.
333                 alreadyHandled = true; // Don't allow the default constant folder to do things to this.
334
335                 if (baseValue.m_structure.isTop() || baseValue.m_structure.isClobbered())
336                     break;
337                 
338                 PutByIdStatus status = PutByIdStatus::computeFor(
339                     m_graph.globalObjectFor(origin.semantic),
340                     baseValue.m_structure.set(),
341                     m_graph.identifiers()[identifierNumber],
342                     node->op() == PutByIdDirect);
343                 
344                 if (!status.isSimple())
345                     break;
346                 
347                 ASSERT(status.numVariants());
348                 
349                 if (status.numVariants() > 1 && !isFTL(m_graph.m_plan.mode))
350                     break;
351                 
352                 changed = true;
353                 
354                 for (unsigned i = status.numVariants(); i--;)
355                     addChecks(origin, indexInBlock, status[i].constantChecks());
356                 
357                 if (status.numVariants() == 1) {
358                     emitPutByOffset(indexInBlock, node, baseValue, status[0], identifierNumber);
359                     break;
360                 }
361                 
362                 ASSERT(isFTL(m_graph.m_plan.mode));
363
364                 MultiPutByOffsetData* data = m_graph.m_multiPutByOffsetData.add();
365                 data->variants = status.variants();
366                 data->identifierNumber = identifierNumber;
367                 node->convertToMultiPutByOffset(data);
368                 break;
369             }
370
371             case ToPrimitive: {
372                 if (m_state.forNode(node->child1()).m_type & ~(SpecFullNumber | SpecBoolean | SpecString | SpecCellOther))
373                     break;
374                 
375                 node->convertToIdentity();
376                 changed = true;
377                 break;
378             }
379                 
380             case GetMyArgumentByVal: {
381                 InlineCallFrame* inlineCallFrame = node->origin.semantic.inlineCallFrame;
382                 JSValue value = m_state.forNode(node->child1()).m_value;
383                 if (inlineCallFrame && value && value.isInt32()) {
384                     int32_t index = value.asInt32();
385                     if (index >= 0
386                         && static_cast<size_t>(index + 1) < inlineCallFrame->arguments.size()) {
387                         // Roll the interpreter over this.
388                         m_interpreter.execute(indexInBlock);
389                         eliminated = true;
390                         
391                         int operand =
392                             inlineCallFrame->stackOffset +
393                             m_graph.baselineCodeBlockFor(inlineCallFrame)->argumentIndexAfterCapture(index);
394                         
395                         m_insertionSet.insertNode(
396                             indexInBlock, SpecNone, CheckArgumentsNotCreated, node->origin);
397                         m_insertionSet.insertNode(
398                             indexInBlock, SpecNone, Phantom, node->origin, node->children);
399                         
400                         if (m_graph.m_form == SSA)
401                             node->convertToGetStack(m_graph.m_stackAccessData.add(VirtualRegister(operand), FlushedJSValue));
402                         else
403                             node->convertToGetLocalUnlinked(VirtualRegister(operand));
404                         break;
405                     }
406                 }
407                 
408                 break;
409             }
410                 
411             case Check: {
412                 alreadyHandled = true;
413                 m_interpreter.execute(indexInBlock);
414                 for (unsigned i = 0; i < AdjacencyList::Size; ++i) {
415                     Edge edge = node->children.child(i);
416                     if (!edge)
417                         break;
418                     if (edge.isProved() || edge.willNotHaveCheck()) {
419                         node->children.removeEdge(i--);
420                         changed = true;
421                     }
422                 }
423                 break;
424             }
425                 
426             default:
427                 break;
428             }
429             
430             if (eliminated) {
431                 changed = true;
432                 continue;
433             }
434                 
435             if (alreadyHandled)
436                 continue;
437             
438             m_interpreter.execute(indexInBlock);
439             if (!m_state.isValid()) {
440                 // If we invalidated then we shouldn't attempt to constant-fold. Here's an
441                 // example:
442                 //
443                 //     c: JSConstant(4.2)
444                 //     x: ValueToInt32(Check:Int32:@const)
445                 //
446                 // It would be correct for an analysis to assume that execution cannot
447                 // proceed past @x. Therefore, constant-folding @x could be rather bad. But,
448                 // the CFA may report that it found a constant even though it also reported
449                 // that everything has been invalidated. This will only happen in a couple of
450                 // the constant folding cases; most of them are also separately defensive
451                 // about such things.
452                 break;
453             }
454             if (!node->shouldGenerate() || m_state.didClobber() || node->hasConstant())
455                 continue;
456             
457             // Interesting fact: this freezing that we do right here may turn an fragile value into
458             // a weak value. See DFGValueStrength.h.
459             FrozenValue* value = m_graph.freeze(m_state.forNode(node).value());
460             if (!*value)
461                 continue;
462             
463             NodeOrigin origin = node->origin;
464             AdjacencyList children = node->children;
465             
466             m_graph.convertToConstant(node, value);
467             if (!children.isEmpty()) {
468                 m_insertionSet.insertNode(
469                     indexInBlock, SpecNone, Phantom, origin, children);
470             }
471             
472             changed = true;
473         }
474         m_state.reset();
475         m_insertionSet.execute(block);
476         
477         return changed;
478     }
479         
480     void emitGetByOffset(unsigned indexInBlock, Node* node, const AbstractValue& baseValue, const GetByIdVariant& variant, unsigned identifierNumber)
481     {
482         NodeOrigin origin = node->origin;
483         Edge childEdge = node->child1();
484
485         addBaseCheck(indexInBlock, node, baseValue, variant.structureSet());
486         
487         JSValue baseForLoad;
488         if (variant.alternateBase())
489             baseForLoad = variant.alternateBase();
490         else
491             baseForLoad = baseValue.m_value;
492         if (JSValue value = m_graph.tryGetConstantProperty(baseForLoad, variant.baseStructure(), variant.offset())) {
493             m_graph.convertToConstant(node, m_graph.freeze(value));
494             return;
495         }
496         
497         if (variant.alternateBase()) {
498             Node* child = m_insertionSet.insertConstant(indexInBlock, origin, variant.alternateBase());
499             childEdge = Edge(child, KnownCellUse);
500         } else
501             childEdge.setUseKind(KnownCellUse);
502         
503         Edge propertyStorage;
504         
505         if (isInlineOffset(variant.offset()))
506             propertyStorage = childEdge;
507         else {
508             propertyStorage = Edge(m_insertionSet.insertNode(
509                 indexInBlock, SpecNone, GetButterfly, origin, childEdge));
510         }
511         
512         StorageAccessData& data = *m_graph.m_storageAccessData.add();
513         data.offset = variant.offset();
514         data.identifierNumber = identifierNumber;
515         
516         node->convertToGetByOffset(data, propertyStorage);
517     }
518
519     void emitPutByOffset(unsigned indexInBlock, Node* node, const AbstractValue& baseValue, const PutByIdVariant& variant, unsigned identifierNumber)
520     {
521         NodeOrigin origin = node->origin;
522         Edge childEdge = node->child1();
523         
524         addBaseCheck(indexInBlock, node, baseValue, variant.oldStructure());
525
526         childEdge.setUseKind(KnownCellUse);
527
528         Transition* transition = 0;
529         if (variant.kind() == PutByIdVariant::Transition) {
530             transition = m_graph.m_transitions.add(
531                 variant.oldStructureForTransition(), variant.newStructure());
532         }
533
534         Edge propertyStorage;
535
536         if (isInlineOffset(variant.offset()))
537             propertyStorage = childEdge;
538         else if (!variant.reallocatesStorage()) {
539             propertyStorage = Edge(m_insertionSet.insertNode(
540                 indexInBlock, SpecNone, GetButterfly, origin, childEdge));
541         } else if (!variant.oldStructureForTransition()->outOfLineCapacity()) {
542             ASSERT(variant.newStructure()->outOfLineCapacity());
543             ASSERT(!isInlineOffset(variant.offset()));
544             Node* allocatePropertyStorage = m_insertionSet.insertNode(
545                 indexInBlock, SpecNone, AllocatePropertyStorage,
546                 origin, OpInfo(transition), childEdge);
547             m_insertionSet.insertNode(indexInBlock, SpecNone, StoreBarrier, origin, Edge(node->child1().node(), KnownCellUse));
548             propertyStorage = Edge(allocatePropertyStorage);
549         } else {
550             ASSERT(variant.oldStructureForTransition()->outOfLineCapacity());
551             ASSERT(variant.newStructure()->outOfLineCapacity() > variant.oldStructureForTransition()->outOfLineCapacity());
552             ASSERT(!isInlineOffset(variant.offset()));
553
554             Node* reallocatePropertyStorage = m_insertionSet.insertNode(
555                 indexInBlock, SpecNone, ReallocatePropertyStorage, origin,
556                 OpInfo(transition), childEdge,
557                 Edge(m_insertionSet.insertNode(
558                     indexInBlock, SpecNone, GetButterfly, origin, childEdge)));
559             m_insertionSet.insertNode(indexInBlock, SpecNone, StoreBarrier, origin, Edge(node->child1().node(), KnownCellUse));
560             propertyStorage = Edge(reallocatePropertyStorage);
561         }
562
563         if (variant.kind() == PutByIdVariant::Transition) {
564             Node* putStructure = m_graph.addNode(SpecNone, PutStructure, origin, OpInfo(transition), childEdge);
565             m_insertionSet.insertNode(indexInBlock, SpecNone, StoreBarrier, origin, Edge(node->child1().node(), KnownCellUse));
566             m_insertionSet.insert(indexInBlock, putStructure);
567         }
568
569         StorageAccessData& data = *m_graph.m_storageAccessData.add();
570         data.offset = variant.offset();
571         data.identifierNumber = identifierNumber;
572         
573         node->convertToPutByOffset(data, propertyStorage);
574         m_insertionSet.insertNode(
575             indexInBlock, SpecNone, StoreBarrier, origin, 
576             Edge(node->child2().node(), KnownCellUse));
577     }
578     
579     void addBaseCheck(
580         unsigned indexInBlock, Node* node, const AbstractValue& baseValue, const StructureSet& set)
581     {
582         if (!baseValue.m_structure.isSubsetOf(set)) {
583             // Arises when we prune MultiGetByOffset. We could have a
584             // MultiGetByOffset with a single variant that checks for structure S,
585             // and the input has structures S and T, for example.
586             m_insertionSet.insertNode(
587                 indexInBlock, SpecNone, CheckStructure, node->origin,
588                 OpInfo(m_graph.addStructureSet(set)), node->child1());
589             return;
590         }
591         
592         if (baseValue.m_type & ~SpecCell) {
593             m_insertionSet.insertNode(
594                 indexInBlock, SpecNone, Phantom, node->origin, node->child1());
595         }
596     }
597     
598     void addChecks(
599         NodeOrigin origin, unsigned indexInBlock, const ConstantStructureCheckVector& checks)
600     {
601         for (unsigned i = 0; i < checks.size(); ++i) {
602             addStructureTransitionCheck(
603                 origin, indexInBlock, checks[i].constant(), checks[i].structure());
604         }
605     }
606
607     void addStructureTransitionCheck(NodeOrigin origin, unsigned indexInBlock, JSCell* cell, Structure* structure)
608     {
609         if (m_graph.registerStructure(cell->structure()) == StructureRegisteredAndWatched)
610             return;
611         
612         m_graph.registerStructure(structure);
613
614         Node* weakConstant = m_insertionSet.insertNode(
615             indexInBlock, speculationFromValue(cell), JSConstant, origin,
616             OpInfo(m_graph.freeze(cell)));
617         
618         m_insertionSet.insertNode(
619             indexInBlock, SpecNone, CheckStructure, origin,
620             OpInfo(m_graph.addStructureSet(structure)), Edge(weakConstant, CellUse));
621     }
622     
623     void fixUpsilons(BasicBlock* block)
624     {
625         for (unsigned nodeIndex = block->size(); nodeIndex--;) {
626             Node* node = block->at(nodeIndex);
627             if (node->op() != Upsilon)
628                 continue;
629             switch (node->phi()->op()) {
630             case Phi:
631                 break;
632             case JSConstant:
633             case DoubleConstant:
634             case Int52Constant:
635                 node->convertToPhantom();
636                 break;
637             default:
638                 DFG_CRASH(m_graph, node, "Bad Upsilon phi() pointer");
639                 break;
640             }
641         }
642     }
643     
644     InPlaceAbstractState m_state;
645     AbstractInterpreter<InPlaceAbstractState> m_interpreter;
646     InsertionSet m_insertionSet;
647 };
648
649 bool performConstantFolding(Graph& graph)
650 {
651     SamplingRegion samplingRegion("DFG Constant Folding Phase");
652     return runPhase<ConstantFoldingPhase>(graph);
653 }
654
655 } } // namespace JSC::DFG
656
657 #endif // ENABLE(DFG_JIT)
658
659