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 idNode
s 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
.