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