]> git.saurik.com Git - apple/javascriptcore.git/blame - dfg/DFGArgumentsSimplificationPhase.cpp
JavaScriptCore-1218.tar.gz
[apple/javascriptcore.git] / dfg / DFGArgumentsSimplificationPhase.cpp
CommitLineData
93a37866
A
1/*
2 * Copyright (C) 2012, 2013 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 "DFGArgumentsSimplificationPhase.h"
28
29#if ENABLE(DFG_JIT)
30
31#include "DFGAbstractState.h"
32#include "DFGBasicBlock.h"
33#include "DFGGraph.h"
34#include "DFGInsertionSet.h"
35#include "DFGPhase.h"
36#include "DFGValidate.h"
37#include "DFGVariableAccessDataDump.h"
38#include <wtf/HashSet.h>
39#include <wtf/HashMap.h>
40
41#if PLATFORM(IOS)
42#include "Operations.h"
43#endif
44
45namespace JSC { namespace DFG {
46
47namespace {
48
49struct ArgumentsAliasingData {
50 InlineCallFrame* callContext;
51 bool callContextSet;
52 bool multipleCallContexts;
53
54 bool assignedFromArguments;
55 bool assignedFromManyThings;
56
57 bool escapes;
58
59 ArgumentsAliasingData()
60 : callContext(0)
61 , callContextSet(false)
62 , multipleCallContexts(false)
63 , assignedFromArguments(false)
64 , assignedFromManyThings(false)
65 , escapes(false)
66 {
67 }
68
69 void mergeCallContext(InlineCallFrame* newCallContext)
70 {
71 if (multipleCallContexts)
72 return;
73
74 if (!callContextSet) {
75 callContext = newCallContext;
76 callContextSet = true;
77 return;
78 }
79
80 if (callContext == newCallContext)
81 return;
82
83 multipleCallContexts = true;
84 }
85
86 bool callContextIsValid()
87 {
88 return callContextSet && !multipleCallContexts;
89 }
90
91 void mergeArgumentsAssignment()
92 {
93 assignedFromArguments = true;
94 }
95
96 void mergeNonArgumentsAssignment()
97 {
98 assignedFromManyThings = true;
99 }
100
101 bool argumentsAssignmentIsValid()
102 {
103 return assignedFromArguments && !assignedFromManyThings;
104 }
105
106 bool isValid()
107 {
108 return callContextIsValid() && argumentsAssignmentIsValid() && !escapes;
109 }
110};
111
112} // end anonymous namespace
113
114class ArgumentsSimplificationPhase : public Phase {
115public:
116 ArgumentsSimplificationPhase(Graph& graph)
117 : Phase(graph, "arguments simplification")
118 {
119 }
120
121 bool run()
122 {
123 if (!m_graph.m_hasArguments)
124 return false;
125
126 bool changed = false;
127
128 // Record which arguments are known to escape no matter what.
129 for (unsigned i = codeBlock()->inlineCallFrames().size(); i--;) {
130 InlineCallFrame* inlineCallFrame = &codeBlock()->inlineCallFrames()[i];
131 if (m_graph.m_executablesWhoseArgumentsEscaped.contains(
132 m_graph.executableFor(inlineCallFrame)))
133 m_createsArguments.add(inlineCallFrame);
134 }
135
136 // Create data for variable access datas that we will want to analyze.
137 for (unsigned i = m_graph.m_variableAccessData.size(); i--;) {
138 VariableAccessData* variableAccessData = &m_graph.m_variableAccessData[i];
139 if (!variableAccessData->isRoot())
140 continue;
141 if (variableAccessData->isCaptured())
142 continue;
143 m_argumentsAliasing.add(variableAccessData, ArgumentsAliasingData());
144 }
145
146 // Figure out which variables are live, using a conservative approximation of
147 // liveness.
148 for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
149 BasicBlock* block = m_graph.m_blocks[blockIndex].get();
150 if (!block)
151 continue;
152 for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
153 Node* node = block->at(indexInBlock);
154 switch (node->op()) {
155 case GetLocal:
156 case Flush:
157 case PhantomLocal:
158 m_isLive.add(node->variableAccessData());
159 break;
160 default:
161 break;
162 }
163 }
164 }
165
166 // Figure out which variables alias the arguments and nothing else, and are
167 // used only for GetByVal and GetArrayLength accesses. At the same time,
168 // identify uses of CreateArguments that are not consistent with the arguments
169 // being aliased only to variables that satisfy these constraints.
170 for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
171 BasicBlock* block = m_graph.m_blocks[blockIndex].get();
172 if (!block)
173 continue;
174 for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
175 Node* node = block->at(indexInBlock);
176 switch (node->op()) {
177 case CreateArguments: {
178 // Ignore this op. If we see a lone CreateArguments then we want to
179 // completely ignore it because:
180 // 1) The default would be to see that the child is a GetLocal on the
181 // arguments register and conclude that we have an arguments escape.
182 // 2) The fact that a CreateArguments exists does not mean that it
183 // will continue to exist after we're done with this phase. As far
184 // as this phase is concerned, a CreateArguments only "exists" if it
185 // is used in a manner that necessitates its existance.
186 break;
187 }
188
189 case TearOffArguments: {
190 // Ignore arguments tear off, because it's only relevant if we actually
191 // need to create the arguments.
192 break;
193 }
194
195 case SetLocal: {
196 Node* source = node->child1().node();
197 VariableAccessData* variableAccessData = node->variableAccessData();
198 int argumentsRegister =
199 m_graph.uncheckedArgumentsRegisterFor(node->codeOrigin);
200 if (source->op() != CreateArguments && source->op() != PhantomArguments) {
201 // Make sure that the source of the SetLocal knows that if it's
202 // a variable that we think is aliased to the arguments, then it
203 // may escape at this point. In future, we could track transitive
204 // aliasing. But not yet.
205 observeBadArgumentsUse(source);
206
207 // If this is an assignment to the arguments register, then
208 // pretend as if the arguments were created. We don't want to
209 // optimize code that explicitly assigns to the arguments,
210 // because that seems too ugly.
211
212 // But, before getting rid of CreateArguments, we will have
213 // an assignment to the arguments registers with JSValue().
214 // That's because CSE will refuse to get rid of the
215 // init_lazy_reg since it treats CreateArguments as reading
216 // local variables. That could be fixed, but it's easier to
217 // work around this here.
218 if (source->op() == JSConstant
219 && !source->valueOfJSConstant(codeBlock()))
220 break;
221
222 // If the variable is totally dead, then ignore it.
223 if (!m_isLive.contains(variableAccessData))
224 break;
225
226 if (argumentsRegister != InvalidVirtualRegister
227 && (variableAccessData->local() == argumentsRegister
228 || variableAccessData->local() == unmodifiedArgumentsRegister(argumentsRegister))) {
229 m_createsArguments.add(node->codeOrigin.inlineCallFrame);
230 break;
231 }
232
233 if (variableAccessData->isCaptured())
234 break;
235
236 // Make sure that if it's a variable that we think is aliased to
237 // the arguments, that we know that it might actually not be.
238 ArgumentsAliasingData& data =
239 m_argumentsAliasing.find(variableAccessData)->value;
240 data.mergeNonArgumentsAssignment();
241 data.mergeCallContext(node->codeOrigin.inlineCallFrame);
242 break;
243 }
244 if (argumentsRegister != InvalidVirtualRegister
245 && (variableAccessData->local() == argumentsRegister
246 || variableAccessData->local() == unmodifiedArgumentsRegister(argumentsRegister))) {
247 if (node->codeOrigin.inlineCallFrame == source->codeOrigin.inlineCallFrame)
248 break;
249 m_createsArguments.add(source->codeOrigin.inlineCallFrame);
250 break;
251 }
252 if (variableAccessData->isCaptured()) {
253 m_createsArguments.add(source->codeOrigin.inlineCallFrame);
254 break;
255 }
256 ArgumentsAliasingData& data =
257 m_argumentsAliasing.find(variableAccessData)->value;
258 data.mergeArgumentsAssignment();
259 // This ensures that the variable's uses are in the same context as
260 // the arguments it is aliasing.
261 data.mergeCallContext(node->codeOrigin.inlineCallFrame);
262 data.mergeCallContext(source->codeOrigin.inlineCallFrame);
263 break;
264 }
265
266 case GetLocal:
267 case Phi: /* FIXME: https://bugs.webkit.org/show_bug.cgi?id=108555 */ {
268 VariableAccessData* variableAccessData = node->variableAccessData();
269 if (variableAccessData->isCaptured())
270 break;
271 ArgumentsAliasingData& data =
272 m_argumentsAliasing.find(variableAccessData)->value;
273 data.mergeCallContext(node->codeOrigin.inlineCallFrame);
274 break;
275 }
276
277 case Flush: {
278 VariableAccessData* variableAccessData = node->variableAccessData();
279 if (variableAccessData->isCaptured())
280 break;
281 ArgumentsAliasingData& data =
282 m_argumentsAliasing.find(variableAccessData)->value;
283 data.mergeCallContext(node->codeOrigin.inlineCallFrame);
284
285 // If a variable is used in a flush then by definition it escapes.
286 data.escapes = true;
287 break;
288 }
289
290 case SetArgument: {
291 VariableAccessData* variableAccessData = node->variableAccessData();
292 if (variableAccessData->isCaptured())
293 break;
294 ArgumentsAliasingData& data =
295 m_argumentsAliasing.find(variableAccessData)->value;
296 data.mergeNonArgumentsAssignment();
297 data.mergeCallContext(node->codeOrigin.inlineCallFrame);
298 break;
299 }
300
301 case GetByVal: {
302 if (node->arrayMode().type() != Array::Arguments) {
303 observeBadArgumentsUses(node);
304 break;
305 }
306
307 // That's so awful and pretty much impossible since it would
308 // imply that the arguments were predicted integer, but it's
309 // good to be defensive and thorough.
310 observeBadArgumentsUse(node->child2().node());
311 observeProperArgumentsUse(node, node->child1());
312 break;
313 }
314
315 case GetArrayLength: {
316 if (node->arrayMode().type() != Array::Arguments) {
317 observeBadArgumentsUses(node);
318 break;
319 }
320
321 observeProperArgumentsUse(node, node->child1());
322 break;
323 }
324
325 case Phantom:
326 // We don't care about phantom uses, since phantom uses are all about
327 // just keeping things alive for OSR exit. If something - like the
328 // CreateArguments - is just being kept alive, then this transformation
329 // will not break this, since the Phantom will now just keep alive a
330 // PhantomArguments and OSR exit will still do the right things.
331 break;
332
333 case CheckStructure:
334 case ForwardCheckStructure:
335 case StructureTransitionWatchpoint:
336 case ForwardStructureTransitionWatchpoint:
337 case CheckArray:
338 // We don't care about these because if we get uses of the relevant
339 // variable then we can safely get rid of these, too. This of course
340 // relies on there not being any information transferred by the CFA
341 // from a CheckStructure on one variable to the information about the
342 // structures of another variable.
343 break;
344
345 default:
346 observeBadArgumentsUses(node);
347 break;
348 }
349 }
350 }
351
352 // Now we know which variables are aliased to arguments. But if any of them are
353 // found to have escaped, or were otherwise invalidated, then we need to mark
354 // the arguments as requiring creation. This is a property of SetLocals to
355 // variables that are neither the correct arguments register nor are marked as
356 // being arguments-aliased.
357 for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
358 BasicBlock* block = m_graph.m_blocks[blockIndex].get();
359 if (!block)
360 continue;
361 for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
362 Node* node = block->at(indexInBlock);
363 if (node->op() != SetLocal)
364 continue;
365 Node* source = node->child1().node();
366 if (source->op() != CreateArguments)
367 continue;
368 VariableAccessData* variableAccessData = node->variableAccessData();
369 if (variableAccessData->isCaptured()) {
370 // The captured case would have already been taken care of in the
371 // previous pass.
372 continue;
373 }
374
375 ArgumentsAliasingData& data =
376 m_argumentsAliasing.find(variableAccessData)->value;
377 if (data.isValid())
378 continue;
379
380 m_createsArguments.add(source->codeOrigin.inlineCallFrame);
381 }
382 }
383
384#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
385 dataLogF("Arguments aliasing states:\n");
386 for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) {
387 VariableAccessData* variableAccessData = &m_graph.m_variableAccessData[i];
388 if (!variableAccessData->isRoot())
389 continue;
390 dataLog(" r", variableAccessData->local(), "(", VariableAccessDataDump(m_graph, variableAccessData), "): ");
391 if (variableAccessData->isCaptured())
392 dataLogF("Captured");
393 else {
394 ArgumentsAliasingData& data =
395 m_argumentsAliasing.find(variableAccessData)->value;
396 bool first = true;
397 if (data.callContextIsValid()) {
398 if (!first)
399 dataLogF(", ");
400 dataLogF("Have Call Context: %p", data.callContext);
401 first = false;
402 if (!m_createsArguments.contains(data.callContext))
403 dataLogF(" (Does Not Create Arguments)");
404 }
405 if (data.argumentsAssignmentIsValid()) {
406 if (!first)
407 dataLogF(", ");
408 dataLogF("Arguments Assignment Is Valid");
409 first = false;
410 }
411 if (!data.escapes) {
412 if (!first)
413 dataLogF(", ");
414 dataLogF("Does Not Escape");
415 first = false;
416 }
417 if (!first)
418 dataLogF(", ");
419 if (data.isValid()) {
420 if (m_createsArguments.contains(data.callContext))
421 dataLogF("VALID");
422 else
423 dataLogF("INVALID (due to argument creation)");
424 } else
425 dataLogF("INVALID (due to bad variable use)");
426 }
427 dataLogF("\n");
428 }
429#endif
430
431 InsertionSet insertionSet(m_graph);
432
433 for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
434 BasicBlock* block = m_graph.m_blocks[blockIndex].get();
435 if (!block)
436 continue;
437 for (unsigned indexInBlock = 0; indexInBlock < block->size(); indexInBlock++) {
438 Node* node = block->at(indexInBlock);
439 switch (node->op()) {
440 case SetLocal: {
441 Node* source = node->child1().node();
442 if (source->op() != CreateArguments)
443 break;
444
445 if (m_createsArguments.contains(source->codeOrigin.inlineCallFrame))
446 break;
447
448 VariableAccessData* variableAccessData = node->variableAccessData();
449
450 if (m_graph.argumentsRegisterFor(node->codeOrigin) == variableAccessData->local()
451 || unmodifiedArgumentsRegister(m_graph.argumentsRegisterFor(node->codeOrigin)) == variableAccessData->local())
452 break;
453
454 ASSERT(!variableAccessData->isCaptured());
455
456 // If this is a store into a VariableAccessData* that is marked as
457 // arguments aliasing for an InlineCallFrame* that does not create
458 // arguments, then flag the VariableAccessData as being an
459 // arguments-aliased. This'll let the OSR exit machinery do the right
460 // things. Note also that the SetLocal should become dead as soon as
461 // we replace all uses of this variable with GetMyArgumentsLength and
462 // GetMyArgumentByVal.
463 ASSERT(m_argumentsAliasing.find(variableAccessData)->value.isValid());
464 if (variableAccessData->mergeIsArgumentsAlias(true)) {
465 changed = true;
466
467 // Make sure that the variable knows, that it may now hold non-cell values.
468 variableAccessData->predict(SpecEmpty);
469 }
470
471 // Make sure that the SetLocal doesn't check that the input is a Cell.
472 if (node->child1().useKind() != UntypedUse) {
473 node->child1().setUseKind(UntypedUse);
474 changed = true;
475 }
476 break;
477 }
478
479 case PhantomLocal: {
480 VariableAccessData* variableAccessData = node->variableAccessData();
481
482 if (variableAccessData->isCaptured()
483 || !m_argumentsAliasing.find(variableAccessData)->value.isValid()
484 || m_createsArguments.contains(node->codeOrigin.inlineCallFrame))
485 break;
486
487 // Turn PhantomLocals into just GetLocals. This will preserve the threading
488 // of the local through to this point, but will allow it to die, causing
489 // only OSR to know about it.
490
491 node->setOpAndDefaultFlags(GetLocal);
492 break;
493 }
494
495 case Flush: {
496 VariableAccessData* variableAccessData = node->variableAccessData();
497
498 if (variableAccessData->isCaptured()
499 || !m_argumentsAliasing.find(variableAccessData)->value.isValid()
500 || m_createsArguments.contains(node->codeOrigin.inlineCallFrame))
501 break;
502
503 RELEASE_ASSERT_NOT_REACHED();
504 break;
505 }
506
507 case Phantom: {
508 // It's highly likely that we will have a Phantom referencing either
509 // CreateArguments, or a local op for the arguments register, or a
510 // local op for an arguments-aliased variable. In any of those cases,
511 // we should remove the phantom reference, since:
512 // 1) Phantoms only exist to aid OSR exit. But arguments simplification
513 // has its own OSR exit story, which is to inform OSR exit to reify
514 // the arguments as necessary.
515 // 2) The Phantom may keep the CreateArguments node alive, which is
516 // precisely what we don't want.
517 for (unsigned i = 0; i < AdjacencyList::Size; ++i)
518 removeArgumentsReferencingPhantomChild(node, i);
519 break;
520 }
521
522 case CheckStructure:
523 case ForwardCheckStructure:
524 case StructureTransitionWatchpoint:
525 case ForwardStructureTransitionWatchpoint:
526 case CheckArray: {
527 // We can just get rid of this node, if it references a phantom argument.
528 if (!isOKToOptimize(node->child1().node()))
529 break;
530 node->convertToPhantom();
531 node->children.setChild1(Edge());
532 break;
533 }
534
535 case GetByVal: {
536 if (node->arrayMode().type() != Array::Arguments)
537 break;
538
539 // This can be simplified to GetMyArgumentByVal if we know that
540 // it satisfies either condition (1) or (2):
541 // 1) Its first child is a valid ArgumentsAliasingData and the
542 // InlineCallFrame* is not marked as creating arguments.
543 // 2) Its first child is CreateArguments and its InlineCallFrame*
544 // is not marked as creating arguments.
545
546 if (!isOKToOptimize(node->child1().node()))
547 break;
548
549 node->children.child1() = node->children.child2();
550 node->children.child2() = Edge();
551 node->setOpAndDefaultFlags(GetMyArgumentByVal);
552 changed = true;
553 --indexInBlock; // Force reconsideration of this op now that it's a GetMyArgumentByVal.
554 break;
555 }
556
557 case GetArrayLength: {
558 if (node->arrayMode().type() != Array::Arguments)
559 break;
560
561 if (!isOKToOptimize(node->child1().node()))
562 break;
563
564 node->children.child1() = Edge();
565 node->setOpAndDefaultFlags(GetMyArgumentsLength);
566 changed = true;
567 --indexInBlock; // Force reconsideration of this op noew that it's a GetMyArgumentsLength.
568 break;
569 }
570
571 case GetMyArgumentsLength:
572 case GetMyArgumentsLengthSafe: {
573 if (m_createsArguments.contains(node->codeOrigin.inlineCallFrame)) {
574 ASSERT(node->op() == GetMyArgumentsLengthSafe);
575 break;
576 }
577 if (node->op() == GetMyArgumentsLengthSafe) {
578 node->setOp(GetMyArgumentsLength);
579 changed = true;
580 }
581
582 CodeOrigin codeOrigin = node->codeOrigin;
583 if (!codeOrigin.inlineCallFrame)
584 break;
585
586 // We know exactly what this will return. But only after we have checked
587 // that nobody has escaped our arguments.
588 insertionSet.insertNode(
589 indexInBlock, SpecNone, CheckArgumentsNotCreated, codeOrigin);
590
591 m_graph.convertToConstant(
592 node, jsNumber(codeOrigin.inlineCallFrame->arguments.size() - 1));
593 changed = true;
594 break;
595 }
596
597 case GetMyArgumentByVal:
598 case GetMyArgumentByValSafe: {
599 if (m_createsArguments.contains(node->codeOrigin.inlineCallFrame)) {
600 ASSERT(node->op() == GetMyArgumentByValSafe);
601 break;
602 }
603 if (node->op() == GetMyArgumentByValSafe) {
604 node->setOp(GetMyArgumentByVal);
605 changed = true;
606 }
607 if (!node->codeOrigin.inlineCallFrame)
608 break;
609 if (!node->child1()->hasConstant())
610 break;
611 JSValue value = node->child1()->valueOfJSConstant(codeBlock());
612 if (!value.isInt32())
613 break;
614 int32_t index = value.asInt32();
615 if (index < 0
616 || static_cast<size_t>(index + 1) >=
617 node->codeOrigin.inlineCallFrame->arguments.size())
618 break;
619
620 // We know which argument this is accessing. But only after we have checked
621 // that nobody has escaped our arguments. We also need to ensure that the
622 // index is kept alive. That's somewhat pointless since it's a constant, but
623 // it's important because this is one of those invariants that we like to
624 // have in the DFG. Note finally that we use the GetLocalUnlinked opcode
625 // here, since this is being done _after_ the prediction propagation phase
626 // has run - therefore it makes little sense to link the GetLocal operation
627 // into the VariableAccessData and Phi graphs.
628
629 CodeOrigin codeOrigin = node->codeOrigin;
630 AdjacencyList children = node->children;
631
632 node->convertToGetLocalUnlinked(
633 static_cast<VirtualRegister>(
634 node->codeOrigin.inlineCallFrame->stackOffset +
635 m_graph.baselineCodeBlockFor(node->codeOrigin)->argumentIndexAfterCapture(index)));
636
637 insertionSet.insertNode(
638 indexInBlock, SpecNone, CheckArgumentsNotCreated,
639 codeOrigin);
640 insertionSet.insertNode(
641 indexInBlock, SpecNone, Phantom, codeOrigin,
642 children);
643
644 changed = true;
645 break;
646 }
647
648 case TearOffArguments: {
649 if (m_createsArguments.contains(node->codeOrigin.inlineCallFrame))
650 continue;
651
652 node->setOpAndDefaultFlags(Nop);
653 m_graph.clearAndDerefChild1(node);
654 m_graph.clearAndDerefChild2(node);
655 break;
656 }
657
658 default:
659 break;
660 }
661 }
662 insertionSet.execute(block);
663 }
664
665 for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
666 BasicBlock* block = m_graph.m_blocks[blockIndex].get();
667 if (!block)
668 continue;
669 for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
670 Node* node = block->at(indexInBlock);
671 if (node->op() != CreateArguments)
672 continue;
673 // If this is a CreateArguments for an InlineCallFrame* that does
674 // not create arguments, then replace it with a PhantomArguments.
675 // PhantomArguments is a non-executing node that just indicates
676 // that the node should be reified as an arguments object on OSR
677 // exit.
678 if (m_createsArguments.contains(node->codeOrigin.inlineCallFrame))
679 continue;
680 insertionSet.insertNode(
681 indexInBlock, SpecNone, Phantom, node->codeOrigin, node->children);
682 node->setOpAndDefaultFlags(PhantomArguments);
683 node->children.reset();
684 changed = true;
685 }
686 insertionSet.execute(block);
687 }
688
689 if (changed) {
690 m_graph.dethread();
691 m_graph.m_form = LoadStore;
692 }
693
694 return changed;
695 }
696
697private:
698 HashSet<InlineCallFrame*,
699 DefaultHash<InlineCallFrame*>::Hash,
700 NullableHashTraits<InlineCallFrame*> > m_createsArguments;
701 HashMap<VariableAccessData*, ArgumentsAliasingData,
702 DefaultHash<VariableAccessData*>::Hash,
703 NullableHashTraits<VariableAccessData*> > m_argumentsAliasing;
704 HashSet<VariableAccessData*> m_isLive;
705
706 void observeBadArgumentsUse(Node* node)
707 {
708 if (!node)
709 return;
710
711 switch (node->op()) {
712 case CreateArguments: {
713 m_createsArguments.add(node->codeOrigin.inlineCallFrame);
714 break;
715 }
716
717 case GetLocal: {
718 int argumentsRegister = m_graph.uncheckedArgumentsRegisterFor(node->codeOrigin);
719 if (argumentsRegister != InvalidVirtualRegister
720 && (node->local() == argumentsRegister
721 || node->local() == unmodifiedArgumentsRegister(argumentsRegister))) {
722 m_createsArguments.add(node->codeOrigin.inlineCallFrame);
723 break;
724 }
725
726 VariableAccessData* variableAccessData = node->variableAccessData();
727 if (variableAccessData->isCaptured())
728 break;
729
730 ArgumentsAliasingData& data = m_argumentsAliasing.find(variableAccessData)->value;
731 data.escapes = true;
732 break;
733 }
734
735 default:
736 break;
737 }
738 }
739
740 void observeBadArgumentsUses(Node* node)
741 {
742 for (unsigned i = m_graph.numChildren(node); i--;)
743 observeBadArgumentsUse(m_graph.child(node, i).node());
744 }
745
746 void observeProperArgumentsUse(Node* node, Edge edge)
747 {
748 if (edge->op() != GetLocal) {
749 // When can this happen? At least two cases that I can think
750 // of:
751 //
752 // 1) Aliased use of arguments in the same basic block,
753 // like:
754 //
755 // var a = arguments;
756 // var x = arguments[i];
757 //
758 // 2) If we're accessing arguments we got from the heap!
759
760 if (edge->op() == CreateArguments
761 && node->codeOrigin.inlineCallFrame
762 != edge->codeOrigin.inlineCallFrame)
763 m_createsArguments.add(edge->codeOrigin.inlineCallFrame);
764
765 return;
766 }
767
768 VariableAccessData* variableAccessData = edge->variableAccessData();
769 if (edge->local() == m_graph.uncheckedArgumentsRegisterFor(edge->codeOrigin)
770 && node->codeOrigin.inlineCallFrame != edge->codeOrigin.inlineCallFrame) {
771 m_createsArguments.add(edge->codeOrigin.inlineCallFrame);
772 return;
773 }
774
775 if (variableAccessData->isCaptured())
776 return;
777
778 ArgumentsAliasingData& data = m_argumentsAliasing.find(variableAccessData)->value;
779 data.mergeCallContext(node->codeOrigin.inlineCallFrame);
780 }
781
782 bool isOKToOptimize(Node* source)
783 {
784 if (m_createsArguments.contains(source->codeOrigin.inlineCallFrame))
785 return false;
786
787 switch (source->op()) {
788 case GetLocal: {
789 VariableAccessData* variableAccessData = source->variableAccessData();
790 int argumentsRegister = m_graph.uncheckedArgumentsRegisterFor(source->codeOrigin);
791 if (argumentsRegister == InvalidVirtualRegister)
792 break;
793 if (argumentsRegister == variableAccessData->local())
794 return true;
795 if (unmodifiedArgumentsRegister(argumentsRegister) == variableAccessData->local())
796 return true;
797 if (variableAccessData->isCaptured())
798 break;
799 ArgumentsAliasingData& data =
800 m_argumentsAliasing.find(variableAccessData)->value;
801 if (!data.isValid())
802 break;
803
804 return true;
805 }
806
807 case CreateArguments: {
808 return true;
809 }
810
811 default:
812 break;
813 }
814
815 return false;
816 }
817
818 void removeArgumentsReferencingPhantomChild(Node* node, unsigned edgeIndex)
819 {
820 Edge edge = node->children.child(edgeIndex);
821 if (!edge)
822 return;
823
824 switch (edge->op()) {
825 case Phi: // Arises if we had CSE on a GetLocal of the arguments register.
826 case GetLocal: // Arises if we had CSE on an arguments access to a variable aliased to the arguments.
827 case SetLocal: { // Arises if we had CSE on a GetLocal of the arguments register.
828 VariableAccessData* variableAccessData = edge->variableAccessData();
829 bool isDeadArgumentsRegister =
830 variableAccessData->local() ==
831 m_graph.uncheckedArgumentsRegisterFor(edge->codeOrigin)
832 && !m_createsArguments.contains(edge->codeOrigin.inlineCallFrame);
833 bool isAliasedArgumentsRegister =
834 !variableAccessData->isCaptured()
835 && m_argumentsAliasing.find(variableAccessData)->value.isValid()
836 && !m_createsArguments.contains(edge->codeOrigin.inlineCallFrame);
837 if (!isDeadArgumentsRegister && !isAliasedArgumentsRegister)
838 break;
839 node->children.removeEdge(edgeIndex);
840 break;
841 }
842
843 case CreateArguments: { // Arises if we CSE two GetLocals to the arguments register and then CSE the second use of the GetLocal to the first.
844 if (m_createsArguments.contains(edge->codeOrigin.inlineCallFrame))
845 break;
846 node->children.removeEdge(edgeIndex);
847 break;
848 }
849
850 default:
851 break;
852 }
853 }
854};
855
856bool performArgumentsSimplification(Graph& graph)
857{
858 SamplingRegion samplingRegion("DFG Arguments Simplification Phase");
859 return runPhase<ArgumentsSimplificationPhase>(graph);
860}
861
862} } // namespace JSC::DFG
863
864#endif // ENABLE(DFG_JIT)
865
866