Example #1
0
PhaseScope::~PhaseScope()
{
    m_code.setLastPhaseName(m_name);
    if (shouldValidateIRAtEachPhase())
        validate(m_code, m_dumpBefore.data());
}
bool simplifyCFG(Code& code)
{
    const bool verbose = false;

    PhaseScope phaseScope(code, "simplifyCFG");

    // We have three easy simplification rules:
    //
    // 1) If a successor is a block that just jumps to another block, then jump directly to
    //    that block.
    //
    // 2) If all successors are the same and the operation has no effects, then use a jump
    //    instead.
    //
    // 3) If you jump to a block that is not you and has one predecessor, then merge.
    //
    // Note that because of the first rule, this phase may introduce critical edges. That's fine.
    // If you need broken critical edges, then you have to break them yourself.

    bool result = false;
    for (;;) {
        if (verbose) {
            dataLog("Air before an iteration of simplifyCFG:\n");
            dataLog(code);
        }

        bool changed = false;
        for (BasicBlock* block : code) {
            // We rely on predecessors being conservatively correct. Verify this here.
            if (shouldValidateIRAtEachPhase()) {
                for (BasicBlock* block : code) {
                    for (BasicBlock* successor : block->successorBlocks())
                        RELEASE_ASSERT(successor->containsPredecessor(block));
                }
            }

            // We don't care about blocks that don't have successors.
            if (!block->numSuccessors())
                continue;

            // First check if any of the successors of this block can be forwarded over.
            for (BasicBlock*& successor : block->successorBlocks()) {
                if (successor != block
                    && successor->size() == 1
                    && successor->last().opcode == Jump) {
                    BasicBlock* newSuccessor = successor->successorBlock(0);
                    if (newSuccessor != successor) {
                        if (verbose) {
                            dataLog(
                                "Replacing ", pointerDump(block), "->", pointerDump(successor),
                                " with ", pointerDump(block), "->", pointerDump(newSuccessor), "\n");
                        }
                        // Note that we do not do replacePredecessor() because the block we're
                        // skipping will still have newSuccessor as its successor.
                        newSuccessor->addPredecessor(block);
                        successor = newSuccessor;
                        changed = true;
                    }
                }
            }

            // Now check if the block's terminal can be replaced with a jump.
            if (block->numSuccessors() > 1) {
                // The terminal must not have weird effects.
                if (!block->last().hasArgEffects()
                    && !block->last().hasNonArgNonControlEffects()) {
                    // All of the successors must be the same.
                    bool allSame = true;
                    BasicBlock* firstSuccessor = block->successorBlock(0);
                    for (unsigned i = 1; i < block->numSuccessors(); ++i) {
                        if (block->successorBlock(i) != firstSuccessor) {
                            allSame = false;
                            break;
                        }
                    }
                    if (allSame) {
                        if (verbose)
                            dataLog("Changing ", pointerDump(block), "'s terminal to a Jump.\n");
                        block->last() = Inst(Jump, block->last().origin);
                        block->successors().resize(1);
                        block->successors()[0].frequency() = FrequencyClass::Normal;
                        changed = true;
                    }
                }
            }

            // Finally handle jumps to a block with one predecessor.
            if (block->numSuccessors() == 1) {
                BasicBlock* successor = block->successorBlock(0);
                if (successor != block && successor->numPredecessors() == 1) {
                    RELEASE_ASSERT(successor->predecessor(0) == block);

                    // We can merge the two blocks because the predecessor only jumps to the successor
                    // and the successor is only reachable from the predecessor.

                    // Remove the terminal.
                    Value* origin = block->insts().takeLast().origin;

                    // Append the full contents of the successor to the predecessor.
                    block->insts().reserveCapacity(block->size() + successor->size());
                    for (Inst& inst : *successor)
                        block->appendInst(WTFMove(inst));

                    // Make sure that our successors are the successor's successors.
                    block->successors() = WTFMove(successor->successors());

                    // Make sure that the successor has nothing left in it except an oops.
                    successor->resize(1);
                    successor->last() = Inst(Oops, origin);
                    successor->successors().clear();

                    // Ensure that the predecessors of block's new successors know what's up.
                    for (BasicBlock* newSuccessor : block->successorBlocks())
                        newSuccessor->replacePredecessor(successor, block);

                    if (verbose)
                        dataLog("Merged ", pointerDump(block), "->", pointerDump(successor), "\n");
                    changed = true;
                }
            }
        }

        if (!changed)
            break;
        result = true;
        code.resetReachability();
    }

    return result;
}