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