quakeforge/tools/qfcc/source/dot_flow.c
Bill Currie 55cc0f9206 Bring back the unsigned type (PROGS version bump)
This is only low-level support (the unsigned keyword still does not work),
but sufficient to make switch statements using jump tables work.
2011-04-09 10:07:47 +09:00

225 lines
5.5 KiB
C

/*
dot_flow.c
"emit" flow graphs to dot (graphvis).
Copyright (C) 2011 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2011/01/21
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
static __attribute__ ((used)) const char rcsid[] = "$Id$";
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdlib.h>
#include <QF/dstring.h>
#include <QF/va.h>
#include "expr.h"
#include "statements.h"
#include "symtab.h"
#include "type.h"
static const char *
quote_string (const char *str)
{
static dstring_t *q;
char c[2] = {0, 0};
if (!str)
return "(null)";
if (!q)
q = dstring_new ();
dstring_clearstr (q);
while ((c[0] = *str++)) {
switch (c[0]) {
case '\n':
dstring_appendstr (q, "\\\\n");
break;
case '<':
dstring_appendstr (q, "&lt;");
break;
case '>':
dstring_appendstr (q, "&gt;");
break;
case '&':
dstring_appendstr (q, "&amp;");
break;
case '"':
dstring_appendstr (q, "&quot;");
break;
default:
dstring_appendstr (q, c);
break;
}
}
return q->str;
}
static const char *
get_operand (operand_t *op)
{
type_t *type;
if (!op)
return "";
switch (op->op_type) {
case op_symbol:
return op->o.symbol->name;
case op_value:
switch (op->o.value->type) {
case ev_string:
return quote_string (op->o.value->v.string_val);
case ev_float:
return va ("%g", op->o.value->v.float_val);
case ev_vector:
return va ("'%g %g %g'",
op->o.value->v.vector_val[0],
op->o.value->v.vector_val[1],
op->o.value->v.vector_val[2]);
case ev_quat:
return va ("'%g %g %g %g'",
op->o.value->v.quaternion_val[0],
op->o.value->v.quaternion_val[1],
op->o.value->v.quaternion_val[2],
op->o.value->v.quaternion_val[3]);
case ev_pointer:
return va ("ptr %d", op->o.value->v.pointer.val);
case ev_field:
return va ("field %d", op->o.value->v.pointer.val);
case ev_entity:
return va ("ent %d", op->o.value->v.integer_val);
case ev_func:
return va ("func %d", op->o.value->v.integer_val);
case ev_integer:
return va ("int %d", op->o.value->v.integer_val);
case ev_uinteger:
return va ("uint %u", op->o.value->v.uinteger_val);
case ev_short:
return va ("short %d", op->o.value->v.short_val);
case ev_void:
return "(void)";
case ev_invalid:
return "(invalid)";
case ev_type_count:
return "(type_count)";
}
break;
case op_label:
return op->o.label->name;
case op_temp:
return va ("tmp %p", op);
case op_pointer:
type = op->o.pointer->type;
if (op->o.pointer->def)
return va ("(%s)[%d]&lt;%s&gt;",
type ? pr_type_name[type->type] : "???",
op->o.pointer->val, op->o.pointer->def->name);
else
return va ("(%s)[%d]",
type ? pr_type_name[type->type] : "???",
op->o.pointer->val);
case op_alias:
return get_operand (op->o.alias);//FIXME better output
}
return ("??");
}
static void
flow_statement (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");
}
static int
is_goto (statement_t *s)
{
if (!s)
return 0;
return !strcmp (s->opcode, "<GOTO>");
}
static sblock_t *
get_target (statement_t *s)
{
if (!s)
return 0;
if (!strncmp (s->opcode, "<IF", 3))
return s->opb->o.label->dest;
if (!strcmp (s->opcode, "<GOTO>"))
return s->opa->o.label->dest;
return 0;
}
static void
flow_sblock (sblock_t *sblock)
{
statement_t *s;
sblock_t *target;
printf (" sb_%p [shape=none,label=<\n", sblock);
printf (" <table border=\"0\" cellborder=\"1\" cellspacing=\"0\">\n");
printf (" <tr>\n");
printf (" <td></td>\n");
printf (" <td height=\"0\" colspan=\"2\" port=\"s\"></td>\n");
printf (" <td></td>\n");
printf (" </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");
if (sblock->next && !is_goto ((statement_t *) sblock->tail))
printf (" 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");
}
void
print_flow (sblock_t *sblock)
{
printf ("digraph flow_%p {\n", sblock);
printf (" layout=dot; rankdir=TB;\n");
for (/**/; sblock; sblock = sblock->next)
flow_sblock (sblock);
printf ("}\n");
}