[INTL] Implement String.prototype.localeCompare in ECMA-402
[WebKit-https.git] / Source / JavaScriptCore / dfg / DFGArgumentsEliminationPhase.cpp
1 /*
2  * Copyright (C) 2015-2016 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 "DFGArgumentsEliminationPhase.h"
28
29 #if ENABLE(DFG_JIT)
30
31 #include "BytecodeLivenessAnalysisInlines.h"
32 #include "ClonedArguments.h"
33 #include "DFGArgumentsUtilities.h"
34 #include "DFGBasicBlockInlines.h"
35 #include "DFGBlockMapInlines.h"
36 #include "DFGClobberize.h"
37 #include "DFGCombinedLiveness.h"
38 #include "DFGForAllKills.h"
39 #include "DFGGraph.h"
40 #include "DFGInsertionSet.h"
41 #include "DFGLivenessAnalysisPhase.h"
42 #include "DFGOSRAvailabilityAnalysisPhase.h"
43 #include "DFGPhase.h"
44 #include "JSCInlines.h"
45 #include <wtf/HashMap.h>
46 #include <wtf/HashSet.h>
47 #include <wtf/ListDump.h>
48
49 namespace JSC { namespace DFG {
50
51 namespace {
52
53 bool verbose = false;
54
55 class ArgumentsEliminationPhase : public Phase {
56 public:
57     ArgumentsEliminationPhase(Graph& graph)
58         : Phase(graph, "arguments elimination")
59     {
60     }
61     
62     bool run()
63     {
64         // For now this phase only works on SSA. This could be changed; we could have a block-local
65         // version over LoadStore.
66         DFG_ASSERT(m_graph, nullptr, m_graph.m_form == SSA);
67         
68         if (verbose) {
69             dataLog("Graph before arguments elimination:\n");
70             m_graph.dump();
71         }
72         
73         identifyCandidates();
74         if (m_candidates.isEmpty())
75             return false;
76         
77         eliminateCandidatesThatEscape();
78         if (m_candidates.isEmpty())
79             return false;
80         
81         eliminateCandidatesThatInterfere();
82         if (m_candidates.isEmpty())
83             return false;
84         
85         transform();
86         
87         return true;
88     }
89
90 private:
91     // Just finds nodes that we know how to work with.
92     void identifyCandidates()
93     {
94         for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
95             for (Node* node : *block) {
96                 switch (node->op()) {
97                 case CreateDirectArguments:
98                 case CreateClonedArguments:
99                     m_candidates.add(node);
100                     break;
101                     
102                 case CreateScopedArguments:
103                     // FIXME: We could handle this if it wasn't for the fact that scoped arguments are
104                     // always stored into the activation.
105                     // https://bugs.webkit.org/show_bug.cgi?id=143072 and
106                     // https://bugs.webkit.org/show_bug.cgi?id=143073
107                     break;
108                     
109                 default:
110                     break;
111                 }
112             }
113         }
114         
115         if (verbose)
116             dataLog("Candidates: ", listDump(m_candidates), "\n");
117     }
118     
119     // Look for escaping sites, and remove from the candidates set if we see an escape.
120     void eliminateCandidatesThatEscape()
121     {
122         auto escape = [&] (Edge edge, Node* source) {
123             if (!edge)
124                 return;
125             bool removed = m_candidates.remove(edge.node());
126             if (removed && verbose)
127                 dataLog("eliminating candidate: ", edge.node(), " because it escapes from: ", source, "\n");
128         };
129         
130         auto escapeBasedOnArrayMode = [&] (ArrayMode mode, Edge edge, Node* source) {
131             switch (mode.type()) {
132             case Array::DirectArguments:
133                 if (edge->op() != CreateDirectArguments)
134                     escape(edge, source);
135                 break;
136             
137             case Array::Contiguous: {
138                 if (edge->op() != CreateClonedArguments) {
139                     escape(edge, source);
140                     return;
141                 }
142             
143                 // Everything is fine if we're doing an in-bounds access.
144                 if (mode.isInBounds())
145                     break;
146                 
147                 // If we're out-of-bounds then we proceed only if the object prototype is
148                 // sane (i.e. doesn't have indexed properties).
149                 JSGlobalObject* globalObject = m_graph.globalObjectFor(edge->origin.semantic);
150                 if (globalObject->objectPrototypeIsSane()) {
151                     m_graph.watchpoints().addLazily(globalObject->objectPrototype()->structure()->transitionWatchpointSet());
152                     if (globalObject->objectPrototypeIsSane())
153                         break;
154                 }
155                 escape(edge, source);
156                 break;
157             }
158             
159             case Array::ForceExit:
160                 break;
161             
162             default:
163                 escape(edge, source);
164                 break;
165             }
166         };
167         
168         for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
169             for (Node* node : *block) {
170                 switch (node->op()) {
171                 case GetFromArguments:
172                     DFG_ASSERT(m_graph, node, node->child1()->op() == CreateDirectArguments);
173                     break;
174                     
175                 case GetByVal:
176                     escapeBasedOnArrayMode(node->arrayMode(), node->child1(), node);
177                     escape(node->child2(), node);
178                     escape(node->child3(), node);
179                     break;
180
181                 case GetArrayLength:
182                     escapeBasedOnArrayMode(node->arrayMode(), node->child1(), node);
183                     escape(node->child2(), node);
184                     break;
185                     
186                 case LoadVarargs:
187                     break;
188                     
189                 case CallVarargs:
190                 case ConstructVarargs:
191                 case TailCallVarargs:
192                 case TailCallVarargsInlinedCaller:
193                     escape(node->child1(), node);
194                     escape(node->child3(), node);
195                     break;
196
197                 case Check:
198                     m_graph.doToChildren(
199                         node,
200                         [&] (Edge edge) {
201                             if (edge.willNotHaveCheck())
202                                 return;
203                             
204                             if (alreadyChecked(edge.useKind(), SpecObject))
205                                 return;
206                             
207                             escape(edge, node);
208                         });
209                     break;
210                     
211                 case MovHint:
212                 case PutHint:
213                     break;
214                     
215                 case GetButterfly:
216                     // This barely works. The danger is that the GetButterfly is used by something that
217                     // does something escaping to a candidate. Fortunately, the only butterfly-using ops
218                     // that we exempt here also use the candidate directly. If there ever was a
219                     // butterfly-using op that we wanted to exempt, then we'd have to look at the
220                     // butterfly's child and check if it's a candidate.
221                     break;
222                     
223                 case CheckArray:
224                     escapeBasedOnArrayMode(node->arrayMode(), node->child1(), node);
225                     break;
226
227                     
228                 // FIXME: We should be able to handle GetById/GetByOffset on callee.
229                 // https://bugs.webkit.org/show_bug.cgi?id=143075
230
231                 case GetByOffset:
232                     if (node->child2()->op() == CreateClonedArguments && node->storageAccessData().offset == clonedArgumentsLengthPropertyOffset)
233                         break;
234                     FALLTHROUGH;
235                 default:
236                     m_graph.doToChildren(node, [&] (Edge edge) { return escape(edge, node); });
237                     break;
238                 }
239             }
240         }
241
242         if (verbose)
243             dataLog("After escape analysis: ", listDump(m_candidates), "\n");
244     }
245
246     // Anywhere that a candidate is live (in bytecode or in DFG), check if there is a chance of
247     // interference between the stack area that the arguments object copies from and the arguments
248     // object's payload. Conservatively this means that the stack region doesn't get stored to.
249     void eliminateCandidatesThatInterfere()
250     {
251         performLivenessAnalysis(m_graph);
252         performOSRAvailabilityAnalysis(m_graph);
253         m_graph.initializeNodeOwners();
254         CombinedLiveness combinedLiveness(m_graph);
255         
256         BlockMap<Operands<bool>> clobberedByBlock(m_graph);
257         for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
258             Operands<bool>& clobberedByThisBlock = clobberedByBlock[block];
259             clobberedByThisBlock = Operands<bool>(OperandsLike, m_graph.block(0)->variablesAtHead);
260             for (Node* node : *block) {
261                 clobberize(
262                     m_graph, node, NoOpClobberize(),
263                     [&] (AbstractHeap heap) {
264                         if (heap.kind() != Stack) {
265                             ASSERT(!heap.overlaps(Stack));
266                             return;
267                         }
268                         ASSERT(!heap.payload().isTop());
269                         VirtualRegister reg(heap.payload().value32());
270                         clobberedByThisBlock.operand(reg) = true;
271                     },
272                     NoOpClobberize());
273             }
274         }
275         
276         for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
277             // Stop if we've already removed all candidates.
278             if (m_candidates.isEmpty())
279                 return;
280             
281             // Ignore blocks that don't write to the stack.
282             bool writesToStack = false;
283             for (unsigned i = clobberedByBlock[block].size(); i--;) {
284                 if (clobberedByBlock[block][i]) {
285                     writesToStack = true;
286                     break;
287                 }
288             }
289             if (!writesToStack)
290                 continue;
291             
292             forAllKillsInBlock(
293                 m_graph, combinedLiveness, block,
294                 [&] (unsigned nodeIndex, Node* candidate) {
295                     if (!m_candidates.contains(candidate))
296                         return;
297                     
298                     // Check if this block has any clobbers that affect this candidate. This is a fairly
299                     // fast check.
300                     bool isClobberedByBlock = false;
301                     Operands<bool>& clobberedByThisBlock = clobberedByBlock[block];
302                     
303                     if (InlineCallFrame* inlineCallFrame = candidate->origin.semantic.inlineCallFrame) {
304                         if (inlineCallFrame->isVarargs()) {
305                             isClobberedByBlock |= clobberedByThisBlock.operand(
306                                 inlineCallFrame->stackOffset + JSStack::ArgumentCount);
307                         }
308                         
309                         if (!isClobberedByBlock || inlineCallFrame->isClosureCall) {
310                             isClobberedByBlock |= clobberedByThisBlock.operand(
311                                 inlineCallFrame->stackOffset + JSStack::Callee);
312                         }
313                         
314                         if (!isClobberedByBlock) {
315                             for (unsigned i = 0; i < inlineCallFrame->arguments.size() - 1; ++i) {
316                                 VirtualRegister reg =
317                                     VirtualRegister(inlineCallFrame->stackOffset) +
318                                     CallFrame::argumentOffset(i);
319                                 if (clobberedByThisBlock.operand(reg)) {
320                                     isClobberedByBlock = true;
321                                     break;
322                                 }
323                             }
324                         }
325                     } else {
326                         // We don't include the ArgumentCount or Callee in this case because we can be
327                         // damn sure that this won't be clobbered.
328                         for (unsigned i = 1; i < static_cast<unsigned>(codeBlock()->numParameters()); ++i) {
329                             if (clobberedByThisBlock.argument(i)) {
330                                 isClobberedByBlock = true;
331                                 break;
332                             }
333                         }
334                     }
335                     
336                     if (!isClobberedByBlock)
337                         return;
338                     
339                     // Check if we can immediately eliminate this candidate. If the block has a clobber
340                     // for this arguments allocation, and we'd have to examine every node in the block,
341                     // then we can just eliminate the candidate.
342                     if (nodeIndex == block->size() && candidate->owner != block) {
343                         if (verbose)
344                             dataLog("eliminating candidate: ", candidate, " because it is clobbered by: ", block->at(nodeIndex), "\n");
345                         m_candidates.remove(candidate);
346                         return;
347                     }
348                     
349                     // This loop considers all nodes up to the nodeIndex, excluding the nodeIndex.
350                     while (nodeIndex--) {
351                         Node* node = block->at(nodeIndex);
352                         if (node == candidate)
353                             break;
354                         
355                         bool found = false;
356                         clobberize(
357                             m_graph, node, NoOpClobberize(),
358                             [&] (AbstractHeap heap) {
359                                 if (heap.kind() == Stack && !heap.payload().isTop()) {
360                                     if (argumentsInvolveStackSlot(candidate, VirtualRegister(heap.payload().value32())))
361                                         found = true;
362                                     return;
363                                 }
364                                 if (heap.overlaps(Stack))
365                                     found = true;
366                             },
367                             NoOpClobberize());
368                         
369                         if (found) {
370                             if (verbose)
371                                 dataLog("eliminating candidate: ", candidate, " because it is clobbered by ", block->at(nodeIndex), "\n");
372                             m_candidates.remove(candidate);
373                             return;
374                         }
375                     }
376                 });
377         }
378         
379         // Q: How do we handle OSR exit with a live PhantomArguments at a point where the inline call
380         // frame is dead?  A: Naively we could say that PhantomArguments must escape the stack slots. But
381         // that would break PutStack sinking, which in turn would break object allocation sinking, in
382         // cases where we have a varargs call to an otherwise pure method. So, we need something smarter.
383         // For the outermost arguments, we just have a PhantomArguments that magically knows that it
384         // should load the arguments from the call frame. For the inline arguments, we have the heap map
385         // in the availabiltiy map track each possible inline argument as a promoted heap location. If the
386         // PutStacks for those arguments aren't sunk, those heap locations will map to very trivial
387         // availabilities (they will be flush availabilities). But if sinking happens then those
388         // availabilities may become whatever. OSR exit should be able to handle this quite naturally,
389         // since those availabilities speak of the stack before the optimizing compiler stack frame is
390         // torn down.
391
392         if (verbose)
393             dataLog("After interference analysis: ", listDump(m_candidates), "\n");
394     }
395     
396     void transform()
397     {
398         InsertionSet insertionSet(m_graph);
399         
400         for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
401             for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
402                 Node* node = block->at(nodeIndex);
403                 
404                 auto getArrayLength = [&] (Node* candidate) -> Node* {
405                     return emitCodeToGetArgumentsArrayLength(
406                         insertionSet, candidate, nodeIndex, node->origin);
407                 };
408         
409                 switch (node->op()) {
410                 case CreateDirectArguments:
411                     if (!m_candidates.contains(node))
412                         break;
413                     
414                     node->setOpAndDefaultFlags(PhantomDirectArguments);
415                     break;
416                     
417                 case CreateClonedArguments:
418                     if (!m_candidates.contains(node))
419                         break;
420                     
421                     node->setOpAndDefaultFlags(PhantomClonedArguments);
422                     break;
423                     
424                 case GetFromArguments: {
425                     Node* candidate = node->child1().node();
426                     if (!m_candidates.contains(candidate))
427                         break;
428                     
429                     DFG_ASSERT(
430                         m_graph, node,
431                         node->child1()->op() == CreateDirectArguments
432                         || node->child1()->op() == PhantomDirectArguments);
433                     VirtualRegister reg =
434                         virtualRegisterForArgument(node->capturedArgumentsOffset().offset() + 1) +
435                         node->origin.semantic.stackOffset();
436                     StackAccessData* data = m_graph.m_stackAccessData.add(reg, FlushedJSValue);
437                     node->convertToGetStack(data);
438                     break;
439                 }
440
441                 case GetByOffset: {
442                     Node* candidate = node->child2().node();
443                     if (!m_candidates.contains(candidate))
444                         break;
445
446                     if (node->child2()->op() != PhantomClonedArguments)
447                         break;
448
449                     ASSERT(node->storageAccessData().offset == clonedArgumentsLengthPropertyOffset);
450
451                     // Meh, this is kind of hackish - we use an Identity so that we can reuse the
452                     // getArrayLength() helper.
453                     node->convertToIdentityOn(getArrayLength(candidate));
454                     break;
455                 }
456                     
457                 case GetArrayLength: {
458                     Node* candidate = node->child1().node();
459                     if (!m_candidates.contains(candidate))
460                         break;
461                     
462                     // Meh, this is kind of hackish - we use an Identity so that we can reuse the
463                     // getArrayLength() helper.
464                     node->convertToIdentityOn(getArrayLength(candidate));
465                     break;
466                 }
467                     
468                 case GetByVal: {
469                     // FIXME: For ClonedArguments, we would have already done a separate bounds check.
470                     // This code will cause us to have two bounds checks - the original one that we
471                     // already factored out in SSALoweringPhase, and the new one we insert here, which is
472                     // often implicitly part of GetMyArgumentByVal. B3 will probably eliminate the
473                     // second bounds check, but still - that's just silly.
474                     // https://bugs.webkit.org/show_bug.cgi?id=143076
475                     
476                     Node* candidate = node->child1().node();
477                     if (!m_candidates.contains(candidate))
478                         break;
479                     
480                     Node* result = nullptr;
481                     if (node->child2()->isInt32Constant()) {
482                         unsigned index = node->child2()->asUInt32();
483                         InlineCallFrame* inlineCallFrame = candidate->origin.semantic.inlineCallFrame;
484                         
485                         bool safeToGetStack;
486                         if (inlineCallFrame)
487                             safeToGetStack = index < inlineCallFrame->arguments.size() - 1;
488                         else {
489                             safeToGetStack =
490                                 index < static_cast<unsigned>(codeBlock()->numParameters()) - 1;
491                         }
492                         if (safeToGetStack) {
493                             StackAccessData* data;
494                             VirtualRegister arg = virtualRegisterForArgument(index + 1);
495                             if (inlineCallFrame)
496                                 arg += inlineCallFrame->stackOffset;
497                             data = m_graph.m_stackAccessData.add(arg, FlushedJSValue);
498                             
499                             if (!inlineCallFrame || inlineCallFrame->isVarargs()) {
500                                 insertionSet.insertNode(
501                                     nodeIndex, SpecNone, CheckInBounds, node->origin,
502                                     node->child2(), Edge(getArrayLength(candidate), Int32Use));
503                             }
504                             
505                             result = insertionSet.insertNode(
506                                 nodeIndex, node->prediction(), GetStack, node->origin, OpInfo(data));
507                         }
508                     }
509                     
510                     if (!result) {
511                         NodeType op;
512                         if (node->arrayMode().isInBounds())
513                             op = GetMyArgumentByVal;
514                         else
515                             op = GetMyArgumentByValOutOfBounds;
516                         result = insertionSet.insertNode(
517                             nodeIndex, node->prediction(), op, node->origin,
518                             node->child1(), node->child2());
519                     }
520                     
521                     // Need to do this because we may have a data format conversion here.
522                     node->convertToIdentityOn(result);
523                     break;
524                 }
525                     
526                 case LoadVarargs: {
527                     Node* candidate = node->child1().node();
528                     if (!m_candidates.contains(candidate))
529                         break;
530                     
531                     LoadVarargsData* varargsData = node->loadVarargsData();
532                     InlineCallFrame* inlineCallFrame = candidate->origin.semantic.inlineCallFrame;
533                     if (inlineCallFrame
534                         && !inlineCallFrame->isVarargs()
535                         && inlineCallFrame->arguments.size() - varargsData->offset <= varargsData->limit) {
536                         
537                         // LoadVarargs can exit, so it better be exitOK.
538                         DFG_ASSERT(m_graph, node, node->origin.exitOK);
539                         bool canExit = true;
540                         
541                         Node* argumentCount = insertionSet.insertConstant(
542                             nodeIndex, node->origin.withExitOK(canExit),
543                             jsNumber(inlineCallFrame->arguments.size() - varargsData->offset));
544                         insertionSet.insertNode(
545                             nodeIndex, SpecNone, MovHint, node->origin.takeValidExit(canExit),
546                             OpInfo(varargsData->count.offset()), Edge(argumentCount));
547                         insertionSet.insertNode(
548                             nodeIndex, SpecNone, PutStack, node->origin.withExitOK(canExit),
549                             OpInfo(m_graph.m_stackAccessData.add(varargsData->count, FlushedInt32)),
550                             Edge(argumentCount, KnownInt32Use));
551                         
552                         DFG_ASSERT(m_graph, node, varargsData->limit - 1 >= varargsData->mandatoryMinimum);
553                         // Define our limit to not include "this", since that's a bit easier to reason about.
554                         unsigned limit = varargsData->limit - 1;
555                         Node* undefined = nullptr;
556                         for (unsigned storeIndex = 0; storeIndex < limit; ++storeIndex) {
557                             // First determine if we have an element we can load, and load it if
558                             // possible.
559                             
560                             unsigned loadIndex = storeIndex + varargsData->offset;
561                             
562                             Node* value;
563                             if (loadIndex + 1 < inlineCallFrame->arguments.size()) {
564                                 VirtualRegister reg =
565                                     virtualRegisterForArgument(loadIndex + 1) +
566                                     inlineCallFrame->stackOffset;
567                                 StackAccessData* data = m_graph.m_stackAccessData.add(
568                                     reg, FlushedJSValue);
569                                 
570                                 value = insertionSet.insertNode(
571                                     nodeIndex, SpecNone, GetStack, node->origin.withExitOK(canExit),
572                                     OpInfo(data));
573                             } else {
574                                 // FIXME: We shouldn't have to store anything if
575                                 // storeIndex >= varargsData->mandatoryMinimum, but we will still
576                                 // have GetStacks in that range. So if we don't do the stores, we'll
577                                 // have degenerate IR: we'll have GetStacks of something that didn't
578                                 // have PutStacks.
579                                 // https://bugs.webkit.org/show_bug.cgi?id=147434
580                                 
581                                 if (!undefined) {
582                                     undefined = insertionSet.insertConstant(
583                                         nodeIndex, node->origin.withExitOK(canExit), jsUndefined());
584                                 }
585                                 value = undefined;
586                             }
587                             
588                             // Now that we have a value, store it.
589                             
590                             VirtualRegister reg = varargsData->start + storeIndex;
591                             StackAccessData* data =
592                                 m_graph.m_stackAccessData.add(reg, FlushedJSValue);
593                             
594                             insertionSet.insertNode(
595                                 nodeIndex, SpecNone, MovHint, node->origin.takeValidExit(canExit),
596                                 OpInfo(reg.offset()), Edge(value));
597                             insertionSet.insertNode(
598                                 nodeIndex, SpecNone, PutStack, node->origin.withExitOK(canExit),
599                                 OpInfo(data), Edge(value));
600                         }
601                         
602                         node->remove();
603                         node->origin.exitOK = canExit;
604                         break;
605                     }
606                     
607                     node->setOpAndDefaultFlags(ForwardVarargs);
608                     break;
609                 }
610                     
611                 case CallVarargs:
612                 case ConstructVarargs:
613                 case TailCallVarargs:
614                 case TailCallVarargsInlinedCaller: {
615                     Node* candidate = node->child2().node();
616                     if (!m_candidates.contains(candidate))
617                         break;
618                     
619                     CallVarargsData* varargsData = node->callVarargsData();
620                     InlineCallFrame* inlineCallFrame = candidate->origin.semantic.inlineCallFrame;
621                     if (inlineCallFrame && !inlineCallFrame->isVarargs()) {
622                         Vector<Node*> arguments;
623                         for (unsigned i = 1 + varargsData->firstVarArgOffset; i < inlineCallFrame->arguments.size(); ++i) {
624                             StackAccessData* data = m_graph.m_stackAccessData.add(
625                                 virtualRegisterForArgument(i) + inlineCallFrame->stackOffset,
626                                 FlushedJSValue);
627                             
628                             Node* value = insertionSet.insertNode(
629                                 nodeIndex, SpecNone, GetStack, node->origin, OpInfo(data));
630                             
631                             arguments.append(value);
632                         }
633                         
634                         unsigned firstChild = m_graph.m_varArgChildren.size();
635                         m_graph.m_varArgChildren.append(node->child1());
636                         m_graph.m_varArgChildren.append(node->child3());
637                         for (Node* argument : arguments)
638                             m_graph.m_varArgChildren.append(Edge(argument));
639                         switch (node->op()) {
640                         case CallVarargs:
641                             node->setOpAndDefaultFlags(Call);
642                             break;
643                         case ConstructVarargs:
644                             node->setOpAndDefaultFlags(Construct);
645                             break;
646                         case TailCallVarargs:
647                             node->setOpAndDefaultFlags(TailCall);
648                             break;
649                         case TailCallVarargsInlinedCaller:
650                             node->setOpAndDefaultFlags(TailCallInlinedCaller);
651                             break;
652                         default:
653                             RELEASE_ASSERT_NOT_REACHED();
654                         }
655                         node->children = AdjacencyList(
656                             AdjacencyList::Variable,
657                             firstChild, m_graph.m_varArgChildren.size() - firstChild);
658                         break;
659                     }
660                     
661                     switch (node->op()) {
662                     case CallVarargs:
663                         node->setOpAndDefaultFlags(CallForwardVarargs);
664                         break;
665                     case ConstructVarargs:
666                         node->setOpAndDefaultFlags(ConstructForwardVarargs);
667                         break;
668                     case TailCallVarargs:
669                         node->setOpAndDefaultFlags(TailCallForwardVarargs);
670                         break;
671                     case TailCallVarargsInlinedCaller:
672                         node->setOpAndDefaultFlags(TailCallForwardVarargsInlinedCaller);
673                         break;
674                     default:
675                         RELEASE_ASSERT_NOT_REACHED();
676                     }
677                     break;
678                 }
679                     
680                 case CheckArray:
681                 case GetButterfly: {
682                     if (!m_candidates.contains(node->child1().node()))
683                         break;
684                     node->remove();
685                     break;
686                 }
687                     
688                 default:
689                     break;
690                 }
691             }
692             
693             insertionSet.execute(block);
694         }
695     }
696     
697     HashSet<Node*> m_candidates;
698 };
699
700 } // anonymous namespace
701
702 bool performArgumentsElimination(Graph& graph)
703 {
704     return runPhase<ArgumentsEliminationPhase>(graph);
705 }
706
707 } } // namespace JSC::DFG
708
709 #endif // ENABLE(DFG_JIT)
710