[pph] Support for references to symbols in other PPH images (issue4873051)

Gabriel Charette gchare@google.com
Tue Aug 16 19:46:00 GMT 2011


I really like the way this is implemented! In particular having our
own cache is so great!

A few comments inline below.

Cheers,
Gab

On Mon, Aug 15, 2011 at 8:05 PM, Diego Novillo <dnovillo@google.com> wrote:
> This patch finishes the support for external references to symbols in
> other PPH files.
>
> This is used when one PPH image includes another.  The symbols in
> the included image should not be emitted from the parent, and references
> to them should go to the correct location in the child's pickle cache.
>
> The main change is the addition of two kinds of references.  We used
> to mark references to already pickled nodes with PPH_RECORD_SHARED.
> We now distinguish internal (PPH_RECORD_IREF) and external
> (PPH_RECORD_XREF) references.
>
> In the first variant, only one index is written as reference: the slot
> into the pickle cache.  In the second variant, two indices are
> written: an index into the include table specifying which child image
> contains the data, and an index into the child's pickle cache.
>
> When writing PPH images, we also need to make sure that we do not
> write any symbols in the file bindings that belong to included images.
> Otherwise, we will emit the same symbol twice.  This is implemented by
> a new filter PPHF_NO_XREFS, which is used when writing the current
> image's global bindings.
>
> Tested on x86_64.  Committed to branch.
>
>
> Diego.
>
>
> cp/ChangeLog.pph
>        * name-lookup.c (pph_out_binding_table): Call
>        pph_out_record_marker instead of pph_out_uchar.
>        (pph_in_binding_table): Call pph_in_record_marker instead of
>        pph_in_uchar.
>        * pph-streamer-in.c (pph_read_images): Declare.
>        (pph_is_reference_marker): New.  Update all previous users of
>        PPH_RECORD_SHARED.
>        (pph_in_start_record): Add argument INCLUDE_IX.  Update all
>        users.
>        Handle PPH_RECORD_XREF markers.
>        (pph_in_includes): After reading INCLUDE_NAME, call
>        pph_add_include.
>        (pph_read_file_contents): Move debugging output from ...
>        (pph_read_file): ... here.
>        Change it to return the opened stream.
>        Add STREAM to pph_read_images.
>        (pph_reader_finish): New.
>        (pph_read_file_1): Rename from pph_read_file_contents.  Update
>        all users.
>        * pph-streamer-out.c (pph_out_start_record): Re-write to
>        handle PPH_RECORD_IREF and PPH_RECORD_XREF.
>        (pph_write_file_contents): Embed ...
>        (pph_write_file): ... here.
>        (pph_add_include): Add new argument INCLUDE.  Update all
>        users.
>        (pph_writer_finish): Do nothing if pph_out_stream is NULL.
>        (pph_tree_matches): New.
>        (pph_out_tree_vec_filtered): New.
>        (pph_out_binding_level): Add new argument FILTER.  Update all users.
>        * pph-streamer.c (pph_stream_close_1): Embed ...
>        (pph_stream_close): ... here.
>        Do not traverse the list of includes.
>        (pph_cache_lookup): Factor out of ...
>        (pph_cache_add): ... here.
>        (pph_cache_lookup_in_includes): New.
>        (pph_cache_get): Add new argument INCLUDE_IX.  Update all users.
>        * pph-streamer.h (enum pph_record_marker): Add PPH_RECORD_IREF
>        and PPH_RECORD_XREF.  Remove PPH_RECORD_SHARED.  Update all users.
>        (struct pph_stream): Remove field open_p and nested_p.  Update
>        all users.
>        (enum chain_filter): Remove.  Change values to #define constants.
>        Update all users.
>        (PPHF_NO_XREFS): Define.
>        (pph_out_record_marker): New.
>        (pph_in_record_marker): New.
>        * pph.c (pph_finish): Always call pph_writer_finish.
>        Call pph_reader_finish.
>
> testsuite/ChangeLog.pph
>        * g++.dg/pph/x1tmplclass2.cc: Update expected ICE.
>        * g++.dg/pph/x4tmplclass2.cc: Update expected ICE.
>        * g++.dg/pph/z4tmplclass2.cc: Update expected ICE.
>
> diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
> index 50c6b66..7798f24 100644
> --- a/gcc/cp/name-lookup.c
> +++ b/gcc/cp/name-lookup.c
> @@ -6002,12 +6002,12 @@ pph_out_binding_table (pph_stream *stream, binding_table bt)
>     {
>       if (bt->chain[i])
>        {
> -         pph_out_uchar (stream, PPH_RECORD_START);
> +         pph_out_record_marker (stream, PPH_RECORD_START);
>          pph_out_tree_or_ref (stream, bt->chain[i]->name);
>          pph_out_tree_or_ref (stream, bt->chain[i]->type);
>        }
>       else
> -       pph_out_uchar (stream, PPH_RECORD_END);
> +       pph_out_record_marker (stream, PPH_RECORD_END);
>     }
>   pph_out_uint (stream, bt->entry_count);
>  }
> @@ -6025,13 +6025,15 @@ pph_in_binding_table (pph_stream *stream)
>   bt = binding_table_new (chain_count);
>   for (i = 0; i < chain_count; i++)
>     {
> -      unsigned char record_marker = pph_in_uchar (stream);
> -      if (record_marker == PPH_RECORD_START)
> +      enum pph_record_marker marker = pph_in_record_marker (stream);
> +      if (marker == PPH_RECORD_START)
>        {
>          tree name = pph_in_tree (stream);
>          tree type = pph_in_tree (stream);
>          binding_table_insert (bt, name, type);
>        }
> +      else
> +       gcc_assert (marker == PPH_RECORD_END);
>     }
>   bt->entry_count = pph_in_uint (stream);
>
> diff --git a/gcc/cp/pph-streamer-in.c b/gcc/cp/pph-streamer-in.c
> index e644113..f87e6a5 100644
> --- a/gcc/cp/pph-streamer-in.c
> +++ b/gcc/cp/pph-streamer-in.c
> @@ -33,6 +33,13 @@ along with GCC; see the file COPYING3.  If not see
>  #include "cppbuiltin.h"
>  #include "toplev.h"
>
> +/* List of PPH images read during parsing.  Images opened during #include
> +   processing and opened from pph_in_includes cannot be closed
> +   immediately after reading, because the pickle cache contained in
> +   them may be referenced from other images.  We delay closing all of
> +   them until the end of parsing (when pph_reader_finish is called).  */
> +static VEC(pph_stream_ptr,heap) *pph_read_images = NULL;
> +
>  typedef char *char_p;
>  DEF_VEC_P(char_p);
>  DEF_VEC_ALLOC_P(char_p,heap);
> @@ -135,43 +142,57 @@ pph_init_read (pph_stream *stream)
>  }
>
>
> -/* Read and return a record marker from STREAM.  When a PPH_RECORD_START
> +/* Return true if MARKER is either PPH_RECORD_IREF or PPH_RECORD_XREF.  */
> +
> +static inline bool
> +pph_is_reference_marker (enum pph_record_marker marker)
> +{
> +  return marker == PPH_RECORD_IREF || marker == PPH_RECORD_XREF;
> +}
> +
> +
> +/* Read and return a record header from STREAM.  When a PPH_RECORD_START
>    marker is read, the next word read is an index into the streamer
>    cache where the rematerialized data structure should be stored.
>    When the writer stored this data structure for the first time, it
> -   added it to its own streamer cache at slot number *CACHE_IX.
> +   added it to its own streamer cache at slot number *CACHE_IX_P.
>
>    This way, if the same data structure was written a second time to
>    the stream, instead of writing the whole structure again, only the
> -   index *CACHE_IX is written as a PPH_RECORD_SHARED record.
> +   index *CACHE_IX_P is written as a PPH_RECORD_IREF record.
>
> -   Therefore, when reading a PPH_RECORD_START marker, *CACHE_IX will
> +   Therefore, when reading a PPH_RECORD_START marker, *CACHE_IX_P will
>    contain the slot number where the materialized data should be
> -   cached at.  When reading a PPH_RECORD_SHARED marker, *CACHE_IX will
> +   cached at.  When reading a PPH_RECORD_IREF marker, *CACHE_IX_P will
>    contain the slot number the reader can find the previously
> -   materialized structure.  */
> +   materialized structure.
> +
> +   If the record starts with PPH_RECORD_XREF, this means that the data
> +   we are about to read is located in the pickle cache of one of
> +   STREAM's included images.  In this case, the record consists of two
> +   indices: the first one (*INCLUDE_IX_P) indicates which included
> +   image contains the data (it is an index into STREAM->INCLUDES), the
> +   second one indicates which slot in that image's pickle cache we can
> +   find the data.  */
>
>  static inline enum pph_record_marker
> -pph_in_start_record (pph_stream *stream, unsigned *cache_ix)
> +pph_in_start_record (pph_stream *stream, unsigned *include_ix_p,
> +                    unsigned *cache_ix_p)
>  {
> -  enum pph_record_marker marker;
> +  enum pph_record_marker marker = pph_in_record_marker (stream);
>
> -  marker = (enum pph_record_marker) pph_in_uchar (stream);
> +  *include_ix_p = (unsigned) -1;
> +  *cache_ix_p = (unsigned) -1;

Setting *cache_ix to (unsigned) -1 used to be a "hack" (with a comment
explaining it which was removed just below), to avoid a warning about
it being unset in a branch, but that branch was only PPH_RECORD_END,
in which case it didn't matter.

Now that we actually check in pph_cache_get that include_ix ==
(unsigned) -1, I'm not so sure this is good... while (unsigned) -1 is
a very large unsigned int, it's not impossible to have an actual cache
entry with that number...

> -  /* For PPH_RECORD_START and PPH_RECORD_SHARED markers, read the
> +  /* For PPH_RECORD_START and PPH_RECORD_IREF markers, read the
>      streamer cache slot where we should store or find the
>      rematerialized data structure (see description above).  */
> -  if (marker == PPH_RECORD_START || marker == PPH_RECORD_SHARED)
> -    *cache_ix = pph_in_uint (stream);
> -  else
> +  if (marker == PPH_RECORD_START || marker == PPH_RECORD_IREF)
> +    *cache_ix_p = pph_in_uint (stream);
> +  else if (marker == PPH_RECORD_XREF)
>     {
> -      gcc_assert (marker == PPH_RECORD_END);
> -
> -      /* Initialize CACHE_IX to an invalid index. Even though this
> -        is never used in practice, the compiler will throw an error
> -        if the optimizer inlines this function in a given build as
> -        it will complain that " 'ix' may be used uninitialized".  */

...this is the comment I'm referring to above.

> -      *cache_ix = -1;
> +      *include_ix_p = pph_in_uint (stream);
> +      *cache_ix_p = pph_in_uint (stream);
>     }
>
>   return marker;
> @@ -457,13 +478,13 @@ pph_in_cxx_binding_1 (pph_stream *stream)
>   cxx_binding *cb;
>   tree value, type;
>   enum pph_record_marker marker;
> -  unsigned ix;
> +  unsigned ix, include_ix;

Sometimes you call the local variable "include_ix"...

>
> -  marker = pph_in_start_record (stream, &ix);
> +  marker = pph_in_start_record (stream, &include_ix, &ix);
>   if (marker == PPH_RECORD_END)
>     return NULL;
> -  else if (marker == PPH_RECORD_SHARED)
> -    return (cxx_binding *) pph_cache_get (stream, ix);
> +  else if (pph_is_reference_marker (marker))
> +    return (cxx_binding *) pph_cache_get (stream, include_ix, ix);
>
>   value = pph_in_tree (stream);
>   type = pph_in_tree (stream);
> @@ -505,13 +526,13 @@ pph_in_class_binding (pph_stream *stream)
>  {
>   cp_class_binding *cb;
>   enum pph_record_marker marker;
> -  unsigned ix;
> +  unsigned image_ix, ix;

... and sometimes image_ix: consistency would be nice, although not necessary...

>
> -  marker = pph_in_start_record (stream, &ix);
> +  marker = pph_in_start_record (stream, &image_ix, &ix);
>   if (marker == PPH_RECORD_END)
>     return NULL;
> -  else if (marker == PPH_RECORD_SHARED)
> -    return (cp_class_binding *) pph_cache_get (stream, ix);
> +  else if (pph_is_reference_marker (marker))
> +    return (cp_class_binding *) pph_cache_get (stream, image_ix, ix);
>
>   ALLOC_AND_REGISTER (stream, ix, cb, ggc_alloc_cleared_cp_class_binding ());
>   cb->base = pph_in_cxx_binding (stream);
> @@ -528,13 +549,13 @@ pph_in_label_binding (pph_stream *stream)
>  {
>   cp_label_binding *lb;
>   enum pph_record_marker marker;
> -  unsigned ix;
> +  unsigned image_ix, ix;
>
> -  marker = pph_in_start_record (stream, &ix);
> +  marker = pph_in_start_record (stream, &image_ix, &ix);
>   if (marker == PPH_RECORD_END)
>     return NULL;
> -  else if (marker == PPH_RECORD_SHARED)
> -    return (cp_label_binding *) pph_cache_get (stream, ix);
> +  else if (pph_is_reference_marker (marker))
> +    return (cp_label_binding *) pph_cache_get (stream, image_ix, ix);
>
>   ALLOC_AND_REGISTER (stream, ix, lb, ggc_alloc_cleared_cp_label_binding ());
>   lb->label = pph_in_tree (stream);
> @@ -565,16 +586,16 @@ pph_in_label_binding (pph_stream *stream)
>  static cp_binding_level *
>  pph_in_binding_level (pph_stream *stream, cp_binding_level *to_register)
>  {
> -  unsigned i, num, ix;
> +  unsigned i, num, image_ix, ix;
>   cp_binding_level *bl;
>   struct bitpack_d bp;
>   enum pph_record_marker marker;
>
> -  marker = pph_in_start_record (stream, &ix);
> +  marker = pph_in_start_record (stream, &image_ix, &ix);
>   if (marker == PPH_RECORD_END)
>     return NULL;
> -  else if (marker == PPH_RECORD_SHARED)
> -    return (cp_binding_level *) pph_cache_get (stream, ix);
> +  else if (pph_is_reference_marker (marker))
> +    return (cp_binding_level *) pph_cache_get (stream, image_ix, ix);
>
>   /* If TO_REGISTER is set, register that binding level instead of the newly
>      allocated binding level into slot IX.  */
> @@ -644,13 +665,13 @@ pph_in_c_language_function (pph_stream *stream)
>  {
>   struct c_language_function *clf;
>   enum pph_record_marker marker;
> -  unsigned ix;
> +  unsigned image_ix, ix;
>
> -  marker = pph_in_start_record (stream, &ix);
> +  marker = pph_in_start_record (stream, &image_ix, &ix);
>   if (marker == PPH_RECORD_END)
>     return NULL;
> -  else if (marker == PPH_RECORD_SHARED)
> -    return (struct c_language_function *) pph_cache_get (stream, ix);
> +  else if (pph_is_reference_marker (marker))
> +    return (struct c_language_function *) pph_cache_get (stream, image_ix, ix);
>
>   ALLOC_AND_REGISTER (stream, ix, clf,
>                      ggc_alloc_cleared_c_language_function ());
> @@ -669,13 +690,13 @@ pph_in_language_function (pph_stream *stream)
>   struct bitpack_d bp;
>   struct language_function *lf;
>   enum pph_record_marker marker;
> -  unsigned ix;
> +  unsigned image_ix, ix;
>
> -  marker = pph_in_start_record (stream, &ix);
> +  marker = pph_in_start_record (stream, &image_ix, &ix);
>   if (marker == PPH_RECORD_END)
>     return NULL;
> -  else if (marker == PPH_RECORD_SHARED)
> -    return (struct language_function *) pph_cache_get (stream, ix);
> +  else if (pph_is_reference_marker (marker))
> +    return (struct language_function *) pph_cache_get (stream, image_ix, ix);
>
>   ALLOC_AND_REGISTER (stream, ix, lf, ggc_alloc_cleared_language_function ());
>   memcpy (&lf->base, pph_in_c_language_function (stream),
> @@ -759,17 +780,17 @@ static struct function *
>  pph_in_struct_function (pph_stream *stream)
>  {
>   size_t count, i;
> -  unsigned ix;
> +  unsigned image_ix, ix;
>   enum pph_record_marker marker;
>   struct function *fn;
>   tree decl;
>
> -  marker = pph_in_start_record (stream, &ix);
> +  marker = pph_in_start_record (stream, &image_ix, &ix);
>   if (marker == PPH_RECORD_END)
>     return NULL;
>
>   /* Since struct function is embedded in every decl, fn cannot be shared.  */
> -  gcc_assert (marker != PPH_RECORD_SHARED);
> +  gcc_assert (!pph_is_reference_marker (marker));
>
>   decl = pph_in_tree (stream);
>   allocate_struct_function (decl, false);
> @@ -864,15 +885,15 @@ pph_in_lang_specific (pph_stream *stream, tree decl)
>   struct lang_decl *ld;
>   struct lang_decl_base *ldb;
>   enum pph_record_marker marker;
> -  unsigned ix;
> +  unsigned image_ix, ix;
>
> -  marker = pph_in_start_record (stream, &ix);
> +  marker = pph_in_start_record (stream, &image_ix, &ix);
>   if (marker == PPH_RECORD_END)
>     return;
> -  else if (marker == PPH_RECORD_SHARED)
> +  else if (pph_is_reference_marker (marker))
>     {
>       DECL_LANG_SPECIFIC (decl) =
> -       (struct lang_decl *) pph_cache_get (stream, ix);
> +       (struct lang_decl *) pph_cache_get (stream, image_ix, ix);
>       return;
>     }
>
> @@ -960,13 +981,13 @@ pph_in_sorted_fields_type (pph_stream *stream)
>   unsigned i, num_fields;
>   struct sorted_fields_type *v;
>   enum pph_record_marker marker;
> -  unsigned ix;
> +  unsigned image_ix, ix;
>
> -  marker = pph_in_start_record (stream, &ix);
> +  marker = pph_in_start_record (stream, &image_ix, &ix);
>   if (marker == PPH_RECORD_END)
>     return NULL;
> -  else if (marker == PPH_RECORD_SHARED)
> -    return (struct sorted_fields_type *) pph_cache_get (stream, ix);
> +  else if (pph_is_reference_marker (marker))
> +    return (struct sorted_fields_type *) pph_cache_get (stream, image_ix, ix);
>
>   num_fields = pph_in_uint (stream);
>   ALLOC_AND_REGISTER (stream, ix, v, sorted_fields_type_new (num_fields));
> @@ -984,7 +1005,7 @@ pph_in_lang_type_class (pph_stream *stream, struct lang_type_class *ltc)
>  {
>   struct bitpack_d bp;
>   enum pph_record_marker marker;
> -  unsigned ix;
> +  unsigned image_ix, ix;
>
>   ltc->align = pph_in_uchar (stream);
>
> @@ -1039,14 +1060,14 @@ pph_in_lang_type_class (pph_stream *stream, struct lang_type_class *ltc)
>   ltc->typeinfo_var = pph_in_tree (stream);
>   ltc->vbases = pph_in_tree_vec (stream);
>
> -  marker = pph_in_start_record (stream, &ix);
> +  marker = pph_in_start_record (stream, &image_ix, &ix);
>   if (marker == PPH_RECORD_START)
>     {
>       ltc->nested_udts = pph_in_binding_table (stream);
>       pph_cache_insert_at (stream, ltc->nested_udts, ix);
>     }
> -  else if (marker == PPH_RECORD_SHARED)
> -    ltc->nested_udts = (binding_table) pph_cache_get (stream, ix);
> +  else if (pph_is_reference_marker (marker))
> +    ltc->nested_udts = (binding_table) pph_cache_get (stream, image_ix, ix);
>
>   ltc->as_base = pph_in_tree (stream);
>   ltc->pure_virtuals = pph_in_tree_vec (stream);
> @@ -1079,13 +1100,13 @@ pph_in_lang_type (pph_stream *stream)
>  {
>   struct lang_type *lt;
>   enum pph_record_marker marker;
> -  unsigned ix;
> +  unsigned image_ix, ix;
>
> -  marker = pph_in_start_record (stream, &ix);
> +  marker = pph_in_start_record (stream, &image_ix, &ix);
>   if (marker == PPH_RECORD_END)
>     return NULL;
> -  else if (marker == PPH_RECORD_SHARED)
> -    return (struct lang_type *) pph_cache_get (stream, ix);
> +  else if (pph_is_reference_marker (marker))
> +    return (struct lang_type *) pph_cache_get (stream, image_ix, ix);
>
>   ALLOC_AND_REGISTER (stream, ix, lt,
>                      ggc_alloc_cleared_lang_type (sizeof (struct lang_type)));
> @@ -1346,10 +1367,8 @@ pph_in_includes (pph_stream *stream)
>   for (i = 0; i < num; i++)
>     {
>       const char *include_name = pph_in_string (stream);
> -      /* FIXME pph.  Disabled for now.  Need to re-work caching so
> -        external symbol references are properly saved and restored.  */
> -      if (0)
> -       pph_read_file (include_name);
> +      pph_stream *include = pph_read_file (include_name);
> +      pph_add_include (stream, include);
>     }
>  }
>
> @@ -1454,10 +1473,10 @@ pph_in_and_merge_line_table (pph_stream *stream, struct line_maps *linetab)
>  }
>
>
> -/* Read contents of PPH file in STREAM.  */
> +/* Helper for pph_read_file.  Read contents of PPH file in STREAM.  */
>
>  static void
> -pph_read_file_contents (pph_stream *stream)
> +pph_read_file_1 (pph_stream *stream)
>  {
>   bool verified;
>   cpp_ident_use *bad_use;
> @@ -1467,6 +1486,9 @@ pph_read_file_contents (pph_stream *stream)
>   unsigned i;
>   VEC(tree,gc) *file_unemitted_tinfo_decls;
>
> +  if (flag_pph_debug >= 1)
> +    fprintf (pph_logfile, "PPH: Reading %s\n", stream->name);
> +
>   /* Read all the images included by STREAM.  */
>   pph_in_includes (stream);
>
> @@ -1483,7 +1505,7 @@ pph_read_file_contents (pph_stream *stream)
>   /* Re-instantiate all the pre-processor symbols defined by STREAM.  */
>   cpp_lt_replay (parse_in, &idents_used);
>
> -  /* Read in the pph's line table and merge it in the current line table.  */
> +  /* Read in STREAM's line table and merge it in the current line table.  */
>   pph_in_and_merge_line_table (stream, line_table);
>
>   /* Read the bindings from STREAM and merge them with the current bindings.  */
> @@ -1514,28 +1536,27 @@ pph_read_file_contents (pph_stream *stream)
>      STREAM will need to be read again the next time we want to read
>      the image we are now generating.  */
>   if (pph_out_file)
> -    pph_add_include (stream);
> +    pph_add_include (NULL, stream);
>  }
>
>
> -/* Read PPH file FILENAME.  */
> +/* Read PPH file FILENAME.  Return the in-memory pph_stream instance.  */
>
> -void
> +pph_stream *
>  pph_read_file (const char *filename)
>  {
>   pph_stream *stream;
>
> -  if (flag_pph_debug >= 1)
> -    fprintf (pph_logfile, "PPH: Reading %s\n", filename);
> -
>   stream = pph_stream_open (filename, "rb");
>   if (stream)
>     {
> -      pph_read_file_contents (stream);
> -      pph_stream_close (stream);
> +      pph_read_file_1 (stream);
> +      VEC_safe_push (pph_stream_ptr, heap, pph_read_images, stream);
>     }
>   else
>     error ("Cannot open PPH file for reading: %s: %m", filename);
> +
> +  return stream;
>  }
>
>
> @@ -1981,13 +2002,13 @@ pph_read_tree (struct lto_input_block *ib ATTRIBUTE_UNUSED,
>   tree expr;
>   bool fully_read_p;
>   enum pph_record_marker marker;
> -  unsigned ix;
> +  unsigned image_ix, ix;
>
> -  marker = pph_in_start_record (stream, &ix);
> +  marker = pph_in_start_record (stream, &image_ix, &ix);
>   if (marker == PPH_RECORD_END)
>     return NULL;
> -  else if (marker == PPH_RECORD_SHARED)
> -    return (tree) pph_cache_get (stream, ix);
> +  else if (pph_is_reference_marker (marker))
> +    return (tree) pph_cache_get (stream, image_ix, ix);
>
>   /* We did not find the tree in the pickle cache, allocate the tree by
>      reading the header fields (different tree nodes need to be
> @@ -1998,3 +2019,17 @@ pph_read_tree (struct lto_input_block *ib ATTRIBUTE_UNUSED,
>
>   return expr;
>  }
> +
> +
> +/* Finalize the PPH reader.  */
> +
> +void
> +pph_reader_finish (void)
> +{
> +  unsigned i;
> +  pph_stream *image;
> +
> +  /* Close any images read during parsing.  */
> +  FOR_EACH_VEC_ELT (pph_stream_ptr, pph_read_images, i, image)
> +    pph_stream_close (image);
> +}
> diff --git a/gcc/cp/pph-streamer-out.c b/gcc/cp/pph-streamer-out.c
> index 2e5543a..b7a965c 100644
> --- a/gcc/cp/pph-streamer-out.c
> +++ b/gcc/cp/pph-streamer-out.c
> @@ -180,32 +180,43 @@ pph_flush_buffers (pph_stream *stream)
>  static inline bool
>  pph_out_start_record (pph_stream *stream, void *data)
>  {
> -  if (data)
> +  unsigned ix, include_ix;
> +
> +  /* Represent NULL pointers with a single PPH_RECORD_END.  */
> +  if (data == NULL)
> +    {
> +      pph_out_record_marker (stream, PPH_RECORD_END);
> +      return false;
> +    }
> +
> +  /* See if we have data in STREAM's cache.  If so, write an internal
> +     reference to it and inform the caller that it should not write a
> +     physical representation for DATA.  */
> +  if (pph_cache_lookup (stream, data, &ix))
>     {
> -      bool existed_p;
> -      unsigned ix;
> -      enum pph_record_marker marker;
> -
> -      /* If the memory at DATA has already been streamed out, make
> -        sure that we don't write it more than once.  Otherwise,
> -        the reader will instantiate two different pointers for
> -        the same object.
> -
> -        Write the index into the cache where DATA has been stored.
> -        This way, the reader will know at which slot to
> -        re-materialize DATA the first time and where to access it on
> -        subsequent reads.  */
> -      existed_p = pph_cache_add (stream, data, &ix);
> -      marker = (existed_p) ? PPH_RECORD_SHARED : PPH_RECORD_START;
> -      pph_out_uchar (stream, marker);
> +      pph_out_record_marker (stream, PPH_RECORD_IREF);
>       pph_out_uint (stream, ix);
> -      return marker == PPH_RECORD_START;
> +      return false;
>     }
> -  else
> +
> +  /* DATA is not in STREAM's cache.  See if it is in any of STREAM's
> +     included images.  If it is, write an external reference to it
> +     and inform the caller that it should not write a physical
> +     representation for DATA.  */
> +  if (pph_cache_lookup_in_includes (stream, data, &include_ix, &ix))
>     {
> -      pph_out_uchar (stream, PPH_RECORD_END);
> +      pph_out_record_marker (stream, PPH_RECORD_XREF);
> +      pph_out_uint (stream, include_ix);
> +      pph_out_uint (stream, ix);
>       return false;
>     }
> +
> +  /* DATA is in none of the pickle caches, add it to STREAM's pickle
> +     cache and tell the caller that it should pickle DATA out.  */
> +  pph_cache_add (stream, data, &ix);
> +  pph_out_record_marker (stream, PPH_RECORD_START);
> +  pph_out_uint (stream, ix);
> +  return true;
>  }
>
>
> @@ -449,7 +460,25 @@ pph_out_ld_min (pph_stream *stream, struct lang_decl_min *ldm)
>  }
>
>
> -/* Write all the trees in gc VEC V to STREAM.  */
> +/* Return true if T matches FILTER for STREAM.  */
> +
> +static inline bool
> +pph_tree_matches (pph_stream *stream, tree t, unsigned filter)
> +{
> +  if ((filter & PPHF_NO_BUILTINS)
> +      && DECL_P (t)
> +      && DECL_IS_BUILTIN (t))
> +    return false;
> +
> +  if ((filter & PPHF_NO_XREFS)
> +      && pph_cache_lookup_in_includes (stream, t, NULL, NULL))
> +    return false;
> +
> +  return true;
> +}
> +
> +
> +/* Write all the trees in VEC V to STREAM.  */
>
>  static void
>  pph_out_tree_vec (pph_stream *stream, VEC(tree,gc) *v)
> @@ -463,6 +492,34 @@ pph_out_tree_vec (pph_stream *stream, VEC(tree,gc) *v)
>  }
>
>
> +/* Write all the trees matching FILTER in VEC V to STREAM.  */
> +
> +static void
> +pph_out_tree_vec_filtered (pph_stream *stream, VEC(tree,gc) *v, unsigned filter)
> +{
> +  unsigned i;
> +  tree t;
> +  VEC(tree, heap) *to_write = NULL;
> +
> +  /* Special case.  If the caller wants no filtering, it is much
> +     faster to just call pph_out_tree_vec.  */
> +  if (filter == PPHF_NONE)
> +    {
> +      pph_out_tree_vec (stream, v);
> +      return;
> +    }
> +
> +  /* Collect all the nodes that match the filter.  */
> +  FOR_EACH_VEC_ELT (tree, v, i, t)
> +    if (pph_tree_matches (stream, t, filter))
> +      VEC_safe_push (tree, heap, to_write, t);
> +
> +  /* Write them.  */
> +  pph_out_tree_vec (stream, (VEC(tree,gc) *)to_write);
> +  VEC_free (tree, heap, to_write);
> +}
> +
> +
>  /* Write all the qualified_typedef_usage_t in VEC V to STREAM.  */
>
>  static void
> @@ -482,7 +539,7 @@ pph_out_qual_use_vec (pph_stream *stream, VEC(qualified_typedef_usage_t,gc) *v)
>
>
>  /* Forward declaration to break cyclic dependencies.  */
> -static void pph_out_binding_level (pph_stream *, cp_binding_level *);
> +static void pph_out_binding_level (pph_stream *, cp_binding_level *, unsigned);
>
>
>  /* Helper for pph_out_cxx_binding.  STREAM and CB are as in
> @@ -498,7 +555,7 @@ pph_out_cxx_binding_1 (pph_stream *stream, cxx_binding *cb)
>
>   pph_out_tree_or_ref (stream, cb->value);
>   pph_out_tree_or_ref (stream, cb->type);
> -  pph_out_binding_level (stream, cb->scope);
> +  pph_out_binding_level (stream, cb->scope, PPHF_NONE);
>   bp = bitpack_create (stream->encoder.w.ob->main_stream);
>   bp_pack_value (&bp, cb->value_is_inherited, 1);
>   bp_pack_value (&bp, cb->is_local, 1);
> @@ -573,61 +630,51 @@ pph_out_chained_tree (pph_stream *stream, tree t)
>    nodes that do not match FILTER.  */
>
>  static void
> -pph_out_chain_filtered (pph_stream *stream, tree first,
> -                       enum chain_filter filter)
> +pph_out_chain_filtered (pph_stream *stream, tree first, unsigned filter)
>  {
> -  unsigned count;
>   tree t;
> +  VEC(tree, heap) *to_write = NULL;
>
>   /* Special case.  If the caller wants no filtering, it is much
>      faster to just call pph_out_chain directly.  */
> -  if (filter == NONE)
> +  if (filter == PPHF_NONE)
>     {
>       pph_out_chain (stream, first);
>       return;
>     }
>
> -  /* Count all the nodes that match the filter.  */
> -  for (t = first, count = 0; t; t = TREE_CHAIN (t))
> -    {
> -      if (filter == NO_BUILTINS && DECL_P (t) && DECL_IS_BUILTIN (t))
> -       continue;
> -      count++;
> -    }
> -  pph_out_uint (stream, count);
> -
> -  /* Output all the nodes that match the filter.  */
> +  /* Collect all the nodes that match the filter.  */
>   for (t = first; t; t = TREE_CHAIN (t))
> -    {
> -      /* Apply filters to T.  */
> -      if (filter == NO_BUILTINS && DECL_P (t) && DECL_IS_BUILTIN (t))
> -       continue;
> +    if (pph_tree_matches (stream, t, filter))
> +      VEC_safe_push (tree, heap, to_write, t);
>
> -      pph_out_chained_tree (stream, t);
> -    }
> +  /* Write them.  */
> +  pph_out_tree_vec (stream, (VEC(tree,gc) *)to_write);
> +  VEC_free (tree, heap, to_write);
>  }
>
>
> -/* Write all the fields of cp_binding_level instance BL to STREAM.  */
> +/* Helper for pph_out_binding_level.  Write all the fields of BL to
> +   STREAM, without checking whether BL was in the streamer cache or not.
> +   Do not emit any nodes in BL that do not match FILTER.  */
>
>  static void
> -pph_out_binding_level (pph_stream *stream, cp_binding_level *bl)
> +pph_out_binding_level_1 (pph_stream *stream, cp_binding_level *bl,
> +                        unsigned filter)
>  {
>   unsigned i;
>   cp_class_binding *cs;
>   cp_label_binding *sl;
>   struct bitpack_d bp;
>
> -  if (!pph_out_start_record (stream, bl))
> -    return;
> -
> -  pph_out_chain_filtered (stream, bl->names, NO_BUILTINS);
> -  pph_out_chain_filtered (stream, bl->namespaces, NO_BUILTINS);
> +  pph_out_chain_filtered (stream, bl->names, PPHF_NO_BUILTINS | filter);
> +  pph_out_chain_filtered (stream, bl->namespaces, PPHF_NO_BUILTINS | filter);
>
> -  pph_out_tree_vec (stream, bl->static_decls);
> +  pph_out_tree_vec_filtered (stream, bl->static_decls, filter);
>
> -  pph_out_chain_filtered (stream, bl->usings, NO_BUILTINS);
> -  pph_out_chain_filtered (stream, bl->using_directives, NO_BUILTINS);
> +  pph_out_chain_filtered (stream, bl->usings, PPHF_NO_BUILTINS | filter);
> +  pph_out_chain_filtered (stream, bl->using_directives,
> +                         PPHF_NO_BUILTINS | filter);
>
>   pph_out_uint (stream, VEC_length (cp_class_binding, bl->class_shadowed));
>   FOR_EACH_VEC_ELT (cp_class_binding, bl->class_shadowed, i, cs)
> @@ -641,7 +688,7 @@ pph_out_binding_level (pph_stream *stream, cp_binding_level *bl)
>
>   pph_out_chain (stream, bl->blocks);
>   pph_out_tree_or_ref (stream, bl->this_entity);
> -  pph_out_binding_level (stream, bl->level_chain);
> +  pph_out_binding_level (stream, bl->level_chain, filter);
>   pph_out_tree_vec (stream, bl->dead_vars_from_for);
>   pph_out_chain (stream, bl->statement_list);
>   pph_out_uint (stream, bl->binding_depth);
> @@ -655,6 +702,20 @@ pph_out_binding_level (pph_stream *stream, cp_binding_level *bl)
>  }
>
>
> +/* Write all the fields of cp_binding_level instance BL to STREAM.  Do not
> +   emit any nodes in BL that do not match FILTER.  */
> +
> +static void
> +pph_out_binding_level (pph_stream *stream, cp_binding_level *bl,
> +                      unsigned filter)
> +{
> +  if (!pph_out_start_record (stream, bl))
> +    return;
> +
> +  pph_out_binding_level_1 (stream, bl, filter);
> +}
> +
> +
>  /* Write out the tree_common fields from T to STREAM.  */
>
>  static void
> @@ -708,7 +769,7 @@ pph_out_language_function (pph_stream *stream, struct language_function *lf)
>
>   /* FIXME pph.  We are not writing lf->x_named_labels.  */
>
> -  pph_out_binding_level (stream, lf->bindings);
> +  pph_out_binding_level (stream, lf->bindings, PPHF_NONE);
>   pph_out_tree_vec (stream, lf->x_local_names);
>
>   /* FIXME pph.  We are not writing lf->extern_decl_map.  */
> @@ -847,7 +908,7 @@ pph_out_struct_function (pph_stream *stream, struct function *fn)
>  static void
>  pph_out_ld_ns (pph_stream *stream, struct lang_decl_ns *ldns)
>  {
> -  pph_out_binding_level (stream, ldns->level);
> +  pph_out_binding_level (stream, ldns->level, PPHF_NONE);
>  }
>
>
> @@ -1058,36 +1119,46 @@ pph_out_lang_type (pph_stream *stream, tree type)
>  }
>
>
> -/* Write saved_scope information stored in SS into STREAM.
> -   This does NOT output all fields, it is meant to be used for the
> -   global variable scope_chain only.  */
> +/* Write the global bindings in scope_chain to STREAM.  */
>
>  static void
> -pph_out_scope_chain (pph_stream *stream, struct saved_scope *ss)
> +pph_out_scope_chain (pph_stream *stream)
>  {
>   /* old_namespace should be global_namespace and all entries listed below
>      should be NULL or 0; otherwise the header parsed was incomplete.  */
> -  gcc_assert (ss->old_namespace == global_namespace
> -             && !(ss->class_name
> -                  || ss->class_type
> -                  || ss->access_specifier
> -                  || ss->function_decl
> -                  || ss->template_parms
> -                  || ss->x_saved_tree
> -                  || ss->class_bindings
> -                  || ss->prev
> -                  || ss->unevaluated_operand
> -                  || ss->inhibit_evaluation_warnings
> -                  || ss->x_processing_template_decl
> -                  || ss->x_processing_specialization
> -                  || ss->x_processing_explicit_instantiation
> -                  || ss->need_pop_function_context
> -                  || ss->x_stmt_tree.x_cur_stmt_list
> -                  || ss->x_stmt_tree.stmts_are_full_exprs_p));
> +  gcc_assert (scope_chain->old_namespace == global_namespace
> +             && !(scope_chain->class_name
> +                  || scope_chain->class_type
> +                  || scope_chain->access_specifier
> +                  || scope_chain->function_decl
> +                  || scope_chain->template_parms
> +                  || scope_chain->x_saved_tree
> +                  || scope_chain->class_bindings
> +                  || scope_chain->prev
> +                  || scope_chain->unevaluated_operand
> +                  || scope_chain->inhibit_evaluation_warnings
> +                  || scope_chain->x_processing_template_decl
> +                  || scope_chain->x_processing_specialization
> +                  || scope_chain->x_processing_explicit_instantiation
> +                  || scope_chain->need_pop_function_context
> +                  || scope_chain->x_stmt_tree.x_cur_stmt_list
> +                  || scope_chain->x_stmt_tree.stmts_are_full_exprs_p));
>
>   /* We only need to write out the bindings, everything else should
> -     be NULL or be some temporary disposable state.  */
> -  pph_out_binding_level (stream, ss->bindings);
> +     be NULL or be some temporary disposable state.
> +
> +     Note that we explicitly force the pickling of
> +     scope_chain->bindings.  If we had previously read another PPH
> +     image, scope_chain->bindings will be in the other image's pickle
> +     cache.  This would cause pph_out_binding_level to emit a cache
> +     reference to it, instead of writing its fields.  */
> +  {
> +    unsigned ix;
> +    pph_cache_add (stream, scope_chain->bindings, &ix);
> +    pph_out_record_marker (stream, PPH_RECORD_START);
> +    pph_out_uint (stream, ix);
> +    pph_out_binding_level_1 (stream, scope_chain->bindings, PPHF_NO_XREFS);
> +  }
>  }
>
>
> @@ -1278,24 +1349,30 @@ pph_out_line_table (pph_stream *stream, struct line_maps *linetab)
>   pph_out_uint (stream, linetab->max_column_hint);
>  }
>
> -/* Write PPH output symbols and IDENTS_USED to STREAM as an object.  */
> +/* Write all the contents of STREAM.  */
>
>  static void
> -pph_write_file_contents (pph_stream *stream, cpp_idents_used *idents_used)
> -{
> +pph_write_file (pph_stream *stream)
> +{
> +  cpp_idents_used idents_used;
> +
> +  if (flag_pph_debug >= 1)
> +    fprintf (pph_logfile, "PPH: Writing %s\n", pph_out_file);
> +
>   /* Emit the list of PPH files included by STREAM.  These files will
>      be read and instantiated before any of the content in STREAM.  */
>   pph_out_includes (stream);
>
>   /* Emit all the identifiers and pre-processor symbols in the global
>      namespace.  */
> -  pph_out_identifiers (stream, idents_used);
> +  idents_used = cpp_lt_capture (parse_in);
> +  pph_out_identifiers (stream, &idents_used);
>
>   /* Emit the line table entries.  */
>   pph_out_line_table (stream, line_table);
>
>   /* Emit the bindings for the global namespace.  */
> -  pph_out_scope_chain (stream, scope_chain);
> +  pph_out_scope_chain (stream);
>   if (flag_pph_dump_tree)
>     pph_dump_namespace (pph_logfile, global_namespace);
>
> @@ -1314,21 +1391,6 @@ pph_write_file_contents (pph_stream *stream, cpp_idents_used *idents_used)
>  }
>
>
> -/* Write all the collected parse trees to STREAM.  */
> -
> -static void
> -pph_write_file (pph_stream *stream)
> -{
> -  cpp_idents_used idents_used;
> -
> -  if (flag_pph_debug >= 1)
> -    fprintf (pph_logfile, "PPH: Writing %s\n", pph_out_file);
> -
> -  idents_used = cpp_lt_capture (parse_in);
> -  pph_write_file_contents (stream, &idents_used);
> -}
> -
> -
>  /* Emit the fields of FUNCTION_DECL FNDECL to STREAM.  */
>
>  static void
> @@ -1794,14 +1856,16 @@ pph_add_decl_to_symtab (tree decl)
>  }
>
>
> -/* Add STREAM to the list of files included by pph_out_stream.  */
> +/* Add INCLUDE to the list of files included by STREAM.  If STREAM is
> +   NULL, INCLUDE is added to the list of includes for pph_out_stream
> +   (the image that we are currently generating).  */
>
>  void
> -pph_add_include (pph_stream *stream)
> +pph_add_include (pph_stream *stream, pph_stream *include)
>  {
> -  pph_stream *out_stream = pph_out_stream;
> -  VEC_safe_push (pph_stream_ptr, heap, out_stream->includes, stream);
> -  stream->nested_p = true;
> +  if (stream == NULL)
> +    stream = pph_out_stream;
> +  VEC_safe_push (pph_stream_ptr, heap, stream->includes, include);
>  }
>
>
> @@ -1823,14 +1887,17 @@ pph_writer_init (void)
>  void
>  pph_writer_finish (void)
>  {
> -  pph_stream *out_stream = pph_out_stream;
> -  const char *offending_file = cpp_main_missing_guard (parse_in);
> +  const char *offending_file;
> +
> +  if (pph_out_stream == NULL)
> +    return;
>
> +  offending_file = cpp_main_missing_guard (parse_in);
>   if (offending_file == NULL)
> -    pph_write_file (out_stream);
> +    pph_write_file (pph_out_stream);
>   else
>     error ("header lacks guard for PPH: %s", offending_file);
>
> -  pph_stream_close (out_stream);
> +  pph_stream_close (pph_out_stream);
>   pph_out_stream = NULL;
>  }
> diff --git a/gcc/cp/pph-streamer.c b/gcc/cp/pph-streamer.c
> index afabeb5..1ac5bf4 100644
> --- a/gcc/cp/pph-streamer.c
> +++ b/gcc/cp/pph-streamer.c
> @@ -114,7 +114,6 @@ pph_stream_open (const char *name, const char *mode)
>   stream->name = xstrdup (name);
>   stream->write_p = (strchr (mode, 'w') != NULL);
>   stream->cache.m = pointer_map_create ();
> -  stream->open_p = true;
>   stream->symtab.m = pointer_set_create ();
>   if (stream->write_p)
>     pph_init_write (stream);
> @@ -128,15 +127,11 @@ pph_stream_open (const char *name, const char *mode)
>
>
>
> -/* Helper for pph_stream_close.  Do not check whether STREAM is a
> -   nested header.  */
> +/* Close PPH stream STREAM.  */
>
> -static void
> -pph_stream_close_1 (pph_stream *stream)
> +void
> +pph_stream_close (pph_stream *stream)
>  {
> -  unsigned i;
> -  pph_stream *include;
> -
>   if (flag_pph_debug >= 1)
>     fprintf (pph_logfile, "PPH: Closing %s\n", stream->name);
>
> @@ -147,10 +142,6 @@ pph_stream_close_1 (pph_stream *stream)
>
>   fclose (stream->file);
>
> -  /* Close all the streams we opened for included PPH images.  */
> -  FOR_EACH_VEC_ELT (pph_stream_ptr, stream->includes, i, include)
> -    pph_stream_close_1 (include);
> -
>   /* Deallocate all memory used.  */
>   stream->file = NULL;
>   VEC_free (void_p, heap, stream->cache.v);
> @@ -180,26 +171,6 @@ pph_stream_close_1 (pph_stream *stream)
>  }
>
>
> -/* Close PPH stream STREAM.  */
> -
> -void
> -pph_stream_close (pph_stream *stream)
> -{
> -  /* If STREAM is nested into another PPH file, then we cannot
> -     close it just yet.  The parent PPH file will need to access
> -     STREAM's symbol table (to avoid writing the same symbol
> -     more than once).  In this case, STREAM will be closed by the
> -     parent file.  */
> -  if (stream->nested_p)
> -    {
> -      gcc_assert (!stream->write_p);
> -      return;
> -    }
> -
> -  pph_stream_close_1 (stream);
> -}
> -
> -
>  /* Data types supported by the PPH tracer.  */
>  enum pph_trace_type
>  {
> @@ -420,13 +391,12 @@ pph_cache_insert_at (pph_stream *stream, void *data, unsigned ix)
>  }
>
>
> -/* Add pointer DATA to the pickle cache in STREAM.  If IX_P is not
> -   NULL, on exit *IX_P will contain the slot number where DATA is
> -   stored.  Return true if DATA already existed in the cache, false
> -   otherwise.  */
> +/* Return true if DATA exists in STREAM's pickle cache.  If IX_P is not
> +   NULL, store the cache slot where DATA resides in *IX_P (or (unsigned)-1
> +   if DATA is not found).  */
>
>  bool
> -pph_cache_add (pph_stream *stream, void *data, unsigned *ix_p)
> +pph_cache_lookup (pph_stream *stream, void *data, unsigned *ix_p)
>  {
>   void **map_slot;
>   unsigned ix;
> @@ -436,13 +406,12 @@ pph_cache_add (pph_stream *stream, void *data, unsigned *ix_p)
>   if (map_slot == NULL)
>     {
>       existed_p = false;
> -      ix = VEC_length (void_p, stream->cache.v);
> -      pph_cache_insert_at (stream, data, ix);
> +      ix = (unsigned) -1;
>     }
>   else
>     {
> -      unsigned HOST_WIDE_INT slot_ix = (unsigned HOST_WIDE_INT) *map_slot;
> -      gcc_assert (slot_ix == (unsigned) slot_ix);
> +      intptr_t slot_ix = (intptr_t) *map_slot;
> +      gcc_assert (slot_ix == (intptr_t)(unsigned) slot_ix);
>       ix = (unsigned) slot_ix;
>       existed_p = true;
>     }
> @@ -454,13 +423,98 @@ pph_cache_add (pph_stream *stream, void *data, unsigned *ix_p)
>  }
>
>
> -/* Return the pointer at slot IX in STREAM's pickle cache.  */
> +/* Return true if DATA is in the pickle cache of one of STREAM's
> +   included images.
> +
> +   If DATA is found:
> +      - the index for INCLUDE_P into IMAGE->INCLUDES is returned in
> +       *INCLUDE_IX_P (if INCLUDE_IX_P is not NULL),
> +      - the cache slot index for DATA into *INCLUDE_P's pickle cache
> +       is returned in *IX_P (if IX_P is not NULL), and,
> +      - the function returns true.
> +
> +   If DATA is not found:
> +      - *INCLUDE_IX_P is set to -1 (if INCLUDE_IX_P is not NULL),
> +      - *IX_P is set to -1 (if IX_P is not NULL), and,
> +      - the function returns false.  */
> +
> +bool
> +pph_cache_lookup_in_includes (pph_stream *stream, void *data,
> +                             unsigned *include_ix_p, unsigned *ix_p)
> +{
> +  unsigned include_ix, ix;
> +  pph_stream *include;
> +  bool found_it;
> +
> +  found_it = false;
> +  FOR_EACH_VEC_ELT (pph_stream_ptr, stream->includes, include_ix, include)
> +    if (pph_cache_lookup (include, data, &ix))
> +      {
> +       found_it = true;
> +       break;
> +      }
> +
> +  if (!found_it)
> +    {
> +      include_ix = ix = (unsigned) -1;
> +      ix = (unsigned) -1;
> +    }
> +
> +  if (include_ix_p)
> +    *include_ix_p = include_ix;
> +
> +  if (ix_p)
> +    *ix_p = ix;

Instead of wasting time on an if to check that include_ix_p or ix_p
are not null, couldn't we simply put a gcc_assert that they aren't
NULL at the beginning of the function? I can't think of a situation
where we want to call this with NULL pointers, i.e. without retrieving
the values we are asking for... unless sometimes it's called only
caring about the boolean returned value?

> +
> +  return found_it;
> +}
> +
> +
> +/* Add pointer DATA to the pickle cache in STREAM.  If IX_P is not
> +   NULL, on exit *IX_P will contain the slot number where DATA is
> +   stored.  Return true if DATA already existed in the cache, false
> +   otherwise.  */
> +
> +bool
> +pph_cache_add (pph_stream *stream, void *data, unsigned *ix_p)
> +{
> +  unsigned ix;
> +  bool existed_p;
> +
> +  if (pph_cache_lookup (stream, data, &ix))
> +    existed_p = true;
> +  else
> +    {
> +      existed_p = false;
> +      ix = VEC_length (void_p, stream->cache.v);
> +      pph_cache_insert_at (stream, data, ix);
> +    }
> +
> +  if (ix_p)
> +    *ix_p = ix;
> +
> +  return existed_p;
> +}
> +
> +
> +/* Return the pointer at slot IX in STREAM's pickle cache.  If INCLUDE_IX
> +   is not -1U, then instead of looking up in STREAM's pickle cache,
> +   the pointer is looked up in the pickle cache for
> +   STREAM->INCLUDES[INCLUDE_IX].  */
>
>  void *
> -pph_cache_get (pph_stream *stream, unsigned ix)
> +pph_cache_get (pph_stream *stream, unsigned include_ix, unsigned ix)
>  {
> -  void *data = VEC_index (void_p, stream->cache.v, ix);
> -  gcc_assert (data);
> +  void *data;
> +  pph_stream *image;
> +
> +  /* Determine which image's pickle cache to use.  */
> +  if (include_ix == (unsigned) -1)

As mentioned above, I feel this is dangerous..

> +    image = stream;
> +  else
> +    image = VEC_index (pph_stream_ptr, stream->includes, include_ix);
>
> +  data = VEC_index (void_p, image->cache.v, ix);
> +  gcc_assert (data);
>   return data;
>  }
> diff --git a/gcc/cp/pph-streamer.h b/gcc/cp/pph-streamer.h
> index d9fe53b..19578fd 100644
> --- a/gcc/cp/pph-streamer.h
> +++ b/gcc/cp/pph-streamer.h
> @@ -28,9 +28,26 @@ along with GCC; see the file COPYING3.  If not see
>
>  /* Record markers.  */
>  enum pph_record_marker {
> -  PPH_RECORD_START = 0xfd,
> +  /* This record contains the physical representation of the memory data.  */
> +  PPH_RECORD_START = 0x23,
> +
> +  /* End of record marker.  If a record starts with PPH_RECORD_END, the
> +     reader should return a NULL pointer.  */
>   PPH_RECORD_END,
> -  PPH_RECORD_SHARED
> +
> +  /* Internal reference.  This marker indicates that this data has
> +     been written before and it resides in the pickle cache for the
> +     current image.  Following this marker, the reader will find the
> +     cache slot where the data has been stored.  */
> +  PPH_RECORD_IREF,
> +
> +  /* External reference.  This marker indicates that this data has
> +     been written before and it resides in the pickle cache for
> +     another image.  Following this marker, the reader will find two
> +     indices: (1) the index into the include table where the other
> +     image lives, and (2) the cache slot into that image's pickle
> +     cache where the data resides.  */
> +  PPH_RECORD_XREF
>  };
>
>  /* Symbol table markers.  These are all the symbols that need to be
> @@ -155,15 +172,6 @@ typedef struct pph_stream {
>   /* Nonzero if the stream was opened for writing.  */
>   unsigned int write_p : 1;
>
> -  /* Nonzero if the file associated with this stream is open.
> -     After we read a PPH image, we deallocate all the memory used
> -     during streaming, but we keep the stream around to access its
> -     symbol table.  */
> -  unsigned int open_p : 1;
> -
> -  /* Nonzero if this PPH file is included from another PPH file.  */
> -  unsigned int nested_p : 1;
> -
>   /* Symbol table.  This is collected as the compiler instantiates
>     symbols and functions.  Once we finish parsing the header file,
>     this array is written out to the PPH image.  This way, the reader
> @@ -177,8 +185,10 @@ typedef struct pph_stream {
>   VEC(pph_stream_ptr,heap) *includes;
>  } pph_stream;
>
> -/* Filter values for pph_out_chain_filtered.  */
> -enum chain_filter { NONE, NO_BUILTINS };
> +/* Filter values to avoid emitting certain objects to a PPH file.  */
> +#define PPHF_NONE              0
> +#define PPHF_NO_BUILTINS       (1 << 0)
> +#define PPHF_NO_XREFS          (1 << 1)
>
>  /* In pph-streamer.c.  */
>  pph_stream *pph_stream_open (const char *, const char *);
> @@ -192,15 +202,18 @@ void pph_trace_location (pph_stream *, location_t);
>  void pph_trace_chain (pph_stream *, tree);
>  void pph_trace_bitpack (pph_stream *, struct bitpack_d *);
>  void pph_cache_insert_at (pph_stream *, void *, unsigned);
> +bool pph_cache_lookup (pph_stream *, void *, unsigned *);
> +bool pph_cache_lookup_in_includes (pph_stream *, void *, unsigned *,
> +                                  unsigned *);
>  bool pph_cache_add (pph_stream *, void *, unsigned *);
> -void *pph_cache_get (pph_stream *, unsigned);
> +void *pph_cache_get (pph_stream *, unsigned, unsigned);
>
>  /* In pph-streamer-out.c.  */
>  void pph_flush_buffers (pph_stream *);
>  void pph_init_write (pph_stream *);
>  void pph_write_tree (struct output_block *, tree, bool);
>  void pph_add_decl_to_symtab (tree);
> -void pph_add_include (pph_stream *);
> +void pph_add_include (pph_stream *, pph_stream *);
>  void pph_writer_init (void);
>  void pph_writer_finish (void);
>  void pph_write_location (struct output_block *, location_t);
> @@ -214,7 +227,8 @@ struct binding_table_s *pph_in_binding_table (pph_stream *);
>  void pph_init_read (pph_stream *);
>  tree pph_read_tree (struct lto_input_block *, struct data_in *);
>  location_t pph_read_location (struct lto_input_block *, struct data_in *);
> -void pph_read_file (const char *);
> +pph_stream *pph_read_file (const char *);
> +void pph_reader_finish (void);
>
>  /* In pt.c.  */
>  extern void pph_out_pending_templates_list (pph_stream *stream);
> @@ -487,4 +501,24 @@ pph_in_bitpack (pph_stream *stream)
>   return bp;
>  }
>
> +/* Write record MARKER to STREAM.  */
> +static inline void
> +pph_out_record_marker (pph_stream *stream, enum pph_record_marker marker)
> +{
> +  gcc_assert (marker == (enum pph_record_marker)(unsigned char) marker);
> +  pph_out_uchar (stream, marker);
> +}
> +
> +/* Read and return a record marker from STREAM.  */
> +static inline enum pph_record_marker
> +pph_in_record_marker (pph_stream *stream)
> +{
> +  enum pph_record_marker m = (enum pph_record_marker) pph_in_uchar (stream);
> +  gcc_assert (m == PPH_RECORD_START
> +             || m == PPH_RECORD_END
> +             || m == PPH_RECORD_IREF
> +             || m == PPH_RECORD_XREF);
> +  return m;
> +}
> +
>  #endif  /* GCC_CP_PPH_STREAMER_H  */
> diff --git a/gcc/cp/pph.c b/gcc/cp/pph.c
> index 9b92f88..91dc622 100644
> --- a/gcc/cp/pph.c
> +++ b/gcc/cp/pph.c
> @@ -171,9 +171,13 @@ pph_init (void)
>  void
>  pph_finish (void)
>  {
> -  if (pph_out_file != NULL)
> +  /* Finalize the writer.  */
>     pph_writer_finish ();
>
> +  /* Finalize the reader.  */
> +  pph_reader_finish ();
> +
> +  /* Close log files.  */
>   if (flag_pph_debug >= 1)
>     fprintf (pph_logfile, "PPH: Finishing.\n");
>
> diff --git a/gcc/testsuite/ChangeLog.pph b/gcc/testsuite/ChangeLog.pph
> index 7ece8ce..b2b87f0 100644
> --- a/gcc/testsuite/ChangeLog.pph
> +++ b/gcc/testsuite/ChangeLog.pph
> @@ -1,3 +1,9 @@
> +2011-08-15   Diego Novillo  <dnovillo@google.com>
> +
> +       * g++.dg/pph/x1tmplclass2.cc: Update expected ICE.
> +       * g++.dg/pph/x4tmplclass2.cc: Update expected ICE.
> +       * g++.dg/pph/z4tmplclass2.cc: Update expected ICE.
> +
>  2011-08-10   Diego Novillo  <dnovillo@google.com>
>
>        * g++.dg/pph/x1tmplclass2.cc: Update expected ICE message.
> diff --git a/gcc/testsuite/g++.dg/pph/x1tmplclass2.cc b/gcc/testsuite/g++.dg/pph/x1tmplclass2.cc
> index 2c7a8f3..f04335d 100644
> --- a/gcc/testsuite/g++.dg/pph/x1tmplclass2.cc
> +++ b/gcc/testsuite/g++.dg/pph/x1tmplclass2.cc
> @@ -1,5 +1,5 @@
>  // { dg-xfail-if "BOGUS" { "*-*-*" } { "-fpph-map=pph.map" } }
> -// { dg-bogus "x1tmplclass2.cc:1:0: internal compiler error: in pph_in_start_record, at cp/pph-streamer-in.c" "" { xfail *-*-* } 0 }
> +// { dg-bogus "x1tmplclass2.cc:1:0: internal compiler error: in pph_in_record_marker, at cp/pph-streamer.h" "" { xfail *-*-* } 0 }
>
>  #include "x0tmplclass23.h"
>  #include "a0tmplclass2_u.h"
> diff --git a/gcc/testsuite/g++.dg/pph/x4tmplclass2.cc b/gcc/testsuite/g++.dg/pph/x4tmplclass2.cc
> index 7c7e6a5..585d4c0 100644
> --- a/gcc/testsuite/g++.dg/pph/x4tmplclass2.cc
> +++ b/gcc/testsuite/g++.dg/pph/x4tmplclass2.cc
> @@ -1,5 +1,5 @@
>  // { dg-xfail-if "BOGUS" { "*-*-*" } { "-fpph-map=pph.map" } }
> -// { dg-bogus "x4tmplclass2.cc:1:0: internal compiler error: in pph_in_start_record, at cp/pph-streamer-in.c" "" { xfail *-*-* } 0 }
> +// { dg-bogus "x4tmplclass2.cc:1:0: internal compiler error: in pph_in_record_marker, at cp/pph-streamer.h" "" { xfail *-*-* } 0 }
>
>  #include "x0tmplclass21.h"
>  #include "x0tmplclass22.h"
> diff --git a/gcc/testsuite/g++.dg/pph/z4tmplclass2.cc b/gcc/testsuite/g++.dg/pph/z4tmplclass2.cc
> index b932f5e..0243829 100644
> --- a/gcc/testsuite/g++.dg/pph/z4tmplclass2.cc
> +++ b/gcc/testsuite/g++.dg/pph/z4tmplclass2.cc
> @@ -1,5 +1,5 @@
>  // { dg-xfail-if "BOGUS" { "*-*-*" } { "-fpph-map=pph.map" } }
> -// { dg-bogus "z4tmplclass2.cc:1:0: internal compiler error: in pph_in_start_record, at cp/pph-streamer-in.c" "" { xfail *-*-* } 0 }
> +// { dg-bogus "z4tmplclass2.cc:1:0: internal compiler error: in pph_in_record_marker, at cp/pph-streamer.h" "" { xfail *-*-* } 0 }
>
>  #include "x0tmplclass23.h"
>  #include "x0tmplclass24.h"
> --
> 1.7.3.1
>
>
> --
> This patch is available for review at http://codereview.appspot.com/4873051
>



More information about the Gcc-patches mailing list