Patchwork PATCH RFA: Ping for -fdump-go-spec

login
register
mail settings
Submitter Ian Taylor
Date Nov. 12, 2010, 9:51 p.m.
Message ID <mcrfwv6jjmi.fsf@google.com>
Download mbox | patch
Permalink /patch/71024/
State New
Headers show

Comments

Ian Taylor - Nov. 12, 2010, 9:51 p.m.
This is a ping for the patch which adds the -fdump-go-spec option.  For
clarity, I have included the complete patch here.  I originally proposed
this patch, under the name -ggo, here:

http://gcc.gnu.org/ml/gcc-patches/2010-11/msg00369.html

Following a suggestion from Joseph Myers, I have changed the proposed
option name to -fdump-go-spec.

This patch adds a -fdump-go-spec option to gcc.  This option is used
when building the Go library.  The Go library calls libc functions
directly, which means that it needs to know the layout of structures
like stat and the size of types like uid_t.  I implemented this by
adding a -fdump-go-spec option to gcc which generates debugging
information as Go language comments in the assembler file.  The shell
script mksysinfo.sh in the libgo library uses that like this:

${CC} -D_GNU_SOURCE -fdump-go-spec=gen-sysinfo.go -S -o sysinfo.s sysinfo.c

where sysinfo.c is just a list of #include statements.  The script then
tweaks the gen-sysinfo.go file to pull together the generated Go
information in a useful form.

This is certainly the most intrusive Go-specific patch to the rest of
the compiler, and it's not really all that intrusive.  This patch is
almost entirely middle-end, so I am asking two things:

* Can the tiny patch to c-lex.c be approved or considered obvious?

* Does anybody have any objections to this patch?

Thanks.

Ian


gcc/:

2010-11-12  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.

gcc/c-family/:

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

	* c-lex.c (init_c_lex): Set macro debug callbacks if
	flag_dump_go_spec is set.
Ian Taylor - Nov. 15, 2010, 9:39 p.m.
Ian Lance Taylor <iant@google.com> writes:

> This is a ping for the patch which adds the -fdump-go-spec option.

> This patch is almost entirely middle-end, so I am asking two things:
>
> * Can the tiny patch to c-lex.c be approved or considered obvious?
>
> * Does anybody have any objections to this patch?

Joseph approved the change to c-lex.c offlist.  Nobody raised any
objections.  I have committed it to mainline as revision 166770.

Ian

Patch

Index: godump.c
===================================================================
--- godump.c	(revision 0)
+++ godump.c	(revision 0)
@@ -0,0 +1,874 @@ 
+/* 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;
+
+  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"
Index: common.opt
===================================================================
--- common.opt	(revision 166677)
+++ common.opt	(working copy)
@@ -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
Index: tree.h
===================================================================
--- tree.h	(revision 166677)
+++ 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"))) 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;
Index: debug.h
===================================================================
--- debug.h	(revision 166677)
+++ 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 166677)
+++ toplev.c	(working copy)
@@ -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.)  */
Index: debug.c
===================================================================
--- debug.c	(revision 166677)
+++ 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 166677)
+++ 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 166677)
+++ 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: vmsdbgout.c
===================================================================
--- vmsdbgout.c	(revision 166677)
+++ 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: sdbout.c
===================================================================
--- sdbout.c	(revision 166677)
+++ 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: doc/invoke.texi
===================================================================
--- doc/invoke.texi	(revision 166677)
+++ 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}.
@@ -1385,6 +1385,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 166677)
+++ 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 \
Index: c-family/c-lex.c
===================================================================
--- c-family/c-lex.c	(revision 166677)
+++ 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;