Rewrite the flow graph code.

The flow graph nodes are now properly separated from the graph, and edge
information is stored in the graph struct. This actually made for much
cleaner code (partly thanks to the use of sets and set iterators).

Flow graph reduction has been (temporarily) ripped out as the entire
approach was wrong. There was also a bug in that I didn't really understand
the dragon book about selecting nodes and thus messed things up. The
depth-first search tree "fixed" the problem, but was really the wrong
solution (sledge hammer :P).

Also, now that I understand that dot's directed graphs must be acyclic, I
now have much better control over the graphs (back edges need to be
flipped).
This commit is contained in:
Bill Currie 2012-11-02 17:30:29 +09:00
parent b9599a7119
commit d7177a78e9
5 changed files with 214 additions and 311 deletions

View file

@ -46,56 +46,43 @@ typedef struct flowloop_s {
} flowloop_t;
typedef struct flowedge_s {
unsigned tail; //< node index
unsigned head; //< successor index
unsigned tail;
unsigned head;
} flowedge_t;
/** Represent a node in a flow graph.
With the \a siblings and \a num_siblings fields, the entire graph can be
accessed via any node within that graph.
*/
typedef struct flownode_s {
struct flownode_s *next;
unsigned id;
int num_pred;
unsigned *predecessors; //< indices into siblings
int num_succ;
unsigned *successors; //< indices into siblings
unsigned num_nodes; //< number of nodes or sblocks
unsigned region; //< the region of which this node is a member
unsigned *depth_first; //< indices into siblings in depth-first order
/// \name Node pointers.
/// Only one of \a sblocks or \a nodes will be non-null. If \a sblocks is
/// non-null, then this is an innermost flow-node, otherwise \a nodes
/// will be non-null and this is a higher level flow-node representing a
/// region.
///
/// \a sblocks points to the array of all sblocks in the function
/// (it is a copy of function_t's graph member). \a id acts as an index
/// into \a sblocks to identify the sblock associated with this node.
///
/// \a nodes is an array of all flow-nodes nested within the region
/// represented by this node.
///
/// \a siblings is an array of all flow-nodes contained within the same
/// region as this node.
//@{
struct sblock_s **sblocks;
struct flownode_s **nodes;
struct flownode_s **siblings;
unsigned num_siblings;
//@}
struct set_s *dom;
struct flownode_s *next; //< for ALLOC
unsigned id; //< index of this node in the flow graph
unsigned dfn; //< depth-first ordering of this node
struct set_s *predecessors; //< predecessors of this node
struct set_s *successors; //< successors of this node
struct set_s *edges; //< edges leaving this node, to successor nodes
struct set_s *dom; //< dominating nodes
struct sblock_s *sblock; //< original statement block
} flownode_t;
typedef struct flowgraph_s {
struct flowgraph_s *next; //< for ALLOC
flownode_t **nodes; //< array of nodes in the graph
int num_nodes;
flowedge_t *edges; //< array of all edges in the graph
int num_edges;
struct set_s *dfst; //< edges in the depth-first search tree
unsigned *dfo; //< depth-first order of nodes
flowloop_t *loops; //< linked list of natural loops
} flowgraph_t;
int flow_is_cond (struct statement_s *s);
int flow_is_goto (struct statement_s *s);
int flow_is_return (struct statement_s *s);
struct sblock_s *flow_get_target (struct statement_s *s);
void flow_build_vars (struct function_s *func);
void flow_build_graph (struct function_s *func);
void print_flowgraph (flownode_t *flow, const char *filename);
flowgraph_t *flow_build_graph (struct sblock_s *func);
void flow_del_graph (flowgraph_t *graph);
void print_flowgraph (flowgraph_t *graph, const char *filename);
//@}

View file

@ -79,14 +79,8 @@ typedef struct function_s {
struct reloc_s *refs; ///< relocation targets for this function
struct expr_s *var_init;
const char *name; ///< nice name for __PRETTY_FUNCTION__
struct sblock_s *sblock; ///< initial node of function's flow graph
/** Array of pointers to all nodes in the function's flow graph.
This permits ready mapping of node number to node in the flow
analyzer.
*/
struct sblock_s **graph;
int num_nodes; ///< number of nodes in the graph
struct sblock_s *sblock; ///< initial node of function's code
struct flowgraph_s *graph; ///< the function's flow graph
/** Array of pointers to all variables referenced by the function's code.
This permits ready mapping of (function specific) variable number to
@ -95,7 +89,6 @@ typedef struct function_s {
struct daglabel_s **vars;
int num_vars; ///< total number of variables referenced
struct flowloop_s *loops; ///< linked list of loops in the function
struct flownode_s *flow; ///< flow graph
} function_t;
extern function_t *current_func;

View file

@ -46,57 +46,65 @@
#include "dags.h"
#include "flow.h"
#include "expr.h"
#include "set.h"
#include "strpool.h"
static void
print_flow_node (dstring_t *dstr, flownode_t *node, int level)
{
int indent = level * 2 + 2;
unsigned j;
if (node->nodes) {
dasprintf (dstr, "%*ssubgraph \"cluster_%p\" {\n", indent, "", node);
//dasprintf (dstr, "%*srankdir=TB;\n", indent + 2, "");
dasprintf (dstr, "%*slabel=\"%d\";\n", indent + 2, "", node->id);
for (j = 0; j < node->num_nodes; j++)
print_flow_node (dstr, node->nodes[j], level + 1);
dasprintf (dstr, "%*s}\n", indent, "");
}
dasprintf (dstr, "%*s\"fn_%p\" [label=\"%d\"];\n", indent, "", node,
node->id);
}
static void
print_flow_edges (dstring_t *dstr, flownode_t *node, int level)
print_flow_edges (dstring_t *dstr, flowgraph_t *graph, int level)
{
int indent = level * 2 + 2;
int i;
unsigned j;
flowedge_t *edge;
flownode_t *t, *h;
const char *style;
int weight;
if (node->nodes) {
for (j = 0; j < node->num_nodes; j++)
print_flow_edges (dstr, node->nodes[j], level + 1);
for (i = 0; i < graph->num_edges; i++) {
edge = &graph->edges[i];
t = graph->nodes[edge->tail];
h = graph->nodes[edge->head];
if (t->dfn < h->dfn) {
style = "solid";
weight = 0;
if (set_is_member (graph->dfst, i)) {
style = "bold";
weight = 10;
}
dasprintf (dstr,
"%*s\"fn_%p\" -> \"fn_%p\" [style=%s,weight=%d];\n",
indent, "", t, h, style, weight);
} else {
dasprintf (dstr,
"%*s\"fn_%p\" -> \"fn_%p\" [dir=back, style=dashed];\n",
indent, "", h, t);
}
}
for (i = 0; i < node->num_succ; i++)
dasprintf (dstr, "%*s\"fn_%p\" -> \"fn_%p\";\n", indent, "", node,
node->siblings[node->successors[i]]);
}
void
print_flowgraph (flownode_t *flow, const char *filename)
print_flowgraph (flowgraph_t *graph, const char *filename)
{
unsigned i;
int i;
dstring_t *dstr = dstring_newstr();
dasprintf (dstr, "digraph flow_%p {\n", flow->siblings);
dasprintf (dstr, "digraph flowgraph_%p {\n", graph);
dasprintf (dstr, " layout=dot;\n");
dasprintf (dstr, " clusterrank=local;\n");
//dasprintf (dstr, " rankdir=TB;\n");
dasprintf (dstr, " rankdir=TB;\n");
dasprintf (dstr, " compound=true;\n");
for (i = 0; i < flow->num_siblings; i++) {
print_flow_node (dstr, flow->siblings[i], 0);
print_flow_edges (dstr, flow->siblings[i], 0);
for (i = 0; i < graph->num_nodes; i++) {
print_flow_node (dstr, graph->nodes[i], 0);
}
print_flow_edges (dstr, graph, 0);
dasprintf (dstr, "}\n");
if (filename) {

View file

@ -51,6 +51,7 @@
static flowloop_t *free_loops;
static flownode_t *free_nodes;
static flowgraph_t *free_graphs;
static flowloop_t *
new_loop (void)
@ -76,20 +77,50 @@ new_node (void)
ALLOC (256, flownode_t, nodes, node);
return node;
}
#if 0
static void
delete_node (flownode_t *node)
{
if (node->nodes)
free (node->nodes);
if (node->predecessors)
free (node->predecessors);
set_delete (node->predecessors);
if (node->successors)
free (node->successors);
set_delete (node->successors);
if (node->edges)
set_delete (node->edges);
if (node->dom)
set_delete (node->dom);
node->next = free_nodes;
free_nodes = node;
}
#endif
static flowgraph_t *
new_graph (void)
{
flowgraph_t *graph;
ALLOC (256, flowgraph_t, graphs, graph);
return graph;
}
static void
delete_graph (flowgraph_t *graph)
{
int i;
if (graph->nodes) {
for (i = 0; i < graph->num_nodes; i++)
delete_node (graph->nodes[i]);
free (graph->nodes);
}
if (graph->edges)
free (graph->edges);
if (graph->dfst)
set_delete (graph->dfst);
if (graph->dfo)
free (graph->dfo);
graph->next = free_graphs;
free_graphs = graph;
}
static int
is_variable (daglabel_t *var)
{
@ -167,6 +198,9 @@ flow_build_vars (function_t *func)
num_vars += count_operand (s->opc);
}
}
if (!num_vars)
return;
func->vars = malloc (num_vars * sizeof (daglabel_t *));
func->num_vars = 0; // incremented by add_operand
@ -214,83 +248,61 @@ flow_get_target (statement_t *s)
}
static void
flow_find_predecessors (flownode_t **node_list, unsigned num_nodes)
flow_find_predecessors (flowgraph_t *graph)
{
unsigned i, j, k;
int i;
flownode_t *node;
set_iter_t *succ;
for (i = 0; i < num_nodes; i++) {
node = node_list[i];
for (j = 0; j < num_nodes; j++) {
unsigned *succ;
flownode_t *n;
n = node_list[j];
for (succ = n->successors; succ - n->successors < n->num_succ;
succ++) {
if (*succ == i) {
node->num_pred++;
break;
}
}
}
node->predecessors = malloc (node->num_pred * sizeof (flownode_t *));
for (k = j = 0; j < num_nodes; j++) {
unsigned *succ;
flownode_t *n;
n = node_list[j];
for (succ = n->successors; succ - n->successors < n->num_succ;
succ++) {
if (*succ == i) {
node->predecessors[k++] = j;
break;
}
}
for (i = 0; i < graph->num_nodes; i++) {
node = graph->nodes[i];
for (succ = set_first (node->successors); succ;
succ = set_next (succ)) {
set_add (graph->nodes[succ->member]->predecessors, i);
}
}
}
static void
flow_calc_dominators (flownode_t **node_list, unsigned num_nodes)
flow_find_dominators (flowgraph_t *graph)
{
set_t *work;
flownode_t *node;
unsigned i;
unsigned *pred;
int i;
set_iter_t *pred;
int changed;
if (!num_nodes)
if (!graph->num_nodes)
return;
// First, create a base set for the initial state of the non-initial nodes
work = set_new ();
for (i = 0; i < num_nodes; i++)
for (i = 0; i < graph->num_nodes; i++)
set_add (work, i);
node_list[0]->dom = set_new ();
set_add (node_list[0]->dom, 0);
set_add (graph->nodes[0]->dom, 0);
// initialize dom for the non-initial nodes
for (i = 1; i < num_nodes; i++) {
node_list[i]->dom = set_new ();
set_assign (node_list[i]->dom, work);
for (i = 1; i < graph->num_nodes; i++) {
set_assign (graph->nodes[i]->dom, work);
}
do {
changed = 0;
for (i = 1; i < num_nodes; i++) {
node = node_list[i];
pred = node->predecessors;
set_assign (work, node_list[*pred]->dom);
for (pred++; pred - node->predecessors < node->num_pred; pred++)
set_intersection (work, node_list[*pred]->dom);
for (i = 1; i < graph->num_nodes; i++) {
node = graph->nodes[i];
pred = set_first (node->predecessors);
if (pred)
set_assign (work, graph->nodes[pred->member]->dom);
for (pred = set_next (pred); pred; pred = set_next (pred))
set_intersection (work, graph->nodes[pred->member]->dom);
set_add (work, i);
if (!set_is_equivalent (work, node->dom))
changed = 1;
set_assign (node->dom, work);
}
} while (changed);
set_delete (work);
}
static void
@ -303,12 +315,12 @@ insert_loop_node (flowloop_t *loop, unsigned n, set_t *stack)
}
static flowloop_t *
make_loop (flownode_t **node_list, unsigned num_nodes, unsigned n, unsigned d)
make_loop (flowgraph_t *graph, unsigned n, unsigned d)
{
flowloop_t *loop = new_loop ();
flownode_t *node;
set_t *stack = set_new ();
unsigned *pred;
set_iter_t *pred;
loop->head = d;
set_add (loop->nodes, d);
@ -318,30 +330,30 @@ make_loop (flownode_t **node_list, unsigned num_nodes, unsigned n, unsigned d)
unsigned m = ss->member;
set_del_iter (ss);
set_remove (stack, m);
node = node_list[m];
for (pred = node->predecessors;
pred - node->predecessors < node->num_pred; pred++)
insert_loop_node (loop, *pred, stack);
node = graph->nodes[m];
for (pred = set_first (node->predecessors); pred;
pred = set_next (pred))
insert_loop_node (loop, pred->member, stack);
}
set_delete (stack);
return loop;
}
static flowloop_t *
flow_find_loops (flownode_t **node_list, unsigned num_nodes)
static void
flow_find_loops (flowgraph_t *graph)
{
flownode_t *node;
unsigned *succ;
set_iter_t *succ;
flowloop_t *loop, *l;
flowloop_t *loop_list = 0;
unsigned i;
int i;
for (i = 0; i < num_nodes; i++) {
node = node_list[i];
for (succ = node->successors; succ - node->successors < node->num_succ;
succ++) {
if (set_is_member (node->dom, *succ)) {
loop = make_loop (node_list, num_nodes, node->id, *succ);
for (i = 0; i < graph->num_nodes; i++) {
node = graph->nodes[i];
for (succ = set_first (node->successors); succ;
succ = set_next (succ)) {
if (set_is_member (node->dom, succ->member)) {
loop = make_loop (graph, node->id, succ->member);
for (l = loop_list; l; l = l->next) {
if (l->head == loop->head
&& !set_is_subset (l->nodes, loop->nodes)
@ -359,210 +371,113 @@ flow_find_loops (flownode_t **node_list, unsigned num_nodes)
}
}
}
return loop_list;
graph->loops = loop_list;
}
static void
df_search (flownode_t *graph, set_t *visited, unsigned *i, unsigned n)
df_search (flowgraph_t *graph, set_t *visited, int *i, int n)
{
int j;
flownode_t *node;
set_iter_t *edge;
int succ;
set_add (visited, n);
node = graph->siblings[n];
for (j = 0; j < node->num_succ; j++) {
if (!set_is_member (visited, node->successors[j])) {
df_search (graph, visited, i, node->successors[j]);
node = graph->nodes[n];
for (edge = set_first (node->edges); edge; edge = set_next (edge)) {
succ = graph->edges[edge->member].head;
if (!set_is_member (visited, succ)) {
set_add (graph->dfst, edge->member);
df_search (graph, visited, i, succ);
}
}
graph->depth_first[--*i] = n;
node->dfn = --*i;
graph->dfo[node->dfn] = n;
}
static void
flow_depth_first (flownode_t *graph)
flow_build_dfst (flowgraph_t *graph)
{
unsigned i = graph->num_siblings;
set_t *visited = set_new ();
graph->depth_first = malloc (graph->num_siblings * sizeof (unsigned));
df_search (graph, visited, &i, 0);
}
static int
is_predecessor (unsigned m, set_t *I, flownode_t *graph)
{
flownode_t *node = graph->siblings[m];
int i;
for (i = 0; i < node->num_pred; i++)
if (!set_is_member (I, node->predecessors[i]))
return 0;
return 1;
graph->dfo = malloc (graph->num_nodes * sizeof (unsigned));
graph->dfst = set_new ();
i = graph->num_nodes;
df_search (graph, visited, &i, 0);
set_delete (visited);
}
static set_t *
select_nodes (flownode_t *graph, set_t *G, unsigned n)
flowgraph_t *
flow_build_graph (sblock_t *sblock)
{
set_t *I;
set_iter_t *m;
I = set_new ();
set_add (I, n);
set_remove (G, n);
for (m = set_first (G); m; m = set_next (m)) {
if (m->member == n || !is_predecessor (m->member, I, graph))
continue;
set_remove (G, m->member);
set_add (I, m->member);
}
return I;
}
static flownode_t *
flow_reduce (flownode_t *graph)
{
set_t *G;
set_t **I;
unsigned i, j, count = 0;
int k;
flownode_t **node_list = 0;
flowgraph_t *graph;
flownode_t *node;
set_iter_t *m;
if (graph->num_siblings < 2)
return 0;
G = set_new ();
// Initialize G to be the set of all nodes in graph
for (i = 0; i < graph->num_siblings; i++)
set_add (G, i);
// allocate space for the interval sets. There will never be more intervals
// than nodes in graph.
I = malloc (graph->num_siblings * sizeof (set_t *));
for (i = 0; i < graph->num_siblings; i++) {
unsigned m = graph->depth_first[i];
if (!set_is_member (G, m))
continue;
I[count++] = select_nodes (graph, G, m);
}
if (count == graph->num_siblings)
goto irreducible;
node_list = malloc (count * sizeof (flownode_t *));
for (i = 0; i < count; i++) {
node = new_node ();
node->siblings = node_list;
node->num_siblings = count;
node->id = i;
node->num_nodes = set_size (I[i]);
node->nodes = malloc (node->num_nodes * sizeof (flownode_t *));
for (j = 0, m = set_first (I[i]); m; m = set_next (m), j++) {
node->nodes[j] = graph->siblings[m->member];
node->nodes[j]->region = i;
}
node_list[node->id] = node;
}
for (i = 0; i < count; i++) {
node = node_list[i];
set_empty (G); // G now represents the set of successors of node
for (j = 0; j < node->num_nodes; j++) {
flownode_t *n = node->nodes[j];
for (k = 0; k < n->num_succ; k++) {
flownode_t *m = n->siblings[n->successors[k]];
if (m->region != i && !set_is_member (G, m->region))
set_add (G, m->region);
}
}
node->num_succ = set_size (G);
node->successors = malloc (node->num_succ * sizeof (unsigned));
for (j = 0, m = set_first (G); m; j++, m = set_next (m))
node->successors[j] = m->member;
}
flow_find_predecessors (node_list, count);
flow_depth_first (node_list[0]);
irreducible:
for (i = 0; i < count; i++)
set_delete (I[i]);
free (I);
set_delete (G);
if (node_list)
return node_list[0];
return 0;
}
void
flow_build_graph (function_t *func)
{
sblock_t *sblock;
sblock_t *sb;
statement_t *st;
flownode_t *node;
flownode_t **node_list;
unsigned num_blocks = 0;
unsigned i;
for (sblock = func->sblock; sblock; sblock = sblock->next)
sblock->number = num_blocks++;
func->graph = malloc (num_blocks * sizeof (sblock_t *));
func->num_nodes = num_blocks;
node_list = malloc (func->num_nodes * sizeof (flownode_t *));
for (sblock = func->sblock; sblock; sblock = sblock->next) {
func->graph[sblock->number] = sblock;
set_iter_t *succ;
int i, j;
graph = new_graph ();
for (sb = sblock; sb; sb = sb->next)
sb->number = graph->num_nodes++;
graph->nodes = malloc (graph->num_nodes * sizeof (flownode_t *));
for (sb = sblock; sb; sb = sb->next) {
node = new_node ();
node->sblocks = func->graph;
node->siblings = node_list;
node->num_siblings = num_blocks;
node->id = sblock->number;
node->num_nodes = func->num_nodes;
node_list[node->id] = node;
node->predecessors = set_new ();
node->successors = set_new ();
node->edges = set_new ();
node->dom = set_new ();
node->id = sb->number;
node->sblock = sb;
graph->nodes[node->id] = node;
}
// "convert" the basic blocks connections to flow-graph connections
for (i = 0; i < num_blocks; i++) {
node = node_list[i];
sblock = node->sblocks[node->id];
for (i = 0; i < graph->num_nodes; i++) {
node = graph->nodes[i];
sb = node->sblock;
st = 0;
if (sblock->statements)
st = (statement_t *) sblock->tail;
//FIXME jump/jumpb
if (sb->statements)
st = (statement_t *) sb->tail;
//NOTE: if st is null (the sblock has no statements), flow_is_* will
//return false
//FIXME jump/jumpb
if (flow_is_goto (st)) {
// sblock's next is never followed.
node->num_succ = 1;
node->successors = calloc (1, sizeof (unsigned));
node->successors[0] = flow_get_target (st)->number;
// sb's next is never followed.
set_add (node->successors, flow_get_target (st)->number);
} else if (flow_is_cond (st)) {
// branch: either sblock's next or the conditional statment's
// branch: either sb's next or the conditional statment's
// target will be followed.
node->num_succ = 2;
node->successors = calloc (2, sizeof (unsigned));
node->successors[0] = sblock->next->number;
node->successors[1] = flow_get_target (st)->number;
set_add (node->successors, sb->next->number);
set_add (node->successors, flow_get_target (st)->number);
} else if (flow_is_return (st)) {
// exit from function (dead end)
node->num_succ = 0;
} else {
// there is no flow-control statement in sblock, so sblock's next
// there is no flow-control statement in sb, so sb's next
// must be followed
node->num_succ = 1;
node->successors = calloc (1, sizeof (unsigned));
node->successors[0] = sblock->next->number;
set_add (node->successors, sb->next->number);
}
graph->num_edges += set_size (node->successors);
}
graph->edges = malloc (graph->num_edges * sizeof (flowedge_t *));
for (j = 0, i = 0; i < graph->num_nodes; i++) {
node = graph->nodes[i];
for (succ = set_first (node->successors); succ;
succ = set_next (succ), j++) {
set_add (node->edges, j);
graph->edges[j].tail = i;
graph->edges[j].head = succ->member;
}
}
flow_find_predecessors (node_list, num_blocks);
flow_depth_first (node_list[0]);
flow_calc_dominators (node_list, num_blocks);
func->loops = flow_find_loops (node_list, num_blocks);
func->flow = node_list[0];
while ((node = flow_reduce (func->flow)))
func->flow = node;
flow_build_dfst (graph);
flow_find_predecessors (graph);
flow_find_dominators (graph);
flow_find_loops (graph);
return graph;
}
void
flow_del_graph (flowgraph_t *graph)
{
delete_graph (graph);
}

View file

@ -644,16 +644,16 @@ emit_function (function_t *f, expr_t *e)
lineno_base = f->def->line;
f->sblock = make_statements (e);
flow_build_vars (f);
flow_build_graph (f);
f->graph = flow_build_graph (f->sblock);
if (options.block_dot.flow)
print_flowgraph (f->flow, nva ("%s.%s.%s.dot", GETSTR (pr.source_file),
f->name, "flow"));
print_flowgraph (f->graph, nva ("%s.%s.%s.dot", GETSTR (pr.source_file),
f->name, "flow"));
{
flowloop_t *l;
int n = 0;
for (l = f->loops; l; l = l->next)
n++;
printf ("%s %d %d %d\n", f->name, f->num_nodes, f->num_vars, n);
printf ("%s %d %d %d\n", f->name, f->graph->num_nodes, f->num_vars, n);
}
emit_statements (f->sblock);
}