| /* Output Go language descriptions of types. | 
 |    Copyright (C) 2008-2025 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 "tree.h" | 
 | #include "diagnostic-core.h" | 
 | #include "debug.h" | 
 | #include "stor-layout.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, va_gc> *queue; | 
 |  | 
 | struct godump_str_hash : string_hash, ggc_remove <const char *> {}; | 
 |  | 
 | /* A hash table of macros we have seen.  */ | 
 |  | 
 | static htab_t macro_hash; | 
 |  | 
 | /* The type of a value in macro_hash.  */ | 
 |  | 
 | struct macro_hash_value | 
 | { | 
 |   /* The name stored in the hash table.  */ | 
 |   char *name; | 
 |   /* The value of the macro.  */ | 
 |   char *value; | 
 | }; | 
 |  | 
 | /* Returns the number of units necessary to represent an integer with the given | 
 |    PRECISION (in bits).  */ | 
 |  | 
 | static inline unsigned int | 
 | precision_to_units (unsigned int precision) | 
 | { | 
 |   return (precision + BITS_PER_UNIT - 1) / BITS_PER_UNIT; | 
 | } | 
 |  | 
 | /* Calculate the hash value for an entry in the macro hash table.  */ | 
 |  | 
 | static hashval_t | 
 | macro_hash_hashval (const void *val) | 
 | { | 
 |   const struct macro_hash_value *mhval = (const struct macro_hash_value *) val; | 
 |   return htab_hash_string (mhval->name); | 
 | } | 
 |  | 
 | /* Compare values in the macro hash table for equality.  */ | 
 |  | 
 | static int | 
 | macro_hash_eq (const void *v1, const void *v2) | 
 | { | 
 |   const struct macro_hash_value *mhv1 = (const struct macro_hash_value *) v1; | 
 |   const struct macro_hash_value *mhv2 = (const struct macro_hash_value *) v2; | 
 |   return strcmp (mhv1->name, mhv2->name) == 0; | 
 | } | 
 |  | 
 | /* Free values deleted from the macro hash table.  */ | 
 |  | 
 | static void | 
 | macro_hash_del (void *v) | 
 | { | 
 |   struct macro_hash_value *mhv = (struct macro_hash_value *) v; | 
 |   XDELETEVEC (mhv->name); | 
 |   XDELETEVEC (mhv->value); | 
 |   XDELETE (mhv); | 
 | } | 
 |  | 
 | /* A macro definition.  */ | 
 |  | 
 | static void | 
 | go_define (unsigned int lineno, const char *buffer) | 
 | { | 
 |   const char *p; | 
 |   const char *name_end; | 
 |   size_t out_len; | 
 |   char *out_buffer; | 
 |   char *q; | 
 |   bool saw_operand; | 
 |   bool need_operand; | 
 |   struct macro_hash_value *mhval; | 
 |   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'; | 
 |  | 
 |   mhval = XNEW (struct macro_hash_value); | 
 |   mhval->name = copy; | 
 |   mhval->value = NULL; | 
 |  | 
 |   hashval = htab_hash_string (copy); | 
 |   slot = htab_find_slot_with_hash (macro_hash, mhval, hashval, NO_INSERT); | 
 |  | 
 |   /* For simplicity, we force all names to be hidden by adding an | 
 |      initial underscore, and let the user undo this as needed.  */ | 
 |   out_len = strlen (p) * 2 + 1; | 
 |   out_buffer = XNEWVEC (char, out_len); | 
 |   q = out_buffer; | 
 |   saw_operand = false; | 
 |   need_operand = false; | 
 |   while (*p != '\0') | 
 |     { | 
 |       switch (*p) | 
 | 	{ | 
 | 	case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': | 
 | 	case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': | 
 | 	case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': | 
 | 	case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': | 
 | 	case 'Y': case 'Z': | 
 | 	case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': | 
 | 	case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': | 
 | 	case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': | 
 | 	case 's': case 't': case 'u': case 'v': case 'w': case 'x': | 
 | 	case 'y': case 'z': | 
 | 	case '_': | 
 | 	  { | 
 | 	    /* The start of an identifier.  Technically we should also | 
 | 	       worry about UTF-8 identifiers, but they are not a | 
 | 	       problem for practical uses of -fdump-go-spec so we | 
 | 	       don't worry about them.  */ | 
 | 	    const char *start; | 
 | 	    char *n; | 
 | 	    struct macro_hash_value idval; | 
 |  | 
 | 	    if (saw_operand) | 
 | 	      goto unknown; | 
 |  | 
 | 	    start = p; | 
 | 	    while (ISALNUM (*p) || *p == '_') | 
 | 	      ++p; | 
 | 	    n = XALLOCAVEC (char, p - start + 1); | 
 | 	    memcpy (n, start, p - start); | 
 | 	    n[p - start] = '\0'; | 
 | 	    idval.name = n; | 
 | 	    idval.value = NULL; | 
 | 	    if (htab_find (macro_hash, &idval) == NULL) | 
 | 	      { | 
 | 		/* This is a reference to a name which was not defined | 
 | 		   as a macro.  */ | 
 | 		goto unknown; | 
 | 	      } | 
 |  | 
 | 	    *q++ = '_'; | 
 | 	    memcpy (q, start, p - start); | 
 | 	    q += p - start; | 
 |  | 
 | 	    saw_operand = true; | 
 | 	    need_operand = false; | 
 | 	  } | 
 | 	  break; | 
 |  | 
 | 	case '.': | 
 | 	  if (!ISDIGIT (p[1])) | 
 | 	    goto unknown; | 
 | 	  /* Fall through.  */ | 
 | 	case '0': case '1': case '2': case '3': case '4': | 
 | 	case '5': case '6': case '7': case '8': case '9': | 
 | 	  { | 
 | 	    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; | 
 | 	      } | 
 |  | 
 | 	    /* We'll pick up the exponent, if any, as an | 
 | 	       expression.  */ | 
 |  | 
 | 	    saw_operand = true; | 
 | 	    need_operand = false; | 
 | 	  } | 
 | 	  break; | 
 |  | 
 | 	case ' ': case '\t': | 
 | 	  *q++ = *p++; | 
 | 	  break; | 
 |  | 
 | 	case '(': | 
 | 	  /* Always OK, not part of an operand, presumed to start an | 
 | 	     operand.  */ | 
 | 	  *q++ = *p++; | 
 | 	  saw_operand = false; | 
 | 	  need_operand = false; | 
 | 	  break; | 
 |  | 
 | 	case ')': | 
 | 	  /* OK if we don't need an operand, and presumed to indicate | 
 | 	     an operand.  */ | 
 | 	  if (need_operand) | 
 | 	    goto unknown; | 
 | 	  *q++ = *p++; | 
 | 	  saw_operand = true; | 
 | 	  break; | 
 |  | 
 | 	case '+': case '-': | 
 | 	  /* Always OK, but not part of an operand.  */ | 
 | 	  *q++ = *p++; | 
 | 	  saw_operand = false; | 
 | 	  break; | 
 |  | 
 | 	case '*': case '/': case '%': case '|': case '&': case '^': | 
 | 	  /* Must be a binary operator.  */ | 
 | 	  if (!saw_operand) | 
 | 	    goto unknown; | 
 | 	  *q++ = *p++; | 
 | 	  saw_operand = false; | 
 | 	  need_operand = true; | 
 | 	  break; | 
 |  | 
 | 	case '=': | 
 | 	  *q++ = *p++; | 
 | 	  if (*p != '=') | 
 | 	    goto unknown; | 
 | 	  /* Must be a binary operator.  */ | 
 | 	  if (!saw_operand) | 
 | 	    goto unknown; | 
 | 	  *q++ = *p++; | 
 | 	  saw_operand = false; | 
 | 	  need_operand = true; | 
 | 	  break; | 
 |  | 
 | 	case '!': | 
 | 	  *q++ = *p++; | 
 | 	  if (*p == '=') | 
 | 	    { | 
 | 	      /* Must be a binary operator.  */ | 
 | 	      if (!saw_operand) | 
 | 		goto unknown; | 
 | 	      *q++ = *p++; | 
 | 	      saw_operand = false; | 
 | 	      need_operand = true; | 
 | 	    } | 
 | 	  else | 
 | 	    { | 
 | 	      /* Must be a unary operator.  */ | 
 | 	      if (saw_operand) | 
 | 		goto unknown; | 
 | 	      need_operand = true; | 
 | 	    } | 
 | 	  break; | 
 |  | 
 | 	case '<': case '>': | 
 | 	  /* Must be a binary operand, may be << or >> or <= or >=.  */ | 
 | 	  if (!saw_operand) | 
 | 	    goto unknown; | 
 | 	  *q++ = *p++; | 
 | 	  if (*p == *(p - 1) || *p == '=') | 
 | 	    *q++ = *p++; | 
 | 	  saw_operand = false; | 
 | 	  need_operand = true; | 
 | 	  break; | 
 |  | 
 | 	case '~': | 
 | 	  /* Must be a unary operand, must be translated for Go.  */ | 
 | 	  if (saw_operand) | 
 | 	    goto unknown; | 
 | 	  *q++ = '^'; | 
 | 	  p++; | 
 | 	  need_operand = true; | 
 | 	  break; | 
 |  | 
 | 	case '"': | 
 | 	case '\'': | 
 | 	  { | 
 | 	    char quote; | 
 | 	    int count; | 
 |  | 
 | 	    if (saw_operand) | 
 | 	      goto unknown; | 
 | 	    quote = *p; | 
 | 	    *q++ = *p++; | 
 | 	    count = 0; | 
 | 	    while (*p != quote) | 
 | 	      { | 
 | 		int c; | 
 |  | 
 | 		if (*p == '\0') | 
 | 		  goto unknown; | 
 |  | 
 | 		++count; | 
 |  | 
 | 		if (*p != '\\') | 
 | 		  { | 
 | 		    *q++ = *p++; | 
 | 		    continue; | 
 | 		  } | 
 |  | 
 | 		*q++ = *p++; | 
 | 		switch (*p) | 
 | 		  { | 
 | 		  case '0': case '1': case '2': case '3': | 
 | 		  case '4': case '5': case '6': case '7': | 
 | 		    c = 0; | 
 | 		    while (*p >= '0' && *p <= '7') | 
 | 		      { | 
 | 			*q++ = *p++; | 
 | 			++c; | 
 | 		      } | 
 | 		    /* Go octal characters are always 3 | 
 | 		       digits.  */ | 
 | 		    if (c != 3) | 
 | 		      goto unknown; | 
 | 		    break; | 
 |  | 
 | 		  case 'x': | 
 | 		    *q++ = *p++; | 
 | 		    c = 0; | 
 | 		    while (ISXDIGIT (*p)) | 
 | 		      { | 
 | 			*q++ = *p++; | 
 | 			++c; | 
 | 		      } | 
 | 		    /* Go hex characters are always 2 digits.  */ | 
 | 		    if (c != 2) | 
 | 		      goto unknown; | 
 | 		    break; | 
 |  | 
 | 		  case 'a': case 'b': case 'f': case 'n': case 'r': | 
 | 		  case 't': case 'v': case '\\': case '\'': case '"': | 
 | 		    *q++ = *p++; | 
 | 		    break; | 
 |  | 
 | 		  default: | 
 | 		    goto unknown; | 
 | 		  } | 
 | 	      } | 
 |  | 
 | 	    *q++ = *p++; | 
 |  | 
 | 	    if (quote == '\'' && count != 1) | 
 | 	      goto unknown; | 
 |  | 
 | 	    saw_operand = true; | 
 | 	    need_operand = false; | 
 |  | 
 | 	    break; | 
 | 	  } | 
 |  | 
 | 	default: | 
 | 	  goto unknown; | 
 | 	} | 
 |     } | 
 |  | 
 |   if (need_operand) | 
 |     goto unknown; | 
 |  | 
 |   gcc_assert ((size_t) (q - out_buffer) < out_len); | 
 |   *q = '\0'; | 
 |  | 
 |   mhval->value = out_buffer; | 
 |  | 
 |   if (slot == NULL) | 
 |     { | 
 |       slot = htab_find_slot_with_hash (macro_hash, mhval, hashval, INSERT); | 
 |       gcc_assert (slot != NULL && *slot == NULL); | 
 |     } | 
 |   else | 
 |     { | 
 |       if (*slot != NULL) | 
 | 	macro_hash_del (*slot); | 
 |     } | 
 |  | 
 |   *slot = mhval; | 
 |  | 
 |   return; | 
 |  | 
 |  unknown: | 
 |   fprintf (go_dump_file, "// unknowndefine %s\n", buffer); | 
 |   if (slot != NULL) | 
 |     htab_clear_slot (macro_hash, slot); | 
 |   XDELETEVEC (out_buffer); | 
 |   XDELETEVEC (copy); | 
 | } | 
 |  | 
 | /* A macro undef.  */ | 
 |  | 
 | static void | 
 | go_undef (unsigned int lineno, const char *buffer) | 
 | { | 
 |   struct macro_hash_value mhval; | 
 |   void **slot; | 
 |  | 
 |   real_debug_hooks->undef (lineno, buffer); | 
 |  | 
 |   mhval.name = CONST_CAST (char *, buffer); | 
 |   mhval.value = NULL; | 
 |   slot = htab_find_slot (macro_hash, &mhval, NO_INSERT); | 
 |   if (slot != NULL) | 
 |     htab_clear_slot (macro_hash, slot); | 
 | } | 
 |  | 
 | /* A function or variable decl.  */ | 
 |  | 
 | static void | 
 | go_decl (tree decl) | 
 | { | 
 |   if (!TREE_PUBLIC (decl) | 
 |       || DECL_IS_UNDECLARED_BUILTIN (decl) | 
 |       || DECL_NAME (decl) == NULL_TREE) | 
 |     return; | 
 |   vec_safe_push (queue, decl); | 
 | } | 
 |  | 
 | /* A function decl.  */ | 
 |  | 
 | static void | 
 | go_function_decl (tree decl) | 
 | { | 
 |   real_debug_hooks->function_decl (decl); | 
 |   go_decl (decl); | 
 | } | 
 |  | 
 | static void | 
 | go_early_global_decl (tree decl) | 
 | { | 
 |   go_decl (decl); | 
 |   if (TREE_CODE (decl) != FUNCTION_DECL || DECL_STRUCT_FUNCTION (decl) != NULL) | 
 |     real_debug_hooks->early_global_decl (decl); | 
 | } | 
 |  | 
 | /* A global variable decl.  */ | 
 |  | 
 | static void | 
 | go_late_global_decl (tree decl) | 
 | { | 
 |   real_debug_hooks->late_global_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_UNDECLARED_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 (queue, decl); | 
 | } | 
 |  | 
 | /* A container for the data we pass around when generating information | 
 |    at the end of the compilation.  */ | 
 |  | 
 | class godump_container | 
 | { | 
 | public: | 
 |   /* DECLs that we have already seen.  */ | 
 |   hash_set<tree> decls_seen; | 
 |  | 
 |   /* Types which may potentially have to be defined as dummy | 
 |      types.  */ | 
 |   hash_set<const char *, false, godump_str_hash> pot_dummy_types; | 
 |  | 
 |   /* Go keywords.  */ | 
 |   htab_t keyword_hash; | 
 |  | 
 |   /* Global type definitions.  */ | 
 |   htab_t type_hash; | 
 |  | 
 |   /* Invalid types.  */ | 
 |   htab_t invalid_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)); | 
 | } | 
 |  | 
 | /* Given an integer PRECISION in bits, returns a constant string that is the | 
 |    matching go int or uint type (depending on the IS_UNSIGNED flag).  Returns a | 
 |    NULL pointer if there is no matching go type.  */ | 
 |  | 
 | static const char * | 
 | go_get_uinttype_for_precision (unsigned int precision, bool is_unsigned) | 
 | { | 
 |   switch (precision) | 
 |     { | 
 |     case 8: | 
 |       return is_unsigned ? "uint8" : "int8"; | 
 |     case 16: | 
 |       return is_unsigned ? "uint16" : "int16"; | 
 |     case 32: | 
 |       return is_unsigned ? "uint32" : "int32"; | 
 |     case 64: | 
 |       return is_unsigned ? "uint64" : "int64"; | 
 |     default: | 
 |       return NULL; | 
 |     } | 
 | } | 
 |  | 
 | /* Append an artificial variable name with the suffix _INDEX to OB.  Returns | 
 |    INDEX + 1.  */ | 
 |  | 
 | static unsigned int | 
 | go_append_artificial_name (struct obstack *ob, unsigned int index) | 
 | { | 
 |   char buf[100]; | 
 |  | 
 |   /* FIXME: identifier may not be unique.  */ | 
 |   obstack_grow (ob, "Godump_", 7); | 
 |   snprintf (buf, sizeof buf, "%u", index); | 
 |   obstack_grow (ob, buf, strlen (buf)); | 
 |  | 
 |   return index + 1; | 
 | } | 
 |  | 
 | /* Append the variable name from DECL to OB.  If the name is in the | 
 |    KEYWORD_HASH, prepend an '_'.  */ | 
 |  | 
 | static void | 
 | go_append_decl_name (struct obstack *ob, tree decl, htab_t keyword_hash) | 
 | { | 
 |   const char *var_name; | 
 |   void **slot; | 
 |  | 
 |   /* Start variable name with an underscore if a keyword.  */ | 
 |   var_name = IDENTIFIER_POINTER (DECL_NAME (decl)); | 
 |   slot = htab_find_slot (keyword_hash, var_name, NO_INSERT); | 
 |   if (slot != NULL) | 
 |     obstack_1grow (ob, '_'); | 
 |   go_append_string (ob, DECL_NAME (decl)); | 
 | } | 
 |  | 
 | /* Appends a byte array with the necessary number of elements and the name | 
 |    "Godump_INDEX_pad" to pad from FROM_OFFSET to TO_OFFSET to OB assuming that | 
 |    the next field is automatically aligned to ALIGN_UNITS.  Returns INDEX + 1, | 
 |    or INDEX if no padding had to be appended.  The resulting offset where the | 
 |    next field is allocated is returned through RET_OFFSET.  */ | 
 |  | 
 | static unsigned int | 
 | go_append_padding (struct obstack *ob, unsigned int from_offset, | 
 | 		   unsigned int to_offset, unsigned int align_units, | 
 | 		   unsigned int index, unsigned int *ret_offset) | 
 | { | 
 |   if (from_offset % align_units > 0) | 
 |     from_offset += align_units - (from_offset % align_units); | 
 |   gcc_assert (to_offset >= from_offset); | 
 |   if (to_offset > from_offset) | 
 |     { | 
 |       char buf[100]; | 
 |  | 
 |       index = go_append_artificial_name (ob, index); | 
 |       snprintf (buf, sizeof buf, "_pad [%u]byte; ", to_offset - from_offset); | 
 |       obstack_grow (ob, buf, strlen (buf)); | 
 |     } | 
 |   *ret_offset = to_offset; | 
 |  | 
 |   return index; | 
 | } | 
 |  | 
 | /* Appends an array of type TYPE_STRING with zero elements and the name | 
 |    "_" to OB.  If TYPE_STRING is a null pointer, ERROR_STRING is appended | 
 |    instead of the type.  Returns INDEX + 1.  */ | 
 |  | 
 | static unsigned int | 
 | go_force_record_alignment (struct obstack *ob, const char *type_string, | 
 | 			   unsigned int index, const char *error_string) | 
 | { | 
 |   obstack_grow (ob, "_ ", 2); | 
 |   if (type_string == NULL) | 
 |     obstack_grow (ob, error_string, strlen (error_string)); | 
 |   else | 
 |     { | 
 |       obstack_grow (ob, "[0]", 3); | 
 |       obstack_grow (ob, type_string, strlen (type_string)); | 
 |     } | 
 |   obstack_grow (ob, "; ", 2); | 
 |  | 
 |   return index; | 
 | } | 
 |  | 
 | /* 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. | 
 |    P_ART_I is used for indexing artificial elements in nested structures and | 
 |    should always be a NULL pointer when called, except by certain recursive | 
 |    calls from go_format_type() itself.  */ | 
 |  | 
 | static bool | 
 | go_format_type (class godump_container *container, tree type, | 
 | 		bool use_type_name, bool is_func_ok, unsigned int *p_art_i, | 
 | 		bool is_anon_record_or_union) | 
 | { | 
 |   bool ret; | 
 |   struct obstack *ob; | 
 |   unsigned int art_i_dummy; | 
 |   bool is_union = false; | 
 |  | 
 |   if (p_art_i == NULL) | 
 |     { | 
 |       art_i_dummy = 0; | 
 |       p_art_i = &art_i_dummy; | 
 |     } | 
 |   ret = true; | 
 |   ob = &container->type_obstack; | 
 |  | 
 |   if (use_type_name | 
 |       && TYPE_NAME (type) != NULL_TREE | 
 |       && (AGGREGATE_TYPE_P (type) | 
 | 	  || POINTER_TYPE_P (type) | 
 | 	  || TREE_CODE (type) == FUNCTION_TYPE)) | 
 |     { | 
 |       tree name; | 
 |       void **slot; | 
 |  | 
 |       /* References to complex builtin types cannot be translated to | 
 | 	Go.  */ | 
 |       if (DECL_P (TYPE_NAME (type)) | 
 | 	  && DECL_IS_UNDECLARED_BUILTIN (TYPE_NAME (type))) | 
 | 	ret = false; | 
 |  | 
 |       name = TYPE_IDENTIFIER (type); | 
 |  | 
 |       slot = htab_find_slot (container->invalid_hash, IDENTIFIER_POINTER (name), | 
 | 			     NO_INSERT); | 
 |       if (slot != NULL) | 
 | 	ret = false; | 
 |  | 
 |       /* References to incomplete structs are permitted in many | 
 | 	 contexts, like behind a pointer or inside of a typedef. So | 
 | 	 consider any referenced struct a potential dummy type.  */ | 
 |       if (RECORD_OR_UNION_TYPE_P (type)) | 
 |        container->pot_dummy_types.add (IDENTIFIER_POINTER (name)); | 
 |  | 
 |       obstack_1grow (ob, '_'); | 
 |       go_append_string (ob, name); | 
 |       return ret; | 
 |     } | 
 |  | 
 |   switch (TREE_CODE (type)) | 
 |     { | 
 |     case TYPE_DECL: | 
 |       { | 
 | 	void **slot; | 
 |  | 
 | 	slot = htab_find_slot (container->invalid_hash, | 
 | 			       IDENTIFIER_POINTER (DECL_NAME (type)), | 
 | 			       NO_INSERT); | 
 | 	if (slot != NULL) | 
 | 	  ret = false; | 
 |  | 
 | 	obstack_1grow (ob, '_'); | 
 | 	go_append_string (ob, DECL_NAME (type)); | 
 |       } | 
 |       break; | 
 |  | 
 |     case ENUMERAL_TYPE: | 
 |     case INTEGER_TYPE: | 
 |       { | 
 | 	const char *s; | 
 | 	char buf[100]; | 
 |  | 
 | 	s = go_get_uinttype_for_precision (TYPE_PRECISION (type), | 
 | 					   TYPE_UNSIGNED (type)); | 
 | 	if (s == NULL) | 
 | 	  { | 
 | 	    snprintf (buf, sizeof buf, "INVALID-int-%u%s", | 
 | 		      TYPE_PRECISION (type), | 
 | 		      TYPE_UNSIGNED (type) ? "u" : ""); | 
 | 	    s = buf; | 
 | 	    ret = false; | 
 | 	  } | 
 | 	obstack_grow (ob, s, strlen (s)); | 
 |       } | 
 |       break; | 
 |  | 
 |     case BITINT_TYPE: | 
 |       { | 
 | 	const char *s; | 
 | 	char buf[100]; | 
 |  | 
 | 	s = go_get_uinttype_for_precision (TYPE_PRECISION (type), | 
 | 					   TYPE_UNSIGNED (type)); | 
 | 	if (s == NULL) | 
 | 	  { | 
 | 	    snprintf (buf, sizeof buf, "INVALID-bitint-%u%s", | 
 | 		      TYPE_PRECISION (type), | 
 | 		      TYPE_UNSIGNED (type) ? "u" : ""); | 
 | 	    s = buf; | 
 | 	    ret = false; | 
 | 	  } | 
 | 	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; | 
 | 	  default: | 
 | 	    snprintf (buf, sizeof buf, "INVALID-float-%u", | 
 | 		      TYPE_PRECISION (type)); | 
 | 	    s = buf; | 
 | 	    ret = false; | 
 | 	    break; | 
 | 	  } | 
 | 	obstack_grow (ob, s, strlen (s)); | 
 |       } | 
 |       break; | 
 |  | 
 |     case COMPLEX_TYPE: | 
 |       { | 
 | 	const char *s; | 
 | 	char buf[100]; | 
 | 	tree real_type; | 
 |  | 
 | 	real_type = TREE_TYPE (type); | 
 | 	if (TREE_CODE (real_type) == REAL_TYPE) | 
 | 	  { | 
 | 	    switch (TYPE_PRECISION (real_type)) | 
 | 	      { | 
 | 	      case 32: | 
 | 		s = "complex64"; | 
 | 		break; | 
 | 	      case 64: | 
 | 		s = "complex128"; | 
 | 		break; | 
 | 	      default: | 
 | 		snprintf (buf, sizeof buf, "INVALID-complex-%u", | 
 | 			  2 * TYPE_PRECISION (real_type)); | 
 | 		s = buf; | 
 | 		ret = false; | 
 | 		break; | 
 | 	      } | 
 | 	  } | 
 | 	else | 
 | 	  { | 
 | 	    s = "INVALID-complex-non-real"; | 
 | 	    ret = false; | 
 | 	  } | 
 | 	obstack_grow (ob, s, strlen (s)); | 
 |       } | 
 |       break; | 
 |  | 
 |     case BOOLEAN_TYPE: | 
 |       obstack_grow (ob, "bool", 4); | 
 |       break; | 
 |  | 
 |     case POINTER_TYPE: | 
 |       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, NULL, false)) | 
 | 	    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 | 
 | 	  && tree_fits_shwi_p (TYPE_MAX_VALUE (TYPE_DOMAIN (type)))) | 
 | 	{ | 
 | 	  char buf[100]; | 
 |  | 
 | 	  snprintf (buf, sizeof buf, HOST_WIDE_INT_PRINT_DEC "+1", | 
 | 		    tree_to_shwi (TYPE_MAX_VALUE (TYPE_DOMAIN (type)))); | 
 | 	  obstack_grow (ob, buf, strlen (buf)); | 
 | 	} | 
 |       else | 
 | 	obstack_1grow (ob, '0'); | 
 |       obstack_1grow (ob, ']'); | 
 |       if (!go_format_type (container, TREE_TYPE (type), use_type_name, false, | 
 | 			   NULL, false)) | 
 | 	ret = false; | 
 |       break; | 
 |  | 
 |     case UNION_TYPE: | 
 |       is_union = true; | 
 |       /* Fall through to RECORD_TYPE case.  */ | 
 |       gcc_fallthrough (); | 
 |     case RECORD_TYPE: | 
 |       { | 
 | 	unsigned int prev_field_end; | 
 | 	unsigned int known_alignment; | 
 | 	tree field; | 
 | 	bool emitted_a_field; | 
 |  | 
 | 	/* FIXME: Why is this necessary?  Without it we can get a core | 
 | 	   dump on the s390x headers, or from a file containing simply | 
 | 	   "typedef struct S T;".  */ | 
 | 	layout_type (type); | 
 |  | 
 | 	prev_field_end = 0; | 
 | 	known_alignment = 1; | 
 | 	/* Anonymous records and unions are flattened, i.e. they are not put | 
 | 	   into "struct { ... }".  */ | 
 | 	if (!is_anon_record_or_union) | 
 | 	  obstack_grow (ob, "struct { ", 9); | 
 | 	for (field = TYPE_FIELDS (type), emitted_a_field = false; | 
 | 	     field != NULL_TREE; | 
 | 	     field = TREE_CHAIN (field)) | 
 | 	  { | 
 | 	    if (TREE_CODE (field) != FIELD_DECL) | 
 | 	      continue; | 
 | 	    if (DECL_BIT_FIELD (field)) | 
 | 	      /* Bit fields are replaced by padding.  */ | 
 | 	      continue; | 
 | 	    /* Only the first non-bitfield field is emitted for unions.  */ | 
 | 	    if (!is_union || !emitted_a_field) | 
 | 	      { | 
 | 		/* Emit the field.  */ | 
 | 		bool field_ok; | 
 | 		bool is_anon_substructure; | 
 | 		unsigned int decl_align_unit; | 
 | 		unsigned int decl_offset; | 
 |  | 
 | 		field_ok = true; | 
 | 		emitted_a_field = true; | 
 | 		is_anon_substructure = | 
 | 		  (DECL_NAME (field) == NULL | 
 | 		   && (TREE_CODE (TREE_TYPE (field)) == RECORD_TYPE | 
 | 		       || TREE_CODE (TREE_TYPE (field)) == UNION_TYPE)); | 
 | 		/* Keep track of the alignment of named substructures, either | 
 | 		   of the whole record, or the alignment of the emitted field | 
 | 		   (for unions).  */ | 
 | 		decl_align_unit = DECL_ALIGN_UNIT (field); | 
 | 		if (!is_anon_substructure && decl_align_unit > known_alignment) | 
 | 		  known_alignment = decl_align_unit; | 
 | 		/* Pad to start of field.  */ | 
 | 		decl_offset = | 
 | 		  TREE_INT_CST_LOW (DECL_FIELD_OFFSET (field)) | 
 | 		  + precision_to_units | 
 | 		  (TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (field))); | 
 | 		{ | 
 | 		  unsigned int align_unit; | 
 |  | 
 | 		  /* For anonymous records and unions there is no automatic | 
 | 		     structure alignment, so use 1 as the alignment.  */ | 
 | 		  align_unit = (is_anon_substructure) ? 1 : decl_align_unit; | 
 | 		  *p_art_i = go_append_padding | 
 | 		    (ob, prev_field_end, decl_offset, align_unit, *p_art_i, | 
 | 		     &prev_field_end); | 
 | 		} | 
 | 		if (DECL_SIZE_UNIT (field)) | 
 | 		  prev_field_end += | 
 | 		    TREE_INT_CST_LOW (DECL_SIZE_UNIT (field)); | 
 | 		/* Emit the field name, but not for anonymous records and | 
 | 		   unions.  */ | 
 | 		if (!is_anon_substructure) | 
 | 		  { | 
 | 		    if (DECL_NAME (field) == NULL) | 
 | 		      *p_art_i = go_append_artificial_name (ob, *p_art_i); | 
 | 		    else | 
 | 		      go_append_decl_name | 
 | 			(ob, field, container->keyword_hash); | 
 | 		    obstack_1grow (ob, ' '); | 
 | 		  } | 
 | 		/* 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; | 
 | 		    void **slot; | 
 |  | 
 | 		    name = TYPE_IDENTIFIER (TREE_TYPE (field)); | 
 |  | 
 | 		    slot = htab_find_slot (container->invalid_hash, | 
 | 					   IDENTIFIER_POINTER (name), | 
 | 					   NO_INSERT); | 
 | 		    if (slot != NULL) | 
 | 		      field_ok = false; | 
 |  | 
 | 		    obstack_1grow (ob, '_'); | 
 | 		    go_append_string (ob, name); | 
 | 		  } | 
 | 		else | 
 | 		  { | 
 | 		    if (!go_format_type (container, TREE_TYPE (field), true, | 
 | 					 false, p_art_i, is_anon_substructure)) | 
 | 		      field_ok = false; | 
 | 		  } | 
 | 		if (!is_anon_substructure) | 
 | 		  obstack_grow (ob, "; ", 2); | 
 | 		if (!field_ok) | 
 | 		  ret = false; | 
 | 	      } | 
 | 	  } | 
 | 	/* Padding.  */ | 
 | 	*p_art_i = go_append_padding (ob, prev_field_end, | 
 | 				      TREE_INT_CST_LOW (TYPE_SIZE_UNIT (type)), | 
 | 				      1, *p_art_i, &prev_field_end); | 
 | 	/* Alignment.  */ | 
 | 	if (!is_anon_record_or_union | 
 | 	    && known_alignment < TYPE_ALIGN_UNIT (type)) | 
 | 	  { | 
 | 	    const char *s; | 
 | 	    char buf[100]; | 
 |  | 
 | 	    /* Enforce proper record alignment.  */ | 
 | 	    s = go_get_uinttype_for_precision | 
 | 	      (TYPE_ALIGN (type), TYPE_UNSIGNED (type)); | 
 | 	    if (s == NULL) | 
 | 	      { | 
 | 		snprintf (buf, sizeof buf, "INVALID-int-%u%s", | 
 | 			  TYPE_ALIGN (type), TYPE_UNSIGNED (type) ? "u" : ""); | 
 | 		s = buf; | 
 | 		ret = false; | 
 | 	      } | 
 | 	    *p_art_i = go_force_record_alignment (ob, s, *p_art_i, buf); | 
 | 	  } | 
 | 	if (!is_anon_record_or_union) | 
 | 	  obstack_1grow (ob, '}'); | 
 |       } | 
 |     break; | 
 |  | 
 |     case FUNCTION_TYPE: | 
 |       { | 
 | 	tree arg_type; | 
 | 	bool is_varargs; | 
 | 	tree result; | 
 | 	function_args_iterator iter; | 
 | 	bool seen_arg; | 
 |  | 
 | 	/* 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 = stdarg_p (type); | 
 | 	seen_arg = false; | 
 | 	FOREACH_FUNCTION_ARGS (type, arg_type, iter) | 
 | 	  { | 
 | 	    if (VOID_TYPE_P (arg_type)) | 
 | 	      break; | 
 | 	    if (seen_arg) | 
 | 	      obstack_grow (ob, ", ", 2); | 
 | 	    if (!go_format_type (container, arg_type, true, false, NULL, false)) | 
 | 	      ret = false; | 
 | 	    seen_arg = true; | 
 | 	  } | 
 | 	if (is_varargs) | 
 | 	  { | 
 | 	    if (prototype_p (type)) | 
 | 	      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, NULL, | 
 | 				 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 (class godump_container *container) | 
 | { | 
 |   struct obstack *ob; | 
 |  | 
 |   ob = &container->type_obstack; | 
 |   obstack_1grow (ob, '\0'); | 
 |   fputs ((char *) obstack_base (ob), go_dump_file); | 
 |   obstack_free (ob, obstack_base (ob)); | 
 | } | 
 |  | 
 | /* Output a function declaration.  */ | 
 |  | 
 | static void | 
 | go_output_fndecl (class godump_container *container, tree decl) | 
 | { | 
 |   if (!go_format_type (container, TREE_TYPE (decl), true, true, NULL, false)) | 
 |     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 (class 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 | 
 |       && !container->decls_seen.contains | 
 | 	    (TYPE_MAIN_VARIANT (TREE_TYPE (decl)))) | 
 |     { | 
 |       tree element; | 
 |  | 
 |       for (element = TYPE_VALUES (TREE_TYPE (decl)); | 
 | 	   element != NULL_TREE; | 
 | 	   element = TREE_CHAIN (element)) | 
 | 	{ | 
 | 	  const char *name; | 
 | 	  struct macro_hash_value *mhval; | 
 | 	  void **slot; | 
 | 	  char buf[WIDE_INT_PRINT_BUFFER_SIZE]; | 
 | 	  tree value = DECL_INITIAL (TREE_VALUE (element)); | 
 |  | 
 | 	  name = IDENTIFIER_POINTER (TREE_PURPOSE (element)); | 
 |  | 
 | 	  /* Sometimes a name will be defined as both an enum constant | 
 | 	     and a macro.  Avoid duplicate definition errors by | 
 | 	     treating enum constants as macros.  */ | 
 | 	  mhval = XNEW (struct macro_hash_value); | 
 | 	  mhval->name = xstrdup (name); | 
 | 	  mhval->value = NULL; | 
 | 	  slot = htab_find_slot (macro_hash, mhval, INSERT); | 
 | 	  if (*slot != NULL) | 
 | 	    macro_hash_del (*slot); | 
 |  | 
 | 	  if (tree_fits_shwi_p (value)) | 
 | 	    snprintf (buf, sizeof buf, HOST_WIDE_INT_PRINT_DEC, | 
 | 		     tree_to_shwi (value)); | 
 | 	  else if (tree_fits_uhwi_p (value)) | 
 | 	    snprintf (buf, sizeof buf, HOST_WIDE_INT_PRINT_UNSIGNED, | 
 | 		      tree_to_uhwi (value)); | 
 | 	  else | 
 | 	    { | 
 | 	      wide_int w = wi::to_wide (element); | 
 | 	      gcc_assert (w.get_len () <= WIDE_INT_MAX_INL_ELTS); | 
 | 	      print_hex (w, buf); | 
 | 	    } | 
 |  | 
 | 	  mhval->value = xstrdup (buf); | 
 | 	  *slot = mhval; | 
 | 	} | 
 |       container->decls_seen.add (TYPE_MAIN_VARIANT (TREE_TYPE (decl))); | 
 |     } | 
 |  | 
 |   if (DECL_NAME (decl) != NULL_TREE) | 
 |     { | 
 |       void **slot; | 
 |       const char *type; | 
 |       tree original_type; | 
 |  | 
 |       type = IDENTIFIER_POINTER (DECL_NAME (decl)); | 
 |       original_type = DECL_ORIGINAL_TYPE (decl); | 
 |       if (original_type == NULL_TREE) | 
 | 	original_type = TREE_TYPE (decl); | 
 |  | 
 |       /* Suppress typedefs where the type name matches the underlying | 
 | 	 struct/union/enum tag. This way we'll emit the struct definition | 
 | 	 instead of an invalid recursive type.  */ | 
 |       if (TYPE_IDENTIFIER (original_type) != NULL | 
 | 	  && IDENTIFIER_POINTER (TYPE_IDENTIFIER (original_type)) == type) | 
 | 	return; | 
 |  | 
 |       /* 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, original_type, true, false, | 
 | 			   NULL, false)) | 
 | 	{ | 
 | 	  fprintf (go_dump_file, "// "); | 
 | 	  slot = htab_find_slot (container->invalid_hash, type, INSERT); | 
 | 	  *slot = CONST_CAST (void *, (const void *) type); | 
 | 	} | 
 |       fprintf (go_dump_file, "type _%s ", | 
 | 	       IDENTIFIER_POINTER (DECL_NAME (decl))); | 
 |       go_output_type (container); | 
 |  | 
 |       if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (decl))) | 
 | 	{ | 
 | 	  HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (decl)); | 
 |  | 
 | 	  if (size > 0) | 
 | 	    fprintf (go_dump_file, | 
 | 		     "\nconst _sizeof_%s = " HOST_WIDE_INT_PRINT_DEC, | 
 | 		     IDENTIFIER_POINTER (DECL_NAME (decl)), | 
 | 		     size); | 
 | 	} | 
 |  | 
 |       container->decls_seen.add (decl); | 
 |     } | 
 |   else if ((RECORD_OR_UNION_TYPE_P (TREE_TYPE (decl)) | 
 | 	    || TREE_CODE (TREE_TYPE (decl)) == ENUMERAL_TYPE) | 
 | 	   && TYPE_NAME (TREE_TYPE (decl)) != NULL) | 
 |     { | 
 |        void **slot; | 
 |        const char *type; | 
 |        HOST_WIDE_INT size; | 
 |  | 
 |        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, NULL, | 
 | 			    false)) | 
 | 	 { | 
 | 	   fprintf (go_dump_file, "// "); | 
 | 	   slot = htab_find_slot (container->invalid_hash, type, INSERT); | 
 | 	   *slot = CONST_CAST (void *, (const void *) type); | 
 | 	 } | 
 |        fprintf (go_dump_file, "type _%s ", | 
 | 	       IDENTIFIER_POINTER (TYPE_NAME (TREE_TYPE (decl)))); | 
 |        go_output_type (container); | 
 |  | 
 |        size = int_size_in_bytes (TREE_TYPE (decl)); | 
 |        if (size > 0) | 
 | 	 fprintf (go_dump_file, | 
 | 		  "\nconst _sizeof_%s = " HOST_WIDE_INT_PRINT_DEC, | 
 | 		  IDENTIFIER_POINTER (TYPE_NAME (TREE_TYPE (decl))), | 
 | 		  size); | 
 |     } | 
 |   else | 
 |     return; | 
 |  | 
 |   fprintf (go_dump_file, "\n"); | 
 | } | 
 |  | 
 | /* Output a variable.  */ | 
 |  | 
 | static void | 
 | go_output_var (class godump_container *container, tree decl) | 
 | { | 
 |   bool is_valid; | 
 |   tree type_name; | 
 |   tree id; | 
 |  | 
 |   if (container->decls_seen.contains (decl) | 
 |       || container->decls_seen.contains (DECL_NAME (decl))) | 
 |     return; | 
 |   container->decls_seen.add (decl); | 
 |   container->decls_seen.add (DECL_NAME (decl)); | 
 |  | 
 |   type_name = TYPE_NAME (TREE_TYPE (decl)); | 
 |   id = NULL_TREE; | 
 |   if (type_name != NULL_TREE && TREE_CODE (type_name) == IDENTIFIER_NODE) | 
 |     id = type_name; | 
 |   else if (type_name != NULL_TREE && TREE_CODE (type_name) == TYPE_DECL | 
 | 	   && DECL_SOURCE_LOCATION (type_name) != BUILTINS_LOCATION | 
 | 	   && DECL_NAME (type_name)) | 
 |     id = DECL_NAME (type_name); | 
 |   if (id != NULL_TREE | 
 |       && (!htab_find_slot (container->type_hash, IDENTIFIER_POINTER (id), | 
 | 			   NO_INSERT) | 
 | 	  || htab_find_slot (container->invalid_hash, IDENTIFIER_POINTER (id), | 
 | 			     NO_INSERT))) | 
 |     id = NULL_TREE; | 
 |   if (id != NULL_TREE) | 
 |     { | 
 |       struct obstack *ob; | 
 |  | 
 |       ob = &container->type_obstack; | 
 |       obstack_1grow (ob, '_'); | 
 |       go_append_string (ob, id); | 
 |       is_valid = htab_find_slot (container->type_hash, IDENTIFIER_POINTER (id), | 
 | 				 NO_INSERT) != NULL; | 
 |     } | 
 |   else | 
 |     is_valid = go_format_type (container, TREE_TYPE (decl), true, false, NULL, | 
 | 			       false); | 
 |   if (is_valid | 
 |       && htab_find_slot (container->type_hash, | 
 | 			 IDENTIFIER_POINTER (DECL_NAME (decl)), | 
 | 			 NO_INSERT) != NULL) | 
 |     { | 
 |       /* There is already a type with this name, probably from a | 
 | 	 struct tag.  Prefer the type to the variable.  */ | 
 |       is_valid = false; | 
 |     } | 
 |   if (!is_valid) | 
 |     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 != NULL_TREE && RECORD_OR_UNION_TYPE_P (TREE_TYPE (decl))) | 
 |     { | 
 |       if (TREE_CODE (type_name) == IDENTIFIER_NODE) | 
 | 	container->pot_dummy_types.add (IDENTIFIER_POINTER (type_name)); | 
 |       else if (TREE_CODE (type_name) == TYPE_DECL) | 
 | 	container->pot_dummy_types.add | 
 | 			    (IDENTIFIER_POINTER (DECL_NAME (type_name))); | 
 |     } | 
 | } | 
 |  | 
 | /* Output the final value of a preprocessor macro or enum constant. | 
 |    This is called via htab_traverse_noresize.  */ | 
 |  | 
 | static int | 
 | go_print_macro (void **slot, void *arg ATTRIBUTE_UNUSED) | 
 | { | 
 |   struct macro_hash_value *mhval = (struct macro_hash_value *) *slot; | 
 |   fprintf (go_dump_file, "const _%s = %s\n", mhval->name, mhval->value); | 
 |   return 1; | 
 | } | 
 |  | 
 | /* 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 (class godump_container *container) | 
 | { | 
 |   size_t i; | 
 |   size_t count = ARRAY_SIZE (keywords); | 
 |   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 hash_set::traverse.  */ | 
 |  | 
 | bool | 
 | find_dummy_types (const char *const &ptr, godump_container *adata) | 
 | { | 
 |   class godump_container *data = (class godump_container *) adata; | 
 |   const char *type = (const char *) ptr; | 
 |   void **slot; | 
 |   void **islot; | 
 |  | 
 |   slot = htab_find_slot (data->type_hash, type, NO_INSERT); | 
 |   islot = htab_find_slot (data->invalid_hash, type, NO_INSERT); | 
 |   if (slot == NULL || islot != NULL) | 
 |     fprintf (go_dump_file, "type _%s struct {}\n", type); | 
 |   return true; | 
 | } | 
 |  | 
 | /* Output symbols.  */ | 
 |  | 
 | static void | 
 | go_finish (const char *filename) | 
 | { | 
 |   class godump_container container; | 
 |   unsigned int ix; | 
 |   tree decl; | 
 |  | 
 |   real_debug_hooks->finish (filename); | 
 |  | 
 |   container.type_hash = htab_create (100, htab_hash_string, | 
 | 				     htab_eq_string, NULL); | 
 |   container.invalid_hash = htab_create (10, htab_hash_string, | 
 | 					htab_eq_string, NULL); | 
 |   container.keyword_hash = htab_create (50, htab_hash_string, | 
 | 					htab_eq_string, NULL); | 
 |   obstack_init (&container.type_obstack); | 
 |  | 
 |   keyword_hash_init (&container); | 
 |  | 
 |   FOR_EACH_VEC_SAFE_ELT (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 (); | 
 | 	} | 
 |     } | 
 |  | 
 |   htab_traverse_noresize (macro_hash, go_print_macro, NULL); | 
 |  | 
 |   /* To emit dummy definitions.  */ | 
 |   container.pot_dummy_types.traverse<godump_container *, find_dummy_types> | 
 |                         (&container); | 
 |  | 
 |   htab_delete (container.type_hash); | 
 |   htab_delete (container.invalid_hash); | 
 |   htab_delete (container.keyword_hash); | 
 |   obstack_free (&container.type_obstack, NULL); | 
 |  | 
 |   vec_free (queue); | 
 |  | 
 |   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.early_global_decl = go_early_global_decl; | 
 |   go_debug_hooks.late_global_decl = go_late_global_decl; | 
 |   go_debug_hooks.type_decl = go_type_decl; | 
 |  | 
 |   macro_hash = htab_create (100, macro_hash_hashval, macro_hash_eq, | 
 | 			    macro_hash_del); | 
 |  | 
 |   return &go_debug_hooks; | 
 | } | 
 |  | 
 | #include "gt-godump.h" |