diff --git a/tools/qfcc/include/flow.h b/tools/qfcc/include/flow.h index 46b8d9fad..a660ac321 100644 --- a/tools/qfcc/include/flow.h +++ b/tools/qfcc/include/flow.h @@ -36,7 +36,13 @@ //@{ struct function_s; +struct sblock_s; +struct statement_s; +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); diff --git a/tools/qfcc/include/options.h b/tools/qfcc/include/options.h index 9b244bee8..54bc55ee8 100644 --- a/tools/qfcc/include/options.h +++ b/tools/qfcc/include/options.h @@ -73,6 +73,7 @@ typedef struct { qboolean dead; qboolean final; qboolean dags; + qboolean flow; } blockdot_options_t; typedef struct { diff --git a/tools/qfcc/include/statements.h b/tools/qfcc/include/statements.h index 551252dfc..260ae820a 100644 --- a/tools/qfcc/include/statements.h +++ b/tools/qfcc/include/statements.h @@ -71,6 +71,8 @@ typedef struct statement_s { typedef struct sblock_s { struct sblock_s *next; + struct sblock_s **pred; ///< predecessors of this node + struct sblock_s **succ; ///< successors of this node struct reloc_s *relocs; struct ex_label_s *labels; struct dagnode_s *dag; diff --git a/tools/qfcc/source/dot_flow.c b/tools/qfcc/source/dot_flow.c index 8be766069..20e646b18 100644 --- a/tools/qfcc/source/dot_flow.c +++ b/tools/qfcc/source/dot_flow.c @@ -44,6 +44,7 @@ #include #include "dags.h" +#include "flow.h" #include "expr.h" #include "statements.h" #include "strpool.h" @@ -61,34 +62,6 @@ flow_statement (dstring_t *dstr, statement_t *s) dasprintf (dstr, "\n"); } -static int -is_goto (statement_t *s) -{ - if (!s) - return 0; - return !strcmp (s->opcode, ""); -} - -static int -is_return (statement_t *s) -{ - if (!s) - return 0; - return !strncmp (s->opcode, "opcode, "opb->o.label->dest; - if (!strcmp (s->opcode, "")) - return s->opa->o.label->dest; - return 0; -} - static void flow_sblock (dstring_t *dstr, sblock_t *sblock, int blockno) { @@ -120,12 +93,22 @@ flow_sblock (dstring_t *dstr, sblock_t *sblock, int blockno) dasprintf (dstr, " \n"); dasprintf (dstr, " \n"); dasprintf (dstr, " >];\n"); - if (sblock->next && !is_goto ((statement_t *) sblock->tail) - && !is_return ((statement_t *) sblock->tail)) - dasprintf (dstr, " sb_%p:e -> sb_%p:s;\n", sblock, sblock->next); - if ((target = get_target ((statement_t *) sblock->tail))) - dasprintf (dstr, " sb_%p:e -> sb_%p:s [label=\"%s\"];\n", sblock, - target, ((statement_t *) sblock->tail)->opcode); + if (!sblock->succ) { + if (sblock->next && !flow_is_goto ((statement_t *) sblock->tail) + && !flow_is_return ((statement_t *) sblock->tail)) + dasprintf (dstr, " sb_%p:e -> sb_%p:s;\n", sblock, sblock->next); + if ((target = flow_get_target ((statement_t *) sblock->tail))) + dasprintf (dstr, " sb_%p:e -> sb_%p:s [label=\"%s\"];\n", sblock, + target, ((statement_t *) sblock->tail)->opcode); + } else { + sblock_t **sb; + for (sb = sblock->succ; *sb; sb++) + dasprintf (dstr, " sb_%p:e -> sb_%p:s [label=\"s\"];\n", sblock, + *sb); + for (sb = sblock->pred; sb && *sb; sb++) + dasprintf (dstr, " sb_%p:e -> sb_%p:s [label=\"p\"];\n", *sb, + sblock); + } dasprintf (dstr, "\n"); } diff --git a/tools/qfcc/source/flow.c b/tools/qfcc/source/flow.c index 91543c530..c0c1d5e1e 100644 --- a/tools/qfcc/source/flow.c +++ b/tools/qfcc/source/flow.c @@ -136,10 +136,45 @@ flow_build_vars (function_t *func) } } +int +flow_is_cond (statement_t *s) +{ + if (!s) + return 0; + return !strncmp (s->opcode, "opcode, ""); +} + +int +flow_is_return (statement_t *s) +{ + if (!s) + return 0; + return !strncmp (s->opcode, "opb->o.label->dest; + if (flow_is_goto (s)) + return s->opa->o.label->dest; + return 0; +} + void flow_build_graph (function_t *func) { sblock_t *sblock; + statement_t *st; int num_blocks = 0; for (sblock = func->sblock; sblock; sblock = sblock->next) @@ -148,4 +183,46 @@ flow_build_graph (function_t *func) for (sblock = func->sblock; sblock; sblock = sblock->next) func->graph[sblock->number] = sblock; func->num_nodes = num_blocks; + + for (sblock = func->sblock; sblock; sblock = sblock->next) { + if (sblock->statements) { + st = (statement_t *) sblock->tail; + //FIXME jump/jumpb + if (flow_is_goto (st)) { + sblock->succ = calloc (2, sizeof (sblock_t *)); + sblock->succ[0] = flow_get_target (st); + } else if (flow_is_cond (st)) { + sblock->succ = calloc (3, sizeof (sblock_t *)); + sblock->succ[0] = sblock->next; + sblock->succ[1] = flow_get_target (st); + } else if (flow_is_return (st)) { + sblock->succ = calloc (1, sizeof (sblock_t *)); + } else { + sblock->succ = calloc (2, sizeof (sblock_t *)); + sblock->succ[0] = sblock->next; + } + } + } + for (sblock = func->sblock; sblock; sblock = sblock->next) { + int num_pred; + sblock_t *sb, **ss; + + for (num_pred = 0, sb = func->sblock; sb; sb = sb->next) { + for (ss = sb->succ; *ss; ss++) { + if (*ss == sblock) { + num_pred++; + break; + } + } + } + sblock->pred = calloc (num_pred + 1, sizeof (sblock_t *)); + for (num_pred = 0, sb = func->sblock; sb; sb = sb->next) { + for (ss = sb->succ; *ss; ss++) { + if (*ss == sblock) { + sblock->pred[num_pred++] = sb; + break; + } + } + } + } } diff --git a/tools/qfcc/source/function.c b/tools/qfcc/source/function.c index d606c1c45..790844bb4 100644 --- a/tools/qfcc/source/function.c +++ b/tools/qfcc/source/function.c @@ -643,8 +643,11 @@ emit_function (function_t *f, expr_t *e) f->code = pr.code->size; lineno_base = f->def->line; f->sblock = make_statements (e); - flow_build_graph (f); flow_build_vars (f); + flow_build_graph (f); + if (options.block_dot.flow) + print_flow (f->sblock, nva ("%s.%s.%s.dot", GETSTR (pr.source_file), + f->name, "flow")); printf ("%s %d %d\n", f->name, f->num_nodes, f->num_vars); emit_statements (f->sblock); } diff --git a/tools/qfcc/source/options.c b/tools/qfcc/source/options.c index 1811b9ebb..85b91b925 100644 --- a/tools/qfcc/source/options.c +++ b/tools/qfcc/source/options.c @@ -391,6 +391,8 @@ DecodeArgs (int argc, char **argv) options.block_dot.final = flag; } else if (!(strcasecmp (temp, "dags"))) { options.block_dot.dags = flag; + } else if (!(strcasecmp (temp, "flow"))) { + options.block_dot.flow = flag; } temp = strtok (NULL, ","); } @@ -401,6 +403,7 @@ DecodeArgs (int argc, char **argv) options.block_dot.dead = true; options.block_dot.final = true; options.block_dot.dags = true; + options.block_dot.flow = true; } break; case 'c': diff --git a/tools/qfcc/source/statements.c b/tools/qfcc/source/statements.c index a42a14c4e..d727d1fcc 100644 --- a/tools/qfcc/source/statements.c +++ b/tools/qfcc/source/statements.c @@ -1397,7 +1397,7 @@ sblock_t * make_statements (expr_t *e) { sblock_t *sblock = new_sblock (); - sblock_t *s; + //sblock_t *s; // print_expr (e); statement_slist (sblock, e); if (options.block_dot.initial) @@ -1414,9 +1414,9 @@ make_statements (expr_t *e) if (options.block_dot.final) dump_flow (sblock, "final"); - for (s = sblock; s; s = s->next) - s->dag = make_dag (s); - if (options.block_dot.dags) - dump_flow (sblock, "dags"); + //for (s = sblock; s; s = s->next) + // s->dag = make_dag (s); + //if (options.block_dot.dags) + // dump_flow (sblock, "dags"); return sblock; }