mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-01-18 23:11:38 +00:00
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:
parent
3da44ace52
commit
e866619de6
5 changed files with 109 additions and 38 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue