ValueNumbering.cpp (47990B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "jit/ValueNumbering.h" 8 9 #include "jit/IonAnalysis.h" 10 #include "jit/JitSpewer.h" 11 #include "jit/MIRGenerator.h" 12 #include "jit/MIRGraph.h" 13 14 using namespace js; 15 using namespace js::jit; 16 17 /* 18 * [SMDOC] IonMonkey Value Numbering 19 * 20 * Some notes on the main algorithm here: 21 * - The SSA identifier id() is the value number. We do replaceAllUsesWith as 22 * we go, so there's always at most one visible value with a given number. 23 * 24 * - Consequently, the GVN algorithm is effectively pessimistic. This means it 25 * is not as powerful as an optimistic GVN would be, but it is simpler and 26 * faster. 27 * 28 * - We iterate in RPO, so that when visiting a block, we've already optimized 29 * and hashed all values in dominating blocks. With occasional exceptions, 30 * this allows us to do everything in a single pass. 31 * 32 * - When we do use multiple passes, we just re-run the algorithm on the whole 33 * graph instead of doing sparse propagation. This is a tradeoff to keep the 34 * algorithm simpler and lighter on inputs that don't have a lot of 35 * interesting unreachable blocks or degenerate loop induction variables, at 36 * the expense of being slower on inputs that do. The loop for this always 37 * terminates, because it only iterates when code is or will be removed, so 38 * eventually it must stop iterating. 39 * 40 * - Values are not immediately removed from the hash set when they go out of 41 * scope. Instead, we check for dominance after a lookup. If the dominance 42 * check fails, the value is removed. 43 */ 44 45 HashNumber ValueNumberer::VisibleValues::ValueHasher::hash(Lookup ins) { 46 return ins->valueHash(); 47 } 48 49 // Test whether two MDefinitions are congruent. 50 bool ValueNumberer::VisibleValues::ValueHasher::match(Key k, Lookup l) { 51 // If one of the instructions depends on a store, and the other instruction 52 // does not depend on the same store, the instructions are not congruent. 53 if (k->dependency() != l->dependency()) { 54 return false; 55 } 56 57 bool congruent = 58 k->congruentTo(l); // Ask the values themselves what they think. 59 #ifdef JS_JITSPEW 60 if (congruent != l->congruentTo(k)) { 61 JitSpew( 62 JitSpew_GVN, 63 " congruentTo relation is not symmetric between %s%u and %s%u!!", 64 k->opName(), k->id(), l->opName(), l->id()); 65 } 66 #endif 67 return congruent; 68 } 69 70 void ValueNumberer::VisibleValues::ValueHasher::rekey(Key& k, Key newKey) { 71 k = newKey; 72 } 73 74 ValueNumberer::VisibleValues::VisibleValues(TempAllocator& alloc) 75 : set_(alloc) {} 76 77 // Look up the first entry for |def|. 78 ValueNumberer::VisibleValues::Ptr ValueNumberer::VisibleValues::findLeader( 79 const MDefinition* def) const { 80 return set_.lookup(def); 81 } 82 83 // Look up the first entry for |def|. 84 ValueNumberer::VisibleValues::AddPtr 85 ValueNumberer::VisibleValues::findLeaderForAdd(MDefinition* def) { 86 return set_.lookupForAdd(def); 87 } 88 89 // Insert a value into the set. 90 bool ValueNumberer::VisibleValues::add(AddPtr p, MDefinition* def) { 91 return set_.add(p, def); 92 } 93 94 // Insert a value onto the set overwriting any existing entry. 95 void ValueNumberer::VisibleValues::overwrite(AddPtr p, MDefinition* def) { 96 set_.replaceKey(p, def); 97 } 98 99 // |def| will be discarded, so remove it from any sets. 100 void ValueNumberer::VisibleValues::forget(const MDefinition* def) { 101 Ptr p = set_.lookup(def); 102 if (p && *p == def) { 103 set_.remove(p); 104 } 105 } 106 107 // Clear all state. 108 void ValueNumberer::VisibleValues::clear() { set_.clear(); } 109 110 #ifdef DEBUG 111 // Test whether |def| is in the set. 112 bool ValueNumberer::VisibleValues::has(const MDefinition* def) const { 113 Ptr p = set_.lookup(def); 114 return p && *p == def; 115 } 116 #endif 117 118 // Call MDefinition::justReplaceAllUsesWith, and add some GVN-specific asserts. 119 static void ReplaceAllUsesWith(MDefinition* from, MDefinition* to) { 120 MOZ_ASSERT(from != to, "GVN shouldn't try to replace a value with itself"); 121 MOZ_ASSERT(from->type() == to->type(), 122 "Def replacement has different MIR type"); 123 MOZ_ASSERT(!to->isDiscarded(), 124 "GVN replaces an instruction by a removed instruction"); 125 126 // Update the node's wasm ref type to the LUB of the two nodes being combined. 127 // This ensures that any type-based optimizations downstream remain correct. 128 to->setWasmRefType(wasm::MaybeRefType::leastUpperBound(from->wasmRefType(), 129 to->wasmRefType())); 130 131 // We don't need the extra setting of ImplicitlyUsed flags that the regular 132 // replaceAllUsesWith does because we do it ourselves. 133 from->justReplaceAllUsesWith(to); 134 } 135 136 // Test whether |succ| is a successor of |block|. 137 static bool HasSuccessor(const MControlInstruction* block, 138 const MBasicBlock* succ) { 139 for (size_t i = 0, e = block->numSuccessors(); i != e; ++i) { 140 if (block->getSuccessor(i) == succ) { 141 return true; 142 } 143 } 144 return false; 145 } 146 147 // Given a block which has had predecessors removed but is still reachable, test 148 // whether the block's new dominator will be closer than its old one and whether 149 // it will expose potential optimization opportunities. 150 static MBasicBlock* ComputeNewDominator(MBasicBlock* block, MBasicBlock* old) { 151 MBasicBlock* now = block->getPredecessor(0); 152 for (size_t i = 1, e = block->numPredecessors(); i < e; ++i) { 153 MBasicBlock* pred = block->getPredecessor(i); 154 // Note that dominators haven't been recomputed yet, so we have to check 155 // whether now dominates pred, not block. 156 while (!now->dominates(pred)) { 157 MBasicBlock* next = now->immediateDominator(); 158 if (next == old) { 159 return old; 160 } 161 if (next == now) { 162 MOZ_ASSERT(block == old, 163 "Non-self-dominating block became self-dominating"); 164 return block; 165 } 166 now = next; 167 } 168 } 169 MOZ_ASSERT(old != block || old != now, 170 "Missed self-dominating block staying self-dominating"); 171 return now; 172 } 173 174 // Test for any defs which look potentially interesting to GVN. 175 static bool BlockHasInterestingDefs(MBasicBlock* block) { 176 return !block->phisEmpty() || *block->begin() != block->lastIns(); 177 } 178 179 // Walk up the dominator tree from |block| to the root and test for any defs 180 // which look potentially interesting to GVN. 181 static bool ScanDominatorsForDefs(MBasicBlock* block) { 182 for (MBasicBlock* i = block;;) { 183 if (BlockHasInterestingDefs(block)) { 184 return true; 185 } 186 187 MBasicBlock* immediateDominator = i->immediateDominator(); 188 if (immediateDominator == i) { 189 break; 190 } 191 i = immediateDominator; 192 } 193 return false; 194 } 195 196 // Walk up the dominator tree from |now| to |old| and test for any defs which 197 // look potentially interesting to GVN. 198 static bool ScanDominatorsForDefs(MBasicBlock* now, MBasicBlock* old) { 199 MOZ_ASSERT(old->dominates(now), 200 "Refined dominator not dominated by old dominator"); 201 202 for (MBasicBlock* i = now; i != old; i = i->immediateDominator()) { 203 if (BlockHasInterestingDefs(i)) { 204 return true; 205 } 206 } 207 return false; 208 } 209 210 // Given a block which has had predecessors removed but is still reachable, test 211 // whether the block's new dominator will be closer than its old one and whether 212 // it will expose potential optimization opportunities. 213 static bool IsDominatorRefined(MBasicBlock* block) { 214 MBasicBlock* old = block->immediateDominator(); 215 MBasicBlock* now = ComputeNewDominator(block, old); 216 217 // If this block is just a goto and it doesn't dominate its destination, 218 // removing its predecessors won't refine the dominators of anything 219 // interesting. 220 MControlInstruction* control = block->lastIns(); 221 if (*block->begin() == control && block->phisEmpty() && control->isGoto() && 222 !block->dominates(control->toGoto()->target())) { 223 return false; 224 } 225 226 // We've computed block's new dominator. Test whether there are any 227 // newly-dominating definitions which look interesting. 228 if (block == old) { 229 return block != now && ScanDominatorsForDefs(now); 230 } 231 MOZ_ASSERT(block != now, "Non-self-dominating block became self-dominating"); 232 return ScanDominatorsForDefs(now, old); 233 } 234 235 // |def| has just had one of its users release it. If it's now dead, enqueue it 236 // for discarding, otherwise just make note of it. 237 bool ValueNumberer::handleUseReleased(MDefinition* def, 238 ImplicitUseOption implicitUseOption) { 239 if (IsDiscardable(def)) { 240 values_.forget(def); 241 if (!deadDefs_.append(def)) { 242 return false; 243 } 244 } else { 245 if (implicitUseOption == SetImplicitUse) { 246 def->setImplicitlyUsedUnchecked(); 247 } 248 } 249 return true; 250 } 251 252 // Discard |def| and anything in its use-def subtree which is no longer needed. 253 bool ValueNumberer::discardDefsRecursively(MDefinition* def, 254 AllowEffectful allowEffectful) { 255 MOZ_ASSERT(deadDefs_.empty(), "deadDefs_ not cleared"); 256 257 return discardDef(def, allowEffectful) && processDeadDefs(); 258 } 259 260 // Assuming |resume| is unreachable, release its operands. 261 // It might be nice to integrate this code with prepareForDiscard, however GVN 262 // needs it to call handleUseReleased so that it can observe when a definition 263 // becomes unused, so it isn't trivial to do. 264 bool ValueNumberer::releaseResumePointOperands(MResumePoint* resume) { 265 for (size_t i = 0, e = resume->numOperands(); i < e; ++i) { 266 if (!resume->hasOperand(i)) { 267 continue; 268 } 269 MDefinition* op = resume->getOperand(i); 270 resume->releaseOperand(i); 271 272 // We set the ImplicitlyUsed flag when removing resume point operands, 273 // because even though we may think we're certain that a particular 274 // branch might not be taken, the type information might be incomplete. 275 if (!handleUseReleased(op, SetImplicitUse)) { 276 return false; 277 } 278 } 279 return true; 280 } 281 282 // Assuming |phi| is dead, release and remove its operands. If an operand 283 // becomes dead, push it to the discard worklist. 284 bool ValueNumberer::releaseAndRemovePhiOperands(MPhi* phi) { 285 // MPhi saves operands in a vector so we iterate in reverse. 286 for (int o = phi->numOperands() - 1; o >= 0; --o) { 287 MDefinition* op = phi->getOperand(o); 288 phi->removeOperand(o); 289 if (!handleUseReleased(op, DontSetImplicitUse)) { 290 return false; 291 } 292 } 293 return true; 294 } 295 296 // Assuming |def| is dead, release its operands. If an operand becomes dead, 297 // push it to the discard worklist. 298 bool ValueNumberer::releaseOperands(MDefinition* def) { 299 for (size_t o = 0, e = def->numOperands(); o < e; ++o) { 300 MDefinition* op = def->getOperand(o); 301 def->releaseOperand(o); 302 if (!handleUseReleased(op, DontSetImplicitUse)) { 303 return false; 304 } 305 } 306 return true; 307 } 308 309 // Discard |def| and mine its operands for any subsequently dead defs. 310 bool ValueNumberer::discardDef(MDefinition* def, 311 AllowEffectful allowEffectful) { 312 #ifdef JS_JITSPEW 313 JitSpew(JitSpew_GVN, " Discarding %s %s%u", 314 def->block()->isMarked() ? "unreachable" : "dead", def->opName(), 315 def->id()); 316 #endif 317 #ifdef DEBUG 318 MOZ_ASSERT(def != nextDef_, "Invalidating the MDefinition iterator"); 319 if (def->block()->isMarked()) { 320 MOZ_ASSERT(!def->hasUses(), "Discarding def that still has uses"); 321 } else { 322 MOZ_ASSERT(allowEffectful == AllowEffectful::Yes 323 ? IsDiscardableAllowEffectful(def) 324 : IsDiscardable(def), 325 "Discarding non-discardable definition"); 326 MOZ_ASSERT(!values_.has(def), "Discarding a definition still in the set"); 327 } 328 #endif 329 330 MBasicBlock* block = def->block(); 331 if (def->isPhi()) { 332 MPhi* phi = def->toPhi(); 333 if (!releaseAndRemovePhiOperands(phi)) { 334 return false; 335 } 336 block->discardPhi(phi); 337 } else { 338 MInstruction* ins = def->toInstruction(); 339 if (MResumePoint* resume = ins->resumePoint()) { 340 if (!releaseResumePointOperands(resume)) { 341 return false; 342 } 343 } 344 if (!releaseOperands(ins)) { 345 return false; 346 } 347 block->discardIgnoreOperands(ins); 348 } 349 350 // If that was the last definition in the block, it can be safely removed 351 // from the graph. 352 if (block->phisEmpty() && block->begin() == block->end()) { 353 MOZ_ASSERT(block->isMarked(), 354 "Reachable block lacks at least a control instruction"); 355 356 // As a special case, don't remove a block which is a dominator tree 357 // root so that we don't invalidate the iterator in visitGraph. We'll 358 // check for this and remove it later. 359 if (block->immediateDominator() != block) { 360 JitSpew(JitSpew_GVN, " Block block%u is now empty; discarding", 361 block->id()); 362 graph_.removeBlock(block); 363 blocksRemoved_ = true; 364 } else { 365 JitSpew(JitSpew_GVN, 366 " Dominator root block%u is now empty; will discard later", 367 block->id()); 368 } 369 } 370 371 return true; 372 } 373 374 // Recursively discard all the defs on the deadDefs_ worklist. 375 bool ValueNumberer::processDeadDefs() { 376 MDefinition* nextDef = nextDef_; 377 while (!deadDefs_.empty()) { 378 MDefinition* def = deadDefs_.popCopy(); 379 380 // Don't invalidate the MDefinition iterator. This is what we're going 381 // to visit next, so we won't miss anything. 382 if (def == nextDef) { 383 continue; 384 } 385 386 if (!discardDef(def)) { 387 return false; 388 } 389 } 390 return true; 391 } 392 393 // Test whether |block|, which is a loop header, has any predecessors other than 394 // |loopPred|, the loop predecessor, which it doesn't dominate. 395 static bool hasNonDominatingPredecessor(MBasicBlock* block, 396 MBasicBlock* loopPred) { 397 MOZ_ASSERT(block->isLoopHeader()); 398 MOZ_ASSERT(block->loopPredecessor() == loopPred); 399 400 for (uint32_t i = 0, e = block->numPredecessors(); i < e; ++i) { 401 MBasicBlock* pred = block->getPredecessor(i); 402 if (pred != loopPred && !block->dominates(pred)) { 403 return true; 404 } 405 } 406 return false; 407 } 408 409 // A loop is about to be made reachable only through an OSR entry into one of 410 // its nested loops. Fix everything up. 411 bool ValueNumberer::fixupOSROnlyLoop(MBasicBlock* block) { 412 // Create an empty and unreachable(!) block which jumps to |block|. This 413 // allows |block| to remain marked as a loop header, so we don't have to 414 // worry about moving a different block into place as the new loop header, 415 // which is hard, especially if the OSR is into a nested loop. Doing all 416 // that would produce slightly more optimal code, but this is so 417 // extraordinarily rare that it isn't worth the complexity. 418 MBasicBlock* fake = MBasicBlock::NewFakeLoopPredecessor(graph_, block); 419 if (!fake) { 420 return false; 421 } 422 fake->setImmediateDominator(fake); 423 fake->addNumDominated(1); 424 fake->setDomIndex(fake->id()); 425 426 JitSpew(JitSpew_GVN, " Created fake block%u", fake->id()); 427 hasOSRFixups_ = true; 428 return true; 429 } 430 431 // Remove the CFG edge between |pred| and |block|, after releasing the phi 432 // operands on that edge and discarding any definitions consequently made dead. 433 bool ValueNumberer::removePredecessorAndDoDCE(MBasicBlock* block, 434 MBasicBlock* pred, 435 size_t predIndex) { 436 MOZ_ASSERT( 437 !block->isMarked(), 438 "Block marked unreachable should have predecessors removed already"); 439 440 // Before removing the predecessor edge, scan the phi operands for that edge 441 // for dead code before they get removed. 442 MOZ_ASSERT(nextDef_ == nullptr); 443 for (MPhiIterator iter(block->phisBegin()), end(block->phisEnd()); 444 iter != end;) { 445 MPhi* phi = *iter++; 446 MOZ_ASSERT(!values_.has(phi), 447 "Visited phi in block having predecessor removed"); 448 MOZ_ASSERT(!phi->isGuard()); 449 450 MDefinition* op = phi->getOperand(predIndex); 451 phi->removeOperand(predIndex); 452 453 nextDef_ = iter != end ? *iter : nullptr; 454 if (!handleUseReleased(op, DontSetImplicitUse) || !processDeadDefs()) { 455 return false; 456 } 457 458 // If |nextDef_| became dead while we had it pinned, advance the 459 // iterator and discard it now. 460 while (nextDef_ && !nextDef_->hasUses() && 461 !nextDef_->isGuardRangeBailouts()) { 462 phi = nextDef_->toPhi(); 463 iter++; 464 nextDef_ = iter != end ? *iter : nullptr; 465 if (!discardDefsRecursively(phi)) { 466 return false; 467 } 468 } 469 } 470 nextDef_ = nullptr; 471 472 block->removePredecessorWithoutPhiOperands(pred, predIndex); 473 return true; 474 } 475 476 // Remove the CFG edge between |pred| and |block|, and if this makes |block| 477 // unreachable, mark it so, and remove the rest of its incoming edges too. And 478 // discard any instructions made dead by the entailed release of any phi 479 // operands. 480 bool ValueNumberer::removePredecessorAndCleanUp(MBasicBlock* block, 481 MBasicBlock* pred) { 482 MOZ_ASSERT(!block->isMarked(), 483 "Removing predecessor on block already marked unreachable"); 484 485 // We'll be removing a predecessor, so anything we know about phis in this 486 // block will be wrong. 487 for (MPhiIterator iter(block->phisBegin()), end(block->phisEnd()); 488 iter != end; ++iter) { 489 values_.forget(*iter); 490 } 491 492 // If this is a loop header, test whether it will become an unreachable 493 // loop, or whether it needs special OSR-related fixups. 494 bool isUnreachableLoop = false; 495 if (block->isLoopHeader()) { 496 if (block->loopPredecessor() == pred) { 497 if (MOZ_UNLIKELY(hasNonDominatingPredecessor(block, pred))) { 498 JitSpew(JitSpew_GVN, 499 " " 500 "Loop with header block%u is now only reachable through an " 501 "OSR entry into the middle of the loop!!", 502 block->id()); 503 } else { 504 // Deleting the entry into the loop makes the loop unreachable. 505 isUnreachableLoop = true; 506 JitSpew(JitSpew_GVN, 507 " " 508 "Loop with header block%u is no longer reachable", 509 block->id()); 510 } 511 #ifdef JS_JITSPEW 512 } else if (block->hasUniqueBackedge() && block->backedge() == pred) { 513 JitSpew(JitSpew_GVN, " Loop with header block%u is no longer a loop", 514 block->id()); 515 #endif 516 } 517 } 518 519 // Actually remove the CFG edge. 520 if (!removePredecessorAndDoDCE(block, pred, 521 block->getPredecessorIndex(pred))) { 522 return false; 523 } 524 525 // We've now edited the CFG; check to see if |block| became unreachable. 526 if (block->numPredecessors() == 0 || isUnreachableLoop) { 527 JitSpew(JitSpew_GVN, " Disconnecting block%u", block->id()); 528 529 // Remove |block| from its dominator parent's subtree. This is the only 530 // immediately-dominated-block information we need to update, because 531 // everything dominated by this block is about to be swept away. 532 MBasicBlock* parent = block->immediateDominator(); 533 if (parent != block) { 534 parent->removeImmediatelyDominatedBlock(block); 535 } 536 537 // Completely disconnect it from the CFG. We do this now rather than 538 // just doing it later when we arrive there in visitUnreachableBlock 539 // so that we don't leave a partially broken loop sitting around. This 540 // also lets visitUnreachableBlock assert that numPredecessors() == 0, 541 // which is a nice invariant. 542 if (block->isLoopHeader()) { 543 block->clearLoopHeader(); 544 } 545 for (size_t i = 0, e = block->numPredecessors(); i < e; ++i) { 546 if (!removePredecessorAndDoDCE(block, block->getPredecessor(i), i)) { 547 return false; 548 } 549 } 550 551 // Clear out the resume point operands, as they can hold things that 552 // don't appear to dominate them live. 553 if (MResumePoint* resume = block->entryResumePoint()) { 554 if (!releaseResumePointOperands(resume) || !processDeadDefs()) { 555 return false; 556 } 557 if (MResumePoint* outer = block->outerResumePoint()) { 558 if (!releaseResumePointOperands(outer) || !processDeadDefs()) { 559 return false; 560 } 561 } 562 MOZ_ASSERT(nextDef_ == nullptr); 563 for (MInstructionIterator iter(block->begin()), end(block->end()); 564 iter != end;) { 565 MInstruction* ins = *iter++; 566 nextDef_ = iter != end ? *iter : nullptr; 567 if (MResumePoint* resume = ins->resumePoint()) { 568 if (!releaseResumePointOperands(resume) || !processDeadDefs()) { 569 return false; 570 } 571 } 572 } 573 nextDef_ = nullptr; 574 } else { 575 #ifdef DEBUG 576 MOZ_ASSERT(block->outerResumePoint() == nullptr, 577 "Outer resume point in block without an entry resume point"); 578 for (MInstructionIterator iter(block->begin()), end(block->end()); 579 iter != end; ++iter) { 580 MOZ_ASSERT(iter->resumePoint() == nullptr, 581 "Instruction with resume point in block without entry " 582 "resume point"); 583 } 584 #endif 585 } 586 587 // Use the mark to note that we've already removed all its predecessors, 588 // and we know it's unreachable. 589 block->mark(); 590 } 591 592 return true; 593 } 594 595 // Return a simplified form of |def|, if we can. 596 MDefinition* ValueNumberer::simplified(MDefinition* def) const { 597 return def->foldsTo(graph_.alloc()); 598 } 599 600 // If an equivalent and dominating value already exists in the set, return it. 601 // Otherwise insert |def| into the set and return it. 602 MDefinition* ValueNumberer::leader(MDefinition* def) { 603 // If the value isn't suitable for eliminating, don't bother hashing it. The 604 // convention is that congruentTo returns false for node kinds that wish to 605 // opt out of redundance elimination. 606 // TODO: It'd be nice to clean up that convention (bug 1031406). 607 if (!def->isEffectful() && def->congruentTo(def)) { 608 // Look for a match. 609 VisibleValues::AddPtr p = values_.findLeaderForAdd(def); 610 if (p) { 611 MDefinition* rep = *p; 612 if (!rep->isDiscarded() && rep->block()->dominates(def->block())) { 613 // We found a dominating congruent value. 614 return rep; 615 } 616 617 // The congruent value doesn't dominate. It never will again in this 618 // dominator tree, so overwrite it. 619 values_.overwrite(p, def); 620 } else { 621 // No match. Add a new entry. 622 if (!values_.add(p, def)) { 623 return nullptr; 624 } 625 } 626 627 #ifdef JS_JITSPEW 628 JitSpew(JitSpew_GVN, " Recording %s%u", def->opName(), def->id()); 629 #endif 630 } 631 632 return def; 633 } 634 635 // Test whether |phi| is dominated by a congruent phi. 636 bool ValueNumberer::hasLeader(const MPhi* phi, 637 const MBasicBlock* phiBlock) const { 638 if (VisibleValues::Ptr p = values_.findLeader(phi)) { 639 const MDefinition* rep = *p; 640 return rep != phi && rep->block()->dominates(phiBlock); 641 } 642 return false; 643 } 644 645 // Test whether there are any phis in |header| which are newly optimizable, as a 646 // result of optimizations done inside the loop. This is not a sparse approach, 647 // but restarting is rare enough in practice. Termination is ensured by 648 // discarding the phi triggering the iteration. 649 bool ValueNumberer::loopHasOptimizablePhi(MBasicBlock* header) const { 650 // If the header is unreachable, don't bother re-optimizing it. 651 if (header->isMarked()) { 652 return false; 653 } 654 655 // Rescan the phis for any that can be simplified, since they may be reading 656 // values from backedges. 657 for (MPhiIterator iter(header->phisBegin()), end(header->phisEnd()); 658 iter != end; ++iter) { 659 MPhi* phi = *iter; 660 MOZ_ASSERT_IF(!phi->hasUses(), !DeadIfUnused(phi)); 661 662 if (phi->operandIfRedundant() || hasLeader(phi, header)) { 663 return true; // Phi can be simplified. 664 } 665 } 666 return false; 667 } 668 669 // Visit |def|. 670 bool ValueNumberer::visitDefinition(MDefinition* def) { 671 // Nop does not fit in any of the previous optimization, as its only purpose 672 // is to reduce the register pressure by keeping additional resume 673 // point. Still, there is no need consecutive list of MNop instructions, and 674 // this will slow down every other iteration on the Graph. 675 if (def->isNop()) { 676 MNop* nop = def->toNop(); 677 MBasicBlock* block = nop->block(); 678 679 // We look backward to know if we can remove the previous Nop, we do not 680 // look forward as we would not benefit from the folding made by GVN. 681 MInstructionReverseIterator iter = ++block->rbegin(nop); 682 683 // This nop is at the beginning of the basic block, just replace the 684 // resume point of the basic block by the one from the resume point. 685 if (iter == block->rend()) { 686 JitSpew(JitSpew_GVN, " Removing Nop%u", nop->id()); 687 nop->moveResumePointAsEntry(); 688 block->discard(nop); 689 return true; 690 } 691 692 // The previous instruction is also a Nop, no need to keep it anymore. 693 MInstruction* prev = *iter; 694 if (prev->isNop()) { 695 JitSpew(JitSpew_GVN, " Removing Nop%u", prev->id()); 696 block->discard(prev); 697 return true; 698 } 699 700 // The Nop is introduced to capture the result and make sure the operands 701 // are not live anymore when there are no further uses. Though when 702 // all operands are still needed the Nop doesn't decrease the liveness 703 // and can get removed. 704 MResumePoint* rp = nop->resumePoint(); 705 if (rp && rp->numOperands() > 0 && 706 rp->getOperand(rp->numOperands() - 1) == prev && 707 !nop->block()->lastIns()->isThrow() && 708 !prev->isAssertRecoveredOnBailout()) { 709 size_t numOperandsLive = 0; 710 for (size_t j = 0; j < prev->numOperands(); j++) { 711 for (size_t i = 0; i < rp->numOperands(); i++) { 712 if (prev->getOperand(j) == rp->getOperand(i)) { 713 numOperandsLive++; 714 break; 715 } 716 } 717 } 718 719 if (numOperandsLive == prev->numOperands()) { 720 JitSpew(JitSpew_GVN, " Removing Nop%u", nop->id()); 721 block->discard(nop); 722 } 723 } 724 725 return true; 726 } 727 728 // Skip optimizations on instructions which are recovered on bailout, to 729 // avoid mixing instructions which are recovered on bailouts with 730 // instructions which are not. 731 if (def->isRecoveredOnBailout()) { 732 return true; 733 } 734 735 // If this instruction has a dependency() into an unreachable block, we'll 736 // need to update AliasAnalysis. 737 MDefinition* dep = def->dependency(); 738 if (dep != nullptr && (dep->isDiscarded() || dep->block()->isDead())) { 739 JitSpew(JitSpew_GVN, " AliasAnalysis invalidated"); 740 if (updateAliasAnalysis_ && !dependenciesBroken_) { 741 // TODO: Recomputing alias-analysis could theoretically expose more 742 // GVN opportunities. 743 JitSpew(JitSpew_GVN, " Will recompute!"); 744 dependenciesBroken_ = true; 745 } 746 // Temporarily clear its dependency, to protect foldsTo, which may 747 // wish to use the dependency to do store-to-load forwarding. 748 def->setDependency(def->toInstruction()); 749 } else { 750 dep = nullptr; 751 } 752 753 // Look for a simplified form of |def|. 754 MDefinition* sim = simplified(def); 755 if (sim != def) { 756 if (sim == nullptr) { 757 return false; 758 } 759 760 bool isNewInstruction = sim->block() == nullptr; 761 762 // If |sim| doesn't belong to a block, insert it next to |def|. 763 if (isNewInstruction) { 764 #ifdef DEBUG 765 if (sim->isObjectKeysLength() && def->isArrayLength()) { 766 // /!\ Exception: MArrayLength::foldsTo replaces a sequence of 767 // instructions containing an effectful instruction by an effectful 768 // instruction. 769 } else { 770 // Otherwise, a new |sim| node mustn't be effectful when |def| wasn't 771 // effectful. 772 MOZ_ASSERT_IF(sim->isEffectful(), def->isEffectful()); 773 } 774 #endif 775 776 // If both instructions are effectful, |sim| must have stolen the resume 777 // point of |def| when it's a new instruction. 778 MOZ_ASSERT_IF(def->isEffectful() && sim->isEffectful(), 779 !def->toInstruction()->resumePoint() && 780 sim->toInstruction()->resumePoint()); 781 782 def->block()->insertAfter(def->toInstruction(), sim->toInstruction()); 783 } 784 785 // Get rid of flags that are meaningless for wasm and that hinder dead code 786 // removal below. Do this separately for |def| and |sim| to guard against 787 // future scenarios where they come from different (JS-vs-wasm) worlds. 788 // See bug 1969987. This is an interim fix to a larger problem, as 789 // described in bug 1973635. 790 if (def->isGuardRangeBailouts() && def->block()->info().compilingWasm()) { 791 def->setNotGuardRangeBailoutsUnchecked(); 792 } 793 if (sim->isGuardRangeBailouts() && sim->block()->info().compilingWasm()) { 794 sim->setNotGuardRangeBailoutsUnchecked(); 795 } 796 797 #ifdef JS_JITSPEW 798 JitSpew(JitSpew_GVN, " Folded %s%u to %s%u", def->opName(), def->id(), 799 sim->opName(), sim->id()); 800 #endif 801 MOZ_ASSERT(!sim->isDiscarded()); 802 ReplaceAllUsesWith(def, sim); 803 804 // The node's foldsTo said |def| can be replaced by |sim|. If |def| is a 805 // guard, then either |sim| is also a guard, or a guard isn't actually 806 // needed, so we can clear |def|'s guard flag and let it be discarded. 807 def->setNotGuardUnchecked(); 808 809 if (def->isGuardRangeBailouts()) { 810 sim->setGuardRangeBailoutsUnchecked(); 811 } 812 813 if (sim->bailoutKind() == BailoutKind::Unknown) { 814 sim->setBailoutKind(def->bailoutKind()); 815 } 816 817 // Discard |def| if it's now unused. Similar to guards, we allow to replace 818 // effectful instructions when the node's foldsTo method said |def| can be 819 // replaced. 820 if (DeadIfUnusedAllowEffectful(def)) { 821 if (!discardDefsRecursively(def, AllowEffectful::Yes)) { 822 return false; 823 } 824 825 // If that ended up discarding |sim|, then we're done here. 826 if (sim->isDiscarded()) { 827 return true; 828 } 829 } 830 831 if (!rerun_ && def->isPhi() && !sim->isPhi()) { 832 rerun_ = true; 833 JitSpew(JitSpew_GVN, 834 " Replacing phi%u may have enabled cascading optimisations; " 835 "will re-run", 836 def->id()); 837 } 838 839 // Otherwise, procede to optimize with |sim| in place of |def|. 840 def = sim; 841 842 // If the simplified instruction was already part of the graph, then we 843 // probably already visited and optimized this instruction. 844 if (!isNewInstruction) { 845 return true; 846 } 847 } 848 849 // Now that foldsTo is done, re-enable the original dependency. Even though 850 // it may be pointing into a discarded block, it's still valid for the 851 // purposes of detecting congruent loads. 852 if (dep != nullptr) { 853 def->setDependency(dep); 854 } 855 856 // Look for a dominating def which makes |def| redundant. 857 MDefinition* rep = leader(def); 858 if (rep != def) { 859 if (rep == nullptr) { 860 return false; 861 } 862 863 if (rep->isPhi()) { 864 MOZ_ASSERT(def->isPhi()); 865 rep->toPhi()->updateForReplacement(def->toPhi()); 866 } 867 868 #ifdef JS_JITSPEW 869 JitSpew(JitSpew_GVN, " Replacing %s%u with %s%u", def->opName(), 870 def->id(), rep->opName(), rep->id()); 871 #endif 872 ReplaceAllUsesWith(def, rep); 873 874 // The node's congruentTo said |def| is congruent to |rep|, and it's 875 // dominated by |rep|. If |def| is a guard, it's covered by |rep|, 876 // so we can clear |def|'s guard flag and let it be discarded. 877 def->setNotGuardUnchecked(); 878 879 if (DeadIfUnused(def)) { 880 // discardDef should not add anything to the deadDefs, as the 881 // redundant operation should have the same input operands. 882 mozilla::DebugOnly<bool> r = discardDef(def); 883 MOZ_ASSERT( 884 r, 885 "discardDef shouldn't have tried to add anything to the worklist, " 886 "so it shouldn't have failed"); 887 MOZ_ASSERT(deadDefs_.empty(), 888 "discardDef shouldn't have added anything to the worklist"); 889 } 890 } 891 892 return true; 893 } 894 895 // Visit the control instruction at the end of |block|. 896 bool ValueNumberer::visitControlInstruction(MBasicBlock* block) { 897 // Look for a simplified form of the control instruction. 898 MControlInstruction* control = block->lastIns(); 899 MDefinition* rep = simplified(control); 900 if (rep == control) { 901 return true; 902 } 903 904 if (rep == nullptr) { 905 return false; 906 } 907 908 MControlInstruction* newControl = rep->toControlInstruction(); 909 MOZ_ASSERT(!newControl->block(), 910 "Control instruction replacement shouldn't already be in a block"); 911 #ifdef JS_JITSPEW 912 JitSpew(JitSpew_GVN, " Folded control instruction %s%u to %s%u", 913 control->opName(), control->id(), newControl->opName(), 914 graph_.getNumInstructionIds()); 915 #endif 916 917 // If the simplification removes any CFG edges, update the CFG and remove 918 // any blocks that become dead. 919 size_t oldNumSuccs = control->numSuccessors(); 920 size_t newNumSuccs = newControl->numSuccessors(); 921 if (newNumSuccs != oldNumSuccs) { 922 MOZ_ASSERT(newNumSuccs < oldNumSuccs, 923 "New control instruction has too many successors"); 924 for (size_t i = 0; i != oldNumSuccs; ++i) { 925 MBasicBlock* succ = control->getSuccessor(i); 926 if (HasSuccessor(newControl, succ)) { 927 continue; 928 } 929 if (succ->isMarked()) { 930 continue; 931 } 932 if (!removePredecessorAndCleanUp(succ, block)) { 933 return false; 934 } 935 if (succ->isMarked()) { 936 continue; 937 } 938 if (!rerun_) { 939 if (!remainingBlocks_.append(succ)) { 940 return false; 941 } 942 } 943 } 944 } 945 946 if (!releaseOperands(control)) { 947 return false; 948 } 949 block->discardIgnoreOperands(control); 950 block->end(newControl); 951 if (block->entryResumePoint() && newNumSuccs != oldNumSuccs) { 952 block->flagOperandsOfPrunedBranches(newControl); 953 } 954 return processDeadDefs(); 955 } 956 957 // |block| is unreachable. Mine it for opportunities to delete more dead 958 // code, and then discard it. 959 bool ValueNumberer::visitUnreachableBlock(MBasicBlock* block) { 960 JitSpew(JitSpew_GVN, " Visiting unreachable block%u%s%s%s", block->id(), 961 block->isLoopHeader() ? " (loop header)" : "", 962 block->isSplitEdge() ? " (split edge)" : "", 963 block->immediateDominator() == block ? " (dominator root)" : ""); 964 965 MOZ_ASSERT(block->isMarked(), 966 "Visiting unmarked (and therefore reachable?) block"); 967 MOZ_ASSERT(block->numPredecessors() == 0, 968 "Block marked unreachable still has predecessors"); 969 MOZ_ASSERT(block != graph_.entryBlock(), "Removing normal entry block"); 970 MOZ_ASSERT(block != graph_.osrBlock(), "Removing OSR entry block"); 971 MOZ_ASSERT(deadDefs_.empty(), "deadDefs_ not cleared"); 972 973 // Disconnect all outgoing CFG edges. 974 for (size_t i = 0, e = block->numSuccessors(); i < e; ++i) { 975 MBasicBlock* succ = block->getSuccessor(i); 976 if (succ->isDead() || succ->isMarked()) { 977 continue; 978 } 979 if (!removePredecessorAndCleanUp(succ, block)) { 980 return false; 981 } 982 if (succ->isMarked()) { 983 continue; 984 } 985 // |succ| is still reachable. Make a note of it so that we can scan 986 // it for interesting dominator tree changes later. 987 if (!rerun_) { 988 if (!remainingBlocks_.append(succ)) { 989 return false; 990 } 991 } 992 } 993 994 // Discard any instructions with no uses. The remaining instructions will be 995 // discarded when their last use is discarded. 996 MOZ_ASSERT(nextDef_ == nullptr); 997 for (MDefinitionIterator iter(block); iter;) { 998 MDefinition* def = *iter++; 999 if (def->hasUses()) { 1000 continue; 1001 } 1002 nextDef_ = iter ? *iter : nullptr; 1003 if (!discardDefsRecursively(def)) { 1004 return false; 1005 } 1006 } 1007 1008 nextDef_ = nullptr; 1009 MControlInstruction* control = block->lastIns(); 1010 return discardDefsRecursively(control); 1011 } 1012 1013 // Visit all the phis and instructions |block|. 1014 bool ValueNumberer::visitBlock(MBasicBlock* block) { 1015 MOZ_ASSERT(!block->isMarked(), "Blocks marked unreachable during GVN"); 1016 MOZ_ASSERT(!block->isDead(), "Block to visit is already dead"); 1017 1018 JitSpew(JitSpew_GVN, " Visiting block%u", block->id()); 1019 1020 // Visit the definitions in the block top-down. 1021 MOZ_ASSERT(nextDef_ == nullptr); 1022 for (MDefinitionIterator iter(block); iter;) { 1023 if (!graph_.alloc().ensureBallast()) { 1024 return false; 1025 } 1026 MDefinition* def = *iter++; 1027 1028 // Remember where our iterator is so that we don't invalidate it. 1029 nextDef_ = iter ? *iter : nullptr; 1030 1031 // If the definition is dead, discard it. 1032 if (IsDiscardable(def)) { 1033 if (!discardDefsRecursively(def)) { 1034 return false; 1035 } 1036 continue; 1037 } 1038 1039 if (!visitDefinition(def)) { 1040 return false; 1041 } 1042 } 1043 nextDef_ = nullptr; 1044 1045 if (!graph_.alloc().ensureBallast()) { 1046 return false; 1047 } 1048 1049 return visitControlInstruction(block); 1050 } 1051 1052 // Visit all the blocks dominated by dominatorRoot. 1053 bool ValueNumberer::visitDominatorTree(MBasicBlock* dominatorRoot) { 1054 JitSpew(JitSpew_GVN, 1055 " Visiting dominator tree (with %" PRIu64 1056 " blocks) rooted at block%u%s", 1057 uint64_t(dominatorRoot->numDominated()), dominatorRoot->id(), 1058 dominatorRoot == graph_.entryBlock() ? " (normal entry block)" 1059 : dominatorRoot == graph_.osrBlock() ? " (OSR entry block)" 1060 : dominatorRoot->numPredecessors() == 0 1061 ? " (odd unreachable block)" 1062 : " (merge point from normal entry and OSR entry)"); 1063 MOZ_ASSERT(dominatorRoot->immediateDominator() == dominatorRoot, 1064 "root is not a dominator tree root"); 1065 1066 // Visit all blocks dominated by dominatorRoot, in RPO. This has the nice 1067 // property that we'll always visit a block before any block it dominates, 1068 // so we can make a single pass through the list and see every full 1069 // redundance. 1070 size_t numVisited = 0; 1071 size_t numDiscarded = 0; 1072 for (ReversePostorderIterator iter(graph_.rpoBegin(dominatorRoot));;) { 1073 MOZ_ASSERT(iter != graph_.rpoEnd(), "Inconsistent dominator information"); 1074 MBasicBlock* block = *iter++; 1075 // We're only visiting blocks in dominatorRoot's tree right now. 1076 if (!dominatorRoot->dominates(block)) { 1077 continue; 1078 } 1079 1080 // If this is a loop backedge, remember the header, as we may not be able 1081 // to find it after we simplify the block. 1082 MBasicBlock* header = 1083 block->isLoopBackedge() ? block->loopHeaderOfBackedge() : nullptr; 1084 1085 if (block->isMarked()) { 1086 // This block has become unreachable; handle it specially. 1087 if (!visitUnreachableBlock(block)) { 1088 return false; 1089 } 1090 ++numDiscarded; 1091 } else { 1092 // Visit the block! 1093 if (!visitBlock(block)) { 1094 return false; 1095 } 1096 ++numVisited; 1097 } 1098 1099 // If the block is/was a loop backedge, check to see if the block that 1100 // is/was its header has optimizable phis, which would want a re-run. 1101 if (!rerun_ && header && loopHasOptimizablePhi(header)) { 1102 JitSpew(JitSpew_GVN, 1103 " Loop phi in block%u can now be optimized; will re-run GVN!", 1104 header->id()); 1105 rerun_ = true; 1106 remainingBlocks_.clear(); 1107 } 1108 1109 MOZ_ASSERT(numVisited <= dominatorRoot->numDominated() - numDiscarded, 1110 "Visited blocks too many times"); 1111 if (numVisited >= dominatorRoot->numDominated() - numDiscarded) { 1112 break; 1113 } 1114 } 1115 1116 totalNumVisited_ += numVisited; 1117 values_.clear(); 1118 return true; 1119 } 1120 1121 // Visit all the blocks in the graph. 1122 bool ValueNumberer::visitGraph() { 1123 // Due to OSR blocks, the set of blocks dominated by a blocks may not be 1124 // contiguous in the RPO. Do a separate traversal for each dominator tree 1125 // root. There's always the main entry, and sometimes there's an OSR entry, 1126 // and then there are the roots formed where the OSR paths merge with the 1127 // main entry paths. 1128 for (ReversePostorderIterator iter(graph_.rpoBegin());;) { 1129 MOZ_ASSERT(iter != graph_.rpoEnd(), "Inconsistent dominator information"); 1130 MBasicBlock* block = *iter; 1131 if (block->immediateDominator() == block) { 1132 if (!visitDominatorTree(block)) { 1133 return false; 1134 } 1135 1136 // Normally unreachable blocks would be removed by now, but if this 1137 // block is a dominator tree root, it has been special-cased and left 1138 // in place in order to avoid invalidating our iterator. Now that 1139 // we've finished the tree, increment the iterator, and then if it's 1140 // marked for removal, remove it. 1141 ++iter; 1142 if (block->isMarked()) { 1143 JitSpew(JitSpew_GVN, " Discarding dominator root block%u", 1144 block->id()); 1145 MOZ_ASSERT( 1146 block->begin() == block->end(), 1147 "Unreachable dominator tree root has instructions after tree walk"); 1148 MOZ_ASSERT(block->phisEmpty(), 1149 "Unreachable dominator tree root has phis after tree walk"); 1150 graph_.removeBlock(block); 1151 blocksRemoved_ = true; 1152 } 1153 1154 MOZ_ASSERT(totalNumVisited_ <= graph_.numBlocks(), 1155 "Visited blocks too many times"); 1156 if (totalNumVisited_ >= graph_.numBlocks()) { 1157 break; 1158 } 1159 } else { 1160 // This block a dominator tree root. Proceed to the next one. 1161 ++iter; 1162 } 1163 } 1164 totalNumVisited_ = 0; 1165 return true; 1166 } 1167 1168 bool ValueNumberer::insertOSRFixups() { 1169 ReversePostorderIterator end(graph_.end()); 1170 for (ReversePostorderIterator iter(graph_.begin()); iter != end;) { 1171 MBasicBlock* block = *iter++; 1172 1173 // Only add fixup block above for loops which can be reached from OSR. 1174 if (!block->isLoopHeader()) { 1175 continue; 1176 } 1177 1178 // If the loop header is not self-dominated, then this loop does not 1179 // have to deal with a second entry point, so there is no need to add a 1180 // second entry point with a fixup block. 1181 if (block->immediateDominator() != block) { 1182 continue; 1183 } 1184 1185 if (!fixupOSROnlyLoop(block)) { 1186 return false; 1187 } 1188 } 1189 1190 return true; 1191 } 1192 1193 // OSR fixups serve the purpose of representing the non-OSR entry into a loop 1194 // when the only real entry is an OSR entry into the middle. However, if the 1195 // entry into the middle is subsequently folded away, the loop may actually 1196 // have become unreachable. Mark-and-sweep all blocks to remove all such code. 1197 bool ValueNumberer::cleanupOSRFixups() { 1198 // Mark. 1199 Vector<MBasicBlock*, 0, JitAllocPolicy> worklist(graph_.alloc()); 1200 unsigned numMarked = 2; 1201 graph_.entryBlock()->mark(); 1202 graph_.osrBlock()->mark(); 1203 if (!worklist.append(graph_.entryBlock()) || 1204 !worklist.append(graph_.osrBlock())) { 1205 return false; 1206 } 1207 while (!worklist.empty()) { 1208 MBasicBlock* block = worklist.popCopy(); 1209 for (size_t i = 0, e = block->numSuccessors(); i != e; ++i) { 1210 MBasicBlock* succ = block->getSuccessor(i); 1211 if (!succ->isMarked()) { 1212 ++numMarked; 1213 succ->mark(); 1214 if (!worklist.append(succ)) { 1215 return false; 1216 } 1217 } else if (succ->isLoopHeader() && succ->loopPredecessor() == block && 1218 succ->numPredecessors() == 3) { 1219 // Unmark fixup blocks if the loop predecessor is marked after 1220 // the loop header. 1221 succ->getPredecessor(1)->unmarkUnchecked(); 1222 } 1223 } 1224 1225 // OSR fixup blocks are needed if and only if the loop header is 1226 // reachable from its backedge (via the OSR block) and not from its 1227 // original loop predecessor. 1228 // 1229 // Thus OSR fixup blocks are removed if the loop header is not 1230 // reachable, or if the loop header is reachable from both its backedge 1231 // and its original loop predecessor. 1232 if (block->isLoopHeader()) { 1233 MBasicBlock* maybeFixupBlock = nullptr; 1234 if (block->numPredecessors() == 2) { 1235 maybeFixupBlock = block->getPredecessor(0); 1236 } else { 1237 MOZ_ASSERT(block->numPredecessors() == 3); 1238 if (!block->loopPredecessor()->isMarked()) { 1239 maybeFixupBlock = block->getPredecessor(1); 1240 } 1241 } 1242 1243 if (maybeFixupBlock && !maybeFixupBlock->isMarked() && 1244 maybeFixupBlock->numPredecessors() == 0) { 1245 MOZ_ASSERT(maybeFixupBlock->numSuccessors() == 1, 1246 "OSR fixup block should have exactly one successor"); 1247 MOZ_ASSERT(maybeFixupBlock != graph_.entryBlock(), 1248 "OSR fixup block shouldn't be the entry block"); 1249 MOZ_ASSERT(maybeFixupBlock != graph_.osrBlock(), 1250 "OSR fixup block shouldn't be the OSR entry block"); 1251 maybeFixupBlock->mark(); 1252 } 1253 } 1254 } 1255 1256 // And sweep. 1257 return RemoveUnmarkedBlocks(mir_, graph_, numMarked); 1258 } 1259 1260 ValueNumberer::ValueNumberer(const MIRGenerator* mir, MIRGraph& graph) 1261 : mir_(mir), 1262 graph_(graph), 1263 // Initialize the value set. It's tempting to pass in a length that is a 1264 // function of graph_.getNumInstructionIds(). But if we start out with a 1265 // large capacity, it will be far larger than the actual element count for 1266 // most of the pass, so when we remove elements, it would often think it 1267 // needs to compact itself. Empirically, just letting the HashTable grow 1268 // as needed on its own seems to work pretty well. 1269 values_(graph.alloc()), 1270 deadDefs_(graph.alloc()), 1271 remainingBlocks_(graph.alloc()), 1272 nextDef_(nullptr), 1273 totalNumVisited_(0), 1274 rerun_(false), 1275 blocksRemoved_(false), 1276 updateAliasAnalysis_(false), 1277 dependenciesBroken_(false), 1278 hasOSRFixups_(false) {} 1279 1280 bool ValueNumberer::run(UpdateAliasAnalysisFlag updateAliasAnalysis) { 1281 updateAliasAnalysis_ = updateAliasAnalysis == UpdateAliasAnalysis; 1282 1283 JitSpew(JitSpew_GVN, "Running GVN on graph (with %" PRIu64 " blocks)", 1284 uint64_t(graph_.numBlocks())); 1285 1286 // Adding fixup blocks only make sense iff we have a second entry point into 1287 // the graph which cannot be reached any more from the entry point. 1288 if (graph_.osrBlock()) { 1289 if (!insertOSRFixups()) { 1290 return false; 1291 } 1292 } 1293 1294 // Top level non-sparse iteration loop. If an iteration performs a 1295 // significant change, such as discarding a block which changes the 1296 // dominator tree and may enable more optimization, this loop takes another 1297 // iteration. 1298 int runs = 0; 1299 for (;;) { 1300 if (!visitGraph()) { 1301 return false; 1302 } 1303 1304 // Test whether any block which was not removed but which had at least 1305 // one predecessor removed will have a new dominator parent. 1306 while (!remainingBlocks_.empty()) { 1307 MBasicBlock* block = remainingBlocks_.popCopy(); 1308 if (!block->isDead() && IsDominatorRefined(block)) { 1309 JitSpew(JitSpew_GVN, 1310 " Dominator for block%u can now be refined; will re-run GVN!", 1311 block->id()); 1312 rerun_ = true; 1313 remainingBlocks_.clear(); 1314 break; 1315 } 1316 } 1317 1318 if (blocksRemoved_) { 1319 if (!AccountForCFGChanges(mir_, graph_, dependenciesBroken_, 1320 /* underValueNumberer = */ true)) { 1321 return false; 1322 } 1323 1324 blocksRemoved_ = false; 1325 dependenciesBroken_ = false; 1326 } 1327 1328 if (mir_->shouldCancel("GVN (outer loop)")) { 1329 return false; 1330 } 1331 1332 // If no further opportunities have been discovered, we're done. 1333 if (!rerun_) { 1334 break; 1335 } 1336 1337 rerun_ = false; 1338 1339 // Enforce an arbitrary iteration limit. This is rarely reached, and 1340 // isn't even strictly necessary, as the algorithm is guaranteed to 1341 // terminate on its own in a finite amount of time (since every time we 1342 // re-run we discard the construct which triggered the re-run), but it 1343 // does help avoid slow compile times on pathological code. 1344 ++runs; 1345 if (runs == 6) { 1346 JitSpew(JitSpew_GVN, "Re-run cutoff of %d reached. Terminating GVN!", 1347 runs); 1348 break; 1349 } 1350 1351 JitSpew(JitSpew_GVN, 1352 "Re-running GVN on graph (run %d, now with %" PRIu64 " blocks)", 1353 runs, uint64_t(graph_.numBlocks())); 1354 } 1355 1356 if (MOZ_UNLIKELY(hasOSRFixups_)) { 1357 if (!cleanupOSRFixups()) { 1358 return false; 1359 } 1360 hasOSRFixups_ = false; 1361 } 1362 1363 return true; 1364 }