diff mbox

[pph] Partial namespace merging. (issue5341047)

Message ID 20111107215750.8C236222644@jade.mtv.corp.google.com
State New
Headers show

Commit Message

Lawrence Crowl Nov. 7, 2011, 9:57 p.m. UTC
Add merging of namespaces.

Add testcase for namespace merging. test fails because lookup fails
for reasons apparently associated with identifiers.

Add tracing of both front and back of trees so as to better identify
the tree structure.

Add tracing of record markers.

Various formatting fixes.

Tested on x64.



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

Patch

Index: gcc/testsuite/ChangeLog.pph

2011-11-07   Lawrence Crowl  <crowl@google.com>

	* lib/dg-pph.exp (dg-pph-pos): Recognize bogus errors.
	* g++.dg/pph/x0namespace2.h: New.
	* g++.dg/pph/x4namespace.cc: New.

Index: gcc/cp/ChangeLog.pph

2011-11-07   Lawrence Crowl  <crowl@google.com>

	* pph.c (pph_tree_code_text): Fix formatting.
	* pph-streamer.h (enum pph_trace_end): New.
	(pph_trace_tree): Add 'which end' parameter.
	(pph_trace_marker): New.
	* gcc/cp/pph-streamer.c (marker_strings[]): New.
	(tag_strings[]): New.
	(pph_trace_marker): New.
	(pph_trace_tree): Add tree prefix tracing.
	* pph-streamer-out.c: Add section separators.
	(pph_out_stream): Move to stream initialization section.
	(pph_out_record_marker): Add record tracing.
	(chain2vec_filter): Disable assert on 'none' filter.
	(pph_out_chain_filtered): Likewise.
	(pph_out_binding_merge_bodies): Add more merge fodder.
	(pph_out_merge_key_tree): Add tree prefix tracing.
	(pph_out_tree): Likewise.
	* pph-streamer-in.c: Add section separators.
	(ALLOC_AND_REGISTER): Move to record handling section.
	(pph_loc_offset): Move to source information section.
	(pph_in_record_marker): Add record tracing.
	(pph_in_binding_level): Factor into pph_in_binding_level and
	pph_in_binding_level_start.
	(pph_in_binding_merge_bodies): Factor actual merging into separate
	pph_in_binding_merge_bodies_1.
	(pph_in_ld_ns): Merge into a binding if one already exists.
	(pph_in_tree_body): Add debugging code.
	(pph_in_merge_key_tree): Add tree prefix tracing.
	Do not allocate a namespace binding if one exists already.
	(pph_in_tree): Add prefix tracing.
	(pph_in_global_binding): Call pph_in_binding_merge_bodies_1 to avoid
	allocation.


Index: gcc/testsuite/lib/dg-pph.exp
===================================================================
--- gcc/testsuite/lib/dg-pph.exp	(revision 180802)
+++ gcc/testsuite/lib/dg-pph.exp	(working copy)
@@ -143,7 +143,8 @@  proc dg-pph-pos { subdir test options ma
     if { ![file_on_host exists "$bname.s"] } {
 	# Expect assembly to be missing when the compile is an
 	# expected fail.
-	if { ![llength [grep $test "dg-xfail-if.*-fpph-map"]] } {
+	if { ![llength [grep $test "dg-xfail-if.*-fpph-map"]]
+	     && ![llength [grep $test "dg-bogus.*error:"]] } {
 	    fail "$nshort $options (pph assembly missing)"
 	}
 	return
Index: gcc/testsuite/g++.dg/pph/x4namespace.cc
===================================================================
--- gcc/testsuite/g++.dg/pph/x4namespace.cc	(revision 0)
+++ gcc/testsuite/g++.dg/pph/x4namespace.cc	(revision 0)
@@ -0,0 +1,29 @@ 
+// { dg-bogus "x4namespace.cc:11:1: error: 'C' does not name a type" "" { xfail *-*-* } 0 }
+// { dg-bogus "x4namespace.cc:27:5: error: 'z' is not a member of 'A'" "" { xfail *-*-* } 0 }
+
+#include "x0namespace.h"
+#include "x0namespace2.h"
+
+namespace A {
+int x = 3;
+int x2 = 5;
+
+C< int > z;
+C2< int > z2;
+
+} // namespace A
+
+int y = 4;
+int y2 = 6;
+
+int D::method()
+{ return y; }
+
+int D2::method()
+{ return y2; }
+
+int main()
+{
+    A::z.method();
+    A::z2.method();
+}
Index: gcc/testsuite/g++.dg/pph/x0namespace2.h
===================================================================
--- gcc/testsuite/g++.dg/pph/x0namespace2.h	(revision 0)
+++ gcc/testsuite/g++.dg/pph/x0namespace2.h	(revision 0)
@@ -0,0 +1,22 @@ 
+#ifndef X0NAMESPACE2_H
+#define X0NAMESPACE2_H
+namespace A {
+extern int x2;
+struct B2;
+template< typename T >
+struct C2 {
+    T* b;
+    int method();
+    int another()
+    { return *b; }
+};
+template< typename T >
+int C2< T >::method()
+{ return x2; }
+} // namespace A
+struct D2 : A::C2< int > {
+    int method();
+    int another()
+    { return *b; }
+};
+#endif
Index: gcc/cp/pph.c
===================================================================
--- gcc/cp/pph.c	(revision 180802)
+++ gcc/cp/pph.c	(working copy)
@@ -45,7 +45,7 @@  FILE *pph_logfile = NULL;
 
 /* Convert a checked tree_code CODE to a string.  */
 
-const char*
+const char *
 pph_tree_code_text (enum tree_code code)
 {
   gcc_assert (code < MAX_TREE_CODES);
Index: gcc/cp/pph-streamer-in.c
===================================================================
--- gcc/cp/pph-streamer-in.c	(revision 180802)
+++ gcc/cp/pph-streamer-in.c	(working copy)
@@ -35,10 +35,25 @@  along with GCC; see the file COPYING3.  
 #include "parser.h"
 #include "pointer-set.h"
 
+
+/********************************************************* type declarations */
+
+
 typedef char *char_p;
 DEF_VEC_P(char_p);
 DEF_VEC_ALLOC_P(char_p,heap);
 
+
+/****************************************************** forward declarations */
+
+
+/* Forward declarations to avoid circularity.  */
+static tree pph_in_merge_key_tree (pph_stream *, tree *);
+
+
+/***************************************************** stream initialization */
+
+
 /* String tables for all input streams.  These are allocated separately
   from streams because they cannot be deallocated after the streams
   have been read (string streaming works by pointing into these
@@ -48,29 +63,6 @@  DEF_VEC_ALLOC_P(char_p,heap);
   memory will remain allocated until the end of compilation.  */
 static VEC(char_p,heap) *string_tables = NULL;
 
-/* Wrapper for memory allocation calls that should have their results
-   registered in the PPH streamer cache.  DATA is the pointer returned
-   by the memory allocation call in ALLOC_EXPR.  IX is the cache slot 
-   in CACHE where the newly allocated DATA should be registered at.  */
-#define ALLOC_AND_REGISTER(CACHE, IX, TAG, DATA, ALLOC_EXPR)	\
-    do {							\
-      (DATA) = (ALLOC_EXPR);					\
-      pph_cache_insert_at (CACHE, DATA, IX, TAG);		\
-    } while (0)
-
-/* Set in pph_in_and_merge_line_table. Represents the source_location offset
-   which every streamed in token must add to it's serialized source_location.
-
-   FIXME pph: Ideally this would be in pph_stream.encoder.r, but for that we
-   first need to get rid of the dependency to the streamer_hook for locations.
-   */
-static int pph_loc_offset;
-
-/* Forward declarations to avoid circularity.  */
-static tree pph_in_merge_key_tree (pph_stream *, tree *);
-
-/***************************************************** stream initialization */
-
 
 /* Read into memory the contents of the file in STREAM.  Initialize
    internal tables and data structures needed to re-construct the
@@ -210,6 +202,15 @@  pph_in_bitpack (pph_stream *stream)
 /******************************************************** source information */
 
 
+/* Set in pph_in_and_merge_line_table. Represents the source_location offset
+   which every streamed in token must add to it's serialized source_location.
+
+   FIXME pph: Ideally this would be in pph_stream.encoder.r, but for that we
+   first need to get rid of the dependency to the streamer_hook for locations.
+   */
+static int pph_loc_offset;
+
+
 /* Read a linenum_type from STREAM.  */
 
 static inline linenum_type
@@ -412,6 +413,17 @@  pph_in_line_table_and_includes (pph_stre
 /*********************************************************** record handling */
 
 
+/* Wrapper for memory allocation calls that should have their results
+   registered in the PPH streamer cache.  DATA is the pointer returned
+   by the memory allocation call in ALLOC_EXPR.  IX is the cache slot 
+   in CACHE where the newly allocated DATA should be registered at.  */
+#define ALLOC_AND_REGISTER(CACHE, IX, TAG, DATA, ALLOC_EXPR)	\
+    do {							\
+      (DATA) = (ALLOC_EXPR);					\
+      pph_cache_insert_at (CACHE, DATA, IX, TAG);		\
+    } while (0)
+
+
 /* Read and return a record marker from STREAM.  On return, *TAG_P will
    contain the tag for the data type stored in this record.  */
 enum pph_record_marker
@@ -431,6 +443,9 @@  pph_in_record_marker (pph_stream *stream
   *tag_p = (enum pph_tag) pph_in_uint (stream);
   gcc_assert ((unsigned) *tag_p < (unsigned) PPH_NUM_TAGS);
 
+  if (flag_pph_tracer >= 5)
+    pph_trace_marker (m, *tag_p);
+
   return m;
 }
 
@@ -1060,26 +1075,42 @@  pph_in_binding_level_1 (pph_stream *stre
 }
 
 
-/* Read and return an instance of cp_binding_level from STREAM.
-   This function is for use when we will not be merging the binding level.  */
+/* Read the start of a binding level.  Return true when processing is done.  */
 
-static cp_binding_level *
-pph_in_binding_level (pph_stream *stream)
+static bool
+pph_in_binding_level_start (pph_stream *stream, cp_binding_level **blp,
+			    unsigned *ixp)
 {
-  unsigned image_ix, ix;
-  cp_binding_level *bl;
+  unsigned image_ix;
   enum pph_record_marker marker;
 
-  marker = pph_in_start_record (stream, &image_ix, &ix, PPH_cp_binding_level);
+  marker = pph_in_start_record (stream, &image_ix, ixp, PPH_cp_binding_level);
   if (marker == PPH_RECORD_END)
     {
-      return NULL;
+      *blp = NULL;
+      return true;
     }
   else if (pph_is_reference_marker (marker))
     {
-      return (cp_binding_level *)
-          pph_cache_find (stream, marker, image_ix, ix, PPH_cp_binding_level);
+      *blp = (cp_binding_level *)
+          pph_cache_find (stream, marker, image_ix, *ixp, PPH_cp_binding_level);
+      return true;
     }
+  return false;
+}
+
+
+/* Read and return an instance of cp_binding_level from STREAM.
+   This function is for use when we will not be merging the binding level.  */
+
+static cp_binding_level *
+pph_in_binding_level (pph_stream *stream)
+{
+  unsigned ix;
+  cp_binding_level *bl;
+
+  if (pph_in_binding_level_start (stream, &bl, &ix))
+    return bl;
 
   bl = ggc_alloc_cleared_cp_binding_level ();
   pph_cache_insert_at (&stream->cache, bl, ix, PPH_cp_binding_level);
@@ -1111,13 +1142,35 @@  pph_in_binding_merge_keys (pph_stream *s
 /* Read all the merge bodies from STREAM into the cp_binding_level BL.  */
 
 static void
-pph_in_binding_merge_bodies (pph_stream *stream, cp_binding_level *bl)
+pph_in_binding_merge_bodies_1 (pph_stream *stream, cp_binding_level *bl)
 {
   pph_in_merge_body_chain (stream);
   pph_in_merge_body_chain (stream);
   pph_in_merge_body_chain (stream);
   pph_in_merge_body_chain (stream);
   pph_union_into_tree_vec (&bl->static_decls, pph_in_tree_vec (stream));
+  /* FIXME pph: The following is probably too aggressive in overwriting.  */
+  pph_in_binding_level_1 (stream, bl);
+}
+
+
+/* Read all the merge bodies from STREAM into the cp_binding_level BL.  */
+
+static void
+pph_in_binding_merge_bodies (pph_stream *stream, cp_binding_level *bl)
+{
+  unsigned ix;
+  cp_binding_level *new_bl;
+
+  if (pph_in_binding_level_start (stream, &new_bl, &ix))
+    {
+      gcc_assert (new_bl == bl);
+      return;
+    }
+
+  /* The binding level is already allocated.  */
+  pph_cache_insert_at (&stream->cache, bl, ix, PPH_cp_binding_level);
+  pph_in_binding_merge_bodies_1 (stream, bl);
 }
 
 
@@ -1363,7 +1416,10 @@  pph_in_ld_fn (pph_stream *stream, struct
 static void
 pph_in_ld_ns (pph_stream *stream, struct lang_decl_ns *ldns)
 {
-  ldns->level = pph_in_binding_level (stream);
+  if (ldns->level == NULL)
+    ldns->level = pph_in_binding_level (stream);
+  else
+    pph_in_binding_merge_bodies (stream, ldns->level);
 }
 
 
@@ -1797,6 +1853,8 @@  pph_in_tree_body (pph_stream *stream, tr
       break;
 
     case IDENTIFIER_NODE:
+      if (flag_pph_debug >= 3)
+	fprintf (pph_logfile, "in identifier %s\n", IDENTIFIER_POINTER (expr));
       IDENTIFIER_NAMESPACE_BINDINGS (expr) = pph_in_cxx_binding (stream);
       IDENTIFIER_BINDING (expr) = pph_in_cxx_binding (stream);
       IDENTIFIER_TEMPLATE (expr) = pph_in_tree (stream);
@@ -2008,14 +2066,26 @@  pph_in_merge_key_tree (pph_stream *strea
 
   pph_cache_insert_at (&stream->cache, expr, ix, pph_tree_code_to_tag (expr));
 
+  if (flag_pph_tracer)
+    pph_trace_tree (expr, pph_trace_front,
+		    expr == read_expr ? pph_trace_unmerged_key
+				      : pph_trace_merged_key);
+
   if (DECL_P (expr))
     {
       if (TREE_CODE (expr) == NAMESPACE_DECL)
         {
 	  cp_binding_level *bl;
-	  retrofit_lang_decl (expr);
-	  bl = ggc_alloc_cleared_cp_binding_level ();
-	  NAMESPACE_LEVEL (expr) = bl;
+	  if (DECL_LANG_SPECIFIC (expr))
+	    /* Merging into an existing namespace.  */
+	    bl = NAMESPACE_LEVEL (expr);
+	  else
+	    {
+	      /* This is a new namespace.  */
+	      retrofit_lang_decl (expr);
+	      bl = ggc_alloc_cleared_cp_binding_level ();
+	      NAMESPACE_LEVEL (expr) = bl;
+	    }
 	  pph_in_binding_merge_keys (stream, bl);
         }
 #if 0
@@ -2036,8 +2106,9 @@  pph_in_merge_key_tree (pph_stream *strea
     }
 
   if (flag_pph_tracer)
-    pph_trace_tree (expr, expr == read_expr ? pph_trace_unmerged_key
-					    : pph_trace_merged_key);
+    pph_trace_tree (expr, pph_trace_back,
+		    expr == read_expr ? pph_trace_unmerged_key
+				      : pph_trace_merged_key);
 
   return expr;
 }
@@ -2122,6 +2193,12 @@  pph_in_tree (pph_stream *stream)
   if (marker != PPH_RECORD_START_MERGE_BODY)
     pph_cache_insert_at (&stream->cache, expr, ix, pph_tree_code_to_tag (expr));
 
+  if (flag_pph_tracer)
+    pph_trace_tree (expr, pph_trace_front,
+	marker == PPH_RECORD_START_MERGE_BODY ? pph_trace_merge_body
+	: marker == PPH_RECORD_START_MUTATED ? pph_trace_mutate
+	: pph_trace_normal );
+
   /* If we are reading a merge body, it means that EXPR is already in
      some chain.  Given that EXPR may now be in a different location
      in the chain, we need to make sure we do not lose it.  */
@@ -2136,7 +2213,7 @@  pph_in_tree (pph_stream *stream)
     TREE_CHAIN (expr) = saved_expr_chain;
 
   if (flag_pph_tracer)
-    pph_trace_tree (expr,
+    pph_trace_tree (expr, pph_trace_back,
 	marker == PPH_RECORD_START_MERGE_BODY ? pph_trace_merge_body
 	: marker == PPH_RECORD_START_MUTATED ? pph_trace_mutate
 	: pph_trace_normal );
@@ -2499,10 +2576,7 @@  pph_in_global_binding (pph_stream *strea
      same slot IX that the writer used, the trees read now will be
      bound to scope_chain->bindings.  */
   pph_in_binding_merge_keys (stream, bl);
-  pph_in_binding_merge_bodies (stream, bl);
-
-  /* FIXME pph: Are we sure this is right?  */
-  pph_in_binding_level_1 (stream, bl);
+  pph_in_binding_merge_bodies_1 (stream, bl);
 }
 
 
@@ -2595,6 +2669,9 @@  pph_read_file (const char *filename)
 }
 
 
+/********************************************************* stream operations */
+
+
 /* Initialize the reader.  */
 
 void
Index: gcc/cp/pph-streamer.c
===================================================================
--- gcc/cp/pph-streamer.c	(revision 180802)
+++ gcc/cp/pph-streamer.c	(working copy)
@@ -335,12 +335,64 @@  pph_add_include (pph_stream *stream, pph
 }
 
 
+/* Trace a record MARKER and TAG.  */
+
+static const char *marker_strings[] =
+{
+  "PPH_RECORD_START",
+  "PPH_RECORD_START_NO_CACHE",
+  "PPH_RECORD_START_MUTATED",
+  "PPH_RECORD_START_MERGE_KEY",
+  "PPH_RECORD_START_MERGE_BODY",
+  "PPH_RECORD_END",
+  "PPH_RECORD_IREF",
+  "PPH_RECORD_XREF",
+  "PPH_RECORD_PREF"
+};
+
+static const char *tag_strings[] =
+{
+  "PPH_any_tree",
+  "PPH_binding_entry",
+  "PPH_binding_table",
+  "PPH_cgraph_node",
+  "PPH_cp_binding_level",
+  "PPH_cp_class_binding",
+  "PPH_cp_label_binding",
+  "PPH_cxx_binding",
+  "PPH_function",
+  "PPH_lang_decl",
+  "PPH_lang_type",
+  "PPH_language_function",
+  "PPH_sorted_fields_type"
+};
+
+void
+pph_trace_marker (enum pph_record_marker marker, enum pph_tag tag)
+{
+  fprintf (pph_logfile, "marker ");
+  if (PPH_RECORD_START <= marker && marker <= PPH_RECORD_PREF)
+    fprintf (pph_logfile, "%s", marker_strings[marker - PPH_RECORD_START]);
+  else
+    fprintf (pph_logfile, "unknown");
+  fprintf (pph_logfile, " tag ");
+  if (tag == PPH_null)
+    fprintf (pph_logfile, "PPH_null\n");
+  else if (tag < PPH_any_tree)
+    fprintf (pph_logfile, "%s\n", tree_code_name[tag]);
+  else if (tag < PPH_NUM_TAGS)
+    fprintf (pph_logfile, "%s\n", tag_strings[tag - PPH_any_tree]);
+  else
+    fprintf (pph_logfile, "unknown\n");
+}
+
+
 /* Print tracing information for a possibly MERGEABLE tree T.  */
 
 void
-pph_trace_tree (tree t, enum pph_trace_kind kind)
+pph_trace_tree (tree t, enum pph_trace_end end, enum pph_trace_kind kind)
 {
-  char kind_char, decl_char;
+  char end_char, kind_char, decl_char;
   bool is_merge, is_decl;
   bool emit = false;
 
@@ -375,6 +427,8 @@  pph_trace_tree (tree t, enum pph_trace_k
 	is_merge = false;
     }
 
+  end_char = end == pph_trace_front ? '{' : '}';
+
   is_decl = DECL_P (t);
   if (is_decl)
     decl_char = 'D';
@@ -392,8 +446,8 @@  pph_trace_tree (tree t, enum pph_trace_k
 
   if (emit)
     {
-      fprintf (pph_logfile, "PPH: %c%c ", kind_char, decl_char);
-      if (kind == pph_trace_unmerged_key)
+      fprintf (pph_logfile, "PPH: %c%c%c ", end_char, kind_char, decl_char);
+      if (kind == pph_trace_unmerged_key || end == pph_trace_front)
 	fprintf (pph_logfile, "%s -------\n",
 			      pph_tree_code_text (TREE_CODE (t)));
       else
Index: gcc/cp/pph-streamer-out.c
===================================================================
--- gcc/cp/pph-streamer-out.c	(revision 180802)
+++ gcc/cp/pph-streamer-out.c	(working copy)
@@ -34,18 +34,24 @@  along with GCC; see the file COPYING3.  
 #include "cgraph.h"
 #include "parser.h"
 
-/* PPH stream that we are currently generating.  FIXME pph, this
-   global is needed because we call back from various parts of the
-   compiler that do not know about PPH (e.g., some LTO callbacks,
-   cp_rest_of_decl_compilation).  */
-static pph_stream *pph_out_stream = NULL;
+
+/****************************************************** forward declarations */
+
 
 /* Forward declarations to avoid circular references.  */
 static void pph_out_merge_key_tree (pph_stream *, tree);
 
+
 /***************************************************** stream initialization */
 
 
+/* PPH stream that we are currently generating.  FIXME pph, this
+   global is needed because we call back from various parts of the
+   compiler that do not know about PPH (e.g., some LTO callbacks,
+   cp_rest_of_decl_compilation).  */
+static pph_stream *pph_out_stream = NULL;
+
+
 /* Initialize buffers and tables in STREAM for writing.  */
 
 void
@@ -417,6 +423,9 @@  pph_out_record_marker (pph_stream *strea
 
   gcc_assert (tag == (enum pph_tag)(unsigned) tag);
   pph_out_uint (stream, tag);
+
+  if (flag_pph_tracer >= 5)
+    pph_trace_marker (marker, tag);
 }
 
 
@@ -589,6 +598,7 @@  pph_out_start_tree_record (pph_stream *s
         marker = PPH_RECORD_START_NO_CACHE;
 
       pph_out_record_marker (stream, marker, tag);
+
       if (marker == PPH_RECORD_START || marker == PPH_RECORD_START_MERGE_BODY)
         {
           unsigned ix;
@@ -645,6 +655,7 @@  pph_out_start_merge_key_record (pph_stre
 
   tag = pph_tree_code_to_tag (expr);
   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);
@@ -964,7 +975,7 @@  chain2vec_filter (pph_stream *stream, tr
   /* Do not accept the nil filter.  The caller is responsible for
      freeing the returned vector and they may inadvertently free
      a vector they assumed to be allocated by this function.  */
-  gcc_assert (filter != PPHF_NONE);
+  /* FIXME crowl: gcc_assert (filter != PPHF_NONE); */
 
   for (t = chain; t; t = TREE_CHAIN (t))
     if (pph_tree_matches (stream, t, filter))
@@ -1004,14 +1015,9 @@  pph_out_chain (pph_stream *stream, tree 
 static void
 pph_out_chain_filtered (pph_stream *stream, tree first, unsigned filter)
 {
-  if (filter == PPHF_NONE)
-    pph_out_chain (stream, first);
-  else
-    {
-      VEC(tree,heap) *w = chain2vec_filter (stream, first, filter);
-      pph_out_tree_vec_unchain (stream, (VEC(tree,gc) *)w);
-      VEC_free (tree, heap, w);
-    }
+  VEC(tree,heap) *w = chain2vec_filter (stream, first, filter);
+  pph_out_tree_vec_unchain (stream, (VEC(tree,gc) *)w);
+  VEC_free (tree, heap, w);
 }
 
 
@@ -1205,6 +1211,7 @@  pph_out_binding_merge_bodies (pph_stream
   pph_out_merge_body_chain (stream, bl->usings, filter);
   pph_out_merge_body_chain (stream, bl->using_directives, filter);
   pph_out_tree_vec_filtered (stream, bl->static_decls, filter);
+  pph_out_binding_level_1 (stream, bl, filter);
 }
 
 
@@ -2034,6 +2041,9 @@  pph_out_merge_key_tree (pph_stream *stre
   if (pph_out_start_merge_key_record (stream, expr))
     return;
 
+  if (flag_pph_tracer)
+    pph_trace_tree (expr, pph_trace_front, pph_trace_key_out);
+
   /* 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.  */
@@ -2061,7 +2071,7 @@  pph_out_merge_key_tree (pph_stream *stre
     }
 
   if (flag_pph_tracer)
-    pph_trace_tree (expr, pph_trace_key_out);
+    pph_trace_tree (expr, pph_trace_back, pph_trace_key_out);
 }
 
 
@@ -2088,6 +2098,7 @@  pph_out_tree (pph_stream *stream, tree e
          the class and code.  */
       gcc_assert (marker == PPH_RECORD_START_NO_CACHE);
       streamer_write_builtin (stream->encoder.w.ob, expr);
+      return;
     }
   else if (TREE_CODE (expr) == INTEGER_CST)
     {
@@ -2096,8 +2107,16 @@  pph_out_tree (pph_stream *stream, tree e
 	 TYPE_CACHED_VALUES).  */
       gcc_assert (marker == PPH_RECORD_START_NO_CACHE);
       streamer_write_integer_cst (stream->encoder.w.ob, expr, false);
+      return;
     }
-  else if (marker == PPH_RECORD_START || marker == PPH_RECORD_START_MUTATED)
+
+  if (flag_pph_tracer)
+    pph_trace_tree (expr, pph_trace_front,
+	marker == PPH_RECORD_START_MERGE_BODY ? pph_trace_merge_body
+	: marker == PPH_RECORD_START_MUTATED ? pph_trace_mutate
+	: pph_trace_normal);
+
+  if (marker == PPH_RECORD_START || marker == PPH_RECORD_START_MUTATED)
     {
 
       /* This is the first time we see EXPR, write it out.  */
@@ -2125,10 +2144,10 @@  pph_out_tree (pph_stream *stream, tree e
     gcc_unreachable ();
 
   if (flag_pph_tracer)
-    pph_trace_tree (expr,
+    pph_trace_tree (expr, pph_trace_back,
 	marker == PPH_RECORD_START_MERGE_BODY ? pph_trace_merge_body
 	: marker == PPH_RECORD_START_MUTATED ? pph_trace_mutate
-	: pph_trace_normal );
+	: pph_trace_normal);
 }
 
 
@@ -2368,11 +2387,6 @@  pph_out_global_binding (pph_stream *stre
      multiple PPH images.  */
   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 180802)
+++ gcc/cp/pph-streamer.h	(working copy)
@@ -239,6 +239,11 @@  enum pph_trace_kind
   pph_trace_merge_body, pph_trace_mutate, pph_trace_normal
 };
 
+enum pph_trace_end
+{
+  pph_trace_front, pph_trace_back
+};
+
 /* In pph-streamer.c.  */
 void pph_streamer_init (void);
 void pph_streamer_finish (void);
@@ -246,7 +251,8 @@  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, enum pph_trace_kind);
+void pph_trace_marker (enum pph_record_marker marker, enum pph_tag tag);
+void pph_trace_tree (tree, enum pph_trace_end, 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 *,