Example #1
0
// Visit |def|.
bool
ValueNumberer::visitDefinition(MDefinition* def)
{
    // Nop does not fit in any of the previous optimization, as its only purpose
    // is to reduce the register pressure by keeping additional resume
    // point. Still, there is no need consecutive list of MNop instructions, and
    // this will slow down every other iteration on the Graph.
    if (def->isNop()) {
        MNop* nop = def->toNop();
        MBasicBlock* block = nop->block();

        // We look backward to know if we can remove the previous Nop, we do not
        // look forward as we would not benefit from the folding made by GVN.
        MInstructionReverseIterator iter = ++block->rbegin(nop);

        // This nop is at the beginning of the basic block, just replace the
        // resume point of the basic block by the one from the resume point.
        if (iter == block->rend()) {
            JitSpew(JitSpew_GVN, "      Removing Nop%u", nop->id());
            nop->moveResumePointAsEntry();
            block->discard(nop);
            return true;
        }

        // The previous instruction is also a Nop, no need to keep it anymore.
        MInstruction* prev = *iter;
        if (prev->isNop()) {
            JitSpew(JitSpew_GVN, "      Removing Nop%u", prev->id());
            block->discard(prev);
            return true;
        }

        // The Nop is introduced to capture the result and make sure the operands
        // are not live anymore when there are no further uses. Though when
        // all operands are still needed the Nop doesn't decrease the liveness
        // and can get removed.
        MResumePoint* rp = nop->resumePoint();
        if (rp && rp->numOperands() > 0 &&
            rp->getOperand(rp->numOperands() - 1) == prev &&
            !nop->block()->lastIns()->isThrow() &&
            !prev->isAssertRecoveredOnBailout())
        {
            size_t numOperandsLive = 0;
            for (size_t j = 0; j < prev->numOperands(); j++) {
                for (size_t i = 0; i < rp->numOperands(); i++) {
                    if (prev->getOperand(j) == rp->getOperand(i)) {
                        numOperandsLive++;
                        break;
                    }
                }
            }

            if (numOperandsLive == prev->numOperands()) {
                JitSpew(JitSpew_GVN, "      Removing Nop%u", nop->id());
                block->discard(nop);
            }
        }

        return true;
    }

    // Skip optimizations on instructions which are recovered on bailout, to
    // avoid mixing instructions which are recovered on bailouts with
    // instructions which are not.
    if (def->isRecoveredOnBailout())
        return true;

    // If this instruction has a dependency() into an unreachable block, we'll
    // need to update AliasAnalysis.
    MDefinition* dep = def->dependency();
    if (dep != nullptr && (dep->isDiscarded() || dep->block()->isDead())) {
        JitSpew(JitSpew_GVN, "      AliasAnalysis invalidated");
        if (updateAliasAnalysis_ && !dependenciesBroken_) {
            // TODO: Recomputing alias-analysis could theoretically expose more
            // GVN opportunities.
            JitSpew(JitSpew_GVN, "        Will recompute!");
            dependenciesBroken_ = true;
        }
        // Temporarily clear its dependency, to protect foldsTo, which may
        // wish to use the dependency to do store-to-load forwarding.
        def->setDependency(def->toInstruction());
    } else {
        dep = nullptr;
    }

    // Look for a simplified form of |def|.
    MDefinition* sim = simplified(def);
    if (sim != def) {
        if (sim == nullptr)
            return false;

        bool isNewInstruction = sim->block() == nullptr;

        // If |sim| doesn't belong to a block, insert it next to |def|.
        if (isNewInstruction)
            def->block()->insertAfter(def->toInstruction(), sim->toInstruction());

#ifdef JS_JITSPEW
        JitSpew(JitSpew_GVN, "      Folded %s%u to %s%u",
                def->opName(), def->id(), sim->opName(), sim->id());
#endif
        MOZ_ASSERT(!sim->isDiscarded());
        ReplaceAllUsesWith(def, sim);

        // The node's foldsTo said |def| can be replaced by |rep|. If |def| is a
        // guard, then either |rep| is also a guard, or a guard isn't actually
        // needed, so we can clear |def|'s guard flag and let it be discarded.
        def->setNotGuardUnchecked();

        if (def->isGuardRangeBailouts())
            sim->setGuardRangeBailoutsUnchecked();

        if (DeadIfUnused(def)) {
            if (!discardDefsRecursively(def))
                return false;

            // If that ended up discarding |sim|, then we're done here.
            if (sim->isDiscarded())
                return true;
        }

        if (!rerun_ && def->isPhi() && !sim->isPhi()) {
            rerun_ = true;
            JitSpew(JitSpew_GVN, "      Replacing phi%u may have enabled cascading optimisations; "
                                 "will re-run", def->id());
        }

        // Otherwise, procede to optimize with |sim| in place of |def|.
        def = sim;

        // If the simplified instruction was already part of the graph, then we
        // probably already visited and optimized this instruction.
        if (!isNewInstruction)
            return true;
    }

    // Now that foldsTo is done, re-enable the original dependency. Even though
    // it may be pointing into a discarded block, it's still valid for the
    // purposes of detecting congruent loads.
    if (dep != nullptr)
        def->setDependency(dep);

    // Look for a dominating def which makes |def| redundant.
    MDefinition* rep = leader(def);
    if (rep != def) {
        if (rep == nullptr)
            return false;
        if (rep->updateForReplacement(def)) {
#ifdef JS_JITSPEW
            JitSpew(JitSpew_GVN,
                    "      Replacing %s%u with %s%u",
                    def->opName(), def->id(), rep->opName(), rep->id());
#endif
            ReplaceAllUsesWith(def, rep);

            // The node's congruentTo said |def| is congruent to |rep|, and it's
            // dominated by |rep|. If |def| is a guard, it's covered by |rep|,
            // so we can clear |def|'s guard flag and let it be discarded.
            def->setNotGuardUnchecked();

            if (DeadIfUnused(def)) {
                // discardDef should not add anything to the deadDefs, as the
                // redundant operation should have the same input operands.
                mozilla::DebugOnly<bool> r = discardDef(def);
                MOZ_ASSERT(r, "discardDef shouldn't have tried to add anything to the worklist, "
                              "so it shouldn't have failed");
                MOZ_ASSERT(deadDefs_.empty(),
                           "discardDef shouldn't have added anything to the worklist");
            }
            def = rep;
        }
    }

    return true;
}