diff mbox

gengtype improvements for plugins, thirdround! patch 6/7 [wstate]

Message ID 20100921213151.0332d8b3.basile@starynkevitch.net
State New
Headers show

Commit Message

Basile Starynkevitch Sept. 21, 2010, 7:31 p.m. UTC
On Tue, 21 Sep 2010 21:29:26 +0200
Basile Starynkevitch <basile@starynkevitch.net> wrote:

> 
> Hello All,
> 
> [join work by Basile Starynkevitch & Jeremie Salvucci]
> 
> References: 
>   http://gcc.gnu.org/ml/gcc-patches/2010-08/msg02060.html
>   http://gcc.gnu.org/ml/gcc-patches/2010-09/msg00616.html
>   http://gcc.gnu.org/ml/gcc-patches/2010-09/msg00663.html
>   http://gcc.gnu.org/ml/gcc-patches/2010-08/msg02063.html
>   http://gcc.gnu.org/ml/gcc-patches/2010-08/msg02065.html
>   http://gcc.gnu.org/ml/gcc-patches/2010-08/msg02068.html
>   http://gcc.gnu.org/ml/gcc-patches/2010-09/msg00983.html
>   http://gcc.gnu.org/ml/gcc-patches/2010-08/msg02069.html
>   http://gcc.gnu.org/ml/gcc-patches/2010-09/msg01150.html
>   http://gcc.gnu.org/ml/gcc-patches/2010-09/msg01153.html
>   http://gcc.gnu.org/ml/gcc-patches/2010-09/msg01713.html
> 
> The 6th part [wstate] of our patch series (thirdround) is a slightly
> improved version of
> http://gcc.gnu.org/ml/gcc-patches/2010-09/msg01150.html by adding
> comments notably taking into account Laurynas remarks in
> http://gcc.gnu.org/ml/gcc-patches/2010-09/msg01151.html &
> http://gcc.gnu.org/ml/gcc-patches/2010-09/msg01153.html
> 
> diff -u -p -N $(svn stat . |awk '/[AM]/{print $2}') \
>      --from-file ../thirdround_05_typedopt/ > \
>      $HOME/Gengtype_Thirdround/patch6_wstate-relto05.diff
> 
> #################### gcc/ChangeLog entry relative to patch piece 5 [typedopt]
> 2010-09-21  Jeremie Salvucci  <jeremie.salvucci@free.fr>
> 	    Basile Starynkevitch  <basile@starynkevitch.net>
> 
> 	* gengtype-state.c: Added new file.
> 
> 	* gengtype.c:
> 	(type_count): New static variable.
> 	(new_structure, find_structure, find_param_structure)
> 	(create_pointer, create_array): Use type_count for initializing
> 	state_number field of types.
> 	(main): Initialize state_number in predefined types.  Call
> 	read_state and write_state appropriately.  Show the
> 	type_count when verbose.
> 
> 	* gengtype.h: Updated comment about per-language directories.
> 	(read_state, write_state): New declarations.
> 
> 	* Makefile.in (MOSTLYCLEANFILES): Added gtype.state.
> 	(GENGTYPE_FLAGS): New variable.
> 	(s-gtype): Runs gengtype twice, once to write the gtype.state,
> 	once to read it.
> 	(build/gengtype-state.o): New target.
> 	(build/gengtype): Use it.
> 	(mostlyclean): Remove gtype.state
> ################################################################
> 
> 
> 
> Also, how should the gengtype program be installed? Perhaps it should
> be named gcc-gengtype?  I still need help on these minor issues, in
> particular as ways to patch gcc/Makefile.in.... There has been some
> discussions & suggestions, but I was not able to imagine a
> gcc/Makefile.in patch from them.  I confess that I don't understand all
> of gcc/Makefile.in in particular the installations tricks. 
> 
> I am as usual attaching the cumulated patch w.r.t. trunk 164437.

Sorry attachment in previous email was wrong.
http://gcc.gnu.org/ml/gcc-patches/2010-09/msg01714.html I attached by
mistake a draft version of the email, not the relative patch. Apologies!



> 
> Ok for trunk?
> 
> Cheers.
> -- 
> Basile STARYNKEVITCH         http://starynkevitch.net/Basile/
> email: basile<at>starynkevitch<dot>net mobile: +33 6 8501 2359
> 8, rue de la Faiencerie, 92340 Bourg La Reine, France
> *** opinions {are only mine, sont seulement les miennes} ***
diff mbox

Patch

--- ../thirdround_05_typedopt//gengtype-state.c	2010-09-21 19:56:44.000000000 +0200
+++ gcc/gengtype-state.c	2010-09-21 19:58:32.000000000 +0200
@@ -0,0 +1,2469 @@ 
+/* Gengtype persistent state serialization & de-serialization.
+   Useful for gengtype in plugin mode.
+
+   Copyright (C) 2010  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/>.
+
+   Contributed by Jeremie Salvucci <jeremie.salvucci@free.fr>
+   and Basile Starynkevitch <basile@starynkevitch.net>
+*/
+
+#include "bconfig.h"
+#include "system.h"
+#include "getopt.h"
+#include "errors.h"	/* For fatal.  */
+#include "double-int.h"
+#include "hashtab.h"
+#include "version.h"	/* For version_string & pkgversion_string.  */
+#include "obstack.h"
+#include "xregex.h"
+#include "gengtype.h"
+
+
+/* The state file has simplistic lispy lexical tokens.  Its lexer gives
+   a linked list of struct state_token_st, thru the peek_state_token
+   function.  Lexical tokens are consumed with next_state_tokens.  */
+
+
+/* The lexical kind of each lispy token.  */
+enum state_token_en
+{
+  STOK_NONE,			/* Never used.  */
+  STOK_INTEGER,		/* Integer token.  */
+  STOK_STRING,			/* String token.  */
+  STOK_LEFTPAR,		/* Left parenthesis.  */
+  STOK_RIGHTPAR,		/* Right parenthesis.  */
+  STOK_NAME			/* hash-consed name or identifier.  */
+};
+
+
+/* Structure and hash-table used to share identifiers or names.  */
+struct state_ident_st
+{
+  /* TODO: We could improve the parser by reserving identifiers for
+     state keywords and adding a keyword number for them.  That would
+     mean adding another field in this state_ident_st struct.  */
+  char stid_name[1];		/* actually bigger & null terminated */
+};
+static htab_t state_ident_tab;
+
+
+/* The state_token_st structure is for lexical tokens in the read
+   state file.  The stok_kind field discriminates the union.  Tokens
+   are allocated by peek_state_token which calls read_a_state_token
+   which allocate them.  Tokens are freed by calls to
+   next_state_tokens.  Token are organized in a FIFO look-ahead queue
+   filled by peek_state_token.  */
+struct state_token_st
+{
+  enum state_token_en stok_kind;	/* the lexical kind
+					   discriminates the stok_un
+					   union  */
+  int stok_line;		/* the line number */
+  int stok_col;			/* the column number */
+  const char *stok_file;	/* the file path */
+  struct state_token_st *stok_next;	/* the next token in the
+					   queue, when peeked */
+  union				/* discriminated by stok_kind! */
+  {
+    int stok_num;		/* when STOK_INTEGER */
+    char stok_string[1];	/* when STOK_STRING, actual size is
+				   bigger and null terminated */
+    struct state_ident_st *stok_ident;	/* when STOK_IDENT */
+    void *stok_ptr;		/* null otherwise */
+  }
+  stok_un;
+};
+
+
+
+
+#define NULL_STATE_TOKEN (struct state_token_st*)0
+
+/* the state_token pointer contains the leftmost current token.  The
+   tokens are organized in a linked queue, using stok_next, for token
+   look-ahead.  */
+struct state_token_st *state_token = NULL_STATE_TOKEN;
+
+/* Used by the reading lexer.  */
+static FILE *state_file;
+static const char *state_path = NULL;
+static int state_line = 0;
+static long state_bol = 0;	/* offset of beginning of line */
+
+
+/* Counter of written types.  */
+static int state_written_type_count = 0;
+
+
+/* Fatal error messages when reading the state.  They are extremely
+   unlikely, and only appear when this gengtype-state.c file is buggy,
+   or when reading a gengtype state which was not generated by the
+   same version of gengtype or GCC.  */
+
+
+/* Fatal message while reading state.  */
+#define fatal_reading_state(Tok,Msg)  do {		\
+    struct state_token_st* badtok = Tok;		\
+    if (badtok)					\
+      fatal ("%s:%d:%d: Invalid state file; " Msg,	\
+	      badtok->stok_file,			\
+	      badtok->stok_line,			\
+	      badtok->stok_col);			\
+    else						\
+      fatal ("%s:%d: Invalid state file; " Msg,	\
+	      state_path, state_line);			\
+  } while(0)
+
+#define fatal_reading_state_printf(Tok,Fmt,...) do {	\
+    struct state_token_st* badtok = Tok;		\
+    if (badtok)					\
+      fatal ("%s:%d:%d: Invalid state file; " Fmt,	\
+	      badtok->stok_file,			\
+	      badtok->stok_line,			\
+	      badtok->stok_col, __VA_ARGS__);		\
+    else						\
+      fatal ("%s:%d: Invalid state file; " Fmt,		\
+	     state_path, state_line, __VA_ARGS__);	\
+  } while(0)
+
+
+/* Find or allocate an identifier in our name hash table.  */
+static struct state_ident_st *
+state_ident_by_name (const char *name, enum insert_option optins)
+{
+  PTR *slot = NULL;
+  int namlen = 0;
+  struct state_ident_st *stid = NULL;
+
+  if (!name || !name[0])
+    return NULL;
+
+  slot = htab_find_slot (state_ident_tab, name, optins);
+  if (!slot)
+    return NULL;
+
+  namlen = strlen (name);
+  stid =
+    (struct state_ident_st *) xmalloc (sizeof (struct state_ident_st) +
+				       namlen);
+  memset (stid, 0, sizeof (struct state_ident_st) + namlen);
+  strcpy (stid->stid_name, name);
+  *slot = stid;
+
+  return stid;
+}
+
+/* Our token lexer is heavily inspired by MELT's lexer, and share some
+   code with the file gcc/melt-runtime.c of the GCC MELT branch!  We
+   really want the gengtype state to be easily parsable by MELT.  This
+   is a usual lispy lexing routine, dealing with spaces and comments,
+   numbers, parenthesis, names, strings.  */
+static struct state_token_st *
+read_a_state_token (void)
+{
+  int c = 0;
+  long curoff = 0;
+  struct state_token_st *tk = NULL;
+ again: /* Read again, e.g. after a comment.  */
+  c = getc (state_file);
+
+  if (c == EOF)
+    return NULL;
+  /* Handle spaces, count lines.  */
+  if (c == '\n')
+    {
+      state_line++;
+      state_bol = curoff = ftell (state_file);
+      goto again;
+    };
+  if (ISSPACE (c))
+    goto again;
+  /* Skip comments starting with semi-colon.  */
+  if (c == ';')
+    {	
+      do
+	{
+	  c = getc (state_file);
+	}
+      while (c > 0 && c != '\n');
+      if (c == '\n')
+	{
+	  state_line++;
+	  state_bol = curoff = ftell (state_file);
+	}
+      goto again;
+    };
+  /* Read signed numbers.  */
+  if (ISDIGIT (c) || c == '-' || c == '+')
+    {				/* number */
+      int n = 0;
+      ungetc (c, state_file);
+      curoff = ftell (state_file);
+      if (fscanf (state_file, "%d", &n) <= 0)
+	fatal_reading_state (NULL_STATE_TOKEN, "Lexical error in number");
+      tk = XCNEW (struct state_token_st);
+      tk->stok_kind = STOK_INTEGER;
+      tk->stok_line = state_line;
+      tk->stok_col = curoff - state_bol;
+      tk->stok_file = state_path;
+      tk->stok_next = NULL;
+      tk->stok_un.stok_num = n;
+
+      return tk;
+    }
+  /* Read an opening left parenthesis.  */
+  else if (c == '(')
+    {
+      curoff = ftell (state_file);
+      tk = XCNEW (struct state_token_st);
+      tk->stok_kind = STOK_LEFTPAR;
+      tk->stok_line = state_line;
+      tk->stok_col = curoff - state_bol;
+      tk->stok_file = state_path;
+      tk->stok_next = NULL;
+
+      return tk;
+    }
+  /* Read an closing right parenthesis.  */
+  else if (c == ')')
+    {
+      curoff = ftell (state_file);
+      tk = XCNEW (struct state_token_st);
+      tk->stok_kind = STOK_RIGHTPAR;
+      tk->stok_line = state_line;
+      tk->stok_col = curoff - state_bol;
+      tk->stok_file = state_path;
+      tk->stok_next = NULL;
+
+      return tk;
+    }
+  /* Read identifiers, using an obstack.  */
+  else if (ISALPHA (c) || c == '_' || c == '$' || c == '!' || c == '#')
+    {
+      struct obstack id_obstack;
+      struct state_ident_st *sid = NULL;
+      char *ids = NULL;
+      obstack_init (&id_obstack);
+      curoff = ftell (state_file);
+      while (ISALNUM (c) || c == '_' || c == '$' || c == '!' || c == '#')
+	{
+	  obstack_1grow (&id_obstack, c);
+	  c = getc (state_file);
+	  if (c < 0)
+	    break;
+	};
+      if (c >= 0)
+	ungetc (c, state_file);
+      obstack_1grow (&id_obstack, (char) 0);
+      ids = XOBFINISH (&id_obstack, char *);
+      sid = state_ident_by_name (ids, INSERT);
+      obstack_free (&id_obstack, ids);
+      ids = NULL;
+      tk = XCNEW (struct state_token_st);
+      tk->stok_kind = STOK_NAME;
+      tk->stok_line = state_line;
+      tk->stok_col = curoff - state_bol;
+      tk->stok_file = state_path;
+      tk->stok_next = NULL;
+      tk->stok_un.stok_ident = sid;
+
+      return tk;
+    }
+  /* Read a string, dealing with escape sequences a la C! */
+  else if (c == '"')
+    {
+      char *cstr = NULL;
+      int cslen = 0;
+      struct obstack bstring_obstack;
+      obstack_init (&bstring_obstack);
+      curoff = ftell (state_file);
+      while ((c = getc (state_file)) != '"' && c >= 0)
+	{
+	  if (ISPRINT (c) && c != '\\')
+	    obstack_1grow (&bstring_obstack, (char) c);
+	  else if (ISSPACE (c) && c != '\n')
+	    obstack_1grow (&bstring_obstack, (char) c);
+	  else if (c == '\\')
+	    {
+	      c = getc (state_file);
+	      switch (c)
+		{
+		case 'a':
+		  obstack_1grow (&bstring_obstack, '\a');
+		  c = getc (state_file);
+		  break;
+		case 'b':
+		  obstack_1grow (&bstring_obstack, '\b');
+		  c = getc (state_file);
+		  break;
+		case 't':
+		  obstack_1grow (&bstring_obstack, '\t');
+		  c = getc (state_file);
+		  break;
+		case 'n':
+		  obstack_1grow (&bstring_obstack, '\n');
+		  c = getc (state_file);
+		  break;
+		case 'v':
+		  obstack_1grow (&bstring_obstack, '\v');
+		  c = getc (state_file);
+		  break;
+		case 'f':
+		  obstack_1grow (&bstring_obstack, '\f');
+		  c = getc (state_file);
+		  break;
+		case 'r':
+		  obstack_1grow (&bstring_obstack, '\r');
+		  c = getc (state_file);
+		  break;
+		case '"':
+		  obstack_1grow (&bstring_obstack, '\"');
+		  c = getc (state_file);
+		  break;
+		case '\\':
+		  obstack_1grow (&bstring_obstack, '\\');
+		  c = getc (state_file);
+		  break;
+		case ' ':
+		  obstack_1grow (&bstring_obstack, ' ');
+		  c = getc (state_file);
+		  break;
+		case 'x':
+		  {
+		    unsigned int cx = 0;
+		    if (fscanf (state_file, "%02x", &cx) > 0 && cx > 0)
+		      obstack_1grow (&bstring_obstack, cx);
+		    else
+		      fatal_reading_state (NULL_STATE_TOKEN,
+					   "Lexical error in string escape");
+		    c = getc (state_file);
+		    break;
+		  }
+		default:
+		  fatal_reading_state (NULL_STATE_TOKEN,
+				       "Lexical error in string escape");
+		}
+	    }
+	  else
+	    fatal_reading_state (NULL_STATE_TOKEN, "Lexical error...");
+	};
+      if (c != '"')
+	fatal_reading_state (NULL_STATE_TOKEN, "Unterminated string");
+      obstack_1grow (&bstring_obstack, (char) 0);
+      cstr = XOBFINISH (&bstring_obstack, char *);
+      cslen = strlen (cstr);
+      tk = (struct state_token_st *)
+	xcalloc (sizeof (struct state_token_st) + cslen, 1);
+      tk->stok_kind = STOK_STRING;
+      tk->stok_line = state_line;
+      tk->stok_col = curoff - state_bol;
+      tk->stok_file = state_path;
+      tk->stok_next = NULL;
+      strcpy (tk->stok_un.stok_string, cstr);
+      obstack_free (&bstring_obstack, cstr);
+
+      return tk;
+    }
+  /* Got an unexpected character.  */
+  fatal_reading_state_printf (NULL_STATE_TOKEN, "Lexical error at offset %ld",
+			      ftell (state_file));
+}
+
+/* Used for lexical look-ahead.  Retrieves the lexical token of rank
+   DEPTH, starting with 0 when reading the state file.  Gives null on
+   end of file.  */
+static struct state_token_st *
+peek_state_token (int depth)
+{
+  int remdepth = depth;
+  struct state_token_st **ptoken = &state_token;
+  struct state_token_st *tok = NULL;
+
+  while (remdepth >= 0)
+    {
+      if (*ptoken == NULL)
+	{
+	  *ptoken = tok = read_a_state_token ();
+	  if (tok == NULL)
+	    return NULL;
+	}
+      tok = *ptoken;
+      ptoken = &((*ptoken)->stok_next);
+      remdepth--;
+    }
+
+  return tok;
+}
+
+/* Consume the next DEPTH tokens and free them.  */
+static void
+next_state_tokens (int depth)
+{
+  struct state_token_st *n;
+
+  while (depth > 0)
+    {
+      if (state_token != NULL)
+	{
+	  n = state_token->stok_next;
+	  free (state_token);
+	  state_token = n;
+	}
+      else
+	fatal_reading_state (NULL_STATE_TOKEN, "Tokens stack empty");
+
+      depth--;
+    }
+}
+
+/* Safely retrieve the lexical kind of a token.  */
+static inline enum state_token_en
+state_token_kind (struct state_token_st *p)
+{
+  if (p == NULL)
+    return STOK_NONE;
+  else
+    return p->stok_kind;
+}
+
+/* Test if a token is a given name i.e. an identifier.  */
+static inline bool
+state_token_is_name (struct state_token_st *p, const char *name)
+{
+  if (p == NULL)
+    return false;
+
+  if (p->stok_kind != STOK_NAME)
+    return false;
+
+  return !strcmp (p->stok_un.stok_ident->stid_name, name);
+}
+
+
+/* Following routines are useful for serializing datas.
+ *
+ * We want to serialize :
+ *          - typedefs list
+ *          - structures list
+ *          - param_structs list
+ *          - variables list
+ *
+ * So, we have one routine for each kind of data.  The main writing
+ * routine is write_state.  The main reading routine is
+ * read_state.  Most writing routines write_state_FOO have a
+ * corresponding reading routine read_state_FOO.  Reading is done in a
+ * recursive descending way, and any read error is fatal.
+ */
+
+/* When reading the state, we need to remember the previously seen
+   types by their state_number, since GTY-ed types are usually
+   shared.  */
+static htab_t state_seen_types;
+
+/* Return the length of a linked list made of pairs.  */
+static int pair_list_length (pair_p list);
+
+/* Write a pair */
+static void write_state_pair (pair_p);
+
+/* return the number of pairs written.  Should match the length given
+   by pair_list_length.  */
+static int write_state_pair_list (pair_p list);
+
+/* Write a type.  When a type is written, its state_number is updated,
+   to ensure that a "reference" to a seen type is written on next
+   occurrences.  */
+static void write_state_type (type_p);
+
+/* Write a null-terminatel string using our Lispy lexical conventions,
+   similar to those of C or MELT.  */
+static void write_state_a_string (const char *s);
+
+/* Compute the length of a list of pairs, starting from the first
+   one.  */
+static int
+pair_list_length (pair_p list)
+{
+  int nbpair = 0;
+  pair_p l = NULL;
+  for (l = list; l; l = l->next)
+    nbpair++;
+  return nbpair;
+}
+
+/* Write a file location.  Files relative to $(srcdir) are quite
+   frequent and are handled specially.  This ensures that two gengtype
+   state file-s produced by gengtype on the same GCC source tree are
+   very similar and can be reasonably compared with diff, even if the
+   two GCC source trees have different absolute paths.  */
+static void
+write_state_fileloc (struct fileloc *floc)
+{
+
+  if (floc != NULL && floc->line > 0)
+    {
+      const char *srcrelpath = NULL;
+      gcc_assert (floc->file != NULL);
+      /* Most of the files are inside $(srcdir) so it is worth to
+         handle them specially.  */
+      srcrelpath = get_file_srcdir_relative_path (floc->file);
+      if (srcrelpath != NULL)
+	{
+	  fprintf (state_file, "\n(!srcfileloc ");
+	  write_state_a_string (srcrelpath);
+	}
+      else
+	{
+	  fprintf (state_file, "\n(!fileloc ");
+	  write_state_a_string (get_input_file_name (floc->file));
+	}
+      fprintf (state_file, " %d", floc->line);
+      fprintf (state_file, ")\n");
+    }
+  else
+    fprintf (state_file, "nil ");
+}
+
+/* Write a list of fields.  */
+static void
+write_state_fields (pair_p fields)
+{
+  int nbfields = pair_list_length (fields);
+  int nbpairs = 0;
+  fprintf (state_file, "\n(!fields %d ", nbfields);
+  nbpairs = write_state_pair_list (fields);
+  gcc_assert (nbpairs == nbfields);
+  fprintf (state_file, ")\n");
+}
+
+/* Write a null-terminated string in our lexical convention, very
+   similar to the convention of C.  */
+static void
+write_state_a_string (const char *s)
+{
+  char c;
+
+  fputs (" \"", state_file);
+  for (; *s != 0; s++)
+    {
+      c = *s;
+      switch (c)
+	{
+	case '\a':
+	  fputs ("\\a", state_file);
+	  break;
+	case '\b':
+	  fputs ("\\b", state_file);
+	  break;
+	case '\t':
+	  fputs ("\\t", state_file);
+	  break;
+	case '\n':
+	  fputs ("\\n", state_file);
+	  break;
+	case '\v':
+	  fputs ("\\v", state_file);
+	  break;
+	case '\f':
+	  fputs ("\\f", state_file);
+	  break;
+	case '\r':
+	  fputs ("\\r", state_file);
+	  break;
+	case '\"':
+	  fputs ("\\\"", state_file);
+	  break;
+	case '\\':
+	  fputs ("\\\\", state_file);
+	  break;
+	default:
+	  if (ISPRINT (c))
+	    putc (c, state_file);
+	  else
+	    fprintf (state_file, "\\x%02x", (unsigned) c);
+	}
+    }
+  fputs ("\"", state_file);
+}
+
+/* Our option-s have three kinds, each with its writer.  */
+static void
+write_state_string_option (options_p current)
+{
+  fprintf (state_file, "string ");
+  if (current->info.string != NULL)
+    write_state_a_string (current->info.string);
+  else
+    fprintf (state_file, " nil ");
+}
+
+static void
+write_state_type_option (options_p current)
+{
+  fprintf (state_file, "type ");
+  write_state_type (current->info.type);
+}
+
+static void
+write_state_nested_option (options_p current)
+{
+  fprintf (state_file, "nested ");
+  write_state_type (current->info.nested->type);
+  if (current->info.nested->convert_from != NULL)
+    write_state_a_string (current->info.nested->convert_from);
+  else
+    fprintf (state_file, " nil ");
+
+  if (current->info.nested->convert_to != NULL)
+    write_state_a_string (current->info.nested->convert_to);
+  else
+    fprintf (state_file, " nil ");
+}
+
+static void
+write_state_option (options_p current)
+{
+  fprintf (state_file, "\n(!option ");
+
+  if (current->name != NULL)
+    fprintf (state_file, "%s ", current->name);
+  else
+    fprintf (state_file, "nil ");
+
+  switch (current->kind)
+    {
+    case OPTION_STRING:
+      write_state_string_option (current);
+      break;
+    case OPTION_TYPE:
+      write_state_type_option (current);
+      break;
+    case OPTION_NESTED:
+      write_state_nested_option (current);
+      break;
+    default:
+      fatal ("Option tag unknown");
+    }
+
+  fprintf (state_file, ")\n");
+}
+
+
+
+/* Write a list of GTY options.  */
+static void
+write_state_options (options_p opt)
+{
+  options_p current;
+
+  if (opt == NULL)
+    {
+      fprintf (state_file, "nil ");
+      return;
+    }
+
+  fprintf (state_file, "\n(!options ");
+  for (current = opt; current != NULL; current = current->next)
+      write_state_option (current);
+  fprintf (state_file, ")\n");
+}
+
+
+/* Write a bitmap representing a set of GCC front-end languages.  */
+static void
+write_state_lang_bitmap (lang_bitmap bitmap)
+{
+  fprintf (state_file, "%d ", (int) bitmap);
+}
+
+/* Write version information.  */
+static void
+write_state_version (const char *version)
+{
+  fprintf (state_file, "\n(!version ");
+  write_state_a_string (version);
+  fprintf (state_file, ")\n");
+}
+
+/* Common routine to write the common content of all types.  */
+static void write_state_common_type_content (type_p current);
+
+/* Write a scalar type.  We have only two of these.  */
+static void
+write_state_scalar_type (type_p current)
+{
+  if (current == &scalar_nonchar)
+    fprintf (state_file, "scalar_nonchar ");
+  else if (current == &scalar_char)
+    fprintf (state_file, "scalar_char ");
+  else
+    fatal ("Unexpected type in write_state_scalar_type");
+
+  write_state_common_type_content (current);
+}
+
+/* Write the string type.  There is only one such thing! */
+static void
+write_state_string_type (type_p current)
+{
+  if (current == &string_type)
+    {
+      fprintf (state_file, "string ");
+      write_state_common_type_content (current);
+    }
+  else
+    fatal ("Unexpected type in write_state_string_type");
+}
+
+
+/* Common code to write structure like types.  */
+static void
+write_state_struct_union_type (type_p current, const char *kindstr)
+{
+  DBGPRINTF ("%s type @ %p #%d '%s'", kindstr, (void *) current,
+	     current->state_number, current->u.s.tag);
+  fprintf (state_file, "%s ", kindstr);
+  write_state_common_type_content (current);
+  if (current->u.s.tag != NULL)
+    write_state_a_string (current->u.s.tag);
+  else
+    fprintf (state_file, "nil");
+
+  write_state_fileloc (type_lineloc (current));
+  write_state_fields (current->u.s.fields);
+  write_state_options (current->u.s.opt);
+  write_state_lang_bitmap (current->u.s.bitmap);
+}
+
+
+/* Write a GTY struct type.  */
+static void
+write_state_struct_type (type_p current)
+{
+  write_state_struct_union_type (current, "struct");
+  write_state_type (current->u.s.lang_struct);
+}
+
+/* write a GTY union type.  */
+static void
+write_state_union_type (type_p current)
+{
+  write_state_struct_union_type (current, "union");
+  write_state_type (current->u.s.lang_struct);
+}
+
+/* Write a lang_struct type.  This is tricky and was painful to debug,
+   we deal with the next field specifically within their lang_struct
+   subfield, which points to a linked list of homonumous types.
+   Change this function with extreme care, see also
+   read_state_lang_struct_type.  */
+static void
+write_state_lang_struct_type (type_p current)
+{
+  int nbhomontype = 0;
+  type_p hty = NULL;
+  const char *homoname = 0;
+  write_state_struct_union_type (current, "lang_struct");
+  /* lang_struct-ures are particularily tricky, since their
+     u.s.lang_struct field gives a list of homonymous struct-s or
+     union-s! */
+  DBGPRINTF ("lang_struct @ %p #%d", (void *) current, current->state_number);
+  for (hty = current->u.s.lang_struct; hty != NULL; hty = hty->next)
+    {
+      nbhomontype++;
+      DBGPRINTF ("homonymous #%d hty @ %p #%d '%s'", nbhomontype,
+		 (void *) hty, hty->state_number, hty->u.s.tag);
+      /* Every member of the homonymous list should have the same tag.  */
+      gcc_assert (UNION_OR_STRUCT_P (hty));
+      gcc_assert (hty->u.s.lang_struct == current);
+      if (!homoname)
+	homoname = hty->u.s.tag;
+      gcc_assert (strcmp (homoname, hty->u.s.tag) == 0);
+    }
+  fprintf (state_file, "(!homotypes %d\n", nbhomontype);
+  for (hty = current->u.s.lang_struct; hty != NULL; hty = hty->next)
+    write_state_type (hty);
+  fprintf (state_file, ")\n");
+}
+
+/* Write a parametrized structure GTY type.  */
+static void
+write_state_param_struct_type (type_p current)
+{
+  int i;
+
+  fprintf (state_file, "param_struct ");
+  write_state_common_type_content (current);
+  write_state_type (current->u.param_struct.stru);
+  for (i = 0; i < NUM_PARAM; i++)
+    {
+      if (current->u.param_struct.param[i] != NULL)
+	write_state_type (current->u.param_struct.param[i]);
+      else
+	fprintf (state_file, "nil ");
+    }
+  write_state_fileloc (&current->u.param_struct.line);
+}
+
+/* Write a pointer type.  */
+static void
+write_state_pointer_type (type_p current)
+{
+  fprintf (state_file, "pointer ");
+  write_state_common_type_content (current);
+  write_state_type (current->u.p);
+}
+
+/* Write an array type.  */
+static void
+write_state_array_type (type_p current)
+{
+  fprintf (state_file, "array ");
+  write_state_common_type_content (current);
+  if (current->u.a.len != NULL)
+    write_state_a_string (current->u.a.len);
+  else
+    fprintf (state_file, " nil");
+
+  fprintf (state_file, " ");
+  write_state_type (current->u.a.p);
+}
+
+/* Write the gc_used information.  */
+static void
+write_state_gc_used (enum gc_used_enum gus)
+{
+  switch (gus)
+    {
+    case GC_UNUSED:
+      fprintf (state_file, " gc_unused");
+      break;
+    case GC_USED:
+      fprintf (state_file, " gc_used");
+      break;
+    case GC_MAYBE_POINTED_TO:
+      fprintf (state_file, " gc_maybe_pointed_to");
+      break;
+    case GC_POINTED_TO:
+      fprintf (state_file, " gc_pointed_to");
+      break;
+    }
+}
+
+/* Utility routine to write the common content of all types.  Notice
+   that the next field is *not* written on purpose.  */
+static void
+write_state_common_type_content (type_p current)
+{
+  fprintf (state_file, "%d ", current->state_number);
+  /* We do not write the next type, because list of types are
+     explicitly written.  However, lang_struct are special in that
+     respect.  See function write_state_lang_struct_type for more.  */
+  write_state_type (current->pointer_to);
+  write_state_gc_used (current->gc_used);
+}
+
+
+/* The important and recursive routine writing GTY types as understood
+   by gengtype.  Types which have a positive state_number have already
+   been seen and written.  */
+static void
+write_state_type (type_p current)
+{
+  if (current == NULL)
+    {
+      fprintf (state_file, "nil ");
+      return;
+    }
+
+  fprintf (state_file, "\n(!type ");
+
+  if (current->state_number > 0)
+    fprintf (state_file, "already_seen %d", current->state_number);
+  else
+    {
+      state_written_type_count++;
+      DBGPRINTF ("writing type #%d @%p old number %d", state_written_type_count,
+		 (void *) current, current->state_number);
+      current->state_number = state_written_type_count;
+      switch (current->kind)
+	{
+	case TYPE_STRUCT:
+	  write_state_struct_type (current);
+	  break;
+	case TYPE_UNION:
+	  write_state_union_type (current);
+	  break;
+	case TYPE_POINTER:
+	  write_state_pointer_type (current);
+	  break;
+	case TYPE_ARRAY:
+	  write_state_array_type (current);
+	  break;
+	case TYPE_LANG_STRUCT:
+	  write_state_lang_struct_type (current);
+	  break;
+	case TYPE_PARAM_STRUCT:
+	  write_state_param_struct_type (current);
+	  break;
+	case TYPE_SCALAR:
+	  write_state_scalar_type (current);
+	  break;
+	case TYPE_STRING:
+	  write_state_string_type (current);
+	  break;
+
+	default:
+	  fatal ("Unexpected type...");
+	}
+    }
+
+  fprintf (state_file, ")\n");
+}
+
+
+/* Write a pair.  */
+static void
+write_state_pair (pair_p current)
+{
+  if (current == NULL)
+    {
+      fprintf (state_file, "nil)");
+      return;
+    }
+
+  fprintf (state_file, "\n(!pair ");
+
+  if (current->name != NULL)
+    write_state_a_string (current->name);
+  else
+    write_state_a_string ("nil");
+
+  write_state_type (current->type);
+  write_state_fileloc (&(current->line));
+  write_state_options (current->opt);
+
+  fprintf (state_file, ")");
+}
+
+/* Write a pair list and return the number of pairs written.  */
+static int
+write_state_pair_list (pair_p list)
+{
+  int nbpair = 0;
+  pair_p current;
+
+  for (current = list; current != NULL; current = current->next)
+    {
+      write_state_pair (current);
+      nbpair++;
+    }
+  return nbpair;
+
+}
+
+/* When writting imported linked lists, like typedefs, structures,
+   param_structs, ... we count their length first and write it.  These
+   eases the reading, and enables an extra verification on the number
+   of actually read items.  */
+
+/* Write our typedefs.  */
+static void
+write_state_typedefs (void)
+{
+  int nbtypedefs = pair_list_length (typedefs);
+  int nbpairs = 0;
+  fprintf (state_file, "\n(!typedefs %d\n", nbtypedefs);
+  nbpairs = write_state_pair_list (typedefs);
+  gcc_assert (nbpairs == nbtypedefs);
+  fprintf (state_file, ")\n");
+  if (verbosity_level >= 2)
+    printf ("%s wrote %d typedefs\n", progname, nbtypedefs);
+}
+
+/* Write our structures.  */
+static void
+write_state_structures (void)
+{
+  int nbstruct = 0;
+  type_p current;
+
+  for (current = structures; current != NULL; current = current->next)
+    nbstruct++;
+
+  fprintf (state_file, "\n(!structures %d\n", nbstruct);
+
+  for (current = structures; current != NULL; current = current->next)
+    write_state_type (current);
+
+  fprintf (state_file, ")\n");
+  if (verbosity_level >= 2)
+    printf ("%s wrote %d structures in state\n", progname, nbstruct);
+}
+
+/* Write our param_struct-s.  */
+static void
+write_state_param_structs (void)
+{
+  int nbparamstruct = 0;
+  type_p current;
+
+  for (current = param_structs; current != NULL; current = current->next)
+    nbparamstruct++;
+
+  fprintf (state_file, "\n(!param_structs %d\n", nbparamstruct);
+
+  for (current = param_structs; current != NULL; current = current->next)
+    write_state_type (current);
+
+  fprintf (state_file, ")\n");
+}
+
+/* Write our variables.  */
+static void
+write_state_variables (void)
+{
+  int nbvars = pair_list_length (variables);
+  int nbpairs = 0;
+  fprintf (state_file, "\n(!variables %d\n", nbvars);
+  nbpairs = write_state_pair_list (variables);
+  gcc_assert (nbpairs == nbvars);
+  fprintf (state_file, ")\n");
+  if (verbosity_level >= 2)
+    printf ("%s wrote %d variables.\n", progname, nbvars);
+}
+
+/* Write the source directory.  File locations within the source
+   directory have been written specifically.  */
+static void
+write_state_srcdir (void)
+{
+  fprintf (state_file, "\n(!srcdir ");
+  write_state_a_string (srcdir);
+  fprintf (state_file, ")\n");
+}
+
+/* Count and write the list of our files.  */
+static void
+write_state_files_list (void)
+{
+  int i = 0;
+  /* Write the list of files with their lang_bitmap.  */
+  fprintf (state_file, "\n(!fileslist %d\n", (int) num_gt_files);
+  for (i = 0; i < (int) num_gt_files; i++)
+    {
+      const char *cursrcrelpath = NULL;
+      input_file *curfil = gt_files[i];
+      /* Most of the files are inside $(srcdir) so it is worth to
+         handle them specially.  */
+      cursrcrelpath = get_file_srcdir_relative_path (curfil);
+      if (cursrcrelpath)
+	{
+	  fprintf (state_file, "(!srcfile %d ", get_lang_bitmap (curfil));
+	  write_state_a_string (cursrcrelpath);
+	}
+      else
+	{
+	  fprintf (state_file, "(!file %d ", get_lang_bitmap (curfil));
+	  write_state_a_string (get_input_file_name (curfil));
+	}
+      fprintf (state_file, ")\n");
+    }
+  fprintf (state_file, ")\n");
+}
+
+/* Write the list of GCC front-end languages.  */
+static void
+write_state_languages (void)
+{
+  int i = 0;
+  fprintf (state_file, "\n(!languages %d", (int) num_lang_dirs);
+  for (i = 0; i < (int) num_lang_dirs; i++)
+    {
+      /* Languages names are identifiers, we expect only letters or
+         underscores or digits in them.  In particular, C++ is not a
+         valid language name, but cp is valid.  */
+      fprintf (state_file, " %s", lang_dir_names[i]);
+    }
+  fprintf (state_file, ")\n");
+}
+
+/* Write the trailer.  */
+static void
+write_state_trailer (void)
+{
+  /* This test should probably catch IO errors like disk full...  */
+  if (fputs ("\n(!endfile)\n", state_file) == EOF)
+    fatal ("failed to write state trailer [%s]", xstrerror (errno));
+}
+
+/* The write_state routine is the only writing routine called by main
+   in gengtype.c.  To avoid messing the state if gengtype is
+   interrupted or aborted, we write a temporary file and rename it
+   after having written it in totality.  */
+void
+write_state (const char *state_path)
+{
+  long statelen = 0;
+  time_t now = 0;
+  char *temp_state_path = NULL;
+  char tempsuffix[40];
+  time (&now);
+
+  /* We write a unique temporary file which is renamed when complete
+   * only.  So even if gengtype is interrupted, the written state file
+   * won't be partially written, since the temporary file is not yet
+   * renamed in that case....  */
+  memset (tempsuffix, 0, sizeof (tempsuffix));
+  snprintf (tempsuffix, sizeof (tempsuffix) - 1, "-%ld-%d.tmp", (long) now,
+	    (int) getpid ());
+  temp_state_path = concat (state_path, tempsuffix, NULL);
+  state_file = fopen (temp_state_path, "w");
+  if (state_file == NULL)
+    fatal ("Failed to open file %s for writing state: %s",
+	   temp_state_path, xstrerror (errno));
+  if (verbosity_level >= 3)
+    printf ("%s writing state file %s temporarily in %s\n",
+	    progname, state_path, temp_state_path);
+  /* This is the first line of the state.  Perhaps the file utility
+     could know about that, so don't change it often.  */
+  fprintf (state_file, ";;; @@@ GCC gengtype state\n");
+  fprintf (state_file,
+	   ";;; DON'T EDIT THIS FILE, since generated by GCC's gengtype\n");
+  fprintf (state_file,
+	   ";;; The format of this file is tied to a particular version of GCC.\n");
+  fprintf (state_file,
+	   ";;; Don't parse this file wihout knowing GCC gengtype internals.\n");
+  fprintf (state_file,
+	   ";;; This file should be parsed by the same %s who wrote it.\n",
+	   progname);
+  fprintf (state_file, ";;; file %s generated on %s\n", state_path,
+	   ctime (&now));
+  /* The first non-comment significant line gives the version string.  */
+  write_state_version (version_string);
+  write_state_srcdir ();
+  write_state_languages ();
+  write_state_files_list ();
+  write_state_structures ();
+  /* The writing of structures probably wrote a lot, so we flush to
+     disk and check for errors, e.g. to catch disk full situations a
+     bit earlier.  */
+  if (fflush (state_file) || ferror (state_file))
+    fatal ("output error when writing state file %s [%s]",
+	   temp_state_path, xstrerror (errno));
+  write_state_typedefs ();
+  write_state_param_structs ();
+  write_state_variables ();
+  write_state_trailer ();
+  statelen = ftell (state_file);
+  if (ferror (state_file))
+    fatal ("output error when writing state file %s [%s]",
+	   temp_state_path, xstrerror (errno));
+  if (fclose (state_file))
+    fatal ("failed to close state file %s [%s]",
+	   temp_state_path, xstrerror (errno));
+  if (rename (temp_state_path, state_path))
+    fatal ("failed to rename %s to state file %s [%s]", temp_state_path,
+	   state_path, xstrerror (errno));
+  free (temp_state_path);
+
+  if (verbosity_level >= 1)
+    printf ("%s wrote state file %s of %ld bytes with %d GTY-ed types\n",
+	    progname, state_path, statelen, state_written_type_count);
+
+}
+
+/*****************************************************************
+ * End of writing routines!  The corresponding reading routines follow.
+ *****************************************************************/
+
+
+/* Forward declarations, since some read_state_* functions are
+   recursive! */
+static void read_state_fileloc (struct fileloc *line);
+static void read_state_options (options_p *opt);
+static void read_state_type (type_p *current);
+static void read_state_pair (pair_p *pair);
+/* Return the number of pairs actually read.  */
+static int read_state_pair_list (pair_p *list);
+static void read_state_fields (pair_p *fields);
+static void read_state_common_type_content (type_p current);
+
+
+
+
+/* Record into the state_seen_types hash-table a type which we are
+   reading, to enable recursive or circular references to it.  */
+static void
+record_type (type_p type)
+{
+  PTR *slot;
+
+  slot = htab_find_slot (state_seen_types, type, INSERT);
+  gcc_assert (slot);
+
+  *slot = type;
+}
+
+/* Read an already seen type.  */
+static void
+read_state_already_seen_type (type_p *type)
+{
+  struct state_token_st *t0;
+
+  t0 = peek_state_token (0);
+
+  if (state_token_kind (t0) == STOK_INTEGER)
+    {
+      PTR *slot = NULL;
+      struct type loctype = { TYPE_SCALAR, 0, 0, 0, GC_UNUSED, {0} };
+
+      loctype.state_number = t0->stok_un.stok_num;
+      slot = htab_find_slot (state_seen_types, &loctype, NO_INSERT);
+      if (slot == NULL)
+	{
+	  fatal_reading_state (t0, "Unknown type");
+	}
+
+      next_state_tokens (1);
+      *type = (type_p) *slot;
+    }
+  else
+    {
+      fatal_reading_state (t0, "Bad seen type");
+    }
+}
+
+
+/* Read the scalar_nonchar type.  */
+static void
+read_state_scalar_nonchar_type (type_p *type)
+{
+  *type = &scalar_nonchar;
+  read_state_common_type_content (*type);
+}
+
+
+/* Read the scalar_char type.  */
+static void
+read_state_scalar_char_type (type_p *type)
+{
+  *type = &scalar_char;
+  read_state_common_type_content (*type);
+}
+
+
+/* Read the string_type.  */
+static void
+read_state_string_type (type_p *type)
+{
+  *type = &string_type;
+  read_state_common_type_content (*type);
+}
+
+
+/* Read a lang_bitmap representing a set of GCC front-end languages.  */
+static void
+read_state_lang_bitmap (lang_bitmap *bitmap)
+{
+  struct state_token_st *t;
+
+  t = peek_state_token (0);
+  if (state_token_kind (t) == STOK_INTEGER)
+    {
+      *bitmap = t->stok_un.stok_num;
+      next_state_tokens (1);
+    }
+  else
+    {
+      fatal_reading_state (t, "Bad syntax for bitmap");
+    }
+}
+
+
+/* Read a GTY-ed struct type.  */
+static void
+read_state_struct_type (type_p type)
+{
+  struct state_token_st *t0;
+
+  type->kind = TYPE_STRUCT;
+  read_state_common_type_content (type);
+  t0 = peek_state_token (0);
+  if (state_token_kind (t0) == STOK_STRING)
+    {
+      if (state_token_is_name (t0, "nil"))
+	{
+	  type->u.s.tag = NULL;
+	  DBGPRINTF ("read anonymous struct type @%p #%d",
+		     (void *) type, type->state_number);
+	}
+      else
+	{
+	  type->u.s.tag = xstrdup (t0->stok_un.stok_string);
+	  DBGPRINTF ("read struct type @%p #%d '%s'",
+		     (void *) type, type->state_number, type->u.s.tag);
+	}
+
+      next_state_tokens (1);
+      read_state_fileloc (&(type->u.s.line));
+      read_state_fields (&(type->u.s.fields));
+      read_state_options (&(type->u.s.opt));
+      read_state_lang_bitmap (&(type->u.s.bitmap));
+      read_state_type (&(type->u.s.lang_struct));
+    }
+  else
+    {
+      fatal_reading_state (t0, "Bad tag in struct type");
+    }
+}
+
+
+/* Read a GTY-ed union type.  */
+static void
+read_state_union_type (type_p type)
+{
+  struct state_token_st *t0;
+
+  type->kind = TYPE_UNION;
+  read_state_common_type_content (type);
+  t0 = peek_state_token (0);
+  if (state_token_kind (t0) == STOK_STRING)
+    {
+      if (state_token_is_name (t0, "nil"))
+	{
+	  type->u.s.tag = NULL;
+	  DBGPRINTF ("read anonymous union type @%p #%d",
+		     (void *) type, type->state_number);
+	}
+      else
+	{
+	  type->u.s.tag = xstrdup (t0->stok_un.stok_string);
+	  DBGPRINTF ("read union type @%p #%d '%s'",
+		     (void *) type, type->state_number, type->u.s.tag);
+	}
+      next_state_tokens (1);
+      read_state_fileloc (&(type->u.s.line));
+      read_state_fields (&(type->u.s.fields));
+      read_state_options (&(type->u.s.opt));
+      read_state_lang_bitmap (&(type->u.s.bitmap));
+      read_state_type (&(type->u.s.lang_struct));
+    }
+  else
+    fatal_reading_state (t0, "Bad tag in union type");
+}
+
+
+/* Read a GTY-ed pointer type.  */
+static void
+read_state_pointer_type (type_p type)
+{
+  type->kind = TYPE_POINTER;
+  read_state_common_type_content (type);
+  DBGPRINTF ("read pointer type @%p #%d", (void *) type, type->state_number);
+  read_state_type (&(type->u.p));
+}
+
+
+/* Read a GTY-ed array type.  */
+static void
+read_state_array_type (type_p type)
+{
+  struct state_token_st *t0;
+
+  type->kind = TYPE_ARRAY;
+  read_state_common_type_content (type);
+  t0 = peek_state_token (0);
+  if (state_token_kind (t0) == STOK_STRING)
+    {
+      type->u.a.len = xstrdup (t0->stok_un.stok_string);
+      DBGPRINTF ("read array type @%p #%d length '%s'",
+		 (void *) type, type->state_number, type->u.a.len);
+      next_state_tokens (1);
+    }
+
+  else if (state_token_is_name (t0, "nil"))
+    {
+      type->u.a.len = NULL;
+      DBGPRINTF ("read array type @%p #%d without length",
+		 (void *) type, type->state_number);
+      next_state_tokens (1);
+    }
+
+  else
+    fatal_reading_state (t0, "Bad array name type");
+  read_state_type (&(type->u.a.p));
+}
+
+
+
+/* Read a lang_struct type for GTY-ed struct-s which depends upon GCC
+   front-end languages.  This is a tricky function and it was painful
+   to debug.  Change it with extreme care.  See also
+   write_state_lang_struct_type.  */
+static void
+read_state_lang_struct_type (type_p type)
+{
+  struct state_token_st *t0 = NULL;
+  struct state_token_st *t1 = NULL;
+  struct state_token_st *t2 = NULL;
+
+  type->kind = TYPE_LANG_STRUCT;
+  read_state_common_type_content (type);
+  t0 = peek_state_token (0);
+  if (state_token_kind (t0) == STOK_STRING)
+    {
+      if (state_token_is_name (t0, "nil"))
+	{
+	  DBGPRINTF ("read anonymous lang_struct type @%p #%d",
+		     (void *) type, type->state_number);
+	  type->u.s.tag = NULL;
+	}
+      else
+	{
+	  type->u.s.tag = xstrdup (t0->stok_un.stok_string);
+	  DBGPRINTF ("read lang_struct type @%p #%d '%s'",
+		     (void *) type, type->state_number, type->u.s.tag);
+	}
+      next_state_tokens (1);
+    }
+  else
+    fatal_reading_state (t0, "Bad tag in lang struct type");
+  read_state_fileloc (&(type->u.s.line));
+  read_state_fields (&(type->u.s.fields));
+  read_state_options (&(type->u.s.opt));
+  read_state_lang_bitmap (&(type->u.s.bitmap));
+  /* Within lang_struct-ures, the lang_struct field is a linked list
+     of homonymous types! */
+  t0 = peek_state_token (0);
+  t1 = peek_state_token (1);
+  t2 = peek_state_token (2);
+  /* Parse (!homotypes <number-types> <type-1> .... <type-n>) */
+  if (state_token_kind (t0) == STOK_LEFTPAR
+      && state_token_is_name (t1, "!homotypes")
+      && state_token_kind (t2) == STOK_INTEGER)
+    {
+      type_p *prevty = &type->u.s.lang_struct;
+      int nbhomotype = t2->stok_un.stok_num;
+      int i = 0;
+      t0 = t1 = t2 = NULL;
+      next_state_tokens (3);
+      for (i = 0; i < nbhomotype; i++)
+	{
+	  read_state_type (prevty);
+	  t0 = peek_state_token (0);
+	  if (*prevty)
+	    prevty = &(*prevty)->next;
+	  else
+	      fatal_reading_state (t0,
+				   "expecting type in homotype list for lang_struct");
+	};
+      if (state_token_kind (t0) != STOK_RIGHTPAR)
+	fatal_reading_state (t0,
+			     "expecting ) in homotype list for lang_struct");
+      next_state_tokens (1);
+    }
+  else
+    fatal_reading_state (t0, "expecting !homotypes for lang_struct");
+}
+
+
+/* Read a param_struct type for GTY parametrized structures.  */
+static void
+read_state_param_struct_type (type_p type)
+{
+  int i;
+  struct state_token_st *t0;
+
+  type->kind = TYPE_PARAM_STRUCT;
+  read_state_common_type_content (type);
+  DBGPRINTF ("read param_struct type @%p #%d",
+	     (void *) type, type->state_number);
+  read_state_type (&(type->u.param_struct.stru));
+
+  for (i = 0; i < NUM_PARAM; i++)
+    {
+      t0 = peek_state_token (0);
+      if (state_token_is_name (t0, "nil"))
+	{
+	  type->u.param_struct.param[i] = NULL;
+	  next_state_tokens (1);
+	}
+      else
+	read_state_type (&(type->u.param_struct.param[i]));
+    }
+  read_state_fileloc (&(type->u.param_struct.line));
+}
+
+
+/* Read the gc used information.  */
+static void
+read_state_gc_used (enum gc_used_enum *pgus)
+{
+  struct state_token_st *t0;
+  t0 = peek_state_token (0);
+  if (state_token_is_name (t0, "gc_unused"))
+    *pgus = GC_UNUSED;
+  else if (state_token_is_name (t0, "gc_used"))
+    *pgus = GC_USED;
+  else if (state_token_is_name (t0, "gc_maybe_pointed_to"))
+    *pgus = GC_MAYBE_POINTED_TO;
+  else if (state_token_is_name (t0, "gc_pointed_to"))
+    *pgus = GC_POINTED_TO;
+  else
+    fatal_reading_state (t0, "invalid gc_used information");
+  next_state_tokens (1);
+}
+
+
+/* Utility function to read the common content of types.  */
+static void
+read_state_common_type_content (type_p current)
+{
+  struct state_token_st *t0;
+
+  t0 = peek_state_token (0);
+
+  if (state_token_kind (t0) == STOK_INTEGER)
+    {
+      current->state_number = t0->stok_un.stok_num;
+      next_state_tokens (1);
+      record_type (current);
+    }
+  else
+      fatal_reading_state_printf (t0,
+				  "Expected integer for state_number line %d",
+				  state_line);
+  /* We don't read the next field of the type.  */
+  read_state_type (&current->pointer_to);
+  read_state_gc_used (&current->gc_used);
+}
+
+
+/* Read a GTY-ed type.  */
+void
+read_state_type (type_p *current)
+{
+  struct state_token_st *t0;
+  struct state_token_st *t1;
+
+  t0 = peek_state_token (0);
+  t1 = peek_state_token (1);
+
+  if (state_token_kind (t0) == STOK_LEFTPAR &&
+      state_token_is_name (t1, "!type"))
+    {
+      next_state_tokens (2);
+      t0 = peek_state_token (0);
+      if (state_token_is_name (t0, "already_seen"))
+	{
+	  next_state_tokens (1);
+	  read_state_already_seen_type (current);
+	}
+      else
+	{
+	  t0 = peek_state_token (0);
+
+	  if (state_token_is_name (t0, "scalar_nonchar"))
+	    {
+	      next_state_tokens (1);
+	      read_state_scalar_nonchar_type (current);
+	    }
+	  else if (state_token_is_name (t0, "scalar_char"))
+	    {
+	      next_state_tokens (1);
+	      read_state_scalar_char_type (current);
+	    }
+	  else if (state_token_is_name (t0, "string"))
+	    {
+	      next_state_tokens (1);
+	      read_state_string_type (current);
+	    }
+	  else if (state_token_is_name (t0, "struct"))
+	    {
+	      *current = XCNEW (struct type);
+	      next_state_tokens (1);
+	      read_state_struct_type (*current);
+	    }
+	  else if (state_token_is_name (t0, "union"))
+	    {
+	      *current = XCNEW (struct type);
+	      next_state_tokens (1);
+	      read_state_union_type (*current);
+	    }
+	  else if (state_token_is_name (t0, "lang_struct"))
+	    {
+	      *current = XCNEW (struct type);
+	      next_state_tokens (1);
+	      read_state_lang_struct_type (*current);
+	    }
+	  else if (state_token_is_name (t0, "param_struct"))
+	    {
+	      *current = XCNEW (struct type);
+	      next_state_tokens (1);
+	      read_state_param_struct_type (*current);
+	    }
+	  else if (state_token_is_name (t0, "pointer"))
+	    {
+	      *current = XCNEW (struct type);
+	      next_state_tokens (1);
+	      read_state_pointer_type (*current);
+	    }
+	  else if (state_token_is_name (t0, "array"))
+	    {
+	      *current = XCNEW (struct type);
+	      next_state_tokens (1);
+	      read_state_array_type (*current);
+	    }
+	  else
+	    fatal_reading_state (t0, "bad type in (!type");
+	}
+      t0 = peek_state_token (0);
+      if (state_token_kind (t0) != STOK_RIGHTPAR)
+	fatal_reading_state (t0, "missing ) in type");
+      next_state_tokens (1);
+    }
+  else if (state_token_is_name (t0, "nil"))
+    {
+      next_state_tokens (1);
+      *current = NULL;
+    }
+  else
+    fatal_reading_state (t0, "bad type syntax");
+}
+
+
+/* Read a file location.  Files within the source directory are dealt
+   with specifically.  */
+void
+read_state_fileloc (struct fileloc *floc)
+{
+  bool issrcfile = false;
+  struct state_token_st *t0;
+  struct state_token_st *t1;
+
+  gcc_assert (floc != NULL);
+  gcc_assert (srcdir != NULL);
+
+  t0 = peek_state_token (0);
+  t1 = peek_state_token (1);
+
+  if (state_token_kind (t0) == STOK_LEFTPAR &&
+      (state_token_is_name (t1, "!fileloc")
+       || (issrcfile = state_token_is_name (t1, "!srcfileloc"))))
+    {
+      next_state_tokens (2);
+      t0 = peek_state_token (0);
+      t1 = peek_state_token (1);
+      if (state_token_kind (t0) == STOK_STRING &&
+	  state_token_kind (t1) == STOK_INTEGER)
+	{
+	  char *path = t0->stok_un.stok_string;
+	  if (issrcfile)
+	    {
+	      static const char dirsepstr[2] = { DIR_SEPARATOR, (char) 0 };
+	      char *fullpath = concat (srcdir, dirsepstr, path, NULL);
+	      floc->file = input_file_by_name (fullpath);
+	      free (fullpath);
+	    }
+	  else
+	    floc->file = input_file_by_name (path);
+	  floc->line = t1->stok_un.stok_num;
+	  next_state_tokens (2);
+	}
+      else
+	fatal_reading_state (t0,
+			     "Bad fileloc syntax, expected path string and line");
+      t0 = peek_state_token (0);
+      if (state_token_kind (t0) != STOK_RIGHTPAR)
+	fatal_reading_state (t0, "Bad fileloc syntax, expected )");
+      next_state_tokens (1);
+    }
+  else if (state_token_is_name (t0, "nil"))
+    {
+      next_state_tokens (1);
+      floc->file = NULL;
+      floc->line = 0;
+    }
+  else
+    fatal_reading_state (t0, "Bad fileloc syntax");
+}
+
+
+/* Read the fields of a GTY-ed type.  */
+void
+read_state_fields (pair_p *fields)
+{
+  pair_p tmp = NULL;
+  struct state_token_st *t0;
+  struct state_token_st *t1;
+  struct state_token_st *t2;
+
+  t0 = peek_state_token (0);
+  t1 = peek_state_token (1);
+  t2 = peek_state_token (2);
+
+  if (state_token_kind (t0) == STOK_LEFTPAR
+      && state_token_is_name (t1, "!fields")
+      && state_token_kind (t2) == STOK_INTEGER)
+    {
+      int nbfields = t2->stok_un.stok_num;
+      int nbpairs = 0;
+      next_state_tokens (3);
+      nbpairs = read_state_pair_list (&tmp);
+      t0 = peek_state_token (0);
+      if (nbpairs != nbfields)
+	fatal_reading_state_printf
+	  (t0,
+	   "Mismatched fields number, expected %d got %d", nbpairs, nbfields);
+      if (state_token_kind (t0) == STOK_RIGHTPAR)
+	next_state_tokens (1);
+      else
+	fatal_reading_state (t0, "Bad fields expecting )");
+    }
+
+  *fields = tmp;
+}
+
+
+/* Read a string option.  */
+static void
+read_state_string_option (options_p opt)
+{
+  struct state_token_st *t0;
+
+  t0 = peek_state_token (0);
+  opt->kind = OPTION_STRING;
+  if (state_token_kind (t0) == STOK_STRING)
+    {
+      opt->info.string = xstrdup (t0->stok_un.stok_string);
+      next_state_tokens (1);
+    }
+  else if (state_token_is_name (t0, "nil"))
+    {
+      opt->info.string = NULL;
+      next_state_tokens (1);
+    }
+  else
+    fatal_reading_state (t0, "Missing name in string option");
+}
+
+
+/* Read a type option.  */
+static void
+read_state_type_option (options_p opt)
+{
+  opt->kind = OPTION_TYPE;
+  read_state_type (&(opt->info.type));
+}
+
+
+/* Read a nested option.  */
+static void
+read_state_nested_option (options_p opt)
+{
+  struct state_token_st *t0;
+
+  opt->info.nested = XCNEW (struct nested_ptr_data);
+  opt->kind = OPTION_NESTED;
+  read_state_type (&(opt->info.nested->type));
+  t0 = peek_state_token (0);
+  if (state_token_kind (t0) == STOK_STRING)
+    {
+      opt->info.nested->convert_from = xstrdup (t0->stok_un.stok_string);
+      next_state_tokens (1);
+    }
+  else if (state_token_is_name (t0, "nil"))
+    {
+      opt->info.nested->convert_from = NULL;
+      next_state_tokens (1);
+    }
+  else
+    fatal_reading_state (t0, "Bad nested convert_from option");
+
+  t0 = peek_state_token (0);
+  if (state_token_kind (t0) == STOK_STRING)
+    {
+      opt->info.nested->convert_to = xstrdup (t0->stok_un.stok_string);
+      next_state_tokens (1);
+    }
+  else if (state_token_is_name (t0, "nil"))
+    {
+      opt->info.nested->convert_to = NULL;
+      next_state_tokens (1);
+    }
+  else
+    fatal_reading_state (t0, "Bad nested convert_from option");
+}
+
+
+/* Read an GTY option.  */
+static void
+read_state_option (options_p *opt)
+{
+  struct state_token_st *t0;
+  struct state_token_st *t1;
+
+  t0 = peek_state_token (0);
+  t1 = peek_state_token (1);
+
+  if (state_token_kind (t0) == STOK_LEFTPAR &&
+      state_token_is_name (t1, "!option"))
+    {
+      next_state_tokens (2);
+      t0 = peek_state_token (0);
+      if (state_token_kind (t0) == STOK_NAME)
+	{
+	  *opt = XCNEW (struct options);
+	  if (state_token_is_name (t0, "nil"))
+	    (*opt)->name = NULL;
+	  else
+	    (*opt)->name = t0->stok_un.stok_ident->stid_name;
+	  next_state_tokens (1);
+	  t0 = peek_state_token (0);
+	  if (state_token_kind (t0) == STOK_NAME)
+	    {
+	      if (state_token_is_name (t0, "string"))
+		{
+		  next_state_tokens (1);
+		  read_state_string_option (*opt);
+		}
+	      else if (state_token_is_name (t0, "type"))
+		{
+		  next_state_tokens (1);
+		  read_state_type_option (*opt);
+		}
+	      else if (state_token_is_name (t0, "nested"))
+		{
+		  next_state_tokens (1);
+		  read_state_nested_option (*opt);
+		}
+	      else
+		fatal_reading_state (t0, "Bad option type");
+	      t0 = peek_state_token (0);
+	      if (state_token_kind (t0) != STOK_RIGHTPAR)
+		fatal_reading_state (t0, "Bad syntax in option, expecting )");
+
+	      next_state_tokens (1);
+	    }
+	  else
+	    fatal_reading_state (t0, "Missing option type");
+	}
+      else
+	fatal_reading_state (t0, "Bad name for option");
+    }
+  else
+    fatal_reading_state (t0, "Bad option, waiting for )");
+}
+
+/* Read a list of options.  */
+void
+read_state_options (options_p *opt)
+{
+  options_p head = NULL;
+  options_p previous = NULL;
+  options_p current_option = NULL;
+  struct state_token_st *t0;
+  struct state_token_st *t1;
+
+  t0 = peek_state_token (0);
+  t1 = peek_state_token (1);
+
+  if (state_token_kind (t0) == STOK_LEFTPAR &&
+      state_token_is_name (t1, "!options"))
+    {
+      next_state_tokens (2);
+      t0 = peek_state_token (0);
+      while (state_token_kind (t0) != STOK_RIGHTPAR)
+	{
+	  read_state_option (&current_option);
+	  if (head == NULL)
+	    {
+	      head = current_option;
+	      previous = head;
+	    }
+	  else
+	    {
+	      previous->next = current_option;
+	      previous = current_option;
+	    }
+	  t0 = peek_state_token (0);
+	}
+      next_state_tokens (1);
+    }
+  else if (state_token_is_name (t0, "nil"))
+    {
+      next_state_tokens (1);
+    }
+  else
+    fatal_reading_state (t0, "Bad options syntax");
+
+  *opt = head;
+}
+
+
+/* Read a version, and check against the version of the gengtype.  */
+static void
+read_state_version (const char *version_string)
+{
+  struct state_token_st *t0;
+  struct state_token_st *t1;
+
+  t0 = peek_state_token (0);
+  t1 = peek_state_token (1);
+
+  if (state_token_kind (t0) == STOK_LEFTPAR &&
+      state_token_is_name (t1, "!version"))
+    {
+      next_state_tokens (2);
+      t0 = peek_state_token (0);
+      t1 = peek_state_token (1);
+      if (state_token_kind (t0) == STOK_STRING &&
+	  state_token_kind (t1) == STOK_RIGHTPAR)
+	{
+	  /* Check that the read version string is the same as current
+	     version.  */
+	  if (strcmp (version_string, t0->stok_un.stok_string))
+	    fatal_reading_state_printf (t0,
+					"version string mismatch; expecting %s but got %s",
+					version_string,
+					t0->stok_un.stok_string);
+	  next_state_tokens (2);
+	}
+      else
+	fatal_reading_state (t0, "Missing version or right parenthesis");
+    }
+  else
+    fatal_reading_state (t0, "Bad version syntax");
+}
+
+
+/* Read a pair.  */
+void
+read_state_pair (pair_p *current)
+{
+  struct state_token_st *t0;
+  struct state_token_st *t1;
+
+  t0 = peek_state_token (0);
+  t1 = peek_state_token (1);
+  if (state_token_kind (t0) == STOK_LEFTPAR &&
+      state_token_is_name (t1, "!pair"))
+    {
+      *current = XCNEW (struct pair);
+      next_state_tokens (2);
+      t0 = peek_state_token (0);
+      if (state_token_kind (t0) == STOK_STRING)
+	{
+	  if (strcmp (t0->stok_un.stok_string, "nil") == 0)
+	    {
+	      (*current)->name = NULL;
+	    }
+	  else
+	    {
+	      (*current)->name = xstrdup (t0->stok_un.stok_string);
+	    }
+	  next_state_tokens (1);
+	  read_state_type (&((*current)->type));
+	  read_state_fileloc (&((*current)->line));
+	  read_state_options (&((*current)->opt));;
+	  t0 = peek_state_token (0);
+	  if (state_token_kind (t0) == STOK_RIGHTPAR)
+	    {
+	      next_state_tokens (1);
+	    }
+	  else
+	    {
+	      fatal_reading_state (t0, "Bad syntax for pair, )");
+	    }
+	}
+      else
+	{
+	  fatal_reading_state (t0, "Bad name for pair");
+	}
+    }
+  else if (state_token_kind (t0) == STOK_NAME &&
+	   state_token_is_name (t0, "nil"))
+    {
+      next_state_tokens (1);
+      *current = NULL;
+    }
+  else
+    fatal_reading_state_printf (t0, "Bad syntax for pair, (!pair %d",
+				state_token->stok_kind);
+}
+
+
+/* Return the number of pairs actually read.  */
+int
+read_state_pair_list (pair_p *list)
+{
+  int nbpair = 0;
+  pair_p head = NULL;
+  pair_p previous = NULL;
+  pair_p tmp = NULL;
+  struct state_token_st *t0;
+  t0 = peek_state_token (0);
+  while (t0 && state_token_kind (t0) != STOK_RIGHTPAR)
+    {
+      read_state_pair (&tmp);
+      if (head == NULL)
+	{
+	  head = tmp;
+	  previous = head;
+	}
+      else
+	{
+	  previous->next = tmp;
+	  previous = tmp;
+	}
+      t0 = peek_state_token (0);
+      nbpair++;
+    }
+
+  /* don't consume the ); the caller will eat it.  */
+  *list = head;
+  return nbpair;
+}
+
+/* Read the typedefs.  */
+static void
+read_state_typedefs (pair_p *typedefs)
+{
+  int nbtypedefs = 0;
+  pair_p list = NULL;
+  struct state_token_st *t0;
+  struct state_token_st *t1;
+  struct state_token_st *t2;
+
+  t0 = peek_state_token (0);
+  t1 = peek_state_token (1);
+  t2 = peek_state_token (2);
+
+  if (state_token_kind (t0) == STOK_LEFTPAR
+      && state_token_is_name (t1, "!typedefs")
+      && state_token_kind (t2) == STOK_INTEGER)
+    {
+      int nbpairs = 0;
+      nbtypedefs = t2->stok_un.stok_num;
+      next_state_tokens (3);
+      nbpairs = read_state_pair_list (&list);
+      t0 = peek_state_token (0);
+      if (nbpairs != nbtypedefs)
+	fatal_reading_state_printf
+	  (t0,
+	   "invalid number of typedefs, expected %d but got %d",
+	   nbtypedefs, nbpairs);
+      if (state_token_kind (t0) == STOK_RIGHTPAR)
+	next_state_tokens (1);
+      else
+	fatal_reading_state (t0, "Bad typedefs syntax )");
+    }
+  else
+    fatal_reading_state (t0, "Bad typedefs syntax (!typedefs");
+
+  if (verbosity_level >= 2)
+    printf ("%s read %d typedefs from state\n", progname, nbtypedefs);
+  *typedefs = list;
+}
+
+
+/* Read the structures.  */
+static void
+read_state_structures (type_p *structures)
+{
+  type_p head = NULL;
+  type_p previous;
+  type_p tmp;
+  int nbstruct = 0, countstruct = 0;
+  struct state_token_st *t0;
+  struct state_token_st *t1;
+  struct state_token_st *t2;
+
+  t0 = peek_state_token (0);
+  t1 = peek_state_token (1);
+  t2 = peek_state_token (2);
+
+  if (state_token_kind (t0) == STOK_LEFTPAR
+      && state_token_is_name (t1, "!structures")
+      && state_token_kind (t2) == STOK_INTEGER)
+    {
+      nbstruct = t2->stok_un.stok_num;
+      next_state_tokens (3);
+      t0 = peek_state_token (0);
+      while (t0 && state_token_kind (t0) != STOK_RIGHTPAR)
+	{
+	  tmp = NULL;
+	  read_state_type (&tmp);
+	  countstruct++;
+	  if (head == NULL)
+	    {
+	      head = tmp;
+	      previous = head;
+	    }
+	  else
+	    {
+	      previous->next = tmp;
+	      previous = tmp;
+	    }
+	  t0 = peek_state_token (0);
+	}
+      next_state_tokens (1);
+    }
+  else
+    fatal_reading_state (t0, "Bad structures syntax");
+  if (countstruct != nbstruct)
+    fatal_reading_state_printf (NULL_STATE_TOKEN, 
+				"expected %d structures but got %d",
+				nbstruct, countstruct);
+  if (verbosity_level >= 2)
+    printf ("%s read %d structures from state\n", progname, nbstruct);
+  *structures = head;
+}
+
+
+/* Read the param_struct-s.  */
+static void
+read_state_param_structs (type_p *param_structs)
+{
+  int nbparamstructs = 0;
+  int countparamstructs = 0;
+  type_p head = NULL;
+  type_p previous;
+  type_p tmp;
+  struct state_token_st *t0;
+  struct state_token_st *t1;
+  struct state_token_st *t2;
+
+  t0 = peek_state_token (0);
+  t1 = peek_state_token (1);
+  t2 = peek_state_token (2);
+
+  if (state_token_kind (t0) == STOK_LEFTPAR
+      && state_token_is_name (t1, "!param_structs")
+      && state_token_kind (t2) == STOK_INTEGER)
+    {
+      nbparamstructs = t2->stok_un.stok_num;
+      next_state_tokens (3);
+      t0 = t1 = t2 = NULL;
+      t0 = peek_state_token (0);
+      while (state_token_kind (t0) != STOK_RIGHTPAR)
+	{
+	  tmp = NULL;
+	  read_state_type (&tmp);
+	  if (head == NULL)
+	    {
+	      head = tmp;
+	      previous = head;
+	    }
+	  else
+	    {
+	      previous->next = tmp;
+	      previous = tmp;
+	    }
+	  t0 = peek_state_token (0);
+	  countparamstructs++;
+	}
+      next_state_tokens (1);
+    }
+  else
+    fatal_reading_state (t0, "Bad param_structs syntax");
+  t0 = peek_state_token (0);
+  if (countparamstructs != nbparamstructs)
+    fatal_reading_state_printf
+      (t0,
+       "invalid number of param_structs expected %d got %d",
+       nbparamstructs, countparamstructs);
+  *param_structs = head;
+}
+
+
+/* Read the variables.  */
+static void
+read_state_variables (pair_p *variables)
+{
+  pair_p list = NULL;
+  int nbvars = 0;
+  struct state_token_st *t0;
+  struct state_token_st *t1;
+  struct state_token_st *t2;
+
+  t0 = peek_state_token (0);
+  t1 = peek_state_token (1);
+  t2 = peek_state_token (2);
+
+  if (state_token_kind (t0) == STOK_LEFTPAR
+      && state_token_is_name (t1, "!variables")
+      && state_token_kind (t2) == STOK_INTEGER)
+    {
+      int nbpairs = 0;
+      nbvars = t2->stok_un.stok_num;
+      next_state_tokens (3);
+      nbpairs = read_state_pair_list (&list);
+      t0 = peek_state_token (0);
+      if (nbpairs != nbvars)
+	fatal_reading_state_printf
+	  (t0, "Invalid number of variables, expected %d but got %d",
+	   nbvars, nbpairs);
+      if (state_token_kind (t0) == STOK_RIGHTPAR)
+	next_state_tokens (1);
+      else
+	fatal_reading_state (t0, "Waiting for ) in variables");
+    }
+  else
+    fatal_reading_state (t0, "Bad variables syntax");
+  *variables = list;
+  if (verbosity_level >= 2)
+    printf ("%s read %d variables from state\n", progname, nbvars);
+}
+
+
+/* Read the source directory.  */
+static void
+read_state_srcdir (void)
+{
+  struct state_token_st *t0 = NULL;
+  struct state_token_st *t1 = NULL;
+
+  t0 = peek_state_token (0);
+  t1 = peek_state_token (1);
+  if (state_token_kind (t0) == STOK_LEFTPAR &&
+      state_token_is_name (t1, "!srcdir"))
+    {
+      next_state_tokens (2);
+      t0 = peek_state_token (0);
+      t1 = peek_state_token (1);
+      if (state_token_kind (t0) == STOK_STRING &&
+	  state_token_kind (t1) == STOK_RIGHTPAR)
+	{
+	  srcdir = xstrdup (t0->stok_un.stok_string);
+	  srcdir_len = strlen (srcdir);
+	  next_state_tokens (2);
+	  return;
+	}
+    }
+
+  fatal_reading_state (t0, "Bad srcdir in state_file");
+}
+
+
+/* Read the sequence of GCC front-end languages.  */
+static void
+read_state_languages (void)
+{
+  struct state_token_st *t0 = NULL;
+  struct state_token_st *t1 = NULL;
+  struct state_token_st *t2 = NULL;
+  t0 = peek_state_token (0);
+  t1 = peek_state_token (1);
+  t2 = peek_state_token (2);
+  if (state_token_kind (t0) == STOK_LEFTPAR
+      && state_token_is_name (t1, "!languages")
+      && state_token_kind (t2) == STOK_INTEGER)
+    {
+      int i = 0;
+      num_lang_dirs = t2->stok_un.stok_num;
+      lang_dir_names = XCNEWVEC (const char *, num_lang_dirs);
+      next_state_tokens (3);
+      t0 = t1 = t2 = NULL;
+      for (i = 0; i < (int) num_lang_dirs; i++)
+	{
+	  t0 = peek_state_token (0);
+	  if (state_token_kind (t0) != STOK_NAME)
+	    fatal_reading_state (t0, "expecting language name in state file");
+	  lang_dir_names[i] = t0->stok_un.stok_ident->stid_name;
+	  next_state_tokens (1);
+	}
+      t0 = peek_state_token (0);
+      if (state_token_kind (t0) != STOK_RIGHTPAR)
+	fatal_reading_state (t0, "missing ) in languages list of state file");
+      next_state_tokens (1);
+    }
+  else
+    fatal_reading_state (t0, "expecting languages list in state file");
+
+}
+
+/* Read the sequences of files.  */
+static void
+read_state_files_list (void)
+{
+  struct state_token_st *t0 = NULL;
+  struct state_token_st *t1 = NULL;
+  struct state_token_st *t2 = NULL;
+
+  t0 = peek_state_token (0);
+  t1 = peek_state_token (1);
+  t2 = peek_state_token (2);
+
+  if (state_token_kind (t0) == STOK_LEFTPAR
+      && state_token_is_name (t1, "!fileslist")
+      && state_token_kind (t2) == STOK_INTEGER)
+    {
+      int i = 0;
+      num_gt_files = t2->stok_un.stok_num;
+      next_state_tokens (3);
+      t0 = t1 = t2 = NULL;
+      gt_files = XCNEWVEC (input_file *, num_gt_files);
+      for (i = 0; i < (int) num_gt_files; i++)
+	{
+	  bool issrcfile = FALSE;
+	  t0 = t1 = t2 = NULL;
+	  t0 = peek_state_token (0);
+	  t1 = peek_state_token (1);
+	  t2 = peek_state_token (2);
+	  if (state_token_kind (t0) == STOK_LEFTPAR
+	      && (state_token_is_name (t1, "!file")
+		  || (issrcfile = state_token_is_name (t1, "!srcfile")))
+	      && state_token_kind (t2) == STOK_INTEGER)
+	    {
+	      lang_bitmap bmap = t2->stok_un.stok_num;
+	      next_state_tokens (3);
+	      t0 = t1 = t2 = NULL;
+	      t0 = peek_state_token (0);
+	      t1 = peek_state_token (1);
+	      if (state_token_kind (t0) == STOK_STRING
+		  && state_token_kind (t1) == STOK_RIGHTPAR)
+		{
+		  const char *fnam = t0->stok_un.stok_string;
+		  /* Allocate & fill a gt_file entry with space for the lang_bitmap before! */
+		  input_file *curgt = NULL;
+		  if (issrcfile)
+		    {
+		      static const char dirsepstr[2] =
+			{ DIR_SEPARATOR, (char) 0 };
+		      char *fullpath = concat (srcdir, dirsepstr, fnam, NULL);
+		      curgt = input_file_by_name (fullpath);
+		      free (fullpath);
+		    }
+		  else
+		    curgt = input_file_by_name (fnam);
+		  set_lang_bitmap (curgt, bmap);
+		  gt_files[i] = curgt;
+		  next_state_tokens (2);
+		}
+	      else
+		fatal_reading_state (t0,
+				     "bad file in !fileslist of state file");
+	    }
+	  else
+	    fatal_reading_state (t0,
+				 "expecting file in !fileslist of state file");
+	};
+      t0 = peek_state_token (0);
+      if (!state_token_kind (t0) == STOK_RIGHTPAR)
+	fatal_reading_state (t0, "missing ) for !fileslist in state file");
+      next_state_tokens (1);
+    }
+  else
+    fatal_reading_state (t0, "missing !fileslist in state file");
+}
+
+
+/* Read the trailer.  */
+static void
+read_state_trailer (void)
+{
+  struct state_token_st *t0 = NULL;
+  struct state_token_st *t1 = NULL;
+  struct state_token_st *t2 = NULL;
+
+  t0 = peek_state_token (0);
+  t1 = peek_state_token (1);
+  t2 = peek_state_token (2);
+
+  if (state_token_kind (t0) == STOK_LEFTPAR
+      && state_token_is_name (t1, "!endfile")
+      && state_token_kind (t2) == STOK_RIGHTPAR)
+    next_state_tokens (3);
+  else
+    fatal_reading_state (t0, "missing !endfile in state file");
+}
+
+
+/* Utility functions for the state_seen_types hash table.  */
+static unsigned
+hash_type_number (const void *ty)
+{
+  const struct type *type = (const struct type *) ty;
+
+  return type->state_number;
+}
+
+static int
+equals_type_number (const void *ty1, const void *ty2)
+{
+  const struct type *type1 = (const struct type *) ty1;
+  const struct type *type2 = (const struct type *) ty2;
+
+  return type1->state_number == type2->state_number;
+}
+
+
+/* The function reading the state, called by main from gengtype.c.  */
+void
+read_state (const char *path)
+{
+  state_file = fopen (path, "r");
+  if (state_file == NULL)
+    fatal ("Failed to open state file %s for reading [%s]", path,
+	   xstrerror (errno));
+  state_path = path;
+  state_line = 1;
+
+  if (verbosity_level >= 1)
+    {
+      printf ("%s reading state file %s;", progname, state_path);
+      if (verbosity_level >= 2)
+	putchar ('\n');
+      fflush (stdout);
+    }
+
+  state_seen_types =
+    htab_create (2017, hash_type_number, equals_type_number, NULL);
+  state_ident_tab =
+    htab_create (4027, htab_hash_string, (htab_eq) strcmp, NULL);
+  read_state_version (version_string);
+  read_state_srcdir ();
+  read_state_languages ();
+  read_state_files_list ();
+  read_state_structures (&structures);
+  if (ferror (state_file))
+    fatal_reading_state_printf
+      (NULL_STATE_TOKEN, "input error while reading state [%s]",
+       xstrerror (errno));
+  read_state_typedefs (&typedefs);
+  read_state_param_structs (&param_structs);
+  read_state_variables (&variables);
+  read_state_trailer ();
+
+  if (verbosity_level >= 1)
+    {
+      printf ("%s read %ld bytes.\n", progname, ftell (state_file));
+      fflush (stdout);
+    };
+
+  if (fclose (state_file))
+    fatal ("failed to close read state file %s [%s]",
+	   path, xstrerror (errno));
+  state_file = NULL;
+  state_path = NULL;
+}
+
+/* End of file gengtype-state.c.  */
--- ../thirdround_05_typedopt//gengtype.c	2010-09-21 16:16:19.000000000 +0200
+++ gcc/gengtype.c	2010-09-21 19:40:11.000000000 +0200
@@ -70,6 +70,10 @@  int do_debug;
 /* For verbose messages to the user.  */
 int verbosity_level;
 
+/* We have a type count and use it to set the state_number of newly
+   allocated types to some unique negative number.  */
+static int type_count;
+
 /* The backup directory should be in the same file-system as the
    generated files, otherwise the rename(2) system call would fail.
    If NULL, no backup is made when overwriting a generated file.  */
@@ -572,12 +576,14 @@  new_structure (const char *name, int isu
 	else if (si->u.s.line.file != NULL && si->u.s.bitmap != bitmap)
 	  {
 	    ls = si;
+	    type_count++;
 	    si = XCNEW (struct type);
 	    memcpy (si, ls, sizeof (struct type));
 	    ls->kind = TYPE_LANG_STRUCT;
 	    ls->u.s.lang_struct = si;
 	    ls->u.s.fields = NULL;
 	    si->next = NULL;
+	    si->state_number = -type_count;
 	    si->pointer_to = NULL;
 	    si->u.s.lang_struct = ls;
 	  }
@@ -587,6 +593,8 @@  new_structure (const char *name, int isu
 	if (ls != NULL && s == NULL)
 	  {
 	    s = XCNEW (struct type);
+	    type_count++;
+	    s->state_number = -type_count;
 	    s->next = ls->u.s.lang_struct;
 	    ls->u.s.lang_struct = s;
 	    s->u.s.lang_struct = ls;
@@ -598,6 +606,8 @@  new_structure (const char *name, int isu
     {
       s = XCNEW (struct type);
       s->next = structures;
+      type_count++;
+      s->state_number = -type_count;
       structures = s;
     }
 
@@ -634,11 +644,12 @@  find_structure (const char *name, int is
     if (strcmp (name, s->u.s.tag) == 0
 	&& UNION_P (s) == isunion)
       return s;
-
-  s = XCNEW (struct type);
+   s = XCNEW (struct type);
+  type_count++;
   s->next = structures;
   structures = s;
   s->kind = isunion ? TYPE_UNION : TYPE_STRUCT;
+  s->state_number = -type_count;
   s->u.s.tag = name;
   structures = s;
   return s;
@@ -661,8 +672,10 @@  find_param_structure (type_p t, type_p p
   if (res == NULL)
     {
       res = XCNEW (struct type);
+      type_count++;
       res->kind = TYPE_PARAM_STRUCT;
       res->next = param_structs;
+      res->state_number = -type_count;
       param_structs = res;
       res->u.param_struct.stru = t;
       memcpy (res->u.param_struct.param, param, sizeof (type_p) * NUM_PARAM);
@@ -689,7 +702,9 @@  create_pointer (type_p t)
   if (! t->pointer_to)
     {
       type_p r = XCNEW (struct type);
+      type_count++;
       r->kind = TYPE_POINTER;
+      r->state_number = -type_count;
       r->u.p = t;
       t->pointer_to = r;
     }
@@ -703,8 +718,10 @@  create_array (type_p t, const char *len)
 {
   type_p v;
 
-  v = XCNEW (struct type);
+   v = XCNEW (struct type);
   v->kind = TYPE_ARRAY;
+  type_count++;
+  v->state_number = -type_count;
   v->u.a.p = t;
   v->u.a.len = len;
   return v;
@@ -4571,8 +4588,10 @@  dump_structures (const char * name, type
   printf ("End of %s\n\n", name);
 }
 
-/* Dumps the internal structures of gengtype.  */
-
+/* Dumps the internal structures of gengtype. This can be used to
+   debug gengtype itself, or perhaps to understand what is happenning,
+   e.g. the role of some difficult to grasp GTY-s.  So it might be
+   useful for plugin or GCC developers.  */
 static void
 dump_everything (void)
 {
@@ -4787,8 +4806,12 @@  main (int argc, char **argv)
   static struct fileloc pos = { NULL, 0 };
   outf_p output_header;
 
-  /* Mandatory common initializations.  */
+   /* Mandatory common initializations.  */
   progname = "gengtype"; /* For fatal and messages.  */
+  /* Initialize the state number of statically predefined types.  */
+  string_type.state_number = - (++type_count);
+  scalar_nonchar.state_number = - (++type_count);
+  scalar_char.state_number = - (++type_count);
   /* Set the scalar_is_char union number for predefined scalar types.  */
   scalar_nonchar.u.scalar_is_char = FALSE;
   scalar_char.u.scalar_is_char = TRUE;
@@ -4815,8 +4838,12 @@  main (int argc, char **argv)
   DBGPRINTF ("inputlist %s", inputlist);
   if (read_state_filename)
     {
-      fatal ("read state %s not implemented yet", read_state_filename);
-      /* TODO: implement read state.  */
+      if (inputlist)
+	fatal ("input list %s cannot be given with a read state file %s",
+	       inputlist, read_state_filename);
+      read_state (read_state_filename);
+      DBGPRINT_COUNT_TYPE ("structures after read_state", structures);
+      DBGPRINT_COUNT_TYPE ("param_structs after read_state", param_structs);
     }
   else if (inputlist)
     {
@@ -4844,9 +4871,9 @@  main (int argc, char **argv)
       DBGPRINT_COUNT_TYPE ("structures after parsing", structures);
       DBGPRINT_COUNT_TYPE ("param_structs after parsing", param_structs);
       if (verbosity_level >= 1)
-	printf ("%s parsed %d files\n", progname, (int) num_gt_files);
-
-    }
+	printf ("%s parsed %d files with %d GTY types\n",
+		progname, (int) num_gt_files, type_count);
+     }
   else
     fatal ("either an input list or a read state file should be given");
   if (hit_error)
@@ -4893,11 +4920,30 @@  main (int argc, char **argv)
      happen before writing the state.  */
   set_gc_used (variables);
 
-  /* We should write the state here, but it is not yet implemented.  */
+
+  /* We write the state here.  It could eventually happen that the
+     state file is written after some plugin files have been parsed,
+     perhaps to enlarge the state file for other plugins needs.  But
+     this is an uncommon scenario.  */
   if (write_state_filename)
     {
-      fatal ("write state %s in not yet implemented", write_state_filename);
-      /* TODO: implement write state.  */
+      DBGPRINT_COUNT_TYPE ("structures before writestate", structures);
+      DBGPRINT_COUNT_TYPE ("param_structs before writestate", param_structs);
+      /* We definitely don't want to write a state file if some error
+	 occurred while reading input files processed by gengtype,
+	 because we only want to write sane state files!  */
+      if (hit_error)
+	fatal ("won't write state %s after errors", write_state_filename);
+      write_state (write_state_filename);
+      if (do_dump)
+	dump_everything ();
+
+      /* After having written the state file we return immediately to
+	 avoid generating any output file.  */
+      if (hit_error)
+	return 1;
+      else
+	return 0;
     }
 
 
--- ../thirdround_05_typedopt//gengtype.h	2010-09-21 16:11:46.000000000 +0200
+++ gcc/gengtype.h	2010-09-21 19:40:29.000000000 +0200
@@ -434,6 +434,13 @@  extern size_t srcdir_len;
 extern const char *read_state_filename;
 extern const char *write_state_filename;
 
+/* Functions reading and writing the entire gengtype state, called from
+   main, and implemented in file gengtype-state.c.  */
+void read_state (const char* path);
+/* Write the state, and update the state_number field in types.  */
+void write_state (const char* path);
+
+
 /* Print an error message.  */
 extern void error_at_line
   (const struct fileloc *pos, const char *msg, ...) ATTRIBUTE_PRINTF_2;
--- ../thirdround_05_typedopt//Makefile.in	2010-09-20 21:13:15.000000000 +0200
+++ gcc/Makefile.in	2010-09-21 19:41:05.000000000 +0200
@@ -1492,7 +1492,7 @@  MOSTLYCLEANFILES = insn-flags.h insn-con
  insn-attr.h insn-attrtab.c insn-opinit.c insn-preds.c insn-constants.h \
  tm-preds.h tm-constrs.h \
  tree-check.h min-insn-modes.c insn-modes.c insn-modes.h \
- genrtl.h gt-*.h gtype-*.h gtype-desc.c gtyp-input.list \
+ genrtl.h gt-*.h gtype-*.h gtype-desc.c gtyp-input.list gtype.state \
  xgcc$(exeext) cpp$(exeext) cc1$(exeext) cc1*-dummy$(exeext) $(EXTRA_PASSES) \
  $(EXTRA_PARTS) $(EXTRA_PROGRAMS) gcc-cross$(exeext) \
  $(SPECS) collect2$(exeext) lto-wrapper$(exeext) \
@@ -3798,6 +3798,9 @@  ALL_GTFILES_H := $(sort $(GTFILES_H) $(G
 
 $(ALL_GTFILES_H) gtype-desc.c gtype-desc.h : s-gtype ; @true
 
+## Common flags to gengtype [e.g. -v for verbosity or -B . for backups].
+GENGTYPE_FLAGS = 
+
 gtyp-input.list: s-gtyp-input ; @true
 s-gtyp-input: Makefile
 	@: $(call write_entries_to_file,$(GTFILES),tmp-gi.list)
@@ -3806,7 +3809,13 @@  s-gtyp-input: Makefile
 
 s-gtype: build/gengtype$(build_exeext) $(filter-out [%], $(GTFILES)) \
 	 gtyp-input.list
-	$(RUN_GEN) build/gengtype$(build_exeext) -S $(srcdir) -I gtyp-input.list
+# First, parse all files and save a state file.
+	$(RUN_GEN) build/gengtype$(build_exeext) $(GENGTYPE_FLAGS) \
+                    -S $(srcdir) -I gtyp-input.list -w gtype.state
+# Second, read the state file and generate all files.  This ensure that
+# gtype.state is correctly read:
+	$(RUN_GEN) build/gengtype$(build_exeext) $(GENGTYPE_FLAGS) \
+                    -r gtype.state
 	$(STAMP) s-gtype
 
 generated_files = config.h tm.h $(TM_P_H) $(TM_H) multilib.h \
@@ -3902,6 +3911,8 @@  build/gengenrtl.o : gengenrtl.c $(BCONFI
 build/gengtype-lex.o : gengtype-lex.c gengtype.h $(BCONFIG_H) $(SYSTEM_H)
 build/gengtype-parse.o : gengtype-parse.c gengtype.h $(BCONFIG_H)	\
   $(SYSTEM_H)
+build/gengtype-state.o : gengtype-state.c gengtype.h $(BCONFIG_H)	\
+  $(SYSTEM_H) errors.h
 build/gengtype.o : gengtype.c $(BCONFIG_H) $(SYSTEM_H) gengtype.h 	\
   rtl.def insn-notes.def errors.h double-int.h $(HASHTAB_H)
 build/genmddeps.o: genmddeps.c $(BCONFIG_H) $(SYSTEM_H) coretypes.h	\
@@ -3948,7 +3959,7 @@  build/genautomata$(build_exeext) : BUILD
 
 # These programs are not linked with the MD reader.
 build/gengtype$(build_exeext) : build/gengtype-lex.o build/gengtype-parse.o \
-              build/version.o
+              build/gengtype-state.o build/version.o
 build/genhooks$(build_exeext) : $(BUILD_ERRORS)
 
 # Generated source files for gengtype.
@@ -4404,6 +4415,7 @@  mostlyclean: lang.mostlyclean
 # Delete files generated by gengtype.c
 	-rm -f gtype-*
 	-rm -f gt-*
+	-rm gtype.state
 # Delete genchecksum outputs
 	-rm -f *-checksum.c