[qfcc] Add type dot dumping

It's not connected up yet because I'm unsure of just where to put things
(it gets messy fast), but just being able to see the structure of
complex types is nice.
This commit is contained in:
Bill Currie 2020-03-28 15:34:34 +09:00
parent 8479cad8a8
commit ec3c2426ff
6 changed files with 299 additions and 3 deletions

View File

@ -74,6 +74,7 @@ typedef struct type_s {
struct type_s *next;
int freeable;
int allocated;
int printid; ///< for dot output
struct protocollist_s *protos;
const char *encoding; ///< Objective-QC encoding
struct def_s *type_def; ///< offset of qfo encodoing
@ -153,6 +154,7 @@ type_t *alias_type (type_t *type, type_t *alias_chain, const char *name);
const type_t *unalias_type (const type_t *type) __attribute__((pure));
void print_type_str (struct dstring_s *str, const type_t *type);
void print_type (const type_t *type);
void dump_dot_type (void *t, const char *filename);
const char *encode_params (const type_t *type);
void encode_type (struct dstring_s *encoding, const type_t *type);
const char *type_get_encoding (const type_t *type);

View File

@ -40,7 +40,9 @@ bin_SCRIPTS= qfpreqcc
common_src=\
class.c codespace.c constfold.c cpp.c dags.c debug.c def.c defspace.c \
diagnostic.c dot.c dot_dag.c dot_expr.c dot_flow.c dot_sblock.c emit.c \
diagnostic.c \
dot.c dot_dag.c dot_expr.c dot_flow.c dot_sblock.c dot_type.c \
emit.c \
expr.c expr_assign.c expr_binary.c expr_bool.c expr_compound.c expr_obj.c \
flow.c function.c grab.c \
idstuff.c \

View File

@ -55,8 +55,12 @@ dump_dot (const char *stage, void *data,
} else {
dot_index++;
}
fname = nva ("%s.%s.%03d.%s.dot", options.output_file, current_func->name,
dot_index, stage);
if (current_func) {
fname = nva ("%s.%s.%03d.%s.dot", options.output_file,
current_func->name, dot_index, stage);
} else {
fname = nva ("%s.%03d.%s.dot", options.output_file, dot_index, stage);
}
dump_func (data, fname);
free (fname);
}

View File

@ -0,0 +1,283 @@
/*
dot_type.c
"emit" types to dot (graphvis).
Copyright (C) 2020 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2020/03/28
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
#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/mathlib.h>
#include <QF/quakeio.h>
#include <QF/va.h>
#include "class.h"
#include "symtab.h"
#include "type.h"
#include "qc-parse.h"
#include "strpool.h"
typedef void (*print_f) (dstring_t *dstr, type_t *, int, int);
static void dot_print_type (dstring_t *dstr, type_t *t, int level, int id);
static void
print_pointer (dstring_t *dstr, type_t *t, int level, int id)
{
int indent = level * 2 + 2;
type_t *aux = t->t.fldptr.type;
dot_print_type (dstr, aux, level, id);
dasprintf (dstr, "%*st_%p -> \"t_%p\";\n", indent, "", t, aux);
dasprintf (dstr, "%*st_%p [label=\"%c\"];\n", indent, "", t,
t->type == ev_pointer ? '*' : '.');
}
static void
print_ellipsis (dstring_t *dstr, int level, int id)
{
static int ellipsis_id;
int indent = level * 2 + 2;
if (ellipsis_id == id) {
return;
}
ellipsis_id = id;
dasprintf (dstr, "%*st_ellipsis [label=\"...\"];\n", indent, "");
}
static void
print_function (dstring_t *dstr, type_t *t, int level, int id)
{
int indent = level * 2 + 2;
const ty_func_t *func = &t->t.func;
type_t *ret = func->type;
type_t *param;
dot_print_type (dstr, ret, level + 1, id);
if (func->num_params < 0) {
for (int i = 0; i < ~func->num_params; i++) {
param = func->param_types[i];
dot_print_type (dstr, param, level + 1, id);
}
print_ellipsis (dstr, level, id);
} else {
for (int i = 0; i < func->num_params; i++) {
param = func->param_types[i];
dot_print_type (dstr, param, level + 1, id);
}
}
dasprintf (dstr, "%*st_%p -> \"t_%p\" [label=\"r\"];\n", indent, "",
t, ret);
if (func->num_params < 0) {
for (int i = 0; i < ~func->num_params; i++) {
param = func->param_types[i];
dasprintf (dstr, "%*st_%p -> \"t_%p\";\n", indent, "", t, param);
}
dasprintf (dstr, "%*st_%p -> \"t_ellipsis\";\n", indent, "", t);
} else {
for (int i = 0; i < func->num_params; i++) {
param = func->param_types[i];
dasprintf (dstr, "%*st_%p -> \"t_%p\";\n", indent, "", t, param);
}
}
dasprintf (dstr, "%*st_%p [label=\"( )\"];\n", indent, "", t);
}
static void
print_basic (dstring_t *dstr, type_t *t, int level, int id)
{
if (t->type == ev_pointer || t->type == ev_field) {
print_pointer (dstr, t, level, id);
} else if (t->type == ev_func) {
print_function (dstr, t, level, id);
} else {
int indent = level * 2 + 2;
dasprintf (dstr, "%*st_%p [label=\"%s\"];\n", indent, "", t, t->name);
}
}
static void
print_struct (dstring_t *dstr, type_t *t, int level, int id)
{
int indent = level * 2 + 2;
const symtab_t *symtab = t->t.symtab;
const symbol_t *sym;
int pnum;
static const char *struct_type_names[3] = {"struct", "union", "enum"};
const char *struct_type = struct_type_names[t->meta - ty_struct];
if (!symtab) {
dasprintf (dstr, "%*st_%p [label=\"%s %s\"];\n", indent, "", t,
struct_type, quote_string (t->name));
return;
}
if (t->meta != ty_enum) {
for (sym = symtab->symbols; sym; sym = sym->next) {
if (sym->sy_type != sy_var) {
continue;
}
dot_print_type (dstr, sym->type, level, id);
}
for (pnum = 0, sym = symtab->symbols; sym; sym = sym->next) {
if (sym->sy_type != sy_var) {
continue;
}
dasprintf (dstr, "%*st_%p:f%d -> \"t_%p\";\n", indent, "",
t, pnum++, sym->type);
}
}
dasprintf (dstr, "%*st_%p [shape=none,label=<\n", indent, "", t);
dasprintf (dstr, "%*s<table border=\"0\" cellborder=\"1\" "
"cellspacing=\"0\">\n",
indent + 2, "");
dasprintf (dstr, "%*s<tr><td colspan=\"2\">%s %s</td></tr>\n",
indent + 4, "",
struct_type, quote_string (t->name));
for (pnum = 0, sym = symtab->symbols; sym; sym = sym->next) {
int val;
const char *port = "";
if (sym->sy_type == sy_const) {
val = sym->s.value->v.integer_val;
} else {
if (sym->sy_type != sy_var) {
continue;
}
val = sym->s.offset;
port = va (" port=\"f%d\"", pnum++);
}
dasprintf (dstr, "%*s<tr><td>%s</td><td%s>%d</td></tr>\n",
indent + 4, "",
quote_string (sym->name), port, val);
}
dasprintf (dstr, "%*s</table>\n", indent + 2, "");
dasprintf (dstr, "%*s>];\n", indent, "");
}
static void
print_array (dstring_t *dstr, type_t *t, int level, int id)
{
int indent = level * 2 + 2;
type_t *type = t->t.array.type;
dot_print_type (dstr, type, level, id);
dasprintf (dstr, "%*st_%p -> \"t_%p\";\n", indent, "", t, type);
if (t->t.array.base) {
dasprintf (dstr, "%*st_%p [label=\"[%d..%d]\"];\n", indent, "", t,
t->t.array.base,
t->t.array.base + t->t.array.size - 1);
} else {
dasprintf (dstr, "%*st_%p [label=\"[%d]\"];\n", indent, "", t,
t->t.array.size);
}
}
static void
print_class (dstring_t *dstr, type_t *t, int level, int id)
{
int indent = level * 2 + 2;
dasprintf (dstr, "%*st_%p [label=\"class '%s'\"];\n", indent, "", t,
t->t.class->name);
}
static void
print_alias (dstring_t *dstr, type_t *t, int level, int id)
{
int indent = level * 2 + 2;
type_t *aux = t->t.alias.aux_type;
type_t *full = t->t.alias.full_type;
dot_print_type (dstr, aux, level, id);
dot_print_type (dstr, full, level, id);
dasprintf (dstr, "%*st_%p -> \"t_%p\";\n", indent, "", t, aux);
dasprintf (dstr, "%*st_%p -> \"t_%p\";\n", indent, "", t, full);
dasprintf (dstr, "%*st_%p [label=\"alias '%s'\"];\n", indent, "", t,
t->name);
}
static void
dot_print_type (dstring_t *dstr, type_t *t, int level, int id)
{
static print_f print_funcs[] = {
print_basic,
print_struct,
print_struct,
print_struct,
print_array,
print_class,
print_alias,
};
int indent = level * 2 + 2;
if (!t) {
dasprintf (dstr, "%*s\"e_%p\" [label=\"(null)\"];\n", indent, "", t);
return;
}
if (t->printid == id) // already printed this type
return;
t->printid = id;
if ((unsigned) t->meta >= sizeof (print_funcs) / sizeof (print_funcs[0])) {
dasprintf (dstr, "%*se_%p [label=\"(bad type meta)\\n%d\"];\n",
indent, "", t, t->meta);
return;
}
print_funcs [t->meta] (dstr, t, level, id);
}
void
dump_dot_type (void *_t, const char *filename)
{
static int id = 0;
dstring_t *dstr = dstring_newstr ();
type_t *t = (type_t *) _t;
dasprintf (dstr, "digraph type_%p {\n", t);
dasprintf (dstr, " graph [label=\"%s\"];\n", quote_string (filename));
dasprintf (dstr, " layout=dot; rankdir=TB; compound=true;\n");
dot_print_type (dstr, t, 0, ++id);
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

@ -7,6 +7,7 @@
#include "class.h"
#include "codespace.h"
#include "diagnostic.h"
#include "dot.h"
#include "debug.h"
#include "def.h"
#include "defspace.h"
@ -49,3 +50,6 @@ int is_id (const type_t *type){return type->type;}
int is_SEL (const type_t *type){return type->type;}
int is_Class (const type_t *type){return type->type;}
int compare_protocols (protocollist_t *protos1, protocollist_t *protos2){return protos1->count - protos2->count;}
void dump_dot (const char *stage, void *data,
void (*dump_func) (void *data, const char *fname)){}
void dump_dot_type (void *_t, const char *filename){}

View File

@ -50,6 +50,7 @@
#include "class.h"
#include "def.h"
#include "diagnostic.h"
#include "dot.h"
#include "expr.h"
#include "function.h"
#include "obj_type.h"