All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jruby.ir.representations.CFG Maven / Gradle / Ivy

There is a newer version: 9.4.9.0
Show newest version
package org.jruby.ir.representations;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import org.jruby.ir.IRClosure;
import org.jruby.ir.IRScope;
import org.jruby.ir.Operation;
import org.jruby.ir.instructions.BranchInstr;
import org.jruby.ir.instructions.CallBase;
import org.jruby.ir.instructions.ExceptionRegionEndMarkerInstr;
import org.jruby.ir.instructions.ExceptionRegionStartMarkerInstr;
import org.jruby.ir.instructions.Instr;
import org.jruby.ir.instructions.JumpIndirectInstr;
import org.jruby.ir.instructions.JumpInstr;
import org.jruby.ir.instructions.LabelInstr;
import org.jruby.ir.instructions.SetReturnAddressInstr;
import org.jruby.ir.instructions.ThrowExceptionInstr;
import org.jruby.ir.operands.Label;
import org.jruby.ir.operands.Operand;
import org.jruby.ir.operands.Variable;
import org.jruby.ir.operands.WrappedIRClosure;
import org.jruby.ir.transformations.inlining.InlinerInfo;
import org.jruby.ir.util.DirectedGraph;
import org.jruby.ir.util.Edge;
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;

/**
 * Represents the base build of a CFG.  All information here is accessed via
 * delegation from the CFG itself so this is meant as an internal
 * organizational structure for a build.
 */
public class CFG {
    public enum EdgeType {
        REGULAR,       // Any non-special edge.  Not really used.
        EXCEPTION,     // Edge to exception handling basic blocks
        FALL_THROUGH,  // Edge which is the natural fall through choice on a branch
        EXIT           // Edge to dummy exit BB
    }

    private static final Logger LOG = LoggerFactory.getLogger("CFG");

    private IRScope scope;
    private Map bbMap;

    // Map of bb -> first bb of the rescue block that initiates exception handling for all exceptions thrown within this bb
    private Map rescuerMap;

    // Map of bb -> first bb of the ensure block that protects this bb
    private Map ensurerMap;

    private List outermostERs;

    /** Entry BB */
    private BasicBlock entryBB;

    /** Exit BB */
    private BasicBlock exitBB;

    /** BB that traps all exception-edges out of the cfg where we could add any cleanup/ensure code (ex: pop frames, etc.) */
    private BasicBlock globalEnsureBB;

    /** The graph itself */
    private DirectedGraph graph;

    private int nextBBId;       // Next available basic block id

    LinkedList postOrderList; // Post order traversal list of the cfg

    public CFG(IRScope scope) {
        this.scope = scope;
        this.graph = new DirectedGraph();
        this.bbMap = new HashMap();
        this.rescuerMap = new HashMap();
        this.ensurerMap = new HashMap();
        this.outermostERs = new ArrayList();
        this.nextBBId = 0;
        this.entryBB = this.exitBB = null;
        this.globalEnsureBB = null;
        this.postOrderList = null;
    }

    public int getNextBBID() {
        nextBBId++;
        return nextBBId;
    }

    public int getMaxNodeID() {
        return nextBBId;
    }

    public boolean bbIsProtected(BasicBlock b) {
        // No need to look in ensurerMap because (_bbEnsurerMap(b) != null) => (_bbResucerMap(b) != null)
        return getRescuerBBFor(b) != null;
    }

    public BasicBlock getBBForLabel(Label label) {
        return bbMap.get(label);
    }

    public BasicBlock getEnsurerBBFor(BasicBlock block) {
        return ensurerMap.get(block);
    }

    public BasicBlock getEntryBB() {
        return entryBB;
    }

    public BasicBlock getExitBB() {
        return exitBB;
    }

    public BasicBlock getGlobalEnsureBB() {
        return globalEnsureBB;
    }

    public List getOutermostExceptionRegions() {
        return outermostERs;
    }

    public LinkedList postOrderList() {
        if (postOrderList == null) postOrderList = buildPostOrderList();
        return postOrderList;
    }

    public ListIterator getPostOrderTraverser() {
        return postOrderList().listIterator();
    }

    public ListIterator getReversePostOrderTraverser() {
        return postOrderList().listIterator(size());
    }

    public void resetState() {
        // SSS FIXME: anything else?
        postOrderList = null;
    }

    public IRScope getScope() {
        return scope;
    }

    public int size() {
        return graph.size();
    }

    public Collection getBasicBlocks() {
        return graph.allData();
    }

    public Collection getSortedBasicBlocks() {
        return graph.getInorderData();
    }

    public void addEdge(BasicBlock source, BasicBlock destination, Object type) {
        graph.vertexFor(source).addEdgeTo(destination, type);
    }

    public int inDegree(BasicBlock b) {
        return graph.findVertexFor(b).inDegree();
    }

    public int outDegree(BasicBlock b) {
        return graph.findVertexFor(b).outDegree();
    }

    public Iterable getIncomingSources(BasicBlock block) {
        return graph.findVertexFor(block).getIncomingSourcesData();
    }

    public Iterable> getIncomingEdges(BasicBlock block) {
        return graph.findVertexFor(block).getIncomingEdges();
    }

    public BasicBlock getIncomingSource(BasicBlock block) {
        return graph.findVertexFor(block).getIncomingSourceData();
    }

    public BasicBlock getIncomingSourceOfType(BasicBlock block, Object type) {
        return graph.findVertexFor(block).getIncomingSourceDataOfType(type);
    }

    public Edge getIncomingEdgeOfType(BasicBlock block, Object type) {
        return graph.findVertexFor(block).getIncomingEdgeOfType(type);
    }

    public Edge getOutgoingEdgeOfType(BasicBlock block, Object type) {
        return graph.findVertexFor(block).getOutgoingEdgeOfType(type);
    }

    public BasicBlock getOutgoingDestination(BasicBlock block) {
        return graph.findVertexFor(block).getOutgoingDestinationData();
    }

    public BasicBlock getOutgoingDestinationOfType(BasicBlock block, Object type) {
        return graph.findVertexFor(block).getOutgoingDestinationDataOfType(type);
    }

    public Iterable getOutgoingDestinations(BasicBlock block) {
        return graph.findVertexFor(block).getOutgoingDestinationsData();
    }

    public Iterable getOutgoingDestinationsOfType(BasicBlock block, Object type) {
        return graph.findVertexFor(block).getOutgoingDestinationsDataOfType(type);
    }

    public Iterable getOutgoingDestinationsNotOfType(BasicBlock block, Object type) {
        return graph.findVertexFor(block).getOutgoingDestinationsDataNotOfType(type);
    }

    public Set> getOutgoingEdges(BasicBlock block) {
        return graph.findVertexFor(block).getOutgoingEdges();
    }

    public Iterable> getOutgoingEdgesNotOfType(BasicBlock block, Object type) {
        return graph.findVertexFor(block).getOutgoingEdgesNotOfType(type);
    }

    public BasicBlock getRescuerBBFor(BasicBlock block) {
        return rescuerMap.get(block);
    }

    /* Add 'b' as a global ensure block that protects all unprotected blocks in this scope */
    public void addGlobalEnsureBB(BasicBlock geb) {
        assert globalEnsureBB == null: "CFG for scope " + getScope() + " already has a global ensure block.";

        globalEnsureBB = geb;

        addEdge(geb, getExitBB(), EdgeType.EXIT);

        for (BasicBlock b: getBasicBlocks()) {
            if (b != geb && !bbIsProtected(b)) {
                addEdge(b, geb, EdgeType.EXCEPTION);
                setRescuerBB(b, geb);
                setEnsurerBB(b, geb);
            }
        }

        // We are not creating a global exception region and adding it to the list of
        // exc. regions because in graph form, we dont know what the "last BB" is.
        // That requires a linearized form of the CFG's bbs.  So, the JIT can add
        // a special case for this global exception block since it has access to the
        // linearized form.
    }

    public void setEnsurerBB(BasicBlock block, BasicBlock ensureBlock) {
        ensurerMap.put(block, ensureBlock);
    }

    public void setRescuerBB(BasicBlock block, BasicBlock rescuerBlock) {
        rescuerMap.put(block, rescuerBlock);
    }

    /**
     *  Build the Control Flow Graph
     */
    public DirectedGraph build(List instrs) {
        // Map of label & basic blocks which are waiting for a bb with that label
        Map> forwardRefs = new HashMap>();

        // Map of return address variable and all possible targets (required to connect up ensure blocks with their targets)
        Map> retAddrMap = new HashMap>();
        Map retAddrTargetMap = new HashMap();

        // List of bbs that have a 'return' instruction
        List returnBBs = new ArrayList();

        // List of bbs that have a 'throw' instruction
        List exceptionBBs = new ArrayList();

        // Stack of nested rescue regions
        Stack nestedExceptionRegions = new Stack();

        // List of all rescued regions
        List allExceptionRegions = new ArrayList();

        // Dummy entry basic block (see note at end to see why)
        entryBB = createBB(nestedExceptionRegions);

        // First real bb
        BasicBlock firstBB = createBB(nestedExceptionRegions);

        // Build the rest!
        BasicBlock currBB = firstBB;
        BasicBlock newBB;
        boolean bbEnded = false;
        boolean nextBBIsFallThrough = true;
        for (Instr i : instrs) {
            Operation iop = i.getOperation();
            if (iop == Operation.LABEL) {
                Label l = ((LabelInstr) i).label;
                newBB = createBB(l, nestedExceptionRegions);
                // Jump instruction bbs dont add an edge to the succeeding bb by default
                if (nextBBIsFallThrough) graph.addEdge(currBB, newBB, EdgeType.FALL_THROUGH);
                currBB = newBB;
                bbEnded = false;
                nextBBIsFallThrough = true;

                // Add forward reference edges
                List frefs = forwardRefs.get(l);
                if (frefs != null) {
                    for (BasicBlock b : frefs) {
                        graph.addEdge(b, newBB, EdgeType.REGULAR);
                    }
                }
            } else if (bbEnded && (iop != Operation.EXC_REGION_END)) {
                newBB = createBB(nestedExceptionRegions);
                // Jump instruction bbs dont add an edge to the succeeding bb by default
                if (nextBBIsFallThrough) graph.addEdge(currBB, newBB, EdgeType.FALL_THROUGH); // currBB cannot be null!
                currBB = newBB;
                bbEnded = false;
                nextBBIsFallThrough = true;
            }

            if (i instanceof ExceptionRegionStartMarkerInstr) {
                // We dont need the instruction anymore -- so it is not added to the CFG.
                ExceptionRegionStartMarkerInstr ersmi = (ExceptionRegionStartMarkerInstr) i;
                ExceptionRegion rr = new ExceptionRegion(ersmi.firstRescueBlockLabel, ersmi.ensureBlockLabel, currBB);
                rr.addBB(currBB);
                allExceptionRegions.add(rr);

                if (nestedExceptionRegions.empty()) {
                    outermostERs.add(rr);
                } else {
                    nestedExceptionRegions.peek().addNestedRegion(rr);
                }

                nestedExceptionRegions.push(rr);
            } else if (i instanceof ExceptionRegionEndMarkerInstr) {
                // We dont need the instruction anymore -- so it is not added to the CFG.
                nestedExceptionRegions.pop().setEndBB(currBB);
            } else if (iop.endsBasicBlock()) {
                bbEnded = true;
                currBB.addInstr(i);
                Label tgt;
                nextBBIsFallThrough = false;
                if (i instanceof BranchInstr) {
                    tgt = ((BranchInstr) i).getJumpTarget();
                    nextBBIsFallThrough = true;
                } else if (i instanceof JumpInstr) {
                    tgt = ((JumpInstr) i).getJumpTarget();
                } else if (iop.isReturn()) { // BREAK, RETURN, CLOSURE_RETURN
                    tgt = null;
                    returnBBs.add(currBB);
                } else if (i instanceof ThrowExceptionInstr) {
                    tgt = null;
                    exceptionBBs.add(currBB);
                } else if (i instanceof JumpIndirectInstr) {
                    tgt = null;
                    Set




© 2015 - 2025 Weber Informatics LLC | Privacy Policy