diff mbox

[pph] Add reading code for DECL_LANG_SPECIFIC (issue4345046)

Message ID 20110404101029.25E4F1DA1BC@topo.tor.corp.google.com
State New
Headers show

Commit Message

Diego Novillo April 4, 2011, 10:10 a.m. UTC
This patch adds the reading code for DECL_LANG_SPECIFIC.  With this we
can write and read global_namespace from a pph image.  There are still
missing bits, since instantiating global_namespace out of the streamed
data produces an ICE in name mangling.

For now, we are only writing and reading global_namespace, but not
really instantiating it on the way back.  For this to work, we still
need to:

- Skip the text of the header file that is being instantiated from an
  image.
- Add the missing data to fix the name mangling issues.

The bulk of the patch deals with the routines to unpickle the
DECL_LANG_SPECIFIC structures that we are writing out (all the new
routines in pph-streamer-in.c).  Nothing too exciting there, just the
mirror of the same routines in pph-streamer-out.c.

I had to add a few more streamer hooks:

- Allocate memory.  Not every node can be built with make_node.  In
  the case of CALL_EXPR, we need to explicitly allocate the number of
  arguments to the call.

- Write tree header.  The complement to the above, when writing some
  streamer-specific nodes, we may need to add more data in the header
  so the node can be re-built during reading.

- Registering decls in LTO symtabs.  This is not used by the FE
  streamer.  There is a new boolean field that the common streamer
  code checks to see if it needs to register decls instantiated from a
  stream.

Added new routines to read/write bitpacks from PPH and also to
read/write token caches (for deferred inline functions).

Tested on x86_64.  Committed to branch.


gcc/cp/ChangeLog.pph
2011-04-03  Diego Novillo  <dnovillo@google.com>

	* cp-tree.h (cxx_binding_make): Declare.
	* name-lookup.c (cxx_binding_make): Make extern.
	* parser.c (cp_token_cache): Make extern.
	* parser.h (cp_token_cache): Declare.
	* pph-streamer-in.c (pph_stream_unpack_value_fields): Move
	earlier in the file.
	(pph_stream_init_read): Call lto_reader_init.
	Associate STREAM with STREAM->DATA_IN.
	(pph_start_record): New.
	(pph_stream_read_ld_base): New.
	(pph_stream_read_ld_min): New.
	(pph_stream_read_tree_vec): New.
	(pph_stream_read_cxx_binding_1): New.
	(pph_stream_read_cxx_binding): New.
	(pph_stream_read_class_binding): New.
	(pph_stream_read_label_binding): New.
	(pph_stream_read_binding_level): New.
	(pph_stream_read_c_language_function): New.
	(pph_stream_read_language_function): New.
	(pph_stream_read_ld_fn): New.
	(pph_stream_read_ld_ns): New.
	(pph_stream_read_ld_parm): New.
	(pph_stream_read_lang_specific_data): New.
	(pph_stream_read_tree): Retrieve pph_stream instance from
	DATA_IN->SDATA.
	Mark IB argument as unused.
	Call pph_stream_read_lang_specific_data for DECLs that have
	lang_specific data.
	Replace calls to lto_input_sleb128 with pph_input_uint.
	(pph_stream_alloc_tree): New.
	* pph-streamer-out.c (pph_stream_init_write): Call
	lto_writer_init.
	Associate STREAM with OB.
	(pph_start_record): New.
	(pph_stream_write_ld_base): Change first argument to be a
	pph_stream.  Change all users.
	Call pph_start_record.
	Call pph_output_bitpack.
	(pph_stream_write_ld_min): Likewise.
	(pph_stream_write_tree_vec): Likewise.
	(pph_stream_write_cxx_binding_1): Likewise.
	(pph_stream_write_cxx_binding): Likewise.
	(pph_stream_write_class_binding): Likewise.
	(pph_stream_write_label_binding): Likewise.
	(pph_stream_write_binding_level): Likewise.
	(pph_stream_write_c_language_function): Likewise.
	(pph_stream_write_language_function): Likewise.
	(pph_stream_write_ld_fn): Likewise.
	(pph_stream_write_ld_ns): Likewise.
	(pph_stream_write_ld_parm): Likewise.
	(pph_stream_write_lang_specific_data): Likewise.
	(pph_stream_write_tree): Only call
	pph_stream_write_lang_specific_data for tree nodes that have
	DECL_LANG_SPECIFIC set.
	* pph-streamer.c (enum pph_trace_type): Add PPH_TRACE_CHAIN
	and PPH_TRACE_BITPACK.
	(pph_stream_trace): Add "chain" and "bitpack" to TYPE_S.
	Handle PPH_TRACE_CHAIN And PPH_TRACE_BITPACK.
	(pph_stream_trace_tree): Do not compute tree code size on
	NULL_TREE.
	(pph_stream_trace_chain): New.
	(pph_stream_trace_bitpack): New.
	(pph_stream_output_tree_header): New.
	(pph_stream_hooks_init): Add hooks for alloc_tree and
	output_tree_header.
	* pph-streamer.h (PPH_RECORD_START): Declare.
	(PPH_RECORD_END): Declare.
	(pph_stream_trace_chain): Declare.
	(pph_stream_trace_bitpack): Declare.
	(pth_load_token_cache): Declare.
	(pph_output_tree_or_ref): New.
	(pph_output_uchar): New.
	(pph_output_string_with_length): Do not trace the string
	twice.
	(pph_output_chain): New.
	(pph_output_bitpack): New.
	(pph_input_uchar): New.
	(pph_input_chain): New.
	(pph_input_bitpack): New.
	(pph_get_pph_stream): Remove.  Update all users.
	(pph_set_pph_stream): Remove.  Update all users.
	(pph_stream_output_tree_header): Declare.
	(pph_stream_alloc_tree): Declare.
	* pph.c (pth_load_token): Factor out of pth_load_hunk.
	(pth_load_token_cache): New.
	(pph_read_file_contents): Read global_namespace.

gcc/ChangeLog.pph
	* lto-streamer-in.c (lto_input_tree_pointers): Remove checks
	for TS_SSA_NAME, TS_STATEMENT_LIST, TS_OMP_CLAUSE and
	TS_OPTIMIZATION.
	(lto_read_tree): Check for
	streamer_hooks.register_decls_in_symtab_p before registering
	symbols in LTO symtabs.
	(gimple_streamer_reader_init): Move file_name_hash_table
	initialization to lto_reader_init.
	(lto_materialize_tree): Call streamer_hooks.alloc_tree if it exists.
	* lto-streamer-out.c (lto_output_tree_header): Remove empty
	line.
	(lto_output_tree_header): Assert that the tag to be written is valid.
	Call streamer_hooks.output_tree_header if it exists.
	* lto-streamer.c (gimple_streamer_hooks_init): Set
	register_decls_in_symtab_p to true.
	* lto-streamer.h (struct lto_streamer_hooks): Add field
	register_decls_in_symtab_p.
	(struct lto_file_decl_data): Add field sdata.
	(struct output_block): Add field sdata.
	(struct lto_streamer_hooks): Add fields
	alloc_tree and output_tree_header.
	(enum LTO_tags): Add an LTO_LAST_TAG marker, force
	LTO_NUM_TAGS have a value bigger than MAX_TREE_CODES and
	LAST_AND_UNUSED_GIMPLE_CODE.
	(lto_tag_is_tree_code_p): Return true for anything less than
	MAX_TREE_CODES.


--
This patch is available for review at http://codereview.appspot.com/4345046
diff mbox

Patch

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index aedcab2..9ce164b 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -4757,6 +4757,7 @@  extern tree check_for_out_of_scope_variable	(tree);
 extern void print_other_binding_stack		(struct cp_binding_level *);
 extern tree maybe_push_decl			(tree);
 extern tree current_decl_namespace		(void);
+extern cxx_binding *cxx_binding_make            (tree, tree);
 
 /* decl.c */
 extern tree poplevel				(int, int, int);
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index defb9d9..dc9cef2 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -285,7 +285,7 @@  cxx_binding_init (cxx_binding *binding, tree value, tree type)
 
 /* (GC)-allocate a binding object with VALUE and TYPE member initialized.  */
 
-static cxx_binding *
+cxx_binding *
 cxx_binding_make (tree value, tree type)
 {
   cxx_binding *binding;
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index ef29f3d..e146048 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -230,9 +230,6 @@  static void cp_lexer_print_token
   (FILE *, cp_token *);
 #endif /* ENABLE_CHECKING */
 
-static cp_token_cache *cp_token_cache_new
-  (cp_token *, cp_token *);
-
 static void cp_parser_initial_pragma
   (cp_token *);
 
@@ -1003,7 +1000,7 @@  cp_lexer_stop_debugging (cp_lexer* lexer)
 
 /* Create a new cp_token_cache, representing a range of tokens.  */
 
-static cp_token_cache *
+cp_token_cache *
 cp_token_cache_new (cp_token *first, cp_token *last)
 {
   cp_token_cache *cache = ggc_alloc_cp_token_cache ();
diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h
index 50efabc..ac08918 100644
--- a/gcc/cp/parser.h
+++ b/gcc/cp/parser.h
@@ -374,5 +374,6 @@  extern void cp_lexer_dump_tokens (FILE *, VEC(cp_token,gc) *, unsigned);
 extern void cp_lexer_get_tokens (cp_lexer *);
 extern cp_token_position cp_lexer_token_position (cp_lexer *, bool);
 extern void cp_lexer_debug_tokens (VEC(cp_token,gc) *);
+extern cp_token_cache *cp_token_cache_new (cp_token *, cp_token *);
 
 #endif  /* GCC_CP_PARSER_H  */
diff --git a/gcc/cp/pph-streamer-in.c b/gcc/cp/pph-streamer-in.c
index aaa0a6c..af7cd57 100644
--- a/gcc/cp/pph-streamer-in.c
+++ b/gcc/cp/pph-streamer-in.c
@@ -32,6 +32,16 @@  along with GCC; see the file COPYING3.  If not see
 #include "version.h"
 #include "cppbuiltin.h"
 
+/* Callback for unpacking value fields in ASTs.  BP is the bitpack 
+   we are unpacking from.  EXPR is the tree to unpack.  */
+
+void
+pph_stream_unpack_value_fields (struct bitpack_d *bp ATTRIBUTE_UNUSED,
+				tree expr ATTRIBUTE_UNUSED)
+{
+  /* Do nothing for now.  */
+}
+
 
 /* Get the section with name NAME and type SECTION_TYPE from FILE_DATA.
    Return a pointer to the start of the section contents and store
@@ -82,6 +92,8 @@  pph_stream_init_read (pph_stream *stream)
   pph_file_header *header;
   const char *strtab, *body;
 
+  lto_reader_init ();
+
   /* Read STREAM->NAME into the memory buffer STREAM->FILE_DATA.
      FIXME pph, we are reading the whole file at once.  This seems
      wasteful.  */
@@ -118,6 +130,436 @@  pph_stream_init_read (pph_stream *stream)
   LTO_INIT_INPUT_BLOCK_PTR (stream->ib, body, 0, body_size);
   stream->data_in = lto_data_in_create (stream->pph_sections[0], strtab,
                                         strtab_size, NULL);
+
+  /* Associate STREAM with STREAM->DATA_IN so we can recover it from
+     the streamer hooks.  */
+  stream->data_in->sdata = (void *) stream;
+}
+
+
+/* Read and return a record marker from STREAM.  The marker
+   must be one of PPH_RECORD_START or PPH_RECORD_END.  If PPH_RECORD_END
+   is read, return false.  Otherwise, return true.  */
+
+static inline bool
+pph_start_record (pph_stream *stream)
+{
+  unsigned char marker = pph_input_uchar (stream);
+  gcc_assert (marker == PPH_RECORD_START || marker == PPH_RECORD_END);
+  return (marker == PPH_RECORD_START);
+}
+
+
+/* Read all fields in lang_decl_base instance LDB from STREAM.  */
+
+static void
+pph_stream_read_ld_base (pph_stream *stream, struct lang_decl_base *ldb)
+{
+  struct bitpack_d bp;
+
+  if (!pph_start_record (stream))
+    return;
+
+  bp = pph_input_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_stream_read_ld_min (pph_stream *stream, struct lang_decl_min *ldm)
+{
+  if (!pph_start_record (stream))
+    return;
+
+  gcc_assert (ldm->base.selector == 0);
+
+  ldm->template_info = pph_input_tree (stream);
+  if (ldm->base.u2sel == 0)
+    ldm->u2.access = pph_input_tree (stream);
+  else if (ldm->base.u2sel == 1)
+    ldm->u2.discriminator = pph_input_uint (stream);
+  else
+    gcc_unreachable ();
+}
+
+
+/* Read and return a VEC of trees from STREAM.  */
+
+static VEC(tree,gc) *
+pph_stream_read_tree_vec (pph_stream *stream)
+{
+  unsigned i, num;
+  VEC(tree,gc) *v;
+
+  num = pph_input_uint (stream);
+  v = NULL;
+  for (i = 0; i < num; i++)
+    {
+      tree t = pph_input_tree (stream);
+      VEC_safe_push (tree, gc, v, t);
+    }
+
+  return v;
+}
+
+/* Forward declaration to break cyclic dependencies.  */
+static struct cp_binding_level *pph_stream_read_binding_level (pph_stream *);
+
+/* Helper for pph_stream_read_cxx_binding.  Read and return a cxx_binding
+   instance from STREAM.  */
+
+static cxx_binding *
+pph_stream_read_cxx_binding_1 (pph_stream *stream)
+{
+  struct bitpack_d bp;
+  cxx_binding *cb;
+  tree value, type;
+
+  if (!pph_start_record (stream))
+    return NULL;
+
+  value = pph_input_tree (stream);
+  type = pph_input_tree (stream);
+  cb = cxx_binding_make (value, type);
+  cb->scope = pph_stream_read_binding_level (stream);
+  bp = pph_input_bitpack (stream);
+  cb->value_is_inherited = bp_unpack_value (&bp, 1);
+  cb->is_local = bp_unpack_value (&bp, 1);
+
+  return cb;
+}
+
+
+/* Read and return an instance of cxx_binding from STREAM.  */
+
+static cxx_binding *
+pph_stream_read_cxx_binding (pph_stream *stream)
+{
+  unsigned i, num_bindings;
+  cxx_binding *curr, *cb;
+
+  if (!pph_start_record (stream))
+    return NULL;
+
+  /* Read the list of previous bindings.  */
+  num_bindings = pph_input_uint (stream);
+  for (curr = NULL, i = 0; i < num_bindings; i++)
+    {
+      cxx_binding *prev = pph_stream_read_cxx_binding_1 (stream);
+      if (curr)
+	curr->previous = prev;
+      curr = prev;
+    }
+
+  /* Read the current binding at the end.  */
+  cb = pph_stream_read_cxx_binding_1 (stream);
+  cb->previous = curr;
+
+  return cb;
+}
+
+
+/* Read all the fields of cp_class_binding instance CB to OB.  REF_P
+   is true if the tree fields should be written as references.  */
+
+static cp_class_binding *
+pph_stream_read_class_binding (pph_stream *stream)
+{
+  cp_class_binding *cb;
+
+  if (!pph_start_record (stream))
+    return NULL;
+
+  cb = ggc_alloc_cleared_cp_class_binding ();
+  memcpy (&cb->base, pph_stream_read_cxx_binding (stream),
+	  sizeof (cxx_binding));
+  cb->identifier = pph_input_tree (stream);
+
+  return cb;
+}
+
+
+/* Read and return an instance of cp_label_binding from STREAM.  */
+
+static cp_label_binding *
+pph_stream_read_label_binding (pph_stream *stream)
+{
+  cp_label_binding *lb;
+
+  if (!pph_start_record (stream))
+    return NULL;
+
+  lb = ggc_alloc_cleared_cp_label_binding ();
+  lb->label = pph_input_tree (stream);
+  lb->prev_value = pph_input_tree (stream);
+
+  return lb;
+}
+
+
+/* Read and return an instance of cp_binding_level from STREAM.  */
+
+static struct cp_binding_level *
+pph_stream_read_binding_level (pph_stream *stream)
+{
+  unsigned i, num;
+  cp_label_binding *sl;
+  struct cp_binding_level *bl;
+  struct bitpack_d bp;
+
+  if (!pph_start_record (stream))
+    return NULL;
+
+  bl = ggc_alloc_cleared_cp_binding_level ();
+  bl->names = pph_input_chain (stream);
+  bl->names_size = pph_input_uint (stream);
+  bl->namespaces = pph_input_chain (stream);
+
+  bl->static_decls = pph_stream_read_tree_vec (stream);
+
+  bl->usings = pph_input_chain (stream);
+  bl->using_directives = pph_input_chain (stream);
+
+  num = pph_input_uint (stream);
+  bl->class_shadowed = NULL;
+  for (i = 0; i < num; i++)
+    {
+      cp_class_binding *cb = pph_stream_read_class_binding (stream);
+      VEC_safe_push (cp_class_binding, gc, bl->class_shadowed, cb);
+    }
+
+  bl->type_shadowed = pph_input_tree (stream);
+
+  num = pph_input_uint (stream);
+  bl->shadowed_labels = NULL;
+  for (i = 0; VEC_iterate (cp_label_binding, bl->shadowed_labels, i, sl); i++)
+    {
+      cp_label_binding *sl = pph_stream_read_label_binding (stream);
+      VEC_safe_push (cp_label_binding, gc, bl->shadowed_labels, sl);
+    }
+
+  bl->blocks = pph_input_chain (stream);
+  bl->this_entity = pph_input_tree (stream);
+  bl->level_chain = pph_stream_read_binding_level (stream);
+  bl->dead_vars_from_for = pph_stream_read_tree_vec (stream);
+  bl->statement_list = pph_input_chain (stream);
+  bl->binding_depth = pph_input_uint (stream);
+
+  bp = pph_input_bitpack (stream);
+  bl->kind = (enum scope_kind) bp_unpack_value (&bp, 4);
+  bl->keep = bp_unpack_value (&bp, 1);
+  bl->more_cleanups_ok = bp_unpack_value (&bp, 1);
+  bl->have_cleanups = bp_unpack_value (&bp, 1);
+
+  return bl;
+}
+
+
+/* Read and return an instance of struct c_language_function from STREAM.  */
+
+static struct c_language_function *
+pph_stream_read_c_language_function (pph_stream *stream)
+{
+  struct c_language_function *clf;
+
+  if (!pph_start_record (stream))
+    return NULL;
+
+  clf = ggc_alloc_cleared_c_language_function ();
+  clf->x_stmt_tree.x_cur_stmt_list = pph_input_tree (stream);
+  clf->x_stmt_tree.stmts_are_full_exprs_p = pph_input_uint (stream);
+
+  return clf;
+}
+
+
+/* Read and return an instance of struct language_function from STREAM.  */
+
+static struct language_function *
+pph_stream_read_language_function (pph_stream *stream)
+{
+  struct bitpack_d bp;
+  struct language_function *lf;
+
+  if (!pph_start_record (stream))
+    return NULL;
+
+  lf = ggc_alloc_cleared_language_function ();
+  memcpy (&lf->base, pph_stream_read_c_language_function (stream),
+	  sizeof (struct c_language_function));
+  lf->x_cdtor_label = pph_input_tree (stream);
+  lf->x_current_class_ptr = pph_input_tree (stream);
+  lf->x_current_class_ref = pph_input_tree (stream);
+  lf->x_eh_spec_block = pph_input_tree (stream);
+  lf->x_in_charge_parm = pph_input_tree (stream);
+  lf->x_vtt_parm = pph_input_tree (stream);
+  lf->x_return_value = pph_input_tree (stream);
+  bp = pph_input_bitpack (stream);
+  lf->x_returns_value = bp_unpack_value (&bp, 1);
+  lf->x_returns_null = bp_unpack_value (&bp, 1);
+  lf->x_returns_abnormally = bp_unpack_value (&bp, 1);
+  lf->x_in_function_try_handler = bp_unpack_value (&bp, 1);
+  lf->x_in_base_initializer = bp_unpack_value (&bp, 1);
+  lf->can_throw = bp_unpack_value (&bp, 1);
+
+  /* FIXME pph.  We are not reading lf->x_named_labels.  */
+
+  lf->bindings = pph_stream_read_binding_level (stream);
+  lf->x_local_names = pph_stream_read_tree_vec (stream);
+
+  /* FIXME pph.  We are not reading lf->extern_decl_map.  */
+
+  return lf;
+}
+
+
+/* Read all the fields of lang_decl_fn instance LDF from STREAM.  */
+
+static void
+pph_stream_read_ld_fn (pph_stream *stream, struct lang_decl_fn *ldf)
+{
+  struct bitpack_d bp;
+
+  if (!pph_start_record (stream))
+    return;
+
+  bp = pph_input_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_input_tree (stream);
+  ldf->context = pph_input_tree (stream);
+
+  if (ldf->thunk_p == 0)
+    ldf->u5.cloned_function = pph_input_tree (stream);
+  else if (ldf->thunk_p == 1)
+    ldf->u5.fixed_offset = pph_input_uint (stream);
+  else
+    gcc_unreachable ();
+
+  if (ldf->pending_inline_p == 1)
+    ldf->u.pending_inline_info = pth_load_token_cache (stream);
+  else if (ldf->pending_inline_p == 0)
+    ldf->u.saved_language_function = pph_stream_read_language_function (stream);
+}
+
+
+/* Read all the fields of lang_decl_ns instance LDNS from STREAM.  */
+
+static void
+pph_stream_read_ld_ns (pph_stream *stream, struct lang_decl_ns *ldns)
+{
+  if (!pph_start_record (stream))
+    return;
+
+  ldns->level = pph_stream_read_binding_level (stream);
+}
+
+
+/* Read all the fields of lang_decl_parm instance LDP from STREAM.  */
+
+static void
+pph_stream_read_ld_parm (pph_stream *stream, struct lang_decl_parm *ldp)
+{
+  if (!pph_start_record (stream))
+    return;
+
+  ldp->level = pph_input_uint (stream);
+  ldp->index = pph_input_uint (stream);
+}
+
+
+/* Read language specific data in DECL from STREAM.  */
+
+static void
+pph_stream_read_lang_specific_data (pph_stream *stream, tree decl)
+{
+  struct lang_decl *ld;
+  struct lang_decl_base *ldb;
+
+  if (!pph_start_record (stream))
+    return;
+
+  /* Allocate a lang_decl structure for DECL.  */
+  retrofit_lang_decl (decl);
+
+  ld = DECL_LANG_SPECIFIC (decl);
+  ldb = &ld->u.base;
+
+  /* Read all the fields in lang_decl_base.  */
+  pph_stream_read_ld_base (stream, ldb);
+
+  if (ldb->selector == 0)
+    {
+      /* Read all the fields in lang_decl_min.  */
+      pph_stream_read_ld_min (stream, &ld->u.min);
+    }
+  else if (ldb->selector == 1)
+    {
+      /* Read all the fields in lang_decl_fn.  */
+      pph_stream_read_ld_fn (stream, &ld->u.fn);
+    }
+  else if (ldb->selector == 2)
+    {
+      /* Read all the fields in lang_decl_ns.  */
+      pph_stream_read_ld_ns (stream, &ld->u.ns);
+    }
+  else if (ldb->selector == 3)
+    {
+      /* Read all the fields in lang_decl_parm.  */
+      pph_stream_read_ld_parm (stream, &ld->u.parm);
+    }
+  else
+    gcc_unreachable ();
+}
+
+
+/* Allocate a tree node with code CODE.  IB and DATA_IN are used to
+   read more data from the stream, if needed to build this node.
+   Return NULL if we did not want to handle this node.  In that case,
+   the caller will call make_node to allocate this tree.  */
+
+tree
+pph_stream_alloc_tree (enum tree_code code,
+	               struct lto_input_block *ib ATTRIBUTE_UNUSED,
+		       struct data_in *data_in)
+{
+  pph_stream *stream = (pph_stream *) data_in->sdata;
+
+  if (code == CALL_EXPR)
+    {
+      unsigned nargs = pph_input_uint (stream);
+      return build_vl_exp (CALL_EXPR, nargs + 3);
+    }
+
+  return NULL_TREE;
 }
 
 
@@ -127,29 +569,30 @@  pph_stream_init_read (pph_stream *stream)
    tree.  */
 
 void
-pph_stream_read_tree (struct lto_input_block *ib, struct data_in *data_in,
-		      tree expr)
+pph_stream_read_tree (struct lto_input_block *ib ATTRIBUTE_UNUSED,
+		      struct data_in *data_in, tree expr)
 {
-  if (TREE_CODE (expr) == FUNCTION_DECL)
-    DECL_SAVED_TREE (expr) = lto_input_tree (ib, data_in);
+  pph_stream *stream = (pph_stream *) data_in->sdata;
+
+  if (DECL_P (expr))
+    {
+      if (TREE_CODE (expr) == FUNCTION_DECL
+	  || TREE_CODE (expr) == NAMESPACE_DECL
+	  || TREE_CODE (expr) == PARM_DECL
+	  || LANG_DECL_HAS_MIN (expr))
+	{
+	  pph_stream_read_lang_specific_data (stream, expr);
+	  if (TREE_CODE (expr) == FUNCTION_DECL)
+	    DECL_SAVED_TREE (expr) = pph_input_tree (stream);
+	}
+    }
   else if (TREE_CODE (expr) == STATEMENT_LIST)
     {
-      HOST_WIDE_INT i, num_trees = lto_input_sleb128 (ib);
+      HOST_WIDE_INT i, num_trees = pph_input_uint (stream);
       for (i = 0; i < num_trees; i++)
 	{
-	  tree stmt = lto_input_tree (ib, data_in);
+	  tree stmt = pph_input_tree (stream);
 	  append_to_statement_list (stmt, &expr);
 	}
     }
 }
-
-
-/* Callback for unpacking value fields in ASTs.  BP is the bitpack 
-   we are unpacking from.  EXPR is the tree to unpack.  */
-
-void
-pph_stream_unpack_value_fields (struct bitpack_d *bp ATTRIBUTE_UNUSED,
-				tree expr ATTRIBUTE_UNUSED)
-{
-  /* Do nothing for now.  */
-}
diff --git a/gcc/cp/pph-streamer-out.c b/gcc/cp/pph-streamer-out.c
index a67d60b..d1b4cec 100644
--- a/gcc/cp/pph-streamer-out.c
+++ b/gcc/cp/pph-streamer-out.c
@@ -37,6 +37,7 @@  along with GCC; see the file COPYING3.  If not see
    not allow passing a FILE handle to them.  */
 static FILE *current_pph_file = NULL;
 
+
 /* Callback for packing value fields in ASTs.  BP is the bitpack 
    we are packing into.  EXPR is the tree to pack.  */
 
@@ -53,11 +54,15 @@  pph_stream_pack_value_fields (struct bitpack_d *bp ATTRIBUTE_UNUSED,
 void
 pph_stream_init_write (pph_stream *stream)
 {
+  lto_writer_init ();
   stream->out_state = lto_new_out_decl_state ();
   lto_push_out_decl_state (stream->out_state);
   stream->decl_state_stream = XCNEW (struct lto_output_stream);
   stream->ob = create_output_block (LTO_section_decls);
-  pph_set_pph_stream (stream->ob, stream);
+
+  /* Associate STREAM with STREAM->OB so we can recover it from the
+     streamer hooks.  */
+  stream->ob->sdata = (void *) stream;
 }
 
 
@@ -160,20 +165,37 @@  pph_stream_flush_buffers (pph_stream *stream)
 }
 
 
+/* Start a new record in STREAM for data in DATA.  If DATA is NULL,
+   write an end-of-record marker and return false.  Otherwise, write a
+   start-of-record marker and return true.  */
+
+static inline bool
+pph_start_record (pph_stream *stream, void *data)
+{
+  if (data)
+    {
+      pph_output_uchar (stream, PPH_RECORD_START);
+      return true;
+    }
+  else
+    {
+      pph_output_uchar (stream, PPH_RECORD_END);
+      return false;
+    }
+}
+
+
 /* Write all the fields in lang_decl_base instance LDB to OB.  */
 
 static void
-pph_stream_write_ld_base (struct output_block *ob, struct lang_decl_base *ldb)
+pph_stream_write_ld_base (pph_stream *stream, struct lang_decl_base *ldb)
 {
   struct bitpack_d bp;
 
-  if (ldb == NULL)
-    {
-      pph_output_uint (pph_get_pph_stream (ob), 0);
-      return;
-    }
+  if (!pph_start_record (stream, ldb))
+    return;
 
-  bp = bitpack_create (ob->main_stream);
+  bp = bitpack_create (stream->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);
@@ -186,277 +208,245 @@  pph_stream_write_ld_base (struct output_block *ob, struct lang_decl_base *ldb)
   bp_pack_value (&bp, ldb->template_conv_p, 1);
   bp_pack_value (&bp, ldb->odr_used, 1);
   bp_pack_value (&bp, ldb->u2sel, 1);
-  lto_output_bitpack (&bp);
+  pph_output_bitpack (stream, &bp);
 }
 
 
-/* Write all the fields in lang_decl_min instance LDM to OB.  If REF_P
+/* Write all the fields in lang_decl_min instance LDM to STREAM.  If REF_P
    is true, all tree fields should be written as references.  */
 
 static void
-pph_stream_write_ld_min (struct output_block *ob, struct lang_decl_min *ldm,
+pph_stream_write_ld_min (pph_stream *stream, struct lang_decl_min *ldm,
 		         bool ref_p)
 {
-  if (ldm == 0)
-    {
-      pph_output_uint (pph_get_pph_stream (ob), 0);
-      return;
-    }
+  if (!pph_start_record (stream, ldm))
+    return;
 
   gcc_assert (ldm->base.selector == 0);
 
-  lto_output_tree_or_ref (ob, ldm->template_info, ref_p);
+  pph_output_tree_or_ref (stream, ldm->template_info, ref_p);
   if (ldm->base.u2sel == 0)
-    lto_output_tree_or_ref (ob, ldm->u2.access, ref_p);
+    pph_output_tree_or_ref (stream, ldm->u2.access, ref_p);
   else if (ldm->base.u2sel == 1)
-    lto_output_sleb128_stream (ob->main_stream, ldm->u2.discriminator);
+    pph_output_uint (stream, ldm->u2.discriminator);
   else
     gcc_unreachable ();
 }
 
 
-/* Write all the trees in VEC V to OB.  REF_P is true if the trees should
+/* Write all the trees in VEC V to STREAM.  REF_P is true if the trees should
    be written as references.  */
 
 static void
-pph_stream_write_tree_vec (struct output_block *ob, VEC(tree,gc) *v, bool ref_p)
+pph_stream_write_tree_vec (pph_stream *stream, VEC(tree,gc) *v, bool ref_p)
 {
   unsigned i;
   tree t;
 
-  lto_output_uleb128_stream (ob->main_stream, VEC_length (tree, v));
+  pph_output_uint (stream, VEC_length (tree, v));
   for (i = 0; VEC_iterate (tree, v, i, t); i++)
-    lto_output_tree_or_ref (ob, t, ref_p);
+    pph_output_tree_or_ref (stream, t, ref_p);
 }
 
 /* Forward declaration to break cyclic dependencies.  */
-static void pph_stream_write_binding_level (struct output_block *,
+static void pph_stream_write_binding_level (pph_stream *,
 					    struct cp_binding_level *, bool);
 
 
-/* Helper for pph_stream_write_cxx_binding.  OB, CB and REF_P are as in
+/* Helper for pph_stream_write_cxx_binding.  STREAM, CB and REF_P are as in
    pph_stream_write_cxx_binding.  */
 
 static void
-pph_stream_write_cxx_binding_1 (struct output_block *ob, cxx_binding *cb,
-				bool ref_p)
+pph_stream_write_cxx_binding_1 (pph_stream *stream, cxx_binding *cb, bool ref_p)
 {
   struct bitpack_d bp;
 
-  if (cb == NULL)
-    {
-      pph_output_uint (pph_get_pph_stream (ob), 0);
-      return;
-    }
+  if (!pph_start_record (stream, cb))
+    return;
 
-  lto_output_tree_or_ref (ob, cb->value, ref_p);
-  lto_output_tree_or_ref (ob, cb->type, ref_p);
-  pph_stream_write_binding_level (ob, cb->scope, ref_p);
-  bp = bitpack_create (ob->main_stream);
+  pph_output_tree_or_ref (stream, cb->value, ref_p);
+  pph_output_tree_or_ref (stream, cb->type, ref_p);
+  pph_stream_write_binding_level (stream, cb->scope, ref_p);
+  bp = bitpack_create (stream->ob->main_stream);
   bp_pack_value (&bp, cb->value_is_inherited, 1);
   bp_pack_value (&bp, cb->is_local, 1);
-  lto_output_bitpack (&bp);
+  pph_output_bitpack (stream, &bp);
 }
 
 
-/* Write all the fields of cxx_binding instance CB to OB.  REF_P is
+/* Write all the fields of cxx_binding instance CB to STREAM.  REF_P is
    true if the tree fields should be written as references.  */
 
 static void
-pph_stream_write_cxx_binding (struct output_block *ob, cxx_binding *cb,
-			      bool ref_p)
+pph_stream_write_cxx_binding (pph_stream *stream, cxx_binding *cb, bool ref_p)
 {
   unsigned num_bindings;
   cxx_binding *prev;
 
-  if (cb == NULL)
-    {
-      pph_output_uint (pph_get_pph_stream (ob), 0);
-      return;
-    }
+  if (!pph_start_record (stream, cb))
+    return;
 
   for (num_bindings = 0, prev = cb->previous; prev; prev = prev->previous)
     num_bindings++;
 
   /* Write the list of previous bindings.  */
-  lto_output_sleb128_stream (ob->main_stream, num_bindings);
+  pph_output_uint (stream, num_bindings);
   for (prev = cb->previous; prev; prev = prev->previous)
-    pph_stream_write_cxx_binding_1 (ob, prev, ref_p);
+    pph_stream_write_cxx_binding_1 (stream, prev, ref_p);
 
   /* Write the current binding at the end.  */
-  pph_stream_write_cxx_binding_1 (ob, cb, ref_p);
+  pph_stream_write_cxx_binding_1 (stream, cb, ref_p);
 }
 
 
-/* Write all the fields of cp_class_binding instance CB to OB.  REF_P
+/* Write all the fields of cp_class_binding instance CB to STREAM.  REF_P
    is true if the tree fields should be written as references.  */
 
 static void
-pph_stream_write_class_binding (struct output_block *ob,
-				cp_class_binding *cb, bool ref_p)
+pph_stream_write_class_binding (pph_stream *stream, cp_class_binding *cb,
+			        bool ref_p)
 {
-  if (cb == NULL)
-    {
-      pph_output_uint (pph_get_pph_stream (ob), 0);
-      return;
-    }
+  if (!pph_start_record (stream, cb))
+    return;
 
-  pph_stream_write_cxx_binding (ob, &cb->base, ref_p);
-  lto_output_tree_or_ref (ob, cb->identifier, ref_p);
+  pph_stream_write_cxx_binding (stream, &cb->base, ref_p);
+  pph_output_tree_or_ref (stream, cb->identifier, ref_p);
 }
 
 
-/* Write all the fields of cp_label_binding instance LB to OB.  If
+/* Write all the fields of cp_label_binding instance LB to STREAM.  If
    REF_P is true, tree fields will be written as references.  */
 
 static void
-pph_stream_write_label_binding (struct output_block *ob,
-				cp_label_binding *lb, bool ref_p)
+pph_stream_write_label_binding (pph_stream *stream, cp_label_binding *lb,
+				bool ref_p)
 {
-  if (lb == NULL)
-    {
-      pph_output_uint (pph_get_pph_stream (ob), 0);
-      return;
-    }
+  if (!pph_start_record (stream, lb))
+    return;
 
-  lto_output_tree_or_ref (ob, lb->label, ref_p);
-  lto_output_tree_or_ref (ob, lb->prev_value, ref_p);
+  pph_output_tree_or_ref (stream, lb->label, ref_p);
+  pph_output_tree_or_ref (stream, lb->prev_value, ref_p);
 }
 
 
-/* Write all the fields of cp_binding_level instance BL to OB.  If
+/* Write all the fields of cp_binding_level instance BL to STREAM.  If
    REF_P is true, tree fields will be written as references.  */
 
 static void
-pph_stream_write_binding_level (struct output_block *ob,
-			        struct cp_binding_level *bl, bool ref_p)
+pph_stream_write_binding_level (pph_stream *stream, struct cp_binding_level *bl,
+				bool ref_p)
 {
   unsigned i;
   cp_class_binding *cs;
   cp_label_binding *sl;
   struct bitpack_d bp;
 
-  if (bl == NULL)
-    {
-      pph_output_uint (pph_get_pph_stream (ob), 0);
-      return;
-    }
+  if (!pph_start_record (stream, bl))
+    return;
 
-  lto_output_chain (ob, bl->names, ref_p);
-  lto_output_uleb128_stream (ob->main_stream, bl->names_size);
-  lto_output_chain (ob, bl->namespaces, ref_p);
+  pph_output_chain (stream, bl->names, ref_p);
+  pph_output_uint (stream, bl->names_size);
+  pph_output_chain (stream, bl->namespaces, ref_p);
 
-  pph_stream_write_tree_vec (ob, bl->static_decls, ref_p);
+  pph_stream_write_tree_vec (stream, bl->static_decls, ref_p);
 
-  lto_output_chain (ob, bl->usings, ref_p);
-  lto_output_chain (ob, bl->using_directives, ref_p);
+  pph_output_chain (stream, bl->usings, ref_p);
+  pph_output_chain (stream, bl->using_directives, ref_p);
 
-  lto_output_uleb128_stream (ob->main_stream, VEC_length (cp_class_binding,
-					                  bl->class_shadowed));
+  pph_output_uint (stream, VEC_length (cp_class_binding, bl->class_shadowed));
   for (i = 0; VEC_iterate (cp_class_binding, bl->class_shadowed, i, cs); i++)
-    pph_stream_write_class_binding (ob, cs, ref_p);
+    pph_stream_write_class_binding (stream, cs, ref_p);
 
-  lto_output_tree_or_ref (ob, bl->type_shadowed, ref_p);
+  pph_output_tree_or_ref (stream, bl->type_shadowed, ref_p);
 
-  lto_output_uleb128_stream (ob->main_stream, VEC_length (cp_label_binding,
-					                  bl->shadowed_labels));
+  pph_output_uint (stream, VEC_length (cp_label_binding, bl->shadowed_labels));
   for (i = 0; VEC_iterate (cp_label_binding, bl->shadowed_labels, i, sl); i++)
-    pph_stream_write_label_binding (ob, sl, ref_p);
+    pph_stream_write_label_binding (stream, sl, ref_p);
 
-  lto_output_chain (ob, bl->blocks, ref_p);
-  lto_output_tree_or_ref (ob, bl->this_entity, ref_p);
-  pph_stream_write_binding_level (ob, bl->level_chain, ref_p);
-  pph_stream_write_tree_vec (ob, bl->dead_vars_from_for, ref_p);
-  lto_output_chain (ob, bl->statement_list, ref_p);
-  lto_output_sleb128_stream (ob->main_stream, bl->binding_depth);
+  pph_output_chain (stream, bl->blocks, ref_p);
+  pph_output_tree_or_ref (stream, bl->this_entity, ref_p);
+  pph_stream_write_binding_level (stream, bl->level_chain, ref_p);
+  pph_stream_write_tree_vec (stream, bl->dead_vars_from_for, ref_p);
+  pph_output_chain (stream, bl->statement_list, ref_p);
+  pph_output_uint (stream, bl->binding_depth);
 
-  bp = bitpack_create (ob->main_stream);
+  bp = bitpack_create (stream->ob->main_stream);
   bp_pack_value (&bp, bl->kind, 4);
   bp_pack_value (&bp, bl->keep, 1);
   bp_pack_value (&bp, bl->more_cleanups_ok, 1);
   bp_pack_value (&bp, bl->have_cleanups, 1);
-  lto_output_bitpack (&bp);
+  pph_output_bitpack (stream, &bp);
 }
 
 
-/* Write all the fields of c_language_function instance CLF to OB.  If
+/* Write all the fields of c_language_function instance CLF to STREAM.  If
    REF_P is true, all tree fields should be written as references.  */
 
 static void
-pph_stream_write_c_language_function (struct output_block *ob,
+pph_stream_write_c_language_function (pph_stream *stream,
 				      struct c_language_function *clf,
 				      bool ref_p)
 {
-  if (clf == NULL)
-    {
-      pph_output_uint (pph_get_pph_stream (ob), 0);
-      return;
-    }
+  if (!pph_start_record (stream, clf))
+    return;
 
-  lto_output_tree_or_ref (ob, clf->x_stmt_tree.x_cur_stmt_list, ref_p);
-  lto_output_sleb128_stream (ob->main_stream,
-			     clf->x_stmt_tree.stmts_are_full_exprs_p);
+  pph_output_tree_or_ref (stream, clf->x_stmt_tree.x_cur_stmt_list, ref_p);
+  pph_output_uint (stream, clf->x_stmt_tree.stmts_are_full_exprs_p);
 }
 
 
-/* Write all the fields of language_function instance LF to OB.  If
+/* Write all the fields of language_function instance LF to STREAM.  If
    REF_P is true, all tree fields should be written as references.  */
 
 static void
-pph_stream_write_language_function (struct output_block *ob,
+pph_stream_write_language_function (pph_stream *stream,
 				    struct language_function *lf,
 				    bool ref_p)
 {
   struct bitpack_d bp;
 
-  if (lf == NULL)
-    {
-      pph_output_uint (pph_get_pph_stream (ob), 0);
-      return;
-    }
-
-  pph_stream_write_c_language_function (ob, &lf->base, ref_p);
-  lto_output_tree_or_ref (ob, lf->x_cdtor_label, ref_p);
-  lto_output_tree_or_ref (ob, lf->x_current_class_ptr, ref_p);
-  lto_output_tree_or_ref (ob, lf->x_current_class_ref, ref_p);
-  lto_output_tree_or_ref (ob, lf->x_eh_spec_block, ref_p);
-  lto_output_tree_or_ref (ob, lf->x_in_charge_parm, ref_p);
-  lto_output_tree_or_ref (ob, lf->x_vtt_parm, ref_p);
-  lto_output_tree_or_ref (ob, lf->x_return_value, ref_p);
-  bp = bitpack_create (ob->main_stream);
+  if (!pph_start_record (stream, lf))
+    return;
+
+  pph_stream_write_c_language_function (stream, &lf->base, ref_p);
+  pph_output_tree_or_ref (stream, lf->x_cdtor_label, ref_p);
+  pph_output_tree_or_ref (stream, lf->x_current_class_ptr, ref_p);
+  pph_output_tree_or_ref (stream, lf->x_current_class_ref, ref_p);
+  pph_output_tree_or_ref (stream, lf->x_eh_spec_block, ref_p);
+  pph_output_tree_or_ref (stream, lf->x_in_charge_parm, ref_p);
+  pph_output_tree_or_ref (stream, lf->x_vtt_parm, ref_p);
+  pph_output_tree_or_ref (stream, lf->x_return_value, ref_p);
+  bp = bitpack_create (stream->ob->main_stream);
   bp_pack_value (&bp, lf->x_returns_value, 1);
   bp_pack_value (&bp, lf->x_returns_null, 1);
   bp_pack_value (&bp, lf->x_returns_abnormally, 1);
   bp_pack_value (&bp, lf->x_in_function_try_handler, 1);
   bp_pack_value (&bp, lf->x_in_base_initializer, 1);
   bp_pack_value (&bp, lf->can_throw, 1);
-  lto_output_bitpack (&bp);
+  pph_output_bitpack (stream, &bp);
 
   /* FIXME pph.  We are not writing lf->x_named_labels.  */
 
-  pph_stream_write_binding_level (ob, lf->bindings, ref_p);
-  pph_stream_write_tree_vec (ob, lf->x_local_names, ref_p);
+  pph_stream_write_binding_level (stream, lf->bindings, ref_p);
+  pph_stream_write_tree_vec (stream, lf->x_local_names, ref_p);
 
   /* FIXME pph.  We are not writing lf->extern_decl_map.  */
 }
 
 
-/* Write all the fields of lang_decl_fn instance LDF to OB.  If REF_P
+/* Write all the fields of lang_decl_fn instance LDF to STREAM.  If REF_P
    is true, all tree fields should be written as references.  */
 
 static void
-pph_stream_write_ld_fn (struct output_block *ob, struct lang_decl_fn *ldf,
+pph_stream_write_ld_fn (pph_stream *stream, struct lang_decl_fn *ldf,
 			bool ref_p)
 {
   struct bitpack_d bp;
 
-  if (ldf == NULL)
-    {
-      pph_output_uint (pph_get_pph_stream (ob), 0);
-      return;
-    }
+  if (!pph_start_record (stream, ldf))
+    return;
 
-  bp = bitpack_create (ob->main_stream);
+  bp = bitpack_create (stream->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);
@@ -473,107 +463,116 @@  pph_stream_write_ld_fn (struct output_block *ob, struct lang_decl_fn *ldf,
   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);
-  lto_output_bitpack (&bp);
+  pph_output_bitpack (stream, &bp);
 
-  lto_output_tree_or_ref (ob, ldf->befriending_classes, ref_p);
-  lto_output_tree_or_ref (ob, ldf->context, ref_p);
+  pph_output_tree_or_ref (stream, ldf->befriending_classes, ref_p);
+  pph_output_tree_or_ref (stream, ldf->context, ref_p);
 
   if (ldf->thunk_p == 0)
-    lto_output_tree_or_ref (ob, ldf->u5.cloned_function, ref_p);
+    pph_output_tree_or_ref (stream, ldf->u5.cloned_function, ref_p);
   else if (ldf->thunk_p == 1)
-    lto_output_sleb128_stream (ob->main_stream, ldf->u5.fixed_offset);
+    pph_output_uint (stream, ldf->u5.fixed_offset);
   else
     gcc_unreachable ();
 
   if (ldf->pending_inline_p == 1)
-    pth_save_token_cache (ldf->u.pending_inline_info, pph_get_pph_stream (ob));
+    pth_save_token_cache (ldf->u.pending_inline_info, stream);
   else if (ldf->pending_inline_p == 0)
-    pph_stream_write_language_function (ob, ldf->u.saved_language_function,
+    pph_stream_write_language_function (stream, ldf->u.saved_language_function,
 					ref_p);
 }
 
 
-/* Write all the fields of lang_decl_ns instance LDNS to OB.  If REF_P
+/* Write all the fields of lang_decl_ns instance LDNS to STREAM.  If REF_P
    is true, all tree fields should be written as references.  */
 
 static void
-pph_stream_write_ld_ns (struct output_block *ob, struct lang_decl_ns *ldns,
+pph_stream_write_ld_ns (pph_stream *stream, struct lang_decl_ns *ldns,
 			bool ref_p)
 {
   struct cp_binding_level *level;
 
-  if (ldns == NULL)
-    {
-      pph_output_uint (pph_get_pph_stream (ob), 0);
-      return;
-    }
+  if (!pph_start_record (stream, ldns))
+    return;
 
   level = ldns->level;
-  pph_stream_write_binding_level (ob, level, ref_p);
+  pph_stream_write_binding_level (stream, level, ref_p);
 }
 
 
-/* Write all the fields of lang_decl_parm instance LDP to OB.  If REF_P
+/* Write all the fields of lang_decl_parm instance LDP to STREAM.  If REF_P
    is true, all tree fields should be written as references.  */
 
 static void
-pph_stream_write_ld_parm (struct output_block *ob, struct lang_decl_parm *ldp)
+pph_stream_write_ld_parm (pph_stream *stream, struct lang_decl_parm *ldp)
 {
-  if (ldp == NULL)
-    {
-      pph_output_uint (pph_get_pph_stream (ob), 0);
-      return;
-    }
+  if (!pph_start_record (stream, ldp))
+    return;
 
-  lto_output_sleb128_stream (ob->main_stream, ldp->level);
-  lto_output_sleb128_stream (ob->main_stream, ldp->index);
+  pph_output_uint (stream, ldp->level);
+  pph_output_uint (stream, ldp->index);
 }
 
 
-/* Write all the lang-specific data in DECL to OB.  REF_P is true if
+/* Write all the lang-specific data in DECL to STREAM.  REF_P is true if
    the trees referenced in lang-specific fields should be written as
    references.  */
 
 static void
-pph_stream_write_lang_specific_data (struct output_block *ob,
-				     tree decl, bool ref_p)
+pph_stream_write_lang_specific_data (pph_stream *stream, tree decl, bool ref_p)
 {
   struct lang_decl *ld;
   struct lang_decl_base *ldb;
 
-  gcc_assert (DECL_P (decl) && DECL_LANG_SPECIFIC (decl));
+  gcc_assert (DECL_P (decl));
 
   ld = DECL_LANG_SPECIFIC (decl);
-  ldb = &ld->u.base;
-
+  if (!pph_start_record (stream, ld))
+    return;
+    
   /* Write all the fields in lang_decl_base.  */
-  pph_stream_write_ld_base (ob, ldb);
+  ldb = &ld->u.base;
+  pph_stream_write_ld_base (stream, ldb);
 
   if (ldb->selector == 0)
     {
       /* Write all the fields in lang_decl_min.  */
-      pph_stream_write_ld_min (ob, &ld->u.min, ref_p);
+      pph_stream_write_ld_min (stream, &ld->u.min, ref_p);
     }
   else if (ldb->selector == 1)
     {
       /* Write all the fields in lang_decl_fn.  */
-      pph_stream_write_ld_fn (ob, &ld->u.fn, ref_p);
+      pph_stream_write_ld_fn (stream, &ld->u.fn, ref_p);
     }
   else if (ldb->selector == 2)
     {
       /* Write all the fields in lang_decl_ns.  */
-      pph_stream_write_ld_ns (ob, &ld->u.ns, ref_p);
+      pph_stream_write_ld_ns (stream, &ld->u.ns, ref_p);
     }
   else if (ldb->selector == 3)
     {
       /* Write all the fields in lang_decl_parm.  */
-      pph_stream_write_ld_parm (ob, &ld->u.parm);
+      pph_stream_write_ld_parm (stream, &ld->u.parm);
     }
   else
     gcc_unreachable ();
 }
 
 
+/* Write header information for some AST nodes not handled by the
+   common streamer code.  EXPR is the tree to write to output block
+   OB.  If EXPR does not need to be handled specially, do nothing.  */
+
+void
+pph_stream_output_tree_header (struct output_block *ob, tree expr)
+{
+  pph_stream *stream = (pph_stream *) ob->sdata;
+
+  if (TREE_CODE (expr) == CALL_EXPR)
+    pph_output_uint (stream, call_expr_nargs (expr));
+}
+
+
 /* Callback for writing ASTs to a stream.  This writes all the fields
    that are not processed by default by the common tree pickler.
    OB and REF_P are as in lto_write_tree.  EXPR is the tree to write.  */
@@ -581,13 +580,20 @@  pph_stream_write_lang_specific_data (struct output_block *ob,
 void
 pph_stream_write_tree (struct output_block *ob, tree expr, bool ref_p)
 {
+  pph_stream *stream = (pph_stream *) ob->sdata;
+
   if (DECL_P (expr))
     {
-      if (DECL_LANG_SPECIFIC (expr))
-	pph_stream_write_lang_specific_data (ob, expr, ref_p);
-
-      if (TREE_CODE (expr) == FUNCTION_DECL)
-	lto_output_tree_or_ref (ob, DECL_SAVED_TREE (expr), ref_p);
+      if (TREE_CODE (expr) == FUNCTION_DECL
+	  || TREE_CODE (expr) == NAMESPACE_DECL
+	  || TREE_CODE (expr) == PARM_DECL
+	  || LANG_DECL_HAS_MIN (expr))
+	{
+	  pph_stream_write_lang_specific_data (stream, expr, ref_p);
+
+	  if (TREE_CODE (expr) == FUNCTION_DECL)
+	    pph_output_tree (stream, DECL_SAVED_TREE (expr), ref_p);
+	}
     }
   else if (TREE_CODE (expr) == STATEMENT_LIST)
     {
@@ -598,10 +604,10 @@  pph_stream_write_tree (struct output_block *ob, tree expr, bool ref_p)
       for (num_stmts = 0, i = tsi_start (expr); !tsi_end_p (i); tsi_next (&i))
 	num_stmts++;
 
-      lto_output_sleb128_stream (ob->main_stream, num_stmts);
+      pph_output_uint (stream, num_stmts);
 
       /* Write the statements.  */
       for (i = tsi_start (expr); !tsi_end_p (i); tsi_next (&i))
-	lto_output_tree_or_ref (ob, tsi_stmt (i), ref_p);
+	pph_output_tree (stream, tsi_stmt (i), ref_p);
     }
 }
diff --git a/gcc/cp/pph-streamer.c b/gcc/cp/pph-streamer.c
index 989bbbe..e65a792 100644
--- a/gcc/cp/pph-streamer.c
+++ b/gcc/cp/pph-streamer.c
@@ -68,6 +68,8 @@  pph_stream_hooks_init (void)
   h->pack_value_fields = pph_stream_pack_value_fields;
   h->indexable_with_decls_p = pph_indexable_with_decls_p;
   h->unpack_value_fields = pph_stream_unpack_value_fields;
+  h->alloc_tree = pph_stream_alloc_tree;
+  h->output_tree_header = pph_stream_output_tree_header;
 }
 
 
@@ -118,7 +120,9 @@  enum pph_trace_type
     PPH_TRACE_TREE,
     PPH_TRACE_UINT,
     PPH_TRACE_BYTES,
-    PPH_TRACE_STRING
+    PPH_TRACE_STRING,
+    PPH_TRACE_CHAIN,
+    PPH_TRACE_BITPACK
 };
 
 
@@ -131,7 +135,8 @@  pph_stream_trace (pph_stream *stream, const void *data, unsigned int nbytes,
 		  enum pph_trace_type type)
 {
   const char *op = (stream->write_p) ? "write" : "read";
-  const char *type_s[] = { "tree", "uint", "bytes", "string" };
+  const char *type_s[] = { "tree", "uint", "bytes", "string", "chain",
+                           "bitpack" };
 
   fprintf (pph_logfile, "*** %s: op=%s, type=%s, size=%u, value=",
 	   stream->name, op, type_s[type], (unsigned) nbytes);
@@ -174,6 +179,22 @@  pph_stream_trace (pph_stream *stream, const void *data, unsigned int nbytes,
 	fprintf (pph_logfile, "<nil>");
       break;
 
+    case PPH_TRACE_CHAIN:
+      {
+	const_tree t = (const_tree) data;
+	print_generic_expr (pph_logfile, CONST_CAST (union tree_node *, t),
+			    TDF_SLIM);
+	fprintf (pph_logfile, " (%d nodes in chain)", list_length (t));
+      }
+      break;
+
+    case PPH_TRACE_BITPACK:
+      {
+	const struct bitpack_d *bp = (const struct bitpack_d *) data;
+	fprintf (pph_logfile, "0x%lx", bp->word);
+      }
+    break;
+
     default:
       gcc_unreachable ();
     }
@@ -187,7 +208,8 @@  pph_stream_trace (pph_stream *stream, const void *data, unsigned int nbytes,
 void
 pph_stream_trace_tree (pph_stream *stream, tree t)
 {
-  pph_stream_trace (stream, t, tree_code_size (TREE_CODE (t)), PPH_TRACE_TREE);
+  pph_stream_trace (stream, t, t ? tree_code_size (TREE_CODE (t)) : 0,
+		    PPH_TRACE_TREE);
 }
 
 
@@ -227,3 +249,22 @@  pph_stream_trace_string_with_length (pph_stream *stream, const char *s,
 {
   pph_stream_trace (stream, s, len, PPH_TRACE_STRING);
 }
+
+
+/* Show tracing information for a tree chain starting with T on STREAM.  */
+
+void
+pph_stream_trace_chain (pph_stream *stream, tree t)
+{
+  pph_stream_trace (stream, t, t ? tree_code_size (TREE_CODE (t)) : 0,
+		    PPH_TRACE_CHAIN);
+}
+
+
+/* Show tracing information for a bitpack BP on STREAM.  */
+
+void
+pph_stream_trace_bitpack (pph_stream *stream, struct bitpack_d *bp)
+{
+  pph_stream_trace (stream, bp, sizeof (*bp), PPH_TRACE_BITPACK);
+}
diff --git a/gcc/cp/pph-streamer.h b/gcc/cp/pph-streamer.h
index 2c6f405..0f4869d 100644
--- a/gcc/cp/pph-streamer.h
+++ b/gcc/cp/pph-streamer.h
@@ -24,6 +24,10 @@  along with GCC; see the file COPYING3.  If not see
 #include "lto-streamer.h"
 #include "tree.h"
 
+/* Record markers.  */
+static const unsigned char PPH_RECORD_START = 0xff;
+static const unsigned char PPH_RECORD_END   = 0xfe;
+
 /* Number of sections in a PPH file.  FIXME, currently only one section
    is supported.  To add more, it will also be necessary to handle
    section names in pph_get_section_data and pph_free_section_data.  */
@@ -91,34 +95,49 @@  void pph_stream_trace_uint (pph_stream *, unsigned int);
 void pph_stream_trace_bytes (pph_stream *, const void *, size_t);
 void pph_stream_trace_string (pph_stream *, const char *);
 void pph_stream_trace_string_with_length (pph_stream *, const char *, unsigned);
+void pph_stream_trace_chain (pph_stream *, tree);
+void pph_stream_trace_bitpack (pph_stream *, struct bitpack_d *);
 
 /* In pph-streamer-out.c.  */
 void pph_stream_flush_buffers (pph_stream *);
 void pph_stream_init_write (pph_stream *);
 void pph_stream_write_tree (struct output_block *, tree, bool ref_p);
 void pph_stream_pack_value_fields (struct bitpack_d *, tree);
+void pph_stream_output_tree_header (struct output_block *, tree);
 
 /* In pph-streamer-in.c.  */
 void pph_stream_init_read (pph_stream *);
 void pph_stream_read_tree (struct lto_input_block *, struct data_in *, tree);
 void pph_stream_unpack_value_fields (struct bitpack_d *, tree);
-
-/* In pph.c.  FIXME move these to pph-streamer.c.  */
+tree pph_stream_alloc_tree (enum tree_code, struct lto_input_block *,
+			    struct data_in *);
+/* In pph.c.  FIXME pph move these to pph-streamer.c.  */
 struct cp_token_cache;
 void pth_save_token_cache (struct cp_token_cache *, pph_stream *);
+struct cp_token_cache *pth_load_token_cache (pph_stream *);
 
 /* Inline functions.  */
 
-/* Output AST T to STREAM.  */
+/* Output AST T to STREAM.  If REF_P is true, output all the leaves of T
+   as references.  */
+static inline void
+pph_output_tree (pph_stream *stream, tree t, bool ref_p)
+{
+  if (flag_pph_tracer)
+    pph_stream_trace_tree (stream, t);
+  lto_output_tree (stream->ob, t, ref_p);
+}
+
+/* Output AST T to STREAM.  If REF_P is true, output a reference to T.  */
 static inline void
-pph_output_tree (pph_stream *stream, tree t)
+pph_output_tree_or_ref (pph_stream *stream, tree t, bool ref_p)
 {
   if (flag_pph_tracer)
     pph_stream_trace_tree (stream, t);
-  lto_output_tree (stream->ob, t, true);
+  lto_output_tree_or_ref (stream->ob, t, ref_p);
 }
 
-/* Write a uint VALUE to STREAM.  */
+/* Write an unsigned int VALUE to STREAM.  */
 static inline void
 pph_output_uint (pph_stream *stream, unsigned int value)
 {
@@ -127,6 +146,15 @@  pph_output_uint (pph_stream *stream, unsigned int value)
   lto_output_sleb128_stream (stream->ob->main_stream, value);
 }
 
+/* Write an unsigned char VALUE to STREAM.  */
+static inline void
+pph_output_uchar (pph_stream *stream, unsigned char value)
+{
+  if (flag_pph_tracer)
+    pph_stream_trace_uint (stream, value);
+  lto_output_1_stream (stream->ob->main_stream, value);
+}
+
 /* Write N bytes from P to STREAM.  */
 static inline void
 pph_output_bytes (pph_stream *stream, const void *p, size_t n)
@@ -151,17 +179,38 @@  pph_output_string_with_length (pph_stream *stream, const char *str,
 			       unsigned int len)
 {
   if (str)
-    lto_output_string_with_length (stream->ob, stream->ob->main_stream,
-				   str, len + 1);
+    {
+      lto_output_string_with_length (stream->ob, stream->ob->main_stream,
+				     str, len + 1);
+      if (flag_pph_tracer)
+	pph_stream_trace_string_with_length (stream, str, len);
+    }
   else
     {
       /* lto_output_string_with_length does not handle NULL strings,
 	 but lto_output_string does.  */
       pph_output_string (stream, NULL);
     }
+}
 
+/* Write a chain of ASTs to STREAM starting with FIRST.  REF_P is true
+   if the nodes should be emitted as references.  */
+static inline void
+pph_output_chain (pph_stream *stream, tree first, bool ref_p)
+{
+  lto_output_chain (stream->ob, first, ref_p);
   if (flag_pph_tracer)
-    pph_stream_trace_string_with_length (stream, str, len);
+    pph_stream_trace_chain (stream, first);
+}
+
+/* Write a bitpack BP to STREAM.  */
+static inline void
+pph_output_bitpack (pph_stream *stream, struct bitpack_d *bp)
+{
+  gcc_assert (stream->ob->main_stream == bp->stream);
+  if (flag_pph_tracer)
+    pph_stream_trace_bitpack (stream, bp);
+  lto_output_bitpack (bp);
 }
 
 /* Read an unsigned HOST_WIDE_INT integer from STREAM.  */
@@ -175,6 +224,16 @@  pph_input_uint (pph_stream *stream)
   return (unsigned) n;
 }
 
+/* Read an unsigned char VALUE to STREAM.  */
+static inline unsigned char
+pph_input_uchar (pph_stream *stream)
+{
+  unsigned char n = lto_input_1_unsigned (stream->ib);
+  if (flag_pph_tracer)
+    pph_stream_trace_uint (stream, n);
+  return n;
+}
+
 /* Read N bytes from STREAM into P.  The caller is responsible for 
    allocating a sufficiently large buffer.  */
 static inline void
@@ -199,7 +258,6 @@  pph_input_string (pph_stream *stream)
 }
 
 /* Load an AST from STREAM.  Return the corresponding tree.  */
-
 static inline tree
 pph_input_tree (pph_stream *stream)
 {
@@ -209,22 +267,24 @@  pph_input_tree (pph_stream *stream)
   return t;
 }
 
-/* Return the PPH stream object associated with output block OB.  */
-
-static inline pph_stream *
-pph_get_pph_stream (struct output_block *ob)
+/* Read a chain of ASTs from STREAM.  */
+static inline tree
+pph_input_chain (pph_stream *stream)
 {
-  /* FIXME pph - Do not overload OB fields this way.  */
-  return ((pph_stream *) ob->cfg_stream);
+  tree t = lto_input_chain (stream->ib, stream->data_in);
+  if (flag_pph_tracer)
+    pph_stream_trace_chain (stream, t);
+  return t;
 }
 
-/* Set the PPH stream object F associated with output block OB.  */
-
-static inline void
-pph_set_pph_stream (struct output_block *ob, pph_stream *f)
+/* Read a bitpack from STREAM.  */
+static inline struct bitpack_d
+pph_input_bitpack (pph_stream *stream)
 {
-  /* FIXME pph - Do not overload OB fields this way.  */
-  ob->cfg_stream = (struct lto_output_stream *) f;
+  struct bitpack_d bp = lto_input_bitpack (stream->ib);
+  if (flag_pph_tracer)
+    pph_stream_trace_bitpack (stream, &bp);
+  return bp;
 }
 
 #endif  /* GCC_CP_PPH_STREAMER_H  */
diff --git a/gcc/cp/pph.c b/gcc/cp/pph.c
index c397751..82d1e1d 100644
--- a/gcc/cp/pph.c
+++ b/gcc/cp/pph.c
@@ -427,6 +427,7 @@  pth_save_token (cp_token *token, pph_stream *f)
   pth_save_token_value (f, token);
 }
 
+
 /* Save all the tokens in CACHE to PPH stream F.  */
 
 void
@@ -990,6 +991,50 @@  pth_load_token_value (cp_token *token, pph_stream *f)
 }
 
 
+/* Read and return a token from STREAM.  */
+
+static cp_token *
+pth_load_token (pph_stream *stream)
+{
+  cp_token *token = ggc_alloc_cleared_cp_token ();
+
+  /* Do not read the whole structure, the token value has
+     dynamic size as it contains swizzled pointers.
+     FIXME pph, restructure to allow bulk reads of the whole
+     section.  */
+  pph_input_bytes (stream, token, sizeof (cp_token) - sizeof (void *));
+
+  /* FIXME pph.  Use an arbitrary (but valid) location to avoid
+     confusing the rest of the compiler for now.  */
+  token->location = input_location;
+
+  /* FIXME pph: verify that pth_load_token_value works with no tokens.  */
+  pth_load_token_value (token, stream);
+
+  return token;
+}
+
+
+/* Read and return a cp_token_cache instance from STREAM.  */
+
+cp_token_cache *
+pth_load_token_cache (pph_stream *stream)
+{
+  unsigned i, num;
+  cp_token *first, *last;
+
+  num = pph_input_uint (stream);
+  for (last = first = NULL, i = 0; i < num; i++)
+    {
+      last = pth_load_token (stream);
+      if (first == NULL)
+	first = last;
+    }
+
+  return cp_token_cache_new (first, last);
+}
+
+
 /* Load the IDENTIFERS for a hunk from a STREAM.  */
 
 static void
@@ -1078,20 +1123,8 @@  pth_load_hunk (pth_image *image, pph_stream *stream)
   hunk->buffer = VEC_alloc (cp_token, gc, num_tokens);
   for (j = 0; j < num_tokens; j++)
     {
-      cp_token *token = VEC_quick_push (cp_token, hunk->buffer, NULL);
-
-      /* Do not read the whole structure, the token value has
-         dynamic size as it contains swizzled pointers.
-         FIXME pph, restructure to allow bulk reads of the whole
-         section.  */
-      pph_input_bytes (stream, token, sizeof (cp_token) - sizeof (void *));
-
-      /* FIXME pph.  Use an arbitrary (but valid) location to avoid
-         confusing the rest of the compiler for now.  */
-      token->location = input_location;
-
-      /* FIXME pph: verify that pth_load_token_value works with no tokens.  */
-      pth_load_token_value (token, stream);
+      cp_token *token = pth_load_token (stream);
+      VEC_quick_push (cp_token, hunk->buffer, token);
     }
   gcc_assert (num_tokens == VEC_length (cp_token, hunk->buffer));
 
@@ -1807,7 +1840,7 @@  static void
 pph_write_file_contents (pph_stream *stream, cpp_idents_used *idents_used)
 { 
   pth_save_identifiers (idents_used, stream);
-  pph_output_tree (stream, global_namespace);
+  pph_output_tree (stream, global_namespace, false);
 }
 
 
@@ -1898,7 +1931,7 @@  pph_read_file_contents (pph_stream *stream)
   cpp_lt_replay (parse_in, &idents_used);
   */
 
-  /* FIXME pph: Also read decls.  */
+  pph_input_tree (stream);
 }
 
 
diff --git a/gcc/lto-streamer-in.c b/gcc/lto-streamer-in.c
index f982329..1b0bc16 100644
--- a/gcc/lto-streamer-in.c
+++ b/gcc/lto-streamer-in.c
@@ -1852,9 +1852,15 @@  lto_materialize_tree (struct lto_input_block *ib, struct data_in *data_in,
     }
   else
     {
-      /* All other nodes can be materialized with a raw make_node
-	 call.  */
-      result = make_node (code);
+      /* For all other nodes, see if the streamer knows how to allocate
+	 it.  */
+      if (streamer_hooks ()->alloc_tree)
+	result = streamer_hooks ()->alloc_tree (code, ib, data_in);
+
+      /* If the hook did not handle it, materialize the tree with a raw
+	 make_node call.  */
+      if (result == NULL_TREE)
+	result = make_node (code);
     }
 
 #ifdef LTO_STREAMER_DEBUG
@@ -2360,38 +2366,15 @@  lto_input_tree_pointers (struct lto_input_block *ib, struct data_in *data_in,
   if (CODE_CONTAINS_STRUCT (code, TS_EXP))
     lto_input_ts_exp_tree_pointers (ib, data_in, expr);
 
-  if (CODE_CONTAINS_STRUCT (code, TS_SSA_NAME))
-    {
-      /* We only stream the version number of SSA names.  */
-      gcc_unreachable ();
-    }
-
   if (CODE_CONTAINS_STRUCT (code, TS_BLOCK))
     lto_input_ts_block_tree_pointers (ib, data_in, expr);
 
   if (CODE_CONTAINS_STRUCT (code, TS_BINFO))
     lto_input_ts_binfo_tree_pointers (ib, data_in, expr);
 
-  if (CODE_CONTAINS_STRUCT (code, TS_STATEMENT_LIST))
-    {
-      /* This should only appear in GENERIC.  */
-      gcc_unreachable ();
-    }
-
   if (CODE_CONTAINS_STRUCT (code, TS_CONSTRUCTOR))
     lto_input_ts_constructor_tree_pointers (ib, data_in, expr);
 
-  if (CODE_CONTAINS_STRUCT (code, TS_OMP_CLAUSE))
-    {
-      /* This should only appear in High GIMPLE.  */
-      gcc_unreachable ();
-    }
-
-  if (CODE_CONTAINS_STRUCT (code, TS_OPTIMIZATION))
-    {
-      sorry ("optimization options not supported yet");
-    }
-
   if (CODE_CONTAINS_STRUCT (code, TS_TARGET_OPTION))
     lto_input_ts_target_option (ib, expr);
 
@@ -2622,10 +2605,13 @@  lto_read_tree (struct lto_input_block *ib, struct data_in *data_in,
   if (TREE_CODE (result) == FUNCTION_DECL)
     gcc_assert (!lto_stream_as_builtin_p (result));
 
-  if (TREE_CODE (result) == VAR_DECL)
-    lto_register_var_decl_in_symtab (data_in, result);
-  else if (TREE_CODE (result) == FUNCTION_DECL && !DECL_BUILT_IN (result))
-    lto_register_function_decl_in_symtab (data_in, result);
+  if (streamer_hooks ()->register_decls_in_symtab_p)
+    {
+      if (TREE_CODE (result) == VAR_DECL)
+	lto_register_var_decl_in_symtab (data_in, result);
+      else if (TREE_CODE (result) == FUNCTION_DECL && !DECL_BUILT_IN (result))
+	lto_register_function_decl_in_symtab (data_in, result);
+    }
 
   /* end_marker = */ lto_input_1_unsigned (ib);
 
@@ -2739,6 +2725,8 @@  void
 lto_reader_init (void)
 {
   lto_streamer_init ();
+  file_name_hash_table = htab_create (37, hash_string_slot_node,
+				      eq_string_slot_node, free);
   if (streamer_hooks ()->reader_init)
     streamer_hooks ()->reader_init ();
 }
@@ -2751,8 +2739,6 @@  gimple_streamer_reader_init (void)
 {
   memset (&lto_stats, 0, sizeof (lto_stats));
   bitmap_obstack_initialize (NULL);
-  file_name_hash_table = htab_create (37, hash_string_slot_node,
-				      eq_string_slot_node, free);
   gimple_register_cfg_hooks ();
 }
 
diff --git a/gcc/lto-streamer-out.c b/gcc/lto-streamer-out.c
index 24543b2..974f3a9 100644
--- a/gcc/lto-streamer-out.c
+++ b/gcc/lto-streamer-out.c
@@ -1206,7 +1206,6 @@  lto_output_tree_header (struct output_block *ob, tree expr, int ix)
   enum tree_code code;
   lto_streamer_hooks *h = streamer_hooks ();
 
-
   /* We should not see any non-GIMPLE tree nodes here.  */
   code = TREE_CODE (expr);
   if (h->is_streamable && !h->is_streamable (expr))
@@ -1218,6 +1217,7 @@  lto_output_tree_header (struct output_block *ob, tree expr, int ix)
      EXPR on the reading side (such as the number of slots in
      variable sized nodes).  */
   tag = lto_tree_code_to_tag (code);
+  gcc_assert ((unsigned) tag < (unsigned) LTO_NUM_TAGS);
   output_record_start (ob, tag);
   output_sleb128 (ob, ix);
 
@@ -1241,6 +1241,11 @@  lto_output_tree_header (struct output_block *ob, tree expr, int ix)
     output_sleb128 (ob, TREE_VEC_LENGTH (expr));
   else if (CODE_CONTAINS_STRUCT (code, TS_BINFO))
     output_uleb128 (ob, BINFO_N_BASE_BINFOS (expr));
+
+  /* Allow the streamer to write any streamer-specific information
+     needed to instantiate the node when reading.  */
+  if (streamer_hooks ()->output_tree_header)
+    streamer_hooks ()->output_tree_header (ob, expr);
 }
 
 
diff --git a/gcc/lto-streamer.c b/gcc/lto-streamer.c
index be8d36d..1b611e6 100644
--- a/gcc/lto-streamer.c
+++ b/gcc/lto-streamer.c
@@ -843,6 +843,7 @@  gimple_streamer_hooks_init (void)
   h->is_streamable = lto_is_streamable;
   h->write_tree = gimple_streamer_write_tree;
   h->read_tree = gimple_streamer_read_tree;
+  h->register_decls_in_symtab_p = true;
 }
 
 
diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h
index 8e49a55..5f56fc6 100644
--- a/gcc/lto-streamer.h
+++ b/gcc/lto-streamer.h
@@ -98,6 +98,28 @@  typedef struct lto_streamer_hooks {
   /* Called by unpack_value_fields to retrieve any non-pointer fields
      in the tree structure.  The arguments are as in unpack_value_fields.  */
   void (*unpack_value_fields) (struct bitpack_d *, tree);
+
+  /* Non-zero if the streamer should register decls in the LTO
+     global symbol tables.  */
+  unsigned register_decls_in_symtab_p : 1;
+
+  /* Called by lto_materialize_tree for tree nodes that it does not
+     know how to allocate memory for.  If defined, this hook should
+     return a new tree node of the given code.  The data_in and
+     input_block arguments are passed in case the hook needs to
+     read more data from the stream to allocate the node.
+     If this hook returns NULL, then lto_materialize_tree will attempt
+     to allocate the tree by calling make_node directly.  */
+  tree (*alloc_tree) (enum tree_code, struct lto_input_block *,
+                      struct data_in *);
+
+  /* Called by lto_output_tree_header to write any streamer-specific
+     information needed to allocate the tree.  This hook may assume
+     that the basic header data (tree code, etc) has already been
+     written.  It should only write any extra data needed to allocate
+     the node (e.g., in the case of CALL_EXPR, this hook would write
+     the number of arguments to the CALL_EXPR).  */
+  void (*output_tree_header) (struct output_block *, tree);
 } lto_streamer_hooks;
 
 
@@ -298,9 +320,10 @@  enum LTO_tags
   LTO_imported_decl_ref,
   LTO_translation_unit_decl_ref,
   LTO_global_decl_ref,			/* Do not change.  */
+  LTO_LAST_TAG,
 
   /* This tag must always be last.  */
-  LTO_NUM_TAGS
+  LTO_NUM_TAGS = LTO_LAST_TAG + MAX_TREE_CODES + LAST_AND_UNUSED_GIMPLE_CODE
 };
 
 
@@ -673,6 +696,9 @@  struct GTY(()) lto_file_decl_data
   VEC(ld_plugin_symbol_resolution_t,heap) * GTY((skip)) resolutions;
 
   struct gcov_ctr_summary GTY((skip)) profile_info;
+
+  /* Any other streamer-specific data needed by the streamer.  */
+  void * GTY((skip)) sdata;
 };
 
 typedef struct lto_file_decl_data *lto_file_decl_data_ptr;
@@ -768,6 +794,9 @@  struct output_block
 
   /* Cache of nodes written in this section.  */
   struct lto_streamer_cache_d *writer_cache;
+
+  /* Any other streamer-specific data needed by the streamer.  */
+  void *sdata;
 };
 
 
@@ -802,6 +831,9 @@  struct data_in
 
   /* Cache of pickled nodes.  */
   struct lto_streamer_cache_d *reader_cache;
+
+  /* Any other streamer-specific data needed by the streamer.  */
+  void *sdata;
 };
 
 
@@ -1029,7 +1061,7 @@  extern VEC(lto_out_decl_state_ptr, heap) *lto_function_decl_states;
 static inline bool
 lto_tag_is_tree_code_p (enum LTO_tags tag)
 {
-  return tag > LTO_null && (unsigned) tag <= NUM_TREE_CODES;
+  return tag > LTO_null && (unsigned) tag <= MAX_TREE_CODES;
 }