From ec3c2426ff94ed7a95c9ecd0715d084c74d43c15 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Sat, 28 Mar 2020 15:34:34 +0900 Subject: [PATCH] [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. --- tools/qfcc/include/type.h | 2 + tools/qfcc/source/Makefile.am | 4 +- tools/qfcc/source/dot.c | 8 +- tools/qfcc/source/dot_type.c | 283 ++++++++++++++++++++++++++++++++++ tools/qfcc/source/stub.c | 4 + tools/qfcc/source/type.c | 1 + 6 files changed, 299 insertions(+), 3 deletions(-) create mode 100644 tools/qfcc/source/dot_type.c diff --git a/tools/qfcc/include/type.h b/tools/qfcc/include/type.h index 70c650c52..686e864e1 100644 --- a/tools/qfcc/include/type.h +++ b/tools/qfcc/include/type.h @@ -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); diff --git a/tools/qfcc/source/Makefile.am b/tools/qfcc/source/Makefile.am index bbaf06ee6..b69dd6323 100644 --- a/tools/qfcc/source/Makefile.am +++ b/tools/qfcc/source/Makefile.am @@ -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 \ diff --git a/tools/qfcc/source/dot.c b/tools/qfcc/source/dot.c index 5d72b5150..51d82993a 100644 --- a/tools/qfcc/source/dot.c +++ b/tools/qfcc/source/dot.c @@ -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); } diff --git a/tools/qfcc/source/dot_type.c b/tools/qfcc/source/dot_type.c new file mode 100644 index 000000000..bffa4aa70 --- /dev/null +++ b/tools/qfcc/source/dot_type.c @@ -0,0 +1,283 @@ +/* + dot_type.c + + "emit" types to dot (graphvis). + + Copyright (C) 2020 Bill Currie + + Author: Bill Currie + 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 +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#include + +#include +#include +#include +#include + +#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\n", + indent + 2, ""); + dasprintf (dstr, "%*s\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%d\n", + indent + 4, "", + quote_string (sym->name), port, val); + } + dasprintf (dstr, "%*s
%s %s
%s
\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); +} diff --git a/tools/qfcc/source/stub.c b/tools/qfcc/source/stub.c index 405104126..f3601d04b 100644 --- a/tools/qfcc/source/stub.c +++ b/tools/qfcc/source/stub.c @@ -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){} diff --git a/tools/qfcc/source/type.c b/tools/qfcc/source/type.c index 475a4e1aa..5bb93b787 100644 --- a/tools/qfcc/source/type.c +++ b/tools/qfcc/source/type.c @@ -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"