[pph] Reorganize function order. Improve tracing. (issue5244059)

Lawrence Crowl crowl@google.com
Tue Oct 11 03:52:00 GMT 2011


This patch reorganizes the PPH streamer sources so that all elements
streamed have the same source order in pph-streamer-out.c and
pph-streamer-in.c.

These are further set off into various sections:
    stream initialization
    primitive values
    source information
    record handling
    tree shells
    lexical elements
    vectors
    chains
    bindings
    tree aux classes
    tree base classes
    tree head and body
    file contents
    stream finalization

Removed tracing for everything but trees.  Such tracing is too verbose
for usability.

The existing tree tracing functions were asymmetric, resulting in
confusing traces.  So, I moved all of the tracing into the one
place where the tracing is effective, in the common tree write/read
functions.  All trees should now be properly traced.

The old tracing infrastructure is now dead, and will be removed in a
future patch.

File-level tracing was moved from control under the -fpph-debug flag
to the -fpph-tracer flag.

Improved template debug dumping.

Tested on x64.


Index: gcc/cp/ChangeLog.pph

2011-10-10   Lawrence Crowl  <crowl@google.com>

	* pph.h (pph_dump_location): New.
	* pph.c (pph_dump_location): New.
	(pph_dump_min_decl): Use new pph_dump_location.
        (pph_dump_tree_name): Use decl_as_* from cxx-pretty-print.h for decls,
	types, and exprs.
	(pph_include_handler): Use flag_pph_tracer instead of flag_pph_debug.
	(pph_init): Likewise.
	* pt.c (pph_dump_tinst_level): Add call to pph_dump_location.
	(pph_dump_spec_entry_slot): Add dump labels.
	* pph-streamer.h (pph_new_trace_tree): New.
	* pph-streamer.c (pph_new_trace_tree): New.
	(pph_stream_close): Use flag_pph_tracer instead of flag_pph_debug.
	* pph-streamer-in.c (pph_in_uchar): Remove tracing.
	(pph_in_uint): Likewise.
	(pph_in_bytes): Likewise.
	(pph_in_string): Likewise.
	(pph_in_bitpack): Likewise.
	(pph_in_location): Likewise.
	(pph_in_bitpack): Likewise.
	(pph_in_linenum_type): Move in source file.
	(pph_in_source_location): Likewise.
	(pph_in_linetable_marker): Likewise.
	(pph_in_line_map): Likewise.
	(pph_in_include): Likewise.
	(pph_in_line_table_and_includes): Likewise.
	(pph_in_ld_base): Likewise.
	(pph_in_ld_min): Likewise.
	(pph_in_tree_pair_vec): Likewise.
	(pph_in_mergeable_chain): Likewise.
	(pph_match_to_overload): Likewise.
	(pph_match_to_function): Likewise.
	(pph_match_to_link): Likewise.
	(pph_search_in_chain): Likewise.
	(pph_prepend_to_chain): Likewise.
	(pph_in_tree_common): Likewise.
	(pph_in_ld_fn): Likewise.
	(pph_in_scope_chain): Likewise.
	(wrap_macro_def): Likewise.
	(report_validation_error): Likewise.
	(pph_in_identifiers): Likewise.
	(pph_in_symtab_action): Likewise.
	(pph_in_cgraph_node): Likewise.
	(pph_in_symtab): Likewise.
	(pph_in_linenum_type): Likewise.
	(pph_in_source_location): Likewise.
	(pph_in_line_map): Likewise.
	(pph_in_include): Likewise.
	(pph_in_line_table_and_includes): Likewise.
	(pph_in_tcc_type): Likewise.
	(pph_image_already_read): Likewise.
	(pph_read_file_1): Likewise.
	(pph_add_read_image): Likewise.
	(pph_read_file): Likewise.
	(pph_in_chain): Move in source file.  Remove tracing.
	(pph_in_tree): Reimplement in terms of pph_read_any_tree.
	Move in source file.  Remove tracing.
	(pph_in_mergeable_tree): Likewise.
	(pph_read_tree): Reimplement in terms of pph_read_any_tree.
	Move in source file.
	(pph_read_any_tree): Add call to pph_new_trace_tree.
	* pph-streamer-in.c (pph_out_uchar): Remove tracing.
	(pph_out_uint): Likewise.
	(pph_out_bytes): Likewise.
	(pph_out_string): Likewise.
	(pph_out_string_with_length): Likewise.
	(pph_out_bitpack): Likewise.
	(pph_begin_section): Move in source file.
	(pph_out): Move in source file.  Rename pph_out_block.
	(pph_end_section): Move in source file.
	(pph_write_location) Likewise.
	(pph_out_location) Likewise.
	(pph_out_chain) Likewise.
	(pph_out_header) Likewise.
	(pph_out_body) Likewise.
	(pph_flush_buffers) Likewise.
	(pph_cache_should_handle) Likewise.
	(pph_out_ld_base) Likewise.
	(pph_out_ld_min) Likewise.
	(pph_tree_matches) Likewise.
	(pph_out_cxx_binding_1) Likewise.
	(pph_out_cxx_binding) Likewise.
	(pph_out_label_binding) Likewise.
	(pph_out_tree_common) Likewise.
	(pph_out_ld_fn) Likewise.
	(pph_out_tree_pair_vec) Likewise.
	(pph_out_scope_chain) Likewise.
	(pph_out_identifiers) Likewise.
	(pph_out_symtab_action) Likewise.
	(pph_out_cgraph_node) Likewise.
	(pph_out_symtab) Likewise.
	(pph_out_linenum_type) Likewise.
	(pph_out_source_location) Likewise.
	(pph_out_linetable_marker) Likewise.
	(pph_out_line_map) Likewise.
	(pph_out_include) Likewise.
	(pph_filename_eq_ignoring_path) Likewise.
	(pph_get_next_include) Likewise.
	(pph_out_line_table_and_includes) Likewise.
	(pph_write_file) Likewise.
	(pph_out_tcc_type) Likewise.
	(pph_write_tree) Likewise.
	(pph_write_mergeable_tree) Likewise.
	(pph_add_include) Likewise.
	(pph_writer_init) Likewise.
	(pph_out_chain): Move in source file.  Remove tracing.
	(pph_out_mergeable_chain): Likewise.
	(pph_out_tree_1) Remove.  Replace all calls with pph_out_tree.
	(pph_out_tree) Reimplement in terms of pph_write_any_tree.
        Move in source file.  Remove tracing.
	(pph_out_mergeable_tree) Likewise.
	(pph_write_tree) Reimplement in terms of pph_write_any_tree.
        Move in source file.



Index: gcc/cp/pph.c
===================================================================
--- gcc/cp/pph.c	(revision 179636)
+++ gcc/cp/pph.c	(working copy)
@@ -35,6 +35,7 @@ along with GCC; see the file COPYING3.  
 #include "tree-dump.h"
 #include "tree-inline.h"
 #include "tree-pretty-print.h"
+#include "cxx-pretty-print.h"
 #include "parser.h"
 #include "pph-streamer.h"
 
@@ -51,15 +52,24 @@ pph_tree_code_text (enum tree_code code)
   return tree_code_name[code];
 }
 
+
+/* Dump a location LOC to FILE.  */
+
+void
+pph_dump_location (FILE *file, location_t loc)
+{
+  expanded_location xloc = expand_location (loc);
+  fprintf (file, "%s:%d", xloc.file, xloc.line);
+}
+
 /* Dump identifying information for a minimal DECL to FILE.  */
 
 void
 pph_dump_min_decl (FILE *file, tree decl)
 {
-  expanded_location xloc = expand_location (DECL_SOURCE_LOCATION (decl));
   print_generic_expr (file, DECL_NAME (decl), 0);
   print_generic_expr (file, DECL_CONTEXT (decl), 0);
-  fprintf (file, "%s:%d", xloc.file, xloc.line);
+  pph_dump_location (file, DECL_SOURCE_LOCATION (decl));
 }
 
 /* Dump a complicated name for tree T to FILE using FLAGS.
@@ -68,6 +78,7 @@ pph_dump_min_decl (FILE *file, tree decl
 void
 pph_dump_tree_name (FILE *file, tree t, int flags)
 {
+#if 0
   enum tree_code code = TREE_CODE (t);
   fprintf (file, "%s\t", pph_tree_code_text (code));
   if (code == FUNCTION_TYPE || code == METHOD_TYPE)
@@ -82,6 +93,16 @@ pph_dump_tree_name (FILE *file, tree t, 
       print_generic_expr (file, t, flags);
     }
   fprintf (file, "\n");
+#else
+  if (DECL_P (t))
+    fprintf (file, "%s\n", decl_as_string (t, flags));
+  else if (TYPE_P (t))
+    fprintf (file, "%s\n", type_as_string (t, flags));
+  else if (EXPR_P (t))
+    fprintf (file, "%s\n", expr_as_string (t, flags));
+  else
+    print_generic_expr (file, t, flags);
+#endif
 }
 
 
@@ -145,7 +166,7 @@ pph_include_handler (cpp_reader *reader,
   const char *pph_file;
   bool read_text_file_p;
 
-  if (flag_pph_debug >= 1)
+  if (flag_pph_tracer >= 1)
     {
       fprintf (pph_logfile, "PPH: #%s", dname);
       fprintf (pph_logfile, " %c", angle_brackets ? '<' : '"');
@@ -193,7 +214,7 @@ pph_init (void)
   else
     pph_logfile = stdout;
 
-  if (flag_pph_debug >= 1)
+  if (flag_pph_tracer >= 1)
     fprintf (pph_logfile, "PPH: Initializing.\n");
 
   /* Set up the libcpp handler for #include.  */
@@ -226,7 +247,7 @@ pph_finish (void)
   pph_reader_finish ();
 
   /* Close log files.  */
-  if (flag_pph_debug >= 1)
+  if (flag_pph_tracer >= 1)
     fprintf (pph_logfile, "PPH: Finishing.\n");
 
   if (flag_pph_logfile)
Index: gcc/cp/pph.h
===================================================================
--- gcc/cp/pph.h	(revision 179709)
+++ gcc/cp/pph.h	(working copy)
@@ -134,6 +134,7 @@ extern FILE *pph_logfile;
 /* In pph.c  */
 extern void pph_init (void);
 extern void pph_finish (void);
+extern void pph_dump_location (FILE *file, location_t loc);
 extern void pph_dump_tree_name (FILE *file, tree t, int flags);
 
 /* In pph-streamer-out.c.  */
Index: gcc/cp/pph-streamer-in.c
===================================================================
--- gcc/cp/pph-streamer-in.c	(revision 179709)
+++ gcc/cp/pph-streamer-in.c	(working copy)
@@ -85,6 +85,9 @@ static int pph_reading_includes = 0;
 static int pph_loc_offset;
 
 
+/***************************************************** stream initialization */
+
+
 /* Read into memory the contents of the file in STREAM.  Initialize
    internal tables and data structures needed to re-construct the
    ASTs in the file.  */
@@ -147,17 +150,21 @@ pph_init_read (pph_stream *stream)
 }
 
 
+/********************************************************** primitive values */
+
+
 /* Read an unsigned char VALUE to STREAM.  */
+
 static unsigned char
 pph_in_uchar (pph_stream *stream)
 {
   unsigned char n = streamer_read_uchar (stream->encoder.r.ib);
-  if (flag_pph_tracer >= 4)
-    pph_trace_uint (stream, n);
   return n;
 }
 
+
 /* Read a HOST_WIDE_INT from STREAM.  */
+
 static inline HOST_WIDE_INT
 pph_in_hwi (pph_stream *stream)
 {
@@ -166,6 +173,7 @@ pph_in_hwi (pph_stream *stream)
 
 
 /* Read an unsigned HOST_WIDE_INT from STREAM.  */
+
 static inline unsigned HOST_WIDE_INT
 pph_in_uhwi (pph_stream *stream)
 {
@@ -174,25 +182,23 @@ pph_in_uhwi (pph_stream *stream)
 
 
 /* Read an unsigned integer from STREAM.  */
+
 unsigned int
 pph_in_uint (pph_stream *stream)
 {
   HOST_WIDE_INT unsigned n = streamer_read_uhwi (stream->encoder.r.ib);
   gcc_assert (n == (unsigned) n);
-  if (flag_pph_tracer >= 4)
-    pph_trace_uint (stream, n);
   return (unsigned) n;
 }
 
 
 /* Read N bytes from STREAM into P.  The caller is responsible for
    allocating a sufficiently large buffer.  */
+
 static void
 pph_in_bytes (pph_stream *stream, void *p, size_t n)
 {
   lto_input_data_block (stream->encoder.r.ib, p, n);
-  if (flag_pph_tracer >= 4)
-    pph_trace_bytes (stream, p, n);
 }
 
 
@@ -203,23 +209,208 @@ pph_in_string (pph_stream *stream)
 {
   const char *s = streamer_read_string (stream->encoder.r.data_in,
                                         stream->encoder.r.ib);
-  if (flag_pph_tracer >= 4)
-    pph_trace_string (stream, s);
   return s;
 }
 
 
 /* Read a bitpack from STREAM.  */
+
 static struct bitpack_d
 pph_in_bitpack (pph_stream *stream)
 {
   struct bitpack_d bp = streamer_read_bitpack (stream->encoder.r.ib);
-  if (flag_pph_tracer >= 4)
-    pph_trace_bitpack (stream, &bp);
   return bp;
 }
 
 
+/******************************************************** source information */
+
+
+/* Read a linenum_type from STREAM.  */
+
+static inline linenum_type
+pph_in_linenum_type (pph_stream *stream)
+{
+  return (linenum_type) pph_in_uint (stream);
+}
+
+
+/* Read a source_location from STREAM.  */
+
+static inline source_location
+pph_in_source_location (pph_stream *stream)
+{
+  return (source_location) pph_in_uint (stream);
+}
+
+
+/* Read a line table marker from STREAM.  */
+
+static inline enum pph_linetable_marker
+pph_in_linetable_marker (pph_stream *stream)
+{
+  enum pph_linetable_marker m =
+    (enum pph_linetable_marker) pph_in_uchar (stream);
+  gcc_assert (m == PPH_LINETABLE_ENTRY
+	      || m == PPH_LINETABLE_REFERENCE
+	      || m == PPH_LINETABLE_END);
+  return m;
+}
+
+
+/* Read a line_map from STREAM into LM.  */
+
+static void
+pph_in_line_map (pph_stream *stream, struct line_map *lm)
+{
+  struct bitpack_d bp;
+
+  lm->to_file = pph_in_string (stream);
+  lm->to_line = pph_in_linenum_type (stream);
+  lm->start_location = pph_in_source_location (stream);
+  lm->included_from = (int) pph_in_uint (stream);
+
+  bp = pph_in_bitpack (stream);
+  lm->reason = (enum lc_reason) bp_unpack_value (&bp, LC_REASON_BIT);
+  gcc_assert (lm->reason == LC_ENTER
+              || lm->reason == LC_LEAVE
+              || lm->reason == LC_RENAME
+              || lm->reason == LC_RENAME_VERBATIM);
+  lm->sysp = (unsigned char) bp_unpack_value (&bp, CHAR_BIT);
+  lm->column_bits = bp_unpack_value (&bp, COLUMN_BITS_BIT);
+}
+
+
+/* Read in from STREAM and merge a referenced include into the current parsing
+   context.  */
+
+static void
+pph_in_include (pph_stream *stream)
+{
+  int old_loc_offset;
+  const char *include_name;
+  source_location prev_start_loc = pph_in_source_location (stream);
+
+  /* Simulate highest_location to be as it would be at this point in a non-pph
+     compilation.  */
+  line_table->highest_location = (prev_start_loc - 1) + pph_loc_offset;
+
+  /* FIXME pph: If we move pph_loc_offset to pph_stream.encoder.r, we could
+     have an independent offset for each stream and not have to save and
+     restore the state of a global pph_loc_offset as we are doing here.  */
+  old_loc_offset = pph_loc_offset;
+
+  include_name = pph_in_string (stream);
+  pph_read_file (include_name);
+
+  pph_loc_offset = old_loc_offset;
+}
+
+
+/* Read the line_table from STREAM and merge it in the current line_table.  At
+   the same time load includes in the order they were originally included by
+   loading them at the point they were referenced in the line_table.
+
+   Returns the source_location of line 1 / col 0 for this include.
+
+   FIXME pph: The line_table is now identical to the non-pph line_table, the
+   only problem is that we load line_table entries twice for headers that are
+   re-included and are #ifdef guarded; thus shouldn't be replayed.  This is
+   a known current issue, so I didn't bother working around it here for now.  */
+
+static source_location
+pph_in_line_table_and_includes (pph_stream *stream)
+{
+  unsigned int old_depth;
+  bool first;
+  int includer_ix = -1;
+  unsigned int used_before = line_table->used;
+  int entries_offset = line_table->used - PPH_NUM_IGNORED_LINE_TABLE_ENTRIES;
+  enum pph_linetable_marker next_lt_marker = pph_in_linetable_marker (stream);
+
+  pph_reading_includes++;
+
+  for (first = true; next_lt_marker != PPH_LINETABLE_END;
+       next_lt_marker = pph_in_linetable_marker (stream))
+    {
+      if (next_lt_marker == PPH_LINETABLE_REFERENCE)
+	{
+	  gcc_assert (!first);
+	  pph_in_include (stream);
+	}
+      else
+	{
+	  struct line_map *lm;
+
+	  linemap_ensure_extra_space_available (line_table);
+
+	  lm = &line_table->maps[line_table->used];
+
+	  pph_in_line_map (stream, lm);
+
+	  if (first)
+	    {
+	      first = false;
+
+	      pph_loc_offset = (line_table->highest_location + 1)
+		               - lm->start_location;
+
+	      includer_ix = line_table->used - 1;
+
+	      gcc_assert (lm->included_from == -1);
+	    }
+
+	  gcc_assert (includer_ix != -1);
+
+	  /* When parsing the pph: the header itself wasn't included by
+	    anything, now it's included by the file just before it in
+	    the current include tree.  */
+	  if (lm->included_from == -1)
+	    lm->included_from = includer_ix;
+	  /* For the other entries in the pph's line_table which were included
+	     from another entry, reflect their included_from to the new position
+	     of the entry which they were included from.  */
+	  else
+	    lm->included_from += entries_offset;
+
+	  gcc_assert (lm->included_from < (int) line_table->used);
+
+	  lm->start_location += pph_loc_offset;
+
+	  line_table->used++;
+	}
+    }
+
+  pph_reading_includes--;
+
+  {
+    unsigned int expected_in = pph_in_uint (stream);
+    gcc_assert (line_table->used - used_before == expected_in);
+  }
+
+  line_table->highest_location = pph_loc_offset + pph_in_uint (stream);
+  line_table->highest_line = pph_loc_offset + pph_in_uint (stream);
+
+  /* The MAX_COLUMN_HINT can be directly overwritten.  */
+  line_table->max_column_hint = pph_in_uint (stream);
+
+  /* The line_table doesn't store the last LC_LEAVE in any given compilation;
+     thus we need to replay the LC_LEAVE for the header now.  For that same
+     reason, the line_table should currently be in a state representing a depth
+     one include deeper then the depth at which this pph was included.  The
+     LC_LEAVE replay will then bring the depth back to what it was before
+     calling this function.  */
+  old_depth = line_table->depth++;
+  linemap_add (line_table, LC_LEAVE, 0, NULL, 0);
+  gcc_assert (line_table->depth == old_depth);
+
+  return line_table->maps[used_before].start_location;
+}
+
+
+/*********************************************************** record handling */
+
+
 /* Read and return a record marker from STREAM.  On return, *TAG_P will
    contain the tag for the data type stored in this record.  */
 enum pph_record_marker
@@ -306,6 +497,48 @@ pph_in_start_record (pph_stream *stream,
 }
 
 
+/*************************************************************** tree shells */
+
+
+/* The core tree reader is defined much later.  */
+
+static tree pph_read_any_tree (pph_stream *stream, tree *chain);
+
+
+/* Load an AST from STREAM.  Return the corresponding tree.  */
+tree
+pph_in_tree (pph_stream *stream)
+{
+  tree t = pph_read_any_tree (stream, NULL);
+  return t;
+}
+
+
+/* Load an AST in an ENCLOSING_NAMESPACE from STREAM.
+   Return the corresponding tree.  */
+static void
+pph_in_mergeable_tree (pph_stream *stream, tree *chain)
+{
+  pph_read_any_tree (stream, chain);
+}
+
+
+/* Callback for reading ASTs from a stream.  Instantiate and return a
+   new tree from the PPH stream in DATA_IN.  */
+
+tree
+pph_read_tree (struct lto_input_block *ib_unused ATTRIBUTE_UNUSED,
+	       struct data_in *root_data_in)
+{
+  /* Find data.  */
+  pph_stream *stream = (pph_stream *) root_data_in->sdata;
+  return pph_read_any_tree (stream, NULL);
+}
+
+
+/********************************************************** lexical elements */
+
+
 /* Callback for streamer_hooks.input_location.  An offset is applied to
    the location_t read in according to the properties of the merged
    line_table.  IB and DATA_IN are as in lto_input_location.  This function
@@ -342,8 +575,6 @@ pph_in_location (pph_stream *stream)
 {
   location_t loc = pph_read_location (stream->encoder.r.ib,
                                        stream->encoder.r.data_in);
-  if (flag_pph_tracer >= 4)
-    pph_trace_location (stream, loc);
   return loc;
 }
 
@@ -432,42 +663,7 @@ pph_in_token_cache (pph_stream *stream)
 }
 
 
-/* Read all fields in lang_decl_base instance LDB from STREAM.  */
-
-static void
-pph_in_ld_base (pph_stream *stream, struct lang_decl_base *ldb)
-{
-  struct bitpack_d bp;
-
-  bp = pph_in_bitpack (stream);
-  ldb->selector = bp_unpack_value (&bp, 16);
-  ldb->language = (enum languages) bp_unpack_value (&bp, 4);
-  ldb->use_template = bp_unpack_value (&bp, 2);
-  ldb->not_really_extern = bp_unpack_value (&bp, 1);
-  ldb->initialized_in_class = bp_unpack_value (&bp, 1);
-  ldb->repo_available_p = bp_unpack_value (&bp, 1);
-  ldb->threadprivate_or_deleted_p = bp_unpack_value (&bp, 1);
-  ldb->anticipated_p = bp_unpack_value (&bp, 1);
-  ldb->friend_attr = bp_unpack_value (&bp, 1);
-  ldb->template_conv_p = bp_unpack_value (&bp, 1);
-  ldb->odr_used = bp_unpack_value (&bp, 1);
-  ldb->u2sel = bp_unpack_value (&bp, 1);
-}
-
-
-/* Read all the fields in lang_decl_min instance LDM from STREAM.  */
-
-static void
-pph_in_ld_min (pph_stream *stream, struct lang_decl_min *ldm)
-{
-  ldm->template_info = pph_in_tree (stream);
-  if (ldm->base.u2sel == 0)
-    ldm->u2.access = pph_in_tree (stream);
-  else if (ldm->base.u2sel == 1)
-    ldm->u2.discriminator = pph_in_uint (stream);
-  else
-    gcc_unreachable ();
-}
+/******************************************************************* vectors */
 
 
 /* Read and return a gc VEC of trees from STREAM.  */
@@ -513,6 +709,195 @@ pph_in_qual_use_vec (pph_stream *stream)
 }
 
 
+/* Read the vector V of tree_pair_s instances from STREAM.  */
+
+static VEC(tree_pair_s,gc) *
+pph_in_tree_pair_vec (pph_stream *stream)
+{
+  unsigned i, num;
+  VEC(tree_pair_s,gc) *v;
+
+  num = pph_in_uint (stream);
+  for (i = 0, v = NULL; i < num; i++)
+    {
+      tree_pair_s p;
+      p.purpose = pph_in_tree (stream);
+      p.value = pph_in_tree (stream);
+      VEC_safe_push (tree_pair_s, gc, v, &p);
+    }
+
+  return v;
+}
+
+
+/******************************************************************** chains */
+
+
+/* Read a chain of ASTs from STREAM.  */
+static tree
+pph_in_chain (pph_stream *stream)
+{
+  tree t = streamer_read_chain (stream->encoder.r.ib,
+                                stream->encoder.r.data_in);
+  return t;
+}
+
+
+/* Read and merge a chain of ASTs from STREAM into an existing CHAIN.  */
+
+void
+pph_in_mergeable_chain (pph_stream *stream, tree *chain)
+{
+  int i, count;
+
+  count = streamer_read_hwi (stream->encoder.r.ib);
+  for (i = 0; i < count; i++)
+    pph_in_mergeable_tree (stream, chain);
+}
+
+
+/* Match a new decl EXPR at location WHERE with identifier string IDSTR
+   against an overload set at the LINK of a chain.
+   The EXPR may be added to that set.  */
+
+static tree
+pph_match_to_overload (tree expr ATTRIBUTE_UNUSED,
+			location_t where ATTRIBUTE_UNUSED,
+			const char *idstr, tree *link ATTRIBUTE_UNUSED)
+{
+  /* FIXME crowl: Assume functions are distinct for now.  */
+  if (flag_pph_debug >= 2)
+    fprintf (pph_logfile, "PPH: function \"%s\" assumed distinct\n", idstr);
+  return NULL;
+}
+
+
+/* Match a new decl EXPR at location WHERE with identifier string IDSTR
+   against a function at the LINK of a chain.
+   We may need to create an overload set if EXPR is not the same overload.  */
+
+static tree
+pph_match_to_function (tree expr ATTRIBUTE_UNUSED,
+			location_t where ATTRIBUTE_UNUSED,
+			const char *idstr, tree *link ATTRIBUTE_UNUSED)
+{
+  /* FIXME crowl: Assume functions are distinct for now.  */
+  if (flag_pph_debug >= 2)
+    fprintf (pph_logfile, "PPH: function \"%s\" assumed distinct\n", idstr);
+  return NULL;
+}
+
+
+/* Match a new decl EXPR at location WHERE with identifier string IDSTR
+   against an LINK of a chain. */
+
+static tree
+pph_match_to_link (tree expr, location_t where, const char *idstr, tree* link)
+{
+  enum tree_code link_code, expr_code;
+  tree idtree;
+  const char *idptr;
+
+  link_code = TREE_CODE (*link);
+  if (link_code == TREE_LIST)
+    return pph_match_to_overload (expr, where, idstr, link);
+
+  expr_code = TREE_CODE (expr);
+  if (link_code != expr_code)
+    return NULL;
+
+  idtree = DECL_NAME (*link);
+  if (!idtree)
+    return NULL;
+
+  idptr = IDENTIFIER_POINTER (idtree);
+  if (!idptr)
+    return NULL;
+
+  if (strcmp (idptr, idstr) != 0)
+    {
+      if (flag_pph_debug >= 4)
+        fprintf (pph_logfile, "PPH: link \"%s\" "
+			      "does not match mergeable \"%s\"\n",
+			      idptr, idstr);
+      return NULL;
+    }
+
+  /* A name match!  */
+
+  if (expr_code == FUNCTION_DECL)
+    return pph_match_to_function (expr, where, idstr, link);
+
+  /* A non-function match.  */
+  return *link;
+}
+
+
+/* Possibly merge a new decl EXPR at location WHERE with identifier
+   string IDSTR into an the decl in the CHAIN. */
+
+static tree
+pph_search_in_chain (tree expr, location_t where, const char *idstr,
+			tree *chain)
+{
+  /* FIXME pph: This could resultin O(POW(n,2)) compilation.  */
+  tree *link = chain;
+  while (*link != NULL)
+    {
+      tree found = pph_match_to_link (expr, where, idstr, link);
+      if (found)
+        return found;
+      link = &DECL_CHAIN (*link);
+    }
+  return NULL;
+}
+
+
+/* Prepend an tree EXPR to a CHAIN.  */
+
+static tree
+pph_prepend_to_chain (tree expr, tree *chain)
+{
+  DECL_CHAIN (expr) = *chain;
+  *chain = expr;
+  return expr;
+}
+
+/* Merge the just-read header for tree EXPR onto the CHAIN,
+   which may require reading more from the STREAM.  */
+
+static tree
+pph_merge_into_chain (pph_stream *stream, tree expr, tree *chain)
+{
+  location_t where;
+  const char *idstr;
+  tree found;
+
+  if (!DECL_P (expr))
+    return pph_prepend_to_chain (expr, chain);
+
+  where = pph_in_location (stream);
+  idstr = pph_in_string (stream);
+  if (!idstr)
+    return pph_prepend_to_chain (expr, chain);
+
+  found = pph_search_in_chain (expr, where, idstr, chain);
+  if (!found)
+    {
+      if (flag_pph_debug >= 3)
+        fprintf (pph_logfile, "PPH: %s NOT found on chain\n", idstr);
+      return pph_prepend_to_chain (expr, chain);
+    }
+
+  if (flag_pph_debug >= 3)
+    fprintf (pph_logfile, "PPH: %s FOUND on chain\n", idstr);
+  return found;
+}
+
+
+/****************************************************************** bindings */
+
+
 /* Forward declaration to break cyclic dependencies.  */
 static void pph_in_binding_level (cp_binding_level **,
 				  pph_stream *, cp_binding_level *);
@@ -620,29 +1005,6 @@ pph_in_label_binding (pph_stream *stream
 }
 
 
-/* Read a chain of ASTs from STREAM.  */
-static tree
-pph_in_chain (pph_stream *stream)
-{
-  tree t = streamer_read_chain (stream->encoder.r.ib,
-                                stream->encoder.r.data_in);
-  if (flag_pph_tracer >= 2)
-    pph_trace_chain (stream, t);
-  return t;
-}
-
-
-static void pph_read_mergeable_chain (pph_stream *stream, tree* chain);
-
-
-/* Read and merge a chain of ASTs from STREAM into an existing CHAIN.  */
-static inline void
-pph_in_mergeable_chain (pph_stream *stream, tree* chain)
-{
-  pph_read_mergeable_chain (stream, chain);
-}
-
-
 /* Read and return an instance of cp_binding_level from STREAM.
    TO_REGISTER is used when the caller wants to read a binding level,
    but register a different binding level in the streaming cache.
@@ -749,14 +1111,8 @@ pph_in_binding_level (cp_binding_level *
 }
 
 
-/* Read in the tree_common fields.  */
+/********************************************************** tree aux classes */
 
-static void
-pph_in_tree_common (pph_stream *stream, tree t)
-{
-  /* The 'struct tree_typed typed' base class is handled in LTO.  */
-  TREE_CHAIN (t) = pph_in_tree (stream);
-}
 
 /* Read and return an instance of struct language_function from STREAM.  */
 
@@ -806,51 +1162,6 @@ pph_in_language_function (pph_stream *st
 }
 
 
-/* Read all the fields of lang_decl_fn instance LDF from STREAM.  */
-
-static void
-pph_in_ld_fn (pph_stream *stream, struct lang_decl_fn *ldf)
-{
-  struct bitpack_d bp;
-
-  /* Read all the fields in lang_decl_min.  */
-  pph_in_ld_min (stream, &ldf->min);
-
-  bp = pph_in_bitpack (stream);
-  ldf->operator_code = (enum tree_code) bp_unpack_value (&bp, 16);
-  ldf->global_ctor_p = bp_unpack_value (&bp, 1);
-  ldf->global_dtor_p = bp_unpack_value (&bp, 1);
-  ldf->constructor_attr = bp_unpack_value (&bp, 1);
-  ldf->destructor_attr = bp_unpack_value (&bp, 1);
-  ldf->assignment_operator_p = bp_unpack_value (&bp, 1);
-  ldf->static_function = bp_unpack_value (&bp, 1);
-  ldf->pure_virtual = bp_unpack_value (&bp, 1);
-  ldf->defaulted_p = bp_unpack_value (&bp, 1);
-  ldf->has_in_charge_parm_p = bp_unpack_value (&bp, 1);
-  ldf->has_vtt_parm_p = bp_unpack_value (&bp, 1);
-  ldf->pending_inline_p = bp_unpack_value (&bp, 1);
-  ldf->nonconverting = bp_unpack_value (&bp, 1);
-  ldf->thunk_p = bp_unpack_value (&bp, 1);
-  ldf->this_thunk_p = bp_unpack_value (&bp, 1);
-  ldf->hidden_friend_p = bp_unpack_value (&bp, 1);
-
-  ldf->befriending_classes = pph_in_tree (stream);
-  ldf->context = pph_in_tree (stream);
-
-  if (ldf->thunk_p == 0)
-    ldf->u5.cloned_function = pph_in_tree (stream);
-  else if (ldf->thunk_p == 1)
-    ldf->u5.fixed_offset = pph_in_uint (stream);
-  else
-    gcc_unreachable ();
-
-  if (ldf->pending_inline_p == 1)
-    ldf->u.pending_inline_info = pph_in_token_cache (stream);
-  else if (ldf->pending_inline_p == 0)
-    ldf->u.saved_language_function = pph_in_language_function (stream);
-}
-
-
 /* Read applicable fields of struct function from STREAM.  Associate
    the read structure to DECL.  */
 
@@ -944,6 +1255,89 @@ pph_in_struct_function (pph_stream *stre
 }
 
 
+/* Read all fields in lang_decl_base instance LDB from STREAM.  */
+
+static void
+pph_in_ld_base (pph_stream *stream, struct lang_decl_base *ldb)
+{
+  struct bitpack_d bp;
+
+  bp = pph_in_bitpack (stream);
+  ldb->selector = bp_unpack_value (&bp, 16);
+  ldb->language = (enum languages) bp_unpack_value (&bp, 4);
+  ldb->use_template = bp_unpack_value (&bp, 2);
+  ldb->not_really_extern = bp_unpack_value (&bp, 1);
+  ldb->initialized_in_class = bp_unpack_value (&bp, 1);
+  ldb->repo_available_p = bp_unpack_value (&bp, 1);
+  ldb->threadprivate_or_deleted_p = bp_unpack_value (&bp, 1);
+  ldb->anticipated_p = bp_unpack_value (&bp, 1);
+  ldb->friend_attr = bp_unpack_value (&bp, 1);
+  ldb->template_conv_p = bp_unpack_value (&bp, 1);
+  ldb->odr_used = bp_unpack_value (&bp, 1);
+  ldb->u2sel = bp_unpack_value (&bp, 1);
+}
+
+
+/* Read all the fields in lang_decl_min instance LDM from STREAM.  */
+
+static void
+pph_in_ld_min (pph_stream *stream, struct lang_decl_min *ldm)
+{
+  ldm->template_info = pph_in_tree (stream);
+  if (ldm->base.u2sel == 0)
+    ldm->u2.access = pph_in_tree (stream);
+  else if (ldm->base.u2sel == 1)
+    ldm->u2.discriminator = pph_in_uint (stream);
+  else
+    gcc_unreachable ();
+}
+
+
+/* Read all the fields of lang_decl_fn instance LDF from STREAM.  */
+
+static void
+pph_in_ld_fn (pph_stream *stream, struct lang_decl_fn *ldf)
+{
+  struct bitpack_d bp;
+
+  /* Read all the fields in lang_decl_min.  */
+  pph_in_ld_min (stream, &ldf->min);
+
+  bp = pph_in_bitpack (stream);
+  ldf->operator_code = (enum tree_code) bp_unpack_value (&bp, 16);
+  ldf->global_ctor_p = bp_unpack_value (&bp, 1);
+  ldf->global_dtor_p = bp_unpack_value (&bp, 1);
+  ldf->constructor_attr = bp_unpack_value (&bp, 1);
+  ldf->destructor_attr = bp_unpack_value (&bp, 1);
+  ldf->assignment_operator_p = bp_unpack_value (&bp, 1);
+  ldf->static_function = bp_unpack_value (&bp, 1);
+  ldf->pure_virtual = bp_unpack_value (&bp, 1);
+  ldf->defaulted_p = bp_unpack_value (&bp, 1);
+  ldf->has_in_charge_parm_p = bp_unpack_value (&bp, 1);
+  ldf->has_vtt_parm_p = bp_unpack_value (&bp, 1);
+  ldf->pending_inline_p = bp_unpack_value (&bp, 1);
+  ldf->nonconverting = bp_unpack_value (&bp, 1);
+  ldf->thunk_p = bp_unpack_value (&bp, 1);
+  ldf->this_thunk_p = bp_unpack_value (&bp, 1);
+  ldf->hidden_friend_p = bp_unpack_value (&bp, 1);
+
+  ldf->befriending_classes = pph_in_tree (stream);
+  ldf->context = pph_in_tree (stream);
+
+  if (ldf->thunk_p == 0)
+    ldf->u5.cloned_function = pph_in_tree (stream);
+  else if (ldf->thunk_p == 1)
+    ldf->u5.fixed_offset = pph_in_uint (stream);
+  else
+    gcc_unreachable ();
+
+  if (ldf->pending_inline_p == 1)
+    ldf->u.pending_inline_info = pph_in_token_cache (stream);
+  else if (ldf->pending_inline_p == 0)
+    ldf->u.saved_language_function = pph_in_language_function (stream);
+}
+
+
 /* Read all the fields of lang_decl_ns instance LDNS from STREAM.  */
 
 static void
@@ -1021,6 +1415,19 @@ pph_in_lang_specific (pph_stream *stream
 }
 
 
+/********************************************************* tree base classes */
+
+
+/* Read in the tree_common fields.  */
+
+static void
+pph_in_tree_common (pph_stream *stream, tree t)
+{
+  /* The 'struct tree_typed typed' base class is handled in LTO.  */
+  TREE_CHAIN (t) = pph_in_tree (stream);
+}
+
+
 /* Read all the fields in lang_type_header instance LTH from STREAM.  */
 
 static void
@@ -1039,27 +1446,6 @@ pph_in_lang_type_header (pph_stream *str
 }
 
 
-/* Read the vector V of tree_pair_s instances from STREAM.  */
-
-static VEC(tree_pair_s,gc) *
-pph_in_tree_pair_vec (pph_stream *stream)
-{
-  unsigned i, num;
-  VEC(tree_pair_s,gc) *v;
-
-  num = pph_in_uint (stream);
-  for (i = 0, v = NULL; i < num; i++)
-    {
-      tree_pair_s p;
-      p.purpose = pph_in_tree (stream);
-      p.value = pph_in_tree (stream);
-      VEC_safe_push (tree_pair_s, gc, v, &p);
-    }
-
-  return v;
-}
-
-
 /* Read a struct sorted_fields_type instance SFT to STREAM.  */
 
 static struct sorted_fields_type *
@@ -1214,629 +1600,46 @@ pph_in_lang_type (pph_stream *stream)
 }
 
 
-/* Merge scope_chain bindings from STREAM into the scope_chain
-   bindings of the current translation unit.  This incorporates all
-   the symbols and types from the PPH image into the current TU so
-   name lookup can find identifiers brought from the image.  */
-
-static void
-pph_in_scope_chain (pph_stream *stream)
-{
-  unsigned i;
-  tree decl;
-  cp_class_binding *cb;
-  cp_label_binding *lb;
-  cp_binding_level *cur_bindings, *new_bindings;
-
-  /* When reading the symbols in STREAM's global binding level, make
-     sure that references to the global binding level point to
-     scope_chain->bindings.  Otherwise, identifiers read from STREAM
-     will have the wrong bindings and will fail name lookups.  */
-  cur_bindings = scope_chain->bindings;
-  pph_in_binding_level (&new_bindings, stream, scope_chain->bindings);
-
-  /* Merge the bindings from STREAM into saved_scope->bindings.  */
-  /* FMIXME crowl: The following should already have been done.
-  chainon (cur_bindings->names, new_bindings->names);
-  chainon (cur_bindings->namespaces, new_bindings->namespaces);
-  */
-
-  FOR_EACH_VEC_ELT (tree, new_bindings->static_decls, i, decl)
-    VEC_safe_push (tree, gc, cur_bindings->static_decls, decl);
-
-  chainon (cur_bindings->usings, new_bindings->usings);
-  chainon (cur_bindings->using_directives, new_bindings->using_directives);
-
-  FOR_EACH_VEC_ELT (cp_class_binding, new_bindings->class_shadowed, i, cb)
-    VEC_safe_push (cp_class_binding, gc, cur_bindings->class_shadowed, cb);
-
-  chainon (cur_bindings->type_shadowed, new_bindings->type_shadowed);
-
-  FOR_EACH_VEC_ELT (cp_label_binding, new_bindings->shadowed_labels, i, lb)
-    VEC_safe_push (cp_label_binding, gc, cur_bindings->shadowed_labels, lb);
-
-  chainon (cur_bindings->blocks, new_bindings->blocks);
-
-  gcc_assert (cur_bindings->this_entity == new_bindings->this_entity);
-  gcc_assert (cur_bindings->level_chain == new_bindings->level_chain);
-  gcc_assert (cur_bindings->dead_vars_from_for
-	      == new_bindings->dead_vars_from_for);
-
-  chainon (cur_bindings->statement_list, new_bindings->statement_list);
-
-  gcc_assert (cur_bindings->binding_depth == new_bindings->binding_depth);
-  gcc_assert (cur_bindings->kind == new_bindings->kind);
-  gcc_assert (cur_bindings->explicit_spec_p == new_bindings->explicit_spec_p);
-  gcc_assert (cur_bindings->keep == new_bindings->keep);
-  gcc_assert (cur_bindings->more_cleanups_ok == new_bindings->more_cleanups_ok);
-  gcc_assert (cur_bindings->have_cleanups == new_bindings->have_cleanups);
-}
-
-
-/* Wrap a macro DEFINITION for printing in an error.  */
-
-static char *
-wrap_macro_def (const char *definition)
-{
-  char *string;
-  if (definition)
-    {
-      size_t length;
-      length = strlen (definition);
-      string = (char *) xmalloc (length+3);
-      string[0] = '"';
-      strcpy (string + 1, definition);
-      string[length + 1] = '"';
-      string[length + 2] = '\0';
-    }
-  else
-    string = xstrdup ("undefined");
-  return string;
-}
-
-
-/* Report a macro validation error in FILENAME for macro IDENT,
-   which should have the value EXPECTED but actually had the value FOUND. */
-
-static void
-report_validation_error (const char *filename,
-			 const char *ident, const char *found,
-			 const char *before, const char *after)
-{
-  char* quote_found = wrap_macro_def (found);
-  char* quote_before = wrap_macro_def (before);
-  char* quote_after = wrap_macro_def (after);
-  error ("PPH file %s fails macro validation, "
-         "%s is %s and should be %s or %s\n",
-         filename, ident, quote_found, quote_before, quote_after);
-  free (quote_found);
-  free (quote_before);
-  free (quote_after);
-}
-
-
-/* Load the IDENTIFERS for a hunk from a STREAM.  */
-
-static void
-pph_in_identifiers (pph_stream *stream, cpp_idents_used *identifiers)
-{
-  unsigned int j;
-  unsigned int max_ident_len, max_value_len, num_entries;
-  unsigned int ident_len, before_len, after_len;
-
-  max_ident_len = pph_in_uint (stream);
-  identifiers->max_ident_len = max_ident_len;
-  max_value_len = pph_in_uint (stream);
-  identifiers->max_value_len = max_value_len;
-  num_entries = pph_in_uint (stream);
-  identifiers->num_entries = num_entries;
-  identifiers->entries = XCNEWVEC (cpp_ident_use, num_entries);
-  identifiers->strings = XCNEW (struct obstack);
-
-  /* Strings need no alignment.  */
-  _obstack_begin (identifiers->strings, 0, 0,
-                  (void *(*) (long)) xmalloc,
-                  (void (*) (void *)) free);
-  obstack_alignment_mask (identifiers->strings) = 0;
-  /* FIXME pph: We probably need to free all these things somewhere.  */
-
-  /* Read the identifiers in HUNK. */
-  for (j = 0; j < num_entries; ++j)
-    {
-      const char *s;
-      identifiers->entries[j].used_by_directive = pph_in_uint (stream);
-      identifiers->entries[j].expanded_to_text = pph_in_uint (stream);
-      s = pph_in_string (stream);
-      gcc_assert (s);
-      ident_len = strlen (s);
-      identifiers->entries[j].ident_len = ident_len;
-      identifiers->entries[j].ident_str =
-        (const char *) obstack_copy0 (identifiers->strings, s, ident_len);
-
-      s = pph_in_string (stream);
-      if (s)
-	{
-	  before_len = strlen (s);
-	  identifiers->entries[j].before_len = before_len;
-	  identifiers->entries[j].before_str = (const char *)
-	      obstack_copy0 (identifiers->strings, s, before_len);
-	}
-      else
-	{
-	  /* The identifier table expects NULL entries to have
-	     a length of -1U.  */
-	  identifiers->entries[j].before_len = -1U;
-	  identifiers->entries[j].before_str = NULL;
-	}
-
-      s = pph_in_string (stream);
-      if (s)
-	{
-	  after_len = strlen (s);
-	  identifiers->entries[j].after_len = after_len;
-	  identifiers->entries[j].after_str = (const char *)
-	      obstack_copy0 (identifiers->strings, s, after_len);
-	}
-      else
-	{
-	  /* The identifier table expects NULL entries to have
-	     a length of -1U.  */
-	  identifiers->entries[j].after_len = -1U;
-	  identifiers->entries[j].after_str = NULL;
-	}
-    }
-}
-
-
-/* Read a symbol table marker from STREAM.  */
-
-static inline enum pph_symtab_action
-pph_in_symtab_action (pph_stream *stream)
-{
-  enum pph_symtab_action m = (enum pph_symtab_action) pph_in_uchar (stream);
-  gcc_assert (m == PPH_SYMTAB_DECLARE || m == PPH_SYMTAB_EXPAND);
-  return m;
-}
-
-
-/* Read and return a callgraph node from STREAM.  If this is the first
-   time we read this node, add it to the callgraph.  */
-
-static struct cgraph_node *
-pph_in_cgraph_node (pph_stream *stream)
-{
-  enum pph_record_marker marker;
-  unsigned image_ix, ix;
-  struct cgraph_node *node;
-  tree fndecl;
-  struct bitpack_d bp;
-
-  marker = pph_in_start_record (stream, &image_ix, &ix, PPH_cgraph_node);
-  if (marker == PPH_RECORD_END)
-    return NULL;
-  else if (pph_is_reference_marker (marker))
-    return (struct cgraph_node *) pph_cache_find (stream, marker, image_ix,
-						  ix, PPH_cgraph_node);
-
-  fndecl = pph_in_tree (stream);
-  ALLOC_AND_REGISTER (&stream->cache, ix, PPH_cgraph_node, node,
-                      cgraph_create_node (fndecl));
-
-  node->origin = pph_in_cgraph_node (stream);
-  node->nested = pph_in_cgraph_node (stream);
-  node->next_nested = pph_in_cgraph_node (stream);
-  node->next_needed = pph_in_cgraph_node (stream);
-  node->next_sibling_clone = pph_in_cgraph_node (stream);
-  node->prev_sibling_clone = pph_in_cgraph_node (stream);
-  node->clones = pph_in_cgraph_node (stream);
-  node->clone_of = pph_in_cgraph_node (stream);
-  node->same_comdat_group = pph_in_cgraph_node (stream);
-  gcc_assert (node->call_site_hash == NULL);
-  node->former_clone_of = pph_in_tree (stream);
-  gcc_assert (node->aux == NULL);
-  gcc_assert (VEC_empty (ipa_opt_pass, node->ipa_transforms_to_apply));
-
-  gcc_assert (VEC_empty (ipa_ref_t, node->ref_list.references));
-  gcc_assert (VEC_empty (ipa_ref_ptr, node->ref_list.refering));
-
-  gcc_assert (node->local.lto_file_data == NULL);
-  bp = pph_in_bitpack (stream);
-  node->local.local = bp_unpack_value (&bp, 1);
-  node->local.externally_visible = bp_unpack_value (&bp, 1);
-  node->local.finalized = bp_unpack_value (&bp, 1);
-  node->local.can_change_signature = bp_unpack_value (&bp, 1);
-  node->local.redefined_extern_inline = bp_unpack_value (&bp, 1);
-
-  node->global.inlined_to = pph_in_cgraph_node (stream);
-
-  node->rtl.preferred_incoming_stack_boundary = pph_in_uint (stream);
-
-  gcc_assert (VEC_empty (ipa_replace_map_p, node->clone.tree_map));
-  node->thunk.fixed_offset = pph_in_uhwi (stream);
-  node->thunk.virtual_value = pph_in_uhwi (stream);
-  node->thunk.alias = pph_in_tree (stream);
-  bp = pph_in_bitpack (stream);
-  node->thunk.this_adjusting = bp_unpack_value (&bp, 1);
-  node->thunk.virtual_offset_p = bp_unpack_value (&bp, 1);
-  node->thunk.thunk_p = bp_unpack_value (&bp, 1);
-
-  node->count = pph_in_uhwi (stream);
-  node->count_materialization_scale = pph_in_uint (stream);
-
-  bp = pph_in_bitpack (stream);
-  node->needed = bp_unpack_value (&bp, 1);
-  node->address_taken = bp_unpack_value (&bp, 1);
-  node->abstract_and_needed = bp_unpack_value (&bp, 1);
-  node->reachable = bp_unpack_value (&bp, 1);
-  node->reachable_from_other_partition = bp_unpack_value (&bp, 1);
-  node->lowered = bp_unpack_value (&bp, 1);
-  node->analyzed = bp_unpack_value (&bp, 1);
-  node->in_other_partition = bp_unpack_value (&bp, 1);
-  node->process = bp_unpack_value (&bp, 1);
-  node->alias = bp_unpack_value (&bp, 1);
-  node->same_body_alias = bp_unpack_value (&bp, 1);
-  node->frequency = (enum node_frequency) bp_unpack_value (&bp, 2);
-  node->only_called_at_startup = bp_unpack_value (&bp, 1);
-  node->only_called_at_exit = bp_unpack_value (&bp, 1);
-
-  return node;
-}
-
-
-/* Read the symbol table from STREAM.  When this image is read into
-   another translation unit, we want to guarantee that the IL
-   instances taken from this image are instantiated in the same order
-   that they were instantiated when we generated this image.
-
-   With this, we can generate code in the same order out of the
-   original header files and out of PPH images.  */
-
-static void
-pph_in_symtab (pph_stream *stream)
-{
-  unsigned i, num;
-
-  /* Register all the symbols in STREAM in the same order of the
-     original compilation for this header file.  */
-  num = pph_in_uint (stream);
-  for (i = 0; i < num; i++)
-    {
-      enum pph_symtab_action action;
-      tree decl;
-      bool top_level, at_end;
-
-      action = pph_in_symtab_action (stream);
-      decl = pph_in_tree (stream);
-      if (action == PPH_SYMTAB_DECLARE)
-	{
-	  struct bitpack_d bp;
-	  bp = pph_in_bitpack (stream);
-	  top_level = bp_unpack_value (&bp, 1);
-	  at_end = bp_unpack_value (&bp, 1);
-	  cp_rest_of_decl_compilation (decl, top_level, at_end);
-	}
-      else if (action == PPH_SYMTAB_EXPAND)
-	{
-	  struct cgraph_node *node;
-
-	  pph_in_struct_function (stream, decl);
-	  node = pph_in_cgraph_node (stream);
-	  if (node && node->local.finalized)
-	    {
-	      /* Since the writer had finalized this cgraph node,
-		 we have to re-play its actions.  To do that, we need
-		 to clear the finalized and reachable bits in the
-		 node, otherwise cgraph_finalize_function will toss
-		 out this node.  */
-	      node->local.finalized = false;
-	      node->reachable = false;
-	      cgraph_finalize_function (node->decl, true);
-	    }
-	}
-      else
-	gcc_unreachable ();
-    }
-}
-
-
-/* Read a linenum_type from STREAM.  */
-
-static inline linenum_type
-pph_in_linenum_type (pph_stream *stream)
-{
-  return (linenum_type) pph_in_uint (stream);
-}
-
-
-/* Read a source_location from STREAM.  */
-
-static inline source_location
-pph_in_source_location (pph_stream *stream)
-{
-  return (source_location) pph_in_uint (stream);
-}
-
-
-/* Read a line table marker from STREAM.  */
-
-static inline enum pph_linetable_marker
-pph_in_linetable_marker (pph_stream *stream)
-{
-  enum pph_linetable_marker m =
-    (enum pph_linetable_marker) pph_in_uchar (stream);
-  gcc_assert (m == PPH_LINETABLE_ENTRY
-	      || m == PPH_LINETABLE_REFERENCE
-	      || m == PPH_LINETABLE_END);
-  return m;
-}
-
-
-/* Read a line_map from STREAM into LM.  */
-
-static void
-pph_in_line_map (pph_stream *stream, struct line_map *lm)
-{
-  struct bitpack_d bp;
-
-  lm->to_file = pph_in_string (stream);
-  lm->to_line = pph_in_linenum_type (stream);
-  lm->start_location = pph_in_source_location (stream);
-  lm->included_from = (int) pph_in_uint (stream);
-
-  bp = pph_in_bitpack (stream);
-  lm->reason = (enum lc_reason) bp_unpack_value (&bp, LC_REASON_BIT);
-  gcc_assert (lm->reason == LC_ENTER
-              || lm->reason == LC_LEAVE
-              || lm->reason == LC_RENAME
-              || lm->reason == LC_RENAME_VERBATIM);
-  lm->sysp = (unsigned char) bp_unpack_value (&bp, CHAR_BIT);
-  lm->column_bits = bp_unpack_value (&bp, COLUMN_BITS_BIT);
-}
-
-
-/* Read in from STREAM and merge a referenced include into the current parsing
-   context.  */
+/* Read from STREAM the body of tcc_type node TYPE.  */
 
 static void
-pph_in_include (pph_stream *stream)
-{
-  int old_loc_offset;
-  const char *include_name;
-  source_location prev_start_loc = pph_in_source_location (stream);
-
-  /* Simulate highest_location to be as it would be at this point in a non-pph
-     compilation.  */
-  line_table->highest_location = (prev_start_loc - 1) + pph_loc_offset;
-
-  /* FIXME pph: If we move pph_loc_offset to pph_stream.encoder.r, we could
-     have an independent offset for each stream and not have to save and
-     restore the state of a global pph_loc_offset as we are doing here.  */
-  old_loc_offset = pph_loc_offset;
-
-  include_name = pph_in_string (stream);
-  pph_read_file (include_name);
-
-  pph_loc_offset = old_loc_offset;
-}
-
-
-/* Read the line_table from STREAM and merge it in the current line_table.  At
-   the same time load includes in the order they were originally included by
-   loading them at the point they were referenced in the line_table.
-
-   Returns the source_location of line 1 / col 0 for this include.
-
-   FIXME pph: The line_table is now identical to the non-pph line_table, the
-   only problem is that we load line_table entries twice for headers that are
-   re-included and are #ifdef guarded; thus shouldn't be replayed.  This is
-   a known current issue, so I didn't bother working around it here for now.  */
-
-static source_location
-pph_in_line_table_and_includes (pph_stream *stream)
+pph_in_tcc_type (pph_stream *stream, tree type)
 {
-  unsigned int old_depth;
-  bool first;
-  int includer_ix = -1;
-  unsigned int used_before = line_table->used;
-  int entries_offset = line_table->used - PPH_NUM_IGNORED_LINE_TABLE_ENTRIES;
-  enum pph_linetable_marker next_lt_marker = pph_in_linetable_marker (stream);
-
-  pph_reading_includes++;
+  TYPE_LANG_SPECIFIC (type) = pph_in_lang_type (stream);
+  TYPE_POINTER_TO (type) = pph_in_tree (stream);
+  TYPE_REFERENCE_TO (type) = pph_in_tree (stream);
+  TYPE_NEXT_VARIANT (type) = pph_in_tree (stream);
+  /* FIXME pph - Streaming TYPE_CANONICAL generates many type comparison
+     failures.  Why?  */
+  /* FIXME pph: apparently redundant.  */
+  TREE_CHAIN (type) = pph_in_tree (stream);
 
-  for (first = true; next_lt_marker != PPH_LINETABLE_END;
-       next_lt_marker = pph_in_linetable_marker (stream))
+  /* The type values cache is built as constants are instantiated,
+     so we only stream it on the nodes that use it for
+     other purposes.  */
+  switch (TREE_CODE (type))
     {
-      if (next_lt_marker == PPH_LINETABLE_REFERENCE)
-	{
-	  gcc_assert (!first);
-	  pph_in_include (stream);
-	}
-      else
-	{
-	  struct line_map *lm;
-
-	  linemap_ensure_extra_space_available (line_table);
-
-	  lm = &line_table->maps[line_table->used];
-
-	  pph_in_line_map (stream, lm);
-
-	  if (first)
-	    {
-	      first = false;
-
-	      pph_loc_offset = (line_table->highest_location + 1)
-		               - lm->start_location;
-
-	      includer_ix = line_table->used - 1;
-
-	      gcc_assert (lm->included_from == -1);
-	    }
-
-	  gcc_assert (includer_ix != -1);
-
-	  /* When parsing the pph: the header itself wasn't included by
-	    anything, now it's included by the file just before it in
-	    the current include tree.  */
-	  if (lm->included_from == -1)
-	    lm->included_from = includer_ix;
-	  /* For the other entries in the pph's line_table which were included
-	     from another entry, reflect their included_from to the new position
-	     of the entry which they were included from.  */
-	  else
-	    lm->included_from += entries_offset;
-
-	  gcc_assert (lm->included_from < (int) line_table->used);
-
-	  lm->start_location += pph_loc_offset;
+    case BOUND_TEMPLATE_TEMPLATE_PARM:
+    case DECLTYPE_TYPE:
+    case TEMPLATE_TEMPLATE_PARM:
+    case TEMPLATE_TYPE_PARM:
+    case TYPENAME_TYPE:
+    case TYPEOF_TYPE:
+      TYPE_VALUES_RAW (type) = pph_in_tree (stream);
+      break;
 
-	  line_table->used++;
-	}
+    default:
+      break;
     }
 
-  pph_reading_includes--;
-
-  {
-    unsigned int expected_in = pph_in_uint (stream);
-    gcc_assert (line_table->used - used_before == expected_in);
-  }
-
-  line_table->highest_location = pph_loc_offset + pph_in_uint (stream);
-  line_table->highest_line = pph_loc_offset + pph_in_uint (stream);
-
-  /* The MAX_COLUMN_HINT can be directly overwritten.  */
-  line_table->max_column_hint = pph_in_uint (stream);
-
-  /* The line_table doesn't store the last LC_LEAVE in any given compilation;
-     thus we need to replay the LC_LEAVE for the header now.  For that same
-     reason, the line_table should currently be in a state representing a depth
-     one include deeper then the depth at which this pph was included.  The
-     LC_LEAVE replay will then bring the depth back to what it was before
-     calling this function.  */
-  old_depth = line_table->depth++;
-  linemap_add (line_table, LC_LEAVE, 0, NULL, 0);
-  gcc_assert (line_table->depth == old_depth);
-
-  return line_table->maps[used_before].start_location;
-}
-
-
-/* If FILENAME has already been read, return the stream associated with it.  */
-
-static pph_stream *
-pph_image_already_read (const char *filename)
-{
-  pph_stream *include;
-  unsigned i;
-
-  /* FIXME pph, implement a hash map to avoid this linear search.  */
-  FOR_EACH_VEC_ELT (pph_stream_ptr, pph_read_images, i, include)
-    if (strcmp (include->name, filename) == 0)
-      return include;
-
-  return NULL;
-}
-
-
-/* Helper for pph_read_file.  Read contents of PPH file in STREAM.  */
-
-static void
-pph_read_file_1 (pph_stream *stream)
-{
-  bool verified;
-  cpp_ident_use *bad_use;
-  const char *cur_def;
-  cpp_idents_used idents_used;
-  tree t, file_keyed_classes, file_static_aggregates;
-  unsigned i;
-  VEC(tree,gc) *file_unemitted_tinfo_decls;
-  source_location cpp_token_replay_loc;
-
-  if (flag_pph_debug >= 1)
-    fprintf (pph_logfile, "PPH: Reading %s\n", stream->name);
-
-  /* Read in STREAM's line table and merge it in the current line table.
-     At the same time, read in includes in the order they were originally
-     read.  */
-  cpp_token_replay_loc = pph_in_line_table_and_includes (stream);
-
-  /* If we have read STREAM before, we do not need to re-read the rest
-     of its body.  We only needed to read its line table.  */
-  if (pph_image_already_read (stream->name))
-    return;
-
-  /* Read all the identifiers and pre-processor symbols in the global
-     namespace.  */
-  pph_in_identifiers (stream, &idents_used);
-
-  /* FIXME pph: This validation is weak.  */
-  verified = cpp_lt_verify_1 (parse_in, &idents_used, &bad_use, &cur_def, true);
-  if (!verified)
-    report_validation_error (stream->name, bad_use->ident_str, cur_def,
-                             bad_use->before_str, bad_use->after_str);
-
-  /* Re-instantiate all the pre-processor symbols defined by STREAM.  Force
-     their source_location to line 1 / column 0 of the file they were included
-     in.  This avoids shifting all of the line_table's locations as we would by
-     adding locations which wouldn't be there in the non-pph compile; thus
-     working towards an identical line_table in pph and non-pph.  */
-  cpp_lt_replay (parse_in, &idents_used, &cpp_token_replay_loc);
-
-  /* Read the bindings from STREAM and merge them with the current bindings.  */
-  pph_in_scope_chain (stream);
-
-  if (flag_pph_dump_tree)
-    pph_dump_namespace (pph_logfile, global_namespace);
-
-  /* Read and merge the other global state collected during parsing of
-     the original header.  */
-  file_keyed_classes = pph_in_tree (stream);
-  keyed_classes = chainon (file_keyed_classes, keyed_classes);
-
-  file_unemitted_tinfo_decls = pph_in_tree_vec (stream);
-  FOR_EACH_VEC_ELT (tree, file_unemitted_tinfo_decls, i, t)
-    VEC_safe_push (tree, gc, unemitted_tinfo_decls, t);
-
-  pph_in_pending_templates_list (stream);
-  pph_in_spec_entry_tables (stream);
-
-  file_static_aggregates = pph_in_tree (stream);
-  static_aggregates = chainon (file_static_aggregates, static_aggregates);
-
-  /* Read and process the symbol table.  */
-  pph_in_symtab (stream);
-
-  /* If we are generating an image, the PPH contents we just read from
-     STREAM will need to be read again the next time we want to read
-     the image we are now generating.  */
-  if (pph_writer_enabled_p () && !pph_reading_includes)
-    pph_add_include (stream);
-}
-
-
-/* Add STREAM to the list of read images.  */
-
-static void
-pph_add_read_image (pph_stream *stream)
-{
-  VEC_safe_push (pph_stream_ptr, heap, pph_read_images, stream);
-}
-
-
-/* Read PPH file FILENAME.  Return the in-memory pph_stream instance.  */
-
-void
-pph_read_file (const char *filename)
-{
-  pph_stream *stream;
-
-  stream = pph_stream_open (filename, "rb");
-  if (stream)
-    pph_read_file_1 (stream);
-  else
-    error ("Cannot open PPH file for reading: %s: %m", filename);
-
-  pph_add_read_image (stream);
+  /* If TYPE has a METHOD_VEC, we need to resort it.  Name lookup in
+     classes relies on the specific ordering of the class method
+     pointers.  Since we generally instantiate them in a different
+     order than the original compile, the pointer values will be
+     different.  This will cause name lookups to fail, unless we
+     resort the vector.  */
+  if (TYPE_LANG_SPECIFIC (type) && CLASSTYPE_METHOD_VEC (type))
+    finish_struct_methods (type);
 }
 
 
@@ -1890,47 +1693,7 @@ pph_in_tcc_declaration (pph_stream *stre
 }
 
 
-/* Read from STREAM the body of tcc_type node TYPE.  */
-
-static void
-pph_in_tcc_type (pph_stream *stream, tree type)
-{
-  TYPE_LANG_SPECIFIC (type) = pph_in_lang_type (stream);
-  TYPE_POINTER_TO (type) = pph_in_tree (stream);
-  TYPE_REFERENCE_TO (type) = pph_in_tree (stream);
-  TYPE_NEXT_VARIANT (type) = pph_in_tree (stream);
-  /* FIXME pph - Streaming TYPE_CANONICAL generates many type comparison
-     failures.  Why?  */
-  /* FIXME pph: apparently redundant.  */
-  TREE_CHAIN (type) = pph_in_tree (stream);
-
-  /* The type values cache is built as constants are instantiated,
-     so we only stream it on the nodes that use it for
-     other purposes.  */
-  switch (TREE_CODE (type))
-    {
-    case BOUND_TEMPLATE_TEMPLATE_PARM:
-    case DECLTYPE_TYPE:
-    case TEMPLATE_TEMPLATE_PARM:
-    case TEMPLATE_TYPE_PARM:
-    case TYPENAME_TYPE:
-    case TYPEOF_TYPE:
-      TYPE_VALUES_RAW (type) = pph_in_tree (stream);
-      break;
-
-    default:
-      break;
-    }
-
-  /* If TYPE has a METHOD_VEC, we need to resort it.  Name lookup in
-     classes relies on the specific ordering of the class method
-     pointers.  Since we generally instantiate them in a different
-     order than the original compile, the pointer values will be
-     different.  This will cause name lookups to fail, unless we
-     resort the vector.  */
-  if (TYPE_LANG_SPECIFIC (type) && CLASSTYPE_METHOD_VEC (type))
-    finish_struct_methods (type);
-}
+/******************************************************** tree head and body */
 
 
 /* Read the body fields of EXPR from STREAM.  */
@@ -2174,149 +1937,10 @@ pph_read_tree_header (pph_stream *stream
   return expr;
 }
 
-
-/* Match a new decl EXPR at location WHERE with identifier string IDSTR
-   against an overload set at the LINK of a chain.
-   The EXPR may be added to that set.  */
-
-static tree
-pph_match_to_overload (tree expr ATTRIBUTE_UNUSED,
-			location_t where ATTRIBUTE_UNUSED,
-			const char *idstr, tree *link ATTRIBUTE_UNUSED)
-{
-  /* FIXME crowl: Assume functions are distinct for now.  */
-  if (flag_pph_debug >= 2)
-    fprintf (pph_logfile, "PPH: function \"%s\" assumed distinct\n", idstr);
-  return NULL;
-}
-
-
-/* Match a new decl EXPR at location WHERE with identifier string IDSTR
-   against a function at the LINK of a chain.
-   We may need to create an overload set if EXPR is not the same overload.  */
-
-static tree
-pph_match_to_function (tree expr ATTRIBUTE_UNUSED,
-			location_t where ATTRIBUTE_UNUSED,
-			const char *idstr, tree *link ATTRIBUTE_UNUSED)
-{
-  /* FIXME crowl: Assume functions are distinct for now.  */
-  if (flag_pph_debug >= 2)
-    fprintf (pph_logfile, "PPH: function \"%s\" assumed distinct\n", idstr);
-  return NULL;
-}
-
-
-/* Match a new decl EXPR at location WHERE with identifier string IDSTR
-   against an LINK of a chain. */
-
-static tree
-pph_match_to_link (tree expr, location_t where, const char *idstr, tree* link)
-{
-  enum tree_code link_code, expr_code;
-  tree idtree;
-  const char *idptr;
-
-  link_code = TREE_CODE (*link);
-  if (link_code == TREE_LIST)
-    return pph_match_to_overload (expr, where, idstr, link);
-
-  expr_code = TREE_CODE (expr);
-  if (link_code != expr_code)
-    return NULL;
-
-  idtree = DECL_NAME (*link);
-  if (!idtree)
-    return NULL;
-
-  idptr = IDENTIFIER_POINTER (idtree);
-  if (!idptr)
-    return NULL;
-
-  if (strcmp (idptr, idstr) != 0)
-    {
-      if (flag_pph_debug >= 4)
-        fprintf (pph_logfile, "PPH: link \"%s\" "
-			      "does not match mergeable \"%s\"\n",
-			      idptr, idstr);
-      return NULL;
-    }
-
-  /* A name match!  */
-
-  if (expr_code == FUNCTION_DECL)
-    return pph_match_to_function (expr, where, idstr, link);
-
-  /* A non-function match.  */
-  return *link;
-}
-
-
-/* Possibly merge a new decl EXPR at location WHERE with identifier
-   string IDSTR into an the decl in the CHAIN. */
-
-static tree
-pph_search_in_chain (tree expr, location_t where, const char *idstr,
-			tree *chain)
-{
-  /* FIXME pph: This could resultin O(POW(n,2)) compilation.  */
-  tree *link = chain;
-  while (*link != NULL)
-    {
-      tree found = pph_match_to_link (expr, where, idstr, link);
-      if (found)
-        return found;
-      link = &DECL_CHAIN (*link);
-    }
-  return NULL;
-}
-
-
-/* Prepend an tree EXPR to a CHAIN.  */
-
-static tree
-pph_prepend_to_chain (tree expr, tree *chain)
-{
-  DECL_CHAIN (expr) = *chain;
-  *chain = expr;
-  return expr;
-}
-
-/* Merge the just-read header for tree EXPR onto the CHAIN,
-   which may require reading more from the STREAM.  */
-
-static tree
-pph_merge_into_chain (pph_stream *stream, tree expr, tree *chain)
-{
-  location_t where;
-  const char *idstr;
-  tree found;
-
-  if (!DECL_P (expr))
-    return pph_prepend_to_chain (expr, chain);
-
-  where = pph_in_location (stream);
-  idstr = pph_in_string (stream);
-  if (!idstr)
-    return pph_prepend_to_chain (expr, chain);
-
-  found = pph_search_in_chain (expr, where, idstr, chain);
-  if (!found)
-    {
-      if (flag_pph_debug >= 3)
-        fprintf (pph_logfile, "PPH: %s NOT found on chain\n", idstr);
-      return pph_prepend_to_chain (expr, chain);
-    }
-
-  if (flag_pph_debug >= 3)
-    fprintf (pph_logfile, "PPH: %s FOUND on chain\n", idstr);
-  return found;
-}
-
 /* Read a tree from the STREAM.  It ENCLOSING_NAMESPACE is not null,
    the tree may be unified with an existing tree in that namespace.  */
 
-tree
+static tree
 pph_read_any_tree (pph_stream *stream, tree *chain)
 {
   struct lto_input_block *ib = stream->encoder.r.ib;
@@ -2383,6 +2007,8 @@ pph_read_any_tree (pph_stream *stream, t
   pph_cache_insert_at (&stream->cache, expr, ix, pph_tree_code_to_tag (expr));
   pph_read_tree_body (stream, expr);
 
+  pph_new_trace_tree (stream, expr, chain != NULL);
+
   /* If needed, sign the recently materialized tree to detect
      mutations.  Note that we only need to compute signatures
      if we are generating a PPH image.  That is the only time
@@ -2401,63 +2027,456 @@ pph_read_any_tree (pph_stream *stream, t
 }
 
 
-/* Callback for reading ASTs from a stream.  Instantiate and return a
-   new tree from the PPH stream in DATA_IN.  */
+/************************************************************* file contents */
 
-tree
-pph_read_tree (struct lto_input_block *ib_unused ATTRIBUTE_UNUSED,
-	       struct data_in *root_data_in)
+
+/* Read a symbol table marker from STREAM.  */
+
+static inline enum pph_symtab_action
+pph_in_symtab_action (pph_stream *stream)
 {
-  /* Find data.  */
-  pph_stream *stream = (pph_stream *) root_data_in->sdata;
-  return pph_read_any_tree (stream, NULL);
+  enum pph_symtab_action m = (enum pph_symtab_action) pph_in_uchar (stream);
+  gcc_assert (m == PPH_SYMTAB_DECLARE || m == PPH_SYMTAB_EXPAND);
+  return m;
 }
 
 
-/* Load an AST from STREAM.  Return the corresponding tree.  */
-tree
-pph_in_tree (pph_stream *stream)
+/* Read and return a callgraph node from STREAM.  If this is the first
+   time we read this node, add it to the callgraph.  */
+
+static struct cgraph_node *
+pph_in_cgraph_node (pph_stream *stream)
 {
-  tree t = pph_read_any_tree (stream, NULL);
-  if (flag_pph_tracer >= 4)
-    pph_trace_tree (stream, t);
-  return t;
+  enum pph_record_marker marker;
+  unsigned image_ix, ix;
+  struct cgraph_node *node;
+  tree fndecl;
+  struct bitpack_d bp;
+
+  marker = pph_in_start_record (stream, &image_ix, &ix, PPH_cgraph_node);
+  if (marker == PPH_RECORD_END)
+    return NULL;
+  else if (pph_is_reference_marker (marker))
+    return (struct cgraph_node *) pph_cache_find (stream, marker, image_ix,
+						  ix, PPH_cgraph_node);
+
+  fndecl = pph_in_tree (stream);
+  ALLOC_AND_REGISTER (&stream->cache, ix, PPH_cgraph_node, node,
+                      cgraph_create_node (fndecl));
+
+  node->origin = pph_in_cgraph_node (stream);
+  node->nested = pph_in_cgraph_node (stream);
+  node->next_nested = pph_in_cgraph_node (stream);
+  node->next_needed = pph_in_cgraph_node (stream);
+  node->next_sibling_clone = pph_in_cgraph_node (stream);
+  node->prev_sibling_clone = pph_in_cgraph_node (stream);
+  node->clones = pph_in_cgraph_node (stream);
+  node->clone_of = pph_in_cgraph_node (stream);
+  node->same_comdat_group = pph_in_cgraph_node (stream);
+  gcc_assert (node->call_site_hash == NULL);
+  node->former_clone_of = pph_in_tree (stream);
+  gcc_assert (node->aux == NULL);
+  gcc_assert (VEC_empty (ipa_opt_pass, node->ipa_transforms_to_apply));
+
+  gcc_assert (VEC_empty (ipa_ref_t, node->ref_list.references));
+  gcc_assert (VEC_empty (ipa_ref_ptr, node->ref_list.refering));
+
+  gcc_assert (node->local.lto_file_data == NULL);
+  bp = pph_in_bitpack (stream);
+  node->local.local = bp_unpack_value (&bp, 1);
+  node->local.externally_visible = bp_unpack_value (&bp, 1);
+  node->local.finalized = bp_unpack_value (&bp, 1);
+  node->local.can_change_signature = bp_unpack_value (&bp, 1);
+  node->local.redefined_extern_inline = bp_unpack_value (&bp, 1);
+
+  node->global.inlined_to = pph_in_cgraph_node (stream);
+
+  node->rtl.preferred_incoming_stack_boundary = pph_in_uint (stream);
+
+  gcc_assert (VEC_empty (ipa_replace_map_p, node->clone.tree_map));
+  node->thunk.fixed_offset = pph_in_uhwi (stream);
+  node->thunk.virtual_value = pph_in_uhwi (stream);
+  node->thunk.alias = pph_in_tree (stream);
+  bp = pph_in_bitpack (stream);
+  node->thunk.this_adjusting = bp_unpack_value (&bp, 1);
+  node->thunk.virtual_offset_p = bp_unpack_value (&bp, 1);
+  node->thunk.thunk_p = bp_unpack_value (&bp, 1);
+
+  node->count = pph_in_uhwi (stream);
+  node->count_materialization_scale = pph_in_uint (stream);
+
+  bp = pph_in_bitpack (stream);
+  node->needed = bp_unpack_value (&bp, 1);
+  node->address_taken = bp_unpack_value (&bp, 1);
+  node->abstract_and_needed = bp_unpack_value (&bp, 1);
+  node->reachable = bp_unpack_value (&bp, 1);
+  node->reachable_from_other_partition = bp_unpack_value (&bp, 1);
+  node->lowered = bp_unpack_value (&bp, 1);
+  node->analyzed = bp_unpack_value (&bp, 1);
+  node->in_other_partition = bp_unpack_value (&bp, 1);
+  node->process = bp_unpack_value (&bp, 1);
+  node->alias = bp_unpack_value (&bp, 1);
+  node->same_body_alias = bp_unpack_value (&bp, 1);
+  node->frequency = (enum node_frequency) bp_unpack_value (&bp, 2);
+  node->only_called_at_startup = bp_unpack_value (&bp, 1);
+  node->only_called_at_exit = bp_unpack_value (&bp, 1);
+
+  return node;
 }
 
 
-/* Read a mergeable tree from STREAM into CHAIN.  */
+/* Read the symbol table from STREAM.  When this image is read into
+   another translation unit, we want to guarantee that the IL
+   instances taken from this image are instantiated in the same order
+   that they were instantiated when we generated this image.
 
-static tree
-pph_read_mergeable_tree (pph_stream *stream, tree *chain)
+   With this, we can generate code in the same order out of the
+   original header files and out of PPH images.  */
+
+static void
+pph_in_symtab (pph_stream *stream)
 {
-  return pph_read_any_tree (stream, chain);
+  unsigned i, num;
+
+  /* Register all the symbols in STREAM in the same order of the
+     original compilation for this header file.  */
+  num = pph_in_uint (stream);
+  for (i = 0; i < num; i++)
+    {
+      enum pph_symtab_action action;
+      tree decl;
+      bool top_level, at_end;
+
+      action = pph_in_symtab_action (stream);
+      decl = pph_in_tree (stream);
+      if (action == PPH_SYMTAB_DECLARE)
+	{
+	  struct bitpack_d bp;
+	  bp = pph_in_bitpack (stream);
+	  top_level = bp_unpack_value (&bp, 1);
+	  at_end = bp_unpack_value (&bp, 1);
+	  cp_rest_of_decl_compilation (decl, top_level, at_end);
+	}
+      else if (action == PPH_SYMTAB_EXPAND)
+	{
+	  struct cgraph_node *node;
+
+	  pph_in_struct_function (stream, decl);
+	  node = pph_in_cgraph_node (stream);
+	  if (node && node->local.finalized)
+	    {
+	      /* Since the writer had finalized this cgraph node,
+		 we have to re-play its actions.  To do that, we need
+		 to clear the finalized and reachable bits in the
+		 node, otherwise cgraph_finalize_function will toss
+		 out this node.  */
+	      node->local.finalized = false;
+	      node->reachable = false;
+	      cgraph_finalize_function (node->decl, true);
+	    }
+	}
+      else
+	gcc_unreachable ();
+    }
 }
 
 
-/* Load an AST in an ENCLOSING_NAMESPACE from STREAM.
-   Return the corresponding tree.  */
+/* Wrap a macro DEFINITION for printing in an error.  */
+
+static char *
+wrap_macro_def (const char *definition)
+{
+  char *string;
+  if (definition)
+    {
+      size_t length;
+      length = strlen (definition);
+      string = (char *) xmalloc (length+3);
+      string[0] = '"';
+      strcpy (string + 1, definition);
+      string[length + 1] = '"';
+      string[length + 2] = '\0';
+    }
+  else
+    string = xstrdup ("undefined");
+  return string;
+}
+
+
+/* Report a macro validation error in FILENAME for macro IDENT,
+   which should have the value EXPECTED but actually had the value FOUND. */
+
 static void
-pph_in_mergeable_tree (pph_stream *stream, tree *chain)
+report_validation_error (const char *filename,
+			 const char *ident, const char *found,
+			 const char *before, const char *after)
 {
-  tree t = pph_read_mergeable_tree (stream, chain);
-  if (flag_pph_tracer >= 3)
-    pph_trace_tree (stream, t);
+  char* quote_found = wrap_macro_def (found);
+  char* quote_before = wrap_macro_def (before);
+  char* quote_after = wrap_macro_def (after);
+  error ("PPH file %s fails macro validation, "
+         "%s is %s and should be %s or %s\n",
+         filename, ident, quote_found, quote_before, quote_after);
+  free (quote_found);
+  free (quote_before);
+  free (quote_after);
 }
 
 
-/* Read a chain of tree nodes from STREAM.  */
+/* Load the IDENTIFERS for a hunk from a STREAM.  */
+
+static void
+pph_in_identifiers (pph_stream *stream, cpp_idents_used *identifiers)
+{
+  unsigned int j;
+  unsigned int max_ident_len, max_value_len, num_entries;
+  unsigned int ident_len, before_len, after_len;
+
+  max_ident_len = pph_in_uint (stream);
+  identifiers->max_ident_len = max_ident_len;
+  max_value_len = pph_in_uint (stream);
+  identifiers->max_value_len = max_value_len;
+  num_entries = pph_in_uint (stream);
+  identifiers->num_entries = num_entries;
+  identifiers->entries = XCNEWVEC (cpp_ident_use, num_entries);
+  identifiers->strings = XCNEW (struct obstack);
+
+  /* Strings need no alignment.  */
+  _obstack_begin (identifiers->strings, 0, 0,
+                  (void *(*) (long)) xmalloc,
+                  (void (*) (void *)) free);
+  obstack_alignment_mask (identifiers->strings) = 0;
+  /* FIXME pph: We probably need to free all these things somewhere.  */
+
+  /* Read the identifiers in HUNK. */
+  for (j = 0; j < num_entries; ++j)
+    {
+      const char *s;
+      identifiers->entries[j].used_by_directive = pph_in_uint (stream);
+      identifiers->entries[j].expanded_to_text = pph_in_uint (stream);
+      s = pph_in_string (stream);
+      gcc_assert (s);
+      ident_len = strlen (s);
+      identifiers->entries[j].ident_len = ident_len;
+      identifiers->entries[j].ident_str =
+        (const char *) obstack_copy0 (identifiers->strings, s, ident_len);
+
+      s = pph_in_string (stream);
+      if (s)
+	{
+	  before_len = strlen (s);
+	  identifiers->entries[j].before_len = before_len;
+	  identifiers->entries[j].before_str = (const char *)
+	      obstack_copy0 (identifiers->strings, s, before_len);
+	}
+      else
+	{
+	  /* The identifier table expects NULL entries to have
+	     a length of -1U.  */
+	  identifiers->entries[j].before_len = -1U;
+	  identifiers->entries[j].before_str = NULL;
+	}
+
+      s = pph_in_string (stream);
+      if (s)
+	{
+	  after_len = strlen (s);
+	  identifiers->entries[j].after_len = after_len;
+	  identifiers->entries[j].after_str = (const char *)
+	      obstack_copy0 (identifiers->strings, s, after_len);
+	}
+      else
+	{
+	  /* The identifier table expects NULL entries to have
+	     a length of -1U.  */
+	  identifiers->entries[j].after_len = -1U;
+	  identifiers->entries[j].after_str = NULL;
+	}
+    }
+}
+
+
+/* Merge scope_chain bindings from STREAM into the scope_chain
+   bindings of the current translation unit.  This incorporates all
+   the symbols and types from the PPH image into the current TU so
+   name lookup can find identifiers brought from the image.  */
+
+static void
+pph_in_scope_chain (pph_stream *stream)
+{
+  unsigned i;
+  tree decl;
+  cp_class_binding *cb;
+  cp_label_binding *lb;
+  cp_binding_level *cur_bindings, *new_bindings;
+
+  /* When reading the symbols in STREAM's global binding level, make
+     sure that references to the global binding level point to
+     scope_chain->bindings.  Otherwise, identifiers read from STREAM
+     will have the wrong bindings and will fail name lookups.  */
+  cur_bindings = scope_chain->bindings;
+  pph_in_binding_level (&new_bindings, stream, scope_chain->bindings);
+
+  /* Merge the bindings from STREAM into saved_scope->bindings.  */
+  /* FMIXME crowl: The following should already have been done.
+  chainon (cur_bindings->names, new_bindings->names);
+  chainon (cur_bindings->namespaces, new_bindings->namespaces);
+  */
+
+  FOR_EACH_VEC_ELT (tree, new_bindings->static_decls, i, decl)
+    VEC_safe_push (tree, gc, cur_bindings->static_decls, decl);
+
+  chainon (cur_bindings->usings, new_bindings->usings);
+  chainon (cur_bindings->using_directives, new_bindings->using_directives);
+
+  FOR_EACH_VEC_ELT (cp_class_binding, new_bindings->class_shadowed, i, cb)
+    VEC_safe_push (cp_class_binding, gc, cur_bindings->class_shadowed, cb);
+
+  chainon (cur_bindings->type_shadowed, new_bindings->type_shadowed);
+
+  FOR_EACH_VEC_ELT (cp_label_binding, new_bindings->shadowed_labels, i, lb)
+    VEC_safe_push (cp_label_binding, gc, cur_bindings->shadowed_labels, lb);
+
+  chainon (cur_bindings->blocks, new_bindings->blocks);
+
+  gcc_assert (cur_bindings->this_entity == new_bindings->this_entity);
+  gcc_assert (cur_bindings->level_chain == new_bindings->level_chain);
+  gcc_assert (cur_bindings->dead_vars_from_for
+	      == new_bindings->dead_vars_from_for);
+
+  chainon (cur_bindings->statement_list, new_bindings->statement_list);
+
+  gcc_assert (cur_bindings->binding_depth == new_bindings->binding_depth);
+  gcc_assert (cur_bindings->kind == new_bindings->kind);
+  gcc_assert (cur_bindings->explicit_spec_p == new_bindings->explicit_spec_p);
+  gcc_assert (cur_bindings->keep == new_bindings->keep);
+  gcc_assert (cur_bindings->more_cleanups_ok == new_bindings->more_cleanups_ok);
+  gcc_assert (cur_bindings->have_cleanups == new_bindings->have_cleanups);
+}
+
+
+/* If FILENAME has already been read, return the stream associated with it.  */
+
+static pph_stream *
+pph_image_already_read (const char *filename)
+{
+  pph_stream *include;
+  unsigned i;
+
+  /* FIXME pph, implement a hash map to avoid this linear search.  */
+  FOR_EACH_VEC_ELT (pph_stream_ptr, pph_read_images, i, include)
+    if (strcmp (include->name, filename) == 0)
+      return include;
+
+  return NULL;
+}
+
+
+/* Helper for pph_read_file.  Read contents of PPH file in STREAM.  */
+
+static void
+pph_read_file_1 (pph_stream *stream)
+{
+  bool verified;
+  cpp_ident_use *bad_use;
+  const char *cur_def;
+  cpp_idents_used idents_used;
+  tree t, file_keyed_classes, file_static_aggregates;
+  unsigned i;
+  VEC(tree,gc) *file_unemitted_tinfo_decls;
+  source_location cpp_token_replay_loc;
+
+  if (flag_pph_tracer >= 1)
+    fprintf (pph_logfile, "PPH: Reading %s\n", stream->name);
+
+  /* Read in STREAM's line table and merge it in the current line table.
+     At the same time, read in includes in the order they were originally
+     read.  */
+  cpp_token_replay_loc = pph_in_line_table_and_includes (stream);
+
+  /* If we have read STREAM before, we do not need to re-read the rest
+     of its body.  We only needed to read its line table.  */
+  if (pph_image_already_read (stream->name))
+    return;
+
+  /* Read all the identifiers and pre-processor symbols in the global
+     namespace.  */
+  pph_in_identifiers (stream, &idents_used);
+
+  /* FIXME pph: This validation is weak.  */
+  verified = cpp_lt_verify_1 (parse_in, &idents_used, &bad_use, &cur_def, true);
+  if (!verified)
+    report_validation_error (stream->name, bad_use->ident_str, cur_def,
+                             bad_use->before_str, bad_use->after_str);
+
+  /* Re-instantiate all the pre-processor symbols defined by STREAM.  Force
+     their source_location to line 1 / column 0 of the file they were included
+     in.  This avoids shifting all of the line_table's locations as we would by
+     adding locations which wouldn't be there in the non-pph compile; thus
+     working towards an identical line_table in pph and non-pph.  */
+  cpp_lt_replay (parse_in, &idents_used, &cpp_token_replay_loc);
+
+  /* Read the bindings from STREAM and merge them with the current bindings.  */
+  pph_in_scope_chain (stream);
+
+  if (flag_pph_dump_tree)
+    pph_dump_namespace (pph_logfile, global_namespace);
+
+  /* Read and merge the other global state collected during parsing of
+     the original header.  */
+  file_keyed_classes = pph_in_tree (stream);
+  keyed_classes = chainon (file_keyed_classes, keyed_classes);
+
+  file_unemitted_tinfo_decls = pph_in_tree_vec (stream);
+  FOR_EACH_VEC_ELT (tree, file_unemitted_tinfo_decls, i, t)
+    VEC_safe_push (tree, gc, unemitted_tinfo_decls, t);
+
+  pph_in_pending_templates_list (stream);
+  pph_in_spec_entry_tables (stream);
+
+  file_static_aggregates = pph_in_tree (stream);
+  static_aggregates = chainon (file_static_aggregates, static_aggregates);
+
+  /* Read and process the symbol table.  */
+  pph_in_symtab (stream);
+
+  /* If we are generating an image, the PPH contents we just read from
+     STREAM will need to be read again the next time we want to read
+     the image we are now generating.  */
+  if (pph_writer_enabled_p () && !pph_reading_includes)
+    pph_add_include (stream);
+}
+
+
+/* Add STREAM to the list of read images.  */
+
+static void
+pph_add_read_image (pph_stream *stream)
+{
+  VEC_safe_push (pph_stream_ptr, heap, pph_read_images, stream);
+}
+
+
+/* Read PPH file FILENAME.  Return the in-memory pph_stream instance.  */
 
 void
-pph_read_mergeable_chain (pph_stream *stream, tree *chain)
+pph_read_file (const char *filename)
 {
-  int i, count;
+  pph_stream *stream;
 
-  count = streamer_read_hwi (stream->encoder.r.ib);
-  for (i = 0; i < count; i++)
-    pph_in_mergeable_tree (stream, chain);
+  stream = pph_stream_open (filename, "rb");
+  if (stream)
+    pph_read_file_1 (stream);
+  else
+    error ("Cannot open PPH file for reading: %s: %m", filename);
+
+  pph_add_read_image (stream);
 }
 
 
+/******************************************************* stream finalization */
+
+
 /* Finalize the PPH reader.  */
 
 void
Index: gcc/cp/pt.c
===================================================================
--- gcc/cp/pt.c	(revision 179636)
+++ gcc/cp/pt.c	(working copy)
@@ -20154,17 +20154,18 @@ pph_dump_tinst_level (FILE *stream, stru
   struct tinst_level *cur;
 
   /* Count the number of items.  */
-  for (cur = tinst; cur != NULL;  cur = cur->next )
+  for (cur = tinst; cur != NULL;  cur = cur->next)
     ++count;
 
   /* Now dump them.  */
-  fprintf (stream, "%d tinst_levels\n", count );
-  for (cur = tinst; cur != NULL;  cur = cur->next )
+  fprintf (stream, "%d tinst_levels\n", count);
+  for (cur = tinst; cur != NULL;  cur = cur->next)
     {
+      pph_dump_location (stream, cur->locus);
+      fprintf (stream, "\n");
       pph_dump_tree_name (stream, cur->decl, 0);
-      /* pph_dump_location (stream, cur->locus); */
-      fprintf (stream, "%d errors, ", cur->errors );
-      fprintf (stream, "%d in system header\n", cur->in_system_header_p );
+      fprintf (stream, "%d errors, ", cur->errors);
+      fprintf (stream, "%d in system header\n", cur->in_system_header_p);
     }
 }
 
@@ -20298,9 +20299,11 @@ pph_dump_spec_entry_slot (void **slot, v
 {
   FILE *stream = (FILE *)aux;
   struct spec_entry *entry = (struct spec_entry *) *slot;
-  fprintf (stream, "dumping a spec_entry\n" );
+  fprintf (stream, "spec_entry.tmpl: " );
   pph_dump_tree_name (stream, entry->tmpl, 0);
+  fprintf (stream, "spec_entry.args: " );
   pph_dump_tree_name (stream, entry->args, 0);
+  fprintf (stream, "\nspec_entry.spec: " );
   pph_dump_tree_name (stream, entry->spec, 0);
   return 1;
 }
Index: gcc/cp/pph-streamer.c
===================================================================
--- gcc/cp/pph-streamer.c	(revision 179580)
+++ gcc/cp/pph-streamer.c	(working copy)
@@ -168,7 +168,7 @@ pph_stream_close (pph_stream *stream)
   if (stream == NULL)
     return;
 
-  if (flag_pph_debug >= 1)
+  if (flag_pph_tracer >= 1)
     fprintf (pph_logfile, "PPH: Closing %s\n", stream->name);
 
   /* If we were writing to STREAM, flush all the memory buffers.  This
@@ -252,8 +252,12 @@ pph_trace (pph_stream *stream, const voi
             if (DECL_P (t))
               {
                 fprintf (pph_logfile, ", value=");
+#if 1
                 print_generic_decl (pph_logfile,
                                     CONST_CAST (union tree_node *, t), 0);
+#else
+                pph_dump_tree_name (pph_logfile, t, 0);
+#endif
               }
           }
 	else
@@ -332,6 +336,28 @@ pph_trace_tree (pph_stream *stream, tree
   pph_trace (stream, t, t ? tree_code_size (TREE_CODE (t)) : 0, PPH_TRACE_TREE);
 }
 
+void
+pph_new_trace_tree (pph_stream *stream ATTRIBUTE_UNUSED, tree t, bool mergeable)
+{
+  bool emit = false;
+  char merging = mergeable ? '*' : '.';
+  bool is_decl = DECL_P (t);
+  char userdef =  is_decl ? '*' : '.';
+
+  if (mergeable && is_decl && flag_pph_tracer >= 2)
+    emit = true;
+  else if ((mergeable || is_decl) && flag_pph_tracer >= 3)
+    emit = true;
+  else if (flag_pph_tracer >= 4)
+    emit = true;
+
+  if (emit)
+    {
+      fprintf (pph_logfile, "PPH: %c%c ", merging, userdef);
+      pph_dump_tree_name (pph_logfile, t, 0);
+    }
+}
+
 
 /* Show tracing information for VAL on STREAM.  */
 
Index: gcc/cp/pph-streamer.h
===================================================================
--- gcc/cp/pph-streamer.h	(revision 179709)
+++ gcc/cp/pph-streamer.h	(working copy)
@@ -229,6 +229,7 @@ void pph_init_preloaded_cache (void);
 pph_stream *pph_stream_open (const char *, const char *);
 void pph_stream_close (pph_stream *);
 void pph_trace_tree (pph_stream *, tree);
+void pph_new_trace_tree (pph_stream *, tree, bool);
 void pph_trace_uint (pph_stream *, unsigned int);
 void pph_trace_bytes (pph_stream *, const void *, size_t);
 void pph_trace_string (pph_stream *, const char *);
Index: gcc/cp/pph-streamer-out.c
===================================================================
--- gcc/cp/pph-streamer-out.c	(revision 179709)
+++ gcc/cp/pph-streamer-out.c	(working copy)
@@ -41,6 +41,9 @@ along with GCC; see the file COPYING3.  
 static pph_stream *pph_out_stream = NULL;
 
 
+/***************************************************** stream initialization */
+
+
 /* Initialize buffers and tables in STREAM for writing.  */
 
 void
@@ -57,47 +60,33 @@ pph_init_write (pph_stream *stream)
 }
 
 
-/* Callback for lang_hooks.lto.begin_section.  Open file NAME.  */
+/* Initialize the PPH writer.  */
 
-static void
-pph_begin_section (const char *name ATTRIBUTE_UNUSED)
+void
+pph_writer_init (void)
 {
-}
-
-
-/* Callback for lang_hooks.lto.append_data.  Write LEN bytes from DATA
-   into pph_out_stream.  BLOCK is currently unused.  */
+  gcc_assert (pph_out_stream == NULL);
 
-static void
-pph_out (const void *data, size_t len, void *block ATTRIBUTE_UNUSED)
-{
-  if (data)
-    {
-      size_t written = fwrite (data, 1, len, pph_out_stream->file);
-      gcc_assert (written == len);
-    }
+  pph_out_stream = pph_stream_open (pph_out_file, "wb");
+  if (pph_out_stream == NULL)
+    fatal_error ("Cannot open PPH file for writing: %s: %m", pph_out_file);
 }
 
 
-/* Callback for lang_hooks.lto.end_section.  */
-
-static void
-pph_end_section (void)
-{
-}
+/********************************************************** primitive values */
 
 
 /* Write an unsigned char VALUE to STREAM.  */
+
 static void
 pph_out_uchar (pph_stream *stream, unsigned char value)
 {
-  if (flag_pph_tracer >= 4)
-    pph_trace_uint (stream, value);
   streamer_write_char_stream (stream->encoder.w.ob->main_stream, value);
 }
 
 
 /* Write a HOST_WIDE_INT VALUE to stream.  */
+
 static inline void
 pph_out_hwi (pph_stream *stream, HOST_WIDE_INT value)
 {
@@ -106,6 +95,7 @@ pph_out_hwi (pph_stream *stream, HOST_WI
 
 
 /* Write an unsigned HOST_WIDE_INT VALUE to STREAM.  */
+
 static inline void
 pph_out_uhwi (pph_stream *stream, unsigned HOST_WIDE_INT value)
 {
@@ -114,31 +104,28 @@ pph_out_uhwi (pph_stream *stream, unsign
 
 
 /* Write an unsigned int VALUE to STREAM.  */
+
 void
 pph_out_uint (pph_stream *stream, unsigned int value)
 {
-  if (flag_pph_tracer >= 4)
-    pph_trace_uint (stream, value);
   streamer_write_uhwi (stream->encoder.w.ob, value);
 }
 
 
 /* Write N bytes from P to STREAM.  */
+
 static void
 pph_out_bytes (pph_stream *stream, const void *p, size_t n)
 {
-  if (flag_pph_tracer >= 4)
-    pph_trace_bytes (stream, p, n);
   lto_output_data_stream (stream->encoder.w.ob->main_stream, p, n);
 }
 
 
 /* Write string STR to STREAM.  */
+
 static inline void
 pph_out_string (pph_stream *stream, const char *str)
 {
-  if (flag_pph_tracer >= 4)
-    pph_trace_string (stream, str);
   streamer_write_string (stream->encoder.w.ob,
                          stream->encoder.w.ob->main_stream, str, false);
 }
@@ -149,8 +136,6 @@ static inline void
 pph_out_string_with_length (pph_stream *stream, const char *str,
                             unsigned int len)
 {
-  if (flag_pph_tracer >= 4)
-    pph_trace_string_with_length (stream, str, len);
   streamer_write_string_with_length (stream->encoder.w.ob,
                                      stream->encoder.w.ob->main_stream,
                                      str, len + 1, false);
@@ -158,178 +143,234 @@ pph_out_string_with_length (pph_stream *
 
 
 /* Write a bitpack BP to STREAM.  */
+
 static inline void
 pph_out_bitpack (pph_stream *stream, struct bitpack_d *bp)
 {
   gcc_assert (stream->encoder.w.ob->main_stream == bp->stream);
-  if (flag_pph_tracer >= 4)
-    pph_trace_bitpack (stream, bp);
   streamer_write_bitpack (bp);
 }
 
 
-/* Callback for streamer_hooks.output_location.  Output the LOC directly,
-   an offset will be applied on input after rebuilding the line_table.
-   OB and LOC are as in lto_output_location.  */
+/******************************************************** source information */
 
-void
-pph_write_location (struct output_block *ob, location_t loc)
-{
-  /* FIXME pph: we are streaming builtin locations, which implies that we are
-     streaming some builtins, we probably want to figure out what those are and
-     simply add them to the cache in the preload.  */
-  struct bitpack_d bp;
 
-  location_t first_non_builtin_loc =
-    line_table->maps[PPH_NUM_IGNORED_LINE_TABLE_ENTRIES].start_location;
+/* Emit linenum_type LN to STREAM.  */
 
-  bp = bitpack_create (ob->main_stream);
-  if (loc < first_non_builtin_loc)
-    {
-      /* We should never stream out trees with locations between builtins
-	 and user locations (e.g. <command-line>).  */
-      if (loc > BUILTINS_LOCATION)
-        gcc_unreachable ();
+static inline void
+pph_out_linenum_type (pph_stream *stream, linenum_type ln)
+{
+  pph_out_uint (stream, ln);
+}
 
-      bp_pack_value (&bp, true, 1);
-    }
-  else
-    {
-      gcc_assert (loc >=
-        line_table->maps[PPH_NUM_IGNORED_LINE_TABLE_ENTRIES].start_location);
-      bp_pack_value (&bp, false, 1);
-    }
 
-  streamer_write_bitpack (&bp);
-  streamer_write_hwi (ob, loc);
+/* Emit source_location SL to STREAM.  */
+
+static inline void
+pph_out_source_location (pph_stream *stream, source_location sl)
+{
+  pph_out_uint (stream, sl);
 }
 
 
-/* Write location LOC of length to STREAM.  */
+/* Emit line table MARKER to STREAM.  */
 
-void
-pph_out_location (pph_stream *stream, location_t loc)
+static inline void
+pph_out_linetable_marker (pph_stream *stream, enum pph_linetable_marker marker)
 {
-  if (flag_pph_tracer >= 4)
-    pph_trace_location (stream, loc);
-  pph_write_location (stream->encoder.w.ob, loc);
+  gcc_assert (marker == (enum pph_linetable_marker)(unsigned char) marker);
+  pph_out_uchar (stream, marker);
 }
 
 
-/* Write a chain of ASTs to STREAM starting with FIRST.  */
+/* Emit all information contained in LM to STREAM.  */
+
 static void
-pph_out_chain (pph_stream *stream, tree first)
+pph_out_line_map (pph_stream *stream, struct line_map *lm)
 {
-  if (flag_pph_tracer >= 2)
-    pph_trace_chain (stream, first);
-  streamer_write_chain (stream->encoder.w.ob, first, false);
+  struct bitpack_d bp;
+
+  pph_out_string (stream, lm->to_file);
+  pph_out_linenum_type (stream, lm->to_line);
+  pph_out_source_location (stream, lm->start_location);
+  pph_out_uint (stream, (unsigned int) lm->included_from);
+
+  bp = bitpack_create (stream->encoder.w.ob->main_stream);
+  bp_pack_value (&bp, lm->reason, CHAR_BIT);
+  bp_pack_value (&bp, lm->sysp, CHAR_BIT);
+  bp_pack_value (&bp, lm->column_bits, COLUMN_BITS_BIT);
+  pph_out_bitpack (stream, &bp);
 }
 
 
-/* Write the header for the PPH file represented by STREAM.  */
+/* Write a reference of INCLUDE to STREAM.  Also write the START_LOCATION of
+   this include in the current line_table.  */
 
 static void
-pph_out_header (pph_stream *stream)
+pph_out_include (pph_stream *stream, pph_stream *include,
+                 source_location start_location)
 {
-  pph_file_header header;
-  struct lto_output_stream header_stream;
-  int major, minor, patchlevel;
+  pph_out_source_location (stream, start_location);
+  pph_out_string (stream, include->name);
+}
 
-  /* Collect version information.  */
-  parse_basever (&major, &minor, &patchlevel);
-  gcc_assert (major == (char) major);
-  gcc_assert (minor == (char) minor);
-  gcc_assert (patchlevel == (char) patchlevel);
 
-  /* Write the header for the PPH file.  */
-  memset (&header, 0, sizeof (header));
-  strcpy (header.id_str, pph_id_str);
-  header.major_version = (char) major;
-  header.minor_version = (char) minor;
-  header.patchlevel = (char) patchlevel;
-  header.strtab_size = stream->encoder.w.ob->string_stream->total_size;
+/* Compare filenames of a header and it's potentially corresponding pph file,
+   stripping the path passed in and the extension. Returns true if HEADER_PATH
+   and PPH_PATH end with the same filename. We expect HEADER_PATH to end in .h
+   and PPH_PATH to end in .pph.
 
-  memset (&header_stream, 0, sizeof (header_stream));
-  lto_output_data_stream (&header_stream, &header, sizeof (header));
-  lto_write_stream (&header_stream);
-}
+   FIXME pph: We should not need to do this if we handled include paths
+   correctly, but for now the linemap holds full paths and the stream's includes
+   list only holds the include name.  Also, the stream's includes hold pph
+   filenames where as the line_table as header filenames.  */
 
+static bool
+pph_filename_eq_ignoring_path (const char *header_path, const char *pph_path)
+{
+  const char *header_name = lbasename (header_path);
+  const char *pph_name = lbasename (pph_path);
 
-/* Write the body of the PPH file represented by STREAM.  */
+  const char *header_ext = strchr (header_name, '.');
+  const char *pph_ext = strchr (pph_name, '.');
 
-static void
-pph_out_body (pph_stream *stream)
-{
-  /* Write the string table.  */
-  lto_write_stream (stream->encoder.w.ob->string_stream);
+  unsigned int name_length;
 
-  /* Write the main stream where we have been pickling the parse trees.  */
-  lto_write_stream (stream->encoder.w.ob->main_stream);
+  if (header_ext != NULL)
+    {
+      name_length = header_ext - header_name;
+      gcc_assert (strcmp (header_ext, ".h") == 0);
+    }
+  else
+    /* Some headers do not have a .h suffix, but will still
+       have a .pph suffix after being pph'ed.  */
+    name_length = strlen (header_name);
+
+  gcc_assert (strcmp (pph_ext, ".pph") == 0);
+
+  /* Compare the filenames without their extension.  */
+  return pph_ext - pph_name == name_length
+         && strncmp (header_name, pph_name, name_length) == 0;
 }
 
 
-/* Flush all the in-memory buffers for STREAM to disk.  */
+/* Add INCLUDE to the list of files included by pph_out_stream.  */
 
 void
-pph_flush_buffers (pph_stream *stream)
+pph_add_include (pph_stream *include)
 {
-  /* Redirect the LTO basic I/O langhooks.  */
-  lang_hooks.lto.begin_section = pph_begin_section;
-  lang_hooks.lto.append_data = pph_out;
-  lang_hooks.lto.end_section = pph_end_section;
+  VEC_safe_push (pph_stream_ptr, heap, pph_out_stream->encoder.w.includes,
+		 include);
+}
 
-  /* Write the state buffers built by pph_out_*() calls.  */
-  lto_begin_section (stream->name, false);
-  pph_out_header (stream);
-  pph_out_body (stream);
-  lto_end_section ();
+
+/* Return the *NEXT_INCLUDE_IX'th pph_stream in STREAM's list of includes.
+   Returns NULL if we have read all includes.  Increments *NEXT_INCLUDE_IX
+   when sucessful.  */
+
+static inline pph_stream *
+pph_get_next_include (pph_stream *stream, unsigned int *next_incl_ix)
+{
+  if (*next_incl_ix < VEC_length (pph_stream_ptr, stream->encoder.w.includes))
+    return VEC_index (pph_stream_ptr, stream->encoder.w.includes,
+	              (*next_incl_ix)++);
+  else
+    return NULL;
 }
 
 
-/* Return true if tree node T should be added to the pickle cache.  */
+/* Emit the required line_map entry (those directly related to this include)
+   and some properties in the line_table to STREAM, ignoring builtin and
+   command-line entries.  We will write references to our direct includes
+   children and skip their actual line_map entries (unless they are non-pph
+   children in which case we have to write out their line_map entries as well).
+   We assume stream->encoder.w.includes contains the pph headers included in the
+   same order they are seen in the line_table.  */
 
-static inline bool
-pph_cache_should_handle (tree t)
+static void
+pph_out_line_table_and_includes (pph_stream *stream)
 {
-  if (t)
+  unsigned int ix, next_incl_ix = 0;
+  pph_stream *current_include;
+
+  /* Any #include should have been fully parsed and exited at this point.  */
+  gcc_assert (line_table->depth == 0);
+
+  current_include = pph_get_next_include (stream, &next_incl_ix);
+
+  for (ix = PPH_NUM_IGNORED_LINE_TABLE_ENTRIES; ix < line_table->used; ix++)
     {
-      if (TREE_CODE (t) == INTEGER_CST)
+      struct line_map *lm = &line_table->maps[ix];
+
+      if (ix == PPH_NUM_IGNORED_LINE_TABLE_ENTRIES)
         {
-          /* With the exception of some special constants that are
-            pointer-compared everywhere (e.g., integer_zero_node), we
-            do not want constants in the cache.  Their physical
-            representation is smaller than a cache reference record
-            and they can also trick the cache in similar ways to
-            builtins (read below).  */
-          return false;
+          /* The first non-ignored entry should be an LC_RENAME back in the
+            header after inserting the builtin and command-line entries.  When
+            reading the pph we want this to be a simple LC_ENTER as the builtin
+            and command_line entries will already exist and we are now entering
+	    a #include.  */
+          gcc_assert (lm->reason == LC_RENAME);
+          lm->reason = LC_ENTER;
         }
-      else if (streamer_handle_as_builtin_p (t))
-        {
-          /* We do not want builtins in the cache for two reasons:
 
-            - They never need pickling.  Much like pre-loaded tree
-              nodes, builtins are pre-built by the compiler and
-              accessible with their class and code.
+      /* If this is an entry from a pph header, only output reference.  */
+      if (current_include != NULL
+	  && pph_filename_eq_ignoring_path (lm->to_file, current_include->name))
+	{
+	  int includer_level;
 
-            - They can trick the cache.  When possible, user-provided
-              functions are generally replaced by their builtin
-              counterparts (e.g., strcmp, malloc, etc).  When this
-              happens, the writer cache will store in its cache two
-              different expressions, one for the user provided
-              function and another for the builtin itself.  However,
-              when written out to the PPH image, they both get
-              emitted as a reference to the builtin.  Therefore, when
-              the reader tries to instantiate the second copy, it
-              tries to store the same tree in two different cache
-              slots (and proceeds to ICE in pph_cache_insert_at).  */
-          return false;
-        }
+	  gcc_assert (lm->reason == LC_ENTER);
+	  gcc_assert (lm->included_from != -1);
+
+	  pph_out_linetable_marker (stream, PPH_LINETABLE_REFERENCE);
+
+	  pph_out_include (stream, current_include, lm->start_location);
+
+	  /* Potentially lm could be included from a header other then the main
+	      one if a textual include includes a pph header (i.e. we can't
+	      simply rely on going back to included_from == -1).  */
+	  includer_level = INCLUDED_FROM (line_table, lm)->included_from;
+
+	  /* Skip all other linemap entries up to and including the LC_LEAVE
+	      from the referenced header back to the one including it.  */
+	  while (line_table->maps[++ix].included_from != includer_level)
+	    /* We should always leave this loop before the end of the
+		current line_table entries.  */
+	    gcc_assert (ix < line_table->used);
+
+	  current_include = pph_get_next_include (stream, &next_incl_ix);
+	}
+      else
+	{
+	  pph_out_linetable_marker (stream, PPH_LINETABLE_ENTRY);
+	  pph_out_line_map (stream, lm);
+	}
+
+      /* Restore changes made to first entry above if needed.  */
+      if (ix == PPH_NUM_IGNORED_LINE_TABLE_ENTRIES)
+	lm->reason = LC_RENAME;
     }
 
-  return true;
+  pph_out_linetable_marker (stream, PPH_LINETABLE_END);
+
+  /* Output the number of entries written to validate on input.  */
+  pph_out_uint (stream, line_table->used - PPH_NUM_IGNORED_LINE_TABLE_ENTRIES);
+
+  /* Every pph header included should have been seen and skipped in the
+     line_table streaming above.  */
+  gcc_assert (next_incl_ix == VEC_length (pph_stream_ptr,
+					  stream->encoder.w.includes));
+
+  pph_out_source_location (stream, line_table->highest_location);
+  pph_out_source_location (stream, line_table->highest_line);
+
+  pph_out_uint (stream, line_table->max_column_hint);
 }
 
 
+/*********************************************************** record handling */
+
+
 /* Return a PPH record marker according to whether DATA is NULL or
    it can be found in one of the caches associated with STREAM.  TAG
    is the data type for DATA.
@@ -453,6 +494,50 @@ pph_out_start_record (pph_stream *stream
 }
 
 
+/* Return true if tree node T should be added to the pickle cache.  */
+
+static inline bool
+pph_cache_should_handle (tree t)
+{
+  if (t)
+    {
+      if (TREE_CODE (t) == INTEGER_CST)
+        {
+          /* With the exception of some special constants that are
+            pointer-compared everywhere (e.g., integer_zero_node), we
+            do not want constants in the cache.  Their physical
+            representation is smaller than a cache reference record
+            and they can also trick the cache in similar ways to
+            builtins (read below).  */
+          return false;
+        }
+      else if (streamer_handle_as_builtin_p (t))
+        {
+          /* We do not want builtins in the cache for two reasons:
+
+            - They never need pickling.  Much like pre-loaded tree
+              nodes, builtins are pre-built by the compiler and
+              accessible with their class and code.
+
+            - They can trick the cache.  When possible, user-provided
+              functions are generally replaced by their builtin
+              counterparts (e.g., strcmp, malloc, etc).  When this
+              happens, the writer cache will store in its cache two
+              different expressions, one for the user provided
+              function and another for the builtin itself.  However,
+              when written out to the PPH image, they both get
+              emitted as a reference to the builtin.  Therefore, when
+              the reader tries to instantiate the second copy, it
+              tries to store the same tree in two different cache
+              slots (and proceeds to ICE in pph_cache_insert_at).  */
+          return false;
+        }
+    }
+
+  return true;
+}
+
+
 /* Start a record for tree node T in STREAM.  This is like
    pph_out_start_record, but it filters certain special trees that
    should never be added to the cache.  Additionally, instead of
@@ -540,6 +625,92 @@ pph_out_start_tree_record (pph_stream *s
 }
 
 
+/*************************************************************** tree shells */
+
+
+/* The core tree writer is defined much later.  */
+
+static void pph_write_any_tree (pph_stream *stream, tree t, bool mergeable);
+
+
+/* Output non-mergeable AST T to STREAM  */
+
+void
+pph_out_tree (pph_stream *stream, tree t)
+{
+  pph_write_any_tree (stream, t, false);
+}
+
+
+/* Output mergable AST T to STREAM.  */
+
+static void
+pph_out_mergeable_tree (pph_stream *stream, tree t)
+{
+  pph_write_any_tree (stream, t, true);
+}
+
+
+/* Callback for writing ASTs to a stream.  Write EXPR to the PPH stream
+   in OB.  */
+
+void
+pph_write_tree (struct output_block *ob, tree expr, bool ref_p ATTRIBUTE_UNUSED)
+{
+  pph_stream *stream = (pph_stream *) ob->sdata;
+  pph_write_any_tree (stream, expr, false);
+}
+
+
+/********************************************************** lexical elements */
+
+
+/* Callback for streamer_hooks.output_location.  Output the LOC directly,
+   an offset will be applied on input after rebuilding the line_table.
+   OB and LOC are as in lto_output_location.  */
+
+void
+pph_write_location (struct output_block *ob, location_t loc)
+{
+  /* FIXME pph: we are streaming builtin locations, which implies that we are
+     streaming some builtins, we probably want to figure out what those are and
+     simply add them to the cache in the preload.  */
+  struct bitpack_d bp;
+
+  location_t first_non_builtin_loc =
+    line_table->maps[PPH_NUM_IGNORED_LINE_TABLE_ENTRIES].start_location;
+
+  bp = bitpack_create (ob->main_stream);
+  if (loc < first_non_builtin_loc)
+    {
+      /* We should never stream out trees with locations between builtins
+	 and user locations (e.g. <command-line>).  */
+      if (loc > BUILTINS_LOCATION)
+        gcc_unreachable ();
+
+      bp_pack_value (&bp, true, 1);
+    }
+  else
+    {
+      gcc_assert (loc >=
+        line_table->maps[PPH_NUM_IGNORED_LINE_TABLE_ENTRIES].start_location);
+      bp_pack_value (&bp, false, 1);
+    }
+
+  streamer_write_bitpack (&bp);
+  streamer_write_hwi (ob, loc);
+}
+
+
+/* Write location LOC of length to STREAM.  */
+
+void
+pph_out_location (pph_stream *stream, location_t loc)
+{
+  pph_write_location (stream->encoder.w.ob, loc);
+}
+
+
 /* Save the tree associated with TOKEN to STREAM.  */
 
 static void
@@ -622,95 +793,7 @@ pph_out_token_cache (pph_stream *f, cp_t
 }
 
 
-/* Output AST T to STREAM.  If -fpph-tracer is set to TLEVEL or
-   higher, T is sent to pph_trace_tree.  */
-static void
-pph_out_tree_1 (pph_stream *stream, tree t, int tlevel)
-{
-  if (flag_pph_tracer >= tlevel)
-    pph_trace_tree (stream, t);
-  pph_write_tree (stream->encoder.w.ob, t, false);
-}
-
-/* Output AST T to STREAM.  Trigger tracing at -fpph-tracer=2.  */
-void
-pph_out_tree (pph_stream *stream, tree t)
-{
-  pph_out_tree_1 (stream, t, 2);
-}
-
-static void pph_write_mergeable_tree (pph_stream *stream, tree expr);
-
-/* Output AST T from ENCLOSING_NAMESPACE to STREAM.
-   Trigger tracing at -fpph-tracer=2.  */
-static void
-pph_out_mergeable_tree (pph_stream *stream, tree t)
-{
-  if (flag_pph_tracer >= 2)
-    pph_trace_tree (stream, t);
-  pph_write_mergeable_tree (stream, t);
-}
-
-
-/* Write all the fields in lang_decl_base instance LDB to OB.  */
-
-static void
-pph_out_ld_base (pph_stream *stream, struct lang_decl_base *ldb)
-{
-  struct bitpack_d bp;
-
-  bp = bitpack_create (stream->encoder.w.ob->main_stream);
-  bp_pack_value (&bp, ldb->selector, 16);
-  bp_pack_value (&bp, ldb->language, 4);
-  bp_pack_value (&bp, ldb->use_template, 2);
-  bp_pack_value (&bp, ldb->not_really_extern, 1);
-  bp_pack_value (&bp, ldb->initialized_in_class, 1);
-  bp_pack_value (&bp, ldb->repo_available_p, 1);
-  bp_pack_value (&bp, ldb->threadprivate_or_deleted_p, 1);
-  bp_pack_value (&bp, ldb->anticipated_p, 1);
-  bp_pack_value (&bp, ldb->friend_attr, 1);
-  bp_pack_value (&bp, ldb->template_conv_p, 1);
-  bp_pack_value (&bp, ldb->odr_used, 1);
-  bp_pack_value (&bp, ldb->u2sel, 1);
-  pph_out_bitpack (stream, &bp);
-}
-
-
-/* Write all the fields in lang_decl_min instance LDM to STREAM.  */
-
-static void
-pph_out_ld_min (pph_stream *stream, struct lang_decl_min *ldm)
-{
-  pph_out_tree_1 (stream, ldm->template_info, 1);
-  if (ldm->base.u2sel == 0)
-    pph_out_tree_1 (stream, ldm->u2.access, 1);
-  else if (ldm->base.u2sel == 1)
-    pph_out_uint (stream, ldm->u2.discriminator);
-  else
-    gcc_unreachable ();
-}
-
-
-/* Return true if T matches FILTER for STREAM.  */
-
-static inline bool
-pph_tree_matches (tree t, unsigned filter)
-{
-  if ((filter & PPHF_NO_BUILTINS)
-      && DECL_P (t)
-      && DECL_IS_BUILTIN (t))
-    return false;
-
-  if ((filter & PPHF_NO_PREFS)
-      && pph_cache_lookup (NULL, t, NULL, pph_tree_code_to_tag (t)))
-    return false;
-
-  if ((filter & PPHF_NO_XREFS)
-      && pph_cache_lookup_in_includes (t, NULL, NULL, pph_tree_code_to_tag (t)))
-    return false;
-
-  return true;
-}
+/******************************************************************* vectors */
 
 
 /* Write all the trees in VEC V to STREAM.  */
@@ -751,6 +834,28 @@ pph_out_mergeable_tree_vec (pph_stream *
 }
 
 
+/* Return true if T matches FILTER for STREAM.  */
+
+static inline bool
+pph_tree_matches (tree t, unsigned filter)
+{
+  if ((filter & PPHF_NO_BUILTINS)
+      && DECL_P (t)
+      && DECL_IS_BUILTIN (t))
+    return false;
+
+  if ((filter & PPHF_NO_PREFS)
+      && pph_cache_lookup (NULL, t, NULL, pph_tree_code_to_tag (t)))
+    return false;
+
+  if ((filter & PPHF_NO_XREFS)
+      && pph_cache_lookup_in_includes (t, NULL, NULL, pph_tree_code_to_tag (t)))
+    return false;
+
+  return true;
+}
+
+
 /* Write all the trees matching FILTER in VEC V to STREAM.  */
 
 static void
@@ -797,74 +902,32 @@ pph_out_qual_use_vec (pph_stream *stream
 }
 
 
-/* Forward declaration to break cyclic dependencies.  */
-static void pph_out_binding_level (pph_stream *, cp_binding_level *, unsigned);
-
-
-/* Helper for pph_out_cxx_binding.  STREAM and CB are as in
-   pph_out_cxx_binding.  */
-
-static void
-pph_out_cxx_binding_1 (pph_stream *stream, cxx_binding *cb)
-{
-  struct bitpack_d bp;
-
-  if (pph_out_start_record (stream, cb, PPH_cxx_binding))
-    return;
-
-  pph_out_tree (stream, cb->value);
-  pph_out_tree (stream, cb->type);
-  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);
-  pph_out_bitpack (stream, &bp);
-}
-
-
-/* Write all the fields of cxx_binding instance CB to STREAM.  */
+/* Write the vector V of tree_pair_s instances to STREAM.  */
 
 static void
-pph_out_cxx_binding (pph_stream *stream, cxx_binding *cb)
+pph_out_tree_pair_vec (pph_stream *stream, VEC(tree_pair_s,gc) *v)
 {
-  cxx_binding *prev;
-
-  /* Write the current binding first.  */
-  pph_out_cxx_binding_1 (stream, cb);
-
-  /* Write the list of previous bindings.  */
-  for (prev = cb ? cb->previous : NULL; prev; prev = prev->previous)
-    pph_out_cxx_binding_1 (stream, prev);
+  unsigned i;
+  tree_pair_s *p;
 
-  /* Mark the end of the list (if there was a list).  */
-  if (cb)
-    pph_out_cxx_binding_1 (stream, NULL);
+  pph_out_uint (stream, VEC_length (tree_pair_s, v));
+  FOR_EACH_VEC_ELT (tree_pair_s, v, i, p)
+    {
+      pph_out_tree (stream, p->purpose);
+      pph_out_tree (stream, p->value);
+    }
 }
 
 
-/* Write all the fields of cp_class_binding instance CB to STREAM.  */
-
-static void
-pph_out_class_binding (pph_stream *stream, cp_class_binding *cb)
-{
-  if (pph_out_start_record (stream, cb, PPH_cp_class_binding))
-    return;
-
-  pph_out_cxx_binding (stream, cb->base);
-  pph_out_tree (stream, cb->identifier);
-}
+/******************************************************************** chains */
 
 
-/* Write all the fields of cp_label_binding instance LB to STREAM.  */
+/* Write a chain of ASTs to STREAM starting with FIRST.  */
 
 static void
-pph_out_label_binding (pph_stream *stream, cp_label_binding *lb)
+pph_out_chain (pph_stream *stream, tree first)
 {
-  if (pph_out_start_record (stream, lb, PPH_cp_label_binding))
-    return;
-
-  pph_out_tree (stream, lb->label);
-  pph_out_tree (stream, lb->prev_value);
+  streamer_write_chain (stream->encoder.w.ob, first, false);
 }
 
 
@@ -896,12 +959,7 @@ pph_write_mergeable_links (pph_stream *s
 void
 pph_out_mergeable_chain (pph_stream *stream, tree t)
 {
-  int count;
-
-  if (flag_pph_tracer >= 2)
-    pph_trace_chain (stream, t);
-
-  count = list_length (t);
+  int count = list_length (t);
   streamer_write_hwi (stream->encoder.w.ob, count);
   pph_write_mergeable_links (stream, t);
 }
@@ -964,6 +1022,80 @@ pph_out_mergeable_chain_filtered (pph_st
 }
 
 
+/****************************************************************** bindings */
+
+
+/* Forward declaration to break cyclic dependencies.  */
+static void pph_out_binding_level (pph_stream *, cp_binding_level *, unsigned);
+
+
+/* Helper for pph_out_cxx_binding.  STREAM and CB are as in
+   pph_out_cxx_binding.  */
+
+static void
+pph_out_cxx_binding_1 (pph_stream *stream, cxx_binding *cb)
+{
+  struct bitpack_d bp;
+
+  if (pph_out_start_record (stream, cb, PPH_cxx_binding))
+    return;
+
+  pph_out_tree (stream, cb->value);
+  pph_out_tree (stream, cb->type);
+  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);
+  pph_out_bitpack (stream, &bp);
+}
+
+
+/* Write all the fields of cxx_binding instance CB to STREAM.  */
+
+static void
+pph_out_cxx_binding (pph_stream *stream, cxx_binding *cb)
+{
+  cxx_binding *prev;
+
+  /* Write the current binding first.  */
+  pph_out_cxx_binding_1 (stream, cb);
+
+  /* Write the list of previous bindings.  */
+  for (prev = cb ? cb->previous : NULL; prev; prev = prev->previous)
+    pph_out_cxx_binding_1 (stream, prev);
+
+  /* Mark the end of the list (if there was a list).  */
+  if (cb)
+    pph_out_cxx_binding_1 (stream, NULL);
+}
+
+
+/* Write all the fields of cp_class_binding instance CB to STREAM.  */
+
+static void
+pph_out_class_binding (pph_stream *stream, cp_class_binding *cb)
+{
+  if (pph_out_start_record (stream, cb, PPH_cp_class_binding))
+    return;
+
+  pph_out_cxx_binding (stream, cb->base);
+  pph_out_tree (stream, cb->identifier);
+}
+
+
+/* Write all the fields of cp_label_binding instance LB to STREAM.  */
+
+static void
+pph_out_label_binding (pph_stream *stream, cp_label_binding *lb)
+{
+  if (pph_out_start_record (stream, lb, PPH_cp_label_binding))
+    return;
+
+  pph_out_tree (stream, lb->label);
+  pph_out_tree (stream, lb->prev_value);
+}
+
+
 /* 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.  */
@@ -1035,14 +1167,7 @@ pph_out_binding_level (pph_stream *strea
 }
 
 
-/* Write out the tree_common fields from T to STREAM.  */
-
-static void
-pph_out_tree_common (pph_stream *stream, tree t)
-{
-  /* The 'struct tree_typed typed' base class is handled in LTO.  */
-  pph_out_tree (stream, TREE_CHAIN (t));
-}
+/********************************************************** tree aux classes */
 
 
 /* Write all the fields of language_function instance LF to STREAM.  */
@@ -1082,52 +1207,6 @@ pph_out_language_function (pph_stream *s
 }
 
 
-/* Write all the fields of lang_decl_fn instance LDF to STREAM.  */
-
-static void
-pph_out_ld_fn (pph_stream *stream, struct lang_decl_fn *ldf)
-{
-  struct bitpack_d bp;
-
-  /* Write all the fields in lang_decl_min.  */
-  pph_out_ld_min (stream, &ldf->min);
-
-  bp = bitpack_create (stream->encoder.w.ob->main_stream);
-  bp_pack_value (&bp, ldf->operator_code, 16);
-  bp_pack_value (&bp, ldf->global_ctor_p, 1);
-  bp_pack_value (&bp, ldf->global_dtor_p, 1);
-  bp_pack_value (&bp, ldf->constructor_attr, 1);
-  bp_pack_value (&bp, ldf->destructor_attr, 1);
-  bp_pack_value (&bp, ldf->assignment_operator_p, 1);
-  bp_pack_value (&bp, ldf->static_function, 1);
-  bp_pack_value (&bp, ldf->pure_virtual, 1);
-  bp_pack_value (&bp, ldf->defaulted_p, 1);
-  bp_pack_value (&bp, ldf->has_in_charge_parm_p, 1);
-  bp_pack_value (&bp, ldf->has_vtt_parm_p, 1);
-  bp_pack_value (&bp, ldf->pending_inline_p, 1);
-  bp_pack_value (&bp, ldf->nonconverting, 1);
-  bp_pack_value (&bp, ldf->thunk_p, 1);
-  bp_pack_value (&bp, ldf->this_thunk_p, 1);
-  bp_pack_value (&bp, ldf->hidden_friend_p, 1);
-  pph_out_bitpack (stream, &bp);
-
-  pph_out_tree (stream, ldf->befriending_classes);
-  pph_out_tree (stream, ldf->context);
-
-  if (ldf->thunk_p == 0)
-    pph_out_tree (stream, ldf->u5.cloned_function);
-  else if (ldf->thunk_p == 1)
-    pph_out_uint (stream, ldf->u5.fixed_offset);
-  else
-    gcc_unreachable ();
-
-  if (ldf->pending_inline_p == 1)
-    pph_out_token_cache (stream, ldf->u.pending_inline_info);
-  else if (ldf->pending_inline_p == 0)
-    pph_out_language_function (stream, ldf->u.saved_language_function);
-}
-
-
 /* A callback of htab_traverse. Just extracts a (type) tree from SLOT
    and writes it out for PPH. */
 
@@ -1209,6 +1288,91 @@ pph_out_struct_function (pph_stream *str
 }
 
 
+/* Write all the fields in lang_decl_base instance LDB to OB.  */
+
+static void
+pph_out_ld_base (pph_stream *stream, struct lang_decl_base *ldb)
+{
+  struct bitpack_d bp;
+
+  bp = bitpack_create (stream->encoder.w.ob->main_stream);
+  bp_pack_value (&bp, ldb->selector, 16);
+  bp_pack_value (&bp, ldb->language, 4);
+  bp_pack_value (&bp, ldb->use_template, 2);
+  bp_pack_value (&bp, ldb->not_really_extern, 1);
+  bp_pack_value (&bp, ldb->initialized_in_class, 1);
+  bp_pack_value (&bp, ldb->repo_available_p, 1);
+  bp_pack_value (&bp, ldb->threadprivate_or_deleted_p, 1);
+  bp_pack_value (&bp, ldb->anticipated_p, 1);
+  bp_pack_value (&bp, ldb->friend_attr, 1);
+  bp_pack_value (&bp, ldb->template_conv_p, 1);
+  bp_pack_value (&bp, ldb->odr_used, 1);
+  bp_pack_value (&bp, ldb->u2sel, 1);
+  pph_out_bitpack (stream, &bp);
+}
+
+
+/* Write all the fields in lang_decl_min instance LDM to STREAM.  */
+
+static void
+pph_out_ld_min (pph_stream *stream, struct lang_decl_min *ldm)
+{
+  pph_out_tree (stream, ldm->template_info);
+  if (ldm->base.u2sel == 0)
+    pph_out_tree (stream, ldm->u2.access);
+  else if (ldm->base.u2sel == 1)
+    pph_out_uint (stream, ldm->u2.discriminator);
+  else
+    gcc_unreachable ();
+}
+
+
+/* Write all the fields of lang_decl_fn instance LDF to STREAM.  */
+
+static void
+pph_out_ld_fn (pph_stream *stream, struct lang_decl_fn *ldf)
+{
+  struct bitpack_d bp;
+
+  /* Write all the fields in lang_decl_min.  */
+  pph_out_ld_min (stream, &ldf->min);
+
+  bp = bitpack_create (stream->encoder.w.ob->main_stream);
+  bp_pack_value (&bp, ldf->operator_code, 16);
+  bp_pack_value (&bp, ldf->global_ctor_p, 1);
+  bp_pack_value (&bp, ldf->global_dtor_p, 1);
+  bp_pack_value (&bp, ldf->constructor_attr, 1);
+  bp_pack_value (&bp, ldf->destructor_attr, 1);
+  bp_pack_value (&bp, ldf->assignment_operator_p, 1);
+  bp_pack_value (&bp, ldf->static_function, 1);
+  bp_pack_value (&bp, ldf->pure_virtual, 1);
+  bp_pack_value (&bp, ldf->defaulted_p, 1);
+  bp_pack_value (&bp, ldf->has_in_charge_parm_p, 1);
+  bp_pack_value (&bp, ldf->has_vtt_parm_p, 1);
+  bp_pack_value (&bp, ldf->pending_inline_p, 1);
+  bp_pack_value (&bp, ldf->nonconverting, 1);
+  bp_pack_value (&bp, ldf->thunk_p, 1);
+  bp_pack_value (&bp, ldf->this_thunk_p, 1);
+  bp_pack_value (&bp, ldf->hidden_friend_p, 1);
+  pph_out_bitpack (stream, &bp);
+
+  pph_out_tree (stream, ldf->befriending_classes);
+  pph_out_tree (stream, ldf->context);
+
+  if (ldf->thunk_p == 0)
+    pph_out_tree (stream, ldf->u5.cloned_function);
+  else if (ldf->thunk_p == 1)
+    pph_out_uint (stream, ldf->u5.fixed_offset);
+  else
+    gcc_unreachable ();
+
+  if (ldf->pending_inline_p == 1)
+    pph_out_token_cache (stream, ldf->u.pending_inline_info);
+  else if (ldf->pending_inline_p == 0)
+    pph_out_language_function (stream, ldf->u.saved_language_function);
+}
+
+
 /* Write all the fields of lang_decl_ns instance LDNS to STREAM.  */
 
 static void
@@ -1269,6 +1433,19 @@ pph_out_lang_specific (pph_stream *strea
 }
 
 
+/********************************************************* tree base classes */
+
+
+/* Write out the tree_common fields from T to STREAM.  */
+
+static void
+pph_out_tree_common (pph_stream *stream, tree t)
+{
+  /* The 'struct tree_typed typed' base class is handled in LTO.  */
+  pph_out_tree (stream, TREE_CHAIN (t));
+}
+
+
 /* Write all the fields in lang_type_header instance LTH to STREAM.  */
 
 static void
@@ -1288,23 +1465,6 @@ pph_out_lang_type_header (pph_stream *st
 }
 
 
-/* Write the vector V of tree_pair_s instances to STREAM.  */
-
-static void
-pph_out_tree_pair_vec (pph_stream *stream, VEC(tree_pair_s,gc) *v)
-{
-  unsigned i;
-  tree_pair_s *p;
-
-  pph_out_uint (stream, VEC_length (tree_pair_s, v));
-  FOR_EACH_VEC_ELT (tree_pair_s, v, i, p)
-    {
-      pph_out_tree (stream, p->purpose);
-      pph_out_tree (stream, p->value);
-    }
-}
-
-
 /* Write a struct sorted_fields_type instance SFT to STREAM.  */
 
 static void
@@ -1425,458 +1585,37 @@ pph_out_lang_type (pph_stream *stream, t
 }
 
 
-/* Write the global bindings in scope_chain to STREAM.  */
-
-static void
-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 (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.
-
-     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->cache, scope_chain->bindings, &ix,
-                   PPH_cp_binding_level);
-    pph_out_record_marker (stream, PPH_RECORD_START, PPH_cp_binding_level);
-    pph_out_uint (stream, ix);
-    pph_out_binding_level_1 (stream, scope_chain->bindings,
-			     PPHF_NO_XREFS | PPHF_NO_PREFS);
-  }
-}
-
-
-/* Save the IDENTIFIERS to the STREAM.  */
-
-static void
-pph_out_identifiers (pph_stream *stream, cpp_idents_used *identifiers)
-{
-  unsigned int num_entries, active_entries, id;
-
-  num_entries = identifiers->num_entries;
-  pph_out_uint (stream, identifiers->max_ident_len);
-  pph_out_uint (stream, identifiers->max_value_len);
-
-  active_entries = 0;
-  for ( id = 0; id < num_entries; ++id )
-    {
-      cpp_ident_use *entry = identifiers->entries + id;
-      if (!(entry->used_by_directive || entry->expanded_to_text))
-        continue;
-      ++active_entries;
-    }
-
-  pph_out_uint (stream, active_entries);
-
-  for ( id = 0; id < num_entries; ++id )
-    {
-      cpp_ident_use *entry = identifiers->entries + id;
-
-      if (!(entry->used_by_directive || entry->expanded_to_text))
-        continue;
-
-      /* FIXME pph: We are wasting space; ident_len, used_by_directive
-      and expanded_to_text together could fit into a single uint. */
-
-      pph_out_uint (stream, entry->used_by_directive);
-      pph_out_uint (stream, entry->expanded_to_text);
-
-      gcc_assert (entry->ident_len <= identifiers->max_ident_len);
-      pph_out_string_with_length (stream, entry->ident_str,
-				     entry->ident_len);
-
-      gcc_assert (entry->before_len <= identifiers->max_value_len);
-      pph_out_string_with_length (stream, entry->before_str,
-				     entry->before_len);
-
-      gcc_assert (entry->after_len <= identifiers->max_value_len);
-      pph_out_string_with_length (stream, entry->after_str,
-				     entry->after_len);
-    }
-}
-
-
-/* Emit symbol table ACTION to STREAM.  */
-
-static inline void
-pph_out_symtab_action (pph_stream *stream, enum pph_symtab_action action)
-{
-  gcc_assert (action == (enum pph_symtab_action)(unsigned char) action);
-  pph_out_uchar (stream, action);
-}
-
-/* Emit callgraph NODE to STREAM.  */
-
-static void
-pph_out_cgraph_node (pph_stream *stream, struct cgraph_node *node)
-{
-  struct bitpack_d bp;
-
-  if (pph_out_start_record (stream, node, PPH_cgraph_node))
-    return;
-
-  pph_out_tree (stream, node->decl);
-  pph_out_cgraph_node (stream, node->origin);
-  pph_out_cgraph_node (stream, node->nested);
-  pph_out_cgraph_node (stream, node->next_nested);
-  pph_out_cgraph_node (stream, node->next_needed);
-  pph_out_cgraph_node (stream, node->next_sibling_clone);
-  pph_out_cgraph_node (stream, node->prev_sibling_clone);
-  pph_out_cgraph_node (stream, node->clones);
-  pph_out_cgraph_node (stream, node->clone_of);
-  pph_out_cgraph_node (stream, node->same_comdat_group);
-  gcc_assert (node->call_site_hash == NULL);
-  pph_out_tree (stream, node->former_clone_of);
-  gcc_assert (node->aux == NULL);
-  gcc_assert (VEC_empty (ipa_opt_pass, node->ipa_transforms_to_apply));
-
-  gcc_assert (VEC_empty (ipa_ref_t, node->ref_list.references));
-  gcc_assert (VEC_empty (ipa_ref_ptr, node->ref_list.refering));
-
-  gcc_assert (node->local.lto_file_data == NULL);
-  bp = bitpack_create (stream->encoder.w.ob->main_stream);
-  bp_pack_value (&bp, node->local.local, 1);
-  bp_pack_value (&bp, node->local.externally_visible, 1);
-  bp_pack_value (&bp, node->local.finalized, 1);
-  bp_pack_value (&bp, node->local.can_change_signature, 1);
-  bp_pack_value (&bp, node->local.redefined_extern_inline, 1);
-  pph_out_bitpack (stream, &bp);
-
-  pph_out_cgraph_node (stream, node->global.inlined_to);
-
-  pph_out_uint (stream, node->rtl.preferred_incoming_stack_boundary);
-
-  gcc_assert (VEC_empty (ipa_replace_map_p, node->clone.tree_map));
-  pph_out_uhwi (stream, node->thunk.fixed_offset);
-  pph_out_uhwi (stream, node->thunk.virtual_value);
-  pph_out_tree (stream, node->thunk.alias);
-  bp = bitpack_create (stream->encoder.w.ob->main_stream);
-  bp_pack_value (&bp, node->thunk.this_adjusting, 1);
-  bp_pack_value (&bp, node->thunk.virtual_offset_p, 1);
-  bp_pack_value (&bp, node->thunk.thunk_p, 1);
-  pph_out_bitpack (stream, &bp);
-
-  pph_out_uhwi (stream, node->count);
-  pph_out_uint (stream, node->count_materialization_scale);
-
-  bp = bitpack_create (stream->encoder.w.ob->main_stream);
-  bp_pack_value (&bp, node->needed, 1);
-  bp_pack_value (&bp, node->address_taken, 1);
-  bp_pack_value (&bp, node->abstract_and_needed, 1);
-  bp_pack_value (&bp, node->reachable, 1);
-  bp_pack_value (&bp, node->reachable_from_other_partition, 1);
-  bp_pack_value (&bp, node->lowered, 1);
-  bp_pack_value (&bp, node->analyzed, 1);
-  bp_pack_value (&bp, node->in_other_partition, 1);
-  bp_pack_value (&bp, node->process, 1);
-  bp_pack_value (&bp, node->alias, 1);
-  bp_pack_value (&bp, node->same_body_alias, 1);
-  bp_pack_value (&bp, node->frequency, 2);
-  bp_pack_value (&bp, node->only_called_at_startup, 1);
-  bp_pack_value (&bp, node->only_called_at_exit, 1);
-  pph_out_bitpack (stream, &bp);
-}
-
-
-/* Emit the symbol table for STREAM.  When this image is read into
-   another translation unit, we want to guarantee that the IL
-   instances taken from this image are instantiated in the same order
-   that they were instantiated when we generated this image.
-
-   With this, we can generate code in the same order out of the
-   original header files and out of PPH images.  */
-
-static void
-pph_out_symtab (pph_stream *stream)
-{
-  pph_symtab_entry *entry;
-  unsigned i;
-
-  pph_out_uint (stream, VEC_length (pph_symtab_entry, stream->symtab.v));
-  FOR_EACH_VEC_ELT (pph_symtab_entry, stream->symtab.v, i, entry)
-    {
-      pph_out_symtab_action (stream, entry->action);
-      pph_out_tree (stream, entry->decl);
-      if (entry->action == PPH_SYMTAB_DECLARE)
-	{
-	  struct bitpack_d bp;
-	  bp = bitpack_create (stream->encoder.w.ob->main_stream);
-	  bp_pack_value (&bp, entry->top_level, 1);
-	  bp_pack_value (&bp, entry->at_end, 1);
-	  pph_out_bitpack (stream, &bp);
-	}
-      else if (entry->action == PPH_SYMTAB_EXPAND)
-	{
-	  pph_out_struct_function (stream, DECL_STRUCT_FUNCTION (entry->decl));
-	  pph_out_cgraph_node (stream, cgraph_get_node (entry->decl));
-	}
-      else
-	gcc_unreachable ();
-    }
-}
-
-
-/* Emit linenum_type LN to STREAM.  */
-
-static inline void
-pph_out_linenum_type (pph_stream *stream, linenum_type ln)
-{
-  pph_out_uint (stream, ln);
-}
-
-
-/* Emit source_location SL to STREAM.  */
-
-static inline void
-pph_out_source_location (pph_stream *stream, source_location sl)
-{
-  pph_out_uint (stream, sl);
-}
-
-
-/* Emit line table MARKER to STREAM.  */
-
-static inline void
-pph_out_linetable_marker (pph_stream *stream, enum pph_linetable_marker marker)
-{
-  gcc_assert (marker == (enum pph_linetable_marker)(unsigned char) marker);
-  pph_out_uchar (stream, marker);
-}
-
-
-/* Emit all information contained in LM to STREAM.  */
-
-static void
-pph_out_line_map (pph_stream *stream, struct line_map *lm)
-{
-  struct bitpack_d bp;
-
-  pph_out_string (stream, lm->to_file);
-  pph_out_linenum_type (stream, lm->to_line);
-  pph_out_source_location (stream, lm->start_location);
-  pph_out_uint (stream, (unsigned int) lm->included_from);
-
-  bp = bitpack_create (stream->encoder.w.ob->main_stream);
-  bp_pack_value (&bp, lm->reason, CHAR_BIT);
-  bp_pack_value (&bp, lm->sysp, CHAR_BIT);
-  bp_pack_value (&bp, lm->column_bits, COLUMN_BITS_BIT);
-  pph_out_bitpack (stream, &bp);
-}
-
-
-/* Write a reference of INCLUDE to STREAM.  Also write the START_LOCATION of
-   this include in the current line_table.  */
-
-static void
-pph_out_include (pph_stream *stream, pph_stream *include,
-                 source_location start_location)
-{
-  pph_out_source_location (stream, start_location);
-  pph_out_string (stream, include->name);
-}
-
-
-/* Compare filenames of a header and it's potentially corresponding pph file,
-   stripping the path passed in and the extension. Returns true if HEADER_PATH
-   and PPH_PATH end with the same filename. We expect HEADER_PATH to end in .h
-   and PPH_PATH to end in .pph.
-
-   FIXME pph: We should not need to do this if we handled include paths
-   correctly, but for now the linemap holds full paths and the stream's includes
-   list only holds the include name.  Also, the stream's includes hold pph
-   filenames where as the line_table as header filenames.  */
-
-static bool
-pph_filename_eq_ignoring_path (const char *header_path, const char *pph_path)
-{
-  const char *header_name = lbasename (header_path);
-  const char *pph_name = lbasename (pph_path);
-
-  const char *header_ext = strchr (header_name, '.');
-  const char *pph_ext = strchr (pph_name, '.');
-
-  unsigned int name_length;
-
-  if (header_ext != NULL)
-    {
-      name_length = header_ext - header_name;
-      gcc_assert (strcmp (header_ext, ".h") == 0);
-    }
-  else
-    /* Some headers do not have a .h suffix, but will still
-       have a .pph suffix after being pph'ed.  */
-    name_length = strlen (header_name);
-
-  gcc_assert (strcmp (pph_ext, ".pph") == 0);
-
-  /* Compare the filenames without their extension.  */
-  return pph_ext - pph_name == name_length
-         && strncmp (header_name, pph_name, name_length) == 0;
-}
-
-
-/* Return the *NEXT_INCLUDE_IX'th pph_stream in STREAM's list of includes.
-   Returns NULL if we have read all includes.  Increments *NEXT_INCLUDE_IX
-   when sucessful.  */
-
-static inline pph_stream *
-pph_get_next_include (pph_stream *stream, unsigned int *next_incl_ix)
-{
-  if (*next_incl_ix < VEC_length (pph_stream_ptr, stream->encoder.w.includes))
-    return VEC_index (pph_stream_ptr, stream->encoder.w.includes,
-	              (*next_incl_ix)++);
-  else
-    return NULL;
-}
-
-
-/* Emit the required line_map entry (those directly related to this include)
-   and some properties in the line_table to STREAM, ignoring builtin and
-   command-line entries.  We will write references to our direct includes
-   children and skip their actual line_map entries (unless they are non-pph
-   children in which case we have to write out their line_map entries as well).
-   We assume stream->encoder.w.includes contains the pph headers included in the
-   same order they are seen in the line_table.  */
+/* Write to STREAM the body of tcc_type node TYPE.  */
 
 static void
-pph_out_line_table_and_includes (pph_stream *stream)
+pph_out_tcc_type (pph_stream *stream, tree type)
 {
-  unsigned int ix, next_incl_ix = 0;
-  pph_stream *current_include;
-
-  /* Any #include should have been fully parsed and exited at this point.  */
-  gcc_assert (line_table->depth == 0);
-
-  current_include = pph_get_next_include (stream, &next_incl_ix);
+  pph_out_lang_type (stream, type);
+  pph_out_tree (stream, TYPE_POINTER_TO (type));
+  pph_out_tree (stream, TYPE_REFERENCE_TO (type));
+  pph_out_tree (stream, TYPE_NEXT_VARIANT (type));
+  /* FIXME pph - Streaming TYPE_CANONICAL generates many type comparison
+     failures.  Why?  */
+  /* FIXME pph: apparently redundant.  */
+  pph_out_tree (stream, TREE_CHAIN (type));
 
-  for (ix = PPH_NUM_IGNORED_LINE_TABLE_ENTRIES; ix < line_table->used; ix++)
+  /* The type values cache is built as constants are instantiated,
+     so we only stream it on the nodes that use it for
+     other purposes.  */
+  switch (TREE_CODE (type))
     {
-      struct line_map *lm = &line_table->maps[ix];
-
-      if (ix == PPH_NUM_IGNORED_LINE_TABLE_ENTRIES)
-        {
-          /* The first non-ignored entry should be an LC_RENAME back in the
-            header after inserting the builtin and command-line entries.  When
-            reading the pph we want this to be a simple LC_ENTER as the builtin
-            and command_line entries will already exist and we are now entering
-	    a #include.  */
-          gcc_assert (lm->reason == LC_RENAME);
-          lm->reason = LC_ENTER;
-        }
-
-      /* If this is an entry from a pph header, only output reference.  */
-      if (current_include != NULL
-	  && pph_filename_eq_ignoring_path (lm->to_file, current_include->name))
-	{
-	  int includer_level;
-
-	  gcc_assert (lm->reason == LC_ENTER);
-	  gcc_assert (lm->included_from != -1);
-
-	  pph_out_linetable_marker (stream, PPH_LINETABLE_REFERENCE);
-
-	  pph_out_include (stream, current_include, lm->start_location);
-
-	  /* Potentially lm could be included from a header other then the main
-	      one if a textual include includes a pph header (i.e. we can't
-	      simply rely on going back to included_from == -1).  */
-	  includer_level = INCLUDED_FROM (line_table, lm)->included_from;
-
-	  /* Skip all other linemap entries up to and including the LC_LEAVE
-	      from the referenced header back to the one including it.  */
-	  while (line_table->maps[++ix].included_from != includer_level)
-	    /* We should always leave this loop before the end of the
-		current line_table entries.  */
-	    gcc_assert (ix < line_table->used);
-
-	  current_include = pph_get_next_include (stream, &next_incl_ix);
-	}
-      else
-	{
-	  pph_out_linetable_marker (stream, PPH_LINETABLE_ENTRY);
-	  pph_out_line_map (stream, lm);
-	}
+    case BOUND_TEMPLATE_TEMPLATE_PARM:
+    case DECLTYPE_TYPE:
+    case TEMPLATE_TEMPLATE_PARM:
+    case TEMPLATE_TYPE_PARM:
+    case TYPENAME_TYPE:
+    case TYPEOF_TYPE:
+      pph_out_tree (stream, TYPE_VALUES_RAW (type));
+      break;
 
-      /* Restore changes made to first entry above if needed.  */
-      if (ix == PPH_NUM_IGNORED_LINE_TABLE_ENTRIES)
-	lm->reason = LC_RENAME;
+    default:
+      break;
     }
-
-  pph_out_linetable_marker (stream, PPH_LINETABLE_END);
-
-  /* Output the number of entries written to validate on input.  */
-  pph_out_uint (stream, line_table->used - PPH_NUM_IGNORED_LINE_TABLE_ENTRIES);
-
-  /* Every pph header included should have been seen and skipped in the
-     line_table streaming above.  */
-  gcc_assert (next_incl_ix == VEC_length (pph_stream_ptr,
-					  stream->encoder.w.includes));
-
-  pph_out_source_location (stream, line_table->highest_location);
-  pph_out_source_location (stream, line_table->highest_line);
-
-  pph_out_uint (stream, line_table->max_column_hint);
-}
-
-/* Write all the contents of 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);
-
-  /* Emit the line table entries and references to our direct includes.   */
-  pph_out_line_table_and_includes (stream);
-
-  /* Emit all the identifiers and pre-processor symbols in the global
-     namespace.  */
-  idents_used = cpp_lt_capture (parse_in);
-  pph_out_identifiers (stream, &idents_used);
-
-  /* Emit the bindings for the global namespace.  */
-  pph_out_scope_chain (stream);
-  if (flag_pph_dump_tree)
-    pph_dump_namespace (pph_logfile, global_namespace);
-
-  /* Emit other global state kept by the parser.  FIXME pph, these
-     globals should be fields in struct cp_parser.  */
-  pph_out_tree (stream, keyed_classes);
-  pph_out_tree_vec (stream, unemitted_tinfo_decls);
-
-  pph_out_pending_templates_list (stream);
-  pph_out_spec_entry_tables (stream);
-
-  pph_out_tree (stream, static_aggregates);
-
-  /* Emit the symbol table.  */
-  pph_out_symtab (stream);
 }
 
 
@@ -1923,38 +1662,7 @@ pph_out_tcc_declaration (pph_stream *str
 }
 
 
-/* Write to STREAM the body of tcc_type node TYPE.  */
-
-static void
-pph_out_tcc_type (pph_stream *stream, tree type)
-{
-  pph_out_lang_type (stream, type);
-  pph_out_tree (stream, TYPE_POINTER_TO (type));
-  pph_out_tree (stream, TYPE_REFERENCE_TO (type));
-  pph_out_tree (stream, TYPE_NEXT_VARIANT (type));
-  /* FIXME pph - Streaming TYPE_CANONICAL generates many type comparison
-     failures.  Why?  */
-  /* FIXME pph: apparently redundant.  */
-  pph_out_tree (stream, TREE_CHAIN (type));
-
-  /* The type values cache is built as constants are instantiated,
-     so we only stream it on the nodes that use it for
-     other purposes.  */
-  switch (TREE_CODE (type))
-    {
-    case BOUND_TEMPLATE_TEMPLATE_PARM:
-    case DECLTYPE_TYPE:
-    case TEMPLATE_TEMPLATE_PARM:
-    case TEMPLATE_TYPE_PARM:
-    case TYPENAME_TYPE:
-    case TYPEOF_TYPE:
-      pph_out_tree (stream, TYPE_VALUES_RAW (type));
-      break;
-
-    default:
-      break;
-    }
-}
+/******************************************************** tree head and body */
 
 
 /* Write the body of EXPR to STREAM.  This writes out all fields not
@@ -1984,7 +1692,7 @@ pph_write_tree_body (pph_stream *stream,
 	if (TREE_CODE (expr) == PTRMEM_CST)
 	  {
 	    pph_out_tree_common (stream, expr);
-	    pph_out_tree_1 (stream, PTRMEM_CST_MEMBER (expr), 3);
+	    pph_out_tree (stream, PTRMEM_CST_MEMBER (expr));
 	  }
 	break;
 
@@ -2025,28 +1733,28 @@ pph_write_tree_body (pph_stream *stream,
 
         /* Write the statements.  */
         for (i = tsi_start (expr); !tsi_end_p (i); tsi_next (&i))
-	  pph_out_tree_1 (stream, tsi_stmt (i), 3);
+	  pph_out_tree (stream, tsi_stmt (i));
       }
       break;
 
     case OVERLOAD:
       pph_out_tree_common (stream, expr);
-      pph_out_tree_1 (stream, OVL_CURRENT (expr), 3);
+      pph_out_tree (stream, OVL_CURRENT (expr));
       break;
 
     case IDENTIFIER_NODE:
       pph_out_cxx_binding (stream, IDENTIFIER_NAMESPACE_BINDINGS (expr));
       pph_out_cxx_binding (stream, IDENTIFIER_BINDING (expr));
-      pph_out_tree_1 (stream, IDENTIFIER_TEMPLATE (expr), 3);
-      pph_out_tree_1 (stream, IDENTIFIER_LABEL_VALUE (expr), 3);
-      pph_out_tree_1 (stream, REAL_IDENTIFIER_TYPE_VALUE (expr), 3);
+      pph_out_tree (stream, IDENTIFIER_TEMPLATE (expr));
+      pph_out_tree (stream, IDENTIFIER_LABEL_VALUE (expr));
+      pph_out_tree (stream, REAL_IDENTIFIER_TYPE_VALUE (expr));
       break;
 
     case BASELINK:
       pph_out_tree_common (stream, expr);
-      pph_out_tree_1 (stream, BASELINK_BINFO (expr), 3);
-      pph_out_tree_1 (stream, BASELINK_FUNCTIONS (expr), 3);
-      pph_out_tree_1 (stream, BASELINK_ACCESS_BINFO (expr), 3);
+      pph_out_tree (stream, BASELINK_BINFO (expr));
+      pph_out_tree (stream, BASELINK_FUNCTIONS (expr));
+      pph_out_tree (stream, BASELINK_ACCESS_BINFO (expr));
       break;
 
     case TEMPLATE_INFO:
@@ -2062,31 +1770,31 @@ pph_write_tree_body (pph_stream *stream,
 
     case STATIC_ASSERT:
       pph_out_tree_common (stream, expr);
-      pph_out_tree_1 (stream, STATIC_ASSERT_CONDITION (expr), 3);
-      pph_out_tree_1 (stream, STATIC_ASSERT_MESSAGE (expr), 3);
+      pph_out_tree (stream, STATIC_ASSERT_CONDITION (expr));
+      pph_out_tree (stream, STATIC_ASSERT_MESSAGE (expr));
       pph_out_location (stream, STATIC_ASSERT_SOURCE_LOCATION (expr));
       break;
 
     case ARGUMENT_PACK_SELECT:
       pph_out_tree_common (stream, expr);
-      pph_out_tree_1 (stream, ARGUMENT_PACK_SELECT_FROM_PACK (expr), 3);
+      pph_out_tree (stream, ARGUMENT_PACK_SELECT_FROM_PACK (expr));
       pph_out_uint (stream, ARGUMENT_PACK_SELECT_INDEX (expr));
       break;
 
     case TRAIT_EXPR:
       pph_out_tree_common (stream, expr);
-      pph_out_tree_1 (stream, TRAIT_EXPR_TYPE1 (expr), 3);
-      pph_out_tree_1 (stream, TRAIT_EXPR_TYPE2 (expr), 3);
+      pph_out_tree (stream, TRAIT_EXPR_TYPE1 (expr));
+      pph_out_tree (stream, TRAIT_EXPR_TYPE2 (expr));
       pph_out_uint (stream, TRAIT_EXPR_KIND (expr));
       break;
 
     case LAMBDA_EXPR:
       pph_out_tree_common (stream, expr);
       pph_out_location (stream, LAMBDA_EXPR_LOCATION (expr));
-      pph_out_tree_1 (stream, LAMBDA_EXPR_CAPTURE_LIST (expr), 3);
-      pph_out_tree_1 (stream, LAMBDA_EXPR_THIS_CAPTURE (expr), 3);
-      pph_out_tree_1 (stream, LAMBDA_EXPR_RETURN_TYPE (expr), 3);
-      pph_out_tree_1 (stream, LAMBDA_EXPR_EXTRA_SCOPE (expr), 3);
+      pph_out_tree (stream, LAMBDA_EXPR_CAPTURE_LIST (expr));
+      pph_out_tree (stream, LAMBDA_EXPR_THIS_CAPTURE (expr));
+      pph_out_tree (stream, LAMBDA_EXPR_RETURN_TYPE (expr));
+      pph_out_tree (stream, LAMBDA_EXPR_EXTRA_SCOPE (expr));
       pph_out_uint (stream, LAMBDA_EXPR_DISCRIMINATOR (expr));
       break;
 
@@ -2105,7 +1813,7 @@ pph_write_tree_body (pph_stream *stream,
       pph_out_uint (stream, TEMPLATE_PARM_LEVEL (expr));
       pph_out_uint (stream, TEMPLATE_PARM_ORIG_LEVEL (expr));
       pph_out_uint (stream, TEMPLATE_PARM_NUM_SIBLINGS (expr));
-      pph_out_tree_1 (stream, TEMPLATE_PARM_DECL (expr), 3);
+      pph_out_tree (stream, TEMPLATE_PARM_DECL (expr));
       break;
 
     case DEFERRED_NOEXCEPT:
@@ -2236,6 +1944,8 @@ pph_write_any_tree (pph_stream *stream, 
     }
   else if (marker == PPH_RECORD_START || marker == PPH_RECORD_START_MUTATED)
     {
+      pph_new_trace_tree (stream, expr, mergeable);
+
       /* This is the first time we see EXPR, write it out.  */
       if (marker == PPH_RECORD_START)
         {
@@ -2264,20 +1974,89 @@ pph_write_any_tree (pph_stream *stream, 
 }
 
 
-/* Callback for writing ASTs to a stream.  Write EXPR to the PPH stream
-   in OB.  */
+/************************************************************* file contents */
 
-void
-pph_write_tree (struct output_block *ob, tree expr, bool ref_p ATTRIBUTE_UNUSED)
+
+/* Emit symbol table ACTION to STREAM.  */
+
+static inline void
+pph_out_symtab_action (pph_stream *stream, enum pph_symtab_action action)
 {
-  pph_stream *stream = (pph_stream *) ob->sdata;
-  pph_write_any_tree (stream, expr, false);
+  gcc_assert (action == (enum pph_symtab_action)(unsigned char) action);
+  pph_out_uchar (stream, action);
 }
 
-void
-pph_write_mergeable_tree (pph_stream *stream, tree expr)
+
+/* Emit callgraph NODE to STREAM.  */
+
+static void
+pph_out_cgraph_node (pph_stream *stream, struct cgraph_node *node)
 {
-  pph_write_any_tree (stream, expr, true);
+  struct bitpack_d bp;
+
+  if (pph_out_start_record (stream, node, PPH_cgraph_node))
+    return;
+
+  pph_out_tree (stream, node->decl);
+  pph_out_cgraph_node (stream, node->origin);
+  pph_out_cgraph_node (stream, node->nested);
+  pph_out_cgraph_node (stream, node->next_nested);
+  pph_out_cgraph_node (stream, node->next_needed);
+  pph_out_cgraph_node (stream, node->next_sibling_clone);
+  pph_out_cgraph_node (stream, node->prev_sibling_clone);
+  pph_out_cgraph_node (stream, node->clones);
+  pph_out_cgraph_node (stream, node->clone_of);
+  pph_out_cgraph_node (stream, node->same_comdat_group);
+  gcc_assert (node->call_site_hash == NULL);
+  pph_out_tree (stream, node->former_clone_of);
+  gcc_assert (node->aux == NULL);
+  gcc_assert (VEC_empty (ipa_opt_pass, node->ipa_transforms_to_apply));
+
+  gcc_assert (VEC_empty (ipa_ref_t, node->ref_list.references));
+  gcc_assert (VEC_empty (ipa_ref_ptr, node->ref_list.refering));
+
+  gcc_assert (node->local.lto_file_data == NULL);
+  bp = bitpack_create (stream->encoder.w.ob->main_stream);
+  bp_pack_value (&bp, node->local.local, 1);
+  bp_pack_value (&bp, node->local.externally_visible, 1);
+  bp_pack_value (&bp, node->local.finalized, 1);
+  bp_pack_value (&bp, node->local.can_change_signature, 1);
+  bp_pack_value (&bp, node->local.redefined_extern_inline, 1);
+  pph_out_bitpack (stream, &bp);
+
+  pph_out_cgraph_node (stream, node->global.inlined_to);
+
+  pph_out_uint (stream, node->rtl.preferred_incoming_stack_boundary);
+
+  gcc_assert (VEC_empty (ipa_replace_map_p, node->clone.tree_map));
+  pph_out_uhwi (stream, node->thunk.fixed_offset);
+  pph_out_uhwi (stream, node->thunk.virtual_value);
+  pph_out_tree (stream, node->thunk.alias);
+  bp = bitpack_create (stream->encoder.w.ob->main_stream);
+  bp_pack_value (&bp, node->thunk.this_adjusting, 1);
+  bp_pack_value (&bp, node->thunk.virtual_offset_p, 1);
+  bp_pack_value (&bp, node->thunk.thunk_p, 1);
+  pph_out_bitpack (stream, &bp);
+
+  pph_out_uhwi (stream, node->count);
+  pph_out_uint (stream, node->count_materialization_scale);
+
+  bp = bitpack_create (stream->encoder.w.ob->main_stream);
+  bp_pack_value (&bp, node->needed, 1);
+  bp_pack_value (&bp, node->address_taken, 1);
+  bp_pack_value (&bp, node->abstract_and_needed, 1);
+  bp_pack_value (&bp, node->reachable, 1);
+  bp_pack_value (&bp, node->reachable_from_other_partition, 1);
+  bp_pack_value (&bp, node->lowered, 1);
+  bp_pack_value (&bp, node->analyzed, 1);
+  bp_pack_value (&bp, node->in_other_partition, 1);
+  bp_pack_value (&bp, node->process, 1);
+  bp_pack_value (&bp, node->alias, 1);
+  bp_pack_value (&bp, node->same_body_alias, 1);
+  bp_pack_value (&bp, node->frequency, 2);
+  bp_pack_value (&bp, node->only_called_at_startup, 1);
+  bp_pack_value (&bp, node->only_called_at_exit, 1);
+  pph_out_bitpack (stream, &bp);
 }
 
 
@@ -2304,26 +2083,266 @@ pph_add_decl_to_symtab (tree decl, enum 
 }
 
 
-/* Add INCLUDE to the list of files included by pph_out_stream.  */
+/* Emit the symbol table for STREAM.  When this image is read into
+   another translation unit, we want to guarantee that the IL
+   instances taken from this image are instantiated in the same order
+   that they were instantiated when we generated this image.
 
-void
-pph_add_include (pph_stream *include)
+   With this, we can generate code in the same order out of the
+   original header files and out of PPH images.  */
+
+static void
+pph_out_symtab (pph_stream *stream)
 {
-  VEC_safe_push (pph_stream_ptr, heap, pph_out_stream->encoder.w.includes,
-		 include);
+  pph_symtab_entry *entry;
+  unsigned i;
+
+  pph_out_uint (stream, VEC_length (pph_symtab_entry, stream->symtab.v));
+  FOR_EACH_VEC_ELT (pph_symtab_entry, stream->symtab.v, i, entry)
+    {
+      pph_out_symtab_action (stream, entry->action);
+      pph_out_tree (stream, entry->decl);
+      if (entry->action == PPH_SYMTAB_DECLARE)
+	{
+	  struct bitpack_d bp;
+	  bp = bitpack_create (stream->encoder.w.ob->main_stream);
+	  bp_pack_value (&bp, entry->top_level, 1);
+	  bp_pack_value (&bp, entry->at_end, 1);
+	  pph_out_bitpack (stream, &bp);
+	}
+      else if (entry->action == PPH_SYMTAB_EXPAND)
+	{
+	  pph_out_struct_function (stream, DECL_STRUCT_FUNCTION (entry->decl));
+	  pph_out_cgraph_node (stream, cgraph_get_node (entry->decl));
+	}
+      else
+	gcc_unreachable ();
+    }
 }
 
 
-/* Initialize the PPH writer.  */
+/* Save the IDENTIFIERS to the STREAM.  */
+
+static void
+pph_out_identifiers (pph_stream *stream, cpp_idents_used *identifiers)
+{
+  unsigned int num_entries, active_entries, id;
+
+  num_entries = identifiers->num_entries;
+  pph_out_uint (stream, identifiers->max_ident_len);
+  pph_out_uint (stream, identifiers->max_value_len);
+
+  active_entries = 0;
+  for ( id = 0; id < num_entries; ++id )
+    {
+      cpp_ident_use *entry = identifiers->entries + id;
+      if (!(entry->used_by_directive || entry->expanded_to_text))
+        continue;
+      ++active_entries;
+    }
+
+  pph_out_uint (stream, active_entries);
+
+  for ( id = 0; id < num_entries; ++id )
+    {
+      cpp_ident_use *entry = identifiers->entries + id;
+
+      if (!(entry->used_by_directive || entry->expanded_to_text))
+        continue;
+
+      /* FIXME pph: We are wasting space; ident_len, used_by_directive
+      and expanded_to_text together could fit into a single uint. */
+
+      pph_out_uint (stream, entry->used_by_directive);
+      pph_out_uint (stream, entry->expanded_to_text);
+
+      gcc_assert (entry->ident_len <= identifiers->max_ident_len);
+      pph_out_string_with_length (stream, entry->ident_str,
+				     entry->ident_len);
+
+      gcc_assert (entry->before_len <= identifiers->max_value_len);
+      pph_out_string_with_length (stream, entry->before_str,
+				     entry->before_len);
+
+      gcc_assert (entry->after_len <= identifiers->max_value_len);
+      pph_out_string_with_length (stream, entry->after_str,
+				     entry->after_len);
+    }
+}
+
+
+/* Write the global bindings in scope_chain to STREAM.  */
+
+static void
+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 (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.
+
+     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->cache, scope_chain->bindings, &ix,
+                   PPH_cp_binding_level);
+    pph_out_record_marker (stream, PPH_RECORD_START, PPH_cp_binding_level);
+    pph_out_uint (stream, ix);
+    pph_out_binding_level_1 (stream, scope_chain->bindings,
+			     PPHF_NO_XREFS | PPHF_NO_PREFS);
+  }
+}
+
+
+/* Write all the contents of STREAM.  */
+
+static void
+pph_write_file (pph_stream *stream)
+{
+  cpp_idents_used idents_used;
+
+  if (flag_pph_tracer >= 1)
+    fprintf (pph_logfile, "PPH: Writing %s\n", pph_out_file);
+
+  /* Emit the line table entries and references to our direct includes.   */
+  pph_out_line_table_and_includes (stream);
+
+  /* Emit all the identifiers and pre-processor symbols in the global
+     namespace.  */
+  idents_used = cpp_lt_capture (parse_in);
+  pph_out_identifiers (stream, &idents_used);
+
+  /* Emit the bindings for the global namespace.  */
+  pph_out_scope_chain (stream);
+  if (flag_pph_dump_tree)
+    pph_dump_namespace (pph_logfile, global_namespace);
+
+  /* Emit other global state kept by the parser.  FIXME pph, these
+     globals should be fields in struct cp_parser.  */
+  pph_out_tree (stream, keyed_classes);
+  pph_out_tree_vec (stream, unemitted_tinfo_decls);
+
+  pph_out_pending_templates_list (stream);
+  pph_out_spec_entry_tables (stream);
+
+  pph_out_tree (stream, static_aggregates);
+
+  /* Emit the symbol table.  */
+  pph_out_symtab (stream);
+}
+
+
+/******************************************************* stream finalization */
+
+
+/* Callback for lang_hooks.lto.begin_section.  Open file NAME.  */
+
+static void
+pph_begin_section (const char *name ATTRIBUTE_UNUSED)
+{
+}
+
+
+/* Callback for lang_hooks.lto.append_data.  Write LEN bytes from DATA
+   into pph_out_stream.  BLOCK is currently unused.  */
+
+static void
+pph_out_data (const void *data, size_t len, void *block ATTRIBUTE_UNUSED)
+{
+  if (data)
+    {
+      size_t written = fwrite (data, 1, len, pph_out_stream->file);
+      gcc_assert (written == len);
+    }
+}
+
+
+/* Callback for lang_hooks.lto.end_section.  */
+
+static void
+pph_end_section (void)
+{
+}
+
+/* Write the header for the PPH file represented by STREAM.  */
+
+static void
+pph_out_header (pph_stream *stream)
+{
+  pph_file_header header;
+  struct lto_output_stream header_stream;
+  int major, minor, patchlevel;
+
+  /* Collect version information.  */
+  parse_basever (&major, &minor, &patchlevel);
+  gcc_assert (major == (char) major);
+  gcc_assert (minor == (char) minor);
+  gcc_assert (patchlevel == (char) patchlevel);
+
+  /* Write the header for the PPH file.  */
+  memset (&header, 0, sizeof (header));
+  strcpy (header.id_str, pph_id_str);
+  header.major_version = (char) major;
+  header.minor_version = (char) minor;
+  header.patchlevel = (char) patchlevel;
+  header.strtab_size = stream->encoder.w.ob->string_stream->total_size;
+
+  memset (&header_stream, 0, sizeof (header_stream));
+  lto_output_data_stream (&header_stream, &header, sizeof (header));
+  lto_write_stream (&header_stream);
+}
+
+
+/* Write the body of the PPH file represented by STREAM.  */
+
+static void
+pph_out_body (pph_stream *stream)
+{
+  /* Write the string table.  */
+  lto_write_stream (stream->encoder.w.ob->string_stream);
+
+  /* Write the main stream where we have been pickling the parse trees.  */
+  lto_write_stream (stream->encoder.w.ob->main_stream);
+}
+
+
+/* Flush all the in-memory buffers for STREAM to disk.  */
 
 void
-pph_writer_init (void)
+pph_flush_buffers (pph_stream *stream)
 {
-  gcc_assert (pph_out_stream == NULL);
+  /* Redirect the LTO basic I/O langhooks.  */
+  lang_hooks.lto.begin_section = pph_begin_section;
+  lang_hooks.lto.append_data = pph_out_data;
+  lang_hooks.lto.end_section = pph_end_section;
 
-  pph_out_stream = pph_stream_open (pph_out_file, "wb");
-  if (pph_out_stream == NULL)
-    fatal_error ("Cannot open PPH file for writing: %s: %m", pph_out_file);
+  /* Write the state buffers built by pph_out_*() calls.  */
+  lto_begin_section (stream->name, false);
+  pph_out_header (stream);
+  pph_out_body (stream);
+  lto_end_section ();
 }
 
 

--
This patch is available for review at http://codereview.appspot.com/5244059



More information about the Gcc-patches mailing list