Example #1
0
MDefinition *
MIRGraph::forkJoinContext()
{
    // Search the entry block to find a ForkJoinContext instruction. If we do
    // not find one, add one after the Start instruction.
    //
    // Note: the original design used a field in MIRGraph to cache the
    // forkJoinContext rather than searching for it again.  However, this
    // could become out of date due to DCE.  Given that we do not generally
    // have to search very far to find the ForkJoinContext instruction if it
    // exists, and that we don't look for it that often, I opted to simply
    // eliminate the cache and search anew each time, so that it is that much
    // easier to keep the IR coherent. - nmatsakis

    MBasicBlock *entry = entryBlock();
    JS_ASSERT(entry->info().executionMode() == ParallelExecution);

    MInstruction *start = nullptr;
    for (MInstructionIterator ins(entry->begin()); ins != entry->end(); ins++) {
        if (ins->isForkJoinContext())
            return *ins;
        else if (ins->isStart())
            start = *ins;
    }
    JS_ASSERT(start);

    MForkJoinContext *cx = MForkJoinContext::New(alloc());
    entry->insertAfter(start, cx);
    return cx;
}
Example #2
0
MDefinition *
MIRGraph::parSlice() {
    // Search the entry block to find a par slice instruction.  If we do not
    // find one, add one after the Start instruction.
    //
    // Note: the original design used a field in MIRGraph to cache the
    // parSlice rather than searching for it again.  However, this
    // could become out of date due to DCE.  Given that we do not
    // generally have to search very far to find the par slice
    // instruction if it exists, and that we don't look for it that
    // often, I opted to simply eliminate the cache and search anew
    // each time, so that it is that much easier to keep the IR
    // coherent. - nmatsakis

    MBasicBlock *entry = entryBlock();
    JS_ASSERT(entry->info().executionMode() == ParallelExecution);

    MInstruction *start = NULL;
    for (MInstructionIterator ins(entry->begin()); ins != entry->end(); ins++) {
        if (ins->isParSlice())
            return *ins;
        else if (ins->isStart())
            start = *ins;
    }
    JS_ASSERT(start);

    MParSlice *parSlice = new MParSlice();
    entry->insertAfter(start, parSlice);
    return parSlice;
}
Example #3
0
MControlInstruction *
ValueNumberer::simplifyControlInstruction(MControlInstruction *def)
{
    if (def->isEffectful())
        return def;

    MDefinition *repl = def->foldsTo(alloc(), false);
    if (repl == def)
        return def;

    // Ensure this instruction has a value number.
    if (!repl->valueNumberData())
        repl->setValueNumberData(new(alloc()) ValueNumberData);

    MBasicBlock *block = def->block();

    // MControlInstructions should not have consumers.
    JS_ASSERT(repl->isControlInstruction());
    JS_ASSERT(!def->hasUses());

    if (def->isInWorklist())
        repl->setInWorklist();

    block->discardLastIns();
    block->end(repl->toControlInstruction());
    return repl->toControlInstruction();
}
// Discard |def| and mine its operands for any subsequently dead defs.
bool
ValueNumberer::discardDef(MDefinition* def)
{
#ifdef JS_JITSPEW
    JitSpew(JitSpew_GVN, "      Discarding %s %s%u",
            def->block()->isMarked() ? "unreachable" : "dead",
            def->opName(), def->id());
#endif
#ifdef DEBUG
    MOZ_ASSERT(def != nextDef_, "Invalidating the MDefinition iterator");
    if (def->block()->isMarked()) {
        MOZ_ASSERT(!def->hasUses(), "Discarding def that still has uses");
    } else {
        MOZ_ASSERT(IsDiscardable(def), "Discarding non-discardable definition");
        MOZ_ASSERT(!values_.has(def), "Discarding a definition still in the set");
    }
#endif

    MBasicBlock* block = def->block();
    if (def->isPhi()) {
        MPhi* phi = def->toPhi();
        if (!releaseAndRemovePhiOperands(phi))
             return false;
        block->discardPhi(phi);
    } else {
        MInstruction* ins = def->toInstruction();
        if (MResumePoint* resume = ins->resumePoint()) {
            if (!releaseResumePointOperands(resume))
                return false;
        }
        if (!releaseOperands(ins))
             return false;
        block->discardIgnoreOperands(ins);
    }

    // If that was the last definition in the block, it can be safely removed
    // from the graph.
    if (block->phisEmpty() && block->begin() == block->end()) {
        MOZ_ASSERT(block->isMarked(), "Reachable block lacks at least a control instruction");

        // As a special case, don't remove a block which is a dominator tree
        // root so that we don't invalidate the iterator in visitGraph. We'll
        // check for this and remove it later.
        if (block->immediateDominator() != block) {
            JitSpew(JitSpew_GVN, "      Block block%u is now empty; discarding", block->id());
            graph_.removeBlock(block);
            blocksRemoved_ = true;
        } else {
            JitSpew(JitSpew_GVN, "      Dominator root block%u is now empty; will discard later",
                    block->id());
        }
    }

    return true;
}
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 {
            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;
                            }
                        }
                    }
                }
            }

            graph_.removeBlock(block);
        }
    }

    JS_ASSERT(id == 0);

    return true;
}
Example #6
0
MBasicBlock *
MBasicBlock::NewParBailout(MIRGraph &graph, CompileInfo &info,
                           MBasicBlock *pred, jsbytecode *entryPc)
{
    MBasicBlock *block = MBasicBlock::New(graph, info, pred, entryPc, NORMAL);
    if (!block)
        return NULL;

    MParBailout *bailout = new MParBailout();
    if (!bailout)
        return NULL;

    block->end(bailout);
    return block;
}
// A loop is about to be made reachable only through an OSR entry into one of
// its nested loops. Fix everything up.
bool
ValueNumberer::fixupOSROnlyLoop(MBasicBlock* block, MBasicBlock* backedge)
{
    // Create an empty and unreachable(!) block which jumps to |block|. This
    // allows |block| to remain marked as a loop header, so we don't have to
    // worry about moving a different block into place as the new loop header,
    // which is hard, especially if the OSR is into a nested loop. Doing all
    // that would produce slightly more optimal code, but this is so
    // extraordinarily rare that it isn't worth the complexity.
    MBasicBlock* fake = MBasicBlock::New(graph_, block->info(), nullptr, MBasicBlock::NORMAL);
    if (fake == nullptr)
        return false;

    graph_.insertBlockBefore(block, fake);
    fake->setImmediateDominator(fake);
    fake->addNumDominated(1);
    fake->setDomIndex(fake->id());
    fake->setUnreachable();

    // Create zero-input phis to use as inputs for any phis in |block|.
    // Again, this is a little odd, but it's the least-odd thing we can do
    // without significant complexity.
    for (MPhiIterator iter(block->phisBegin()), end(block->phisEnd()); iter != end; ++iter) {
        MPhi* phi = *iter;
        MPhi* fakePhi = MPhi::New(graph_.alloc(), phi->type());
        fake->addPhi(fakePhi);
        if (!phi->addInputSlow(fakePhi))
            return false;
    }

    fake->end(MGoto::New(graph_.alloc(), block));

    if (!block->addPredecessorWithoutPhis(fake))
        return false;

    // Restore |backedge| as |block|'s loop backedge.
    block->clearLoopHeader();
    block->setLoopHeader(backedge);

    JitSpew(JitSpew_GVN, "        Created fake block%u", fake->id());
    hasOSRFixups_ = true;
    return true;
}
Example #8
0
// Visit all the blocks in the graph.
bool
ValueNumberer::visitGraph()
{
    // Due to OSR blocks, the set of blocks dominated by a blocks may not be
    // contiguous in the RPO. Do a separate traversal for each dominator tree
    // root. There's always the main entry, and sometimes there's an OSR entry,
    // and then there are the roots formed where the OSR paths merge with the
    // main entry paths.
    for (ReversePostorderIterator iter(graph_.rpoBegin()); ; ) {
        MOZ_ASSERT(iter != graph_.rpoEnd(), "Inconsistent dominator information");
        MBasicBlock* block = *iter;
        if (block->immediateDominator() == block) {
            if (!visitDominatorTree(block))
                return false;

            // Normally unreachable blocks would be removed by now, but if this
            // block is a dominator tree root, it has been special-cased and left
            // in place in order to avoid invalidating our iterator. Now that
            // we've finished the tree, increment the iterator, and then if it's
            // marked for removal, remove it.
            ++iter;
            if (block->isMarked()) {
                JitSpew(JitSpew_GVN, "      Discarding dominator root block%u",
                        block->id());
                MOZ_ASSERT(block->begin() == block->end(),
                           "Unreachable dominator tree root has instructions after tree walk");
                MOZ_ASSERT(block->phisEmpty(),
                           "Unreachable dominator tree root has phis after tree walk");
                graph_.removeBlock(block);
                blocksRemoved_ = true;
            }

            MOZ_ASSERT(totalNumVisited_ <= graph_.numBlocks(), "Visited blocks too many times");
            if (totalNumVisited_ >= graph_.numBlocks())
                break;
        } else {
            // This block a dominator tree root. Proceed to the next one.
            ++iter;
        }
    }
    totalNumVisited_ = 0;
    return true;
}
Example #9
0
MBasicBlock *
MBasicBlock::NewAbortPar(MIRGraph &graph, CompileInfo &info,
                         MBasicBlock *pred, jsbytecode *entryPc,
                         MResumePoint *resumePoint)
{
    MBasicBlock *block = new MBasicBlock(graph, info, entryPc, NORMAL);

    resumePoint->block_ = block;
    block->entryResumePoint_ = resumePoint;

    if (!block->init())
        return NULL;

    if (!block->addPredecessorWithoutPhis(pred))
        return NULL;

    block->end(new MAbortPar());
    return block;
}
Example #10
0
MBasicBlock *
MBasicBlock::NewAbortPar(MIRGraph &graph, CompileInfo &info,
                         MBasicBlock *pred, const BytecodeSite &site,
                         MResumePoint *resumePoint)
{
    MBasicBlock *block = new(graph.alloc()) MBasicBlock(graph, info, site, NORMAL);

    resumePoint->block_ = block;
    block->entryResumePoint_ = resumePoint;

    if (!block->init())
        return nullptr;

    if (!block->addPredecessorWithoutPhis(pred))
        return nullptr;

    block->end(MAbortPar::New(graph.alloc()));
    return block;
}
Example #11
0
// Test whether any instruction in the loop possiblyCalls().
static bool
LoopContainsPossibleCall(MIRGraph &graph, MBasicBlock *header, MBasicBlock *backedge)
{
    for (auto i(graph.rpoBegin(header)); ; ++i) {
        MOZ_ASSERT(i != graph.rpoEnd(), "Reached end of graph searching for blocks in loop");
        MBasicBlock *block = *i;
        if (!block->isMarked())
            continue;

        for (auto insIter(block->begin()), insEnd(block->end()); insIter != insEnd; ++insIter) {
            MInstruction *ins = *insIter;
            if (ins->possiblyCalls()) {
                JitSpew(JitSpew_LICM, "    Possile call found at %s%u", ins->opName(), ins->id());
                return true;
            }
        }

        if (block == backedge)
            break;
    }
    return false;
}
// A critical edge is an edge which is neither its successor's only predecessor
// nor its predecessor's only successor. Critical edges must be split to
// prevent copy-insertion and code motion from affecting other edges.
bool
ion::SplitCriticalEdges(MIRGraph &graph)
{
    for (MBasicBlockIterator block(graph.begin()); block != graph.end(); block++) {
        if (block->numSuccessors() < 2)
            continue;
        for (size_t i = 0; i < block->numSuccessors(); i++) {
            MBasicBlock *target = block->getSuccessor(i);
            if (target->numPredecessors() < 2)
                continue;

            // Create a new block inheriting from the predecessor.
            MBasicBlock *split = MBasicBlock::NewSplitEdge(graph, block->info(), *block);
            split->setLoopDepth(block->loopDepth());
            graph.insertBlockAfter(*block, split);
            split->end(MGoto::New(target));

            block->replaceSuccessor(i, split);
            target->replacePredecessor(*block, split);
        }
    }
    return true;
}
Example #13
0
void
LoopUnroller::go(LoopIterationBound *bound)
{
    // For now we always unroll loops the same number of times.
    static const size_t UnrollCount = 10;

    JitSpew(JitSpew_Unrolling, "Attempting to unroll loop");

    header = bound->header;

    // UCE might have determined this isn't actually a loop.
    if (!header->isLoopHeader())
        return;

    backedge = header->backedge();
    oldPreheader = header->loopPredecessor();

    JS_ASSERT(oldPreheader->numSuccessors() == 1);

    // Only unroll loops with two blocks: an initial one ending with the
    // bound's test, and the body ending with the backedge.
    MTest *test = bound->test;
    if (header->lastIns() != test)
        return;
    if (test->ifTrue() == backedge) {
        if (test->ifFalse()->id() <= backedge->id())
            return;
    } else if (test->ifFalse() == backedge) {
        if (test->ifTrue()->id() <= backedge->id())
            return;
    } else {
        return;
    }
    if (backedge->numPredecessors() != 1 || backedge->numSuccessors() != 1)
        return;
    JS_ASSERT(backedge->phisEmpty());

    MBasicBlock *bodyBlocks[] = { header, backedge };

    // All instructions in the header and body must be clonable.
    for (size_t i = 0; i < ArrayLength(bodyBlocks); i++) {
        MBasicBlock *block = bodyBlocks[i];
        for (MInstructionIterator iter(block->begin()); iter != block->end(); iter++) {
            MInstruction *ins = *iter;
            if (ins->canClone())
                continue;
            if (ins->isTest() || ins->isGoto() || ins->isInterruptCheck())
                continue;
#ifdef DEBUG
            JitSpew(JitSpew_Unrolling, "Aborting: can't clone instruction %s", ins->opName());
#endif
            return;
        }
    }

    // Compute the linear inequality we will use for exiting the unrolled loop:
    //
    // iterationBound - iterationCount - UnrollCount >= 0
    //
    LinearSum remainingIterationsInequality(bound->boundSum);
    if (!remainingIterationsInequality.add(bound->currentSum, -1))
        return;
    if (!remainingIterationsInequality.add(-int32_t(UnrollCount)))
        return;

    // Terms in the inequality need to be either loop invariant or phis from
    // the original header.
    for (size_t i = 0; i < remainingIterationsInequality.numTerms(); i++) {
        MDefinition *def = remainingIterationsInequality.term(i).term;
        if (def->block()->id() < header->id())
            continue;
        if (def->block() == header && def->isPhi())
            continue;
        return;
    }

    // OK, we've checked everything, now unroll the loop.

    JitSpew(JitSpew_Unrolling, "Unrolling loop");

    // The old preheader will go before the unrolled loop, and the old loop
    // will need a new empty preheader.
    CompileInfo &info = oldPreheader->info();
    if (header->trackedSite().pc()) {
        unrolledHeader =
            MBasicBlock::New(graph, nullptr, info,
                             oldPreheader, header->trackedSite(), MBasicBlock::LOOP_HEADER);
        unrolledBackedge =
            MBasicBlock::New(graph, nullptr, info,
                             unrolledHeader, backedge->trackedSite(), MBasicBlock::NORMAL);
        newPreheader =
            MBasicBlock::New(graph, nullptr, info,
                             unrolledHeader, oldPreheader->trackedSite(), MBasicBlock::NORMAL);
    } else {
        unrolledHeader = MBasicBlock::NewAsmJS(graph, info, oldPreheader, MBasicBlock::LOOP_HEADER);
        unrolledBackedge = MBasicBlock::NewAsmJS(graph, info, unrolledHeader, MBasicBlock::NORMAL);
        newPreheader = MBasicBlock::NewAsmJS(graph, info, unrolledHeader, MBasicBlock::NORMAL);
    }

    unrolledHeader->discardAllResumePoints();
    unrolledBackedge->discardAllResumePoints();
    newPreheader->discardAllResumePoints();

    // Insert new blocks at their RPO position, and update block ids.
    graph.insertBlockAfter(oldPreheader, unrolledHeader);
    graph.insertBlockAfter(unrolledHeader, unrolledBackedge);
    graph.insertBlockAfter(unrolledBackedge, newPreheader);
    graph.renumberBlocksAfter(oldPreheader);

    if (!unrolledDefinitions.init())
        CrashAtUnhandlableOOM("LoopUnroller::go");

    // Add phis to the unrolled loop header which correspond to the phis in the
    // original loop header.
    JS_ASSERT(header->getPredecessor(0) == oldPreheader);
    for (MPhiIterator iter(header->phisBegin()); iter != header->phisEnd(); iter++) {
        MPhi *old = *iter;
        JS_ASSERT(old->numOperands() == 2);
        MPhi *phi = MPhi::New(alloc);
        phi->setResultType(old->type());
        phi->setResultTypeSet(old->resultTypeSet());
        phi->setRange(old->range());

        unrolledHeader->addPhi(phi);

        if (!phi->reserveLength(2))
            CrashAtUnhandlableOOM("LoopUnroller::go");

        // Set the first input for the phi for now. We'll set the second after
        // finishing the unroll.
        phi->addInput(old->getOperand(0));

        // The old phi will now take the value produced by the unrolled loop.
        old->replaceOperand(0, phi);

        if (!unrolledDefinitions.putNew(old, phi))
            CrashAtUnhandlableOOM("LoopUnroller::go");
    }

    // The loop condition can bail out on e.g. integer overflow, so make a
    // resume point based on the initial resume point of the original header.
    MResumePoint *headerResumePoint = header->entryResumePoint();
    if (headerResumePoint) {
        MResumePoint *rp = makeReplacementResumePoint(unrolledHeader, headerResumePoint);
        unrolledHeader->setEntryResumePoint(rp);

        // Perform an interrupt check at the start of the unrolled loop.
        unrolledHeader->add(MInterruptCheck::New(alloc));
    }

    // Generate code for the test in the unrolled loop.
    for (size_t i = 0; i < remainingIterationsInequality.numTerms(); i++) {
        MDefinition *def = remainingIterationsInequality.term(i).term;
        MDefinition *replacement = getReplacementDefinition(def);
        remainingIterationsInequality.replaceTerm(i, replacement);
    }
    MCompare *compare = ConvertLinearInequality(alloc, unrolledHeader, remainingIterationsInequality);
    MTest *unrolledTest = MTest::New(alloc, compare, unrolledBackedge, newPreheader);
    unrolledHeader->end(unrolledTest);

    // Make an entry resume point for the unrolled body. The unrolled header
    // does not have side effects on stack values, even if the original loop
    // header does, so use the same resume point as for the unrolled header.
    if (headerResumePoint) {
        MResumePoint *rp = makeReplacementResumePoint(unrolledBackedge, headerResumePoint);
        unrolledBackedge->setEntryResumePoint(rp);
    }

    // Make an entry resume point for the new preheader. There are no
    // instructions which use this but some other stuff wants one to be here.
    if (headerResumePoint) {
        MResumePoint *rp = makeReplacementResumePoint(newPreheader, headerResumePoint);
        newPreheader->setEntryResumePoint(rp);
    }

    // Generate the unrolled code.
    JS_ASSERT(UnrollCount > 1);
    size_t unrollIndex = 0;
    while (true) {
        // Clone the contents of the original loop into the unrolled loop body.
        for (size_t i = 0; i < ArrayLength(bodyBlocks); i++) {
            MBasicBlock *block = bodyBlocks[i];
            for (MInstructionIterator iter(block->begin()); iter != block->end(); iter++) {
                MInstruction *ins = *iter;
                if (ins->canClone()) {
                    makeReplacementInstruction(*iter);
                } else {
                    // Control instructions are handled separately.
                    JS_ASSERT(ins->isTest() || ins->isGoto() || ins->isInterruptCheck());
                }
            }
        }

        // Compute the value of each loop header phi after the execution of
        // this unrolled iteration.
        MDefinitionVector phiValues(alloc);
        JS_ASSERT(header->getPredecessor(1) == backedge);
        for (MPhiIterator iter(header->phisBegin()); iter != header->phisEnd(); iter++) {
            MPhi *old = *iter;
            MDefinition *oldInput = old->getOperand(1);
            if (!phiValues.append(getReplacementDefinition(oldInput)))
                CrashAtUnhandlableOOM("LoopUnroller::go");
        }

        unrolledDefinitions.clear();

        if (unrollIndex == UnrollCount - 1) {
            // We're at the end of the last unrolled iteration, set the
            // backedge input for the unrolled loop phis.
            size_t phiIndex = 0;
            for (MPhiIterator iter(unrolledHeader->phisBegin()); iter != unrolledHeader->phisEnd(); iter++) {
                MPhi *phi = *iter;
                phi->addInput(phiValues[phiIndex++]);
            }
            JS_ASSERT(phiIndex == phiValues.length());
            break;
        }

        // Update the map for the phis in the next iteration.
        size_t phiIndex = 0;
        for (MPhiIterator iter(header->phisBegin()); iter != header->phisEnd(); iter++) {
            MPhi *old = *iter;
            if (!unrolledDefinitions.putNew(old, phiValues[phiIndex++]))
                CrashAtUnhandlableOOM("LoopUnroller::go");
        }
        JS_ASSERT(phiIndex == phiValues.length());

        unrollIndex++;
    }

    MGoto *backedgeJump = MGoto::New(alloc, unrolledHeader);
    unrolledBackedge->end(backedgeJump);

    // Place the old preheader before the unrolled loop.
    JS_ASSERT(oldPreheader->lastIns()->isGoto());
    oldPreheader->discardLastIns();
    oldPreheader->end(MGoto::New(alloc, unrolledHeader));

    // Place the new preheader before the original loop.
    newPreheader->end(MGoto::New(alloc, header));

    // Cleanup the MIR graph.
    if (!unrolledHeader->addPredecessorWithoutPhis(unrolledBackedge))
        CrashAtUnhandlableOOM("LoopUnroller::go");
    header->replacePredecessor(oldPreheader, newPreheader);
    oldPreheader->setSuccessorWithPhis(unrolledHeader, 0);
    newPreheader->setSuccessorWithPhis(header, 0);
    unrolledBackedge->setSuccessorWithPhis(unrolledHeader, 1);
}
Example #14
0
Loop::LoopReturn
Loop::init()
{
    IonSpew(IonSpew_LICM, "Loop identified, headed by block %d", header_->id());
    IonSpew(IonSpew_LICM, "footer is block %d", header_->backedge()->id());

    // The first predecessor of the loop header must dominate the header.
    JS_ASSERT(header_->id() > header_->getPredecessor(0)->id());

    // Loops from backedge to header and marks all visited blocks
    // as part of the loop. At the same time add all hoistable instructions
    // (in RPO order) to the instruction worklist.
    Vector<MBasicBlock *, 1, IonAllocPolicy> inlooplist;
    if (!inlooplist.append(header_->backedge()))
        return LoopReturn_Error;
    header_->backedge()->mark();

    while (!inlooplist.empty()) {
        MBasicBlock *block = inlooplist.back();

        // Hoisting requires more finesse if the loop contains a block that
        // self-dominates: there exists control flow that may enter the loop
        // without passing through the loop preheader.
        //
        // Rather than perform a complicated analysis of the dominance graph,
        // just return a soft error to ignore this loop.
        if (block->immediateDominator() == block) {
            while (!worklist_.empty())
                popFromWorklist();
            return LoopReturn_Skip;
        }

        // Add not yet visited predecessors to the inlooplist.
        if (block != header_) {
            for (size_t i = 0; i < block->numPredecessors(); i++) {
                MBasicBlock *pred = block->getPredecessor(i);
                if (pred->isMarked())
                    continue;

                if (!inlooplist.append(pred))
                    return LoopReturn_Error;
                pred->mark();
            }
        }

        // If any block was added, process them first.
        if (block != inlooplist.back())
            continue;

        // Add all instructions in this block (but the control instruction) to the worklist
        for (MInstructionIterator i = block->begin(); i != block->end(); i++) {
            MInstruction *ins = *i;

            if (isHoistable(ins)) {
                if (!insertInWorklist(ins))
                    return LoopReturn_Error;
            }
        }

        // All successors of this block are visited.
        inlooplist.popBack();
    }

    return LoopReturn_Success;
}
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(graph_.alloc(), succ);
        block->discardLastIns();
        block->end(gotoIns);
        MBasicBlock *successorWithPhis = block->successorWithPhis();
        if (successorWithPhis && successorWithPhis != succ)
            block->setSuccessorWithPhis(nullptr, 0);
    }

    return true;
}
Example #16
0
bool
MIRGraph::removeSuccessorBlocks(MBasicBlock* start)
{
    if (!start->hasLastIns())
        return true;

    start->mark();

    // Mark all successors.
    Vector<MBasicBlock*, 4, SystemAllocPolicy> blocks;
    for (size_t i = 0; i < start->numSuccessors(); i++) {
        if (start->getSuccessor(i)->isMarked())
            continue;
        if (!blocks.append(start->getSuccessor(i)))
            return false;
        start->getSuccessor(i)->mark();
    }
    for (size_t i = 0; i < blocks.length(); i++) {
        MBasicBlock* block = blocks[i];
        if (!block->hasLastIns())
            continue;

        for (size_t j = 0; j < block->numSuccessors(); j++) {
            if (block->getSuccessor(j)->isMarked())
                continue;
            if (!blocks.append(block->getSuccessor(j)))
                return false;
            block->getSuccessor(j)->mark();
        }
    }

    if (osrBlock()) {
        if (osrBlock()->getSuccessor(0)->isMarked())
            osrBlock()->mark();
    }

    // Remove blocks.
    // If they don't have any predecessor
    for (size_t i = 0; i < blocks.length(); i++) {
        MBasicBlock* block = blocks[i];
        bool allMarked = true;
        for (size_t i = 0; i < block->numPredecessors(); i++) {
            if (block->getPredecessor(i)->isMarked())
                continue;
            allMarked = false;
            break;
        }
        if (allMarked) {
            removeBlock(block);
        } else {
            MOZ_ASSERT(block != osrBlock());
            for (size_t j = 0; j < block->numPredecessors(); ) {
                if (!block->getPredecessor(j)->isMarked()) {
                    j++;
                    continue;
                }
                block->removePredecessor(block->getPredecessor(j));
            }
            // This shouldn't have any instructions yet.
            MOZ_ASSERT(block->begin() == block->end());
        }
    }

    if (osrBlock()) {
        if (osrBlock()->getSuccessor(0)->isDead())
            removeBlock(osrBlock());
    }

    for (size_t i = 0; i < blocks.length(); i++)
        blocks[i]->unmark();
    start->unmark();

    return true;
}
Example #17
0
MBasicBlock*
MBasicBlock::NewSplitEdge(MIRGraph& graph, MBasicBlock* pred, size_t predEdgeIdx, MBasicBlock* succ)
{
    MBasicBlock* split = nullptr;
    if (!succ->pc()) {
        // The predecessor does not have a PC, this is a Wasm compilation.
        split = MBasicBlock::New(graph, succ->info(), pred, SPLIT_EDGE);
        if (!split)
            return nullptr;
    } else {
        // The predecessor has a PC, this is an IonBuilder compilation.
        MResumePoint* succEntry = succ->entryResumePoint();

        BytecodeSite* site = new(graph.alloc()) BytecodeSite(succ->trackedTree(), succEntry->pc());
        split = new(graph.alloc()) MBasicBlock(graph, succ->info(), site, SPLIT_EDGE);

        if (!split->init())
            return nullptr;

        // A split edge is used to simplify the graph to avoid having a
        // predecessor with multiple successors as well as a successor with
        // multiple predecessors.  As instructions can be moved in this
        // split-edge block, we need to give this block a resume point. To do
        // so, we copy the entry resume points of the successor and filter the
        // phis to keep inputs from the current edge.

        // Propagate the caller resume point from the inherited block.
        split->callerResumePoint_ = succ->callerResumePoint();

        // Split-edge are created after the interpreter stack emulation. Thus,
        // there is no need for creating slots.
        split->stackPosition_ = succEntry->stackDepth();

        // Create a resume point using our initial stack position.
        MResumePoint* splitEntry = new(graph.alloc()) MResumePoint(split, succEntry->pc(),
                                                                   MResumePoint::ResumeAt);
        if (!splitEntry->init(graph.alloc()))
            return nullptr;
        split->entryResumePoint_ = splitEntry;

        // The target entry resume point might have phi operands, keep the
        // operands of the phi coming from our edge.
        size_t succEdgeIdx = succ->indexForPredecessor(pred);

        for (size_t i = 0, e = splitEntry->numOperands(); i < e; i++) {
            MDefinition* def = succEntry->getOperand(i);
            // This early in the pipeline, we have no recover instructions in
            // any entry resume point.
            MOZ_ASSERT_IF(def->block() == succ, def->isPhi());
            if (def->block() == succ)
                def = def->toPhi()->getOperand(succEdgeIdx);

            splitEntry->initOperand(i, def);
        }

        // This is done in the New variant for wasm, so we cannot keep this
        // line below, where the rest of the graph is modified.
        if (!split->predecessors_.append(pred))
            return nullptr;
    }

    split->setLoopDepth(succ->loopDepth());

    // Insert the split edge block in-between.
    split->end(MGoto::New(graph.alloc(), succ));

    graph.insertBlockAfter(pred, split);

    pred->replaceSuccessor(predEdgeIdx, split);
    succ->replacePredecessor(pred, split);
    return split;
}
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(nullptr, 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)->hasOneDefUse());
                        MPassArg *arg = call->getArg(i)->toPassArg();
                        arg->replaceAllUsesWith(arg->getArgument());
                    }
                }
            }

            graph_.removeBlock(block);
        }
    }

    JS_ASSERT(id == 0);

    return true;
}