Output basic block flow diagrams to files.

The output can be controlled via --block-dot (not yet documented). The
files a named <sourcefile>.<function>.<stage>.dot. Currently, stage will be
one of "initial" (after expression to statement conversion), "thread"
(after jump threading), "dead" (after dead block removal), "final" (final
state before actual code emission).
This commit is contained in:
Bill Currie 2012-05-04 09:45:51 +09:00
parent 3da44ace52
commit e866619de6
5 changed files with 109 additions and 38 deletions

View file

@ -67,10 +67,18 @@ typedef struct {
qboolean silent; // don't even bother (overrides promote)
} notice_options_t;
typedef struct {
qboolean initial;
qboolean thread;
qboolean dead;
qboolean final;
} blockdot_options_t;
typedef struct {
code_options_t code; // Code generation options
warn_options_t warnings; // Warning options
notice_options_t notices; // Notice options
blockdot_options_t block_dot; // Statement block flow diagrams
int verbosity; // 0=silent, goes up to 2 currently
qboolean single_cpp; // process progs.src into a series of

View file

@ -77,6 +77,6 @@ struct expr_s;
sblock_t *make_statements (struct expr_s *expr);
void print_statement (statement_t *s);
void print_flow (sblock_t *sblock);
void print_flow (sblock_t *sblock, const char *filename);
#endif//statement_h

View file

@ -40,6 +40,7 @@
#include <stdlib.h>
#include <QF/dstring.h>
#include <QF/quakeio.h>
#include <QF/va.h>
#include "expr.h"
@ -153,14 +154,14 @@ get_operand (operand_t *op)
}
static void
flow_statement (statement_t *s)
flow_statement (dstring_t *dstr, statement_t *s)
{
printf (" <tr>");
printf ("<td>%s</td>", quote_string (s->opcode));
printf ("<td>%s</td>", get_operand (s->opa));
printf ("<td>%s</td>", get_operand (s->opb));
printf ("<td>%s</td>", get_operand (s->opc));
printf ("</tr>\n");
dasprintf (dstr, " <tr>");
dasprintf (dstr, "<td>%s</td>", quote_string (s->opcode));
dasprintf (dstr, "<td>%s</td>", get_operand (s->opa));
dasprintf (dstr, "<td>%s</td>", get_operand (s->opb));
dasprintf (dstr, "<td>%s</td>", get_operand (s->opc));
dasprintf (dstr, "</tr>\n");
}
static int
@ -184,46 +185,60 @@ get_target (statement_t *s)
}
static void
flow_sblock (sblock_t *sblock, int blockno)
flow_sblock (dstring_t *dstr, sblock_t *sblock, int blockno)
{
statement_t *s;
sblock_t *target;
ex_label_t *l;
printf (" sb_%p [shape=none,label=<\n", sblock);
printf (" <table border=\"0\" cellborder=\"1\" cellspacing=\"0\">\n");
printf (" <tr>\n");
printf (" <td>%p(%d)</td>\n", sblock, blockno);
printf (" <td height=\"0\" colspan=\"2\" port=\"s\">\n");
dasprintf (dstr, " sb_%p [shape=none,label=<\n", sblock);
dasprintf (dstr, " <table border=\"0\" cellborder=\"1\" "
"cellspacing=\"0\">\n");
dasprintf (dstr, " <tr>\n");
dasprintf (dstr, " <td>%p(%d)</td>\n", sblock, blockno);
dasprintf (dstr, " <td height=\"0\" colspan=\"2\" port=\"s\">\n");
for (l = sblock->labels; l; l = l->next)
printf (" %s(%d)\n", l->name, l->used);
printf (" </td>\n");
printf (" <td></td>\n");
printf (" </tr>\n");
dasprintf (dstr, " %s(%d)\n", l->name, l->used);
dasprintf (dstr, " </td>\n");
dasprintf (dstr, " <td></td>\n");
dasprintf (dstr, " </tr>\n");
for (s = sblock->statements; s; s = s->next)
flow_statement (s);
printf (" <tr>\n");
printf (" <td></td>\n");
printf (" <td height=\"0\" colspan=\"2\" port=\"e\"></td>\n");
printf (" <td></td>\n");
printf (" </tr>\n");
printf (" </table>>];\n");
flow_statement (dstr, s);
dasprintf (dstr, " <tr>\n");
dasprintf (dstr, " <td></td>\n");
dasprintf (dstr, " <td height=\"0\" colspan=\"2\" "
"port=\"e\"></td>\n");
dasprintf (dstr, " <td></td>\n");
dasprintf (dstr, " </tr>\n");
dasprintf (dstr, " </table>>];\n");
if (sblock->next && !is_goto ((statement_t *) sblock->tail))
printf (" sb_%p:e -> sb_%p:s;\n", sblock, sblock->next);
dasprintf (dstr, " sb_%p:e -> sb_%p:s;\n", sblock, sblock->next);
if ((target = get_target ((statement_t *) sblock->tail)))
printf (" sb_%p:e -> sb_%p:s [label=\"%s\"];\n", sblock, target,
((statement_t *) sblock->tail)->opcode);
printf ("\n");
dasprintf (dstr, " sb_%p:e -> sb_%p:s [label=\"%s\"];\n", sblock,
target, ((statement_t *) sblock->tail)->opcode);
dasprintf (dstr, "\n");
}
void
print_flow (sblock_t *sblock)
print_flow (sblock_t *sblock, const char *filename)
{
int i;
dstring_t *dstr = dstring_newstr();
printf ("digraph flow_%p {\n", sblock);
printf (" layout=dot; rankdir=TB;\n");
dasprintf (dstr, "digraph flow_%p {\n", sblock);
dasprintf (dstr, " layout=dot; rankdir=TB;\n");
for (i = 0; sblock; sblock = sblock->next, i++)
flow_sblock (sblock, i);
printf ("}\n");
flow_sblock (dstr, sblock, i);
dasprintf (dstr, "}\n");
if (filename) {
QFile *file;
file = Qopen (filename, "wt");
Qwrite (file, dstr->str, dstr->size - 1);
Qclose (file);
} else {
fputs (dstr->str, stdout);
}
dstring_delete (dstr);
}

View file

@ -60,6 +60,7 @@ static int files_size;
enum {
start_opts = 255, // not used, starts the enum.
OPT_ADVANCED,
OPT_BLOCK_DOT,
OPT_CPP,
OPT_INCLUDE,
OPT_NO_DEFAULT_PATHS,
@ -70,6 +71,7 @@ enum {
static struct option const long_options[] = {
{"advanced", no_argument, 0, OPT_ADVANCED},
{"block-dot", optional_argument, 0, OPT_BLOCK_DOT},
{"code", required_argument, 0, 'C'},
{"cpp", required_argument, 0, OPT_CPP},
{"define", required_argument, 0, 'D'},
@ -359,6 +361,37 @@ DecodeArgs (int argc, char **argv)
options.advanced = true;
options.code.progsversion = PROG_VERSION;
break;
case OPT_BLOCK_DOT:
if (optarg) {
char *opts = strdup (optarg);
char *temp = strtok (opts, ",");
while (temp) {
qboolean flag = true;
if (!strncasecmp (temp, "no-", 3)) {
flag = false;
temp += 3;
}
if (!strcasecmp (temp, "initial")) {
options.block_dot.initial = flag;
} else if (!(strcasecmp (temp, "thread"))) {
options.block_dot.thread = flag;
} else if (!(strcasecmp (temp, "dead"))) {
options.block_dot.dead = flag;
} else if (!(strcasecmp (temp, "final"))) {
options.block_dot.final = flag;
}
temp = strtok (NULL, ",");
}
free (opts);
} else {
options.block_dot.initial = true;
options.block_dot.thread = true;
options.block_dot.dead = true;
options.block_dot.final = true;
}
break;
case 'c':
options.compile = true;
break;

View file

@ -1074,6 +1074,17 @@ remove_label_from_dest (ex_label_t *label)
}
}
static void
dump_flow (sblock_t *sblock, const char *stage)
{
char *fname;
fname = nva ("%s.%s.%s.dot", GETSTR (pr.source_file), current_func->name,
stage);
print_flow (sblock, fname);
free (fname);
}
static void
thread_jumps (sblock_t *blocks)
{
@ -1203,12 +1214,16 @@ make_statements (expr_t *e)
sblock_t *sblock = new_sblock ();
// print_expr (e);
statement_slist (sblock, e);
// print_flow (sblock);
if (options.block_dot.initial)
dump_flow (sblock, "initial");
thread_jumps (sblock);
// print_flow (sblock);
if (options.block_dot.thread)
dump_flow (sblock, "thread");
remove_dead_blocks (sblock);
// print_flow (sblock);
if (options.block_dot.dead)
dump_flow (sblock, "dead");
check_final_block (sblock);
// print_flow (sblock);
if (options.block_dot.final)
dump_flow (sblock, "final");
return sblock;
}