diff mbox

[pph] Fix x1key* tests (issue4969041)

Message ID 20110825173926.0780F1DA1D2@topo.tor.corp.google.com
State New
Headers show

Commit Message

Diego Novillo Aug. 25, 2011, 5:39 p.m. UTC
This patch fixes the x1key* tests by changing the way we register
functions to the middle end in the reader.

We were calling expand_or_defer_fn, but that was re-computing tree
attributes that we had already computed and, worse, it was assuming
parser context in scope_chain that the reader did not have.

The fix involves saving the callgraph node of the expanded function,
so that the reader can rematerialize it and register with the
callgraph on the way in.

I adjusted symbol table entries to capture more context that we save
with the entry so the reader knows what to do on the way in.  This
includes the two arguments TOP_LEVEL and AT_END that is needed for
rest_of_decl_compilation.

In fixing this, I found a buglet in the saving of decl chains.  When
we use filters, we turn the chain into a VEC and save that VEC, but
the reader does not know that the chain was saved as a VEC, so it
calls pph_in_chain, which assumes that the length of the chain is a
HOST_WIDE_INT.  However, we were saving the length as an unsigned int.

Lawrence, this patch almost fixes x4keyno.cc.  The only reason we
still fail is due to LFB/LFE asm labels having different numbers.
This test case includes two different pph images, which declare
exactly the same structures and decls.  What's happening is that the
compiler ends up using the symbols coming from the last pph image,
which have different function numbers, so the code is identical but
the labels are different.

I documented the issue in the comments for g++.dg/pph/x4keyno.cc.  I
think the merging process we talked about recently will fix this
problem.

Tested on x86_64.  Committed to branch.


	* Make-lang.in (cp/semantics.o): Add dependency on
	$(CXX_PPH_STREAMER_H).
	* decl.c (start_preparsed_function): Remove call to
	pph_add_decl_to_symtab.
	* pph-streamer-in.c (pph_in_ld_min): Call pph_in_hwi instead
	of pph_in_uint.
	(pph_in_struct_function): Do not return the allocated struct
	function.
	Add argument DECL.  Update all callers.
	Allow references to an cached function.
	(pph_in_cgraph_node): New.
	(pph_in_symtab): Call it for PPH_SYMTAB_EXPAND records.
	Call cgraph_finalize_function instead of expand_or_defer_fn.
	(pph_in_tcc_declaration): Adjust DECL_EXTERNAL for
	DECL_NOT_REALLY_EXTERN functions.  Document why.
	* pph-streamer-out.c (pph_out): Assert that the call to fwrite
	worked.
	(pph_out_tree_vec): Output the length as a HOST_WIDE_INT.
	Document why.
	(pph_out_chained_tree): Remove unused function.
	(pph_out_cgraph_node): New.
	(pph_out_symtab): Call it for PPH_SYMTAB_EXPAND records.
	(pph_add_decl_to_symtab): Add arguments ACTION, TOP_LEVEL and
	AT_END.  Update all callers.
	* pph-streamer.h (enum pph_symtab_action): Rename from
	pph_symtab_marker.  Change values to PPH_SYMTAB_DECLARE and
	PPH_SYMTAB_EXPAND.  Update all users.
	(struct pph_symtab_entry): Declare.
	(struct pph_symtab): Remove field 'm'.  Change field 'v' to be
	VEC(pph_symtab_entry,heap).  Update all users.
	(pph_out_uhwi): New.
	(pph_out_hwi): New.
	(pph_in_uhwi): New.
	(pph_in_hwi): New.
	* semantics.c: Include pph-streamer.h.
	(expand_or_defer_fn_1): Call pph_add_decl_to_symtab.

testsuite/ChangeLog.pph:
	* g++.dg/pph/x1keyed.cc: Mark fixed.
	* g++.dg/pph/x1keyno.cc: Mark fixed.
	* g++.dg/pph/x4keyno.cc: Change asm diff signature.  Document
	source of difference.


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

Patch

diff --git a/gcc/cp/Make-lang.in b/gcc/cp/Make-lang.in
index 3c47a4e..a83d5a2 100644
--- a/gcc/cp/Make-lang.in
+++ b/gcc/cp/Make-lang.in
@@ -332,7 +332,7 @@  cp/repo.o: cp/repo.c $(CXX_TREE_H) $(TM_H) toplev.h $(DIAGNOSTIC_CORE_H) \
 cp/semantics.o: cp/semantics.c $(CXX_TREE_H) $(TM_H) toplev.h \
   $(FLAGS_H) output.h $(RTL_H) $(TIMEVAR_H) \
   $(TREE_INLINE_H) $(CGRAPH_H) $(TARGET_H) $(C_COMMON_H) $(GIMPLE_H) \
-  bitmap.h gt-cp-semantics.h c-family/c-objc.h
+  bitmap.h gt-cp-semantics.h c-family/c-objc.h $(CXX_PPH_STREAMER_H)
 cp/dump.o: cp/dump.c $(CXX_TREE_H) $(TM_H) $(TREE_DUMP_H)
 cp/optimize.o: cp/optimize.c $(CXX_TREE_H) $(TM_H) \
   input.h $(PARAMS_H) debug.h $(TREE_INLINE_H) $(GIMPLE_H) \
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 31d0e43..5dd9980 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -5900,7 +5900,7 @@  cp_rest_of_decl_compilation (tree decl, int top_level, int at_end)
 
   /* If we are generating a PPH image, add DECL to its symbol table.  */
   if (pph_out_file)
-    pph_add_decl_to_symtab (decl);
+    pph_add_decl_to_symtab (decl, PPH_SYMTAB_DECLARE, top_level, at_end);
 }
 
 
@@ -12834,10 +12834,6 @@  start_preparsed_function (tree decl1, tree attrs, int flags)
   start_fname_decls ();
 
   store_parm_decls (current_function_parms);
-
-  /* If we are generating a PPH image, add DECL1 to its symbol table.  */
-  if (pph_out_file)
-    pph_add_decl_to_symtab (decl1);
 }
 
 
diff --git a/gcc/cp/pph-streamer-in.c b/gcc/cp/pph-streamer-in.c
index 720da6a..6da8ac0 100644
--- a/gcc/cp/pph-streamer-in.c
+++ b/gcc/cp/pph-streamer-in.c
@@ -355,10 +355,10 @@  pph_in_ld_min (pph_stream *stream, struct lang_decl_min *ldm)
 static VEC(tree,gc) *
 pph_in_tree_vec (pph_stream *stream)
 {
-  unsigned i, num;
+  HOST_WIDE_INT i, num;
   VEC(tree,gc) *v;
 
-  num = pph_in_uint (stream);
+  num = pph_in_hwi (stream);
   v = NULL;
   for (i = 0; i < num; i++)
     {
@@ -708,29 +708,37 @@  pph_in_ld_fn (pph_stream *stream, struct lang_decl_fn *ldf)
 
 
 /* Read applicable fields of struct function from STREAM.  Associate
-   the read structure to its corresponding FUNCTION_DECL and return
-   it.  */
+   the read structure to DECL.  */
 
-static struct function *
-pph_in_struct_function (pph_stream *stream)
+static void
+pph_in_struct_function (pph_stream *stream, tree decl)
 {
   size_t count, i;
   unsigned image_ix, ix;
   enum pph_record_marker marker;
   struct function *fn;
-  tree decl;
+  tree t;
 
   marker = pph_in_start_record (stream, &image_ix, &ix);
   if (marker == PPH_RECORD_END)
-    return NULL;
-
-  /* Since struct function is embedded in every decl, fn cannot be shared.  */
-  gcc_assert (!pph_is_reference_marker (marker));
+    return;
+  else if (pph_is_reference_marker (marker))
+    {
+      fn = (struct function *) pph_cache_get (&stream->cache, image_ix, ix);
+      gcc_assert (DECL_STRUCT_FUNCTION (decl) == fn);
+      return;
+    }
 
-  decl = pph_in_tree (stream);
+  /* Allocate a new DECL_STRUCT_FUNCTION for DECL.  */
+  t = pph_in_tree (stream);
+  gcc_assert (t == decl);
   allocate_struct_function (decl, false);
   fn = DECL_STRUCT_FUNCTION (decl);
 
+  /* Now register it.  We would normally use ALLOC_AND_REGISTER,
+     but allocate_struct_function does not return a pointer.  */
+  pph_cache_insert_at (&stream->cache, fn, ix);
+
   input_struct_function_base (fn, stream->encoder.r.data_in,
 			      stream->encoder.r.ib);
 
@@ -788,8 +796,6 @@  pph_in_struct_function (pph_stream *stream)
   /* unsigned int after_tree_profile : 1;			-- in base */
   /* unsigned int has_local_explicit_reg_vars : 1;		-- in base */
   /* unsigned int is_thunk : 1;					-- in base */
-
-  return fn;
 }
 
 
@@ -1233,17 +1239,97 @@  pph_in_identifiers (pph_stream *stream, cpp_idents_used *identifiers)
 
 /* Read a symbol table marker from STREAM.  */
 
-static inline enum pph_symtab_marker
-pph_in_symtab_marker (pph_stream *stream)
+static inline enum pph_symtab_action
+pph_in_symtab_action (pph_stream *stream)
 {
-  enum pph_symtab_marker m = (enum pph_symtab_marker) pph_in_uchar (stream);
-  gcc_assert (m == PPH_SYMTAB_FUNCTION
-	      || m == PPH_SYMTAB_FUNCTION_BODY
-	      || m == PPH_SYMTAB_DECL);
+  enum pph_symtab_action m = (enum pph_symtab_action) pph_in_uchar (stream);
+  gcc_assert (m == PPH_SYMTAB_DECLARE || m == PPH_SYMTAB_EXPAND);
   return m;
 }
 
 
+/* Read and return a callgraph node from STREAM.  If this is the first
+   time we read this node, add it to the callgraph.  */
+
+static struct cgraph_node *
+pph_in_cgraph_node (pph_stream *stream)
+{
+  enum pph_record_marker marker;
+  unsigned image_ix, ix;
+  struct cgraph_node *node;
+  tree fndecl;
+  struct bitpack_d bp;
+
+  marker = pph_in_start_record (stream, &image_ix, &ix);
+  if (marker == PPH_RECORD_END)
+    return NULL;
+  else if (pph_is_reference_marker (marker))
+    return (struct cgraph_node *) pph_cache_get (&stream->cache, image_ix, ix);
+
+  fndecl = pph_in_tree (stream);
+  ALLOC_AND_REGISTER (&stream->cache, ix, node, cgraph_create_node (fndecl));
+
+  node->origin = pph_in_cgraph_node (stream);
+  node->nested = pph_in_cgraph_node (stream);
+  node->next_nested = pph_in_cgraph_node (stream);
+  node->next_needed = pph_in_cgraph_node (stream);
+  node->next_sibling_clone = pph_in_cgraph_node (stream);
+  node->prev_sibling_clone = pph_in_cgraph_node (stream);
+  node->clones = pph_in_cgraph_node (stream);
+  node->clone_of = pph_in_cgraph_node (stream);
+  node->same_comdat_group = pph_in_cgraph_node (stream);
+  gcc_assert (node->call_site_hash == NULL);
+  node->former_clone_of = pph_in_tree (stream);
+  gcc_assert (node->aux == NULL);
+  gcc_assert (VEC_empty (ipa_opt_pass, node->ipa_transforms_to_apply));
+
+  gcc_assert (VEC_empty (ipa_ref_t, node->ref_list.references));
+  gcc_assert (VEC_empty (ipa_ref_ptr, node->ref_list.refering));
+
+  gcc_assert (node->local.lto_file_data == NULL);
+  bp = pph_in_bitpack (stream);
+  node->local.local = bp_unpack_value (&bp, 1);
+  node->local.externally_visible = bp_unpack_value (&bp, 1);
+  node->local.finalized = bp_unpack_value (&bp, 1);
+  node->local.can_change_signature = bp_unpack_value (&bp, 1);
+  node->local.redefined_extern_inline = bp_unpack_value (&bp, 1);
+
+  node->global.inlined_to = pph_in_cgraph_node (stream);
+
+  node->rtl.preferred_incoming_stack_boundary = pph_in_uint (stream);
+
+  gcc_assert (VEC_empty (ipa_replace_map_p, node->clone.tree_map));
+  node->thunk.fixed_offset = pph_in_uhwi (stream);
+  node->thunk.virtual_value = pph_in_uhwi (stream);
+  node->thunk.alias = pph_in_tree (stream);
+  bp = pph_in_bitpack (stream);
+  node->thunk.this_adjusting = bp_unpack_value (&bp, 1);
+  node->thunk.virtual_offset_p = bp_unpack_value (&bp, 1);
+  node->thunk.thunk_p = bp_unpack_value (&bp, 1);
+
+  node->count = pph_in_uhwi (stream);
+  node->count_materialization_scale = pph_in_uint (stream);
+
+  bp = pph_in_bitpack (stream);
+  node->needed = bp_unpack_value (&bp, 1);
+  node->address_taken = bp_unpack_value (&bp, 1);
+  node->abstract_and_needed = bp_unpack_value (&bp, 1);
+  node->reachable = bp_unpack_value (&bp, 1);
+  node->reachable_from_other_partition = bp_unpack_value (&bp, 1);
+  node->lowered = bp_unpack_value (&bp, 1);
+  node->analyzed = bp_unpack_value (&bp, 1);
+  node->in_other_partition = bp_unpack_value (&bp, 1);
+  node->process = bp_unpack_value (&bp, 1);
+  node->alias = bp_unpack_value (&bp, 1);
+  node->same_body_alias = bp_unpack_value (&bp, 1);
+  node->frequency = (enum node_frequency) bp_unpack_value (&bp, 2);
+  node->only_called_at_startup = bp_unpack_value (&bp, 1);
+  node->only_called_at_exit = bp_unpack_value (&bp, 1);
+
+  return node;
+}
+
+
 /* Read the symbol table from STREAM.  When this image is read into
    another translation unit, we want to guarantee that the IL
    instances taken from this image are instantiated in the same order
@@ -1262,32 +1348,38 @@  pph_in_symtab (pph_stream *stream)
   num = pph_in_uint (stream);
   for (i = 0; i < num; i++)
     {
-      enum pph_symtab_marker kind = pph_in_symtab_marker (stream);
-      if (kind == PPH_SYMTAB_FUNCTION || kind == PPH_SYMTAB_FUNCTION_BODY)
+      enum pph_symtab_action action;
+      tree decl;
+      bool top_level, at_end;
+
+      action = pph_in_symtab_action (stream);
+      decl = pph_in_tree (stream);
+      if (action == PPH_SYMTAB_DECLARE)
+	{
+	  struct bitpack_d bp;
+	  bp = pph_in_bitpack (stream);
+	  top_level = bp_unpack_value (&bp, 1);
+	  at_end = bp_unpack_value (&bp, 1);
+	  cp_rest_of_decl_compilation (decl, top_level, at_end);
+	}
+      else if (action == PPH_SYMTAB_EXPAND)
 	{
-	  struct function *fn = pph_in_struct_function (stream);
+	  struct cgraph_node *node;
 
-	  if (kind == PPH_SYMTAB_FUNCTION_BODY)
+	  pph_in_struct_function (stream, decl);
+	  node = pph_in_cgraph_node (stream);
+	  if (node && node->local.finalized)
 	    {
-	      /* FIXME pph - This is somewhat gross.  When we
-		 generated the PPH image, the parser called
-		 expand_or_defer_fn on FN->DECL, which marked it
-		 DECL_EXTERNAL (see expand_or_defer_fn_1 for details).
-
-		 However, this is not really an extern definition, so
-		 it was also marked not-really-extern (yes, I
-		 know...). If this happens, we need to unmark it,
-		 otherwise the code generator will toss it out.  */
-	      if (DECL_NOT_REALLY_EXTERN (fn->decl))
-		DECL_EXTERNAL (fn->decl) = 0;
-	      expand_or_defer_fn (fn->decl);
+	      /* Since the writer had finalized this cgraph node,
+		 we have to re-play its actions.  To do that, we need
+		 to clear the finalized and reachable bits in the
+		 node, otherwise cgraph_finalize_function will toss
+		 out this node.  */
+	      node->local.finalized = false;
+	      node->reachable = false;
+	      cgraph_finalize_function (node->decl, true);
 	    }
 	}
-      else if (kind == PPH_SYMTAB_DECL)
-	{
-	  tree t = pph_in_tree (stream);
-	  cp_rest_of_decl_compilation (t, decl_function_context (t) == NULL, 1);
-	}
       else
 	gcc_unreachable ();
     }
@@ -1598,6 +1690,20 @@  pph_in_tcc_declaration (pph_stream *stream, tree decl)
     default:
       break;
     }
+
+  /* When the declaration was compiled originally, the parser marks
+     it DECL_EXTERNAL, to avoid emitting it more than once.  It also
+     remembers the true external state in DECL_NOT_REALLY_EXTERN.  So,
+     if both bits are set, the declaration should not be considered
+     external.  */
+  if (TREE_CODE (decl) == FUNCTION_DECL
+      && DECL_LANG_SPECIFIC (decl)
+      && DECL_NOT_REALLY_EXTERN (decl)
+      && DECL_EXTERNAL (decl))
+    {
+      DECL_EXTERNAL (decl) = 0;
+      DECL_NOT_REALLY_EXTERN (decl) = 0;
+    }
 }
 
 
@@ -1612,6 +1718,7 @@  pph_in_tcc_type (pph_stream *stream, tree type)
   TYPE_NEXT_VARIANT (type) = pph_in_tree (stream);
   /* FIXME pph - Streaming TYPE_CANONICAL generates many type comparison
      failures.  Why?  */
+  TREE_CHAIN (type) = pph_in_tree (stream);
 
   /* The type values cache is built as constants are instantiated,
      so we only stream it on the nodes that use it for
diff --git a/gcc/cp/pph-streamer-out.c b/gcc/cp/pph-streamer-out.c
index 83f98c3..dc15730 100644
--- a/gcc/cp/pph-streamer-out.c
+++ b/gcc/cp/pph-streamer-out.c
@@ -71,7 +71,10 @@  static void
 pph_out (const void *data, size_t len, void *block ATTRIBUTE_UNUSED)
 {
   if (data)
-    fwrite (data, len, 1, pph_out_stream->file);
+    {
+      size_t written = fwrite (data, 1, len, pph_out_stream->file);
+      gcc_assert (written == len);
+    }
 }
 
 
@@ -374,7 +377,12 @@  pph_out_tree_vec (pph_stream *stream, VEC(tree,gc) *v)
   unsigned i;
   tree t;
 
-  pph_out_uint (stream, VEC_length (tree, v));
+  /* Note that we use the same format used by streamer_write_chain.
+     This is to support pph_out_chain_filtered, which writes the
+     filtered chain as a VEC.  Since the reader always reads chains
+     using streamer_read_chain, we have to write VECs in exactly the
+     same way as tree chains.  */
+  pph_out_hwi (stream, VEC_length (tree, v));
   FOR_EACH_VEC_ELT (tree, v, i, t)
     pph_out_tree (stream, t);
 }
@@ -497,23 +505,6 @@  pph_out_label_binding (pph_stream *stream, cp_label_binding *lb)
 }
 
 
-/* Outputs chained tree T to STREAM by nulling out its chain first and
-   restoring it after the streaming is done.  */
-
-static inline void
-pph_out_chained_tree (pph_stream *stream, tree t)
-{
-  tree saved_chain;
-
-  saved_chain = TREE_CHAIN (t);
-  TREE_CHAIN (t) = NULL_TREE;
-
-  pph_out_tree_1 (stream, t, 2);
-
-  TREE_CHAIN (t) = saved_chain;
-}
-
-
 /* Output a chain of nodes to STREAM starting with FIRST.  Skip any
    nodes that do not match FILTER.  */
 
@@ -1100,13 +1091,85 @@  pph_out_identifiers (pph_stream *stream, cpp_idents_used *identifiers)
 }
 
 
-/* Emit symbol table MARKER to STREAM.  */
+/* Emit symbol table ACTION to STREAM.  */
 
 static inline void
-pph_out_symtab_marker (pph_stream *stream, enum pph_symtab_marker marker)
+pph_out_symtab_action (pph_stream *stream, enum pph_symtab_action action)
 {
-  gcc_assert (marker == (enum pph_symtab_marker)(unsigned char) marker);
-  pph_out_uchar (stream, marker);
+  gcc_assert (action == (enum pph_symtab_action)(unsigned char) action);
+  pph_out_uchar (stream, action);
+}
+
+/* Emit callgraph NODE to STREAM.  */
+
+static void
+pph_out_cgraph_node (pph_stream *stream, struct cgraph_node *node)
+{
+  struct bitpack_d bp;
+
+  if (!pph_out_start_record (stream, node))
+    return;
+
+  pph_out_tree (stream, node->decl);
+  pph_out_cgraph_node (stream, node->origin);
+  pph_out_cgraph_node (stream, node->nested);
+  pph_out_cgraph_node (stream, node->next_nested);
+  pph_out_cgraph_node (stream, node->next_needed);
+  pph_out_cgraph_node (stream, node->next_sibling_clone);
+  pph_out_cgraph_node (stream, node->prev_sibling_clone);
+  pph_out_cgraph_node (stream, node->clones);
+  pph_out_cgraph_node (stream, node->clone_of);
+  pph_out_cgraph_node (stream, node->same_comdat_group);
+  gcc_assert (node->call_site_hash == NULL);
+  pph_out_tree (stream, node->former_clone_of);
+  gcc_assert (node->aux == NULL);
+  gcc_assert (VEC_empty (ipa_opt_pass, node->ipa_transforms_to_apply));
+
+  gcc_assert (VEC_empty (ipa_ref_t, node->ref_list.references));
+  gcc_assert (VEC_empty (ipa_ref_ptr, node->ref_list.refering));
+
+  gcc_assert (node->local.lto_file_data == NULL);
+  bp = bitpack_create (stream->encoder.w.ob->main_stream);
+  bp_pack_value (&bp, node->local.local, 1);
+  bp_pack_value (&bp, node->local.externally_visible, 1);
+  bp_pack_value (&bp, node->local.finalized, 1);
+  bp_pack_value (&bp, node->local.can_change_signature, 1);
+  bp_pack_value (&bp, node->local.redefined_extern_inline, 1);
+  pph_out_bitpack (stream, &bp);
+
+  pph_out_cgraph_node (stream, node->global.inlined_to);
+
+  pph_out_uint (stream, node->rtl.preferred_incoming_stack_boundary);
+
+  gcc_assert (VEC_empty (ipa_replace_map_p, node->clone.tree_map));
+  pph_out_uhwi (stream, node->thunk.fixed_offset);
+  pph_out_uhwi (stream, node->thunk.virtual_value);
+  pph_out_tree (stream, node->thunk.alias);
+  bp = bitpack_create (stream->encoder.w.ob->main_stream);
+  bp_pack_value (&bp, node->thunk.this_adjusting, 1);
+  bp_pack_value (&bp, node->thunk.virtual_offset_p, 1);
+  bp_pack_value (&bp, node->thunk.thunk_p, 1);
+  pph_out_bitpack (stream, &bp);
+
+  pph_out_uhwi (stream, node->count);
+  pph_out_uint (stream, node->count_materialization_scale);
+
+  bp = bitpack_create (stream->encoder.w.ob->main_stream);
+  bp_pack_value (&bp, node->needed, 1);
+  bp_pack_value (&bp, node->address_taken, 1);
+  bp_pack_value (&bp, node->abstract_and_needed, 1);
+  bp_pack_value (&bp, node->reachable, 1);
+  bp_pack_value (&bp, node->reachable_from_other_partition, 1);
+  bp_pack_value (&bp, node->lowered, 1);
+  bp_pack_value (&bp, node->analyzed, 1);
+  bp_pack_value (&bp, node->in_other_partition, 1);
+  bp_pack_value (&bp, node->process, 1);
+  bp_pack_value (&bp, node->alias, 1);
+  bp_pack_value (&bp, node->same_body_alias, 1);
+  bp_pack_value (&bp, node->frequency, 2);
+  bp_pack_value (&bp, node->only_called_at_startup, 1);
+  bp_pack_value (&bp, node->only_called_at_exit, 1);
+  pph_out_bitpack (stream, &bp);
 }
 
 
@@ -1121,26 +1184,30 @@  pph_out_symtab_marker (pph_stream *stream, enum pph_symtab_marker marker)
 static void
 pph_out_symtab (pph_stream *stream)
 {
-  tree decl;
+  pph_symtab_entry *entry;
   unsigned i;
 
-  pph_out_uint (stream, VEC_length (tree, stream->symtab.v));
-  FOR_EACH_VEC_ELT (tree, stream->symtab.v, i, decl)
-    if (TREE_CODE (decl) == FUNCTION_DECL && DECL_STRUCT_FUNCTION (decl))
-      {
-	/* If this is a regular (non-template) function with a body,
-	   mark it for expansion during reading.  */
-	if (DECL_SAVED_TREE (decl) && cgraph_get_node (decl))
-	  pph_out_symtab_marker (stream, PPH_SYMTAB_FUNCTION_BODY);
-	else
-	  pph_out_symtab_marker (stream, PPH_SYMTAB_FUNCTION);
-	pph_out_struct_function (stream, DECL_STRUCT_FUNCTION (decl));
-      }
-    else
-      {
-	pph_out_symtab_marker (stream, PPH_SYMTAB_DECL);
-	pph_out_tree (stream, decl);
-      }
+  pph_out_uint (stream, VEC_length (pph_symtab_entry, stream->symtab.v));
+  FOR_EACH_VEC_ELT (pph_symtab_entry, stream->symtab.v, i, entry)
+    {
+      pph_out_symtab_action (stream, entry->action);
+      pph_out_tree (stream, entry->decl);
+      if (entry->action == PPH_SYMTAB_DECLARE)
+	{
+	  struct bitpack_d bp;
+	  bp = bitpack_create (stream->encoder.w.ob->main_stream);
+	  bp_pack_value (&bp, entry->top_level, 1);
+	  bp_pack_value (&bp, entry->at_end, 1);
+	  pph_out_bitpack (stream, &bp);
+	}
+      else if (entry->action == PPH_SYMTAB_EXPAND)
+	{
+	  pph_out_struct_function (stream, DECL_STRUCT_FUNCTION (entry->decl));
+	  pph_out_cgraph_node (stream, cgraph_get_node (entry->decl));
+	}
+      else
+	gcc_unreachable ();
+    }
 }
 
 
@@ -1437,6 +1504,7 @@  pph_out_tcc_type (pph_stream *stream, tree type)
   pph_out_tree (stream, TYPE_NEXT_VARIANT (type));
   /* FIXME pph - Streaming TYPE_CANONICAL generates many type comparison
      failures.  Why?  */
+  pph_out_tree (stream, TREE_CHAIN (type));
 
   /* The type values cache is built as constants are instantiated,
      so we only stream it on the nodes that use it for
@@ -1751,18 +1819,26 @@  pph_write_tree (struct output_block *ob, tree expr, bool ref_p ATTRIBUTE_UNUSED)
 }
 
 
-/* Add DECL to the symbol table for pph_out_stream.  */
+/* 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.  */
 
 void
-pph_add_decl_to_symtab (tree decl)
+pph_add_decl_to_symtab (tree decl, enum pph_symtab_action action,
+			bool top_level, bool at_end)
 {
-  pph_stream *stream = pph_out_stream;
+  pph_symtab_entry entry;
 
-  if (decl == NULL || stream == NULL)
+  if (decl == NULL || pph_out_stream == NULL)
     return;
 
-  if (!pointer_set_insert (stream->symtab.m, decl))
-    VEC_safe_push (tree, heap, stream->symtab.v, decl);
+  gcc_assert (DECL_P (decl));
+
+  entry.action = action;
+  entry.decl = decl;
+  entry.top_level = top_level;
+  entry.at_end = at_end;
+  VEC_safe_push (pph_symtab_entry, heap, pph_out_stream->symtab.v, &entry);
 }
 
 
diff --git a/gcc/cp/pph-streamer.c b/gcc/cp/pph-streamer.c
index 8ac3ddc..340d220 100644
--- a/gcc/cp/pph-streamer.c
+++ b/gcc/cp/pph-streamer.c
@@ -121,7 +121,6 @@  pph_stream_open (const char *name, const char *mode)
   stream->name = xstrdup (name);
   stream->write_p = (strchr (mode, 'w') != NULL);
   stream->cache.m = pointer_map_create ();
-  stream->symtab.m = pointer_set_create ();
   if (stream->write_p)
     pph_init_write (stream);
   else
@@ -153,8 +152,7 @@  pph_stream_close (pph_stream *stream)
   stream->file = NULL;
   VEC_free (void_p, heap, stream->cache.v);
   pointer_map_destroy (stream->cache.m);
-  VEC_free (tree, heap, stream->symtab.v);
-  pointer_set_destroy (stream->symtab.m);
+  VEC_free (pph_symtab_entry, heap, stream->symtab.v);
 
   if (stream->write_p)
     {
diff --git a/gcc/cp/pph-streamer.h b/gcc/cp/pph-streamer.h
index a51db3b..5b0d3bc 100644
--- a/gcc/cp/pph-streamer.h
+++ b/gcc/cp/pph-streamer.h
@@ -50,19 +50,6 @@  enum pph_record_marker {
   PPH_RECORD_XREF
 };
 
-/* Symbol table markers.  These are all the symbols that need to be 
-   registered with the middle end.  */
-enum pph_symtab_marker {
-  /* A FUNCTION_DECL with DECL_STRUCT_FUNCTION but no body.  */
-  PPH_SYMTAB_FUNCTION = 0x01,
-
-  /* A FUNCTION_DECL with DECL_STRUCT_FUNCTION and a body.  */
-  PPH_SYMTAB_FUNCTION_BODY,
-
-  /* All other symbols.  */
-  PPH_SYMTAB_DECL
-};
-
 /* Line table markers. We only stream line table entries from the parent header
    file, other entries are referred to by the name of the file which is then
    loaded as an include at the correct point in time.  */
@@ -134,15 +121,41 @@  typedef struct pph_pickle_cache {
   struct pointer_map_t *m;
 } pph_pickle_cache;
 
+
+/* Actions associated with each symbol table entry.  These indicate
+   what the reader should do when registering each entry with the
+   middle-end.  */
+enum pph_symtab_action {
+  /* Declare this symbol with rest_of_decl_compilation.  */
+  PPH_SYMTAB_DECLARE = 0x23,
+
+  /* Expand this function with expand_or_defer_fn.  */
+  PPH_SYMTAB_EXPAND
+};
+
+
+/* Symbol table entry.  */
+typedef struct pph_symtab_entry
+{
+  /* Registration action to perform by the reader.  */
+  enum pph_symtab_action action;
+
+  /* VAR_DECL or FUNCTION_DECL to declare.  */
+  tree decl;
+
+  /* Values to be passed to rest_of_decl_compilation.  */
+  unsigned int top_level : 1;
+  unsigned int at_end : 1;
+} pph_symtab_entry;
+
+DEF_VEC_O(pph_symtab_entry);
+DEF_VEC_ALLOC_O(pph_symtab_entry,heap);
+
 /* Symbol table for a PPH stream.  */
 typedef struct pph_symtab
 {
   /* Table of all the declarations to register in declaration order.  */
-  VEC(tree,heap) *v;
-
-  /* Set of declarations to register used to avoid adding duplicate
-     entries to the table.  */
-  struct pointer_set_t *m;
+  VEC(pph_symtab_entry,heap) *v;
 } pph_symtab;
 
 /* Vector of pph_stream pointers.  */
@@ -233,7 +246,7 @@  void *pph_cache_get (pph_pickle_cache *, unsigned, unsigned);
 void pph_flush_buffers (pph_stream *);
 void pph_init_write (pph_stream *);
 void pph_write_tree (struct output_block *, tree, bool);
-void pph_add_decl_to_symtab (tree);
+void pph_add_decl_to_symtab (tree, enum pph_symtab_action, bool, bool);
 void pph_add_include (pph_stream *, bool);
 void pph_writer_init (void);
 void pph_writer_finish (void);
@@ -299,6 +312,20 @@  pph_out_uint (pph_stream *stream, unsigned int value)
 {
   if (flag_pph_tracer >= 4)
     pph_trace_uint (stream, value);
+  streamer_write_uhwi (stream->encoder.w.ob, value);
+}
+
+/* Write an unsigned HOST_WIDE_INT VALUE to STREAM.  */
+static inline void
+pph_out_uhwi (pph_stream *stream, unsigned HOST_WIDE_INT value)
+{
+  streamer_write_uhwi (stream->encoder.w.ob, value);
+}
+
+/* Write a HOST_WIDE_INT VALUE to stream.  */
+static inline void
+pph_out_hwi (pph_stream *stream, HOST_WIDE_INT value)
+{
   streamer_write_hwi (stream->encoder.w.ob, value);
 }
 
@@ -389,7 +416,7 @@  pph_out_bitpack (pph_stream *stream, struct bitpack_d *bp)
   streamer_write_bitpack (bp);
 }
 
-/* Read an unsigned HOST_WIDE_INT integer from STREAM.  */
+/* Read an unsigned integer from STREAM.  */
 static inline unsigned int
 pph_in_uint (pph_stream *stream)
 {
@@ -400,6 +427,20 @@  pph_in_uint (pph_stream *stream)
   return (unsigned) n;
 }
 
+/* Read an unsigned HOST_WIDE_INT from STREAM.  */
+static inline unsigned HOST_WIDE_INT
+pph_in_uhwi (pph_stream *stream)
+{
+  return streamer_read_uhwi (stream->encoder.r.ib);
+}
+
+/* Read a HOST_WIDE_INT from STREAM.  */
+static inline HOST_WIDE_INT
+pph_in_hwi (pph_stream *stream)
+{
+  return streamer_read_hwi (stream->encoder.r.ib);
+}
+
 /* Read an unsigned char VALUE to STREAM.  */
 static inline unsigned char
 pph_in_uchar (pph_stream *stream)
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 1862be6..64c668e 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -45,6 +45,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "target.h"
 #include "gimple.h"
 #include "bitmap.h"
+#include "pph-streamer.h"
 
 /* There routines provide a modular interface to perform many parsing
    operations.  They may therefore be used during actual parsing, or
@@ -3566,6 +3567,10 @@  is_instantiation_of_constexpr (tree fun)
 bool
 expand_or_defer_fn_1 (tree fn)
 {
+  /* If we are generating a PPH image, add FN to its symbol table.  */
+  if (pph_out_file)
+    pph_add_decl_to_symtab (fn, PPH_SYMTAB_EXPAND, false, at_eof);
+
   /* When the parser calls us after finishing the body of a template
      function, we don't really want to expand the body.  */
   if (processing_template_decl)
diff --git a/gcc/testsuite/g++.dg/pph/x1keyed.cc b/gcc/testsuite/g++.dg/pph/x1keyed.cc
index 16ba989..6ef9b9a 100644
--- a/gcc/testsuite/g++.dg/pph/x1keyed.cc
+++ b/gcc/testsuite/g++.dg/pph/x1keyed.cc
@@ -1,6 +1,3 @@ 
-// pph asm xdiff 63070
-// Looks like destructors botched.
-
 #include "x0keyed1.h"
 
 int keyed::key( int arg ) { return mix( field & arg ); }
diff --git a/gcc/testsuite/g++.dg/pph/x1keyno.cc b/gcc/testsuite/g++.dg/pph/x1keyno.cc
index c93297b..5478e90 100644
--- a/gcc/testsuite/g++.dg/pph/x1keyno.cc
+++ b/gcc/testsuite/g++.dg/pph/x1keyno.cc
@@ -1,6 +1,3 @@ 
-// pph asm xdiff 46318
-// Looks like destructors botched.
-
 #include "x0keyno1.h"
 
 int main()
diff --git a/gcc/testsuite/g++.dg/pph/x4keyno.cc b/gcc/testsuite/g++.dg/pph/x4keyno.cc
index f1fdebe..6299d40 100644
--- a/gcc/testsuite/g++.dg/pph/x4keyno.cc
+++ b/gcc/testsuite/g++.dg/pph/x4keyno.cc
@@ -1,5 +1,19 @@ 
-// pph asm xdiff 17472
-// Looks like destructors botched.
+// pph asm xdiff 32642
+//
+// This test case fails to compare because LFB/LFE labels are different.
+//
+// 1- Both x0keyno1.h and x0keyno2.h declare exactly the same, as
+//    they both textually include a0keyed.h.
+// 2- When we read x0keyno1.pph, we instantiate all the decls and cgraph
+//    nodes for struct keyed.
+// 3- When we read x0keyno2.pph, we re-instantiate the same decls and
+//    cgraph nodes, which are used instead of the ones in x0keyno1.pph.
+// 4- Since the new decls and nodes have been created later, the functions
+//    have different function numbers (function::funcdef_no field), which
+//    is used to emit the LFB/LFE labels.
+//
+// The future merging process will fix this by choosing the decls and nodes
+// created first by x0keyno1.pph.
 
 #include "x0keyno1.h"
 #include "x0keyno2.h"