Do unified source builds for JSC
[WebKit-https.git] / Source / JavaScriptCore / dfg / DFGPutStackSinkingPhase.cpp
1 /*
2  * Copyright (C) 2014, 2015 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 "DFGPutStackSinkingPhase.h"
28
29 #if ENABLE(DFG_JIT)
30
31 #include "DFGBlockMapInlines.h"
32 #include "DFGGraph.h"
33 #include "DFGInsertionSet.h"
34 #include "DFGPhase.h"
35 #include "DFGPreciseLocalClobberize.h"
36 #include "DFGSSACalculator.h"
37 #include "DFGValidate.h"
38 #include "JSCInlines.h"
39 #include "OperandsInlines.h"
40
41 namespace JSC { namespace DFG {
42
43 namespace {
44
45 namespace DFGPutStackSinkingPhaseInternal {
46 static const bool verbose = false;
47 }
48
49 class PutStackSinkingPhase : public Phase {
50 public:
51     PutStackSinkingPhase(Graph& graph)
52         : Phase(graph, "PutStack sinking")
53     {
54     }
55     
56     bool run()
57     {
58         // FIXME: One of the problems of this approach is that it will create a duplicate Phi graph
59         // for sunken PutStacks in the presence of interesting control flow merges, and where the
60         // value being PutStack'd is also otherwise live in the DFG code. We could work around this
61         // by doing the sinking over CPS, or maybe just by doing really smart hoisting. It's also
62         // possible that the duplicate Phi graph can be deduplicated by B3. It would be best if we
63         // could observe that there is already a Phi graph in place that does what we want. In
64         // principle if we have a request to place a Phi at a particular place, we could just check
65         // if there is already a Phi that does what we want. Because PutStackSinkingPhase runs just
66         // after SSA conversion, we have almost a guarantee that the Phi graph we produce here would
67         // be trivially redundant to the one we already have.
68         
69         // FIXME: This phase doesn't adequately use KillStacks. KillStack can be viewed as a def.
70         // This is mostly inconsequential; it would be a bug to have a local live at a KillStack.
71         // More important is that KillStack should swallow any deferral. After a KillStack, the
72         // local should behave like a TOP deferral because it would be invalid for anyone to trust
73         // the stack. It's not clear to me if this is important or not.
74         // https://bugs.webkit.org/show_bug.cgi?id=145296
75         
76         if (DFGPutStackSinkingPhaseInternal::verbose) {
77             dataLog("Graph before PutStack sinking:\n");
78             m_graph.dump();
79         }
80
81         m_graph.ensureSSADominators();
82         
83         SSACalculator ssaCalculator(m_graph);
84         InsertionSet insertionSet(m_graph);
85         
86         // First figure out where various locals are live.
87         BlockMap<Operands<bool>> liveAtHead(m_graph);
88         BlockMap<Operands<bool>> liveAtTail(m_graph);
89         
90         for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
91             liveAtHead[block] = Operands<bool>(OperandsLike, block->variablesAtHead);
92             liveAtTail[block] = Operands<bool>(OperandsLike, block->variablesAtHead);
93             
94             liveAtHead[block].fill(false);
95             liveAtTail[block].fill(false);
96         }
97         
98         bool changed;
99         do {
100             changed = false;
101             
102             for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) {
103                 BasicBlock* block = m_graph.block(blockIndex);
104                 if (!block)
105                     continue;
106                 
107                 Operands<bool> live = liveAtTail[block];
108                 for (unsigned nodeIndex = block->size(); nodeIndex--;) {
109                     Node* node = block->at(nodeIndex);
110                     if (DFGPutStackSinkingPhaseInternal::verbose)
111                         dataLog("Live at ", node, ": ", live, "\n");
112                     
113                     Vector<VirtualRegister, 4> reads;
114                     Vector<VirtualRegister, 4> writes;
115                     auto escapeHandler = [&] (VirtualRegister operand) {
116                         if (operand.isHeader())
117                             return;
118                         if (DFGPutStackSinkingPhaseInternal::verbose)
119                             dataLog("    ", operand, " is live at ", node, "\n");
120                         reads.append(operand);
121                     };
122
123                     auto writeHandler = [&] (VirtualRegister operand) {
124                         RELEASE_ASSERT(node->op() == PutStack || node->op() == LoadVarargs || node->op() == ForwardVarargs);
125                         writes.append(operand);
126                     };
127
128                     preciseLocalClobberize(
129                         m_graph, node, escapeHandler, writeHandler,
130                         [&] (VirtualRegister, LazyNode) { });
131
132                     for (VirtualRegister operand : writes)
133                         live.operand(operand) = false;
134                     for (VirtualRegister operand : reads)
135                         live.operand(operand) = true;
136                 }
137                 
138                 if (live == liveAtHead[block])
139                     continue;
140                 
141                 liveAtHead[block] = live;
142                 changed = true;
143                 
144                 for (BasicBlock* predecessor : block->predecessors) {
145                     for (size_t i = live.size(); i--;)
146                         liveAtTail[predecessor][i] |= live[i];
147                 }
148             }
149             
150         } while (changed);
151         
152         // All of the arguments should be live at head of root. Note that we may find that some
153         // locals are live at head of root. This seems wrong but isn't. This will happen for example
154         // if the function accesses closure variable #42 for some other function and we either don't
155         // have variable #42 at all or we haven't set it at root, for whatever reason. Basically this
156         // arises since our aliasing for closure variables is conservatively based on variable number
157         // and ignores the owning symbol table. We should probably fix this eventually and make our
158         // aliasing more precise.
159         //
160         // For our purposes here, the imprecision in the aliasing is harmless. It just means that we
161         // may not do as much Phi pruning as we wanted.
162         for (size_t i = liveAtHead.atIndex(0).numberOfArguments(); i--;)
163             DFG_ASSERT(m_graph, nullptr, liveAtHead.atIndex(0).argument(i));
164         
165         // Next identify where we would want to sink PutStacks to. We say that there is a deferred
166         // flush if we had a PutStack with a given FlushFormat but it hasn't been materialized yet.
167         // Deferrals have the following lattice; but it's worth noting that the TOP part of the
168         // lattice serves an entirely different purpose than the rest of the lattice: it just means
169         // that we're in a region of code where nobody should have been relying on the value. The
170         // rest of the lattice means that we either have a PutStack that is deferred (i.e. still
171         // needs to be executed) or there isn't one (because we've alraedy executed it).
172         //
173         // Bottom:
174         //     Represented as DeadFlush. 
175         //     Means that all previous PutStacks have been executed so there is nothing deferred.
176         //     During merging this is subordinate to the other kinds of deferrals, because it
177         //     represents the fact that we've already executed all necessary PutStacks. This implies
178         //     that there *had* been some PutStacks that we should have executed.
179         //
180         // Top:
181         //     Represented as ConflictingFlush.
182         //     Represents the fact that we know, via forward flow, that there isn't any value in the
183         //     given local that anyone should have been relying on. This comes into play at the
184         //     prologue (because in SSA form at the prologue no local has any value) or when we merge
185         //     deferrals for different formats's. A lexical scope in which a local had some semantic
186         //     meaning will by this point share the same format; if we had stores from different
187         //     lexical scopes that got merged together then we may have a conflicting format. Hence
188         //     a conflicting format proves that we're no longer in an area in which the variable was
189         //     in scope. Note that this is all approximate and only precise enough to later answer
190         //     questions pertinent to sinking. For example, this doesn't always detect when a local
191         //     is no longer semantically relevant - we may well have a deferral from inside some
192         //     inlined call survive outside of that inlined code, and this is generally OK. In the
193         //     worst case it means that we might think that a deferral that is actually dead must
194         //     still be executed. But we usually catch that with liveness. Liveness usually catches
195         //     such cases, but that's not guaranteed since liveness is conservative.
196         //
197         //     What Top does give us is detects situations where we both don't need to care about a
198         //     deferral and there is no way that we could reason about it anyway. If we merged
199         //     deferrals for different formats then we wouldn't know the format to use. So, we use
200         //     Top in that case because that's also a case where we know that we can ignore the
201         //     deferral.
202         //
203         // Deferral with a concrete format:
204         //     Represented by format values other than DeadFlush or ConflictingFlush.
205         //     Represents the fact that the original code would have done a PutStack but we haven't
206         //     identified an operation that would have observed that PutStack.
207         //
208         // We need to be precise about liveness in this phase because not doing so
209         // could cause us to insert a PutStack before a node we thought may escape a 
210         // value that it doesn't really escape. Sinking this PutStack above such a node may
211         // cause us to insert a GetStack that we forward to the Phi we're feeding into the
212         // sunken PutStack. Inserting such a GetStack could cause us to load garbage and
213         // can confuse the AI to claim untrue things (like that the program will exit when
214         // it really won't).
215         BlockMap<Operands<FlushFormat>> deferredAtHead(m_graph);
216         BlockMap<Operands<FlushFormat>> deferredAtTail(m_graph);
217         
218         for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
219             deferredAtHead[block] =
220                 Operands<FlushFormat>(OperandsLike, block->variablesAtHead);
221             deferredAtTail[block] =
222                 Operands<FlushFormat>(OperandsLike, block->variablesAtHead);
223         }
224
225         for (unsigned local = deferredAtHead.atIndex(0).numberOfLocals(); local--;)
226             deferredAtHead.atIndex(0).local(local) = ConflictingFlush;
227         
228         do {
229             changed = false;
230             
231             for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
232                 Operands<FlushFormat> deferred = deferredAtHead[block];
233                 
234                 for (Node* node : *block) {
235                     if (DFGPutStackSinkingPhaseInternal::verbose)
236                         dataLog("Deferred at ", node, ":", deferred, "\n");
237                     
238                     if (node->op() == GetStack) {
239                         // Handle the case that the input doesn't match our requirements. This is
240                         // really a bug, but it's a benign one if we simply don't run this phase.
241                         // It usually arises because of patterns like:
242                         //
243                         // if (thing)
244                         //     PutStack()
245                         // ...
246                         // if (thing)
247                         //     GetStack()
248                         //
249                         // Or:
250                         //
251                         // if (never happens)
252                         //     GetStack()
253                         //
254                         // Because this phase runs early in SSA, it should be sensible to enforce
255                         // that no such code pattern has arisen yet. So, when validation is
256                         // enabled, we assert that we aren't seeing this. But with validation
257                         // disabled we silently let this fly and we just abort this phase.
258                         // FIXME: Get rid of all remaining cases of conflicting GetStacks.
259                         // https://bugs.webkit.org/show_bug.cgi?id=150398
260
261                         bool isConflicting =
262                             deferred.operand(node->stackAccessData()->local) == ConflictingFlush;
263                         
264                         if (validationEnabled())
265                             DFG_ASSERT(m_graph, node, !isConflicting);
266
267                         if (isConflicting) {
268                             // Oh noes! Abort!!
269                             return false;
270                         }
271
272                         // A GetStack doesn't affect anything, since we know which local we are reading
273                         // from.
274                         continue;
275                     } else if (node->op() == PutStack) {
276                         VirtualRegister operand = node->stackAccessData()->local;
277                         deferred.operand(operand) = node->stackAccessData()->format;
278                         continue;
279                     }
280                     
281                     auto escapeHandler = [&] (VirtualRegister operand) {
282                         if (DFGPutStackSinkingPhaseInternal::verbose)
283                             dataLog("For ", node, " escaping ", operand, "\n");
284                         if (operand.isHeader())
285                             return;
286                         // We will materialize just before any reads.
287                         deferred.operand(operand) = DeadFlush;
288                     };
289
290                     auto writeHandler = [&] (VirtualRegister operand) {
291                         RELEASE_ASSERT(node->op() == LoadVarargs || node->op() == ForwardVarargs);
292                         deferred.operand(operand) = DeadFlush;
293                     };
294                     
295                     preciseLocalClobberize(
296                         m_graph, node, escapeHandler, writeHandler,
297                         [&] (VirtualRegister, LazyNode) { });
298                 }
299                 
300                 if (deferred == deferredAtTail[block])
301                     continue;
302                 
303                 deferredAtTail[block] = deferred;
304                 changed = true;
305                 
306                 for (BasicBlock* successor : block->successors()) {
307                     for (size_t i = deferred.size(); i--;) {
308                         if (DFGPutStackSinkingPhaseInternal::verbose)
309                             dataLog("Considering ", VirtualRegister(deferred.operandForIndex(i)), " at ", pointerDump(block), "->", pointerDump(successor), ": ", deferred[i], " and ", deferredAtHead[successor][i], " merges to ");
310
311                         deferredAtHead[successor][i] =
312                             merge(deferredAtHead[successor][i], deferred[i]);
313                         
314                         if (DFGPutStackSinkingPhaseInternal::verbose)
315                             dataLog(deferredAtHead[successor][i], "\n");
316                     }
317                 }
318             }
319             
320         } while (changed);
321         
322         // We wish to insert PutStacks at all of the materialization points, which are defined
323         // implicitly as the places where we set deferred to Dead while it was previously not Dead.
324         // To do this, we may need to build some Phi functions to handle stuff like this:
325         //
326         // Before:
327         //
328         //     if (p)
329         //         PutStack(r42, @x)
330         //     else
331         //         PutStack(r42, @y)
332         //
333         // After:
334         //
335         //     if (p)
336         //         Upsilon(@x, ^z)
337         //     else
338         //         Upsilon(@y, ^z)
339         //     z: Phi()
340         //     PutStack(r42, @z)
341         //
342         // This means that we have an SSACalculator::Variable for each local, and a Def is any
343         // PutStack in the original program. The original PutStacks will simply vanish.
344         
345         Operands<SSACalculator::Variable*> operandToVariable(
346             OperandsLike, m_graph.block(0)->variablesAtHead);
347         Vector<VirtualRegister> indexToOperand;
348         for (size_t i = m_graph.block(0)->variablesAtHead.size(); i--;) {
349             VirtualRegister operand(m_graph.block(0)->variablesAtHead.operandForIndex(i));
350             
351             SSACalculator::Variable* variable = ssaCalculator.newVariable();
352             operandToVariable.operand(operand) = variable;
353             ASSERT(indexToOperand.size() == variable->index());
354             indexToOperand.append(operand);
355         }
356         
357         HashSet<Node*> putStacksToSink;
358         
359         for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
360             for (Node* node : *block) {
361                 switch (node->op()) {
362                 case PutStack:
363                     putStacksToSink.add(node);
364                     ssaCalculator.newDef(
365                         operandToVariable.operand(node->stackAccessData()->local),
366                         block, node->child1().node());
367                     break;
368                 case GetStack:
369                     ssaCalculator.newDef(
370                         operandToVariable.operand(node->stackAccessData()->local),
371                         block, node);
372                     break;
373                 default:
374                     break;
375                 }
376             }
377         }
378         
379         ssaCalculator.computePhis(
380             [&] (SSACalculator::Variable* variable, BasicBlock* block) -> Node* {
381                 VirtualRegister operand = indexToOperand[variable->index()];
382                 
383                 if (!liveAtHead[block].operand(operand))
384                     return nullptr;
385                 
386                 FlushFormat format = deferredAtHead[block].operand(operand);
387
388                 // We could have an invalid deferral because liveness is imprecise.
389                 if (!isConcrete(format))
390                     return nullptr;
391
392                 if (DFGPutStackSinkingPhaseInternal::verbose)
393                     dataLog("Adding Phi for ", operand, " at ", pointerDump(block), "\n");
394                 
395                 Node* phiNode = m_graph.addNode(SpecHeapTop, Phi, block->at(0)->origin.withInvalidExit());
396                 phiNode->mergeFlags(resultFor(format));
397                 return phiNode;
398             });
399         
400         Operands<Node*> mapping(OperandsLike, m_graph.block(0)->variablesAtHead);
401         Operands<FlushFormat> deferred;
402         for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
403             mapping.fill(nullptr);
404             
405             for (size_t i = mapping.size(); i--;) {
406                 VirtualRegister operand(mapping.operandForIndex(i));
407                 
408                 SSACalculator::Variable* variable = operandToVariable.operand(operand);
409                 SSACalculator::Def* def = ssaCalculator.reachingDefAtHead(block, variable);
410                 if (!def)
411                     continue;
412                 
413                 mapping.operand(operand) = def->value();
414             }
415             
416             if (DFGPutStackSinkingPhaseInternal::verbose)
417                 dataLog("Mapping at top of ", pointerDump(block), ": ", mapping, "\n");
418             
419             for (SSACalculator::Def* phiDef : ssaCalculator.phisForBlock(block)) {
420                 VirtualRegister operand = indexToOperand[phiDef->variable()->index()];
421                 
422                 insertionSet.insert(0, phiDef->value());
423                 
424                 if (DFGPutStackSinkingPhaseInternal::verbose)
425                     dataLog("   Mapping ", operand, " to ", phiDef->value(), "\n");
426                 mapping.operand(operand) = phiDef->value();
427             }
428             
429             deferred = deferredAtHead[block];
430             for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
431                 Node* node = block->at(nodeIndex);
432                 if (DFGPutStackSinkingPhaseInternal::verbose)
433                     dataLog("Deferred at ", node, ":", deferred, "\n");
434                 
435                 switch (node->op()) {
436                 case PutStack: {
437                     StackAccessData* data = node->stackAccessData();
438                     VirtualRegister operand = data->local;
439                     deferred.operand(operand) = data->format;
440                     if (DFGPutStackSinkingPhaseInternal::verbose)
441                         dataLog("   Mapping ", operand, " to ", node->child1().node(), " at ", node, "\n");
442                     mapping.operand(operand) = node->child1().node();
443                     break;
444                 }
445                     
446                 case GetStack: {
447                     StackAccessData* data = node->stackAccessData();
448                     FlushFormat format = deferred.operand(data->local);
449                     if (!isConcrete(format)) {
450                         DFG_ASSERT(
451                             m_graph, node,
452                             deferred.operand(data->local) != ConflictingFlush);
453                         
454                         // This means there is no deferral. No deferral means that the most
455                         // authoritative value for this stack slot is what is stored in the stack. So,
456                         // keep the GetStack.
457                         mapping.operand(data->local) = node;
458                         break;
459                     }
460                     
461                     // We have a concrete deferral, which means a PutStack that hasn't executed yet. It
462                     // would have stored a value with a certain format. That format must match our
463                     // format. But more importantly, we can simply use the value that the PutStack would
464                     // have stored and get rid of the GetStack.
465                     DFG_ASSERT(m_graph, node, format == data->format);
466                     
467                     Node* incoming = mapping.operand(data->local);
468                     node->child1() = incoming->defaultEdge();
469                     node->convertToIdentity();
470                     break;
471                 }
472                 
473                 default: {
474                     auto escapeHandler = [&] (VirtualRegister operand) {
475                         if (DFGPutStackSinkingPhaseInternal::verbose)
476                             dataLog("For ", node, " escaping ", operand, "\n");
477
478                         if (operand.isHeader())
479                             return;
480                     
481                         FlushFormat format = deferred.operand(operand);
482                         if (!isConcrete(format)) {
483                             // It's dead now, rather than conflicting.
484                             deferred.operand(operand) = DeadFlush;
485                             return;
486                         }
487                     
488                         // Gotta insert a PutStack.
489                         if (DFGPutStackSinkingPhaseInternal::verbose)
490                             dataLog("Inserting a PutStack for ", operand, " at ", node, "\n");
491
492                         Node* incoming = mapping.operand(operand);
493                         DFG_ASSERT(m_graph, node, incoming);
494                     
495                         insertionSet.insertNode(
496                             nodeIndex, SpecNone, PutStack, node->origin,
497                             OpInfo(m_graph.m_stackAccessData.add(operand, format)),
498                             Edge(incoming, uncheckedUseKindFor(format)));
499                     
500                         deferred.operand(operand) = DeadFlush;
501                     };
502
503                     auto writeHandler = [&] (VirtualRegister operand) {
504                         // LoadVarargs and ForwardVarargs are unconditional writes to the stack
505                         // locations they claim to write to. They do not read from the stack 
506                         // locations they write to. This makes those stack locations dead right 
507                         // before a LoadVarargs/ForwardVarargs. This means we should never sink
508                         // PutStacks right to this point.
509                         RELEASE_ASSERT(node->op() == LoadVarargs || node->op() == ForwardVarargs);
510                         deferred.operand(operand) = DeadFlush;
511                     };
512
513                     preciseLocalClobberize(
514                         m_graph, node, escapeHandler, writeHandler,
515                         [&] (VirtualRegister, LazyNode) { });
516                     break;
517                 } }
518             }
519             
520             NodeAndIndex terminal = block->findTerminal();
521             size_t upsilonInsertionPoint = terminal.index;
522             NodeOrigin upsilonOrigin = terminal.node->origin;
523             for (BasicBlock* successorBlock : block->successors()) {
524                 for (SSACalculator::Def* phiDef : ssaCalculator.phisForBlock(successorBlock)) {
525                     Node* phiNode = phiDef->value();
526                     SSACalculator::Variable* variable = phiDef->variable();
527                     VirtualRegister operand = indexToOperand[variable->index()];
528                     if (DFGPutStackSinkingPhaseInternal::verbose)
529                         dataLog("Creating Upsilon for ", operand, " at ", pointerDump(block), "->", pointerDump(successorBlock), "\n");
530                     FlushFormat format = deferredAtHead[successorBlock].operand(operand);
531                     DFG_ASSERT(m_graph, nullptr, isConcrete(format));
532                     UseKind useKind = uncheckedUseKindFor(format);
533                     
534                     // We need to get a value for the stack slot. This phase doesn't really have a
535                     // good way of determining if a stack location got clobbered. It just knows if
536                     // there is a deferral. The lack of a deferral might mean that a PutStack or
537                     // GetStack had never happened, or it might mean that the value was read, or
538                     // that it was written. It's OK for us to make some bad decisions here, since
539                     // GCSE will clean it up anyway.
540                     Node* incoming;
541                     if (isConcrete(deferred.operand(operand))) {
542                         incoming = mapping.operand(operand);
543                         DFG_ASSERT(m_graph, phiNode, incoming);
544                     } else {
545                         // Issue a GetStack to get the value. This might introduce some redundancy
546                         // into the code, but if it's bad enough, GCSE will clean it up.
547                         incoming = insertionSet.insertNode(
548                             upsilonInsertionPoint, SpecNone, GetStack, upsilonOrigin,
549                             OpInfo(m_graph.m_stackAccessData.add(operand, format)));
550                         incoming->setResult(resultFor(format));
551                     }
552                     
553                     insertionSet.insertNode(
554                         upsilonInsertionPoint, SpecNone, Upsilon, upsilonOrigin,
555                         OpInfo(phiNode), Edge(incoming, useKind));
556                 }
557             }
558             
559             insertionSet.execute(block);
560         }
561         
562         // Finally eliminate the sunken PutStacks by turning them into Checks. This keeps whatever
563         // type check they were doing.
564         for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
565             for (auto* node : *block) {
566                 if (!putStacksToSink.contains(node))
567                     continue;
568                 
569                 node->remove();
570             }
571         }
572         
573         if (DFGPutStackSinkingPhaseInternal::verbose) {
574             dataLog("Graph after PutStack sinking:\n");
575             m_graph.dump();
576         }
577         
578         return true;
579     }
580 };
581
582 } // anonymous namespace
583     
584 bool performPutStackSinking(Graph& graph)
585 {
586     return runPhase<PutStackSinkingPhase>(graph);
587 }
588
589 } } // namespace JSC::DFG
590
591 #endif // ENABLE(DFG_JIT)
592