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"