This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

gengtype plugin improvement last4round -patch 6 [wstate]


Hello All,

References:
http://gcc.gnu.org/ml/gcc-patches/2010-09/msg01716.html
http://gcc.gnu.org/ml/gcc-patches/2010-09/msg01745.html
http://gcc.gnu.org/ml/gcc-patches/2010-11/msg02350.html
http://gcc.gnu.org/ml/gcc-patches/2010-11/msg02321.html
and all the messages referenced by above messages.

Patch for trunk rev 167136 adding the write & reread of state files for
gengtype to be usable by plugin developers.

I am attaching the diff file gengtype-wstate-r167136.diff and the
gcc/ChangeLog entry file gengtype-wstate-r167136.ChangeLog

Some comments about Laurynas remarks in
http://gcc.gnu.org/ml/gcc-patches/2010-09/msg01745.html

> 2010/9/21 Basile Starynkevitch <basile@starynkevitch.net>:
> 
> +/* Fatal message while reading state.  */
> +#define fatal_reading_state(Tok,Msg)  do {		\
> ...
> 
> +#define fatal_reading_state_printf(Tok,Fmt,...) do {	\
> 
> Would you mind doing functions instead of function-like macros here?

I did made fatal_reading_state an inline function to please Laurynas 
(but I don't think that brings much).

However I cannot make the variadic fatal_reading_state_printf a
function, because I see no va_arg argument accepting function in
errors.h, so I have to leave fatal_reading_state_printf as a macro. I
am not sure that having  the binary fatal_reading_state a function and
the variary fatal_reading_state_printf staying a macro is prettier, but
I try to do what Laurynas wants.

> +/* Write the gc_used information.  */
> +static void
> +write_state_gc_used (enum gc_used_enum gus)
> Please add default: ggc_unreachable() or something in that spirit.

Done. I corrected various indentations & typos. However, Laurynas asked
> +  /* 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.  */
> 
> Probably not the best place to discuss what may or may not happen in
> the future. More like wiki material.

I respectfully disagree with Laurynas comments.  We have to explain why
write_state is called precisely at this place in main (intuitively, Jeremie 
& me placed it elsewhere, and that was wrong). So I shortened the comment, 
but did say something.

The uncommon scenario (this word does not appear any more in the patch) is:
A plugin foo.so is so common and widely used that it adds new GTY-ed stuff 
and redistribute a gtype-foo.state file augmented with the GTY-ed types 
defined by that plugin. Other plugins foobar.so and foodee.so are based 
upon foo.so (that is, they require the foo.so plugin to be loaded before 
them) and use gtype-foo.state as the state file. Laurynas, could you accept 
the small comment that remains?  I strongly think we have to say something!

I am bootstrapping it right now on x86_64-unknown-linux-gnu (i.e. 
Debian/Sid/AMD64) with lto & c+++ & plugins enabled.

Ok for trunk if it bootstraps? With what changes?

Cheers

PS. Before a documentation patch, we still need a minor Makefile.in patch 
to install gengtype appropriately. Alexander Oliva suggested me at the 
GCC summit to install gengtype, which becomes a user visible utility, 
in $(DESTDIR)$(libexecsubdir)/ where cc1 is already sitting.

-- 
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} ***
Index: gcc/gengtype-state.c
===================================================================
--- gcc/gengtype-state.c	(revision 0)
+++ gcc/gengtype-state.c	(revision 0)
@@ -0,0 +1,2474 @@
+/* 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 opening parenthesis.  */
+  STOK_RIGHTPAR,                /* Right closing 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.  */
+static inline void 
+fatal_reading_state (struct state_token_st* tok, const char*msg)
+{
+  if (tok)
+    fatal ("%s:%d:%d: Invalid state file; %s",
+	   tok->stok_file, tok->stok_line, tok->stok_col, 
+	   msg); 
+  else
+    fatal ("%s:%d: Invalid state file; %s", 
+	   state_path, state_line, msg);
+}
+
+
+/* Fatal printf-like message while reading state.  This can't be a
+   function, because there is no way to pass a va_arg to a variant of
+   fatal.  */
+#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 or spaces.  */
+  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 hex escape");
+		    c = getc (state_file);
+		    break;
+		  }
+		default:
+		  fatal_reading_state
+		    (NULL_STATE_TOKEN,
+		     "Lexical error - unknown 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, '\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 - bad character \\%03o = '%c'",
+     ftell (state_file), c, c);
+}
+
+/* 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;
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* 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;
+      const 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");
+  /* Output a few comments for humans. */
+  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 which 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 ();
+  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 (const 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.  */
Index: gcc/gengtype.c
===================================================================
--- gcc/gengtype.c	(revision 167136)
+++ gcc/gengtype.c	(working copy)
@@ -67,6 +67,10 @@ int do_debug;
 /* Level for verbose messages.  */
 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.  */
@@ -563,12 +567,14 @@ new_structure (const char *name, int isunion, stru
 	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;
 	  }
@@ -577,7 +583,9 @@ new_structure (const char *name, int isunion, stru
 
 	if (ls != NULL && s == NULL)
 	  {
+	    type_count++;
 	    s = XCNEW (struct type);
+	    s->state_number = -type_count;
 	    s->next = ls->u.s.lang_struct;
 	    ls->u.s.lang_struct = s;
 	    s->u.s.lang_struct = ls;
@@ -587,7 +595,9 @@ new_structure (const char *name, int isunion, stru
 
   if (s == NULL)
     {
+      type_count++;
       s = XCNEW (struct type);
+      s->state_number = -type_count;
       s->next = structures;
       structures = s;
     }
@@ -625,8 +635,10 @@ find_structure (const char *name, int isunion)
     if (strcmp (name, s->u.s.tag) == 0 && UNION_P (s) == isunion)
       return s;
 
+  type_count++;
   s = XCNEW (struct type);
   s->next = structures;
+  s->state_number = -type_count;
   structures = s;
   s->kind = isunion ? TYPE_UNION : TYPE_STRUCT;
   s->u.s.tag = name;
@@ -650,9 +662,11 @@ find_param_structure (type_p t, type_p param[NUM_P
       break;
   if (res == NULL)
     {
+      type_count++;
       res = XCNEW (struct type);
       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);
@@ -679,6 +693,8 @@ create_pointer (type_p t)
   if (!t->pointer_to)
     {
       type_p r = XCNEW (struct type);
+      type_count++;
+      r->state_number = -type_count;
       r->kind = TYPE_POINTER;
       r->u.p = t;
       t->pointer_to = r;
@@ -693,8 +709,10 @@ create_array (type_p t, const char *len)
 {
   type_p v;
 
+  type_count++;
   v = XCNEW (struct type);
   v->kind = TYPE_ARRAY;
+  v->state_number = -type_count;
   v->u.a.p = t;
   v->u.a.len = len;
   return v;
@@ -4600,7 +4618,9 @@ dump_structures (const char *name, type_p structur
   printf ("End of %s\n\n", name);
 }
 
-/* Dumps the internal structures of gengtype.  */
+/* Dumps the internal structures of gengtype.  This is useful to debug
+   gengtype itself, or to understand what it does, e.g. for plugin
+   developers.  */
 
 static void
 dump_everything (void)
@@ -4836,8 +4856,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)
     {
@@ -4867,7 +4891,8 @@ main (int argc, char **argv)
 		     (int) i, get_input_file_name (gt_files[i]));
 	}
       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);
 
       DBGPRINT_COUNT_TYPE ("structures after parsing", structures);
       DBGPRINT_COUNT_TYPE ("param_structs after parsing", param_structs);
@@ -4917,11 +4942,30 @@ main (int argc, char **argv)
      hence enlarge the param_structs list of types.  */
   set_gc_used (variables);
 
-  /* We should write the state here, but it is not yet implemented.  */
+  /* We write the state here, in particular to handle the case when
+     the state file is augmented by some plugins, to be later used by
+     other plugins depending upon the first one.  */
   if (write_state_filename)
     {
-      fatal ("write state %s in not yet implemented", write_state_filename);
-      /* TODO: implement write state.  */
+      DBGPRINT_COUNT_TYPE ("structures before write_state", structures);
+      DBGPRINT_COUNT_TYPE ("param_structs before write_state", param_structs);
+
+      if (hit_error)
+	fatal ("didn't write state file %s after errors", 
+	       write_state_filename);
+
+      DBGPRINTF ("before write_state %s", 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;
     }
 
 
Index: gcc/gengtype.h
===================================================================
--- gcc/gengtype.h	(revision 167136)
+++ gcc/gengtype.h	(working copy)
@@ -312,6 +312,32 @@ extern struct type string_type;
 extern struct type scalar_nonchar;
 extern struct type scalar_char;
 
+/* Gives the file location of a type, if any.  */
+static inline struct fileloc*
+type_lineloc (const_type_p ty)
+{
+  if (!ty)
+    return NULL;
+  switch (ty->kind)
+    {
+    case TYPE_NONE:
+      gcc_unreachable ();
+    case TYPE_STRUCT:
+    case TYPE_UNION:
+    case TYPE_LANG_STRUCT:
+      return CONST_CAST (struct fileloc*, &ty->u.s.line);
+    case TYPE_PARAM_STRUCT:
+      return CONST_CAST (struct fileloc*, &ty->u.param_struct.line);
+    case TYPE_SCALAR:
+    case TYPE_STRING:
+    case TYPE_POINTER:
+    case TYPE_ARRAY:
+      return NULL;
+    default:
+      gcc_unreachable ();
+    }
+}
+
 /* Test if a type is a union, either a plain one or a language
    specific one.  */
 #define UNION_P(x)                                      \
@@ -374,6 +400,13 @@ extern size_t srcdir_len;
 extern const char *read_state_filename; /* (-r) program argument. */
 extern const char *write_state_filename; /* (-w) program argument. */
 
+/* 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;
Index: gcc/Makefile.in
===================================================================
--- gcc/Makefile.in	(revision 167136)
+++ gcc/Makefile.in	(working copy)
@@ -3810,6 +3810,9 @@ ALL_GTFILES_H := $(sort $(GTFILES_H) $(GTFILES_LAN
 
 $(ALL_GTFILES_H) gtype-desc.c gtype-desc.h : s-gtype ; @true
 
+### Common flags to gengtype [e.g. -v or -B backupdir]
+GENGTYPE_FLAGS= 
+
 gtyp-input.list: s-gtyp-input ; @true
 s-gtyp-input: Makefile
 	@: $(call write_entries_to_file,$(GTFILES),tmp-gi.list)
@@ -3818,7 +3821,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 \
@@ -3914,6 +3923,8 @@ build/gengenrtl.o : gengenrtl.c $(BCONFIG_H) $(SYS
 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)             \
   $(OBSTACK_H) $(XREGEX_H)
@@ -3959,7 +3970,7 @@ build/genautomata$(build_exeext) : BUILD_LIBS += -
 
 # 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
 
 # Rule for the generator programs:
 $(genprog:%=build/gen%$(build_exeext)): build/gen%$(build_exeext): build/gen%.o $(BUILD_LIBDEPS)
@@ -4432,9 +4443,10 @@ mostlyclean: lang.mostlyclean
 	-rm -f core */core
 # Delete file generated for gengtype
 	-rm -f gtyp-input.list
-# Delete files generated by gengtype.c
+# Delete files generated by gengtype
 	-rm -f gtype-*
 	-rm -f gt-*
+	-rm -f gtype.state
 # Delete genchecksum outputs
 	-rm -f *-checksum.c
 

Attachment: gengtype-wstate-r167136.ChangeLog
Description: Text document


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]