LCOV - code coverage report
Current view: top level - js/src/jit - ValueNumbering.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 432 608 71.1 %
Date: 2017-07-14 16:53:18 Functions: 40 45 88.9 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13