public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc(refs/users/ibuclaw/heads/dumpspec)] d: Initial draft of -fdump-d-spec=
@ 2020-12-10 16:45 Iain Buclaw
  0 siblings, 0 replies; 2+ messages in thread
From: Iain Buclaw @ 2020-12-10 16:45 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:e492fbff07b4ddf9bf9f0296032c3c1bf1e0efad

commit e492fbff07b4ddf9bf9f0296032c3c1bf1e0efad
Author: Iain Buclaw <ibuclaw@gdcproject.org>
Date:   Mon Apr 29 06:30:43 2019 +0200

    d: Initial draft of -fdump-d-spec=
    
    For any input files in any language, generate corresponding bindings in
    the D programming language.  This generates variable, function, struct
    and enum declarations which may be a useful way to start writing a D
    interface to code written in some other language.

Diff:
---
 gcc/Makefile.in      |    2 +
 gcc/c-family/c-lex.c |    2 +-
 gcc/common.opt       |    4 +
 gcc/d-dump-spec.cc   | 1904 ++++++++++++++++++++++++++++++++++++++++++++++++++
 gcc/debug.h          |    5 +
 gcc/passes.c         |    2 +-
 gcc/toplev.c         |   10 +-
 7 files changed, 1924 insertions(+), 5 deletions(-)

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 16be66fefc6..9333469be44 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1316,6 +1316,7 @@ OBJS = \
 	cprop.o \
 	cse.o \
 	cselib.o \
+	d-dump-spec.o \
 	data-streamer.o \
 	data-streamer-in.o \
 	data-streamer-out.o \
@@ -2678,6 +2679,7 @@ GTFILES = $(CPPLIB_H) $(srcdir)/input.h $(srcdir)/coretypes.h \
   $(srcdir)/internal-fn.h \
   $(srcdir)/calls.c \
   $(srcdir)/omp-general.h \
+  $(srcdir)/d-dump-spec.cc \
   @all_gtfiles@
 
 # Compute the list of GT header files from the corresponding C sources,
diff --git a/gcc/c-family/c-lex.c b/gcc/c-family/c-lex.c
index c8d33d0c9d1..93c4354ab41 100644
--- a/gcc/c-family/c-lex.c
+++ b/gcc/c-family/c-lex.c
@@ -89,7 +89,7 @@ init_c_lex (void)
   if ((debug_info_level == DINFO_LEVEL_VERBOSE
        && (write_symbols == DWARF2_DEBUG
 	   || write_symbols == VMS_AND_DWARF2_DEBUG))
-      || flag_dump_go_spec != NULL)
+      || flag_dump_go_spec != NULL || flag_dump_d_spec != NULL)
     {
       cb->define = cb_define;
       cb->undef = cb_undef;
diff --git a/gcc/common.opt b/gcc/common.opt
index 6645539f5e5..eabda5b73ce 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1426,6 +1426,10 @@ fdump-
 Common Joined RejectNegative Var(common_deferred_options) Defer
 -fdump-<type>	Dump various compiler internals to a file.
 
+fdump-d-spec=
+Common RejectNegative Joined Var(flag_dump_d_spec)
+-fdump-d-spec=filename	Write all declarations to file as D code.
+
 fdump-final-insns
 Driver RejectNegative
 
diff --git a/gcc/d-dump-spec.cc b/gcc/d-dump-spec.cc
new file mode 100644
index 00000000000..5c01d5d08d8
--- /dev/null
+++ b/gcc/d-dump-spec.cc
@@ -0,0 +1,1904 @@
+/* Output D language descriptions of types.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+
+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 D language
+   descriptions of declarations from C header files.  It uses the
+   debug info hooks to emit the descriptions.  The D language
+   descriptions then become part of the D runtime support library.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "diagnostic-core.h"
+#include "debug.h"
+#include "stor-layout.h"
+#include "fold-const.h"
+
+namespace {
+
+/* 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_d_spec_init.  */
+
+struct gcc_debug_hooks d_debug_hooks;
+
+/* The real debug hooks.  */
+
+const struct gcc_debug_hooks *real_debug_hooks;
+
+/* The file where we should write information.  */
+
+FILE *d_dump_file;
+
+/* A queue of decls to output.  */
+
+static GTY(()) vec<tree, va_gc> *queue;
+
+/* A hash table of macros we have seen.  */
+
+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;
+};
+
+/* A container for the data we pass around when generating information
+   at the end of the compilation.  */
+
+struct ddump_container
+{
+  /* 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 *> pot_dummy_types;
+
+  /* D 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;
+};
+
+/* Calculate the hash value for an entry in the macro hash table.  */
+
+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.  */
+
+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.  */
+
+void
+macro_hash_del (void *v)
+{
+  struct macro_hash_value *mhv = (struct macro_hash_value *) v;
+  XDELETEVEC (mhv->name);
+  XDELETEVEC (mhv->value);
+  XDELETE (mhv);
+}
+
+/* For the string hash tables.  */
+
+int
+string_hash_eq (const void *y1, const void *y2)
+{
+  return strcmp ((const char *) y1, (const char *) y2) == 0;
+}
+
+} // namespace
+
+namespace d_dump_spec {
+
+/* Specific flags for individual FORMAT_INFO bit values.  */
+
+enum dfi_mask
+{
+  /* If we can simply use a type name without needing to define it.  */
+  DFI_TYPE_NAME  = 1 << 0,
+  /* If we can output a function type.  */
+  DFI_FUNCTION   = 1 << 1,
+  /* If we can output reference types.  */
+  DFI_REFERENCE  = 1 << 2,
+  /* If we can output const qualifiers.  */
+  DFI_CONST_QUAL = 1 << 3,
+  /* If the type is an anonymous record or enum field.  */
+  DFI_ANON_FIELD = 1 << 4
+};
+
+/* Format information flags we pass around.  */
+
+struct format_info
+{
+  unsigned flags;
+  const char *anon_type_name;
+
+  /* Constructor.  */
+  explicit format_info (unsigned flags_)
+  : flags (flags_), anon_type_name (NULL)
+  { }
+
+  explicit format_info (unsigned flags_, const char *anon_type_name_)
+  : flags (flags_), anon_type_name (anon_type_name_)
+  { }
+
+  inline bool
+  use_type_name (void)
+  {
+    return (flags & DFI_TYPE_NAME) != 0;
+  }
+
+  inline bool
+  is_func_ok (void)
+  {
+    return (flags & DFI_FUNCTION) != 0;
+  }
+
+  inline bool
+  is_ref_ok (void)
+  {
+    return (flags & DFI_REFERENCE) != 0;
+  }
+
+  inline bool
+  is_const_ok (void)
+  {
+    return (flags & DFI_CONST_QUAL) != 0;
+  }
+
+  inline bool
+  is_anon_field_type (void)
+  {
+    return (flags & DFI_ANON_FIELD) != 0;
+  }
+};
+
+/* Prototypes for forward referenced functions */
+
+static bool format_type (struct ddump_container &, tree, format_info &);
+
+/* A macro definition.  */
+
+static void
+define (unsigned int lineno, const char *buffer)
+{
+  const char *p;
+
+  real_debug_hooks->define (lineno, buffer);
+
+  /* Skip macro functions.  */
+  for (p = buffer; *p != '\0' && *p != ' '; ++p)
+    {
+      if (*p == '(')
+	return;
+    }
+
+  if (*p == '\0')
+    return;
+
+  const char *name_end = p;
+  ++p;
+
+  if (*p == '\0')
+    return;
+
+  char *copy = XNEWVEC (char, name_end - buffer + 1);
+  memcpy (copy, buffer, name_end - buffer);
+  copy[name_end - buffer] = '\0';
+
+  struct macro_hash_value *mhval = XNEW (struct macro_hash_value);
+  mhval->name = copy;
+  mhval->value = NULL;
+
+  hashval_t hashval = htab_hash_string (copy);
+  void **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.  */
+  size_t out_len = strlen (p) * 2 + 1;
+  char *out_buffer = XNEWVEC (char, out_len);
+  char *q = out_buffer;
+  bool saw_operand = false;
+  bool need_operand = false;
+  bool saw_long_suffix = 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-d-spec so we
+	       don't worry about them.  */
+	    if (saw_operand)
+	      goto Lunknown;
+
+	    const char *start = p;
+	    while (ISALNUM (*p) || *p == '_')
+	      ++p;
+
+	    char *n = XALLOCAVEC (char, p - start + 1);
+	    memcpy (n, start, p - start);
+	    n[p - start] = '\0';
+
+	    struct macro_hash_value idval;
+	    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 Lunknown;
+	      }
+
+	    memcpy (q, start, p - start);
+	    q += p - start;
+	    saw_operand = true;
+	    need_operand = false;
+	  }
+	  break;
+
+	case '.':
+	  if (!ISDIGIT (p[1]))
+	    goto Lunknown;
+
+	  gcc_fallthrough ();
+
+	case '0': case '1': case '2': case '3': case '4':
+	case '5': case '6': case '7': case '8': case '9':
+	  {
+	    const char *start = p;
+	    int base = 10;
+
+	    /* Handle base-switching prefixes for hex and octal.  */
+	    if (*p == '0')
+	      {
+		switch (p[1])
+		  {
+		  case 'x':
+		  case 'X':
+		    p += 2;
+		    base = 16;
+		    break;
+
+		  case '0': case '1': case '2': case '3':
+		  case '4': case '5': case '6': case '7':
+		    base = 8;
+		    break;
+		  }
+	      }
+
+	    if (base != 8)
+	      {
+		while (ISDIGIT (*p) || *p == '.' || *p == 'e' || *p == 'E'
+		       || (base == 16 && ((*p >= 'a' && *p <= 'f')
+					  || (*p >= 'A' && *p <= 'F'))))
+		  ++p;
+
+		memcpy (q, start, p - start);
+		q += p - start;
+	      }
+	    else
+	      {
+		char buf[100];
+		HOST_WIDE_INT num = 0;
+
+		while (ISDIGIT (*p))
+		  {
+		    int i = *p - '0';
+
+		    if (i >= base)
+		      goto Lunknown;
+
+		    num *= base;
+		    num += i;
+		    ++p;
+		  }
+
+		int buf_len = snprintf (buf, sizeof (buf),
+					HOST_WIDE_INT_PRINT_HEX, num);
+		memcpy (q, buf, buf_len);
+		q += buf_len;
+	      }
+	    while (*p == 'u' || *p == 'U' || *p == 'l' || *p == 'L'
+		   || *p == 'f' || *p == 'F'
+		   || *p == 'd' || *p == 'D')
+	      {
+		/* D doesn't have decimal floats.  */
+		if (*p == 'd' || *p == 'D')
+		  goto Lunknown;
+
+		/* D doesn't recognize 'l' or 'LL' suffixes, so rewrite to
+		   ensure there's only ever one 'L'.  */
+		if (*p == 'l' || *p == 'L')
+		  {
+		    if (!saw_long_suffix)
+		      *q++ = 'L';
+
+		    saw_long_suffix = 1;
+		    ++p;
+		  }
+		else
+		  *q++ = *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 Lunknown;
+
+	  *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 Lunknown;
+
+	  *q++ = *p++;
+	  saw_operand = false;
+	  need_operand = true;
+	  break;
+
+	case '=':
+	  *q++ = *p++;
+
+	  if (*p != '=')
+	    goto Lunknown;
+
+	  /* Must be a binary operator.  */
+	  if (!saw_operand)
+	    goto Lunknown;
+
+	  *q++ = *p++;
+	  saw_operand = false;
+	  need_operand = true;
+	  break;
+
+	case '!':
+	  *q++ = *p++;
+
+	  if (*p == '=')
+	    {
+	      /* Must be a binary operator.  */
+	      if (!saw_operand)
+		goto Lunknown;
+
+	      *q++ = *p++;
+	      saw_operand = false;
+	      need_operand = true;
+	    }
+	  else
+	    {
+	      /* Must be a unary operator.  */
+	      if (saw_operand)
+		goto Lunknown;
+
+	      need_operand = true;
+	    }
+	  break;
+
+	case '<': case '>':
+	  /* Must be a binary operand, may be << or >> or <= or >=.  */
+	  if (!saw_operand)
+	    goto Lunknown;
+
+	  *q++ = *p++;
+
+	  if (*p == *(p - 1) || *p == '=')
+	    *q++ = *p++;
+
+	  saw_operand = false;
+	  need_operand = true;
+	  break;
+
+	case '~':
+	  /* Must be a unary operand.  */
+	  if (saw_operand)
+	    goto Lunknown;
+
+	  *q++ = *p++;
+	  need_operand = true;
+	  break;
+
+	case '"':
+	case '\'':
+	  {
+	    if (saw_operand)
+	      goto Lunknown;
+
+	    char quote = *p;
+	    *q++ = *p++;
+	    int count = 0;
+
+	    while (*p != quote)
+	      {
+		if (*p == '\0')
+		  goto Lunknown;
+
+		++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':
+		    {
+		      int c = 0;
+
+		      while (*p >= '0' && *p <= '7')
+			{
+			  *q++ = *p++;
+			  ++c;
+			}
+
+		      /* D octal characters are always 3 digits.  */
+		      if (c != 3)
+			goto Lunknown;
+		    }
+		    break;
+
+		  case 'x':
+		    {
+		      int c = 0;
+		      *q++ = *p++;
+
+		      while (ISXDIGIT (*p))
+			{
+			  *q++ = *p++;
+			  ++c;
+			}
+
+		      /* D hex characters are always 2 digits.  */
+		      if (c != 2)
+			goto Lunknown;
+		    }
+		    break;
+
+		  case 'a': case 'b': case 'f': case 'n': case 'r':
+		  case 't': case 'v': case '\\': case '\'': case '"':
+		    *q++ = *p++;
+		    break;
+
+		  default:
+		    goto Lunknown;
+		  }
+	      }
+
+	    *q++ = *p++;
+
+	    if (quote == '\'' && count != 1)
+	      goto Lunknown;
+
+	    saw_operand = true;
+	    need_operand = false;
+	    break;
+	  }
+
+	default:
+	  goto Lunknown;
+	}
+    }
+
+  if (need_operand)
+    goto Lunknown;
+
+  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;
+
+Lunknown:
+  fprintf (d_dump_file, "// unknown define %s\n", buffer);
+
+  if (slot != NULL)
+    htab_clear_slot (macro_hash, slot);
+
+  XDELETEVEC (out_buffer);
+  XDELETEVEC (copy);
+}
+
+/* A macro undef.  */
+
+static void
+undef (unsigned int lineno, const char *buffer)
+{
+  real_debug_hooks->undef (lineno, buffer);
+
+  struct macro_hash_value mhval;
+  mhval.name = CONST_CAST (char *, buffer);
+  mhval.value = NULL;
+
+  void **slot = htab_find_slot (macro_hash, &mhval, NO_INSERT);
+  if (slot != NULL)
+    htab_clear_slot (macro_hash, slot);
+}
+
+/* Add a function or variable DECL to the QUEUE vector.  */
+
+static void
+enqueue_decl (tree decl)
+{
+  if (!TREE_PUBLIC (decl)
+      || DECL_IS_BUILTIN (decl)
+      || DECL_NAME (decl) == NULL_TREE)
+    return;
+
+  vec_safe_push (queue, decl);
+}
+
+/* A function decl.  */
+
+static void
+function_decl (tree decl)
+{
+  real_debug_hooks->function_decl (decl);
+  enqueue_decl (decl);
+}
+
+/* A global variable decl.  */
+
+static void
+early_global_decl (tree decl)
+{
+  enqueue_decl (decl);
+  if (TREE_CODE (decl) != FUNCTION_DECL || DECL_STRUCT_FUNCTION (decl) != NULL)
+    real_debug_hooks->early_global_decl (decl);
+}
+
+static void
+late_global_decl (tree decl)
+{
+  real_debug_hooks->late_global_decl (decl);
+}
+
+/* A type declaration.  */
+
+static void
+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 (queue, decl);
+}
+
+/* Append an IDENTIFIER_NODE to OB.  */
+
+static void
+append_string (struct obstack *ob, tree id)
+{
+  obstack_grow (ob, IDENTIFIER_POINTER (id), IDENTIFIER_LENGTH (id));
+}
+
+/* Append an artificial variable name with the suffix _INDEX to OB.
+   Returns INDEX + 1.  */
+
+static unsigned int
+append_artificial_bitfield (struct obstack *ob, unsigned int index)
+{
+  char buf[100];
+
+  /* Identifier may not be unique.  */
+  obstack_grow (ob, "__bitfield_padding", 18);
+  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
+append_decl_name (struct obstack *ob, tree decl, htab_t keyword_hash)
+{
+  append_string (ob, DECL_NAME (decl));
+  /* Add underscore after variable name if a keyword.  */
+  const char *var_name = IDENTIFIER_POINTER (DECL_NAME (decl));
+  if (htab_find_slot (keyword_hash, var_name, NO_INSERT) != NULL)
+    obstack_1grow (ob, '_');
+}
+
+/* Returns true if TYPE corresponds to a D tagged type.  */
+
+static bool
+is_tagged_type (const_tree type)
+{
+  enum tree_code code = TREE_CODE (type);
+
+  return TYPE_IDENTIFIER (type)
+    && (code == RECORD_TYPE || code == UNION_TYPE
+	|| code == QUAL_UNION_TYPE || code == ENUMERAL_TYPE);
+}
+
+/* Write the D version of integer TYPE to TYPE_OBSTACK.  Return true if the type
+   can be represented in D, false otherwise.  */
+
+static bool
+format_integer_type (struct obstack *type_obstack, tree type)
+{
+  bool ret = true;
+  const char *s;
+  char buf[100];
+
+  switch (int_size_in_bytes (type))
+    {
+    case 1:
+      s = TREE_CODE (type) == INTEGER_TYPE && TYPE_STRING_FLAG (type) ? "char"
+	: TYPE_UNSIGNED (type) ? "ubyte" : "byte";
+      break;
+
+    case 2:
+      s = TREE_CODE (type) == INTEGER_TYPE && TYPE_STRING_FLAG (type) ? "wchar"
+	: TYPE_UNSIGNED (type) ? "ushort" : "short";
+      break;
+
+    case 4:
+      s = TREE_CODE (type) == INTEGER_TYPE && TYPE_STRING_FLAG (type) ? "dchar"
+	: TYPE_UNSIGNED (type) ? "uint" : "int";
+      break;
+
+    case 8:
+      s = TYPE_UNSIGNED (type) ? "ulong" : "long";
+      break;
+
+    default:
+      snprintf (buf, sizeof (buf), "INVALID-int-%u%s",
+		TYPE_PRECISION (type),
+		TYPE_UNSIGNED (type) ? "u" : "");
+      s = buf;
+      ret = false;
+      break;
+    }
+
+  obstack_grow (type_obstack, s, strlen (s));
+  return ret;
+}
+
+/* Write the D version of real TYPE to TYPE_OBSTACK.  Return true if the type
+   can be represented in D, false otherwise.  */
+
+static bool
+format_real_type (struct obstack *type_obstack, tree type)
+{
+  bool ret = true;
+  const char *s;
+  char buf[100];
+
+  switch (TYPE_PRECISION (type))
+    {
+    case 32:
+      s = "float";
+      break;
+
+    case 64:
+      s = "double";
+      break;
+
+    default:
+      if (TYPE_PRECISION (type) == LONG_DOUBLE_TYPE_SIZE)
+	s = "real";
+      else
+	{
+	  snprintf (buf, sizeof (buf), "INVALID-float-%u",
+		    TYPE_PRECISION (type));
+	  s = buf;
+	  ret = false;
+	}
+      break;
+    }
+
+  obstack_grow (type_obstack, s, strlen (s));
+  return ret;
+}
+
+/* Write the D version of complex TYPE to TYPE_OBSTACK.  Return true if the type
+   can be represented in D, false otherwise.  */
+
+static bool
+format_complex_type (struct obstack *type_obstack, tree type)
+{
+  bool ret = true;
+  const char *s;
+  char buf[100];
+  tree real_type = TREE_TYPE (type);
+
+  if (TREE_CODE (real_type) == REAL_TYPE)
+    {
+      switch (TYPE_PRECISION (real_type))
+	{
+	case 32:
+	  s = "cfloat";
+	  break;
+
+	case 64:
+	  s = "cdouble";
+	  break;
+
+	default:
+	  if (TYPE_PRECISION (real_type) == LONG_DOUBLE_TYPE_SIZE)
+	    s = "creal";
+	  else
+	    {
+	      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 (type_obstack, s, strlen (s));
+  return ret;
+}
+
+/* Write the D version of pointer TYPE to CONTAINER.TYPE_OBSTACK using INFO to
+   control how the type is formatted.  Return true if the type can be
+   represented in D, false otherwise.  */
+
+static bool
+format_pointer_type (struct ddump_container &container, tree type,
+		     format_info &info)
+{
+  tree basetype = TREE_TYPE (type);
+  bool is_pointer_const = TYPE_READONLY (type);
+  bool is_base_const = TYPE_READONLY (basetype);
+  struct obstack *ob = &container.type_obstack;
+  bool ret = true;
+  format_info basetype_info (info.flags & DFI_ANON_FIELD, info.anon_type_name);
+
+  /* Don't force the TYPE_NAME if base type is anonymous.  */
+  if (!info.is_anon_field_type ())
+    basetype_info.flags |= DFI_TYPE_NAME;
+
+  /* Allow FUNCTION_TYPE types to be emitted.  */
+  if (TREE_CODE (basetype) == FUNCTION_TYPE)
+    basetype_info.flags |= DFI_FUNCTION;
+
+  /* Emit const qualifier if haven't already done so.  */
+  if (info.is_const_ok ())
+    {
+      if (is_pointer_const)
+	obstack_grow (ob, "const ", 6);
+      else if (is_base_const)
+	obstack_grow (ob, "const(", 6);
+      else
+	basetype_info.flags |= DFI_CONST_QUAL;
+    }
+
+  if (!format_type (container, basetype, basetype_info))
+    ret = false;
+
+  if (info.is_const_ok () && !is_pointer_const && is_base_const)
+    obstack_1grow (ob, ')');
+
+  if (!basetype_info.is_func_ok ())
+    obstack_1grow (ob, '*');
+
+  /* The pointer here can be used without the struct or union
+     definition.  So this struct or union is a potential dummy
+     type.  */
+  if (is_tagged_type (basetype))
+    {
+      const char *ident = IDENTIFIER_POINTER (TYPE_IDENTIFIER (basetype));
+      container.pot_dummy_types.add (ident);
+    }
+
+  return ret;
+}
+
+/* Write the D version of reference TYPE to CONTAINER.TYPE_OBSTACK using INFO
+   to control how the type is formatted.  Return true if the type can be
+   represented in D, false otherwise.  */
+
+static bool
+format_reference_type (struct ddump_container &container, tree type,
+		       format_info &info)
+{
+  struct obstack *ob = &container.type_obstack;
+  bool ret = true;
+
+  /* The ref keyword is only valid in the context of function return or
+     parameter types.  */
+  if (!info.is_ref_ok ())
+    ret = false;
+
+  obstack_grow (ob, "ref ", 4);
+
+  /* Strip 'ref' from the info context.  */
+  unsigned basetype_info_mask = (DFI_TYPE_NAME | (info.flags & DFI_CONST_QUAL));
+  format_info basetype_info (basetype_info_mask);
+
+  if (!format_type (container, TREE_TYPE (type), basetype_info))
+    ret = false;
+
+  return ret;
+}
+
+/* Write the D version of array TYPE to CONTAINER.TYPE_OBSTACK using INFO to
+   control how the type is formatted.  Return true if the type can be
+   represented in D, false otherwise.  */
+
+static bool
+format_array_type (struct ddump_container &container, tree type,
+		   format_info &info)
+{
+  struct obstack *ob = &container.type_obstack;
+  bool ret = true;
+  format_info basetype_info (info.flags & (DFI_CONST_QUAL | DFI_ANON_FIELD),
+			     info.anon_type_name);
+
+  /* Don't force the TYPE_NAME if base type is anonymous.  */
+  if (!info.is_anon_field_type ())
+    basetype_info.flags |= DFI_TYPE_NAME;
+
+  if (!format_type (container, TREE_TYPE (type), basetype_info))
+    ret = false;
+
+  obstack_1grow (ob, '[');
+
+  if (TYPE_DOMAIN (type) != NULL_TREE
+      && TYPE_MAX_VALUE (TYPE_DOMAIN (type)) != NULL_TREE)
+    {
+      tree nelts = fold_build2 (PLUS_EXPR, sizetype,
+				array_type_nelts (type),
+				size_one_node);
+      char buf[100];
+
+      snprintf (buf, sizeof (buf), HOST_WIDE_INT_PRINT_DEC,
+		tree_to_shwi (nelts));
+      obstack_grow (ob, buf, strlen (buf));
+    }
+  else
+    obstack_1grow (ob, '0');
+
+  obstack_1grow (ob, ']');
+  return ret;
+}
+
+/* Write the D version of vector TYPE to CONTAINER.TYPE_OBSTACK using INFO to
+   control how the type is formatted.  Return true if the type can be
+   represented in D, false otherwise.  */
+
+static bool
+format_vector_type (struct ddump_container &container, tree type,
+		    format_info &info)
+{
+  struct obstack *ob = &container.type_obstack;
+  bool ret = true;
+  unsigned basetype_info_mask = (DFI_TYPE_NAME | (info.flags & DFI_CONST_QUAL));
+  format_info basetype_info (basetype_info_mask);
+
+  obstack_grow (ob, "__vector(", 9);
+  if (!format_type (container, TREE_TYPE (type), basetype_info))
+    ret = false;
+
+  obstack_1grow (ob, '[');
+
+  unsigned HOST_WIDE_INT nunits;
+  if (TYPE_VECTOR_SUBPARTS (type).is_constant (&nunits))
+    {
+      char buf[100];
+
+      snprintf (buf, sizeof (buf), HOST_WIDE_INT_PRINT_DEC, nunits);
+      obstack_grow (ob, buf, strlen (buf));
+    }
+  else
+    ret = false;
+
+  obstack_grow (ob, "])", 2);
+  return ret;
+}
+
+/* Write the D version of enumeral TYPE to CONTAINER.TYPE_OBSTACK using INFO to
+   control how the type is formatted.  Return true if the type can be
+   represented in D, false otherwise.  */
+
+static bool
+format_enumeral_type (struct ddump_container &container, tree type,
+		      format_info &info)
+{
+  struct obstack *ob = &container.type_obstack;
+  bool ret = true;
+
+  if (TYPE_IDENTIFIER (type))
+    container.decls_seen.add (TYPE_IDENTIFIER (type));
+
+  /* Don't emit the body of an enum in a context where it isn't a valid D
+     declaration.  */
+  if (info.use_type_name ())
+    {
+      if (TYPE_IDENTIFIER (type))
+	append_string (ob, TYPE_IDENTIFIER (type));
+      else
+	ret = false;
+
+      return ret;
+    }
+  else if (info.is_anon_field_type ())
+    {
+      if (info.anon_type_name != NULL)
+	ret = false;
+
+      if (!format_integer_type (ob, type))
+	ret = false;
+
+      return ret;
+    }
+
+  obstack_grow (ob, "enum", 4);
+
+  if (TYPE_IDENTIFIER (type) && !IDENTIFIER_ANON_P (TYPE_IDENTIFIER (type)))
+    {
+      obstack_1grow (ob, ' ');
+      append_string (ob, TYPE_IDENTIFIER (type));
+    }
+
+  if (TREE_TYPE (type))
+    {
+      format_info basetype_info (DFI_TYPE_NAME | DFI_CONST_QUAL);
+
+      obstack_grow (ob, " : ", 3);
+      if (!format_type (container, TREE_TYPE (type), basetype_info))
+	ret = false;
+    }
+
+  obstack_grow (ob, " { ", 3);
+
+  for (tree element = TYPE_VALUES (type);
+       element != NULL_TREE;
+       element = TREE_CHAIN (element))
+    {
+      char buf[WIDE_INT_PRINT_BUFFER_SIZE];
+      const char *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
+	 removing the macro.  */
+      struct macro_hash_value mhval;
+      mhval.name = CONST_CAST (char *, name);
+      mhval.value = NULL;
+
+      void **slot = htab_find_slot (macro_hash, &mhval, NO_INSERT);
+      if (slot != NULL)
+	htab_clear_slot (macro_hash, slot);
+
+      obstack_grow (ob, name, strlen (name));
+      obstack_grow (ob, " = ", 3);
+
+      tree value = TREE_VALUE (element);
+      if (TREE_CODE (value) == CONST_DECL)
+	value = DECL_INITIAL (value);
+
+      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
+	print_hex (wi::to_wide (value), buf);
+
+      obstack_grow (ob, buf, strlen (buf));
+      if (TREE_CHAIN (element))
+	obstack_1grow (ob, ',');
+
+      obstack_1grow (ob, ' ');
+    }
+
+  obstack_1grow (ob, '}');
+  return ret;
+}
+
+/* Write the D version of struct or union TYPE to CONTAINER.TYPE_OBSTACK using
+   INFO to control how the type is formatted.  P_ART_I is used for indexing
+   artifical elements in nested structures and should always be a NULL pointer
+   when called, except by recursive calls from format_record_type() itself.
+   Return true if the type can be represented in D, false otherwise.  */
+
+static bool
+format_record_type (struct ddump_container &container, tree type,
+		    format_info &info, unsigned *p_art_i)
+{
+  struct obstack *ob = &container.type_obstack;
+  bool ret = true;
+  unsigned int art_i_dummy;
+
+  if (p_art_i == NULL)
+    {
+      art_i_dummy = 0;
+      p_art_i = &art_i_dummy;
+    }
+
+  /* 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);
+
+  if (TYPE_IDENTIFIER (type))
+    container.decls_seen.add (TYPE_IDENTIFIER (type));
+
+  /* Don't emit the body of a struct/union in a context where it
+     isn't a valid D declaration.  */
+  if (info.use_type_name ())
+    {
+      if (TYPE_IDENTIFIER (type))
+	append_string (ob, TYPE_IDENTIFIER (type));
+      else
+	ret = false;
+
+      return ret;
+    }
+
+  const char *record_type
+    = (TREE_CODE (type) == UNION_TYPE) ? "union" : "struct";
+  obstack_grow (ob, record_type, strlen (record_type));
+  if (TYPE_IDENTIFIER (type))
+    {
+      obstack_1grow (ob, ' ');
+      append_string (ob, TYPE_IDENTIFIER (type));
+    }
+  else if (info.is_anon_field_type () && info.anon_type_name)
+    {
+      obstack_1grow (ob, ' ');
+      obstack_grow (ob, info.anon_type_name, strlen (info.anon_type_name));
+    }
+
+  obstack_grow (ob, " { ", 3);
+
+  for (tree field = TYPE_FIELDS (type); field != NULL_TREE;
+       field = TREE_CHAIN (field))
+    {
+      if (TREE_CODE (field) != FIELD_DECL)
+	continue;
+
+      /* Bit fields are replaced by their representative field.  */
+      if (DECL_BIT_FIELD_TYPE (field))
+	{
+	  if (integer_zerop (DECL_SIZE (field)))
+	    continue;
+
+	  tree repr = DECL_BIT_FIELD_REPRESENTATIVE (field);
+
+	  while (TREE_CHAIN (field) != NULL_TREE
+		 && DECL_BIT_FIELD_REPRESENTATIVE (TREE_CHAIN (field)) == repr)
+	    field = TREE_CHAIN (field);
+
+	  format_info field_info (DFI_TYPE_NAME | DFI_CONST_QUAL);
+	  if (!format_type (container, TREE_TYPE (repr), field_info))
+	    ret = false;
+
+	  obstack_1grow (ob, ' ');
+	  *p_art_i = append_artificial_bitfield (ob, *p_art_i);
+	  obstack_grow (ob, "; ", 2);
+	  continue;
+	}
+
+      /* Emit the field.  */
+      tree field_type = TREE_TYPE (field);
+      tree base_field_type = field_type;
+      bool is_anon_field_type = false;
+
+      while (POINTER_TYPE_P (base_field_type)
+	     || TREE_CODE (base_field_type) == ARRAY_TYPE)
+	base_field_type = TREE_TYPE (base_field_type);
+
+      if ((AGGREGATE_TYPE_P (base_field_type)
+	   || TREE_CODE (base_field_type) == ENUMERAL_TYPE)
+	  && TYPE_IDENTIFIER (base_field_type) == NULL_TREE)
+	is_anon_field_type = true;
+
+      if (TYPE_USER_ALIGN (field_type))
+	{
+	  char buf[100];
+
+	  snprintf (buf, sizeof (buf), "align(%u) ",
+		    TYPE_ALIGN_UNIT (field_type));
+	  obstack_grow (ob, buf, strlen (buf));
+	}
+      else if (TYPE_PACKED (field_type))
+	obstack_grow (ob, "align(1) ", 9);
+
+      if (is_anon_field_type)
+	{
+	  const char *type_name = NULL;
+	  char buf[100];
+
+	  /* Give anonymous types a fake type name.  */
+	  if (RECORD_OR_UNION_TYPE_P (base_field_type)
+	      && DECL_NAME (field) != NULL_TREE)
+	    {
+	      snprintf (buf, sizeof (buf), "__anonymous_type_%u", *p_art_i);
+	      *p_art_i += 1;
+	      type_name = buf;
+	    }
+
+	  format_info field_info (DFI_CONST_QUAL | DFI_ANON_FIELD,
+				  type_name);
+
+	  if (RECORD_OR_UNION_TYPE_P (field_type))
+	    {
+	      if (!format_record_type (container, field_type, field_info,
+				       p_art_i))
+		ret = false;
+	    }
+	  else if (INTEGRAL_TYPE_P (field_type)
+		   || POINTER_TYPE_P (field_type)
+		   || TREE_CODE (field_type) == ARRAY_TYPE)
+	    {
+	      if (DECL_NAME (field) == NULL_TREE)
+		ret = false;
+
+	      if (!format_type (container, field_type, field_info))
+		ret = false;
+	    }
+	  else
+	    ret = false;
+	}
+      else
+	{
+	  format_info field_info (DFI_TYPE_NAME | DFI_CONST_QUAL);
+
+	  if (!format_type (container, field_type, field_info))
+	    ret = false;
+	}
+
+      /* Emit the field name, but not for anonymous records and
+	 unions.  */
+      if (!is_anon_field_type || DECL_NAME (field) != NULL_TREE)
+	{
+	  obstack_1grow (ob, ' ');
+	  append_decl_name (ob, field, container.keyword_hash);
+	  obstack_grow (ob, "; ", 2);
+	}
+    }
+
+  obstack_1grow (ob, '}');
+
+  /* If an anonymous record or union type with a field name.  Put out the
+     artifically generated name now.  */
+  if (!TYPE_IDENTIFIER (type) && info.is_anon_field_type ())
+    {
+      obstack_1grow (ob, ' ');
+      if (info.anon_type_name != NULL)
+	obstack_grow (ob, info.anon_type_name, strlen (info.anon_type_name));
+    }
+
+  return ret;
+}
+
+/* Write the TYPE_ARG_TYPES of the function TYPE to CONTAINER.TYPE_OBSTACK.
+   Return true if all arguments can be represented in D, false otherwise.  */
+
+static bool
+format_function_args (struct ddump_container &container, tree type)
+{
+  bool ret = true;
+  struct obstack *ob = &container.type_obstack;
+  bool seen_arg = false;
+  format_info info (DFI_TYPE_NAME | DFI_REFERENCE | DFI_CONST_QUAL);
+  tree arg_type;
+  function_args_iterator iter;
+
+  obstack_1grow (ob, '(');
+
+  FOREACH_FUNCTION_ARGS (type, arg_type, iter)
+    {
+      if (VOID_TYPE_P (arg_type))
+	break;
+
+      if (seen_arg)
+	obstack_grow (ob, ", ", 2);
+
+      if (!format_type (container, arg_type, info))
+	ret = false;
+
+      seen_arg = true;
+    }
+
+  if (stdarg_p (type))
+    {
+      if (prototype_p (type))
+	obstack_grow (ob, ", ", 2);
+
+      obstack_grow (ob, "...", 3);
+    }
+
+  obstack_1grow (ob, ')');
+  return ret;
+}
+
+/* Write the D version of function TYPE to CONTAINER.TYPE_OBSTACK using INFO to
+   control how the type is formatted.  Return true if the type can be
+   represented in D, false otherwise.  */
+
+static bool
+format_function_type (struct ddump_container &container, tree type,
+		      format_info &info)
+{
+  struct obstack *ob = &container.type_obstack;
+  bool ret = true;
+
+  /* D has no way to write a type which is a function but not a
+     pointer to a function.  */
+  if (!info.is_func_ok ())
+    ret = false;
+
+  unsigned return_info_mask
+    = (DFI_TYPE_NAME | DFI_REFERENCE | (info.flags & DFI_CONST_QUAL));
+  format_info return_info (return_info_mask);
+
+  if (!format_type (container, TREE_TYPE (type), return_info))
+    ret = false;
+
+  obstack_1grow (ob, ' ');
+  obstack_grow (ob, "function", 8);
+
+  if (!format_function_args (container, type))
+    ret = false;
+
+  return ret;
+}
+
+/* Write the D version of TYPE to CONTAINER.TYPE_OBSTACK using INFO to control
+   how the type is formatted.  P_ART_I is used for indexing artifical elements
+   in nested structures and should always be a NULL pointer when called, except
+   by certain recursive calls from format_type() itself.
+   IS_ANON_RECORD_OR_UNION is true the type is an anonymous field type.
+   Return true if the type can be represented in D, false otherwise.  */
+
+static bool
+format_type (struct ddump_container &container, tree type, format_info &info)
+{
+  bool ret = true;
+  struct obstack *ob = &container.type_obstack;
+
+  /* Shortcut formatting the type if TYPE_NAME is both and set and requested.
+     If the type was used in a typedef, it will use the alias instead of
+     writing out the base type.  */
+  if (info.use_type_name ()
+      && TYPE_NAME (type) != NULL_TREE
+      && (TREE_CODE (TYPE_NAME (type)) != TYPE_DECL
+	  || !DECL_IS_BUILTIN (TYPE_NAME (type)))
+      && (container.decls_seen.contains (type)
+	  || container.decls_seen.contains (TYPE_NAME (type)))
+      )
+    {
+      tree name = TYPE_IDENTIFIER (type);
+
+      if (htab_find_slot (container.invalid_hash, IDENTIFIER_POINTER (name),
+			  NO_INSERT) != NULL)
+	ret = false;
+
+      append_string (ob, name);
+      return ret;
+    }
+
+  container.decls_seen.add (type);
+
+  switch (TREE_CODE (type))
+    {
+    case INTEGER_TYPE:
+      ret = format_integer_type (ob, type);
+      break;
+
+    case REAL_TYPE:
+      ret = format_real_type (ob, type);
+      break;
+
+    case COMPLEX_TYPE:
+      ret = format_complex_type (ob, type);
+      break;
+
+    case BOOLEAN_TYPE:
+      obstack_grow (ob, "bool", 4);
+      break;
+
+    case VOID_TYPE:
+      obstack_grow (ob, "void", 4);
+      break;
+
+    case POINTER_TYPE:
+      ret = format_pointer_type (container, type, info);
+      break;
+
+    case REFERENCE_TYPE:
+      ret = format_reference_type (container, type, info);
+      break;
+
+    case ARRAY_TYPE:
+      ret = format_array_type (container, type, info);
+      break;
+
+    case VECTOR_TYPE:
+      ret = format_vector_type (container, type, info);
+      break;
+
+    case ENUMERAL_TYPE:
+      ret = format_enumeral_type (container, type, info);
+      break;
+
+    case RECORD_TYPE:
+    case UNION_TYPE:
+      ret = format_record_type (container, type, info, NULL);
+      break;
+
+    case FUNCTION_TYPE:
+      ret = format_function_type (container, type, info);
+      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
+output_type (struct ddump_container &container)
+{
+  struct obstack *ob = &container.type_obstack;
+  obstack_1grow (ob, '\0');
+  fputs ((char *) obstack_base (ob), d_dump_file);
+  obstack_free (ob, obstack_base (ob));
+}
+
+/* Output a function declaration.  */
+
+static void
+output_fndecl (struct ddump_container &container, tree decl)
+{
+  struct obstack *ob = &container.type_obstack;
+  bool is_valid = true;
+  char buf[100];
+
+  tree decl_name = DECL_NAME (decl);
+  tree decl_asm_name = DECL_ASSEMBLER_NAME (decl);
+
+  if (decl_asm_name != decl_name
+      || htab_find_slot (container.keyword_hash,
+			 IDENTIFIER_POINTER (decl_name), NO_INSERT))
+    {
+      const char *asm_name = IDENTIFIER_POINTER (decl_asm_name);
+      if (*asm_name == '*')
+	asm_name++;
+
+      snprintf (buf, sizeof (buf), "pragma(mangle, \"%s\") ", asm_name);
+      obstack_grow (ob, buf, strlen (buf));
+    }
+
+  format_info info (DFI_TYPE_NAME | DFI_REFERENCE | DFI_CONST_QUAL);
+  if (!format_type (container, TREE_TYPE (TREE_TYPE (decl)), info))
+    is_valid = false;
+
+  obstack_1grow (ob, ' ');
+  append_decl_name (ob, decl, container.keyword_hash);
+
+  if (!format_function_args (container, TREE_TYPE (decl)))
+    is_valid = false;
+
+  if (!is_valid)
+    fprintf (d_dump_file, "// ");
+
+  output_type (container);
+  fprintf (d_dump_file, ";\n");
+}
+
+/* Returns true if DECL is an implicitly generated TYPE_DECL for a type.
+   These do not require an alias to be declared in D.  */
+
+static bool
+is_implicit_typedef (const_tree decl)
+{
+  if (DECL_NAME (decl) == NULL_TREE)
+    return true;
+
+  if (DECL_ARTIFICIAL (decl)
+      && TYPE_STUB_DECL (TREE_TYPE (decl)) == decl)
+    return true;
+
+  return false;
+}
+
+/* Output a typedef or something like a struct definition.  */
+
+static void
+output_typedef (struct ddump_container &container, tree decl)
+{
+  if (!is_implicit_typedef (decl))
+    {
+      const char *ident = IDENTIFIER_POINTER (DECL_NAME (decl));
+
+      /* If type is a keyword, skip.  */
+      if (htab_find_slot (container.keyword_hash, ident, NO_INSERT) != NULL)
+	return;
+
+      /* If type defined already, skip.  */
+      void **slot = htab_find_slot (container.type_hash, ident, INSERT);
+      if (*slot != NULL)
+	return;
+
+      *slot = CONST_CAST (void *, (const void *) ident);
+
+      tree type = TREE_TYPE (decl);
+      format_info info (DFI_CONST_QUAL);
+
+      if (is_tagged_type (type))
+	{
+	  /* If this is a plain typedef, and not a typedef struct, then only get
+	     the type name for the alias declaration.  */
+	  if (TYPE_NAME (type) == decl
+	      && DECL_ORIGINAL_TYPE (decl) != NULL_TREE
+	      && TYPE_NAME (DECL_ORIGINAL_TYPE (decl)) != NULL_TREE)
+	    {
+	      type = DECL_ORIGINAL_TYPE (decl);
+	      info.flags |= DFI_TYPE_NAME;
+
+	      /* The typedef can be to an opaque struct or union, so is a
+		 potential dummy type.  */
+	      const char *ident = IDENTIFIER_POINTER (TYPE_IDENTIFIER (type));
+	      container.pot_dummy_types.add (ident);
+	    }
+	}
+      else
+	info.flags |= DFI_TYPE_NAME;
+
+      /* Allow FUNCTION_TYPE types to be emitted as typedefs.  */
+      if (TREE_CODE (type) == FUNCTION_TYPE)
+	info.flags |= DFI_FUNCTION;
+
+      if (!format_type (container, type, info))
+	{
+	  fprintf (d_dump_file, "// ");
+	  slot = htab_find_slot (container.invalid_hash, ident, INSERT);
+	  *slot = CONST_CAST (void *, (const void *) ident);
+	}
+
+      if (info.use_type_name ())
+	{
+	  fprintf (d_dump_file, "alias %s = ",
+		   IDENTIFIER_POINTER (DECL_NAME (decl)));
+	}
+
+      output_type (container);
+      container.decls_seen.add (decl);
+
+      if (info.use_type_name ())
+	fprintf (d_dump_file, ";");
+    }
+  else if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (decl)))
+    {
+      tree type = TREE_TYPE (decl);
+      const char *ident = IDENTIFIER_POINTER (TYPE_IDENTIFIER (type));
+
+       /* If type defined already, skip.  */
+       void **slot = htab_find_slot (container.type_hash, ident, INSERT);
+       if (*slot != NULL)
+	 return;
+
+       *slot = CONST_CAST (void *, (const void *) ident);
+
+       format_info info (DFI_CONST_QUAL);
+       if (!format_type (container, type, info))
+	 {
+	   fprintf (d_dump_file, "// ");
+	   slot = htab_find_slot (container.invalid_hash, ident, INSERT);
+	   *slot = CONST_CAST (void *, (const void *) ident);
+	 }
+
+       output_type (container);
+    }
+  else if (TREE_CODE (TREE_TYPE (decl)) == ENUMERAL_TYPE)
+    {
+      tree type = TREE_TYPE (decl);
+      bool is_anon_enum = (TYPE_IDENTIFIER (type) == NULL_TREE
+			   || IDENTIFIER_ANON_P (TYPE_IDENTIFIER (type)));
+      const char *ident = NULL;
+      void **slot;
+
+      if (!is_anon_enum)
+	{
+	  ident = IDENTIFIER_POINTER (TYPE_IDENTIFIER (type));
+
+	  /* If type defined already, skip.  */
+	  slot = htab_find_slot (container.type_hash, ident, INSERT);
+	  if (*slot != NULL)
+	    return;
+
+	  *slot = CONST_CAST (void *, (const void *) ident);
+	}
+
+      format_info info (DFI_CONST_QUAL);
+      if (!format_type (container, type, info))
+	{
+	  fprintf (d_dump_file, "// ");
+	  if (ident)
+	    {
+	      slot = htab_find_slot (container.invalid_hash, ident, INSERT);
+	      *slot = CONST_CAST (void *, (const void *) ident);
+	    }
+	}
+
+      output_type (container);
+    }
+  else
+    return;
+
+  fprintf (d_dump_file, "\n");
+}
+
+/* Output a variable.  */
+
+static void
+output_var (struct ddump_container &container, tree decl)
+{
+  bool is_valid;
+
+  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));
+
+  tree type_name = TYPE_NAME (TREE_TYPE (decl));
+  tree 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 = &container.type_obstack;
+
+      append_string (ob, id);
+      is_valid = htab_find_slot (container.type_hash, IDENTIFIER_POINTER (id),
+				 NO_INSERT) != NULL;
+    }
+  else
+    {
+      format_info info (DFI_TYPE_NAME | DFI_CONST_QUAL);
+      is_valid = format_type (container, TREE_TYPE (decl), info);
+    }
+
+  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 (d_dump_file, "// ");
+
+  fprintf (d_dump_file, "__gshared ");
+  output_type (container);
+  fprintf (d_dump_file, " %s;\n", IDENTIFIER_POINTER (DECL_NAME (decl)));
+
+  /* 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
+print_macro (void **slot, void *arg)
+{
+  struct macro_hash_value *mhval = (struct macro_hash_value *) *slot;
+  struct ddump_container *container = (struct ddump_container *) arg;
+
+  fprintf (d_dump_file, "enum ");
+
+  if (htab_find_slot (container->keyword_hash, mhval->name, NO_INSERT) != NULL)
+    fprintf (d_dump_file, "_");
+
+  fprintf (d_dump_file, "%s = %s;\n", mhval->name, mhval->value);
+
+  return 1;
+}
+
+/* Build a hash table with the D keywords.  */
+
+const char * const keywords[] = {
+  /* Basic types.  */
+  "void", "byte", "ubyte", "short", "ushort", "int", "uint", "long", "ulong",
+  "cent", "ucent", "float", "double", "real", "ifloat", "idouble", "ireal",
+  "cfloat", "cdouble", "creal", "char", "wchar", "dchar", "bool",
+  /* Aggregates.  */
+  "struct", "class", "interface", "union", "enum", "import", "alias",
+  "override", "delegate", "function", "mixin", "align", "extern", "private",
+  "protected", "public", "export", "static", "final", "const", "abstract",
+  "debug", "deprecated", "inout", "lazy", "auto", "package", "immutable",
+  /* Statements.  */
+  "if", "else", "while", "for", "do", "switch", "case", "default", "break",
+  "continue", "with", "synchronized", "return", "goto", "try", "catch",
+  "finally", "asm", "foreach", "foreach_reverse", "scope",
+  /* Contracts.  */
+  "invariant", "in", "out", "body",
+  /* Operators.  */
+  "is", "this", "super",
+  /* Testing.  */
+  "unittest",
+  /* Literals.  */
+  "__LINE__", "__FILE__", "__MODULE__", "__FUNCTION__", "__PRETTY_FUNCTION__",
+  "__DATE__", "__TIME__", "__TIMESTAMP__", "__VENDOR__", "__VERSION__",
+  "__EOF__",
+  /* Other.  */
+  "cast", "null", "assert", "true", "false", "throw", "new", "delete",
+  "version", "module", "template", "typeof", "pragma", "typeid",
+  "ref", "__traits", "pure", "nothrow", "__gshared", "shared",
+};
+
+static void
+keyword_hash_init (struct ddump_container &container)
+{
+  size_t count = sizeof (keywords) / sizeof (keywords[0]);
+
+  for (size_t i = 0; i < count; i++)
+    {
+      void **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.  */
+
+static bool
+find_dummy_types (const char *const &ptr, ddump_container *adata)
+{
+  struct ddump_container *data = (struct ddump_container *) adata;
+  const char *type = (const char *) ptr;
+
+  void **slot = htab_find_slot (data->type_hash, type, NO_INSERT);
+  void **islot = htab_find_slot (data->invalid_hash, type, NO_INSERT);
+  if (slot == NULL || islot != NULL)
+    fprintf (d_dump_file, "struct %s;\n", type);
+
+  return true;
+}
+
+/* Output symbols.  */
+
+static void
+finish (const char *filename)
+{
+  struct ddump_container container;
+  unsigned int ix;
+  tree decl;
+
+  real_debug_hooks->finish (filename);
+
+  container.type_hash = htab_create (100, htab_hash_string,
+				     string_hash_eq, NULL);
+  container.invalid_hash = htab_create (10, htab_hash_string,
+					string_hash_eq, NULL);
+  container.keyword_hash = htab_create (100, htab_hash_string,
+					string_hash_eq, 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:
+	  output_fndecl (container, decl);
+	  break;
+
+	case TYPE_DECL:
+	  output_typedef (container, decl);
+	  break;
+
+	case VAR_DECL:
+	  output_var (container, decl);
+	  break;
+
+	default:
+	  gcc_unreachable ();
+	}
+    }
+
+  htab_traverse_noresize (macro_hash, print_macro, &container);
+
+  /* To emit dummy definitions.  */
+  container.pot_dummy_types.traverse<ddump_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 (d_dump_file) != 0)
+    error ("could not close D dump file: %m");
+
+  d_dump_file = NULL;
+}
+
+} // namespace d_dump_spec
+
+/* Set up our hooks.  */
+
+const struct gcc_debug_hooks *
+dump_d_spec_init (const char *filename, const struct gcc_debug_hooks *hooks)
+{
+  d_dump_file = fopen (filename, "w");
+  if (d_dump_file == NULL)
+    {
+      error ("could not open D dump file %qs: %m", filename);
+      return hooks;
+    }
+
+  d_debug_hooks = *hooks;
+  real_debug_hooks = hooks;
+
+  d_debug_hooks.finish = d_dump_spec::finish;
+  d_debug_hooks.define = d_dump_spec::define;
+  d_debug_hooks.undef = d_dump_spec::undef;
+  d_debug_hooks.function_decl = d_dump_spec::function_decl;
+  d_debug_hooks.early_global_decl = d_dump_spec::early_global_decl;
+  d_debug_hooks.late_global_decl = d_dump_spec::late_global_decl;
+  d_debug_hooks.type_decl = d_dump_spec::type_decl;
+
+  macro_hash = htab_create (100, macro_hash_hashval, macro_hash_eq,
+			    macro_hash_del);
+
+  return &d_debug_hooks;
+}
+
+#include "gt-d-dump-spec.h"
diff --git a/gcc/debug.h b/gcc/debug.h
index 260325920ea..b1f50751405 100644
--- a/gcc/debug.h
+++ b/gcc/debug.h
@@ -256,6 +256,11 @@ extern bool dwarf2out_default_as_locview_support (void);
 extern const struct gcc_debug_hooks *
 dump_go_spec_init (const char *, const struct gcc_debug_hooks *);
 
+/* For -fdump-d-spec.  */
+
+extern const struct gcc_debug_hooks *
+dump_d_spec_init (const char *, const struct gcc_debug_hooks *);
+
 /* Instance discriminator mapping table.  See final.c.  */
 typedef hash_map<const_tree, int> decl_to_instance_map_t;
 extern decl_to_instance_map_t *decl_to_instance_map;
diff --git a/gcc/passes.c b/gcc/passes.c
index 973c958f769..cb5b67377d9 100644
--- a/gcc/passes.c
+++ b/gcc/passes.c
@@ -273,7 +273,7 @@ rest_of_decl_compilation (tree decl,
 	     which are not visible in finalize_compilation_unit()
 	     while iterating with FOR_EACH_*_FUNCTION through the
 	     symbol table.  */
-	  || (flag_dump_go_spec != NULL
+	  || ((flag_dump_go_spec != NULL || flag_dump_d_spec != NULL)
 	      && !DECL_SAVED_TREE (decl)
 	      && DECL_STRUCT_FUNCTION (decl) == NULL))
 
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 93a41943b8c..116976cd6da 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1481,12 +1481,16 @@ 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.  */
+  /* The debug hooks are used to implement -fdump-go-spec and -fdump-d-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 (flag_dump_d_spec != NULL)
+    debug_hooks = dump_d_spec_init (flag_dump_d_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.)  */


^ permalink raw reply	[flat|nested] 2+ messages in thread

* [gcc(refs/users/ibuclaw/heads/dumpspec)] d: Initial draft of -fdump-d-spec=
@ 2020-12-10 23:43 Iain Buclaw
  0 siblings, 0 replies; 2+ messages in thread
From: Iain Buclaw @ 2020-12-10 23:43 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:a43b3947ee71cddea6117cf608b7fe607e283f9c

commit a43b3947ee71cddea6117cf608b7fe607e283f9c
Author: Iain Buclaw <ibuclaw@gdcproject.org>
Date:   Mon Apr 29 06:30:43 2019 +0200

    d: Initial draft of -fdump-d-spec=
    
    For any input files in any language, generate corresponding bindings in
    the D programming language.  This generates variable, function, struct
    and enum declarations which may be a useful way to start writing a D
    interface to code written in some other language.

Diff:
---
 gcc/Makefile.in      |    2 +
 gcc/c-family/c-lex.c |    2 +-
 gcc/common.opt       |    4 +
 gcc/d-dump-spec.cc   | 1904 ++++++++++++++++++++++++++++++++++++++++++++++++++
 gcc/debug.h          |    5 +
 gcc/passes.c         |    2 +-
 gcc/toplev.c         |   10 +-
 7 files changed, 1924 insertions(+), 5 deletions(-)

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 16be66fefc6..9333469be44 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1316,6 +1316,7 @@ OBJS = \
 	cprop.o \
 	cse.o \
 	cselib.o \
+	d-dump-spec.o \
 	data-streamer.o \
 	data-streamer-in.o \
 	data-streamer-out.o \
@@ -2678,6 +2679,7 @@ GTFILES = $(CPPLIB_H) $(srcdir)/input.h $(srcdir)/coretypes.h \
   $(srcdir)/internal-fn.h \
   $(srcdir)/calls.c \
   $(srcdir)/omp-general.h \
+  $(srcdir)/d-dump-spec.cc \
   @all_gtfiles@
 
 # Compute the list of GT header files from the corresponding C sources,
diff --git a/gcc/c-family/c-lex.c b/gcc/c-family/c-lex.c
index c8d33d0c9d1..93c4354ab41 100644
--- a/gcc/c-family/c-lex.c
+++ b/gcc/c-family/c-lex.c
@@ -89,7 +89,7 @@ init_c_lex (void)
   if ((debug_info_level == DINFO_LEVEL_VERBOSE
        && (write_symbols == DWARF2_DEBUG
 	   || write_symbols == VMS_AND_DWARF2_DEBUG))
-      || flag_dump_go_spec != NULL)
+      || flag_dump_go_spec != NULL || flag_dump_d_spec != NULL)
     {
       cb->define = cb_define;
       cb->undef = cb_undef;
diff --git a/gcc/common.opt b/gcc/common.opt
index 6645539f5e5..eabda5b73ce 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1426,6 +1426,10 @@ fdump-
 Common Joined RejectNegative Var(common_deferred_options) Defer
 -fdump-<type>	Dump various compiler internals to a file.
 
+fdump-d-spec=
+Common RejectNegative Joined Var(flag_dump_d_spec)
+-fdump-d-spec=filename	Write all declarations to file as D code.
+
 fdump-final-insns
 Driver RejectNegative
 
diff --git a/gcc/d-dump-spec.cc b/gcc/d-dump-spec.cc
new file mode 100644
index 00000000000..c16afb42b52
--- /dev/null
+++ b/gcc/d-dump-spec.cc
@@ -0,0 +1,1904 @@
+/* Output D language descriptions of types.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+
+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 D language
+   descriptions of declarations from C header files.  It uses the
+   debug info hooks to emit the descriptions.  The D language
+   descriptions then become part of the D runtime support library.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "diagnostic-core.h"
+#include "debug.h"
+#include "stor-layout.h"
+#include "fold-const.h"
+
+namespace {
+
+/* 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_d_spec_init.  */
+
+struct gcc_debug_hooks d_debug_hooks;
+
+/* The real debug hooks.  */
+
+const struct gcc_debug_hooks *real_debug_hooks;
+
+/* The file where we should write information.  */
+
+FILE *d_dump_file;
+
+/* A queue of decls to output.  */
+
+static GTY(()) vec<tree, va_gc> *queue;
+
+/* A hash table of macros we have seen.  */
+
+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;
+};
+
+/* A container for the data we pass around when generating information
+   at the end of the compilation.  */
+
+struct ddump_container
+{
+  /* 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 *> pot_dummy_types;
+
+  /* D 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;
+};
+
+/* Calculate the hash value for an entry in the macro hash table.  */
+
+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.  */
+
+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.  */
+
+void
+macro_hash_del (void *v)
+{
+  struct macro_hash_value *mhv = (struct macro_hash_value *) v;
+  XDELETEVEC (mhv->name);
+  XDELETEVEC (mhv->value);
+  XDELETE (mhv);
+}
+
+/* For the string hash tables.  */
+
+int
+string_hash_eq (const void *y1, const void *y2)
+{
+  return strcmp ((const char *) y1, (const char *) y2) == 0;
+}
+
+} // namespace
+
+namespace d_dump_spec {
+
+/* Specific flags for individual FORMAT_INFO bit values.  */
+
+enum dfi_mask
+{
+  /* If we can simply use a type name without needing to define it.  */
+  DFI_TYPE_NAME  = 1 << 0,
+  /* If we can output a function type.  */
+  DFI_FUNCTION   = 1 << 1,
+  /* If we can output reference types.  */
+  DFI_REFERENCE  = 1 << 2,
+  /* If we can output const qualifiers.  */
+  DFI_CONST_QUAL = 1 << 3,
+  /* If the type is an anonymous record or enum field.  */
+  DFI_ANON_FIELD = 1 << 4
+};
+
+/* Format information flags we pass around.  */
+
+struct format_info
+{
+  unsigned flags;
+  const char *anon_type_name;
+
+  /* Constructor.  */
+  explicit format_info (unsigned flags_)
+  : flags (flags_), anon_type_name (NULL)
+  { }
+
+  explicit format_info (unsigned flags_, const char *anon_type_name_)
+  : flags (flags_), anon_type_name (anon_type_name_)
+  { }
+
+  inline bool
+  use_type_name (void)
+  {
+    return (flags & DFI_TYPE_NAME) != 0;
+  }
+
+  inline bool
+  is_func_ok (void)
+  {
+    return (flags & DFI_FUNCTION) != 0;
+  }
+
+  inline bool
+  is_ref_ok (void)
+  {
+    return (flags & DFI_REFERENCE) != 0;
+  }
+
+  inline bool
+  is_const_ok (void)
+  {
+    return (flags & DFI_CONST_QUAL) != 0;
+  }
+
+  inline bool
+  is_anon_field_type (void)
+  {
+    return (flags & DFI_ANON_FIELD) != 0;
+  }
+};
+
+/* Prototypes for forward referenced functions */
+
+static bool format_type (struct ddump_container &, tree, format_info &);
+
+/* A macro definition.  */
+
+static void
+define (unsigned int lineno, const char *buffer)
+{
+  const char *p;
+
+  real_debug_hooks->define (lineno, buffer);
+
+  /* Skip macro functions.  */
+  for (p = buffer; *p != '\0' && *p != ' '; ++p)
+    {
+      if (*p == '(')
+	return;
+    }
+
+  if (*p == '\0')
+    return;
+
+  const char *name_end = p;
+  ++p;
+
+  if (*p == '\0')
+    return;
+
+  char *copy = XNEWVEC (char, name_end - buffer + 1);
+  memcpy (copy, buffer, name_end - buffer);
+  copy[name_end - buffer] = '\0';
+
+  struct macro_hash_value *mhval = XNEW (struct macro_hash_value);
+  mhval->name = copy;
+  mhval->value = NULL;
+
+  hashval_t hashval = htab_hash_string (copy);
+  void **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.  */
+  size_t out_len = strlen (p) * 2 + 1;
+  char *out_buffer = XNEWVEC (char, out_len);
+  char *q = out_buffer;
+  bool saw_operand = false;
+  bool need_operand = false;
+  bool saw_long_suffix = 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-d-spec so we
+	       don't worry about them.  */
+	    if (saw_operand)
+	      goto Lunknown;
+
+	    const char *start = p;
+	    while (ISALNUM (*p) || *p == '_')
+	      ++p;
+
+	    char *n = XALLOCAVEC (char, p - start + 1);
+	    memcpy (n, start, p - start);
+	    n[p - start] = '\0';
+
+	    struct macro_hash_value idval;
+	    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 Lunknown;
+	      }
+
+	    memcpy (q, start, p - start);
+	    q += p - start;
+	    saw_operand = true;
+	    need_operand = false;
+	  }
+	  break;
+
+	case '.':
+	  if (!ISDIGIT (p[1]))
+	    goto Lunknown;
+
+	  gcc_fallthrough ();
+
+	case '0': case '1': case '2': case '3': case '4':
+	case '5': case '6': case '7': case '8': case '9':
+	  {
+	    const char *start = p;
+	    int base = 10;
+
+	    /* Handle base-switching prefixes for hex and octal.  */
+	    if (*p == '0')
+	      {
+		switch (p[1])
+		  {
+		  case 'x':
+		  case 'X':
+		    p += 2;
+		    base = 16;
+		    break;
+
+		  case '0': case '1': case '2': case '3':
+		  case '4': case '5': case '6': case '7':
+		    base = 8;
+		    break;
+		  }
+	      }
+
+	    if (base != 8)
+	      {
+		while (ISDIGIT (*p) || *p == '.' || *p == 'e' || *p == 'E'
+		       || (base == 16 && ((*p >= 'a' && *p <= 'f')
+					  || (*p >= 'A' && *p <= 'F'))))
+		  ++p;
+
+		memcpy (q, start, p - start);
+		q += p - start;
+	      }
+	    else
+	      {
+		char buf[100];
+		HOST_WIDE_INT num = 0;
+
+		while (ISDIGIT (*p))
+		  {
+		    int i = *p - '0';
+
+		    if (i >= base)
+		      goto Lunknown;
+
+		    num *= base;
+		    num += i;
+		    ++p;
+		  }
+
+		int buf_len = snprintf (buf, sizeof (buf),
+					HOST_WIDE_INT_PRINT_HEX, num);
+		memcpy (q, buf, buf_len);
+		q += buf_len;
+	      }
+	    while (*p == 'u' || *p == 'U' || *p == 'l' || *p == 'L'
+		   || *p == 'f' || *p == 'F'
+		   || *p == 'd' || *p == 'D')
+	      {
+		/* D doesn't have decimal floats.  */
+		if (*p == 'd' || *p == 'D')
+		  goto Lunknown;
+
+		/* D doesn't recognize 'l' or 'LL' suffixes, so rewrite to
+		   ensure there's only ever one 'L'.  */
+		if (*p == 'l' || *p == 'L')
+		  {
+		    if (!saw_long_suffix)
+		      *q++ = 'L';
+
+		    saw_long_suffix = 1;
+		    ++p;
+		  }
+		else
+		  *q++ = *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 Lunknown;
+
+	  *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 Lunknown;
+
+	  *q++ = *p++;
+	  saw_operand = false;
+	  need_operand = true;
+	  break;
+
+	case '=':
+	  *q++ = *p++;
+
+	  if (*p != '=')
+	    goto Lunknown;
+
+	  /* Must be a binary operator.  */
+	  if (!saw_operand)
+	    goto Lunknown;
+
+	  *q++ = *p++;
+	  saw_operand = false;
+	  need_operand = true;
+	  break;
+
+	case '!':
+	  *q++ = *p++;
+
+	  if (*p == '=')
+	    {
+	      /* Must be a binary operator.  */
+	      if (!saw_operand)
+		goto Lunknown;
+
+	      *q++ = *p++;
+	      saw_operand = false;
+	      need_operand = true;
+	    }
+	  else
+	    {
+	      /* Must be a unary operator.  */
+	      if (saw_operand)
+		goto Lunknown;
+
+	      need_operand = true;
+	    }
+	  break;
+
+	case '<': case '>':
+	  /* Must be a binary operand, may be << or >> or <= or >=.  */
+	  if (!saw_operand)
+	    goto Lunknown;
+
+	  *q++ = *p++;
+
+	  if (*p == *(p - 1) || *p == '=')
+	    *q++ = *p++;
+
+	  saw_operand = false;
+	  need_operand = true;
+	  break;
+
+	case '~':
+	  /* Must be a unary operand.  */
+	  if (saw_operand)
+	    goto Lunknown;
+
+	  *q++ = *p++;
+	  need_operand = true;
+	  break;
+
+	case '"':
+	case '\'':
+	  {
+	    if (saw_operand)
+	      goto Lunknown;
+
+	    char quote = *p;
+	    *q++ = *p++;
+	    int count = 0;
+
+	    while (*p != quote)
+	      {
+		if (*p == '\0')
+		  goto Lunknown;
+
+		++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':
+		    {
+		      int c = 0;
+
+		      while (*p >= '0' && *p <= '7')
+			{
+			  *q++ = *p++;
+			  ++c;
+			}
+
+		      /* D octal characters are always 3 digits.  */
+		      if (c != 3)
+			goto Lunknown;
+		    }
+		    break;
+
+		  case 'x':
+		    {
+		      int c = 0;
+		      *q++ = *p++;
+
+		      while (ISXDIGIT (*p))
+			{
+			  *q++ = *p++;
+			  ++c;
+			}
+
+		      /* D hex characters are always 2 digits.  */
+		      if (c != 2)
+			goto Lunknown;
+		    }
+		    break;
+
+		  case 'a': case 'b': case 'f': case 'n': case 'r':
+		  case 't': case 'v': case '\\': case '\'': case '"':
+		    *q++ = *p++;
+		    break;
+
+		  default:
+		    goto Lunknown;
+		  }
+	      }
+
+	    *q++ = *p++;
+
+	    if (quote == '\'' && count != 1)
+	      goto Lunknown;
+
+	    saw_operand = true;
+	    need_operand = false;
+	    break;
+	  }
+
+	default:
+	  goto Lunknown;
+	}
+    }
+
+  if (need_operand)
+    goto Lunknown;
+
+  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;
+
+Lunknown:
+  fprintf (d_dump_file, "// unknown define %s\n", buffer);
+
+  if (slot != NULL)
+    htab_clear_slot (macro_hash, slot);
+
+  XDELETEVEC (out_buffer);
+  XDELETEVEC (copy);
+}
+
+/* A macro undef.  */
+
+static void
+undef (unsigned int lineno, const char *buffer)
+{
+  real_debug_hooks->undef (lineno, buffer);
+
+  struct macro_hash_value mhval;
+  mhval.name = CONST_CAST (char *, buffer);
+  mhval.value = NULL;
+
+  void **slot = htab_find_slot (macro_hash, &mhval, NO_INSERT);
+  if (slot != NULL)
+    htab_clear_slot (macro_hash, slot);
+}
+
+/* Add a function or variable DECL to the QUEUE vector.  */
+
+static void
+enqueue_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
+function_decl (tree decl)
+{
+  real_debug_hooks->function_decl (decl);
+  enqueue_decl (decl);
+}
+
+/* A global variable decl.  */
+
+static void
+early_global_decl (tree decl)
+{
+  enqueue_decl (decl);
+  if (TREE_CODE (decl) != FUNCTION_DECL || DECL_STRUCT_FUNCTION (decl) != NULL)
+    real_debug_hooks->early_global_decl (decl);
+}
+
+static void
+late_global_decl (tree decl)
+{
+  real_debug_hooks->late_global_decl (decl);
+}
+
+/* A type declaration.  */
+
+static void
+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);
+}
+
+/* Append an IDENTIFIER_NODE to OB.  */
+
+static void
+append_string (struct obstack *ob, tree id)
+{
+  obstack_grow (ob, IDENTIFIER_POINTER (id), IDENTIFIER_LENGTH (id));
+}
+
+/* Append an artificial variable name with the suffix _INDEX to OB.
+   Returns INDEX + 1.  */
+
+static unsigned int
+append_artificial_bitfield (struct obstack *ob, unsigned int index)
+{
+  char buf[100];
+
+  /* Identifier may not be unique.  */
+  obstack_grow (ob, "__bitfield_padding", 18);
+  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
+append_decl_name (struct obstack *ob, tree decl, htab_t keyword_hash)
+{
+  append_string (ob, DECL_NAME (decl));
+  /* Add underscore after variable name if a keyword.  */
+  const char *var_name = IDENTIFIER_POINTER (DECL_NAME (decl));
+  if (htab_find_slot (keyword_hash, var_name, NO_INSERT) != NULL)
+    obstack_1grow (ob, '_');
+}
+
+/* Returns true if TYPE corresponds to a D tagged type.  */
+
+static bool
+is_tagged_type (const_tree type)
+{
+  enum tree_code code = TREE_CODE (type);
+
+  return TYPE_IDENTIFIER (type)
+    && (code == RECORD_TYPE || code == UNION_TYPE
+	|| code == QUAL_UNION_TYPE || code == ENUMERAL_TYPE);
+}
+
+/* Write the D version of integer TYPE to TYPE_OBSTACK.  Return true if the type
+   can be represented in D, false otherwise.  */
+
+static bool
+format_integer_type (struct obstack *type_obstack, tree type)
+{
+  bool ret = true;
+  const char *s;
+  char buf[100];
+
+  switch (int_size_in_bytes (type))
+    {
+    case 1:
+      s = TREE_CODE (type) == INTEGER_TYPE && TYPE_STRING_FLAG (type) ? "char"
+	: TYPE_UNSIGNED (type) ? "ubyte" : "byte";
+      break;
+
+    case 2:
+      s = TREE_CODE (type) == INTEGER_TYPE && TYPE_STRING_FLAG (type) ? "wchar"
+	: TYPE_UNSIGNED (type) ? "ushort" : "short";
+      break;
+
+    case 4:
+      s = TREE_CODE (type) == INTEGER_TYPE && TYPE_STRING_FLAG (type) ? "dchar"
+	: TYPE_UNSIGNED (type) ? "uint" : "int";
+      break;
+
+    case 8:
+      s = TYPE_UNSIGNED (type) ? "ulong" : "long";
+      break;
+
+    default:
+      snprintf (buf, sizeof (buf), "INVALID-int-%u%s",
+		TYPE_PRECISION (type),
+		TYPE_UNSIGNED (type) ? "u" : "");
+      s = buf;
+      ret = false;
+      break;
+    }
+
+  obstack_grow (type_obstack, s, strlen (s));
+  return ret;
+}
+
+/* Write the D version of real TYPE to TYPE_OBSTACK.  Return true if the type
+   can be represented in D, false otherwise.  */
+
+static bool
+format_real_type (struct obstack *type_obstack, tree type)
+{
+  bool ret = true;
+  const char *s;
+  char buf[100];
+
+  switch (TYPE_PRECISION (type))
+    {
+    case 32:
+      s = "float";
+      break;
+
+    case 64:
+      s = "double";
+      break;
+
+    default:
+      if (TYPE_PRECISION (type) == LONG_DOUBLE_TYPE_SIZE)
+	s = "real";
+      else
+	{
+	  snprintf (buf, sizeof (buf), "INVALID-float-%u",
+		    TYPE_PRECISION (type));
+	  s = buf;
+	  ret = false;
+	}
+      break;
+    }
+
+  obstack_grow (type_obstack, s, strlen (s));
+  return ret;
+}
+
+/* Write the D version of complex TYPE to TYPE_OBSTACK.  Return true if the type
+   can be represented in D, false otherwise.  */
+
+static bool
+format_complex_type (struct obstack *type_obstack, tree type)
+{
+  bool ret = true;
+  const char *s;
+  char buf[100];
+  tree real_type = TREE_TYPE (type);
+
+  if (TREE_CODE (real_type) == REAL_TYPE)
+    {
+      switch (TYPE_PRECISION (real_type))
+	{
+	case 32:
+	  s = "cfloat";
+	  break;
+
+	case 64:
+	  s = "cdouble";
+	  break;
+
+	default:
+	  if (TYPE_PRECISION (real_type) == LONG_DOUBLE_TYPE_SIZE)
+	    s = "creal";
+	  else
+	    {
+	      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 (type_obstack, s, strlen (s));
+  return ret;
+}
+
+/* Write the D version of pointer TYPE to CONTAINER.TYPE_OBSTACK using INFO to
+   control how the type is formatted.  Return true if the type can be
+   represented in D, false otherwise.  */
+
+static bool
+format_pointer_type (struct ddump_container &container, tree type,
+		     format_info &info)
+{
+  tree basetype = TREE_TYPE (type);
+  bool is_pointer_const = TYPE_READONLY (type);
+  bool is_base_const = TYPE_READONLY (basetype);
+  struct obstack *ob = &container.type_obstack;
+  bool ret = true;
+  format_info basetype_info (info.flags & DFI_ANON_FIELD, info.anon_type_name);
+
+  /* Don't force the TYPE_NAME if base type is anonymous.  */
+  if (!info.is_anon_field_type ())
+    basetype_info.flags |= DFI_TYPE_NAME;
+
+  /* Allow FUNCTION_TYPE types to be emitted.  */
+  if (TREE_CODE (basetype) == FUNCTION_TYPE)
+    basetype_info.flags |= DFI_FUNCTION;
+
+  /* Emit const qualifier if haven't already done so.  */
+  if (info.is_const_ok ())
+    {
+      if (is_pointer_const)
+	obstack_grow (ob, "const ", 6);
+      else if (is_base_const)
+	obstack_grow (ob, "const(", 6);
+      else
+	basetype_info.flags |= DFI_CONST_QUAL;
+    }
+
+  if (!format_type (container, basetype, basetype_info))
+    ret = false;
+
+  if (info.is_const_ok () && !is_pointer_const && is_base_const)
+    obstack_1grow (ob, ')');
+
+  if (!basetype_info.is_func_ok ())
+    obstack_1grow (ob, '*');
+
+  /* The pointer here can be used without the struct or union
+     definition.  So this struct or union is a potential dummy
+     type.  */
+  if (is_tagged_type (basetype))
+    {
+      const char *ident = IDENTIFIER_POINTER (TYPE_IDENTIFIER (basetype));
+      container.pot_dummy_types.add (ident);
+    }
+
+  return ret;
+}
+
+/* Write the D version of reference TYPE to CONTAINER.TYPE_OBSTACK using INFO
+   to control how the type is formatted.  Return true if the type can be
+   represented in D, false otherwise.  */
+
+static bool
+format_reference_type (struct ddump_container &container, tree type,
+		       format_info &info)
+{
+  struct obstack *ob = &container.type_obstack;
+  bool ret = true;
+
+  /* The ref keyword is only valid in the context of function return or
+     parameter types.  */
+  if (!info.is_ref_ok ())
+    ret = false;
+
+  obstack_grow (ob, "ref ", 4);
+
+  /* Strip 'ref' from the info context.  */
+  unsigned basetype_info_mask = (DFI_TYPE_NAME | (info.flags & DFI_CONST_QUAL));
+  format_info basetype_info (basetype_info_mask);
+
+  if (!format_type (container, TREE_TYPE (type), basetype_info))
+    ret = false;
+
+  return ret;
+}
+
+/* Write the D version of array TYPE to CONTAINER.TYPE_OBSTACK using INFO to
+   control how the type is formatted.  Return true if the type can be
+   represented in D, false otherwise.  */
+
+static bool
+format_array_type (struct ddump_container &container, tree type,
+		   format_info &info)
+{
+  struct obstack *ob = &container.type_obstack;
+  bool ret = true;
+  format_info basetype_info (info.flags & (DFI_CONST_QUAL | DFI_ANON_FIELD),
+			     info.anon_type_name);
+
+  /* Don't force the TYPE_NAME if base type is anonymous.  */
+  if (!info.is_anon_field_type ())
+    basetype_info.flags |= DFI_TYPE_NAME;
+
+  if (!format_type (container, TREE_TYPE (type), basetype_info))
+    ret = false;
+
+  obstack_1grow (ob, '[');
+
+  if (TYPE_DOMAIN (type) != NULL_TREE
+      && TYPE_MAX_VALUE (TYPE_DOMAIN (type)) != NULL_TREE)
+    {
+      tree nelts = fold_build2 (PLUS_EXPR, sizetype,
+				array_type_nelts (type),
+				size_one_node);
+      char buf[100];
+
+      snprintf (buf, sizeof (buf), HOST_WIDE_INT_PRINT_DEC,
+		tree_to_shwi (nelts));
+      obstack_grow (ob, buf, strlen (buf));
+    }
+  else
+    obstack_1grow (ob, '0');
+
+  obstack_1grow (ob, ']');
+  return ret;
+}
+
+/* Write the D version of vector TYPE to CONTAINER.TYPE_OBSTACK using INFO to
+   control how the type is formatted.  Return true if the type can be
+   represented in D, false otherwise.  */
+
+static bool
+format_vector_type (struct ddump_container &container, tree type,
+		    format_info &info)
+{
+  struct obstack *ob = &container.type_obstack;
+  bool ret = true;
+  unsigned basetype_info_mask = (DFI_TYPE_NAME | (info.flags & DFI_CONST_QUAL));
+  format_info basetype_info (basetype_info_mask);
+
+  obstack_grow (ob, "__vector(", 9);
+  if (!format_type (container, TREE_TYPE (type), basetype_info))
+    ret = false;
+
+  obstack_1grow (ob, '[');
+
+  unsigned HOST_WIDE_INT nunits;
+  if (TYPE_VECTOR_SUBPARTS (type).is_constant (&nunits))
+    {
+      char buf[100];
+
+      snprintf (buf, sizeof (buf), HOST_WIDE_INT_PRINT_DEC, nunits);
+      obstack_grow (ob, buf, strlen (buf));
+    }
+  else
+    ret = false;
+
+  obstack_grow (ob, "])", 2);
+  return ret;
+}
+
+/* Write the D version of enumeral TYPE to CONTAINER.TYPE_OBSTACK using INFO to
+   control how the type is formatted.  Return true if the type can be
+   represented in D, false otherwise.  */
+
+static bool
+format_enumeral_type (struct ddump_container &container, tree type,
+		      format_info &info)
+{
+  struct obstack *ob = &container.type_obstack;
+  bool ret = true;
+
+  if (TYPE_IDENTIFIER (type))
+    container.decls_seen.add (TYPE_IDENTIFIER (type));
+
+  /* Don't emit the body of an enum in a context where it isn't a valid D
+     declaration.  */
+  if (info.use_type_name ())
+    {
+      if (TYPE_IDENTIFIER (type))
+	append_string (ob, TYPE_IDENTIFIER (type));
+      else
+	ret = false;
+
+      return ret;
+    }
+  else if (info.is_anon_field_type ())
+    {
+      if (info.anon_type_name != NULL)
+	ret = false;
+
+      if (!format_integer_type (ob, type))
+	ret = false;
+
+      return ret;
+    }
+
+  obstack_grow (ob, "enum", 4);
+
+  if (TYPE_IDENTIFIER (type) && !IDENTIFIER_ANON_P (TYPE_IDENTIFIER (type)))
+    {
+      obstack_1grow (ob, ' ');
+      append_string (ob, TYPE_IDENTIFIER (type));
+    }
+
+  if (TREE_TYPE (type))
+    {
+      format_info basetype_info (DFI_TYPE_NAME | DFI_CONST_QUAL);
+
+      obstack_grow (ob, " : ", 3);
+      if (!format_type (container, TREE_TYPE (type), basetype_info))
+	ret = false;
+    }
+
+  obstack_grow (ob, " { ", 3);
+
+  for (tree element = TYPE_VALUES (type);
+       element != NULL_TREE;
+       element = TREE_CHAIN (element))
+    {
+      char buf[WIDE_INT_PRINT_BUFFER_SIZE];
+      const char *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
+	 removing the macro.  */
+      struct macro_hash_value mhval;
+      mhval.name = CONST_CAST (char *, name);
+      mhval.value = NULL;
+
+      void **slot = htab_find_slot (macro_hash, &mhval, NO_INSERT);
+      if (slot != NULL)
+	htab_clear_slot (macro_hash, slot);
+
+      obstack_grow (ob, name, strlen (name));
+      obstack_grow (ob, " = ", 3);
+
+      tree value = TREE_VALUE (element);
+      if (TREE_CODE (value) == CONST_DECL)
+	value = DECL_INITIAL (value);
+
+      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
+	print_hex (wi::to_wide (value), buf);
+
+      obstack_grow (ob, buf, strlen (buf));
+      if (TREE_CHAIN (element))
+	obstack_1grow (ob, ',');
+
+      obstack_1grow (ob, ' ');
+    }
+
+  obstack_1grow (ob, '}');
+  return ret;
+}
+
+/* Write the D version of struct or union TYPE to CONTAINER.TYPE_OBSTACK using
+   INFO to control how the type is formatted.  P_ART_I is used for indexing
+   artifical elements in nested structures and should always be a NULL pointer
+   when called, except by recursive calls from format_record_type() itself.
+   Return true if the type can be represented in D, false otherwise.  */
+
+static bool
+format_record_type (struct ddump_container &container, tree type,
+		    format_info &info, unsigned *p_art_i)
+{
+  struct obstack *ob = &container.type_obstack;
+  bool ret = true;
+  unsigned int art_i_dummy;
+
+  if (p_art_i == NULL)
+    {
+      art_i_dummy = 0;
+      p_art_i = &art_i_dummy;
+    }
+
+  /* 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);
+
+  if (TYPE_IDENTIFIER (type))
+    container.decls_seen.add (TYPE_IDENTIFIER (type));
+
+  /* Don't emit the body of a struct/union in a context where it
+     isn't a valid D declaration.  */
+  if (info.use_type_name ())
+    {
+      if (TYPE_IDENTIFIER (type))
+	append_string (ob, TYPE_IDENTIFIER (type));
+      else
+	ret = false;
+
+      return ret;
+    }
+
+  const char *record_type
+    = (TREE_CODE (type) == UNION_TYPE) ? "union" : "struct";
+  obstack_grow (ob, record_type, strlen (record_type));
+  if (TYPE_IDENTIFIER (type))
+    {
+      obstack_1grow (ob, ' ');
+      append_string (ob, TYPE_IDENTIFIER (type));
+    }
+  else if (info.is_anon_field_type () && info.anon_type_name)
+    {
+      obstack_1grow (ob, ' ');
+      obstack_grow (ob, info.anon_type_name, strlen (info.anon_type_name));
+    }
+
+  obstack_grow (ob, " { ", 3);
+
+  for (tree field = TYPE_FIELDS (type); field != NULL_TREE;
+       field = TREE_CHAIN (field))
+    {
+      if (TREE_CODE (field) != FIELD_DECL)
+	continue;
+
+      /* Bit fields are replaced by their representative field.  */
+      if (DECL_BIT_FIELD_TYPE (field))
+	{
+	  if (integer_zerop (DECL_SIZE (field)))
+	    continue;
+
+	  tree repr = DECL_BIT_FIELD_REPRESENTATIVE (field);
+
+	  while (TREE_CHAIN (field) != NULL_TREE
+		 && DECL_BIT_FIELD_REPRESENTATIVE (TREE_CHAIN (field)) == repr)
+	    field = TREE_CHAIN (field);
+
+	  format_info field_info (DFI_TYPE_NAME | DFI_CONST_QUAL);
+	  if (!format_type (container, TREE_TYPE (repr), field_info))
+	    ret = false;
+
+	  obstack_1grow (ob, ' ');
+	  *p_art_i = append_artificial_bitfield (ob, *p_art_i);
+	  obstack_grow (ob, "; ", 2);
+	  continue;
+	}
+
+      /* Emit the field.  */
+      tree field_type = TREE_TYPE (field);
+      tree base_field_type = field_type;
+      bool is_anon_field_type = false;
+
+      while (POINTER_TYPE_P (base_field_type)
+	     || TREE_CODE (base_field_type) == ARRAY_TYPE)
+	base_field_type = TREE_TYPE (base_field_type);
+
+      if ((AGGREGATE_TYPE_P (base_field_type)
+	   || TREE_CODE (base_field_type) == ENUMERAL_TYPE)
+	  && TYPE_IDENTIFIER (base_field_type) == NULL_TREE)
+	is_anon_field_type = true;
+
+      if (TYPE_USER_ALIGN (field_type))
+	{
+	  char buf[100];
+
+	  snprintf (buf, sizeof (buf), "align(%u) ",
+		    TYPE_ALIGN_UNIT (field_type));
+	  obstack_grow (ob, buf, strlen (buf));
+	}
+      else if (TYPE_PACKED (field_type))
+	obstack_grow (ob, "align(1) ", 9);
+
+      if (is_anon_field_type)
+	{
+	  const char *type_name = NULL;
+	  char buf[100];
+
+	  /* Give anonymous types a fake type name.  */
+	  if (RECORD_OR_UNION_TYPE_P (base_field_type)
+	      && DECL_NAME (field) != NULL_TREE)
+	    {
+	      snprintf (buf, sizeof (buf), "__anonymous_type_%u", *p_art_i);
+	      *p_art_i += 1;
+	      type_name = buf;
+	    }
+
+	  format_info field_info (DFI_CONST_QUAL | DFI_ANON_FIELD,
+				  type_name);
+
+	  if (RECORD_OR_UNION_TYPE_P (field_type))
+	    {
+	      if (!format_record_type (container, field_type, field_info,
+				       p_art_i))
+		ret = false;
+	    }
+	  else if (INTEGRAL_TYPE_P (field_type)
+		   || POINTER_TYPE_P (field_type)
+		   || TREE_CODE (field_type) == ARRAY_TYPE)
+	    {
+	      if (DECL_NAME (field) == NULL_TREE)
+		ret = false;
+
+	      if (!format_type (container, field_type, field_info))
+		ret = false;
+	    }
+	  else
+	    ret = false;
+	}
+      else
+	{
+	  format_info field_info (DFI_TYPE_NAME | DFI_CONST_QUAL);
+
+	  if (!format_type (container, field_type, field_info))
+	    ret = false;
+	}
+
+      /* Emit the field name, but not for anonymous records and
+	 unions.  */
+      if (!is_anon_field_type || DECL_NAME (field) != NULL_TREE)
+	{
+	  obstack_1grow (ob, ' ');
+	  append_decl_name (ob, field, container.keyword_hash);
+	  obstack_grow (ob, "; ", 2);
+	}
+    }
+
+  obstack_1grow (ob, '}');
+
+  /* If an anonymous record or union type with a field name.  Put out the
+     artifically generated name now.  */
+  if (!TYPE_IDENTIFIER (type) && info.is_anon_field_type ())
+    {
+      obstack_1grow (ob, ' ');
+      if (info.anon_type_name != NULL)
+	obstack_grow (ob, info.anon_type_name, strlen (info.anon_type_name));
+    }
+
+  return ret;
+}
+
+/* Write the TYPE_ARG_TYPES of the function TYPE to CONTAINER.TYPE_OBSTACK.
+   Return true if all arguments can be represented in D, false otherwise.  */
+
+static bool
+format_function_args (struct ddump_container &container, tree type)
+{
+  bool ret = true;
+  struct obstack *ob = &container.type_obstack;
+  bool seen_arg = false;
+  format_info info (DFI_TYPE_NAME | DFI_REFERENCE | DFI_CONST_QUAL);
+  tree arg_type;
+  function_args_iterator iter;
+
+  obstack_1grow (ob, '(');
+
+  FOREACH_FUNCTION_ARGS (type, arg_type, iter)
+    {
+      if (VOID_TYPE_P (arg_type))
+	break;
+
+      if (seen_arg)
+	obstack_grow (ob, ", ", 2);
+
+      if (!format_type (container, arg_type, info))
+	ret = false;
+
+      seen_arg = true;
+    }
+
+  if (stdarg_p (type))
+    {
+      if (prototype_p (type))
+	obstack_grow (ob, ", ", 2);
+
+      obstack_grow (ob, "...", 3);
+    }
+
+  obstack_1grow (ob, ')');
+  return ret;
+}
+
+/* Write the D version of function TYPE to CONTAINER.TYPE_OBSTACK using INFO to
+   control how the type is formatted.  Return true if the type can be
+   represented in D, false otherwise.  */
+
+static bool
+format_function_type (struct ddump_container &container, tree type,
+		      format_info &info)
+{
+  struct obstack *ob = &container.type_obstack;
+  bool ret = true;
+
+  /* D has no way to write a type which is a function but not a
+     pointer to a function.  */
+  if (!info.is_func_ok ())
+    ret = false;
+
+  unsigned return_info_mask
+    = (DFI_TYPE_NAME | DFI_REFERENCE | (info.flags & DFI_CONST_QUAL));
+  format_info return_info (return_info_mask);
+
+  if (!format_type (container, TREE_TYPE (type), return_info))
+    ret = false;
+
+  obstack_1grow (ob, ' ');
+  obstack_grow (ob, "function", 8);
+
+  if (!format_function_args (container, type))
+    ret = false;
+
+  return ret;
+}
+
+/* Write the D version of TYPE to CONTAINER.TYPE_OBSTACK using INFO to control
+   how the type is formatted.  P_ART_I is used for indexing artifical elements
+   in nested structures and should always be a NULL pointer when called, except
+   by certain recursive calls from format_type() itself.
+   IS_ANON_RECORD_OR_UNION is true the type is an anonymous field type.
+   Return true if the type can be represented in D, false otherwise.  */
+
+static bool
+format_type (struct ddump_container &container, tree type, format_info &info)
+{
+  bool ret = true;
+  struct obstack *ob = &container.type_obstack;
+
+  /* Shortcut formatting the type if TYPE_NAME is both and set and requested.
+     If the type was used in a typedef, it will use the alias instead of
+     writing out the base type.  */
+  if (info.use_type_name ()
+      && TYPE_NAME (type) != NULL_TREE
+      && (TREE_CODE (TYPE_NAME (type)) != TYPE_DECL
+	  || !DECL_IS_UNDECLARED_BUILTIN (TYPE_NAME (type)))
+      && (container.decls_seen.contains (type)
+	  || container.decls_seen.contains (TYPE_NAME (type)))
+      )
+    {
+      tree name = TYPE_IDENTIFIER (type);
+
+      if (htab_find_slot (container.invalid_hash, IDENTIFIER_POINTER (name),
+			  NO_INSERT) != NULL)
+	ret = false;
+
+      append_string (ob, name);
+      return ret;
+    }
+
+  container.decls_seen.add (type);
+
+  switch (TREE_CODE (type))
+    {
+    case INTEGER_TYPE:
+      ret = format_integer_type (ob, type);
+      break;
+
+    case REAL_TYPE:
+      ret = format_real_type (ob, type);
+      break;
+
+    case COMPLEX_TYPE:
+      ret = format_complex_type (ob, type);
+      break;
+
+    case BOOLEAN_TYPE:
+      obstack_grow (ob, "bool", 4);
+      break;
+
+    case VOID_TYPE:
+      obstack_grow (ob, "void", 4);
+      break;
+
+    case POINTER_TYPE:
+      ret = format_pointer_type (container, type, info);
+      break;
+
+    case REFERENCE_TYPE:
+      ret = format_reference_type (container, type, info);
+      break;
+
+    case ARRAY_TYPE:
+      ret = format_array_type (container, type, info);
+      break;
+
+    case VECTOR_TYPE:
+      ret = format_vector_type (container, type, info);
+      break;
+
+    case ENUMERAL_TYPE:
+      ret = format_enumeral_type (container, type, info);
+      break;
+
+    case RECORD_TYPE:
+    case UNION_TYPE:
+      ret = format_record_type (container, type, info, NULL);
+      break;
+
+    case FUNCTION_TYPE:
+      ret = format_function_type (container, type, info);
+      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
+output_type (struct ddump_container &container)
+{
+  struct obstack *ob = &container.type_obstack;
+  obstack_1grow (ob, '\0');
+  fputs ((char *) obstack_base (ob), d_dump_file);
+  obstack_free (ob, obstack_base (ob));
+}
+
+/* Output a function declaration.  */
+
+static void
+output_fndecl (struct ddump_container &container, tree decl)
+{
+  struct obstack *ob = &container.type_obstack;
+  bool is_valid = true;
+  char buf[100];
+
+  tree decl_name = DECL_NAME (decl);
+  tree decl_asm_name = DECL_ASSEMBLER_NAME (decl);
+
+  if (decl_asm_name != decl_name
+      || htab_find_slot (container.keyword_hash,
+			 IDENTIFIER_POINTER (decl_name), NO_INSERT))
+    {
+      const char *asm_name = IDENTIFIER_POINTER (decl_asm_name);
+      if (*asm_name == '*')
+	asm_name++;
+
+      snprintf (buf, sizeof (buf), "pragma(mangle, \"%s\") ", asm_name);
+      obstack_grow (ob, buf, strlen (buf));
+    }
+
+  format_info info (DFI_TYPE_NAME | DFI_REFERENCE | DFI_CONST_QUAL);
+  if (!format_type (container, TREE_TYPE (TREE_TYPE (decl)), info))
+    is_valid = false;
+
+  obstack_1grow (ob, ' ');
+  append_decl_name (ob, decl, container.keyword_hash);
+
+  if (!format_function_args (container, TREE_TYPE (decl)))
+    is_valid = false;
+
+  if (!is_valid)
+    fprintf (d_dump_file, "// ");
+
+  output_type (container);
+  fprintf (d_dump_file, ";\n");
+}
+
+/* Returns true if DECL is an implicitly generated TYPE_DECL for a type.
+   These do not require an alias to be declared in D.  */
+
+static bool
+is_implicit_typedef (const_tree decl)
+{
+  if (DECL_NAME (decl) == NULL_TREE)
+    return true;
+
+  if (DECL_ARTIFICIAL (decl)
+      && TYPE_STUB_DECL (TREE_TYPE (decl)) == decl)
+    return true;
+
+  return false;
+}
+
+/* Output a typedef or something like a struct definition.  */
+
+static void
+output_typedef (struct ddump_container &container, tree decl)
+{
+  if (!is_implicit_typedef (decl))
+    {
+      const char *ident = IDENTIFIER_POINTER (DECL_NAME (decl));
+
+      /* If type is a keyword, skip.  */
+      if (htab_find_slot (container.keyword_hash, ident, NO_INSERT) != NULL)
+	return;
+
+      /* If type defined already, skip.  */
+      void **slot = htab_find_slot (container.type_hash, ident, INSERT);
+      if (*slot != NULL)
+	return;
+
+      *slot = CONST_CAST (void *, (const void *) ident);
+
+      tree type = TREE_TYPE (decl);
+      format_info info (DFI_CONST_QUAL);
+
+      if (is_tagged_type (type))
+	{
+	  /* If this is a plain typedef, and not a typedef struct, then only get
+	     the type name for the alias declaration.  */
+	  if (TYPE_NAME (type) == decl
+	      && DECL_ORIGINAL_TYPE (decl) != NULL_TREE
+	      && TYPE_NAME (DECL_ORIGINAL_TYPE (decl)) != NULL_TREE)
+	    {
+	      type = DECL_ORIGINAL_TYPE (decl);
+	      info.flags |= DFI_TYPE_NAME;
+
+	      /* The typedef can be to an opaque struct or union, so is a
+		 potential dummy type.  */
+	      const char *ident = IDENTIFIER_POINTER (TYPE_IDENTIFIER (type));
+	      container.pot_dummy_types.add (ident);
+	    }
+	}
+      else
+	info.flags |= DFI_TYPE_NAME;
+
+      /* Allow FUNCTION_TYPE types to be emitted as typedefs.  */
+      if (TREE_CODE (type) == FUNCTION_TYPE)
+	info.flags |= DFI_FUNCTION;
+
+      if (!format_type (container, type, info))
+	{
+	  fprintf (d_dump_file, "// ");
+	  slot = htab_find_slot (container.invalid_hash, ident, INSERT);
+	  *slot = CONST_CAST (void *, (const void *) ident);
+	}
+
+      if (info.use_type_name ())
+	{
+	  fprintf (d_dump_file, "alias %s = ",
+		   IDENTIFIER_POINTER (DECL_NAME (decl)));
+	}
+
+      output_type (container);
+      container.decls_seen.add (decl);
+
+      if (info.use_type_name ())
+	fprintf (d_dump_file, ";");
+    }
+  else if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (decl)))
+    {
+      tree type = TREE_TYPE (decl);
+      const char *ident = IDENTIFIER_POINTER (TYPE_IDENTIFIER (type));
+
+       /* If type defined already, skip.  */
+       void **slot = htab_find_slot (container.type_hash, ident, INSERT);
+       if (*slot != NULL)
+	 return;
+
+       *slot = CONST_CAST (void *, (const void *) ident);
+
+       format_info info (DFI_CONST_QUAL);
+       if (!format_type (container, type, info))
+	 {
+	   fprintf (d_dump_file, "// ");
+	   slot = htab_find_slot (container.invalid_hash, ident, INSERT);
+	   *slot = CONST_CAST (void *, (const void *) ident);
+	 }
+
+       output_type (container);
+    }
+  else if (TREE_CODE (TREE_TYPE (decl)) == ENUMERAL_TYPE)
+    {
+      tree type = TREE_TYPE (decl);
+      bool is_anon_enum = (TYPE_IDENTIFIER (type) == NULL_TREE
+			   || IDENTIFIER_ANON_P (TYPE_IDENTIFIER (type)));
+      const char *ident = NULL;
+      void **slot;
+
+      if (!is_anon_enum)
+	{
+	  ident = IDENTIFIER_POINTER (TYPE_IDENTIFIER (type));
+
+	  /* If type defined already, skip.  */
+	  slot = htab_find_slot (container.type_hash, ident, INSERT);
+	  if (*slot != NULL)
+	    return;
+
+	  *slot = CONST_CAST (void *, (const void *) ident);
+	}
+
+      format_info info (DFI_CONST_QUAL);
+      if (!format_type (container, type, info))
+	{
+	  fprintf (d_dump_file, "// ");
+	  if (ident)
+	    {
+	      slot = htab_find_slot (container.invalid_hash, ident, INSERT);
+	      *slot = CONST_CAST (void *, (const void *) ident);
+	    }
+	}
+
+      output_type (container);
+    }
+  else
+    return;
+
+  fprintf (d_dump_file, "\n");
+}
+
+/* Output a variable.  */
+
+static void
+output_var (struct ddump_container &container, tree decl)
+{
+  bool is_valid;
+
+  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));
+
+  tree type_name = TYPE_NAME (TREE_TYPE (decl));
+  tree 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 = &container.type_obstack;
+
+      append_string (ob, id);
+      is_valid = htab_find_slot (container.type_hash, IDENTIFIER_POINTER (id),
+				 NO_INSERT) != NULL;
+    }
+  else
+    {
+      format_info info (DFI_TYPE_NAME | DFI_CONST_QUAL);
+      is_valid = format_type (container, TREE_TYPE (decl), info);
+    }
+
+  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 (d_dump_file, "// ");
+
+  fprintf (d_dump_file, "__gshared ");
+  output_type (container);
+  fprintf (d_dump_file, " %s;\n", IDENTIFIER_POINTER (DECL_NAME (decl)));
+
+  /* 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
+print_macro (void **slot, void *arg)
+{
+  struct macro_hash_value *mhval = (struct macro_hash_value *) *slot;
+  struct ddump_container *container = (struct ddump_container *) arg;
+
+  fprintf (d_dump_file, "enum ");
+
+  if (htab_find_slot (container->keyword_hash, mhval->name, NO_INSERT) != NULL)
+    fprintf (d_dump_file, "_");
+
+  fprintf (d_dump_file, "%s = %s;\n", mhval->name, mhval->value);
+
+  return 1;
+}
+
+/* Build a hash table with the D keywords.  */
+
+const char * const keywords[] = {
+  /* Basic types.  */
+  "void", "byte", "ubyte", "short", "ushort", "int", "uint", "long", "ulong",
+  "cent", "ucent", "float", "double", "real", "ifloat", "idouble", "ireal",
+  "cfloat", "cdouble", "creal", "char", "wchar", "dchar", "bool",
+  /* Aggregates.  */
+  "struct", "class", "interface", "union", "enum", "import", "alias",
+  "override", "delegate", "function", "mixin", "align", "extern", "private",
+  "protected", "public", "export", "static", "final", "const", "abstract",
+  "debug", "deprecated", "inout", "lazy", "auto", "package", "immutable",
+  /* Statements.  */
+  "if", "else", "while", "for", "do", "switch", "case", "default", "break",
+  "continue", "with", "synchronized", "return", "goto", "try", "catch",
+  "finally", "asm", "foreach", "foreach_reverse", "scope",
+  /* Contracts.  */
+  "invariant", "in", "out", "body",
+  /* Operators.  */
+  "is", "this", "super",
+  /* Testing.  */
+  "unittest",
+  /* Literals.  */
+  "__LINE__", "__FILE__", "__MODULE__", "__FUNCTION__", "__PRETTY_FUNCTION__",
+  "__DATE__", "__TIME__", "__TIMESTAMP__", "__VENDOR__", "__VERSION__",
+  "__EOF__",
+  /* Other.  */
+  "cast", "null", "assert", "true", "false", "throw", "new", "delete",
+  "version", "module", "template", "typeof", "pragma", "typeid",
+  "ref", "__traits", "pure", "nothrow", "__gshared", "shared",
+};
+
+static void
+keyword_hash_init (struct ddump_container &container)
+{
+  size_t count = sizeof (keywords) / sizeof (keywords[0]);
+
+  for (size_t i = 0; i < count; i++)
+    {
+      void **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.  */
+
+static bool
+find_dummy_types (const char *const &ptr, ddump_container *adata)
+{
+  struct ddump_container *data = (struct ddump_container *) adata;
+  const char *type = (const char *) ptr;
+
+  void **slot = htab_find_slot (data->type_hash, type, NO_INSERT);
+  void **islot = htab_find_slot (data->invalid_hash, type, NO_INSERT);
+  if (slot == NULL || islot != NULL)
+    fprintf (d_dump_file, "struct %s;\n", type);
+
+  return true;
+}
+
+/* Output symbols.  */
+
+static void
+finish (const char *filename)
+{
+  struct ddump_container container;
+  unsigned int ix;
+  tree decl;
+
+  real_debug_hooks->finish (filename);
+
+  container.type_hash = htab_create (100, htab_hash_string,
+				     string_hash_eq, NULL);
+  container.invalid_hash = htab_create (10, htab_hash_string,
+					string_hash_eq, NULL);
+  container.keyword_hash = htab_create (100, htab_hash_string,
+					string_hash_eq, 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:
+	  output_fndecl (container, decl);
+	  break;
+
+	case TYPE_DECL:
+	  output_typedef (container, decl);
+	  break;
+
+	case VAR_DECL:
+	  output_var (container, decl);
+	  break;
+
+	default:
+	  gcc_unreachable ();
+	}
+    }
+
+  htab_traverse_noresize (macro_hash, print_macro, &container);
+
+  /* To emit dummy definitions.  */
+  container.pot_dummy_types.traverse<ddump_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 (d_dump_file) != 0)
+    error ("could not close D dump file: %m");
+
+  d_dump_file = NULL;
+}
+
+} // namespace d_dump_spec
+
+/* Set up our hooks.  */
+
+const struct gcc_debug_hooks *
+dump_d_spec_init (const char *filename, const struct gcc_debug_hooks *hooks)
+{
+  d_dump_file = fopen (filename, "w");
+  if (d_dump_file == NULL)
+    {
+      error ("could not open D dump file %qs: %m", filename);
+      return hooks;
+    }
+
+  d_debug_hooks = *hooks;
+  real_debug_hooks = hooks;
+
+  d_debug_hooks.finish = d_dump_spec::finish;
+  d_debug_hooks.define = d_dump_spec::define;
+  d_debug_hooks.undef = d_dump_spec::undef;
+  d_debug_hooks.function_decl = d_dump_spec::function_decl;
+  d_debug_hooks.early_global_decl = d_dump_spec::early_global_decl;
+  d_debug_hooks.late_global_decl = d_dump_spec::late_global_decl;
+  d_debug_hooks.type_decl = d_dump_spec::type_decl;
+
+  macro_hash = htab_create (100, macro_hash_hashval, macro_hash_eq,
+			    macro_hash_del);
+
+  return &d_debug_hooks;
+}
+
+#include "gt-d-dump-spec.h"
diff --git a/gcc/debug.h b/gcc/debug.h
index 260325920ea..b1f50751405 100644
--- a/gcc/debug.h
+++ b/gcc/debug.h
@@ -256,6 +256,11 @@ extern bool dwarf2out_default_as_locview_support (void);
 extern const struct gcc_debug_hooks *
 dump_go_spec_init (const char *, const struct gcc_debug_hooks *);
 
+/* For -fdump-d-spec.  */
+
+extern const struct gcc_debug_hooks *
+dump_d_spec_init (const char *, const struct gcc_debug_hooks *);
+
 /* Instance discriminator mapping table.  See final.c.  */
 typedef hash_map<const_tree, int> decl_to_instance_map_t;
 extern decl_to_instance_map_t *decl_to_instance_map;
diff --git a/gcc/passes.c b/gcc/passes.c
index 973c958f769..cb5b67377d9 100644
--- a/gcc/passes.c
+++ b/gcc/passes.c
@@ -273,7 +273,7 @@ rest_of_decl_compilation (tree decl,
 	     which are not visible in finalize_compilation_unit()
 	     while iterating with FOR_EACH_*_FUNCTION through the
 	     symbol table.  */
-	  || (flag_dump_go_spec != NULL
+	  || ((flag_dump_go_spec != NULL || flag_dump_d_spec != NULL)
 	      && !DECL_SAVED_TREE (decl)
 	      && DECL_STRUCT_FUNCTION (decl) == NULL))
 
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 93a41943b8c..116976cd6da 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -1481,12 +1481,16 @@ 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.  */
+  /* The debug hooks are used to implement -fdump-go-spec and -fdump-d-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 (flag_dump_d_spec != NULL)
+    debug_hooks = dump_d_spec_init (flag_dump_d_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.)  */


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2020-12-10 23:43 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-10 16:45 [gcc(refs/users/ibuclaw/heads/dumpspec)] d: Initial draft of -fdump-d-spec= Iain Buclaw
2020-12-10 23:43 Iain Buclaw

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).