diff mbox

[pph] Various Merging Fixes (issue5330048)

Message ID 20111028175651.522312225E6@jade.mtv.corp.google.com
State New
Headers show

Commit Message

Lawrence Crowl Oct. 28, 2011, 5:56 p.m. UTC
Add namespace merging.  This change generalizes pph_out_merge_keys on
the global namespace to all namspaces.  Preallocate decl_lang_specific
for namespaces with streaming in their keys.  Stream out namespace
members in declaration order.  This change mysteriously fixes
mysterious bugs.

Add initial support for type merging.  This support includes
references to merge keys.  It modifies the key hash to include the
tree code, because type decls and types otherwise have the same
hash.  Types do not appear on a chain, so merging into a null chain is
avoided.  Type merging is off at the moment to test namespaces.

Handle unnamed decls by using the location instead of the mangled name
in creating the hash string.

Change the pph_trace_tree function from a set of bool parameters to a
single enum pph_trace_kind.  This change captures more information and
avoids printing trees before their contents are streamed in.  Change
the call sites to uniformly use a postorder traversal for tracing.
This makes in and out traces directly comparable.

Bootstrapped on x64.



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

Patch

Index: gcc/cp/ChangeLog.pph

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

	* pph.c (pph_dump_tree_name): Remove dead code.  Dump tree_code also.
	* pph-streamer.h (enum pph_trace_kind): New.
	(pph_trace_tree): Change bool parameters to a single enum parameter.
	Update callers to match.
	(pph_tree_is_mergeable): All decls and types are mergeable.
	* pph-streamer.c (pph_trace_tree): Avoid printing names for unmerge
	keys, as they are too sparse to print.  Change bool parameters to a
	single enum parameter.  Update body to match.
	* pph-streamer-out.c (pph_out_start_merge_key_record): Handle reference
	merge keys.  Return status.
	(pph_out_merge_body_vec): New.
	(pph_out_merge_body_chain): New.
	(pph_out_merge_keys): Replace with general binding routines below.
	(pph_out_binding_merge_keys): New.
	(pph_out_binding_merge_bodies): New.
	(pph_out_global_binding): Use the above.
	(pph_merge_name): Handle types as well as decls.  Handle unnamed decls.
	Handle namespaces.  Add disabled handling of types.
	(pph_out_tree): Move tracing to postorder traversal to match tracing of
	input streaming.
	* pph-streamer-in.c (htab_merge_key_hash): Hash in tree code.
	(pph_merge_into_chain): Do not merge into null chains.
	(pph_in_binding_level): Split ALLOC_AND_REGISTER into constituents
	for future registration of previously allocated binding level.
	(pph_in_merge_keys): Replace with general binding routines below.
	(pph_in_binding_merge_keys): New.
	(pph_in_binding_merge_bodies): New.
	(pph_in_global_binding): Use the above.
	(pph_in_lang_specific): Avoid reallocating DECL_LANG_SPECIFIC.
	(pph_in_merge_key_tree): Reformat comment.  Handle null and reference
	markers, which may be needed for types.  Add handling of namespaces.
	Add disabled handling of classes and types.


Index: gcc/cp/pph.c
===================================================================
--- gcc/cp/pph.c	(revision 180550)
+++ gcc/cp/pph.c	(working copy)
@@ -78,34 +78,20 @@  pph_dump_min_decl (FILE *file, tree decl
 void
 pph_dump_tree_name (FILE *file, tree t, int flags)
 {
-#if 0
   enum tree_code code = TREE_CODE (t);
-  fprintf (file, "%s\t", pph_tree_code_text (code));
-  if (code == FUNCTION_TYPE || code == METHOD_TYPE)
-    {
-      dump_function_to_file (t, file, flags);
-    }
-  else
-    {
-      print_generic_expr (file, TREE_TYPE (t), flags);
-      /* FIXME pph: fprintf (file, " ", cxx_printable_name (t, 0)); */
-      fprintf (file, " " );
-      print_generic_expr (file, t, flags);
-    }
-  fprintf (file, "\n");
-#else
+  const char *text = pph_tree_code_text (code);
   if (DECL_P (t))
-    fprintf (file, "%s\n", decl_as_string (t, flags));
+    fprintf (file, "%s %s\n", text, decl_as_string (t, flags));
   else if (TYPE_P (t))
-    fprintf (file, "%s\n", type_as_string (t, flags));
+    fprintf (file, "%s %s\n", text, type_as_string (t, flags));
   else if (EXPR_P (t))
-    fprintf (file, "%s\n", expr_as_string (t, flags));
+    fprintf (file, "%s %s\n", text, expr_as_string (t, flags));
   else
     {
+      fprintf (file, "%s ", text );
       print_generic_expr (file, t, flags);
       fprintf (file, "\n");
     }
-#endif
 }
 
 
Index: gcc/cp/pph-streamer-in.c
===================================================================
--- gcc/cp/pph-streamer-in.c	(revision 180550)
+++ gcc/cp/pph-streamer-in.c	(working copy)
@@ -750,7 +750,8 @@  htab_merge_key_hash (const void *p)
   const merge_toc_entry *key = (const merge_toc_entry *) p;
   hashval_t context_val = htab_hash_pointer (key->context);
   hashval_t name_val = htab_hash_string (key->name);
-  return iterative_hash_hashval_t (context_val, name_val);
+  hashval_t id_val = iterative_hash_hashval_t (name_val, TREE_CODE (key->expr));
+  return iterative_hash_hashval_t (context_val, id_val);
 }
 
 static int
@@ -832,7 +833,11 @@  pph_merge_into_chain (tree expr, const c
       if (flag_pph_debug >= 3)
         fprintf (pph_logfile, "PPH: %s NOT found on chain\n", name);
 
-      return pph_prepend_to_chain (expr, chain);
+      /* Types (as opposed to type decls) are not on a chain.  */
+      if (chain)
+        return pph_prepend_to_chain (expr, chain);
+      else
+        return expr;
     }
 
   if (flag_pph_debug >= 3)
@@ -1017,8 +1022,8 @@  pph_in_binding_level (pph_stream *stream
           pph_cache_find (stream, marker, image_ix, ix, PPH_cp_binding_level);
     }
 
-  ALLOC_AND_REGISTER (&stream->cache, ix, PPH_cp_binding_level, bl,
-		      ggc_alloc_cleared_cp_binding_level ());
+  bl = ggc_alloc_cleared_cp_binding_level ();
+  pph_cache_insert_at (&stream->cache, bl, ix, PPH_cp_binding_level);
 
   bl->names = pph_in_chain (stream);
   bl->namespaces = pph_in_chain (stream);
@@ -1030,6 +1035,31 @@  pph_in_binding_level (pph_stream *stream
 }
 
 
+/* Read all the merge keys from STREAM into the cp_binding_level BL.  */
+
+static void
+pph_in_binding_merge_keys (pph_stream *stream, cp_binding_level *bl)
+{
+  /* Read all the merge keys and merge into the bindings.  */
+  pph_in_merge_key_chain (stream, &bl->names);
+  pph_in_merge_key_chain (stream, &bl->namespaces);
+  pph_in_merge_key_chain (stream, &bl->usings);
+  pph_in_merge_key_chain (stream, &bl->using_directives);
+}
+
+
+/* Read all the merge bodies from STREAM into the cp_binding_level BL.  */
+
+static void
+pph_in_binding_merge_bodies (pph_stream *stream)
+{
+  pph_in_merge_body_chain (stream);
+  pph_in_merge_body_chain (stream);
+  pph_in_merge_body_chain (stream);
+  pph_in_merge_body_chain (stream);
+}
+
+
 /********************************************************** tree aux classes */
 
 
@@ -1307,9 +1337,14 @@  pph_in_lang_specific (pph_stream *stream
       return;
     }
 
-  /* Allocate a lang_decl structure for DECL.  */
-  retrofit_lang_decl (decl);
+  /* Allocate a lang_decl structure for DECL, if not already present.
+     Namespace merge keys preallocate it.  */
   ld = DECL_LANG_SPECIFIC (decl);
+  if (!ld)
+    {
+      retrofit_lang_decl (decl);
+      ld = DECL_LANG_SPECIFIC (decl);
+    }
 
   /* Now register it.  We would normally use ALLOC_AND_REGISTER,
      but retrofit_lang_decl does not return a pointer.  */
@@ -1875,9 +1910,8 @@  pph_in_tree_header (pph_stream *stream, 
 }
 
 
-/* Read a merge key from STREAM.  If the merge key read from
-   STREAM is not found in *CHAIN, the newly allocated tree is added to
-   it.  */
+/* Read a merge key from STREAM.  If the merge key read from STREAM
+   is not found in *CHAIN, the newly allocated tree is added to it.  */
 
 static tree
 pph_in_merge_key_tree (pph_stream *stream, tree *chain)
@@ -1890,12 +1924,18 @@  pph_in_merge_key_tree (pph_stream *strea
   const char *name;
 
   marker = pph_in_start_record (stream, &image_ix, &ix, PPH_any_tree);
+  if (marker == PPH_RECORD_END)
+    return NULL;
+  else if (pph_is_reference_marker (marker))
+    return (tree) pph_cache_find (stream, marker, image_ix, ix, PPH_any_tree);
   gcc_assert (marker == PPH_RECORD_START_MERGE_KEY);
+
   tag = streamer_read_record_start (ib);
 
   /* Materialize a new node from STREAM.  This will also read all the
      language-independent bitfields for the new tree.  */
   read_expr = pph_in_tree_header (stream, tag);
+  gcc_assert (pph_tree_is_mergeable (read_expr));
   name = pph_in_string (stream);
 
   /* Look for a match in CHAIN to READ_EXPR's header.  If we found a
@@ -1907,8 +1947,36 @@  pph_in_merge_key_tree (pph_stream *strea
 
   pph_cache_insert_at (&stream->cache, expr, ix, pph_tree_code_to_tag (expr));
 
+  if (DECL_P (expr))
+    {
+      if (TREE_CODE (expr) == NAMESPACE_DECL)
+        {
+	  /* struct lang_decl *ld; */
+          retrofit_lang_decl (expr);
+	  /* ld = DECL_LANG_SPECIFIC (expr); */
+	  /* FIXME NOW: allocate binding.  */
+          pph_in_binding_merge_keys (stream, NAMESPACE_LEVEL (expr));
+        }
+#if 0
+/* FIXME pph: Disable type merging for the moment.  */
+      else if (TREE_CODE (expr) == TYPE_DECL)
+        /* Types are not on a chain.  */
+        TREE_TYPE (expr) = pph_in_merge_key_tree (stream, NULL);
+    }
+  else if (CLASS_TYPE_P (expr))
+    {
+      pph_in_merge_key_chain (stream, &TYPE_FIELDS (expr));
+      pph_in_merge_key_chain (stream, &TYPE_METHODS (expr));
+      /*FIXME pph: Nested types are broken.
+      pph_in_binding_table (stream, &CLASSTYPE_NESTED_UTDS (expr));
+      pph_in_merge_key_chain (stream, &CLASSTYPE_DECL_LIST (expr));
+      */
+#endif
+    }
+
   if (flag_pph_tracer)
-    pph_trace_tree (expr, true, expr != read_expr);
+    pph_trace_tree (expr, expr == read_expr ? pph_trace_unmerged_key
+					    : pph_trace_merged_key);
 
   return expr;
 }
@@ -2007,7 +2075,10 @@  pph_in_tree (pph_stream *stream)
     TREE_CHAIN (expr) = saved_expr_chain;
 
   if (flag_pph_tracer)
-    pph_trace_tree (expr, false, false);
+    pph_trace_tree (expr,
+	marker == PPH_RECORD_START_MERGE_BODY ? pph_trace_merge_body
+	: marker == PPH_RECORD_START_MUTATED ? pph_trace_mutate
+	: pph_trace_normal );
 
   /* If needed, sign the recently materialized tree to detect
      mutations.  Note that we only need to compute signatures
@@ -2313,28 +2384,6 @@  pph_in_identifiers (pph_stream *stream, 
 }
 
 
-/* Read all the merge keys from STREAM.  Merge into the corresponding
-   contexts.  Return a VEC of all the merge keys read.  */
-
-static void
-pph_in_merge_keys (pph_stream *stream)
-{
-  cp_binding_level *bl = scope_chain->bindings;
-
-  /* First read all the merge keys and merge into the global bindings.  */
-  pph_in_merge_key_chain (stream, &bl->names);
-  pph_in_merge_key_chain (stream, &bl->namespaces);
-  pph_in_merge_key_chain (stream, &bl->usings);
-  pph_in_merge_key_chain (stream, &bl->using_directives);
-
-  /* Now read the bodies of all the trees merged above.  */
-  pph_in_merge_body_chain (stream);
-  pph_in_merge_body_chain (stream);
-  pph_in_merge_body_chain (stream);
-  pph_in_merge_body_chain (stream);
-}
-
-
 /* Read global bindings from STREAM into scope_chain->bindings.  Note
    that this does not call pph_in_binding_level because that would
    overwrite the fields merged by pph_in_merge_keys.  */
@@ -2373,8 +2422,10 @@  pph_in_global_binding (pph_stream *strea
      context.  Since we have registered scope_chain->bindings in the
      same slot IX that the writer used, the trees read now will be
      bound to scope_chain->bindings.  */
-  pph_in_merge_keys (stream);
+  pph_in_binding_merge_keys (stream, bl);
+  pph_in_binding_merge_bodies (stream);
 
+  /* FIXME pph: Are we sure this is right?  */
   pph_in_binding_level_1 (stream, bl);
 }
 
Index: gcc/cp/pph-streamer.c
===================================================================
--- gcc/cp/pph-streamer.c	(revision 180550)
+++ gcc/cp/pph-streamer.c	(working copy)
@@ -338,26 +338,66 @@  pph_add_include (pph_stream *stream, pph
 /* Print tracing information for a possibly MERGEABLE tree T.  */
 
 void
-pph_trace_tree (tree t, bool mergeable, bool merged)
+pph_trace_tree (tree t, enum pph_trace_kind kind)
 {
+  char kind_char, decl_char;
+  bool is_merge, is_decl;
   bool emit = false;
-  char merging = merged ? '#' : mergeable ? '*' : '.';
-  bool is_decl = DECL_P (t);
-  char userdef =  is_decl ? '*' : '.';
 
-  if (mergeable && is_decl && flag_pph_tracer >= 2)
+  switch (kind)
+    {
+      case pph_trace_key_out:
+	kind_char = 'K';
+	is_merge = true;
+	break;
+      case pph_trace_unmerged_key:
+	kind_char = 'U';
+	is_merge = true;
+	break;
+      case pph_trace_merged_key:
+	kind_char = 'M';
+	is_merge = true;
+	break;
+      case pph_trace_merge_body:
+	kind_char = 'B';
+	is_merge = true;
+	break;
+      case pph_trace_mutate:
+	kind_char = '=';
+	is_merge = false;
+	break;
+      case pph_trace_normal:
+	kind_char = '.';
+	is_merge = false;
+	break;
+      default:
+	kind_char = '?';
+	is_merge = false;
+    }
+
+  is_decl = DECL_P (t);
+  if (is_decl)
+    decl_char = 'D';
+  else if (TYPE_P (t))
+    decl_char = 'T';
+  else
+    decl_char = '.';
+
+  if (is_merge && is_decl && flag_pph_tracer >= 2)
     emit = true;
-  else if ((mergeable || is_decl) && flag_pph_tracer >= 3)
+  else if ((is_merge || is_decl) && flag_pph_tracer >= 3)
     emit = true;
   else if (!EXPR_P (t) && flag_pph_tracer >= 4)
     emit = true;
 
   if (emit)
     {
-      enum tree_code code = TREE_CODE (t);
-      fprintf (pph_logfile, "PPH: %c%c ", merging, userdef);
-      fprintf (pph_logfile, "%-19s ", pph_tree_code_text (code));
-      pph_dump_tree_name (pph_logfile, t, 0);
+      fprintf (pph_logfile, "PPH: %c%c ", kind_char, decl_char);
+      if (kind == pph_trace_unmerged_key)
+	fprintf (pph_logfile, "%s -------\n",
+			      pph_tree_code_text (TREE_CODE (t)));
+      else
+        pph_dump_tree_name (pph_logfile, t, 0);
     }
 }
 
Index: gcc/cp/pph-streamer-out.c
===================================================================
--- gcc/cp/pph-streamer-out.c	(revision 180550)
+++ gcc/cp/pph-streamer-out.c	(working copy)
@@ -635,16 +635,21 @@  pph_out_start_tree_record (pph_stream *s
 
 /* Start a merge key record for EXPR on STREAM.  */
 
-static void
+static bool
 pph_out_start_merge_key_record (pph_stream *stream, tree expr)
 {
   enum pph_tag tag;
   enum pph_record_marker marker;
   pph_cache_entry *e;
-  unsigned ix;
+  unsigned include_ix, ix;
 
   tag = pph_tree_code_to_tag (expr);
-  marker = pph_get_marker_for (stream, expr, NULL, NULL, tag);
+  marker = pph_get_marker_for (stream, expr, &include_ix, &ix, tag);
+  if (marker == PPH_RECORD_END || pph_is_reference_marker (marker))
+    {
+      pph_out_reference_record (stream, marker, include_ix, ix, tag);
+      return true;
+    }
 
   /* This should be the first time that we try to write EXPR.  */
   gcc_assert (marker == PPH_RECORD_START);
@@ -653,6 +658,7 @@  pph_out_start_merge_key_record (pph_stre
   e = pph_cache_add (&stream->cache, expr, &ix, tag);
   e->needs_merge_body = true;
   pph_out_uint (stream, ix);
+  return false;
 }
 
 
@@ -879,6 +885,20 @@  pph_out_merge_key_vec (pph_stream *strea
 }
 
 
+/* Write all the merge bodies for trees in VEC V to STREAM.  The bodies
+   should/must go out in declaration order, i.e. reversed.  */
+
+static void
+pph_out_merge_body_vec (pph_stream *stream, VEC(tree,gc) *v)
+{
+  unsigned i;
+  tree t;
+  pph_out_hwi (stream, VEC_length (tree, v));
+  FOR_EACH_VEC_ELT_REVERSE (tree, v, i, t)
+    pph_out_tree (stream, t);
+}
+
+
 /* Write all the trees in VEC V that match FILTER to STREAM.  */
 
 static void
@@ -1012,6 +1032,23 @@  pph_out_merge_key_chain (pph_stream *str
 }
 
 
+/* Write, in reverse, a chain of merge bodies to STREAM starting
+   with the last element of CHAIN.  Only write the trees that match
+   FILTER.  */
+
+static void
+pph_out_merge_body_chain (pph_stream *stream, tree chain, unsigned filter)
+{
+  VEC(tree,heap) *w;
+  if (filter == PPHF_NONE)
+    w = chain2vec (chain);
+  else
+    w = chain2vec_filter (stream, chain, filter);
+  pph_out_merge_body_vec (stream, (VEC(tree,gc) *)w);
+  VEC_free (tree, heap, w);
+}
+
+
 /****************************************************************** bindings */
 
 
@@ -1145,6 +1182,32 @@  pph_out_binding_level (pph_stream *strea
 }
 
 
+/* Write an index of mergeable objects from cp_binding_level BL to STREAM.  */
+
+static void
+pph_out_binding_merge_keys (pph_stream *stream, cp_binding_level *bl)
+{
+  unsigned filter = PPHF_NO_XREFS | PPHF_NO_PREFS | PPHF_NO_BUILTINS;
+  pph_out_merge_key_chain (stream, bl->names, filter);
+  pph_out_merge_key_chain (stream, bl->namespaces, filter);
+  pph_out_merge_key_chain (stream, bl->usings, filter);
+  pph_out_merge_key_chain (stream, bl->using_directives, filter);
+}
+
+
+/* Write bodies of mergeable objects from cp_binding_level BL to STREAM.  */
+
+static void
+pph_out_binding_merge_bodies (pph_stream *stream, cp_binding_level *bl)
+{
+  unsigned filter = PPHF_NO_XREFS | PPHF_NO_PREFS | PPHF_NO_BUILTINS;
+  pph_out_merge_body_chain (stream, bl->names, filter);
+  pph_out_merge_body_chain (stream, bl->namespaces, filter);
+  pph_out_merge_body_chain (stream, bl->usings, filter);
+  pph_out_merge_body_chain (stream, bl->using_directives, filter);
+}
+
+
 /********************************************************** tree aux classes */
 
 
@@ -1908,27 +1971,43 @@  pph_merge_name (tree expr)
   char *retval;
   size_t len;
   const char *mangled_str, *name_str;
+  tree name;
+
   unsigned flags = TFF_PLAIN_IDENTIFIER
 		   | TFF_SCOPE
 		   | TFF_DECL_SPECIFIERS
 		   | TFF_CLASS_KEY_OR_ENUM
 		   | TFF_RETURN_TYPE;
 
+  if (TYPE_P (expr))
+    expr = TYPE_NAME (expr);
 
-  if (TREE_CODE (expr) == FUNCTION_DECL)
-    flags |= TFF_RETURN_TYPE
-	     | TFF_FUNCTION_DEFAULT_ARGUMENTS
-	     | TFF_EXCEPTION_SPECIFICATION;
-  else if (TREE_CODE (expr) == TEMPLATE_DECL)
-    flags |= TFF_TEMPLATE_HEADER
-	     | TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS;
-
-  mangled_str = IDENTIFIER_POINTER (get_mangled_id (expr));
   name_str = decl_as_string (expr, flags);
 
-  len = strlen (name_str) + sizeof("|") + strlen (mangled_str);
-  retval = XNEWVEC (char, len + 1);
-  sprintf (retval, "%s|%s", name_str, mangled_str);
+  name = DECL_NAME (expr);
+  if (name)
+    {
+      if (TREE_CODE (expr) == FUNCTION_DECL)
+	flags |= TFF_RETURN_TYPE
+		 | TFF_FUNCTION_DEFAULT_ARGUMENTS
+		 | TFF_EXCEPTION_SPECIFICATION;
+      else if (TREE_CODE (expr) == TEMPLATE_DECL)
+	flags |= TFF_TEMPLATE_HEADER
+		 | TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS;
+      mangled_str = IDENTIFIER_POINTER (get_mangled_id (expr));
+      len = strlen (name_str) + sizeof("|") + strlen (mangled_str);
+      retval = XNEWVEC (char, len + 1);
+      sprintf (retval, "%s|%s", name_str, mangled_str);
+    }
+  else
+    {
+      location_t locus = DECL_SOURCE_LOCATION (expr);
+      expanded_location xloc = expand_location (locus);
+      /* There are at most 20 digits in size_t.  Add :| for 22 characters.  */
+      len = strlen (name_str) + strlen (xloc.file) + 22;
+      retval = XNEWVEC (char, len + 1);
+      sprintf (retval, "%s|%s:%u", name_str, xloc.file, xloc.line);
+    }
 
   return retval;
 }
@@ -1952,16 +2031,37 @@  pph_out_merge_key_tree (pph_stream *stre
 {
   gcc_assert (pph_tree_is_mergeable (expr));
 
-  pph_out_start_merge_key_record (stream, expr);
-
-  if (flag_pph_tracer)
-    pph_trace_tree (expr, true, false);
+  if (pph_out_start_merge_key_record (stream, expr))
+    return;
 
   /* Write merge key information.  This includes EXPR's header (needed
      to re-allocate EXPR in the reader) and the merge key, used to
      lookup EXPR in the reader's context and merge if necessary.  */
   pph_out_tree_header (stream, expr);
   pph_out_merge_name (stream, expr);
+  if (DECL_P (expr))
+    {
+      if (TREE_CODE (expr) == NAMESPACE_DECL)
+        pph_out_binding_merge_keys (stream, NAMESPACE_LEVEL (expr));
+#if 0
+/* FIXME pph: Distable tree merging for the moment.  */
+      else if (TREE_CODE (expr) == TYPE_DECL)
+        pph_out_merge_key_tree (stream, TREE_TYPE (expr));
+    }
+  else if (CLASS_TYPE_P (expr))
+    {
+      unsigned filter = PPHF_NO_XREFS | PPHF_NO_PREFS | PPHF_NO_BUILTINS;
+      pph_out_merge_key_chain (stream, TYPE_FIELDS (expr), filter);
+      pph_out_merge_key_chain (stream, TYPE_METHODS (expr), filter);
+      /* FIXME pph: Nested types are broken.
+      pph_out_binding_table (stream, CLASSTYPE_NESTED_UTDS (expr));
+      pph_out_merge_key_chain (stream, CLASSTYPE_DECL_LIST (expr), filter);
+      */
+#endif
+    }
+
+  if (flag_pph_tracer)
+    pph_trace_tree (expr, pph_trace_key_out);
 }
 
 
@@ -1999,8 +2099,6 @@  pph_out_tree (pph_stream *stream, tree e
     }
   else if (marker == PPH_RECORD_START || marker == PPH_RECORD_START_MUTATED)
     {
-      if (flag_pph_tracer)
-	pph_trace_tree (expr, false, false);
 
       /* This is the first time we see EXPR, write it out.  */
       if (marker == PPH_RECORD_START)
@@ -2018,9 +2116,6 @@  pph_out_tree (pph_stream *stream, tree e
     {
       gcc_assert (pph_tree_is_mergeable (expr));
 
-      if (flag_pph_tracer)
-	pph_trace_tree (expr, true, false);
-
       /* When writing a merge body, we do not need to write EXPR's
 	 header, since that was written out when we wrote the merge
 	 key record for it.  */
@@ -2028,6 +2123,12 @@  pph_out_tree (pph_stream *stream, tree e
     }
   else
     gcc_unreachable ();
+
+  if (flag_pph_tracer)
+    pph_trace_tree (expr,
+	marker == PPH_RECORD_START_MERGE_BODY ? pph_trace_merge_body
+	: marker == PPH_RECORD_START_MUTATED ? pph_trace_mutate
+	: pph_trace_normal );
 }
 
 
@@ -2228,28 +2329,6 @@  pph_out_identifiers (pph_stream *stream,
 }
 
 
-/* Write an index of mergeable objects to STREAM.  */
-
-static void
-pph_out_merge_keys (pph_stream *stream)
-{
-  cp_binding_level *bl = scope_chain->bindings;
-  unsigned filter = PPHF_NO_XREFS | PPHF_NO_PREFS | PPHF_NO_BUILTINS;
-
-  /* First emit all the merge keys.  */
-  pph_out_merge_key_chain (stream, bl->names, filter);
-  pph_out_merge_key_chain (stream, bl->namespaces, filter);
-  pph_out_merge_key_chain (stream, bl->usings, filter);
-  pph_out_merge_key_chain (stream, bl->using_directives, filter);
-
-  /* Now emit all the merge bodies.  */
-  pph_out_chain_filtered (stream, bl->names, filter);
-  pph_out_chain_filtered (stream, bl->namespaces, filter);
-  pph_out_chain_filtered (stream, bl->usings, filter);
-  pph_out_chain_filtered (stream, bl->using_directives, filter);
-}
-
-
 /* Write the global bindings in scope_chain to STREAM.  */
 
 static void
@@ -2287,9 +2366,11 @@  pph_out_global_binding (pph_stream *stre
 
   /* Emit all the merge keys for objects that need to be merged when reading
      multiple PPH images.  */
-  pph_out_merge_keys (stream);
+  pph_out_binding_merge_keys (stream, bl);
+  pph_out_binding_merge_bodies (stream, bl);
 
   /* Emit the other fields in BL that need no merging.  */
+  /* FIXME pph: Are we sure this is right?  */
   pph_out_binding_level_1 (stream, bl,
 			   PPHF_NO_XREFS | PPHF_NO_PREFS | PPHF_NO_BUILTINS);
 }
Index: gcc/cp/pph-streamer.h
===================================================================
--- gcc/cp/pph-streamer.h	(revision 180550)
+++ gcc/cp/pph-streamer.h	(working copy)
@@ -233,6 +233,12 @@  extern void pph_dump_chain (FILE *, tree
 extern void pph_dump_binding (FILE *, cp_binding_level *level);
 extern void pph_dump_namespace (FILE *, tree ns);
 
+enum pph_trace_kind
+{
+  pph_trace_key_out, pph_trace_unmerged_key, pph_trace_merged_key,
+  pph_trace_merge_body, pph_trace_mutate, pph_trace_normal
+};
+
 /* In pph-streamer.c.  */
 void pph_streamer_init (void);
 void pph_streamer_finish (void);
@@ -240,7 +246,7 @@  pph_stream *pph_stream_open (const char 
 void pph_mark_stream_read (pph_stream *);
 void pph_stream_close (pph_stream *);
 void pph_add_include (pph_stream *, pph_stream *);
-void pph_trace_tree (tree, bool, bool);
+void pph_trace_tree (tree, enum pph_trace_kind);
 pph_cache_entry *pph_cache_insert_at (pph_cache *, void *, unsigned,
 				      enum pph_tag);
 pph_cache_entry *pph_cache_lookup (pph_cache *, void *, unsigned *,
@@ -390,12 +396,7 @@  pph_tree_code_to_tag (tree t)
 static inline bool
 pph_tree_is_mergeable (tree expr)
 {
-  return TREE_CODE (expr) == VAR_DECL
-	 || TREE_CODE (expr) == FUNCTION_DECL
-	 || TREE_CODE (expr) == TYPE_DECL
-	 || TREE_CODE (expr) == TEMPLATE_DECL
-	 || TREE_CODE (expr) == NAMESPACE_DECL
-	 || TREE_CODE (expr) == CONST_DECL;
+  return DECL_P (expr) || TYPE_P (expr);
 }
 
 #endif  /* GCC_CP_PPH_STREAMER_H  */