package org.jruby.ir.dataflow;
import org.jruby.dirgra.Edge;
import org.jruby.ir.instructions.Instr;
import org.jruby.ir.representations.BasicBlock;
import org.jruby.ir.representations.CFG;
import java.util.BitSet;
import java.util.List;
import java.util.ListIterator;
/* This framework right now implicitly uses the CFG as the flow graph -- perhaps it is worth abstracting away from this assumption
* so that we can use this framework over non-CFG flow graphs.
*
* In any case, till such time we generalize this to other kinds of flow graphs, a flow graph node is just a wrapper over a CFG node,
* the basic block. As such, a flow graph is not explicitly constructed.
*
* Different dataflow problems encapsulate different dataflow properties, and a different flow graph node is built in each case */
public abstract class FlowGraphNode<T extends DataFlowProblem<T, U>, U extends FlowGraphNode<T, U>> {
public FlowGraphNode(T problem, BasicBlock basicBlock) {
this.problem = problem;
this.basicBlock = basicBlock;
// Cache the rescuer node for easy access
rescuer = problem.getFIC().getCFG().getRescuerBBFor(basicBlock);
}
Builds the data-flow variables (or facts) for a particular instruction.
/**
* Builds the data-flow variables (or facts) for a particular instruction.
*/
public abstract void buildDataFlowVars(Instr i);
Initialize this data flow node for solving the current problem
This is done after building dataflow variables for the problem.
/**
* Initialize this data flow node for solving the current problem
* This is done after building dataflow variables for the problem.
*/
public void init() { }
Initialize this data flow node to compute the new solution
This is done before iteratively calling the MEET operator.
/**
* Initialize this data flow node to compute the new solution
* This is done before iteratively calling the MEET operator.
*/
public abstract void applyPreMeetHandler();
"MEET" current solution of "IN/OUT" with "OUT/IN(pred)", where "pred"
is a predecessor of the current node! The choice of "IN/OUT" is
determined by the direction of data flow.
/**
* "MEET" current solution of "IN/OUT" with "OUT/IN(pred)", where "pred"
* is a predecessor of the current node! The choice of "IN/OUT" is
* determined by the direction of data flow.
*/
public abstract void compute_MEET(Edge e, U pred);
Any setting up of state/initialization before applying transfer function
/**
* Any setting up of state/initialization before applying transfer function
*/
public void initSolution() { }
Apply transfer function to the instruction
/**
* Apply transfer function to the instruction
*/
public abstract void applyTransferFunction(Instr i);
Did dataflow solution for this node change from last time?
/**
* Did dataflow solution for this node change from last time?
*/
public abstract boolean solutionChanged();
Any required cleanup of state after applying transfer function
/**
* Any required cleanup of state after applying transfer function
*/
public void finalizeSolution() { }
public BasicBlock getBB() {
return basicBlock;
}
Get the control flow graph
/**
* Get the control flow graph
*/
public CFG getCFG() {
return problem.fic.getCFG();
}
Builds the data-flow variables (or facts) for a particular node.
Need only create the DF_Var for them to be added to the problem.
Goes over the instructions in this basic block and collect
all relevant LOCAL data flow vars for this problem! /** Builds the data-flow variables (or facts) for a particular node.
Need only create the DF_Var for them to be added to the problem.
Goes over the instructions in this basic block and collect
all relevant LOCAL data flow vars for this problem! */
public void buildDataFlowVars() {
for (Instr i: basicBlock.getInstrs()) {
buildDataFlowVars(i);
}
}
private void processDestBB(List<U> workList, BitSet bbSet, BasicBlock d) {
int id = d.getID();
if (!bbSet.get(id)) {
bbSet.set(id);
workList.add(problem.getFlowGraphNode(d));
}
}
public void computeDataFlowInfo(List<U> workList, BitSet bbSet) {
if (problem.getFlowDirection() == DataFlowProblem.DF_Direction.BIDIRECTIONAL) {
throw new RuntimeException("Bidirectional data flow computation not implemented yet!");
}
// System.out.println("----- processing bb " + basicBlock.getID() + " -----");
bbSet.clear(basicBlock.getID());
// Compute meet over all "sources" and compute "destination" basic blocks that should then be processed.
// sources & targets depends on direction of the data flow problem
applyPreMeetHandler();
if (problem.getFlowDirection() == DataFlowProblem.DF_Direction.FORWARD) {
computeDataFlowInfoForward(workList, bbSet);
} else {
computeDataFlowInfoBackward(workList, bbSet);
}
}
public void computeDataFlowInfoBackward(List<U> workList, BitSet bbSet) {
for (Edge<BasicBlock> e: getCFG().getOutgoingEdges(basicBlock)) {
compute_MEET(e, problem.getFlowGraphNode(e.getDestination().getData()));
}
initSolution(); // Initialize computation
// Apply transfer function (analysis-specific) based on new facts after computing MEET
List<Instr> instrs = basicBlock.getInstrs();
ListIterator<Instr> it = instrs.listIterator(instrs.size());
while (it.hasPrevious()) {
Instr i = it.previous();
// System.out.println("TF: Processing: " + i);
applyTransferFunction(i);
}
// If the solution has changed, add "dsts" to the work list.
// No duplicates please which is why we have bbset.
if (solutionChanged()) {
for (BasicBlock b: getCFG().getIncomingSources(basicBlock)) {
processDestBB(workList, bbSet, b);
}
}
finalizeSolution(); // Any post-computation cleanup
}
public void computeDataFlowInfoForward(List<U> workList, BitSet bbSet) {
for (Edge<BasicBlock> e: getCFG().getIncomingEdges(basicBlock)) {
compute_MEET(e, problem.getFlowGraphNode(e.getSource().getData()));
}
initSolution(); // Initialize computation
// Apply transfer function (analysis-specific) based on new facts after computing MEET
for (Instr i : basicBlock.getInstrs()) {
// System.out.println("TF: Processing: " + i);
applyTransferFunction(i);
}
// If the solution has changed, add "dsts" to the work list.
// No duplicates please which is why we have bbset.
if (solutionChanged()) {
for (BasicBlock b: getCFG().getOutgoingDestinations(basicBlock)) {
processDestBB(workList, bbSet, b);
}
}
finalizeSolution(); // Any post-computation cleanup
}
public boolean hasExceptionsRescued() {
return rescuer != null;
}
public U getExceptionTargetNode() {
// If there is a rescue node, on exception, control goes to the rescuer bb. If not, it goes to the scope exit.
return problem.getFlowGraphNode(rescuer == null ? getCFG().getExitBB() : rescuer);
}
/* --------- protected fields/methods below --------- */
protected final T problem; // Dataflow problem with which this node is associated
protected final BasicBlock basicBlock; // CFG node for which this node contains info.
private final BasicBlock rescuer; // Basicblock that protects any exceptions raised in this node
}