blob: e6585b06baf8ee7517d30f979bd30e43793f7212 [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "UnreachableCodeElimination.h"
#include "IonAnalysis.h"
#include "AliasAnalysis.h"
#include "ValueNumbering.h"
using namespace js;
using namespace jit;
bool
UnreachableCodeElimination::analyze()
{
// The goal of this routine is to eliminate code that is
// unreachable, either because there is no path from the entry
// block to the code, or because the path traverses a conditional
// branch where the condition is a constant (e.g., "if (false) {
// ... }"). The latter can either appear in the source form or
// arise due to optimizations.
//
// The stategy is straightforward. The pass begins with a
// depth-first search. We set a bit on each basic block that
// is visited. If a block terminates in a conditional branch
// predicated on a constant, we rewrite the block to an unconditional
// jump and do not visit the now irrelevant basic block.
//
// Once the initial DFS is complete, we do a second pass over the
// blocks to find those that were not reached. Those blocks are
// simply removed wholesale. We must also correct any phis that
// may be affected..
// Pass 1: Identify unreachable blocks (if any).
if (!prunePointlessBranchesAndMarkReachableBlocks())
return false;
return removeUnmarkedBlocksAndCleanup();
}
bool
UnreachableCodeElimination::removeUnmarkedBlocks(size_t marked)
{
marked_ = marked;
return removeUnmarkedBlocksAndCleanup();
}
bool
UnreachableCodeElimination::removeUnmarkedBlocksAndCleanup()
{
// Everything is reachable, no work required.
JS_ASSERT(marked_ <= graph_.numBlocks());
if (marked_ == graph_.numBlocks()) {
graph_.unmarkBlocks();
return true;
}
// Pass 2: Remove unmarked blocks (see analyze() above).
if (!removeUnmarkedBlocksAndClearDominators())
return false;
graph_.unmarkBlocks();
AssertGraphCoherency(graph_);
IonSpewPass("UCE-mid-point");
// Pass 3: Recompute dominators and tweak phis.
BuildDominatorTree(graph_);
if (redundantPhis_ && !EliminatePhis(mir_, graph_, ConservativeObservability))
return false;
// Pass 4: Rerun alias analysis
if (rerunAliasAnalysis_) {
AliasAnalysis analysis(mir_, graph_);
if (!analysis.analyze())
return false;
}
// Pass 5: It's important for optimizations to re-run GVN (and in
// turn alias analysis) after UCE if we eliminated branches.
if (rerunAliasAnalysis_ && js_IonOptions.gvn) {
ValueNumberer gvn(mir_, graph_, js_IonOptions.gvnIsOptimistic);
if (!gvn.clear() || !gvn.analyze())
return false;
IonSpewPass("GVN-after-UCE");
AssertExtendedGraphCoherency(graph_);
if (mir_->shouldCancel("GVN-after-UCE"))
return false;
}
return true;
}
bool
UnreachableCodeElimination::enqueue(MBasicBlock *block, BlockList &list)
{
if (block->isMarked())
return true;
block->mark();
marked_++;
return list.append(block);
}
MBasicBlock *
UnreachableCodeElimination::optimizableSuccessor(MBasicBlock *block)
{
// If the last instruction in `block` is a test instruction of a
// constant value, returns the successor that the branch will
// always branch to at runtime. Otherwise, returns NULL.
MControlInstruction *ins = block->lastIns();
if (!ins->isTest())
return NULL;
MTest *testIns = ins->toTest();
MDefinition *v = testIns->getOperand(0);
if (!v->isConstant())
return NULL;
const Value &val = v->toConstant()->value();
BranchDirection bdir = ToBoolean(val) ? TRUE_BRANCH : FALSE_BRANCH;
return testIns->branchSuccessor(bdir);
}
bool
UnreachableCodeElimination::prunePointlessBranchesAndMarkReachableBlocks()
{
BlockList worklist, optimizableBlocks;
// Process everything reachable from the start block, ignoring any
// OSR block.
if (!enqueue(graph_.entryBlock(), worklist))
return false;
while (!worklist.empty()) {
if (mir_->shouldCancel("Eliminate Unreachable Code"))
return false;
MBasicBlock *block = worklist.popCopy();
// If this block is a test on a constant operand, only enqueue
// the relevant successor. Also, remember the block for later.
if (MBasicBlock *succ = optimizableSuccessor(block)) {
if (!optimizableBlocks.append(block))
return false;
if (!enqueue(succ, worklist))
return false;
} else {
// Otherwise just visit all successors.
for (size_t i = 0; i < block->numSuccessors(); i++) {
MBasicBlock *succ = block->getSuccessor(i);
if (!enqueue(succ, worklist))
return false;
}
}
}
// Now, if there is an OSR block, check that all of its successors
// were reachable (bug 880377). If not, we are in danger of
// creating a CFG with two disjoint parts, so simply mark all
// blocks as reachable. This generally occurs when the TI info for
// stack types is incorrect or incomplete, due to operations that
// have not yet executed in baseline.
if (graph_.osrBlock()) {
MBasicBlock *osrBlock = graph_.osrBlock();
JS_ASSERT(!osrBlock->isMarked());
if (!enqueue(osrBlock, worklist))
return false;
for (size_t i = 0; i < osrBlock->numSuccessors(); i++) {
if (!osrBlock->getSuccessor(i)->isMarked()) {
// OSR block has an otherwise unreachable successor, abort.
for (MBasicBlockIterator iter(graph_.begin()); iter != graph_.end(); iter++)
iter->mark();
marked_ = graph_.numBlocks();
return true;
}
}
}
// Now that we know we will not abort due to OSR, go back and
// transform any tests on constant operands into gotos.
for (uint32_t i = 0; i < optimizableBlocks.length(); i++) {
MBasicBlock *block = optimizableBlocks[i];
MBasicBlock *succ = optimizableSuccessor(block);
JS_ASSERT(succ);
MGoto *gotoIns = MGoto::New(succ);
block->discardLastIns();
block->end(gotoIns);
MBasicBlock *successorWithPhis = block->successorWithPhis();
if (successorWithPhis && successorWithPhis != succ)
block->setSuccessorWithPhis(NULL, 0);
}
return true;
}
void
UnreachableCodeElimination::checkDependencyAndRemoveUsesFromUnmarkedBlocks(MDefinition *instr)
{
// When the instruction depends on removed block,
// alias analysis needs to get rerun to have the right dependency.
if (instr->dependency() && !instr->dependency()->block()->isMarked())
rerunAliasAnalysis_ = true;
for (MUseIterator iter(instr->usesBegin()); iter != instr->usesEnd(); ) {
if (!iter->consumer()->block()->isMarked()) {
instr->setUseRemovedUnchecked();
iter = instr->removeUse(iter);
} else {
iter++;
}
}
}
bool
UnreachableCodeElimination::removeUnmarkedBlocksAndClearDominators()
{
// Removes blocks that are not marked from the graph. For blocks
// that *are* marked, clears the mark and adjusts the id to its
// new value. Also adds blocks that are immediately reachable
// from an unmarked block to the frontier.
size_t id = marked_;
for (PostorderIterator iter(graph_.poBegin()); iter != graph_.poEnd();) {
if (mir_->shouldCancel("Eliminate Unreachable Code"))
return false;
MBasicBlock *block = *iter;
iter++;
// Unconditionally clear the dominators. It's somewhat complex to
// adjust the values and relatively fast to just recompute.
block->clearDominatorInfo();
if (block->isMarked()) {
block->setId(--id);
for (MPhiIterator iter(block->phisBegin()); iter != block->phisEnd(); iter++)
checkDependencyAndRemoveUsesFromUnmarkedBlocks(*iter);
for (MInstructionIterator iter(block->begin()); iter != block->end(); iter++)
checkDependencyAndRemoveUsesFromUnmarkedBlocks(*iter);
} else {
if (block->numPredecessors() > 1) {
// If this block had phis, then any reachable
// predecessors need to have the successorWithPhis
// flag cleared.
for (size_t i = 0; i < block->numPredecessors(); i++)
block->getPredecessor(i)->setSuccessorWithPhis(NULL, 0);
}
if (block->isLoopBackedge()) {
// NB. We have to update the loop header if we
// eliminate the backedge. At first I thought this
// check would be insufficient, because it would be
// possible to have code like this:
//
// while (true) {
// ...;
// if (1 == 1) break;
// }
//
// in which the backedge is removed as part of
// rewriting the condition, but no actual blocks are
// removed. However, in all such cases, the backedge
// would be a critical edge and hence the critical
// edge block is being removed.
block->loopHeaderOfBackedge()->clearLoopHeader();
}
for (size_t i = 0, c = block->numSuccessors(); i < c; i++) {
MBasicBlock *succ = block->getSuccessor(i);
if (succ->isMarked()) {
// succ is on the frontier of blocks to be removed:
succ->removePredecessor(block);
if (!redundantPhis_) {
for (MPhiIterator iter(succ->phisBegin()); iter != succ->phisEnd(); iter++) {
if (iter->operandIfRedundant()) {
redundantPhis_ = true;
break;
}
}
}
}
}
// When we remove a call, we can't leave the corresponding MPassArg
// in the graph. Since lowering will fail. Replace it with the
// argument for the exceptional case when it is kept alive in a
// ResumePoint. DCE will remove the unused MPassArg instruction.
for (MInstructionIterator iter(block->begin()); iter != block->end(); iter++) {
if (iter->isCall()) {
MCall *call = iter->toCall();
for (size_t i = 0; i < call->numStackArgs(); i++) {
JS_ASSERT(call->getArg(i)->isPassArg());
JS_ASSERT(call->getArg(i)->defUseCount() == 1);
MPassArg *arg = call->getArg(i)->toPassArg();
arg->replaceAllUsesWith(arg->getArgument());
}
}
}
graph_.removeBlock(block);
}
}
JS_ASSERT(id == 0);
return true;
}