When the flow values converge, we want to use or store the resulting dataflow
information. The framework makes a final pass over the program with the
last boolean set to true. This flag is a member of the
FlowProblem class and is used inside the transfer functions. For
example, if we are implementing reaching definitions, then we may want to
print out the list of definitions that reach each identifier. We override
the flow_id method, but only do something special when the last
flag is set:
void reaching_defs::flow_id(FlowVal * v, idNode * the_id, Point p)
{
my_flowval * rv = (my_flowval *)v;
if (last() && (p == Exit)) {
cout << the_id->name() << " at " << the_id->coord() << " reached by defs at:
" << endl;
for (int i = 0; i < rv->bits().size(); ++i)
if (rv->bits[i]) {
// Look up the def corresponding to bit i
}
}
flow_expr(v, the_id, p);
}
Notice that we also call the transfer function for the superclass to make sure that the flow values continue to be properly propagated.
In order to store the information for later use, we have three options.
First, we can store the flow values directly on the node using the
kill or gen members. This is a little tricky, though, because
we may still need the gen and kill sets. Second, we can store the information
we want in an auxiliary data structure. For example, we could construct a map
from idNodes to their reaching definitions. Finally, C-Breeze has a
general annotation mechanism: each node has a list of Annote objects.
To use this mechanism, create a subclass of Annote and use it to store
the information. The add the object to the node using the
annotations() method on Node.