The C-Breeze
framework is implemented by the dataflow
methods
on each AST node class. Each dataflow
method implements the dataflow
semantics of the particular C language construct of the node. For example,
the following code shows the dataflow
method for the ifelseNode
(note that this is only the forward case):
void ifelseNode::dataflow(FlowVal * v, FlowProblem & fp) { if (fp.forward()) { fp.flow_ifelse(v, this, FlowProblem::Entry); if (expr()) expr()->dataflow(v, fp); FlowVal * fv = v->clone(); if (true_br()) true_br()->dataflow(v, fp); if (false_br()) false_br()->dataflow(fv, fp); v->meet(fv); delete fv; fp.flow_ifelse(v, this, FlowProblem::Exit); } }
The method flow_ifelse
is the transfer function that you define. Note
that many constructs have a no-op transfer function, which is the default.
The transfer function is called before the construct is processed (the
``entry'' point) and after the construct is processed (the ``exit'' point).
The transfer function needs to distinguish between these two cases so that it
does not accidentally exert it's effects twice.
After calling the entry transfer function, the method calls dataflow
on the conditional expression. The two branches of the if-else are modeled
by cloning the flow value and passing the two independent values through the
true and false branches separately. The two different control paths may yield
different resulting flow values, so the meet
method of FlowVal
is called to reconcile them. Finally, the transfer function is called
again.
The top-most call to dataflow
should be made on a procNode
(although this is not strictly necessary, it makes the most sense). When the
call returns, one pass of the analysis over the procedure is complete. The
framework repeats passes until the flow values no longer change. It than
makes one more pass over the procedure to allow the resulting dataflow
information to be used or stored.