From patchwork Thu Nov 25 08:54:30 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Basile Starynkevitch X-Patchwork-Id: 73031 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id BF417B70A7 for ; Thu, 25 Nov 2010 19:57:38 +1100 (EST) Received: (qmail 14475 invoked by alias); 25 Nov 2010 08:57:17 -0000 Received: (qmail 13263 invoked by uid 22791); 25 Nov 2010 08:56:01 -0000 X-SWARE-Spam-Status: No, hits=-0.2 required=5.0 tests=AWL, BAYES_50, TW_CP, TW_NB, T_FRT_STOCK1, T_FRT_STOCK2 X-Spam-Check-By: sourceware.org Received: from smtp-154-thursday.nerim.net (HELO maiev.nerim.net) (194.79.134.154) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Thu, 25 Nov 2010 08:54:37 +0000 Received: from hector.lesours (ours.starynkevitch.net [213.41.244.95]) by maiev.nerim.net (Postfix) with ESMTPS id 364582E01D; Thu, 25 Nov 2010 09:54:32 +0100 (CET) Received: from glinka.lesours ([192.168.0.1]) by hector.lesours with smtp (Exim 4.72) (envelope-from ) id 1PLXb5-0002pI-GG; Thu, 25 Nov 2010 09:54:31 +0100 Date: Thu, 25 Nov 2010 09:54:30 +0100 From: Basile Starynkevitch To: gcc-patches@gcc.gnu.org Cc: laurynas.biveinis@gmail.com, jeremie.salvucci@free.fr, aoliva@redhat.com Subject: gengtype plugin improvement last4round -patch 6 [wstate] Message-Id: <20101125095430.e2a946e5.basile@starynkevitch.net> In-Reply-To: <20101109134907.GA11038@ours.starynkevitch.net> References: <20101104190650.8e141486.basile@starynkevitch.net> <20101108193358.58a88fd1.basile@starynkevitch.net> <20101109134907.GA11038@ours.starynkevitch.net> Mime-Version: 1.0 X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org 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 : > > +/* 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. 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 + . + + Contributed by Jeremie Salvucci + and Basile Starynkevitch +*/ + +#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 (¤t->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 .... ) */ + 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 (¤t->pointer_to); + read_state_gc_used (¤t->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 (¤t_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 (¶m_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