// |block| is unreachable. Mine it for opportunities to delete more dead // code, and then discard it. bool ValueNumberer::visitUnreachableBlock(MBasicBlock* block) { JitSpew(JitSpew_GVN, " Visiting unreachable block%u%s%s%s", block->id(), block->isLoopHeader() ? " (loop header)" : "", block->isSplitEdge() ? " (split edge)" : "", block->immediateDominator() == block ? " (dominator root)" : ""); MOZ_ASSERT(block->isMarked(), "Visiting unmarked (and therefore reachable?) block"); MOZ_ASSERT(block->numPredecessors() == 0, "Block marked unreachable still has predecessors"); MOZ_ASSERT(block != graph_.entryBlock(), "Removing normal entry block"); MOZ_ASSERT(block != graph_.osrBlock(), "Removing OSR entry block"); MOZ_ASSERT(deadDefs_.empty(), "deadDefs_ not cleared"); // Disconnect all outgoing CFG edges. for (size_t i = 0, e = block->numSuccessors(); i < e; ++i) { MBasicBlock* succ = block->getSuccessor(i); if (succ->isDead() || succ->isMarked()) continue; if (!removePredecessorAndCleanUp(succ, block)) return false; if (succ->isMarked()) continue; // |succ| is still reachable. Make a note of it so that we can scan // it for interesting dominator tree changes later. if (!rerun_) { if (!remainingBlocks_.append(succ)) return false; } } // Discard any instructions with no uses. The remaining instructions will be // discarded when their last use is discarded. MOZ_ASSERT(nextDef_ == nullptr); for (MDefinitionIterator iter(block); iter; ) { MDefinition* def = *iter++; if (def->hasUses()) continue; nextDef_ = *iter; if (!discardDefsRecursively(def)) return false; } nextDef_ = nullptr; MControlInstruction* control = block->lastIns(); return discardDefsRecursively(control); }
bool ValueNumberer::run(UpdateAliasAnalysisFlag updateAliasAnalysis) { updateAliasAnalysis_ = updateAliasAnalysis == UpdateAliasAnalysis; JitSpew(JitSpew_GVN, "Running GVN on graph (with %llu blocks)", uint64_t(graph_.numBlocks())); // Top level non-sparse iteration loop. If an iteration performs a // significant change, such as discarding a block which changes the // dominator tree and may enable more optimization, this loop takes another // iteration. int runs = 0; for (;;) { if (!visitGraph()) return false; // Test whether any block which was not removed but which had at least // one predecessor removed will have a new dominator parent. while (!remainingBlocks_.empty()) { MBasicBlock* block = remainingBlocks_.popCopy(); if (!block->isDead() && IsDominatorRefined(block)) { JitSpew(JitSpew_GVN, " Dominator for block%u can now be refined; will re-run GVN!", block->id()); rerun_ = true; remainingBlocks_.clear(); break; } } if (blocksRemoved_) { if (!AccountForCFGChanges(mir_, graph_, dependenciesBroken_)) return false; blocksRemoved_ = false; dependenciesBroken_ = false; } if (mir_->shouldCancel("GVN (outer loop)")) return false; // If no further opportunities have been discovered, we're done. if (!rerun_) break; rerun_ = false; // Enforce an arbitrary iteration limit. This is rarely reached, and // isn't even strictly necessary, as the algorithm is guaranteed to // terminate on its own in a finite amount of time (since every time we // re-run we discard the construct which triggered the re-run), but it // does help avoid slow compile times on pathological code. ++runs; if (runs == 6) { JitSpew(JitSpew_GVN, "Re-run cutoff of %d reached. Terminating GVN!", runs); break; } JitSpew(JitSpew_GVN, "Re-running GVN on graph (run %d, now with %llu blocks)", runs, uint64_t(graph_.numBlocks())); } return true; }