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 41namespace JSC { namespace DFG { 42 43namespace { 44 45struct 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 110class ArgumentsSimplificationPhase : public Phase { 111public: 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 630private: 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 787bool 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 798