mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-10 23:32:09 +00:00
[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:
parent
8479cad8a8
commit
ec3c2426ff
6 changed files with 299 additions and 3 deletions
|
@ -74,6 +74,7 @@ typedef struct type_s {
|
||||||
struct type_s *next;
|
struct type_s *next;
|
||||||
int freeable;
|
int freeable;
|
||||||
int allocated;
|
int allocated;
|
||||||
|
int printid; ///< for dot output
|
||||||
struct protocollist_s *protos;
|
struct protocollist_s *protos;
|
||||||
const char *encoding; ///< Objective-QC encoding
|
const char *encoding; ///< Objective-QC encoding
|
||||||
struct def_s *type_def; ///< offset of qfo encodoing
|
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));
|
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_str (struct dstring_s *str, const type_t *type);
|
||||||
void print_type (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);
|
const char *encode_params (const type_t *type);
|
||||||
void encode_type (struct dstring_s *encoding, const type_t *type);
|
void encode_type (struct dstring_s *encoding, const type_t *type);
|
||||||
const char *type_get_encoding (const type_t *type);
|
const char *type_get_encoding (const type_t *type);
|
||||||
|
|
|
@ -40,7 +40,9 @@ bin_SCRIPTS= qfpreqcc
|
||||||
|
|
||||||
common_src=\
|
common_src=\
|
||||||
class.c codespace.c constfold.c cpp.c dags.c debug.c def.c defspace.c \
|
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 \
|
expr.c expr_assign.c expr_binary.c expr_bool.c expr_compound.c expr_obj.c \
|
||||||
flow.c function.c grab.c \
|
flow.c function.c grab.c \
|
||||||
idstuff.c \
|
idstuff.c \
|
||||||
|
|
|
@ -55,8 +55,12 @@ dump_dot (const char *stage, void *data,
|
||||||
} else {
|
} else {
|
||||||
dot_index++;
|
dot_index++;
|
||||||
}
|
}
|
||||||
fname = nva ("%s.%s.%03d.%s.dot", options.output_file, current_func->name,
|
if (current_func) {
|
||||||
dot_index, stage);
|
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);
|
dump_func (data, fname);
|
||||||
free (fname);
|
free (fname);
|
||||||
}
|
}
|
||||||
|
|
283
tools/qfcc/source/dot_type.c
Normal file
283
tools/qfcc/source/dot_type.c
Normal 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);
|
||||||
|
}
|
|
@ -7,6 +7,7 @@
|
||||||
#include "class.h"
|
#include "class.h"
|
||||||
#include "codespace.h"
|
#include "codespace.h"
|
||||||
#include "diagnostic.h"
|
#include "diagnostic.h"
|
||||||
|
#include "dot.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "def.h"
|
#include "def.h"
|
||||||
#include "defspace.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_SEL (const type_t *type){return type->type;}
|
||||||
int is_Class (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;}
|
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){}
|
||||||
|
|
|
@ -50,6 +50,7 @@
|
||||||
#include "class.h"
|
#include "class.h"
|
||||||
#include "def.h"
|
#include "def.h"
|
||||||
#include "diagnostic.h"
|
#include "diagnostic.h"
|
||||||
|
#include "dot.h"
|
||||||
#include "expr.h"
|
#include "expr.h"
|
||||||
#include "function.h"
|
#include "function.h"
|
||||||
#include "obj_type.h"
|
#include "obj_type.h"
|
||||||
|
|
Loading…
Reference in a new issue