From c6a13190a1554ba9bb016e0cea2c1562a8278ab9 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Mon, 15 Nov 2010 21:38:40 +0000 Subject: [PATCH] godump.c: New file. gcc/: * godump.c: New file. * common.opt (fdump-go-spec=): New option. * tree.h: Add comments for TYPE_SYMTAB_ADDRESS and friends. (TYPE_SYMTAB_IS_ADDRESS, TYPE_SYMTAB_IS_POINTER): Define. (TYPE_SYMTAB_IS_DIE): Define. (struct tree_type): Change GTY for symtab field to use TYPE_SYMTAB_IS_ADDRESS and friends and to use a debug_hooks field to pick the union field. * debug.h (struct gcc_debug_hooks): Add tree_type_symtab_field. (dump_go_spec_init): Declare. * toplev.c (process_options): Handle flag_dump_go_spec. * debug.c: Include "tree.h". (do_nothing_debug_hooks): Set tree_type_symtab_field. * dwarf2out.c (dwarf2_debug_hooks): Likewise. * dbxout.c (dbx_debug_hooks): Likewise. (xcoff_debug_hooks): Likewise. * vmsdbgout.c (vmsdbg_debug_hooks): Likewise. * sdbout.c (sdb_debug_hooks): Likewise. Do not define if SDB_DEBUGGING_INFO is not defined. * doc/invoke.texi (Option Summary): Mention -fdump-go-spec. (Overall Options): Document -fdump-go-spec. * Makefile.in (OBJS-common): Add godump.o. (debug.o): Add dependency on $(TREE_H). (godump.o): New target. (GTFILES): Add $(srcdir)/godump.c. gcc/c-family/: * c-lex.c (init_c_lex): Set macro debug callbacks if flag_dump_go_spec is set. From-SVN: r166770 --- gcc/ChangeLog | 28 ++ gcc/Makefile.in | 8 +- gcc/c-family/ChangeLog | 5 + gcc/c-family/c-lex.c | 7 +- gcc/common.opt | 4 + gcc/dbxout.c | 6 +- gcc/debug.c | 4 +- gcc/debug.h | 9 + gcc/doc/invoke.texi | 9 +- gcc/dwarf2out.c | 3 +- gcc/godump.c | 874 +++++++++++++++++++++++++++++++++++++++++ gcc/sdbout.c | 44 +-- gcc/toplev.c | 6 + gcc/tree.h | 38 +- gcc/vmsdbgout.c | 3 +- 15 files changed, 988 insertions(+), 60 deletions(-) create mode 100644 gcc/godump.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index b0cd604822a..5fe196602dd 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,31 @@ +2010-11-15 Ian Lance Taylor + + * godump.c: New file. + * common.opt (fdump-go-spec=): New option. + * tree.h: Add comments for TYPE_SYMTAB_ADDRESS and friends. + (TYPE_SYMTAB_IS_ADDRESS, TYPE_SYMTAB_IS_POINTER): Define. + (TYPE_SYMTAB_IS_DIE): Define. + (struct tree_type): Change GTY for symtab field to use + TYPE_SYMTAB_IS_ADDRESS and friends and to use a debug_hooks field + to pick the union field. + * debug.h (struct gcc_debug_hooks): Add tree_type_symtab_field. + (dump_go_spec_init): Declare. + * toplev.c (process_options): Handle flag_dump_go_spec. + * debug.c: Include "tree.h". + (do_nothing_debug_hooks): Set tree_type_symtab_field. + * dwarf2out.c (dwarf2_debug_hooks): Likewise. + * dbxout.c (dbx_debug_hooks): Likewise. + (xcoff_debug_hooks): Likewise. + * vmsdbgout.c (vmsdbg_debug_hooks): Likewise. + * sdbout.c (sdb_debug_hooks): Likewise. Do not define if + SDB_DEBUGGING_INFO is not defined. + * doc/invoke.texi (Option Summary): Mention -fdump-go-spec. + (Overall Options): Document -fdump-go-spec. + * Makefile.in (OBJS-common): Add godump.o. + (debug.o): Add dependency on $(TREE_H). + (godump.o): New target. + (GTFILES): Add $(srcdir)/godump.c. + 2010-11-15 Jakub Jelinek PR debug/46095 diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 28315012d4b..6bf5a5148e8 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1234,6 +1234,7 @@ OBJS-common = \ gimple-low.o \ gimple-pretty-print.o \ gimplify.o \ + godump.o \ graph.o \ graphds.o \ graphite.o \ @@ -2945,7 +2946,7 @@ dbxout.o : dbxout.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \ $(RTL_H) $(FLAGS_H) $(REGS_H) debug.h $(TM_P_H) $(TARGET_H) $(FUNCTION_H) \ langhooks.h insn-config.h reload.h $(GSTAB_H) xcoffout.h output.h dbxout.h \ $(TOPLEV_H) $(DIAGNOSTIC_CORE_H) $(GGC_H) $(OBSTACK_H) $(EXPR_H) gt-dbxout.h -debug.o : debug.c debug.h $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) +debug.o : debug.c debug.h $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) sdbout.o : sdbout.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) debug.h \ $(TREE_H) $(GGC_H) $(RTL_H) $(REGS_H) $(FLAGS_H) insn-config.h \ output.h $(TOPLEV_H) $(DIAGNOSTIC_CORE_H) $(TM_P_H) gsyms.h langhooks.h $(TARGET_H) sdbout.h \ @@ -2965,6 +2966,8 @@ vmsdbgout.o : vmsdbgout.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) xcoffout.o : xcoffout.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(TREE_H) $(RTL_H) xcoffout.h $(FLAGS_H) $(TOPLEV_H) $(DIAGNOSTIC_CORE_H) output.h dbxout.h \ $(GGC_H) $(TARGET_H) debug.h $(GSTAB_H) xcoff.h +godump.o : godump.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(DIAGNOSTIC_CORE_H) \ + $(TREE_H) $(GGC_H) pointer-set.h $(OBSTACK_H) debug.h emit-rtl.o : emit-rtl.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ $(TREE_H) $(FLAGS_H) $(FUNCTION_H) $(REGS_H) insn-config.h $(RECOG_H) \ $(GGC_H) $(EXPR_H) hard-reg-set.h $(BITMAP_H) $(TOPLEV_H) $(DIAGNOSTIC_CORE_H) $(BASIC_BLOCK_H) \ @@ -3739,7 +3742,8 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/input.h $(srcdir)/coretypes.h \ $(srcdir)/emit-rtl.c $(srcdir)/except.h $(srcdir)/explow.c $(srcdir)/expr.c \ $(srcdir)/expr.h \ $(srcdir)/function.c $(srcdir)/except.c \ - $(srcdir)/gcse.c $(srcdir)/integrate.c $(srcdir)/lists.c $(srcdir)/optabs.c \ + $(srcdir)/gcse.c $(srcdir)/godump.c \ + $(srcdir)/integrate.c $(srcdir)/lists.c $(srcdir)/optabs.c \ $(srcdir)/profile.c $(srcdir)/mcf.c \ $(srcdir)/reg-stack.c $(srcdir)/cfglayout.c $(srcdir)/cfglayout.h \ $(srcdir)/sdbout.c $(srcdir)/stor-layout.c \ diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index a66f37acefd..3af8ebeb5d6 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,8 @@ +2010-11-15 Ian Lance Taylor + + * c-lex.c (init_c_lex): Set macro debug callbacks if + flag_dump_go_spec is set. + 2010-11-15 Nicola Pero * c-common.h (objc_build_incr_expr_for_property_ref): New. diff --git a/gcc/c-family/c-lex.c b/gcc/c-family/c-lex.c index 851449fbac4..68a0fe04a5c 100644 --- a/gcc/c-family/c-lex.c +++ b/gcc/c-family/c-lex.c @@ -87,9 +87,10 @@ init_c_lex (void) cb->read_pch = c_common_read_pch; /* Set the debug callbacks if we can use them. */ - if (debug_info_level == DINFO_LEVEL_VERBOSE - && (write_symbols == DWARF2_DEBUG - || write_symbols == VMS_AND_DWARF2_DEBUG)) + if ((debug_info_level == DINFO_LEVEL_VERBOSE + && (write_symbols == DWARF2_DEBUG + || write_symbols == VMS_AND_DWARF2_DEBUG)) + || flag_dump_go_spec != NULL) { cb->define = cb_define; cb->undef = cb_undef; diff --git a/gcc/common.opt b/gcc/common.opt index d9006eec403..0a6e38c4bbd 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -825,6 +825,10 @@ fdump-final-insns= Common RejectNegative Joined Var(flag_dump_final_insns) -fdump-final-insns=filename Dump to filename the insns at the end of translation +fdump-go-spec= +Common RejectNegative Joined Var(flag_dump_go_spec) +-fdump-go-spec=filename Write all declarations to file as Go code + fdump-noaddr Common Report Var(flag_dump_noaddr) Suppress output of addresses in debugging dumps diff --git a/gcc/dbxout.c b/gcc/dbxout.c index c9ca04d197e..89ea0f36090 100644 --- a/gcc/dbxout.c +++ b/gcc/dbxout.c @@ -384,7 +384,8 @@ const struct gcc_debug_hooks dbx_debug_hooks = debug_nothing_rtx_rtx, /* copy_call_info */ debug_nothing_uid, /* virtual_call */ debug_nothing_tree_tree, /* set_name */ - 0 /* start_end_main_source_file */ + 0, /* start_end_main_source_file */ + TYPE_SYMTAB_IS_ADDRESS /* tree_type_symtab_field */ }; #endif /* DBX_DEBUGGING_INFO */ @@ -423,7 +424,8 @@ const struct gcc_debug_hooks xcoff_debug_hooks = debug_nothing_rtx_rtx, /* copy_call_info */ debug_nothing_uid, /* virtual_call */ debug_nothing_tree_tree, /* set_name */ - 0 /* start_end_main_source_file */ + 0, /* start_end_main_source_file */ + TYPE_SYMTAB_IS_ADDRESS /* tree_type_symtab_field */ }; #endif /* XCOFF_DEBUGGING_INFO */ diff --git a/gcc/debug.c b/gcc/debug.c index c53d05eae58..47d5a13e22c 100644 --- a/gcc/debug.c +++ b/gcc/debug.c @@ -20,6 +20,7 @@ #include "system.h" #include "coretypes.h" #include "tm.h" +#include "tree.h" #include "debug.h" /* The do-nothing debug hooks. */ @@ -57,7 +58,8 @@ const struct gcc_debug_hooks do_nothing_debug_hooks = debug_nothing_rtx_rtx, /* copy_call_info */ debug_nothing_uid, /* virtual_call */ debug_nothing_tree_tree, /* set_name */ - 0 /* start_end_main_source_file */ + 0, /* start_end_main_source_file */ + TYPE_SYMTAB_IS_ADDRESS /* tree_type_symtab_field */ }; /* This file contains implementations of each debug hook that do diff --git a/gcc/debug.h b/gcc/debug.h index 2ca0ad86e50..cdaf457cb4c 100644 --- a/gcc/debug.h +++ b/gcc/debug.h @@ -165,6 +165,10 @@ struct gcc_debug_hooks /* This is 1 if the debug writer wants to see start and end commands for the main source files, and 0 otherwise. */ int start_end_main_source_file; + + /* The type of symtab field used by these debug hooks. This is one + of the TYPE_SYMTAB_IS_xxx values defined in tree.h. */ + int tree_type_symtab_field; }; extern const struct gcc_debug_hooks *debug_hooks; @@ -217,4 +221,9 @@ extern int symbol_queue_index; const char *remap_debug_filename (const char *); void add_debug_prefix_map (const char *); +/* For -fdump-go-spec. */ + +extern const struct gcc_debug_hooks * +dump_go_spec_init (const char *, const struct gcc_debug_hooks *); + #endif /* !GCC_DEBUG_H */ diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 2cc28219fa3..9509b835c9c 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -165,7 +165,7 @@ in the following sections. -pipe -pass-exit-codes @gol -x @var{language} -v -### --help@r{[}=@var{class}@r{[},@dots{}@r{]]} --target-help @gol --version -wrapper @@@var{file} -fplugin=@var{file} -fplugin-arg-@var{name}=@var{arg} @gol --fdump-ada-spec@r{[}-slim@r{]}} +-fdump-ada-spec@r{[}-slim@r{]}} -fdump-go-spec=@var{file} @item C Language Options @xref{C Dialect Options,,Options Controlling C Dialect}. @@ -1385,6 +1385,13 @@ For C and C++ source and include files, generate corresponding Ada specs. @xref{Generating Ada Bindings for C and C++ headers,,, gnat_ugn, GNAT User's Guide}, which provides detailed documentation on this feature. +@item -fdump-go-spec=@var{file} +For input files in any language, generate corresponding Go +declarations in @var{file}. This generates Go @code{const}, +@code{type}, @code{var}, and @code{func} declarations which may be a +useful way to start writing a Go interface to code written in some +other language. + @include @value{srcdir}/../libiberty/at-file.texi @end table diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c index 144c2862422..4eade28815a 100644 --- a/gcc/dwarf2out.c +++ b/gcc/dwarf2out.c @@ -5648,7 +5648,8 @@ const struct gcc_debug_hooks dwarf2_debug_hooks = dwarf2out_copy_call_info, dwarf2out_virtual_call, dwarf2out_set_name, - 1 /* start_end_main_source_file */ + 1, /* start_end_main_source_file */ + TYPE_SYMTAB_IS_DIE /* tree_type_symtab_field */ }; /* NOTE: In the comments in this file, many references are made to diff --git a/gcc/godump.c b/gcc/godump.c new file mode 100644 index 00000000000..662aa92284a --- /dev/null +++ b/gcc/godump.c @@ -0,0 +1,874 @@ +/* Output Go language descriptions of types. + Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc. + Written by Ian Lance Taylor . + +This file is part of GCC. + +GCC 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 3, or (at your option) any later +version. + +GCC 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 GCC; see the file COPYING3. If not see +. */ + +/* This file is used during the build process to emit Go language + descriptions of declarations from C header files. It uses the + debug info hooks to emit the descriptions. The Go language + descriptions then become part of the Go runtime support + library. + + All global names are output with a leading underscore, so that they + are all hidden in Go. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "diagnostic-core.h" +#include "tree.h" +#include "ggc.h" +#include "pointer-set.h" +#include "obstack.h" +#include "debug.h" + +/* We dump this information from the debug hooks. This gives us a + stable and maintainable API to hook into. In order to work + correctly when -g is used, we build our own hooks structure which + wraps the hooks we need to change. */ + +/* Our debug hooks. This is initialized by dump_go_spec_init. */ + +static struct gcc_debug_hooks go_debug_hooks; + +/* The real debug hooks. */ + +static const struct gcc_debug_hooks *real_debug_hooks; + +/* The file where we should write information. */ + +static FILE *go_dump_file; + +/* A queue of decls to output. */ + +static GTY(()) VEC(tree,gc) *queue; + +/* A hash table of macros we have seen. */ + +static htab_t macro_hash; + +/* For the hash tables. */ + +static int +string_hash_eq (const void *y1, const void *y2) +{ + return strcmp ((const char *) y1, (const char *) y2) == 0; +} + +/* A macro definition. */ + +static void +go_define (unsigned int lineno, const char *buffer) +{ + const char *p; + const char *name_end; + char *out_buffer; + char *q; + char *copy; + hashval_t hashval; + void **slot; + + real_debug_hooks->define (lineno, buffer); + + /* Skip macro functions. */ + for (p = buffer; *p != '\0' && *p != ' '; ++p) + if (*p == '(') + return; + + if (*p == '\0') + return; + + name_end = p; + + ++p; + if (*p == '\0') + return; + + copy = XNEWVEC (char, name_end - buffer + 1); + memcpy (copy, buffer, name_end - buffer); + copy[name_end - buffer] = '\0'; + + hashval = htab_hash_string (copy); + slot = htab_find_slot_with_hash (macro_hash, copy, hashval, NO_INSERT); + if (slot != NULL) + { + XDELETEVEC (copy); + return; + } + + /* For simplicity, we force all names to be hidden by adding an + initial underscore, and let the user undo this as needed. */ + out_buffer = XNEWVEC (char, strlen (p) * 2 + 1); + q = out_buffer; + while (*p != '\0') + { + if (ISALPHA (*p) || *p == '_') + { + const char *start; + char *n; + + start = p; + while (ISALNUM (*p) || *p == '_') + ++p; + n = XALLOCAVEC (char, p - start + 1); + memcpy (n, start, p - start); + n[p - start] = '\0'; + slot = htab_find_slot (macro_hash, n, NO_INSERT); + if (slot == NULL || *slot == NULL) + { + /* This is a reference to a name which was not defined + as a macro. */ + fprintf (go_dump_file, "// unknowndefine %s\n", buffer); + return; + } + + *q++ = '_'; + memcpy (q, start, p - start); + q += p - start; + } + else if (ISDIGIT (*p) + || (*p == '.' && ISDIGIT (p[1]))) + { + const char *start; + bool is_hex; + + start = p; + is_hex = false; + if (*p == '0' && (p[1] == 'x' || p[1] == 'X')) + { + p += 2; + is_hex = true; + } + while (ISDIGIT (*p) || *p == '.' || *p == 'e' || *p == 'E' + || (is_hex + && ((*p >= 'a' && *p <= 'f') + || (*p >= 'A' && *p <= 'F')))) + ++p; + memcpy (q, start, p - start); + q += p - start; + while (*p == 'u' || *p == 'U' || *p == 'l' || *p == 'L' + || *p == 'f' || *p == 'F' + || *p == 'd' || *p == 'D') + { + /* Go doesn't use any of these trailing type + modifiers. */ + ++p; + } + } + else if (ISSPACE (*p) + || *p == '+' || *p == '-' + || *p == '*' || *p == '/' || *p == '%' + || *p == '|' || *p == '&' + || *p == '>' || *p == '<' + || *p == '!' + || *p == '(' || *p == ')' + || *p == '"' || *p == '\'') + *q++ = *p++; + else + { + /* Something we don't recognize. */ + fprintf (go_dump_file, "// unknowndefine %s\n", buffer); + return; + } + } + *q = '\0'; + + slot = htab_find_slot_with_hash (macro_hash, copy, hashval, INSERT); + *slot = copy; + + fprintf (go_dump_file, "const _%s = %s\n", copy, out_buffer); + + XDELETEVEC (out_buffer); +} + +/* A macro undef. */ + +static void +go_undef (unsigned int lineno, const char *buffer) +{ + void **slot; + + real_debug_hooks->undef (lineno, buffer); + + slot = htab_find_slot (macro_hash, buffer, NO_INSERT); + if (slot == NULL) + return; + fprintf (go_dump_file, "// undef _%s\n", buffer); + /* We don't delete the slot from the hash table because that will + cause a duplicate const definition. */ +} + +/* A function or variable decl. */ + +static void +go_decl (tree decl) +{ + if (!TREE_PUBLIC (decl) + || DECL_IS_BUILTIN (decl) + || DECL_NAME (decl) == NULL_TREE) + return; + VEC_safe_push (tree, gc, queue, decl); +} + +/* A function decl. */ + +static void +go_function_decl (tree decl) +{ + real_debug_hooks->function_decl (decl); + go_decl (decl); +} + +/* A global variable decl. */ + +static void +go_global_decl (tree decl) +{ + real_debug_hooks->global_decl (decl); + go_decl (decl); +} + +/* A type declaration. */ + +static void +go_type_decl (tree decl, int local) +{ + real_debug_hooks->type_decl (decl, local); + + if (local || DECL_IS_BUILTIN (decl)) + return; + if (DECL_NAME (decl) == NULL_TREE + && (TYPE_NAME (TREE_TYPE (decl)) == NULL_TREE + || TREE_CODE (TYPE_NAME (TREE_TYPE (decl))) != IDENTIFIER_NODE) + && TREE_CODE (TREE_TYPE (decl)) != ENUMERAL_TYPE) + return; + VEC_safe_push (tree, gc, queue, decl); +} + +/* A container for the data we pass around when generating information + at the end of the compilation. */ + +struct godump_container +{ + /* DECLs that we have already seen. */ + struct pointer_set_t *decls_seen; + + /* Types which may potentially have to be defined as dummy + types. */ + struct pointer_set_t *pot_dummy_types; + + /* Go keywords. */ + htab_t keyword_hash; + + /* Global type definitions. */ + htab_t type_hash; + + /* Obstack used to write out a type definition. */ + struct obstack type_obstack; +}; + +/* Append an IDENTIFIER_NODE to OB. */ + +static void +go_append_string (struct obstack *ob, tree id) +{ + obstack_grow (ob, IDENTIFIER_POINTER (id), IDENTIFIER_LENGTH (id)); +} + +/* Write the Go version of TYPE to CONTAINER->TYPE_OBSTACK. + USE_TYPE_NAME is true if we can simply use a type name here without + needing to define it. IS_FUNC_OK is true if we can output a func + type here; the "func" keyword will already have been added. Return + true if the type can be represented in Go, false otherwise. */ + +static bool +go_format_type (struct godump_container *container, tree type, + bool use_type_name, bool is_func_ok) +{ + bool ret; + struct obstack *ob; + + ret = true; + ob = &container->type_obstack; + + if (TYPE_NAME (type) != NULL_TREE + && (pointer_set_contains (container->decls_seen, type) + || pointer_set_contains (container->decls_seen, TYPE_NAME (type))) + && (AGGREGATE_TYPE_P (type) + || POINTER_TYPE_P (type) + || TREE_CODE (type) == FUNCTION_TYPE)) + { + tree name; + + name = TYPE_NAME (type); + if (TREE_CODE (name) == IDENTIFIER_NODE) + { + obstack_1grow (ob, '_'); + go_append_string (ob, name); + return ret; + } + else if (TREE_CODE (name) == TYPE_DECL) + { + obstack_1grow (ob, '_'); + go_append_string (ob, DECL_NAME (name)); + return ret; + } + } + + pointer_set_insert (container->decls_seen, type); + + switch (TREE_CODE (type)) + { + case ENUMERAL_TYPE: + obstack_grow (ob, "int", 3); + break; + + case TYPE_DECL: + obstack_1grow (ob, '_'); + go_append_string (ob, DECL_NAME (type)); + break; + + case INTEGER_TYPE: + { + const char *s; + char buf[100]; + + switch (TYPE_PRECISION (type)) + { + case 8: + s = TYPE_UNSIGNED (type) ? "uint8" : "int8"; + break; + case 16: + s = TYPE_UNSIGNED (type) ? "uint16" : "int16"; + break; + case 32: + s = TYPE_UNSIGNED (type) ? "uint32" : "int32"; + break; + case 64: + s = TYPE_UNSIGNED (type) ? "uint64" : "int64"; + break; + default: + snprintf (buf, sizeof buf, "INVALID-int-%u%s", + TYPE_PRECISION (type), + TYPE_UNSIGNED (type) ? "u" : ""); + s = buf; + ret = false; + break; + } + obstack_grow (ob, s, strlen (s)); + } + break; + + case REAL_TYPE: + { + const char *s; + char buf[100]; + + switch (TYPE_PRECISION (type)) + { + case 32: + s = "float32"; + break; + case 64: + s = "float64"; + break; + case 80: + s = "float80"; + break; + default: + snprintf (buf, sizeof buf, "INVALID-float-%u", + TYPE_PRECISION (type)); + s = buf; + ret = false; + break; + } + obstack_grow (ob, s, strlen (s)); + } + break; + + case BOOLEAN_TYPE: + obstack_grow (ob, "bool", 4); + break; + + case POINTER_TYPE: + if (use_type_name + && TYPE_NAME (TREE_TYPE (type)) != NULL_TREE + && (RECORD_OR_UNION_TYPE_P (TREE_TYPE (type)) + || (POINTER_TYPE_P (TREE_TYPE (type)) + && (TREE_CODE (TREE_TYPE (TREE_TYPE (type))) + == FUNCTION_TYPE)))) + { + tree name; + + name = TYPE_NAME (TREE_TYPE (type)); + if (TREE_CODE (name) == IDENTIFIER_NODE) + { + obstack_grow (ob, "*_", 2); + go_append_string (ob, name); + + /* The pointer here can be used without the struct or + union definition. So this struct or union is a a + potential dummy type. */ + if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (type))) + pointer_set_insert (container->pot_dummy_types, + IDENTIFIER_POINTER (name)); + + return ret; + } + else if (TREE_CODE (name) == TYPE_DECL) + { + obstack_grow (ob, "*_", 2); + go_append_string (ob, DECL_NAME (name)); + if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (type))) + pointer_set_insert (container->pot_dummy_types, + IDENTIFIER_POINTER (DECL_NAME (name))); + return ret; + } + } + if (TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE) + obstack_grow (ob, "func", 4); + else + obstack_1grow (ob, '*'); + if (VOID_TYPE_P (TREE_TYPE (type))) + obstack_grow (ob, "byte", 4); + else + { + if (!go_format_type (container, TREE_TYPE (type), use_type_name, + true)) + ret = false; + } + break; + + case ARRAY_TYPE: + obstack_1grow (ob, '['); + if (TYPE_DOMAIN (type) != NULL_TREE + && TREE_CODE (TYPE_DOMAIN (type)) == INTEGER_TYPE + && TYPE_MIN_VALUE (TYPE_DOMAIN (type)) != NULL_TREE + && TREE_CODE (TYPE_MIN_VALUE (TYPE_DOMAIN (type))) == INTEGER_CST + && tree_int_cst_sgn (TYPE_MIN_VALUE (TYPE_DOMAIN (type))) == 0 + && TYPE_MAX_VALUE (TYPE_DOMAIN (type)) != NULL_TREE + && TREE_CODE (TYPE_MAX_VALUE (TYPE_DOMAIN (type))) == INTEGER_CST + && host_integerp (TYPE_MAX_VALUE (TYPE_DOMAIN (type)), 0)) + { + char buf[100]; + + snprintf (buf, sizeof buf, HOST_WIDE_INT_PRINT_DEC "+1", + tree_low_cst (TYPE_MAX_VALUE (TYPE_DOMAIN (type)), 0)); + obstack_grow (ob, buf, strlen (buf)); + } + obstack_1grow (ob, ']'); + if (!go_format_type (container, TREE_TYPE (type), use_type_name, false)) + ret = false; + break; + + case UNION_TYPE: + case RECORD_TYPE: + { + tree field; + int i; + + obstack_grow (ob, "struct { ", 9); + i = 0; + for (field = TYPE_FIELDS (type); + field != NULL_TREE; + field = TREE_CHAIN (field)) + { + if (DECL_NAME (field) == NULL) + { + char buf[100]; + + obstack_grow (ob, "_f", 2); + snprintf (buf, sizeof buf, "%d", i); + obstack_grow (ob, buf, strlen (buf)); + i++; + } + else + { + const char *var_name; + void **slot; + + /* Start variable name with an underscore if a keyword. */ + var_name = IDENTIFIER_POINTER (DECL_NAME (field)); + slot = htab_find_slot (container->keyword_hash, var_name, + NO_INSERT); + if (slot != NULL) + obstack_1grow (ob, '_'); + go_append_string (ob, DECL_NAME (field)); + } + obstack_1grow (ob, ' '); + if (DECL_BIT_FIELD (field)) + { + obstack_grow (ob, "INVALID-bit-field", 17); + ret = false; + } + else + { + /* Do not expand type if a record or union type or a + function pointer. */ + if (TYPE_NAME (TREE_TYPE (field)) != NULL_TREE + && (RECORD_OR_UNION_TYPE_P (TREE_TYPE (field)) + || (POINTER_TYPE_P (TREE_TYPE (field)) + && (TREE_CODE (TREE_TYPE (TREE_TYPE (field))) + == FUNCTION_TYPE)))) + { + tree name = TYPE_NAME (TREE_TYPE (field)); + if (TREE_CODE (name) == IDENTIFIER_NODE) + { + obstack_1grow (ob, '_'); + go_append_string (ob, name); + } + else if (TREE_CODE (name) == TYPE_DECL) + { + obstack_1grow (ob, '_'); + go_append_string (ob, DECL_NAME (name)); + } + } + else + { + if (!go_format_type (container, TREE_TYPE (field), true, + false)) + ret = false; + } + } + obstack_grow (ob, "; ", 2); + + /* Only output the first field of a union, and hope for + the best. */ + if (TREE_CODE (type) == UNION_TYPE) + break; + } + obstack_1grow (ob, '}'); + } + break; + + case FUNCTION_TYPE: + { + tree args; + bool is_varargs; + tree result; + + /* Go has no way to write a type which is a function but not a + pointer to a function. */ + if (!is_func_ok) + { + obstack_grow (ob, "func*", 5); + ret = false; + } + + obstack_1grow (ob, '('); + is_varargs = true; + for (args = TYPE_ARG_TYPES (type); + args != NULL_TREE; + args = TREE_CHAIN (args)) + { + if (VOID_TYPE_P (TREE_VALUE (args))) + { + gcc_assert (TREE_CHAIN (args) == NULL); + is_varargs = false; + break; + } + if (args != TYPE_ARG_TYPES (type)) + obstack_grow (ob, ", ", 2); + if (!go_format_type (container, TREE_VALUE (args), true, false)) + ret = false; + } + if (is_varargs) + { + if (TYPE_ARG_TYPES (type) != NULL_TREE) + obstack_grow (ob, ", ", 2); + obstack_grow (ob, "...interface{}", 14); + } + obstack_1grow (ob, ')'); + + result = TREE_TYPE (type); + if (!VOID_TYPE_P (result)) + { + obstack_1grow (ob, ' '); + if (!go_format_type (container, result, use_type_name, false)) + ret = false; + } + } + break; + + default: + obstack_grow (ob, "INVALID-type", 12); + ret = false; + break; + } + + return ret; +} + +/* Output the type which was built on the type obstack, and then free + it. */ + +static void +go_output_type (struct godump_container *container) +{ + struct obstack *ob; + + ob = &container->type_obstack; + obstack_1grow (ob, '\0'); + fputs (obstack_base (ob), go_dump_file); + obstack_free (ob, obstack_base (ob)); +} + +/* Output a function declaration. */ + +static void +go_output_fndecl (struct godump_container *container, tree decl) +{ + if (!go_format_type (container, TREE_TYPE (decl), false, true)) + fprintf (go_dump_file, "// "); + fprintf (go_dump_file, "func _%s ", + IDENTIFIER_POINTER (DECL_NAME (decl))); + go_output_type (container); + fprintf (go_dump_file, " __asm__(\"%s\")\n", + IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))); +} + +/* Output a typedef or something like a struct definition. */ + +static void +go_output_typedef (struct godump_container *container, tree decl) +{ + /* If we have an enum type, output the enum constants + separately. */ + if (TREE_CODE (TREE_TYPE (decl)) == ENUMERAL_TYPE + && TYPE_SIZE (TREE_TYPE (decl)) != 0 + && !pointer_set_contains (container->decls_seen, TREE_TYPE (decl)) + && (TYPE_CANONICAL (TREE_TYPE (decl)) == NULL_TREE + || !pointer_set_contains (container->decls_seen, + TYPE_CANONICAL (TREE_TYPE (decl))))) + { + tree element; + + for (element = TYPE_VALUES (TREE_TYPE (decl)); + element != NULL_TREE; + element = TREE_CHAIN (element)) + fprintf (go_dump_file, "const _%s = " HOST_WIDE_INT_PRINT_DEC "\n", + IDENTIFIER_POINTER (TREE_PURPOSE (element)), + tree_low_cst (TREE_VALUE (element), 0)); + pointer_set_insert (container->decls_seen, TREE_TYPE (decl)); + if (TYPE_CANONICAL (TREE_TYPE (decl)) != NULL_TREE) + pointer_set_insert (container->decls_seen, + TYPE_CANONICAL (TREE_TYPE (decl))); + } + + if (DECL_NAME (decl) != NULL_TREE) + { + void **slot; + const char *type; + + type = IDENTIFIER_POINTER (DECL_NAME (decl)); + /* If type defined already, skip. */ + slot = htab_find_slot (container->type_hash, type, INSERT); + if (*slot != NULL) + return; + *slot = CONST_CAST (void *, (const void *) type); + + if (!go_format_type (container, TREE_TYPE (decl), false, false)) + fprintf (go_dump_file, "// "); + fprintf (go_dump_file, "type _%s ", + IDENTIFIER_POINTER (DECL_NAME (decl))); + go_output_type (container); + pointer_set_insert (container->decls_seen, decl); + } + else if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (decl))) + { + void **slot; + const char *type; + + type = IDENTIFIER_POINTER (TYPE_NAME (TREE_TYPE ((decl)))); + /* If type defined already, skip. */ + slot = htab_find_slot (container->type_hash, type, INSERT); + if (*slot != NULL) + return; + *slot = CONST_CAST (void *, (const void *) type); + + if (!go_format_type (container, TREE_TYPE (decl), false, false)) + fprintf (go_dump_file, "// "); + fprintf (go_dump_file, "type _%s ", + IDENTIFIER_POINTER (TYPE_NAME (TREE_TYPE (decl)))); + go_output_type (container); + } + else + return; + + fprintf (go_dump_file, "\n"); +} + +/* Output a variable. */ + +static void +go_output_var (struct godump_container *container, tree decl) +{ + if (pointer_set_contains (container->decls_seen, decl) + || pointer_set_contains (container->decls_seen, DECL_NAME (decl))) + return; + pointer_set_insert (container->decls_seen, decl); + pointer_set_insert (container->decls_seen, DECL_NAME (decl)); + if (!go_format_type (container, TREE_TYPE (decl), true, false)) + fprintf (go_dump_file, "// "); + fprintf (go_dump_file, "var _%s ", + IDENTIFIER_POINTER (DECL_NAME (decl))); + go_output_type (container); + fprintf (go_dump_file, "\n"); + + /* Sometimes an extern variable is declared with an unknown struct + type. */ + if (TYPE_NAME (TREE_TYPE (decl)) != NULL_TREE + && RECORD_OR_UNION_TYPE_P (TREE_TYPE (decl))) + { + tree type_name = TYPE_NAME (TREE_TYPE (decl)); + if (TREE_CODE (type_name) == IDENTIFIER_NODE) + pointer_set_insert (container->pot_dummy_types, + IDENTIFIER_POINTER (type_name)); + else if (TREE_CODE (type_name) == TYPE_DECL) + pointer_set_insert (container->pot_dummy_types, + IDENTIFIER_POINTER (DECL_NAME (type_name))); + } +} + +/* Build a hash table with the Go keywords. */ + +static const char * const keywords[] = { + "__asm__", "break", "case", "chan", "const", "continue", "default", + "defer", "else", "fallthrough", "for", "func", "go", "goto", "if", + "import", "interface", "map", "package", "range", "return", "select", + "struct", "switch", "type", "var" +}; + +static void +keyword_hash_init (struct godump_container *container) +{ + size_t i; + size_t count = sizeof (keywords) / sizeof (keywords[0]); + void **slot; + + for (i = 0; i < count; i++) + { + slot = htab_find_slot (container->keyword_hash, keywords[i], INSERT); + *slot = CONST_CAST (void *, (const void *) keywords[i]); + } +} + +/* Traversing the pot_dummy_types and seeing which types are present + in the global types hash table and creating dummy definitions if + not found. This function is invoked by pointer_set_traverse. */ + +static bool +find_dummy_types (const void *ptr, void *adata) +{ + struct godump_container *data = (struct godump_container *) adata; + const char *type = (const char *) ptr; + void **slot; + + slot = htab_find_slot (data->type_hash, type, NO_INSERT); + if (slot == NULL) + fprintf (go_dump_file, "type _%s struct {}\n", type); + return true; +} + +/* Output symbols. */ + +static void +go_finish (const char *filename) +{ + struct godump_container container; + unsigned int ix; + tree decl; + + real_debug_hooks->finish (filename); + + container.decls_seen = pointer_set_create (); + container.pot_dummy_types = pointer_set_create (); + container.type_hash = htab_create (100, htab_hash_string, + string_hash_eq, NULL); + container.keyword_hash = htab_create (50, htab_hash_string, + string_hash_eq, NULL); + obstack_init (&container.type_obstack); + + keyword_hash_init (&container); + + FOR_EACH_VEC_ELT (tree, queue, ix, decl) + { + switch (TREE_CODE (decl)) + { + case FUNCTION_DECL: + go_output_fndecl (&container, decl); + break; + + case TYPE_DECL: + go_output_typedef (&container, decl); + break; + + case VAR_DECL: + go_output_var (&container, decl); + break; + + default: + gcc_unreachable(); + } + } + + /* To emit dummy definitions. */ + pointer_set_traverse (container.pot_dummy_types, find_dummy_types, + (void *) &container); + + pointer_set_destroy (container.decls_seen); + pointer_set_destroy (container.pot_dummy_types); + htab_delete (container.type_hash); + htab_delete (container.keyword_hash); + obstack_free (&container.type_obstack, NULL); + + queue = NULL; + + if (fclose (go_dump_file) != 0) + error ("could not close Go dump file: %m"); + go_dump_file = NULL; +} + +/* Set up our hooks. */ + +const struct gcc_debug_hooks * +dump_go_spec_init (const char *filename, const struct gcc_debug_hooks *hooks) +{ + go_dump_file = fopen (filename, "w"); + if (go_dump_file == NULL) + { + error ("could not open Go dump file %qs: %m", filename); + return hooks; + } + + go_debug_hooks = *hooks; + real_debug_hooks = hooks; + + go_debug_hooks.finish = go_finish; + go_debug_hooks.define = go_define; + go_debug_hooks.undef = go_undef; + go_debug_hooks.function_decl = go_function_decl; + go_debug_hooks.global_decl = go_global_decl; + go_debug_hooks.type_decl = go_type_decl; + + macro_hash = htab_create (100, htab_hash_string, string_hash_eq, NULL); + + return &go_debug_hooks; +} + +#include "gt-godump.h" diff --git a/gcc/sdbout.c b/gcc/sdbout.c index 53075acdcb7..95d7120dd49 100644 --- a/gcc/sdbout.c +++ b/gcc/sdbout.c @@ -345,7 +345,8 @@ const struct gcc_debug_hooks sdb_debug_hooks = debug_nothing_rtx_rtx, /* copy_call_info */ debug_nothing_uid, /* virtual_call */ debug_nothing_tree_tree, /* set_name */ - 0 /* start_end_main_source_file */ + 0, /* start_end_main_source_file */ + TYPE_SYMTAB_IS_POINTER /* tree_type_symtab_field */ }; /* Return a unique string to name an anonymous type. */ @@ -1704,47 +1705,6 @@ sdbout_init (const char *input_file_name ATTRIBUTE_UNUSED) preinit_symbols = 0; } -#else /* SDB_DEBUGGING_INFO */ - -/* This should never be used, but its address is needed for comparisons. */ -const struct gcc_debug_hooks sdb_debug_hooks = -{ - 0, /* init */ - 0, /* finish */ - 0, /* assembly_start */ - 0, /* define */ - 0, /* undef */ - 0, /* start_source_file */ - 0, /* end_source_file */ - 0, /* begin_block */ - 0, /* end_block */ - 0, /* ignore_block */ - 0, /* source_line */ - 0, /* begin_prologue */ - 0, /* end_prologue */ - 0, /* begin_epilogue */ - 0, /* end_epilogue */ - 0, /* begin_function */ - 0, /* end_function */ - 0, /* function_decl */ - 0, /* global_decl */ - 0, /* type_decl */ - 0, /* imported_module_or_decl */ - 0, /* deferred_inline_function */ - 0, /* outlining_inline_function */ - 0, /* label */ - 0, /* handle_pch */ - 0, /* var_location */ - 0, /* switch_text_section */ - 0, /* direct_call */ - 0, /* virtual_call_token */ - 0, /* copy_call_info */ - 0, /* virtual_call */ - 0, /* set_name */ - 0 /* start_end_main_source_file */ -}; - - #endif /* SDB_DEBUGGING_INFO */ #include "gt-sdbout.h" diff --git a/gcc/toplev.c b/gcc/toplev.c index 1b25485bd87..3f158a4426d 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -1916,6 +1916,12 @@ process_options (void) flag_var_tracking_uninit = 0; } + /* The debug hooks are used to implement -fdump-go-spec because it + gives a simple and stable API for all the information we need to + dump. */ + if (flag_dump_go_spec != NULL) + debug_hooks = dump_go_spec_init (flag_dump_go_spec, debug_hooks); + /* If the user specifically requested variable tracking with tagging uninitialized variables, we need to turn on variable tracking. (We already determined above that variable tracking is feasible.) */ diff --git a/gcc/tree.h b/gcc/tree.h index d4827a63f1d..6ae90f4128a 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -2076,9 +2076,6 @@ struct GTY(()) tree_block { #define TYPE_MIN_VALUE(NODE) (NUMERICAL_TYPE_CHECK (NODE)->type.minval) #define TYPE_MAX_VALUE(NODE) (NUMERICAL_TYPE_CHECK (NODE)->type.maxval) #define TYPE_PRECISION(NODE) (TYPE_CHECK (NODE)->type.precision) -#define TYPE_SYMTAB_ADDRESS(NODE) (TYPE_CHECK (NODE)->type.symtab.address) -#define TYPE_SYMTAB_POINTER(NODE) (TYPE_CHECK (NODE)->type.symtab.pointer) -#define TYPE_SYMTAB_DIE(NODE) (TYPE_CHECK (NODE)->type.symtab.die) #define TYPE_NAME(NODE) (TYPE_CHECK (NODE)->type.name) #define TYPE_NEXT_VARIANT(NODE) (TYPE_CHECK (NODE)->type.next_variant) #define TYPE_MAIN_VARIANT(NODE) (TYPE_CHECK (NODE)->type.main_variant) @@ -2300,6 +2297,33 @@ extern enum machine_mode vector_type_mode (const_tree); #define TYPE_CONTAINS_PLACEHOLDER_INTERNAL(NODE) \ (TYPE_CHECK (NODE)->type.contains_placeholder_bits) +/* The debug output functions use the symtab union field to store + information specific to the debugging format. The different debug + output hooks store different types in the union field. These three + macros are used to access different fields in the union. The debug + hooks are responsible for consistently using only a specific + macro. */ + +/* Symtab field as an integer. Used by stabs generator in dbxout.c to + hold the type's number in the generated stabs. */ +#define TYPE_SYMTAB_ADDRESS(NODE) (TYPE_CHECK (NODE)->type.symtab.address) + +/* Symtab field as a string. Used by COFF generator in sdbout.c to + hold struct/union type tag names. */ +#define TYPE_SYMTAB_POINTER(NODE) (TYPE_CHECK (NODE)->type.symtab.pointer) + +/* Symtab field as a pointer to a DWARF DIE. Used by DWARF generator + in dwarf2out.c to point to the DIE generated for the type. */ +#define TYPE_SYMTAB_DIE(NODE) (TYPE_CHECK (NODE)->type.symtab.die) + +/* The garbage collector needs to know the interpretation of the + symtab field. These constants represent the different types in the + union. */ + +#define TYPE_SYMTAB_IS_ADDRESS (0) +#define TYPE_SYMTAB_IS_POINTER (1) +#define TYPE_SYMTAB_IS_DIE (2) + struct die_struct; struct GTY(()) tree_type { @@ -2333,10 +2357,10 @@ struct GTY(()) tree_type { tree pointer_to; tree reference_to; union tree_type_symtab { - int GTY ((tag ("0"))) address; - const char * GTY ((tag ("1"))) pointer; - struct die_struct * GTY ((tag ("2"))) die; - } GTY ((desc ("debug_hooks == &sdb_debug_hooks ? 1 : debug_hooks == &dwarf2_debug_hooks ? 2 : 0"))) symtab; + int GTY ((tag ("TYPE_SYMTAB_IS_ADDRESS"))) address; + const char * GTY ((tag ("TYPE_SYMTAB_IS_POINTER"))) pointer; + struct die_struct * GTY ((tag ("TYPE_SYMTAB_IS_DIE"))) die; + } GTY ((desc ("debug_hooks->tree_type_symtab_field"))) symtab; tree name; tree minval; tree maxval; diff --git a/gcc/vmsdbgout.c b/gcc/vmsdbgout.c index 8ae5f2b6229..45da4419722 100644 --- a/gcc/vmsdbgout.c +++ b/gcc/vmsdbgout.c @@ -210,7 +210,8 @@ const struct gcc_debug_hooks vmsdbg_debug_hooks debug_nothing_rtx_rtx, /* copy_call_info */ debug_nothing_uid, /* virtual_call */ debug_nothing_tree_tree, /* set_name */ - 0 /* start_end_main_source_file */ + 0, /* start_end_main_source_file */ + TYPE_SYMTAB_IS_ADDRESS /* tree_type_symtab_field */ }; /* Definitions of defaults for assembler-dependent names of various