This package includes classes for building control-flow graphs. While the basics were designed with Java in mind, the classes here are not specific to Java. Java-specific additions to the classes are defined in {@link fluid.java.control} and in specific operator files in {@link fluid.java.operator}. Java specific analyses using these nodes are defined in {@link fluid.java.analysis}

The Control-Flow Graph (CFG)

The control-flow graph is built with explicit nodes and edges and can be traversed in either direction. (The alert reader may notice that this implies the structure is a symmetric edge directed graph. We are in the process of fitting the graph to the {@link fluid.tree.SymmetricEdgeDigraphInterface} interface.) The edges carry analysis information and inherit from {@link fluid.control.ControlEdge}. The nodes potentially affect analysis information flowing through them; they inherit from {@link fluid.control.ControlNode}. Apart from ports (see below), all control nodes fall into one of five abstract classes depending on how many input edges and output edges they have, as pictorially represented in the following diagram:

     			+-@--{@link fluid.control.Flow}--@-+
     		       /      	      \
     {@link fluid.control.Source}---@---{@link fluid.control.Split}	      	       {@link fluid.control.Join}---@---{@link fluid.control.Sink}
		       \      	      /
			+-----@------+
(If one needs, say, a three way split, one must use two split nodes.) The actual class of node determines the semantic action. For example a {@link fluid.control.Merge} node (inheriting from {@link fluid.control.Join}) leads to the ``meeting'' of two lattice values in forward data-flow analysis. For a complete list of nodes other than ports, see below.

This package includes the capability to place two or more independent flows on the same edge. For example a {@link fluid.control.TrackedMerge} node can be used to join two control flows into a single edge. Later in the graph, a {@link fluid.control.TrackedDemerge} node can be used to separate them. A good example of using this for Java is for finally clauses of throw statements: no matter whether the try clause terminates normally or abruptly, the finally clause is always executed. Rather than copy the finally clause for the two flows, or worse, to merge them, the control-flow graph gives each an independent flow of control through the finally clause. When the clause terminates, the flows are separated again. The ability to have independent flows on a single edge is also used to separate exception throws from control-flow caused by break or continue. Internally each flow is named by a sequence (see {@link fluid.control.LabelList}) of control-flow labels (see {@link fluid.control.ControlLabel}).

Constructing the Control-Flow Graph

The control-flow graph is highly structured, following the abstract syntax of the program. Each abstract syntax node has an associate control flow component (see {@link fluid.control.Component}). All control flow enters or leaves a component only through a fixed number of ports (see {@link fluid.control.Port}). That means even control flow caused by thrown exceptions or method returns must be directed through some port. Each component has three ports, each of a different type:

{@link fluid.control.EntryPort}
Control flow enters here.
{@link fluid.control.NormalExitPort}
Normal control-flow exits here.
{@link fluid.control.AbruptExitPort}
Abrupt termination (exceptions and non-local control flow) uses this port.

The control-flow graph is constructed by associating a fixed control-flow component with every syntax node participating in control-flow. The association is carried out by an instance of {@link fluid.control.ComponentFactory}. If a node has children which participate in control flow, then its component includes subcomponents (see {@link fluid.control.Subcomponent}) which are in essence holes in the control-flow graph. The subcomponent has ports that correspond to the ports of the component for the corresponding child. The ports of the subcomponent are never directly connected to the ports of the component of the child. Instead, the connection is computed whenever analysis comes up to a port. For instance, if analysis follows control-flow out through a port of a subcomponent, the appropriate child node of the current node is found and its component determined. Then analysis continues with the appropriate port of this component. This all happens invisibly to individual analyses. This indirection enables the control-flow graph to be up-to-date at all times.

Handling nodes with a varying number of children is more difficult. All the children are linked together with a certain number of edges (specified by the component). The component (which must be an instance of the class {@link fluid.control.VariableComponent}) also determines how control-flow enters and leaves the sequence and for every child how the sequence edges connect up to its ports (or not).

Summary

Here is a listing of all node types except for ports:

{@link fluid.control.Source}
{@link fluid.control.Sink}
{@link fluid.control.Flow}
{@link fluid.control.Split}
{@link fluid.control.Join}
See {@link fluid.control.Port} for a breakdown of the various port classes.

Control-Flow Analysis

This package also includes control-flow analysis template classes. The basic worklisting algorithm is specified in the abstract class {@link fluid.control.FlowAnalysis}. This class is refined in {@link fluid.control.ForwardAnalysis} and {@link fluid.control.BackwardAnalysis}. These two classes must be parameterized by classes implementing {@link fluid.control.ForwardTransfer} and {@link fluid.control.BackwardTransfer} respectively. The transfer classes implement the language-specific actions. All other details are handled by these generic classes: ports, components, and labels. We will discuss only labels here.

An edge can have a different control-flow analysis value for any sequence of labels. These sequences are affected by only three control-flow nodes: {@link fluid.control.AddLabel}, {@link fluid.control.LabelTest}, and {@link fluid.control.PendingLabelStrip}. The sequence is treated as a stack: we only make changes to the front, adding or removing the first label. During analysis, the actual effect on the sequence is determined by the direction of analysis (forward or backward) and by language specific actions (as we shall see):

AddLabel (push)
forward
Prepend the specified label to the sequence.
backward
See if the first label of the sequence overlaps the specific label. In other words, see if the first label could have been added at this point. It is not a simple case of equality, because of cases where we don't know precisely the exception thrown of what abrupt termination condition we are looking for (see use of UnknownControlLabel in the discussion of PendingLabelStrip). The method testAddLabel in {@link fluid.control.BackwardTransfer} does this operation.
LabelTest (if match then pop, continue(1) else continue(2))
forward
Check if the first label is one being tested for, and also check to see if the first label might not be the one tested for. If cases of uncertainty, we may end up trying both ways. The method transferLabelTest does the work here.
backward
If coming from the `true' side, add the label being tested for and continue backwards, otherwise just continue backwards. (This is not as precise as it could be in the second case.)
PendingLabelStrip (swap, if pop then nop else swap, pop endif)
forward
Remove and save the first label. Remove the next label, which must be a tracking label, if
true
put back saved label
false
drop next label and put back saved label
backward
Remove and save the first label. Then generate two label lists for the incoming edge:
  1. One that adds the true tracking label and then adds the saved label
  2. One that adds an unknown label, then the false tracking label and then the saved label
This node is only used with {@link fluid.control.TrackedMerge} and {@link fluid.control.TrackedDemerge} nodes. It is a complex operation, that perhaps should be expressed using more nodes, but doing so would simply result in a larger graph.