diff mbox

[pph] More merging. (issue5222045)

Message ID 20111006210448.E06AC222606@jade.mtv.corp.google.com
State New
Headers show

Commit Message

Lawrence Crowl Oct. 6, 2011, 9:04 p.m. UTC
Merge symbols.  This patch doesn't actually fix anything, but it does
reorganize to address known issues.  In particular, the code shifts
from merging into a namespace to merging into a particular chain.
This prevents a latent bug in merging into inappropriate chains.  It
also sets us up to merge structs, which we may need due to users
binding directly to members.



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

Patch

Index: gcc/testsuite/ChangeLog.pph

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

	* g++.dg/pph/c4inline.cc: Clarify failure.
	* g++.dg/pph/x4keyno.cc: Clarify failure.
	* g++.dg/pph/x4keyex.cc: Clarify failure.
	* g++.dg/pph/x4keyed.cc: Clarify failure.
	* g++.dg/pph/x0tmplclass23.h: Fix header guard variables.

Index: gcc/cp/ChangeLog.pph

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

        * pt.c (pph_in_pending_templates_list): Send pph logging to
	the pph log file.
	* pph.c (pph_tree_code_text): Correct specification of enum bound.
	* pph-streamer-in.c (pph_in_namespace_tree_vec):  Remove.
	(pph_in_binding_level): Change to mutator.  Mutate early so that
	the result is usable while reading sub-trees.
	(pph_in_tcc_declaration): Comment on redundancy.
	(pph_match_to_overload): New.
	(pph_match_to_function): New.
	(pph_match_to_link): New.
	(pph_search_in_chain): New.
	(pph_prepend_to_chain): New.
	(pph_merge_into_chain): New.
	(pph_read_namespace_tree): Rename to pph_read_any_tree.  Mutate
	a specific chain rather than mutate a namespace.
	(pph_read_tree): Move within source.
	(pph_read_mergeable_tree): New.
        (pph_read_mergeable_chain): Chaining is now done by
	pph_in_mergeable_tree.
	* pph-streamer-out.c (pph_out_namespace_tree_vec):  Rename to
	pph_out_mergeable_tree_vec.  Remove unneeded namespace parameter.
	(pph_out_namespace_tree_vec_filtered): Remove unneeded.
	(pph_write_namespace_chain): Replace with pph_write_mergeable_chain.	
	(pph_write_mergeable_links): New.
	(pph_out_namespace_chain_filtered):  Rename to
	pph_out_mergeable_chain_filtered.  Remove unneeded namespace parameter.
	(pph_out_binding_level_1): Adjust code to track the above changes.
	->static_decls are already in declaration order, factor them out of
	mergable tests.
	(pph_out_tcc_declaration): Comment on redundancy.
	(pph_out_tcc_type): Comment on redundancy.
	(pph_write_namespace_tree): Rename to pph_write_any_tree.
	Change namespace parameter to boolean.  Emit location info for merging.
	(pph_write_mergeable_tree): New.
	* pph-streamer.h: Modify function declarations to reflect the above.
	(pph_out_namespace_tree): Rename to pph_out_mergeable_tree.  Remove
	unneeded namespace argument.
	(pph_out_namespace_tree_1): Fold into pph_out_mergeable_tree.
	(pph_out_namespace_chain): Rname to pph_out_mergeable_chain.  Remove
	unneeded namespace argument.
	(pph_in_namespace_tree): Rname to pph_in_mergeable_tree.  Change
	argument to chain instead of namespace.
	(pph_in_namespace_chain): Rname to pph_in_mergeable_chain.  Change
	argument to chain instead of namespace.


Index: gcc/testsuite/g++.dg/pph/x4keyno.cc
===================================================================
--- gcc/testsuite/g++.dg/pph/x4keyno.cc	(revision 179580)
+++ gcc/testsuite/g++.dg/pph/x4keyno.cc	(working copy)
@@ -1,4 +1,4 @@ 
-// { dg-xfail-if "BOGUS" { "*-*-*" } { "-fpph-map=pph.map" } }
+// { dg-xfail-if "BOGUS MERGE AUXVAR" { "*-*-*" } { "-fpph-map=pph.map" } }
 // { dg-bogus "x4keyno.cc:11:1: error: redefinition of 'const char _ZTS5keyno" "" { xfail *-*-* } 0 }
 
 #include "x0keyno1.h"
Index: gcc/testsuite/g++.dg/pph/c4inline.cc
===================================================================
--- gcc/testsuite/g++.dg/pph/c4inline.cc	(revision 179580)
+++ gcc/testsuite/g++.dg/pph/c4inline.cc	(working copy)
@@ -1,6 +1,8 @@ 
 // pph asm xdiff 36250
-//Emitting a second copy of the inline function f.
-//With comdat, the linker may paper over the differences.
+// xfail BOGUS DUPFUN
+//
+// Emitting a second copy of the inline function f.
+// With comdat, the linker may paper over the differences.
 
 #include "c0inline1.h"
 #include "c0inline2.h"
Index: gcc/testsuite/g++.dg/pph/x4keyex.cc
===================================================================
--- gcc/testsuite/g++.dg/pph/x4keyex.cc	(revision 179580)
+++ gcc/testsuite/g++.dg/pph/x4keyex.cc	(working copy)
@@ -1,4 +1,5 @@ 
 // pph asm xdiff 32642
+// xfail BOGUS LABELS
 //
 // This test case fails to compare because LFB/LFE labels are different.
 //
Index: gcc/testsuite/g++.dg/pph/x4keyed.cc
===================================================================
--- gcc/testsuite/g++.dg/pph/x4keyed.cc	(revision 179580)
+++ gcc/testsuite/g++.dg/pph/x4keyed.cc	(working copy)
@@ -1,4 +1,4 @@ 
-// { dg-xfail-if "BOGUS" { "*-*-*" } { "-fpph-map=pph.map" } }
+// { dg-xfail-if "BOGUS MERGE AUXVAR" { "*-*-*" } { "-fpph-map=pph.map" } }
 // { dg-bogus "x4keyed.cc:13:1: error: redefinition of 'const char _ZTS5keyed ..'" "" { xfail *-*-* } 0 }
 
 #include "x0keyed1.h"
Index: gcc/testsuite/g++.dg/pph/x0tmplclass23.h
===================================================================
--- gcc/testsuite/g++.dg/pph/x0tmplclass23.h	(revision 179580)
+++ gcc/testsuite/g++.dg/pph/x0tmplclass23.h	(working copy)
@@ -1,5 +1,5 @@ 
-#ifndef X0TMPLCLASS13_H
-#define X0TMPLCLASS13_H
+#ifndef X0TMPLCLASS23_H
+#define X0TMPLCLASS23_H
 
 #include "a0tmplclass1_g.h"
 #include "a0tmplclass1_s.h"
Index: gcc/cp/pph.c
===================================================================
--- gcc/cp/pph.c	(revision 179580)
+++ gcc/cp/pph.c	(working copy)
@@ -47,7 +47,7 @@  FILE *pph_logfile = NULL;
 const char*
 pph_tree_code_text (enum tree_code code)
 {
-  gcc_assert (code <= TEMPLATE_INFO);
+  gcc_assert (code < MAX_TREE_CODES);
   return tree_code_name[code];
 }
 
Index: gcc/cp/pph-streamer-in.c
===================================================================
--- gcc/cp/pph-streamer-in.c	(revision 179580)
+++ gcc/cp/pph-streamer-in.c	(working copy)
@@ -379,26 +379,6 @@  pph_in_tree_vec (pph_stream *stream)
 }
 
 
-/* Read and return a gc VEC of trees in ENCLOSING_NAMESPACE from STREAM.  */
-
-static VEC(tree,gc) *
-pph_in_namespace_tree_vec (pph_stream *stream, tree enclosing_namespace)
-{
-  HOST_WIDE_INT i, num;
-  VEC(tree,gc) *v;
-
-  num = pph_in_hwi (stream);
-  v = NULL;
-  for (i = 0; i < num; i++)
-    {
-      tree t = pph_in_namespace_tree (stream, enclosing_namespace);
-      VEC_safe_push (tree, gc, v, t);
-    }
-
-  return v;
-}
-
-
 /* Read and return a gc VEC of qualified_typedef_usage_t from STREAM.  */
 
 static VEC(qualified_typedef_usage_t,gc) *
@@ -423,8 +403,8 @@  pph_in_qual_use_vec (pph_stream *stream)
 
 
 /* Forward declaration to break cyclic dependencies.  */
-static cp_binding_level *pph_in_binding_level (pph_stream *,
-					       cp_binding_level *);
+static void pph_in_binding_level (cp_binding_level **,
+				  pph_stream *, cp_binding_level *);
 
 /* Helper for pph_in_cxx_binding.  Read and return a cxx_binding
    instance from STREAM.  */
@@ -449,7 +429,7 @@  pph_in_cxx_binding_1 (pph_stream *stream
   type = pph_in_tree (stream);
   ALLOC_AND_REGISTER (&stream->cache, ix, PPH_cxx_binding, cb,
                       cxx_binding_make (value, type));
-  cb->scope = pph_in_binding_level (stream, NULL);
+  pph_in_binding_level (&cb->scope, stream, NULL);
   bp = pph_in_bitpack (stream);
   cb->value_is_inherited = bp_unpack_value (&bp, 1);
   cb->is_local = bp_unpack_value (&bp, 1);
@@ -547,8 +527,9 @@  pph_in_label_binding (pph_stream *stream
    level given in TO_REGISTER.  This way, subsequent references to the
    global binding level will be done to the one set in TO_REGISTER.  */
 
-static cp_binding_level *
-pph_in_binding_level (pph_stream *stream, cp_binding_level *to_register)
+static void
+pph_in_binding_level (cp_binding_level **out_field,
+                      pph_stream *stream, cp_binding_level *to_register)
 {
   unsigned i, num, image_ix, ix;
   cp_binding_level *bl;
@@ -558,10 +539,16 @@  pph_in_binding_level (pph_stream *stream
 
   marker = pph_in_start_record (stream, &image_ix, &ix, PPH_cp_binding_level);
   if (marker == PPH_RECORD_END)
-    return NULL;
+    {
+      *out_field = NULL;
+      return;
+    }
   else if (pph_is_reference_marker (marker))
-    return (cp_binding_level *) pph_cache_find (stream, marker, image_ix, ix,
-						PPH_cp_binding_level);
+    {
+      *out_field = (cp_binding_level *)
+          pph_cache_find (stream, marker, image_ix, ix, PPH_cp_binding_level);
+      return;
+    }
 
   /* If TO_REGISTER is set, register that binding level instead of the newly
      allocated binding level into slot IX.  */
@@ -573,23 +560,28 @@  pph_in_binding_level (pph_stream *stream
 				  ggc_alloc_cleared_cp_binding_level (),
 				  to_register);
 
+  /* Now activate the encompasing field in case we need to insert into a
+     namespace that we just read.  */
+  *out_field = bl;
+
   entity = bl->this_entity = pph_in_tree (stream);
   if (NAMESPACE_SCOPE_P (entity))
     {
-      bl->names = pph_in_namespace_chain (stream, entity);
-      bl->namespaces = pph_in_namespace_chain (stream, entity);
-      bl->static_decls = pph_in_namespace_tree_vec (stream, entity);
-      bl->usings = pph_in_namespace_chain (stream, entity);
-      bl->using_directives = pph_in_namespace_chain (stream, entity);
+      if (flag_pph_debug >= 3)
+        debug_tree_chain (bl->names);
+      pph_in_mergeable_chain (stream, &bl->names);
+      pph_in_mergeable_chain (stream, &bl->namespaces);
+      pph_in_mergeable_chain (stream, &bl->usings);
+      pph_in_mergeable_chain (stream, &bl->using_directives);
     }
   else
     {
       bl->names = pph_in_chain (stream);
       bl->namespaces = pph_in_chain (stream);
-      bl->static_decls = pph_in_tree_vec (stream);
       bl->usings = pph_in_chain (stream);
       bl->using_directives = pph_in_chain (stream);
     }
+  bl->static_decls = pph_in_tree_vec (stream);
 
   num = pph_in_uint (stream);
   bl->class_shadowed = NULL;
@@ -610,7 +602,7 @@  pph_in_binding_level (pph_stream *stream
     }
 
   bl->blocks = pph_in_tree (stream);
-  bl->level_chain = pph_in_binding_level (stream, NULL);
+  pph_in_binding_level (&bl->level_chain, stream, NULL);
   bl->dead_vars_from_for = pph_in_tree_vec (stream);
   bl->statement_list = pph_in_chain (stream);
   bl->binding_depth = pph_in_uint (stream);
@@ -620,8 +612,6 @@  pph_in_binding_level (pph_stream *stream
   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;
 }
 
 
@@ -673,7 +663,7 @@  pph_in_language_function (pph_stream *st
 
   /* FIXME pph.  We are not reading lf->x_named_labels.  */
 
-  lf->bindings = pph_in_binding_level (stream, NULL);
+  pph_in_binding_level (&lf->bindings, stream, NULL);
   lf->x_local_names = pph_in_tree_vec (stream);
 
   /* FIXME pph.  We are not reading lf->extern_decl_map.  */
@@ -825,7 +815,7 @@  pph_in_struct_function (pph_stream *stre
 static void
 pph_in_ld_ns (pph_stream *stream, struct lang_decl_ns *ldns)
 {
-  ldns->level = pph_in_binding_level (stream, NULL);
+  pph_in_binding_level (&ldns->level, stream, NULL);
 }
 
 
@@ -1109,11 +1099,13 @@  pph_in_scope_chain (pph_stream *stream)
      scope_chain->bindings.  Otherwise, identifiers read from STREAM
      will have the wrong bindings and will fail name lookups.  */
   cur_bindings = scope_chain->bindings;
-  new_bindings = pph_in_binding_level (stream, 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);
@@ -1723,6 +1715,7 @@  pph_in_tcc_declaration (pph_stream *stre
   DECL_INITIAL (decl) = pph_in_tree (stream);
 
   /* The tree streamer only writes DECL_CHAIN for PARM_DECL nodes.  */
+  /* FIXME pph: almost redundant.  */
   if (TREE_CODE (decl) == VAR_DECL
       || TREE_CODE (decl) == FUNCTION_DECL)
     DECL_CHAIN (decl) = pph_in_tree (stream);
@@ -1774,6 +1767,7 @@  pph_in_tcc_type (pph_stream *stream, tre
   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,
@@ -2035,7 +2029,7 @@  pph_read_tree_header (pph_stream *stream
   /* Allocate the tree.  */
   tree expr = streamer_alloc_tree (ib, data_in, tag);
 
-  /* Read the language-independent bitfields for expr.  */
+  /* Read the language-independent bitfields for EXPR.  */
   bp = streamer_read_tree_bitfields (ib, expr);
 
       /* Unpack all language-dependent bitfields.  */
@@ -2047,24 +2041,149 @@  pph_read_tree_header (pph_stream *stream
 }
 
 
-/* Callback for reading ASTs from a stream.  Instantiate and return a
-   new tree from the PPH stream in DATA_IN.  */
+/* 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.  */
 
-tree
-pph_read_tree (struct lto_input_block *ib_unused ATTRIBUTE_UNUSED,
-	       struct data_in *root_data_in)
+static tree
+pph_match_to_overload (tree expr ATTRIBUTE_UNUSED,
+			location_t where ATTRIBUTE_UNUSED,
+			const char *idstr, tree *link ATTRIBUTE_UNUSED)
 {
-  /* Find data.  */
-  pph_stream *stream = (pph_stream *) root_data_in->sdata;
-  return pph_read_namespace_tree (stream, NULL);
+  /* 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
-pph_read_namespace_tree (pph_stream *stream, tree enclosing_namespace)
+pph_read_any_tree (pph_stream *stream, tree *chain)
 {
   struct lto_input_block *ib = stream->encoder.r.ib;
   struct data_in *data_in = stream->encoder.r.data_in;
@@ -2105,18 +2224,8 @@  pph_read_namespace_tree (pph_stream *str
       /* Materialize a new node from IB.  This will also read all the
          language-independent bitfields for the new tree.  */
       expr = pph_read_tree_header (stream, tag);
-      if (enclosing_namespace && DECL_P (expr))
-        {
-          /* We may need to unify two declarations.  */
-          /* FIXME crowl: location_t where = pph_in_location (stream); */
-          const char *idstr = pph_in_string (stream);
-          /* But we only search if we have a name.  */
-          if (!idstr)
-            {
-              /* FIXME crowl search for idstr in namespace.  */
-              expr = expr;
-            }
-        }
+      if (chain)
+        expr = pph_merge_into_chain (stream, expr, chain);
     }
 
   gcc_assert (marker == PPH_RECORD_START
@@ -2158,30 +2267,38 @@  pph_read_namespace_tree (pph_stream *str
 }
 
 
-/* Read a chain of tree nodes from input block IB. DATA_IN contains
-   tables and descriptors for the file being read.  */
+/* Callback for reading ASTs from a stream.  Instantiate and return a
+   new tree from the PPH stream in DATA_IN.  */
 
 tree
-pph_read_namespace_chain (pph_stream *stream, tree enclosing_namespace)
+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);
+}
+
+
+/* Read a mergeable tree from STREAM into CHAIN.  */
+
+tree
+pph_read_mergeable_tree (pph_stream *stream, tree *chain)
+{
+  return pph_read_any_tree (stream, chain);
+}
+
+
+/* Read a chain of tree nodes from STREAM.  */
+
+void
+pph_read_mergeable_chain (pph_stream *stream, tree *chain)
 {
   int i, count;
-  tree first, prev, curr;
 
-  first = prev = NULL_TREE;
   count = streamer_read_hwi (stream->encoder.r.ib);
   for (i = 0; i < count; i++)
-    {
-      curr = pph_in_namespace_tree (stream, enclosing_namespace);
-      if (prev)
-        TREE_CHAIN (prev) = curr;
-      else
-        first = curr;
-
-      TREE_CHAIN (curr) = NULL_TREE;
-      prev = curr;
-    }
-
-  return first;
+    pph_in_mergeable_tree (stream, chain);
 }
 
 
Index: gcc/cp/pt.c
===================================================================
--- gcc/cp/pt.c	(revision 179580)
+++ gcc/cp/pt.c	(working copy)
@@ -20243,7 +20243,7 @@  pph_in_pending_templates_list (pph_strea
     {
       struct pending_template *pt;
       if (flag_pph_debug >= 2)
-        fprintf (stderr, "loading %d pending templates\n", count );
+        fprintf (pph_logfile, "PPH: loading %d pending templates\n", count );
       pt = ggc_alloc_pending_template ();
       pt->next = NULL;
       pt->tinst = pph_in_tinst_level (stream);
Index: gcc/cp/pph-streamer-out.c
===================================================================
--- gcc/cp/pph-streamer-out.c	(revision 179580)
+++ gcc/cp/pph-streamer-out.c	(working copy)
@@ -584,11 +584,10 @@  pph_out_tree_vec (pph_stream *stream, VE
 }
 
 
-/* Write all the trees (from ENCLOSING_NAMESPACE) in VEC V to STREAM.  */
+/* Write all the trees in VEC V to STREAM.  */
 
 static void
-pph_out_namespace_tree_vec (pph_stream *stream, VEC(tree,gc) *v,
-                            tree enclosing_namespace)
+pph_out_mergeable_tree_vec (pph_stream *stream, VEC(tree,gc) *v)
 {
   unsigned i;
   tree t;
@@ -600,7 +599,7 @@  pph_out_namespace_tree_vec (pph_stream *
      same way as tree chains.  */
   pph_out_hwi (stream, VEC_length (tree, v));
   FOR_EACH_VEC_ELT (tree, v, i, t)
-    pph_out_namespace_tree (stream, t, enclosing_namespace);
+    pph_out_mergeable_tree (stream, t);
 }
 
 
@@ -632,37 +631,6 @@  pph_out_tree_vec_filtered (pph_stream *s
 }
 
 
-/* Write all the trees in ENCLOSING_NAMESPACE matching FILTER
-   in VEC V to STREAM.  */
-
-static void
-pph_out_namespace_tree_vec_filtered (pph_stream *stream, VEC(tree,gc) *v,
-                                     tree enclosing_namespace, unsigned filter)
-{
-  unsigned i;
-  tree t;
-  VEC(tree, heap) *to_write = NULL;
-
-  /* Special case.  If the caller wants no filtering, it is much
-     faster to just call pph_out_tree_vec.  */
-  if (filter == PPHF_NONE)
-    {
-      pph_out_namespace_tree_vec (stream, v, enclosing_namespace);
-      return;
-    }
-
-  /* Collect all the nodes that match the filter.  */
-  FOR_EACH_VEC_ELT (tree, v, i, t)
-    if (pph_tree_matches (t, filter))
-      VEC_safe_push (tree, heap, to_write, t);
-
-  /* Write them.  */
-  pph_out_namespace_tree_vec (stream, (VEC(tree,gc) *)to_write,
-                                      enclosing_namespace);
-  VEC_free (tree, heap, to_write);
-}
-
-
 /* Write all the qualified_typedef_usage_t in VEC V to STREAM.  */
 
 static void
@@ -752,33 +720,37 @@  pph_out_label_binding (pph_stream *strea
 }
 
 
-/* Emit the chain of tree nodes from ENCLOSING_NAMESPACE starting at T
-   to STREAM.
-   OB is the output block
-   to write to.  REF_P is true if chain elements should be emitted
-   as references.  */
+/* Emit the links of a chain to the STREAM in reverse order
+   from the ENCLOSING_NAMESPACE starting at T.  */
 
-void
-pph_write_namespace_chain (pph_stream *stream, tree t, tree enclosing_namespace)
+static void
+pph_write_mergeable_links (pph_stream *stream, tree t)
 {
-  int i, count;
+  tree next_link;
+  if (!t)
+    return;
 
-  count = list_length (t);
-  streamer_write_hwi (stream->encoder.w.ob, count);
-  for (i = 0; i < count; i++)
-    {
-      tree saved_chain;
+  next_link = TREE_CHAIN (t);
+  pph_write_mergeable_links (stream, next_link);
 
-      /* Clear TREE_CHAIN to avoid blindly recursing into the rest
-         of the list.  */
-      saved_chain = TREE_CHAIN (t);
-      TREE_CHAIN (t) = NULL_TREE;
+  /*FIXME pph: Is this circumlocution still needed? */
+  TREE_CHAIN (t) = NULL_TREE;
 
-      pph_write_namespace_tree (stream, t, enclosing_namespace);
+  pph_out_mergeable_tree (stream, t);
 
-      TREE_CHAIN (t) = saved_chain;
-      t = TREE_CHAIN (t);
-    }
+  TREE_CHAIN (t) = next_link;
+}
+
+
+/* Emit the chain of tree nodes from ENCLOSING_NAMESPACE starting at T
+   to STREAM.  */
+
+void
+pph_write_mergeable_chain (pph_stream *stream, tree t)
+{
+  int count = list_length (t);
+  streamer_write_hwi (stream->encoder.w.ob, count);
+  pph_write_mergeable_links (stream, t);
 }
 
 
@@ -814,8 +786,8 @@  pph_out_chain_filtered (pph_stream *stre
    starting with FIRST.  Skip any nodes that do not match FILTER.  */
 
 static void
-pph_out_namespace_chain_filtered (pph_stream *stream, tree first,
-                                  tree enclosing_namespace, unsigned filter)
+pph_out_mergeable_chain_filtered (pph_stream *stream, tree first,
+					unsigned filter)
 {
   tree t;
   VEC(tree, heap) *to_write = NULL;
@@ -824,7 +796,7 @@  pph_out_namespace_chain_filtered (pph_st
      faster to just call pph_out_chain directly.  */
   if (filter == PPHF_NONE)
     {
-      pph_out_namespace_chain (stream, first, enclosing_namespace);
+      pph_out_mergeable_chain (stream, first);
       return;
     }
 
@@ -834,8 +806,7 @@  pph_out_namespace_chain_filtered (pph_st
       VEC_safe_push (tree, heap, to_write, t);
 
   /* Write them.  */
-  pph_out_namespace_tree_vec (stream, (VEC(tree,gc) *)to_write,
-                                      enclosing_namespace);
+  pph_out_mergeable_tree_vec (stream, (VEC(tree,gc) *)to_write);
   VEC_free (tree, heap, to_write);
 }
 
@@ -857,25 +828,20 @@  pph_out_binding_level_1 (pph_stream *str
   pph_out_tree (stream, entity);
   if (NAMESPACE_SCOPE_P (entity))
     {
-      pph_out_namespace_chain_filtered (stream, bl->names,
-                                                entity, aux_filter);
-      pph_out_namespace_chain_filtered (stream, bl->namespaces,
-                                                entity, aux_filter);
-      pph_out_namespace_tree_vec_filtered (stream, bl->static_decls,
-                                                   entity, filter);
-      pph_out_namespace_chain_filtered (stream, bl->usings,
-                                                entity, aux_filter);
-      pph_out_namespace_chain_filtered (stream, bl->using_directives,
-                                                entity, aux_filter);
+      pph_out_mergeable_chain_filtered (stream, bl->names, aux_filter);
+      pph_out_mergeable_chain_filtered (stream, bl->namespaces, aux_filter);
+      pph_out_mergeable_chain_filtered (stream, bl->usings, aux_filter);
+      pph_out_mergeable_chain_filtered (stream, bl->using_directives,
+                                                aux_filter);
     }
   else
     {
       pph_out_chain_filtered (stream, bl->names, aux_filter);
       pph_out_chain_filtered (stream, bl->namespaces, aux_filter);
-      pph_out_tree_vec_filtered (stream, bl->static_decls, filter);
       pph_out_chain_filtered (stream, bl->usings, aux_filter);
       pph_out_chain_filtered (stream, bl->using_directives, aux_filter);
     }
+  pph_out_tree_vec_filtered (stream, bl->static_decls, filter);
 
   pph_out_uint (stream, VEC_length (cp_class_binding, bl->class_shadowed));
   FOR_EACH_VEC_ELT (cp_class_binding, bl->class_shadowed, i, cs)
@@ -1770,6 +1736,7 @@  pph_out_tcc_declaration (pph_stream *str
   pph_out_tree (stream, DECL_INITIAL (decl));
 
   /* The tree streamer only writes DECL_CHAIN for PARM_DECL nodes.  */
+  /* FIXME pph: almost redundant.  */
   if (TREE_CODE (decl) == VAR_DECL
       || TREE_CODE (decl) == FUNCTION_DECL)
     pph_out_tree (stream, DECL_CHAIN (decl));
@@ -1814,6 +1781,7 @@  pph_out_tcc_type (pph_stream *stream, tr
   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,
@@ -2081,19 +2049,10 @@  pph_write_tree_header (pph_stream *strea
 }
 
 
-/* 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_namespace_tree (stream, expr, NULL);
-}
+/* Write a tree EXPR (MERGEABLE or not) to STREAM.  */
 
 void
-pph_write_namespace_tree (pph_stream *stream, tree expr,
-                          tree enclosing_namespace )
+pph_write_any_tree (pph_stream *stream, tree expr, bool mergeable)
 {
   enum pph_record_marker marker;
 
@@ -2132,14 +2091,14 @@  pph_write_namespace_tree (pph_stream *st
              state of an existing tree, then we only need to write its
              body.  */
           pph_write_tree_header (stream, expr);
-          if (enclosing_namespace && DECL_P (expr))
+          if (mergeable && DECL_P (expr))
             {
               /* We may need to unify two declarations.  */
-              /* FIXME crowl: pph_out_location (stream, DECL_SOURCE_LOCATION (expr)); */
+              pph_out_location (stream, DECL_SOURCE_LOCATION (expr));
               tree name = DECL_NAME (expr);
               if (name)
                 pph_out_string_with_length (stream, IDENTIFIER_POINTER (name),
-                    IDENTIFIER_LENGTH (name));
+                                            IDENTIFIER_LENGTH (name));
               else
                 pph_out_string (stream, NULL);
             }
@@ -2152,6 +2111,23 @@  pph_write_namespace_tree (pph_stream *st
 }
 
 
+/* 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);
+}
+
+void
+pph_write_mergeable_tree (pph_stream *stream, tree expr)
+{
+  pph_write_any_tree (stream, expr, true);
+}
+
+
 /* Add DECL to the symbol table for pph_out_stream.  ACTION determines
    how DECL should be presented to the middle-end when reading this
    image.  TOP_LEVEL and AT_END are as in rest_of_decl_compilation.  */
Index: gcc/cp/pph-streamer.h
===================================================================
--- gcc/cp/pph-streamer.h	(revision 179580)
+++ gcc/cp/pph-streamer.h	(working copy)
@@ -336,8 +336,8 @@  unsigned pph_get_signature (tree, size_t
 void pph_flush_buffers (pph_stream *);
 void pph_init_write (pph_stream *);
 void pph_write_tree (struct output_block *, tree, bool);
-void pph_write_namespace_tree (pph_stream *, tree, tree);
-void pph_write_namespace_chain (pph_stream *, tree, tree);
+void pph_write_mergeable_tree (pph_stream *, tree);
+void pph_write_mergeable_chain (pph_stream *, tree);
 void pph_add_decl_to_symtab (tree, enum pph_symtab_action, bool, bool);
 void pph_add_include (pph_stream *);
 void pph_writer_init (void);
@@ -352,8 +352,8 @@  struct binding_table_s *pph_in_binding_t
 /* In pph-streamer-in.c.  */
 void pph_init_read (pph_stream *);
 tree pph_read_tree (struct lto_input_block *, struct data_in *);
-tree pph_read_namespace_tree (pph_stream *, tree);
-tree pph_read_namespace_chain (pph_stream *, tree);
+tree pph_read_mergeable_tree (pph_stream *, tree *);
+void pph_read_mergeable_chain (pph_stream *, tree *);
 location_t pph_read_location (struct lto_input_block *, struct data_in *);
 void pph_read_file (const char *);
 void pph_reader_finish (void);
@@ -473,22 +473,13 @@  pph_out_tree (pph_stream *stream, tree t
 }
 
 /* Output AST T from ENCLOSING_NAMESPACE to STREAM.
-   If -fpph-tracer is set to TLEVEL or higher, T is sent to pph_trace_tree.  */
-static inline void
-pph_out_namespace_tree_1 (pph_stream *stream, tree t, int tlevel,
-                          tree enclosing_namespace)
-{
-  if (flag_pph_tracer >= tlevel)
-    pph_trace_tree (stream, t);
-  pph_write_namespace_tree (stream, t, enclosing_namespace);
-}
-
-/* Output AST T from ENCLOSING_NAMESPACE to STREAM.
    Trigger tracing at -fpph-tracer=2.  */
 static inline void
-pph_out_namespace_tree (pph_stream *stream, tree t, tree enclosing_namespace)
+pph_out_mergeable_tree (pph_stream *stream, tree t)
 {
-  pph_out_namespace_tree_1 (stream, t, 2, enclosing_namespace);
+  if (flag_pph_tracer >= 2)
+    pph_trace_tree (stream, t);
+  pph_write_mergeable_tree (stream, t);
 }
 
 /* Write an unsigned int VALUE to STREAM.  */
@@ -574,12 +565,11 @@  pph_out_chain (pph_stream *stream, tree 
 
 /* Write a chain of ASTs to STREAM starting with FIRST.  */
 static inline void
-pph_out_namespace_chain (pph_stream *stream, tree first,
-                         tree enclosing_namespace)
+pph_out_mergeable_chain (pph_stream *stream, tree first)
 {
   if (flag_pph_tracer >= 2)
     pph_trace_chain (stream, first);
-  pph_write_namespace_chain (stream, first, enclosing_namespace);
+  pph_write_mergeable_chain (stream, first);
 }
 
 /* Write a bitpack BP to STREAM.  */
@@ -676,13 +666,12 @@  pph_in_tree (pph_stream *stream)
 
 /* Load an AST in an ENCLOSING_NAMESPACE from STREAM.
    Return the corresponding tree.  */
-static inline tree
-pph_in_namespace_tree (pph_stream *stream, tree enclosing_namespace)
+static inline void
+pph_in_mergeable_tree (pph_stream *stream, tree *chain)
 {
-  tree t = pph_read_namespace_tree (stream, enclosing_namespace);
-  if (flag_pph_tracer >= 4)
+  tree t = pph_read_mergeable_tree (stream, chain);
+  if (flag_pph_tracer >= 3)
     pph_trace_tree (stream, t);
-  return t;
 }
 
 /* Load into an array A of cardinality C of AST from STREAM.  */
@@ -731,14 +720,11 @@  pph_in_chain (pph_stream *stream)
   return t;
 }
 
-/* Read a chain of ASTs in ENCLOSING_NAMESPACE from STREAM.  */
-static inline tree
-pph_in_namespace_chain (pph_stream *stream, tree enclosing_namespace)
+/* 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)
 {
-  tree t = pph_read_namespace_chain (stream, enclosing_namespace);
-  if (flag_pph_tracer >= 2)
-    pph_trace_chain (stream, t);
-  return t;
+  pph_read_mergeable_chain (stream, chain);
 }
 
 /* Read a bitpack from STREAM.  */