diff options
Diffstat (limited to 'contrib/gcc/cpppch.c')
-rw-r--r-- | contrib/gcc/cpppch.c | 717 |
1 files changed, 717 insertions, 0 deletions
diff --git a/contrib/gcc/cpppch.c b/contrib/gcc/cpppch.c new file mode 100644 index 0000000..872908d --- /dev/null +++ b/contrib/gcc/cpppch.c @@ -0,0 +1,717 @@ +/* Part of CPP library. (Precompiled header reading/writing.) + Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc. + +This program 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 2, or (at your option) any +later version. + +This program 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 this program; if not, write to the Free Software +Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "config.h" +#include "system.h" +#include "cpplib.h" +#include "cpphash.h" +#include "intl.h" +#include "hashtab.h" +#include "mkdeps.h" + +static int write_macdef (cpp_reader *, cpp_hashnode *, void *); +static int save_idents (cpp_reader *, cpp_hashnode *, void *); +static hashval_t hashmem (const void *, size_t); +static hashval_t cpp_string_hash (const void *); +static int cpp_string_eq (const void *, const void *); +static int count_defs (cpp_reader *, cpp_hashnode *, void *); +static int comp_hashnodes (const void *, const void *); +static int collect_ht_nodes (cpp_reader *, cpp_hashnode *, void *); +static int write_defs (cpp_reader *, cpp_hashnode *, void *); +static int save_macros (cpp_reader *, cpp_hashnode *, void *); + +/* This structure represents a macro definition on disk. */ +struct macrodef_struct +{ + unsigned int definition_length; + unsigned short name_length; + unsigned short flags; +}; + +/* This is how we write out a macro definition. + Suitable for being called by cpp_forall_identifiers. */ + +static int +write_macdef (cpp_reader *pfile, cpp_hashnode *hn, void *file_p) +{ + FILE *f = (FILE *) file_p; + switch (hn->type) + { + case NT_VOID: + if (! (hn->flags & NODE_POISONED)) + return 1; + + case NT_MACRO: + if ((hn->flags & NODE_BUILTIN)) + return 1; + + { + struct macrodef_struct s; + const unsigned char *defn; + + s.name_length = NODE_LEN (hn); + s.flags = hn->flags & NODE_POISONED; + + if (hn->type == NT_MACRO) + { + defn = cpp_macro_definition (pfile, hn); + s.definition_length = ustrlen (defn); + } + else + { + defn = NODE_NAME (hn); + s.definition_length = s.name_length; + } + + if (fwrite (&s, sizeof (s), 1, f) != 1 + || fwrite (defn, 1, s.definition_length, f) != s.definition_length) + { + cpp_errno (pfile, CPP_DL_ERROR, + "while writing precompiled header"); + return 0; + } + } + return 1; + + case NT_ASSERTION: + /* Not currently implemented. */ + return 1; + + default: + abort (); + } +} + +/* This structure records the names of the defined macros. + It's also used as a callback structure for size_initial_idents + and save_idents. */ + +struct cpp_savedstate +{ + /* A hash table of the defined identifiers. */ + htab_t definedhash; + /* The size of the definitions of those identifiers (the size of + 'definedstrs'). */ + size_t hashsize; + /* Number of definitions */ + size_t n_defs; + /* Array of definitions. In cpp_write_pch_deps it is used for sorting. */ + cpp_hashnode **defs; + /* Space for the next definition. Definitions are null-terminated + strings. */ + unsigned char *definedstrs; +}; + +/* Save this identifier into the state: put it in the hash table, + put the definition in 'definedstrs'. */ + +static int +save_idents (cpp_reader *pfile ATTRIBUTE_UNUSED, cpp_hashnode *hn, void *ss_p) +{ + struct cpp_savedstate *const ss = (struct cpp_savedstate *)ss_p; + + if (hn->type != NT_VOID) + { + struct cpp_string news; + void **slot; + + news.len = NODE_LEN (hn); + news.text= NODE_NAME (hn); + slot = htab_find_slot (ss->definedhash, &news, INSERT); + if (*slot == NULL) + { + struct cpp_string *sp; + unsigned char *text; + + sp = xmalloc (sizeof (struct cpp_string)); + *slot = sp; + + sp->len = NODE_LEN (hn); + sp->text = text = xmalloc (NODE_LEN (hn)); + memcpy (text, NODE_NAME (hn), NODE_LEN (hn)); + } + } + + return 1; +} + +/* Hash some memory in a generic way. */ + +static hashval_t +hashmem (const void *p_p, size_t sz) +{ + const unsigned char *p = (const unsigned char *)p_p; + size_t i; + hashval_t h; + + h = 0; + for (i = 0; i < sz; i++) + h = h * 67 - (*p++ - 113); + return h; +} + +/* Hash a cpp string for the hashtable machinery. */ + +static hashval_t +cpp_string_hash (const void *a_p) +{ + const struct cpp_string *a = (const struct cpp_string *) a_p; + return hashmem (a->text, a->len); +} + +/* Compare two cpp strings for the hashtable machinery. */ + +static int +cpp_string_eq (const void *a_p, const void *b_p) +{ + const struct cpp_string *a = (const struct cpp_string *) a_p; + const struct cpp_string *b = (const struct cpp_string *) b_p; + return (a->len == b->len + && memcmp (a->text, b->text, a->len) == 0); +} + +/* Save the current definitions of the cpp_reader for dependency + checking purposes. When writing a precompiled header, this should + be called at the same point in the compilation as cpp_valid_state + would be called when reading the precompiled header back in. */ + +int +cpp_save_state (cpp_reader *r, FILE *f) +{ + /* Save the list of non-void identifiers for the dependency checking. */ + r->savedstate = xmalloc (sizeof (struct cpp_savedstate)); + r->savedstate->definedhash = htab_create (100, cpp_string_hash, + cpp_string_eq, NULL); + cpp_forall_identifiers (r, save_idents, r->savedstate); + + /* Write out the list of defined identifiers. */ + cpp_forall_identifiers (r, write_macdef, f); + + return 0; +} + +/* Calculate the 'hashsize' field of the saved state. */ + +static int +count_defs (cpp_reader *pfile ATTRIBUTE_UNUSED, cpp_hashnode *hn, void *ss_p) +{ + struct cpp_savedstate *const ss = (struct cpp_savedstate *)ss_p; + + switch (hn->type) + { + case NT_MACRO: + if (hn->flags & NODE_BUILTIN) + return 1; + + /* else fall through. */ + + case NT_VOID: + { + struct cpp_string news; + void **slot; + + news.len = NODE_LEN (hn); + news.text = NODE_NAME (hn); + slot = htab_find (ss->definedhash, &news); + if (slot == NULL) + { + ss->hashsize += NODE_LEN (hn) + 1; + ss->n_defs += 1; + } + } + return 1; + + case NT_ASSERTION: + /* Not currently implemented. */ + return 1; + + default: + abort (); + } +} + +/* Collect the identifiers into the state's string table. */ +static int +write_defs (cpp_reader *pfile ATTRIBUTE_UNUSED, cpp_hashnode *hn, void *ss_p) +{ + struct cpp_savedstate *const ss = (struct cpp_savedstate *)ss_p; + + switch (hn->type) + { + case NT_MACRO: + if (hn->flags & NODE_BUILTIN) + return 1; + + /* else fall through. */ + + case NT_VOID: + { + struct cpp_string news; + void **slot; + + news.len = NODE_LEN (hn); + news.text = NODE_NAME (hn); + slot = htab_find (ss->definedhash, &news); + if (slot == NULL) + { + ss->defs[ss->n_defs] = hn; + ss->n_defs += 1; + } + } + return 1; + + case NT_ASSERTION: + /* Not currently implemented. */ + return 1; + + default: + abort (); + } +} + +/* Comparison function for qsort. The arguments point to pointers of + type ht_hashnode *. */ +static int +comp_hashnodes (const void *px, const void *py) +{ + cpp_hashnode *x = *(cpp_hashnode **) px; + cpp_hashnode *y = *(cpp_hashnode **) py; + return ustrcmp (NODE_NAME (x), NODE_NAME (y)); +} + +/* Write out the remainder of the dependency information. This should be + called after the PCH is ready to be saved. */ + +int +cpp_write_pch_deps (cpp_reader *r, FILE *f) +{ + struct macrodef_struct z; + struct cpp_savedstate *const ss = r->savedstate; + unsigned char *definedstrs; + size_t i; + + /* Collect the list of identifiers which have been seen and + weren't defined to anything previously. */ + ss->hashsize = 0; + ss->n_defs = 0; + cpp_forall_identifiers (r, count_defs, ss); + + ss->defs = xmalloc (ss->n_defs * sizeof (cpp_hashnode *)); + ss->n_defs = 0; + cpp_forall_identifiers (r, write_defs, ss); + + /* Sort the list, copy it into a buffer, and write it out. */ + qsort (ss->defs, ss->n_defs, sizeof (cpp_hashnode *), &comp_hashnodes); + definedstrs = ss->definedstrs = xmalloc (ss->hashsize); + for (i = 0; i < ss->n_defs; ++i) + { + size_t len = NODE_LEN (ss->defs[i]); + memcpy (definedstrs, NODE_NAME (ss->defs[i]), len + 1); + definedstrs += len + 1; + } + + memset (&z, 0, sizeof (z)); + z.definition_length = ss->hashsize; + if (fwrite (&z, sizeof (z), 1, f) != 1 + || fwrite (ss->definedstrs, ss->hashsize, 1, f) != 1) + { + cpp_errno (r, CPP_DL_ERROR, "while writing precompiled header"); + return -1; + } + free (ss->definedstrs); + + /* Free the saved state. */ + free (ss); + r->savedstate = NULL; + return 0; +} + +/* Write out the definitions of the preprocessor, in a form suitable for + cpp_read_state. */ + +int +cpp_write_pch_state (cpp_reader *r, FILE *f) +{ + struct macrodef_struct z; + + /* Write out the list of defined identifiers. */ + cpp_forall_identifiers (r, write_macdef, f); + memset (&z, 0, sizeof (z)); + if (fwrite (&z, sizeof (z), 1, f) != 1) + { + cpp_errno (r, CPP_DL_ERROR, "while writing precompiled header"); + return -1; + } + + if (!r->deps) + r->deps = deps_init (); + + if (deps_save (r->deps, f) != 0) + { + cpp_errno (r, CPP_DL_ERROR, "while writing precompiled header"); + return -1; + } + + return 0; +} + + +/* Data structure to transform hash table nodes into a sorted list */ + +struct ht_node_list +{ + /* Array of nodes */ + cpp_hashnode **defs; + /* Number of nodes in the array */ + size_t n_defs; + /* Size of the allocated array */ + size_t asize; +}; + +/* Callback for collecting identifiers from hash table */ + +static int +collect_ht_nodes (cpp_reader *pfile ATTRIBUTE_UNUSED, cpp_hashnode *hn, + void *nl_p) +{ + struct ht_node_list *const nl = (struct ht_node_list *)nl_p; + + if (hn->type != NT_VOID || hn->flags & NODE_POISONED) + { + if (nl->n_defs == nl->asize) + { + nl->asize *= 2; + nl->defs = xrealloc (nl->defs, nl->asize * sizeof (cpp_hashnode *)); + } + + nl->defs[nl->n_defs] = hn; + ++nl->n_defs; + } + return 1; +} + + +/* Return nonzero if FD is a precompiled header which is consistent + with the preprocessor's current definitions. It will be consistent + when: + + - anything that was defined just before the PCH was generated + is defined the same way now; and + - anything that was not defined then, but is defined now, was not + used by the PCH. + + NAME is used to print warnings if `warn_invalid_pch' is set in the + reader's flags. +*/ + +int +cpp_valid_state (cpp_reader *r, const char *name, int fd) +{ + struct macrodef_struct m; + size_t namebufsz = 256; + unsigned char *namebuf = xmalloc (namebufsz); + unsigned char *undeftab = NULL; + struct ht_node_list nl = { 0, 0, 0 }; + unsigned char *first, *last; + unsigned int i; + + /* Read in the list of identifiers that must be defined + Check that they are defined in the same way. */ + for (;;) + { + cpp_hashnode *h; + const unsigned char *newdefn; + + if (read (fd, &m, sizeof (m)) != sizeof (m)) + goto error; + + if (m.name_length == 0) + break; + + if (m.definition_length > namebufsz) + { + free (namebuf); + namebufsz = m.definition_length + 256; + namebuf = xmalloc (namebufsz); + } + + if ((size_t)read (fd, namebuf, m.definition_length) + != m.definition_length) + goto error; + + h = cpp_lookup (r, namebuf, m.name_length); + if (m.flags & NODE_POISONED + || h->type != NT_MACRO + || h->flags & NODE_POISONED) + { + if (CPP_OPTION (r, warn_invalid_pch)) + cpp_error (r, CPP_DL_WARNING_SYSHDR, + "%s: not used because `%.*s' not defined", + name, m.name_length, namebuf); + goto fail; + } + + newdefn = cpp_macro_definition (r, h); + + if (m.definition_length != ustrlen (newdefn) + || memcmp (namebuf, newdefn, m.definition_length) != 0) + { + if (CPP_OPTION (r, warn_invalid_pch)) + cpp_error (r, CPP_DL_WARNING_SYSHDR, + "%s: not used because `%.*s' defined as `%s' not `%.*s'", + name, m.name_length, namebuf, newdefn + m.name_length, + m.definition_length - m.name_length, + namebuf + m.name_length); + goto fail; + } + } + free (namebuf); + namebuf = NULL; + + /* Read in the list of identifiers that must not be defined. + Check that they really aren't. */ + undeftab = xmalloc (m.definition_length); + if ((size_t) read (fd, undeftab, m.definition_length) != m.definition_length) + goto error; + + /* Collect identifiers from the current hash table. */ + nl.n_defs = 0; + nl.asize = 10; + nl.defs = xmalloc (nl.asize * sizeof (cpp_hashnode *)); + cpp_forall_identifiers (r, &collect_ht_nodes, &nl); + qsort (nl.defs, nl.n_defs, sizeof (cpp_hashnode *), &comp_hashnodes); + + /* Loop through nl.defs and undeftab, both of which are sorted lists. + There should be no matches. */ + first = undeftab; + last = undeftab + m.definition_length; + i = 0; + + while (first < last && i < nl.n_defs) + { + int cmp = ustrcmp (first, NODE_NAME (nl.defs[i])); + + if (cmp < 0) + first += ustrlen (first) + 1; + else if (cmp > 0) + ++i; + else + { + if (CPP_OPTION (r, warn_invalid_pch)) + cpp_error (r, CPP_DL_WARNING_SYSHDR, + "%s: not used because `%s' is defined", + name, first); + goto fail; + } + } + + free(nl.defs); + free (undeftab); + + /* We win! */ + return 0; + + error: + cpp_errno (r, CPP_DL_ERROR, "while reading precompiled header"); + return -1; + + fail: + if (namebuf != NULL) + free (namebuf); + if (undeftab != NULL) + free (undeftab); + if (nl.defs != NULL) + free (nl.defs); + return 1; +} + +/* Save all the existing macros and assertions. + This code assumes that there might be hundreds, but not thousands of + existing definitions. */ + +struct save_macro_item { + struct save_macro_item *next; + struct cpp_hashnode macs[64]; +}; + +struct save_macro_data +{ + struct save_macro_item *macros; + size_t count; + char **saved_pragmas; +}; + +/* Save the definition of a single macro, so that it will persist across + a PCH restore. */ + +static int +save_macros (cpp_reader *r ATTRIBUTE_UNUSED, cpp_hashnode *h, void *data_p) +{ + struct save_macro_data *data = (struct save_macro_data *)data_p; + if (h->type != NT_VOID + && (h->flags & NODE_BUILTIN) == 0) + { + cpp_hashnode *save; + if (data->count == ARRAY_SIZE (data->macros->macs)) + { + struct save_macro_item *d = data->macros; + data->macros = xmalloc (sizeof (struct save_macro_item)); + data->macros->next = d; + data->count = 0; + } + save = data->macros->macs + data->count; + data->count++; + memcpy (save, h, sizeof (struct cpp_hashnode)); + HT_STR (&save->ident) = xmemdup (HT_STR (HT_NODE (save)), + HT_LEN (HT_NODE (save)), + HT_LEN (HT_NODE (save)) + 1); + } + return 1; +} + +/* Prepare to restore the state, by saving the currently-defined + macros in 'data'. */ + +void +cpp_prepare_state (cpp_reader *r, struct save_macro_data **data) +{ + struct save_macro_data *d = xmalloc (sizeof (struct save_macro_data)); + + d->macros = NULL; + d->count = ARRAY_SIZE (d->macros->macs); + cpp_forall_identifiers (r, save_macros, d); + d->saved_pragmas = _cpp_save_pragma_names (r); + *data = d; +} + +/* Given a precompiled header that was previously determined to be valid, + apply all its definitions (and undefinitions) to the current state. + DEPNAME is passed to deps_restore. */ + +int +cpp_read_state (cpp_reader *r, const char *name, FILE *f, + struct save_macro_data *data) +{ + struct macrodef_struct m; + size_t defnlen = 256; + unsigned char *defn = xmalloc (defnlen); + struct lexer_state old_state; + struct save_macro_item *d; + size_t i, mac_count; + int saved_line = r->line; + + /* Restore spec_nodes, which will be full of references to the old + hashtable entries and so will now be invalid. */ + { + struct spec_nodes *s = &r->spec_nodes; + s->n_defined = cpp_lookup (r, DSC("defined")); + s->n_true = cpp_lookup (r, DSC("true")); + s->n_false = cpp_lookup (r, DSC("false")); + s->n__VA_ARGS__ = cpp_lookup (r, DSC("__VA_ARGS__")); + } + + /* Run through the carefully-saved macros, insert them. */ + d = data->macros; + mac_count = data->count; + while (d) + { + struct save_macro_item *nextd; + for (i = 0; i < mac_count; i++) + { + cpp_hashnode *h; + + h = cpp_lookup (r, HT_STR (HT_NODE (&d->macs[i])), + HT_LEN (HT_NODE (&d->macs[i]))); + h->type = d->macs[i].type; + h->flags = d->macs[i].flags; + h->value = d->macs[i].value; + free ((void *)HT_STR (HT_NODE (&d->macs[i]))); + } + nextd = d->next; + free (d); + d = nextd; + mac_count = ARRAY_SIZE (d->macs); + } + + _cpp_restore_pragma_names (r, data->saved_pragmas); + + free (data); + + old_state = r->state; + + r->state.in_directive = 1; + r->state.prevent_expansion = 1; + r->state.angled_headers = 0; + + /* Read in the identifiers that must be defined. */ + for (;;) + { + cpp_hashnode *h; + + if (fread (&m, sizeof (m), 1, f) != 1) + goto error; + + if (m.name_length == 0) + break; + + if (defnlen < m.definition_length + 1) + { + defnlen = m.definition_length + 256; + defn = xrealloc (defn, defnlen); + } + + if (fread (defn, 1, m.definition_length, f) != m.definition_length) + goto error; + defn[m.definition_length] = '\n'; + + h = cpp_lookup (r, defn, m.name_length); + + if (h->type == NT_MACRO) + _cpp_free_definition (h); + if (m.flags & NODE_POISONED) + h->flags |= NODE_POISONED | NODE_DIAGNOSTIC; + else if (m.name_length != m.definition_length) + { + if (cpp_push_buffer (r, defn + m.name_length, + m.definition_length - m.name_length, true) + != NULL) + { + _cpp_clean_line (r); + if (!_cpp_create_definition (r, h)) + abort (); + _cpp_pop_buffer (r); + } + else + abort (); + } + } + + r->state = old_state; + r->line = saved_line; + free (defn); + defn = NULL; + + if (deps_restore (r->deps, f, CPP_OPTION (r, restore_pch_deps) ? name : NULL) + != 0) + goto error; + + return 0; + + error: + cpp_errno (r, CPP_DL_ERROR, "while reading precompiled header"); + return -1; +} |