diff mbox

PATCH RFA: Add -ggo option

Message ID mcroca4e1mn.fsf@google.com
State New
Headers show

Commit Message

Ian Lance Taylor Nov. 5, 2010, 6:16 a.m. UTC
"Joseph S. Myers" <joseph@codesourcery.com> writes:

> Using debug hooks may be a reasonable implementation approach, but an 
> option outputting a separate file in Go syntax, like the other options I 
> mentioned (or the various debugging dump options), certainly seems a much 
> cleaner interface than using .s comments - I think it at least ought to 
> change to output a separate file.

This version of the patch implements the same functionality as
-fdump-go-spec=FILENAME.

I did it by having it make a copy of the debug hooks and install the
cases it needed to catch.  This required fixing the GTY desc field in
struct tree_type to avoid doing an address comparison of debug_hooks.  I
added some comments to make it more clear what is going on there.

OK for mainline?

Ian


2010-11-04  Ian Lance Taylor  <iant@google.com>

	* 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.

Comments

Joseph Myers Nov. 5, 2010, 1:31 p.m. UTC | #1
On Thu, 4 Nov 2010, Ian Lance Taylor wrote:

> +	  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.  */

I presume it's a deliberate choice here not to do anything about handling 
hex floats using 'p' with their exponents.

I don't see anything in this patch calling fclose on go_dump_file.  You 
should always call fclose and check the return value before exiting (or 
call fflush and ferror), so that errors can be detected, rather than lost 
if you rely on the implicit closing of files on exit.
Ian Lance Taylor Nov. 5, 2010, 2:02 p.m. UTC | #2
"Joseph S. Myers" <joseph@codesourcery.com> writes:

> On Thu, 4 Nov 2010, Ian Lance Taylor wrote:
>
>> +	  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.  */
>
> I presume it's a deliberate choice here not to do anything about handling 
> hex floats using 'p' with their exponents.

Yes, it's a deliberate choice, because Go does not support that format.
It would of course be possible to use the target floating point support
to rewrite those constants into a format which Go does understand.  I
think this is rather low priority.

I should perhaps mention that it's fairly unlikely that anybody would be
able to use -fgo-dump-spec to get something which could be used directly
with a Go program.  Some massaging would almost always be required.  The
option gives a starting point at best.  The shell script
libgo/mksysinfo.sh on the branch is an example of such massaging.


> I don't see anything in this patch calling fclose on go_dump_file.  You 
> should always call fclose and check the return value before exiting (or 
> call fflush and ferror), so that errors can be detected, rather than lost 
> if you rely on the implicit closing of files on exit.

Good point.  Thanks.  Fixed by adding:

  if (fclose (go_dump_file) != 0)
    error ("could not close Go dump file: %m");
  go_dump_file = NULL;

to the bottom of go_finish.

Ian
diff mbox

Patch

Index: godump.c
===================================================================
--- godump.c	(revision 0)
+++ godump.c	(revision 0)
@@ -0,0 +1,870 @@ 
+/* Output Go language descriptions of types.
+   Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor <iant@google.com>.
+
+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
+<http://www.gnu.org/licenses/>.  */
+
+/* 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;
+}
+
+/* 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"
Index: common.opt
===================================================================
--- common.opt	(revision 166300)
+++ common.opt	(working copy)
@@ -778,6 +778,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
Index: tree.h
===================================================================
--- tree.h	(revision 166300)
+++ tree.h	(working copy)
@@ -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_mod
 #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"),
+    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"),
 	  descbits ("2"))) symtab;
   tree name;
   tree minval;
Index: debug.h
===================================================================
--- debug.h	(revision 166300)
+++ debug.h	(working copy)
@@ -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  */
Index: toplev.c
===================================================================
--- toplev.c	(revision 166300)
+++ toplev.c	(working copy)
@@ -1962,6 +1962,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.)  */
Index: c-family/c-lex.c
===================================================================
--- c-family/c-lex.c	(revision 166300)
+++ c-family/c-lex.c	(working copy)
@@ -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;
Index: debug.c
===================================================================
--- debug.c	(revision 166300)
+++ debug.c	(working copy)
@@ -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_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
Index: dwarf2out.c
===================================================================
--- dwarf2out.c	(revision 166300)
+++ dwarf2out.c	(working copy)
@@ -5648,7 +5648,8 @@  const struct gcc_debug_hooks dwarf2_debu
   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
Index: dbxout.c
===================================================================
--- dbxout.c	(revision 166300)
+++ dbxout.c	(working copy)
@@ -384,7 +384,8 @@  const struct gcc_debug_hooks dbx_debug_h
   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
   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  */
 
Index: sdbout.c
===================================================================
--- sdbout.c	(revision 166300)
+++ sdbout.c	(working copy)
@@ -345,7 +345,8 @@  const struct gcc_debug_hooks sdb_debug_h
   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
   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"
Index: vmsdbgout.c
===================================================================
--- vmsdbgout.c	(revision 166300)
+++ vmsdbgout.c	(working copy)
@@ -210,7 +210,8 @@  const struct gcc_debug_hooks vmsdbg_debu
    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
Index: doc/invoke.texi
===================================================================
--- doc/invoke.texi	(revision 166300)
+++ doc/invoke.texi	(working copy)
@@ -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}.
@@ -1384,6 +1384,13 @@  For C and C++ source and include files, 
 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
 
Index: Makefile.in
===================================================================
--- Makefile.in	(revision 166300)
+++ Makefile.in	(working copy)
@@ -1233,6 +1237,7 @@  OBJS-common = \
 	gimple-low.o \
 	gimple-pretty-print.o \
 	gimplify.o \
+	godump.o \
 	graph.o \
 	graphds.o \
 	graphite.o \
@@ -2944,7 +2949,7 @@  dbxout.o : dbxout.c $(CONFIG_H) $(SYSTEM
    $(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 \
@@ -2964,6 +2969,8 @@  vmsdbgout.o : vmsdbgout.c $(CONFIG_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) \
@@ -3736,7 +3743,8 @@  GTFILES = $(CPP_ID_DATA_H) $(srcdir)/inp
   $(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 \