diff --git a/gcc/c-family/ChangeLog.pph b/gcc/c-family/ChangeLog.pph
index bf2304b..8c896d7 100644
--- a/gcc/c-family/ChangeLog.pph
+++ b/gcc/c-family/ChangeLog.pph
@@ -1,3 +1,11 @@
+2012-03-13   Diego Novillo  <dnovillo@google.com>
+
+	* c-common.h (pph_writer_enabled_p): Declare.
+	(pph_enabled_p): Declare.
+	* c-opts.c (pph_writer_enabled_p): New.
+	(pph_enabled_p): New.
+	* c.opt (fpph-gen): Add Var(flag_pph_gen).
+
 2012-03-09   Lawrence Crowl  <crowl@google.com>
 
 	* c.opt (-fprimary-system-header-okay): New.
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 315e99e..a1f4e81 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -590,6 +590,16 @@ extern const char *pph_out_file;
 extern bool
 pph_reader_enabled_p (void);
 
+/* Return true if we are generating a PPH file.  */
+
+extern bool
+pph_writer_enabled_p (void);
+
+/* Return true if we are either generating or reading a PPH file.  */
+
+extern bool
+pph_enabled_p (void);
+
 /* Disable the PPH reader.  */
 
 extern void
diff --git a/gcc/c-family/c-opts.c b/gcc/c-family/c-opts.c
index 8c02010..63be92b 100644
--- a/gcc/c-family/c-opts.c
+++ b/gcc/c-family/c-opts.c
@@ -132,6 +132,24 @@ static void c_finish_options (void);
 static strstrmap_t *include_pph_mapping;
 
 
+/* Return true if we are generating a PPH image.  */
+
+bool
+pph_writer_enabled_p (void)
+{
+  return flag_pph_gen;
+}
+
+
+/* Return true if PPH has been enabled.  */
+
+bool
+pph_enabled_p (void)
+{
+  return pph_writer_enabled_p () || pph_reader_enabled_p ();
+}
+
+
 /* Return true if we have any map from INCLUDE to PPH file.  */
 
 bool
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 7caae53..021bf0a 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -990,7 +990,7 @@ C++ Var(flag_pph_dump_tree)
 -fpph-dump-tree	Dump global namespace tree around PPH reads/writes.
 
 fpph-gen
-C++
+C++ Var(flag_pph_gen)
 -fpph-gen	Generate a PPH image from the input file
 
 fpph-hdr=
diff --git a/gcc/cp/ChangeLog.pph b/gcc/cp/ChangeLog.pph
index 697ae2b..edb83f5 100644
--- a/gcc/cp/ChangeLog.pph
+++ b/gcc/cp/ChangeLog.pph
@@ -1,5 +1,45 @@
 2012-03-13   Diego Novillo  <dnovillo@google.com>
 
+	* decl.c (cp_rest_of_decl_compilation): When generating a PPH
+	file, call pph_add_decl_to_symtab and return.
+	* pph-core.c (tag_strings): Remove "PPH_cgraph_node".
+	* pph-in.c (pph_in_merge_ld_base): Merge
+	ldb->not_really_extern with |=.
+	(pph_in_merge_lang_indep_tree_body): Preserve existing
+	DECL_ARGUMENTS for a FUNCTION_DECL only if the decl already
+	has a DECL_SAVED_TREE.
+	(pph_reset_external): Remove.  Update all users.
+	(pph_in_nonnull_tree): Overwrite *FIELD only if it is
+	currently NULL.
+	(pph_in_cgraph_node): Remove.  Update all users.
+	(pph_node_already_emitted): Remove.  Update all users.
+	(pph_fn_already_expanded): New.
+	(pph_in_symtab): Call it.
+	Read all new fields in pph_symtab_entry.
+	Handle PPH_SYMTAB_EXPAND and PPH_SYMTAB_EXPAND_1 actions.
+	* pph-out.c (pph_out_struct_function_for_decl): Remove.
+	(pph_out_symtab_action): Assert that the action is in the set
+	of known action.
+	(pph_out_cgraph_node): Remove.  Update all users.
+	(pph_out_cgraph_node_for_decl): Remove.  Update all users.
+	(pph_add_decl_to_symtab): Capture global state required to
+	re-play symtab actions in the reader.
+	(pph_out_symtab): Write all new fields from pph_symtab_entry.
+	* pph-streamer.h (struct pph_symtab_entry): Add fields AT_EOF,
+	X_PROCESSING_TEMPLATE_DECL and FUNCTION_DEPTH.
+	* pph.h (enum pph_symtab_action): Add value PPH_SYMTAB_EXPAND_1.
+	(enum pph_tag): Remove value PPH_cgraph_node.  Update all
+	users.
+	(pph_writer_enabled_p): Move to c-family/c-opts.c.
+	(pph_enabled_p): Likewise.
+	* cp/semantics.c (expand_or_defer_fn_1): If we are generating
+	a PPH file, add it to the symbol table and return.
+	(expand_or_defer_fn): Likewise.
+	* toplev.c (compile_file): If -fpph-gen is used, do not
+	generate code.
+
+2012-03-13   Diego Novillo  <dnovillo@google.com>
+
 	* decl.c (record_builtin_type): Call pph_register_builtin_type.
 	* pph-core.c (pph_cache_add_full_tree_r): New.
 	(pph_cache_add_full_tree): New.
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index ab2b7d2..ddc5444 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -5961,11 +5961,14 @@ value_dependent_init_p (tree init)
 void
 cp_rest_of_decl_compilation (tree decl, int top_level, int at_end)
 {
-  rest_of_decl_compilation (decl, top_level, at_end);
-
   /* If we are generating a PPH image, add DECL to its symbol table.  */
   if (pph_writer_enabled_p ())
-    pph_add_decl_to_symtab (decl, PPH_SYMTAB_DECLARE, top_level, at_end);
+    {
+      pph_add_decl_to_symtab (decl, PPH_SYMTAB_DECLARE, top_level, at_end);
+      return;
+    }
+
+  rest_of_decl_compilation (decl, top_level, at_end);
 }
 
 
diff --git a/gcc/cp/pph-core.c b/gcc/cp/pph-core.c
index 8c79017..f2984df 100644
--- a/gcc/cp/pph-core.c
+++ b/gcc/cp/pph-core.c
@@ -338,7 +338,6 @@ 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",
diff --git a/gcc/cp/pph-in.c b/gcc/cp/pph-in.c
index 12685a9..a328e13 100644
--- a/gcc/cp/pph-in.c
+++ b/gcc/cp/pph-in.c
@@ -1483,7 +1483,7 @@ pph_in_merge_ld_base (pph_stream *stream, struct lang_decl_base *ldb)
   ldb->selector = bp_unpack_value (&bp, 16);
   ldb->language = (enum languages) bp_unpack_value (&bp, 4);
   ldb->use_template = bp_unpack_value (&bp, 2);
-  ldb->not_really_extern = bp_unpack_value (&bp, 1);
+  ldb->not_really_extern |= bp_unpack_value (&bp, 1);
   ldb->initialized_in_class = bp_unpack_value (&bp, 1);
   ldb->repo_available_p = bp_unpack_value (&bp, 1);
   ldb->threadprivate_or_deleted_p = bp_unpack_value (&bp, 1);
@@ -1647,7 +1647,13 @@ pph_in_lang_indep_tree_body (pph_stream *stream, tree expr)
 }
 
 
-/* Stream in the language-independent parts of EXPR's body.  */
+/* Merge in from STREAM the language-independent parts of EXPR's body.
+   This merging operates in three phases: (1) fields that need to be
+   preserved are saved, (2) the new body for EXPR is read-in, (3)
+   preserved fields are restored back into EXPR.
+
+   This three stage merging is needed because the generic tree reader
+   is not merge-aware.  */
 
 static void
 pph_in_merge_lang_indep_tree_body (pph_stream *stream, tree expr)
@@ -1657,7 +1663,10 @@ pph_in_merge_lang_indep_tree_body (pph_stream *stream, tree expr)
   tree decl_comdat_group = NULL;
   bool decl_declared_inline = false;
   tree decl_result = NULL;
+  tree decl_arguments = NULL;
 
+  /* Preserve some fields which should not be overwritten by the DECL
+     that we are merging in.  */
   if (CODE_CONTAINS_STRUCT (code, TS_DECL_WITH_VIS))
     {
       decl_comdat = DECL_COMDAT (expr);
@@ -1670,16 +1679,19 @@ pph_in_merge_lang_indep_tree_body (pph_stream *stream, tree expr)
 	 tree decl_section_name = DECL_SECTION_NAME (expr);
       */
     }
+
   if (CODE_CONTAINS_STRUCT (code, TS_FUNCTION_DECL))
     {
       decl_declared_inline = DECL_DECLARED_INLINE_P (expr);
-    }
-  if (code == FUNCTION_DECL)
-    {
-      decl_result = DECL_RESULT (expr);
-      /* FIXME pph: These too?
-	 decl_arguments = DECL_ARGUMENTS (expr);
-      */
+
+      /* DECL_RESULT and DECL_ARGUMENTS are used inside the body
+	 of a function.  Preserve them only if EXPR already has a
+	 body.  */
+      if (DECL_SAVED_TREE (expr))
+	{
+	  decl_result = DECL_RESULT (expr);
+	  decl_arguments = DECL_ARGUMENTS (expr);
+	}
     }
 
   /* FIXME pph: Also see the functions below for more potential fields.
@@ -1689,8 +1701,11 @@ pph_in_merge_lang_indep_tree_body (pph_stream *stream, tree expr)
 	lto_input_ts_decl_non_common_tree_pointers
   */
 
+  /* Read the body of the new decl (this overwrites all the fields of
+     EXPR).  */
   pph_in_lang_indep_tree_body (stream, expr);
 
+  /* Restore values that we do not want overwritten.  */
   if (CODE_CONTAINS_STRUCT (code, TS_DECL_WITH_VIS))
     {
       decl_comdat ? DECL_COMDAT (expr) = true : 0;
@@ -1703,16 +1718,19 @@ pph_in_merge_lang_indep_tree_body (pph_stream *stream, tree expr)
 	 decl_section_name ? DECL_SECTION_NAME (expr) = decl_section_name : 0;
       */
     }
+
   if (CODE_CONTAINS_STRUCT (code, TS_FUNCTION_DECL))
     {
       decl_declared_inline ? DECL_DECLARED_INLINE_P (expr) = true : 0;
-    }
-  if (code == FUNCTION_DECL)
-    {
-      decl_result ? DECL_RESULT (expr) = decl_result : 0;
-      /* FIXME pph: These too?
-	 decl_arguments ? DECL_ARGUMENTS (expr) = decl_arguments : 0;
-      */
+
+      /* DECL_RESULT and DECL_ARGUMENTS are used inside the body
+	 of a function.  Preserve them only if EXPR already has a
+	 body.  */
+      if (DECL_SAVED_TREE (expr))
+	{
+	  decl_result ? DECL_RESULT (expr) = decl_result : 0;
+	  decl_arguments ? DECL_ARGUMENTS (expr) = decl_arguments : 0;
+	}
     }
 }
 
@@ -1997,26 +2015,6 @@ pph_in_tcc_type (pph_stream *stream, tree type)
 }
 
 
-/* Reset the external state for a function DECL.  */
-
-static void
-pph_reset_external (tree decl)
-{
-  /* 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.  */
-  gcc_assert (DECL_LANG_SPECIFIC (decl));
-  if (DECL_NOT_REALLY_EXTERN (decl)
-      && DECL_EXTERNAL (decl))
-    {
-      DECL_EXTERNAL (decl) = 0;
-      DECL_NOT_REALLY_EXTERN (decl) = 0;
-    }
-}
-
-
 /* Read from STREAM the body of tcc_declaration tree DECL.  */
 
 static void
@@ -2042,7 +2040,6 @@ pph_in_tcc_declaration (pph_stream *stream, tree decl)
     case FUNCTION_DECL:
       TREE_CHAIN (decl) = pph_in_tree (stream);
       DECL_SAVED_TREE (decl) = pph_in_tree (stream);
-      pph_reset_external (decl);
       break;
 
     case TYPE_DECL:
@@ -2067,7 +2064,7 @@ pph_in_nonnull_tree (tree *field, pph_stream *stream)
 {
   tree candidate;
   candidate = pph_in_tree (stream);
-  if (candidate)
+  if (candidate && *field == NULL)
     *field = candidate;
 }
 
@@ -2081,7 +2078,6 @@ pph_in_merge_tcc_declaration (pph_stream *stream, tree decl)
   pph_in_lang_decl (stream, decl);
 
   /* FIXME pph: Some of the following may not be necessary.  */
-
   pph_in_nonnull_tree (&DECL_INITIAL (decl), stream);
   pph_in_nonnull_tree (&DECL_ABSTRACT_ORIGIN (decl), stream);
 
@@ -2102,7 +2098,6 @@ pph_in_merge_tcc_declaration (pph_stream *stream, tree decl)
     case FUNCTION_DECL:
       /* ignore TREE_CHAIN (decl) = */ pph_in_tree (stream);
       pph_in_nonnull_tree (&DECL_SAVED_TREE (decl), stream);
-      pph_reset_external (decl);
       break;
 
     case TYPE_DECL:
@@ -2315,10 +2310,8 @@ pph_in_merge_tree_body (pph_stream *stream, tree expr)
   switch (TREE_CODE_CLASS (TREE_CODE (expr)))
     {
       case tcc_declaration:
-	{
-	  pph_in_merge_tcc_declaration (stream, expr);
-	}
-       break;
+	pph_in_merge_tcc_declaration (stream, expr);
+	break;
 
       case tcc_type:
 	pph_in_lang_indep_tree_body (stream, expr);
@@ -2841,90 +2834,6 @@ pph_in_symtab_action (pph_stream *stream)
 }
 
 
-/* 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, PPH_cgraph_node);
-  if (marker == PPH_RECORD_END)
-    return NULL;
-  else if (pph_is_reference_marker (marker))
-    return (struct cgraph_node *) pph_cache_find (stream, marker, image_ix,
-						  ix, PPH_cgraph_node);
-
-  fndecl = pph_in_tree (stream);
-  ALLOC_AND_REGISTER (&stream->cache, ix, PPH_cgraph_node, node,
-                      cgraph_get_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;
-}
-
-
 /* Have we already emitted this DECL?  */
 
 static bool
@@ -2938,16 +2847,16 @@ pph_decl_already_emitted (tree decl)
 }
 
 
-/* Have we already emitted this cgraph NODE?  */
+/* Have we already expanded this FN?  */
 
 static bool
-pph_node_already_emitted (struct cgraph_node *node)
+pph_fn_already_expanded (tree fn)
 {
-  static struct pointer_set_t *emitted_nodes = NULL;
-  gcc_assert (node != NULL);
-  if (!emitted_nodes)
-    emitted_nodes = pointer_set_create ();
-  return pointer_set_insert (emitted_nodes, node) != 0;
+  static struct pointer_set_t *expanded_fns = NULL;
+  gcc_assert (fn != NULL);
+  if (!expanded_fns)
+    expanded_fns = pointer_set_create ();
+  return pointer_set_insert (expanded_fns, fn) != 0;
 }
 
 
@@ -2969,44 +2878,74 @@ pph_in_symtab (pph_stream *stream)
   num = pph_in_uint (stream);
   for (i = 0; i < num; i++)
     {
-      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)
+      pph_symtab_entry entry;
+      struct bitpack_d bp;
+
+      entry.action = pph_in_symtab_action (stream);
+      entry.decl = pph_in_tree (stream);
+      bp = pph_in_bitpack (stream);
+      entry.top_level = bp_unpack_value (&bp, 1);
+      entry.at_end = bp_unpack_value (&bp, 1);
+      entry.at_eof = bp_unpack_value (&bp, 1);
+      entry.x_processing_template_decl = pph_in_int (stream);
+      entry.function_depth = pph_in_int (stream);
+
+      if (entry.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);
-          if (pph_decl_already_emitted (decl))
+          if (pph_decl_already_emitted (entry.decl))
             continue;
-
-	  cp_rest_of_decl_compilation (decl, top_level, at_end);
+	  cp_rest_of_decl_compilation (entry.decl, entry.top_level,
+				       entry.at_end);
 	}
-      else if (action == PPH_SYMTAB_EXPAND)
+      else if (entry.action == PPH_SYMTAB_EXPAND
+	       || entry.action == PPH_SYMTAB_EXPAND_1)
 	{
-	  struct cgraph_node *node;
+	  bool prev_processing_template_decl, prev_at_eof;
+	  int prev_function_depth;
+	  bool need_cgraph_node_p;
+	  tree prev_current_function_decl;
+
+	  pph_in_struct_function (stream, entry.decl);
+	  need_cgraph_node_p = pph_in_bool (stream);
+	  if (pph_fn_already_expanded (entry.decl))
+	    continue;
+
+	  /* ENTRY.DECL was reset to being external by the original
+	     expansion code, so we need to restore its value before
+	     expanding again.  */
+	  DECL_EXTERNAL (entry.decl) = false;
+
+	  /* Re-establish needed global state before calling the
+	     expanders.  FIXME pph, get rid of this by fixing the
+	     expanders.  */
+	  prev_processing_template_decl = processing_template_decl;
+	  prev_at_eof = at_eof;
+	  prev_function_depth = function_depth;
+
+	  processing_template_decl = entry.x_processing_template_decl;
+	  at_eof = entry.at_eof;
+	  function_depth = entry.function_depth;
+
+	  /* Do the expansion.  */
+	  prev_current_function_decl = current_function_decl;
+	  current_function_decl = entry.decl;
+	  push_cfun (DECL_STRUCT_FUNCTION (entry.decl));
+
+	  if (entry.action == PPH_SYMTAB_EXPAND)
+	    expand_or_defer_fn (entry.decl);
+	  else
+	    expand_or_defer_fn_1 (entry.decl);
 
-	  pph_in_struct_function (stream, decl);
-	  node = pph_in_cgraph_node (stream);
-	  if (node && node->local.finalized)
-	    {
-	      if (pph_node_already_emitted (node))
-		continue;
-
-	      /* 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.  */
-	      DECL_EXTERNAL (node->decl) = false;
-	      node->local.finalized = false;
-	      node->reachable = false;
-	      cgraph_finalize_function (node->decl, true);
-	    }
+	  if (need_cgraph_node_p)
+	    cgraph_get_create_node (entry.decl);
+
+	  current_function_decl = prev_current_function_decl;
+	  pop_cfun ();
+
+	  /* Restore global state.  */
+	  processing_template_decl = prev_processing_template_decl;
+	  at_eof = prev_at_eof;
+	  function_depth = prev_function_depth;
 	}
       else
 	gcc_unreachable ();
diff --git a/gcc/cp/pph-out.c b/gcc/cp/pph-out.c
index c817f22..b94cb77 100644
--- a/gcc/cp/pph-out.c
+++ b/gcc/cp/pph-out.c
@@ -1477,15 +1477,6 @@ pph_out_struct_function (pph_stream *stream, struct function *fn)
 }
 
 
-/* Write the struct function instance for DECL to STREAM.  */
-
-static void
-pph_out_struct_function_for_decl (pph_stream *stream, tree decl)
-{
-  pph_out_struct_function (stream, DECL_STRUCT_FUNCTION (decl));
-}
-
-
 /* Write all the fields in lang_decl_base instance LDB to OB.  */
 
 static void
@@ -2437,97 +2428,13 @@ pph_out_tree (pph_stream *stream, tree expr)
 static inline void
 pph_out_symtab_action (pph_stream *stream, enum pph_symtab_action action)
 {
-  gcc_assert (action == (enum pph_symtab_action)(unsigned char) action);
+  gcc_assert ((action == PPH_SYMTAB_DECLARE
+	       || action == PPH_SYMTAB_EXPAND
+	       || action == PPH_SYMTAB_EXPAND_1)
+              && (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;
-  enum pph_record_marker marker;
-
-  marker = pph_out_start_record (stream, node, PPH_cgraph_node);
-  if (pph_is_reference_or_end_marker (marker))
-    return;
-
-  /* Remove if we start emitting merge keys for this structure.  */
-  gcc_assert (marker == PPH_RECORD_START);
-
-  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);
-}
-
-/* Emit callgraph for DECL to STREAM.  */
-
-static void
-pph_out_cgraph_node_for_decl (pph_stream *stream, tree decl)
-{
-  pph_out_cgraph_node (stream, cgraph_get_node (decl));
-}
-
-
 /* 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.  */
@@ -2547,6 +2454,17 @@ pph_add_decl_to_symtab (tree decl, enum pph_symtab_action action,
   entry.decl = decl;
   entry.top_level = top_level;
   entry.at_end = at_end;
+
+  /* Capture some global state that is needed when re-playing this
+     entry.  FIXME pph, this should not be needed.  There is even
+     inconsistencies in the values for AT_END, the values passed on by
+     various callers is not necessarily the same as the value for
+     ENTRY.AT_EOF below (sigh).  */
+  entry.x_processing_template_decl = processing_template_decl;
+  entry.function_depth = function_depth;
+  gcc_assert (at_end == at_eof && (at_eof == 0 || at_eof == 1));
+  entry.at_eof = at_eof;
+
   VEC_safe_push (pph_symtab_entry, heap, pph_out_stream->symtab.v, &entry);
 }
 
@@ -2568,23 +2486,23 @@ pph_out_symtab (pph_stream *stream)
   pph_out_uint (stream, VEC_length (pph_symtab_entry, stream->symtab.v));
   FOR_EACH_VEC_ELT (pph_symtab_entry, stream->symtab.v, i, entry)
     {
+      struct bitpack_d bp;
+
       pph_out_symtab_action (stream, entry->action);
       pph_out_tree (stream, entry->decl);
-      if (entry->action == PPH_SYMTAB_DECLARE)
+      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);
+      bp_pack_value (&bp, entry->at_eof, 1);
+      pph_out_bitpack (stream, &bp);
+      pph_out_int (stream, entry->x_processing_template_decl);
+      pph_out_int (stream, entry->function_depth);
+      if (entry->action == PPH_SYMTAB_EXPAND
+	  || entry->action == PPH_SYMTAB_EXPAND_1)
 	{
-	  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);
+	  pph_out_struct_function (stream, DECL_STRUCT_FUNCTION (entry->decl));
+	  pph_out_bool (stream, cgraph_get_node (entry->decl) != NULL);
 	}
-      else if (entry->action == PPH_SYMTAB_EXPAND)
-	{
-	  pph_out_struct_function_for_decl (stream, entry->decl);
-	  pph_out_cgraph_node_for_decl (stream, entry->decl);
-	}
-      else
-	gcc_unreachable ();
     }
 }
 
diff --git a/gcc/cp/pph-streamer.h b/gcc/cp/pph-streamer.h
index 62cbdbb..78e0d77 100644
--- a/gcc/cp/pph-streamer.h
+++ b/gcc/cp/pph-streamer.h
@@ -141,6 +141,13 @@ typedef struct pph_symtab_entry
   /* Values to be passed to rest_of_decl_compilation.  */
   unsigned int top_level : 1;
   unsigned int at_end : 1;
+
+  /* Values captured from global state used by expand_or_defer_fn and
+     expand_or_defer_fn_1.  FIXME pph, all this global state should
+     disappear.  */
+  unsigned int at_eof : 1;
+  int x_processing_template_decl;
+  int function_depth;
 } pph_symtab_entry;
 
 DEF_VEC_O(pph_symtab_entry);
diff --git a/gcc/cp/pph.h b/gcc/cp/pph.h
index c6ec227..eaae534 100644
--- a/gcc/cp/pph.h
+++ b/gcc/cp/pph.h
@@ -39,7 +39,10 @@ enum pph_symtab_action {
   PPH_SYMTAB_DECLARE = 0x23,
 
   /* Expand this function with expand_or_defer_fn.  */
-  PPH_SYMTAB_EXPAND
+  PPH_SYMTAB_EXPAND,
+
+  /* Expand this function with expand_or_defer_fn_1.  */
+  PPH_SYMTAB_EXPAND_1
 };
 
 /* Record markers.  */
@@ -113,7 +116,6 @@ enum pph_tag {
   /* Maintain the tags below in alphabetical order.  */
   PPH_binding_entry,
   PPH_binding_table,
-  PPH_cgraph_node,
   PPH_cp_binding_level,
   PPH_cp_class_binding,
   PPH_cp_label_binding,
@@ -198,22 +200,4 @@ extern void pph_in_canonical_template_parms (pph_stream *);
 /* FIXME pph: These functions should be moved to tree.c on merge.  */
 extern VEC(tree,heap) *chain2vec (tree chain);  /* In pph-out.c.  */
 
-
-/* Inline functions.  */
-
-/* Return true if we are generating a PPH image.  */
-static inline bool
-pph_writer_enabled_p (void)
-{
-  return pph_out_file != NULL;
-}
-
-/* Return true if PPH has been enabled.  */
-static inline bool
-pph_enabled_p (void)
-{
-  return pph_writer_enabled_p () || pph_reader_enabled_p ();
-}
-
-
 #endif  /* GCC_CP_PPH_H  */
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 89c23ed..2356b46 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -3720,7 +3720,10 @@ expand_or_defer_fn_1 (tree fn)
 {
   /* If we are generating a PPH image, add FN to its symbol table.  */
   if (pph_writer_enabled_p ())
-    pph_add_decl_to_symtab (fn, PPH_SYMTAB_EXPAND, false, at_eof);
+    {
+      pph_add_decl_to_symtab (fn, PPH_SYMTAB_EXPAND_1, false, at_eof);
+      return false;
+    }
 
   /* When the parser calls us after finishing the body of a template
      function, we don't really want to expand the body.  */
@@ -3811,6 +3814,13 @@ expand_or_defer_fn_1 (tree fn)
 void
 expand_or_defer_fn (tree fn)
 {
+  /* If we are generating a PPH image, add FN to its symbol table.  */
+  if (pph_writer_enabled_p ())
+    {
+      pph_add_decl_to_symtab (fn, PPH_SYMTAB_EXPAND, false, at_eof);
+      return;
+    }
+
   if (expand_or_defer_fn_1 (fn))
     {
       function_depth++;
diff --git a/gcc/testsuite/ChangeLog.pph b/gcc/testsuite/ChangeLog.pph
index 916b69c..fd643f6 100644
--- a/gcc/testsuite/ChangeLog.pph
+++ b/gcc/testsuite/ChangeLog.pph
@@ -1,3 +1,15 @@
+2012-03-13   Diego Novillo  <dnovillo@google.com>
+
+	* g++.dg/pph/x2incomplete4.cc: Mark fixed.
+	* g++.dg/pph/x4incomplete4123.cc: Likewise.
+	* g++.dg/pph/x4incomplete4321.cc: Likewise.
+	* g++.dg/pph/x4keyed.cc: Likewise.
+	* g++.dg/pph/x4keyex.cc: Likewise.
+	* g++.dg/pph/x4keyno.cc: Likewise.
+	* g++.dg/pph/x6dynarray5.h: Update expected failure patterns.
+	* g++.dg/pph/x7dynarray6.cc: Likewise.
+	* g++.dg/pph/x7rtti.cc: Likewise.
+
 2012-03-09   Lawrence Crowl  <crowl@google.com>
 
 	* g++.dg/pph/a0neststruct.cci: New.
diff --git a/gcc/testsuite/g++.dg/pph/x2incomplete4.cc b/gcc/testsuite/g++.dg/pph/x2incomplete4.cc
index 1ae17bf..028aa94 100644
--- a/gcc/testsuite/g++.dg/pph/x2incomplete4.cc
+++ b/gcc/testsuite/g++.dg/pph/x2incomplete4.cc
@@ -1,6 +1,2 @@
-// pph asm xdiff 21766
-// xfail-if BOGUS XTRAFUN
-// copies::copies() is wrongly generated
-
 #include "x1incomplete3.h"
 #include "a0incomplete4.cci"
diff --git a/gcc/testsuite/g++.dg/pph/x4incomplete4123.cc b/gcc/testsuite/g++.dg/pph/x4incomplete4123.cc
index 88e9425..4f3168e 100644
--- a/gcc/testsuite/g++.dg/pph/x4incomplete4123.cc
+++ b/gcc/testsuite/g++.dg/pph/x4incomplete4123.cc
@@ -1,6 +1,3 @@
-// pph asm xdiff 21766
-// xfail-if unneeded copy of copies::copies() emitted
-
 #include "x0incomplete1.h"
 #include "x0incomplete2.h"
 #include "x0incomplete3.h"
diff --git a/gcc/testsuite/g++.dg/pph/x4incomplete4321.cc b/gcc/testsuite/g++.dg/pph/x4incomplete4321.cc
index 3152d50..3d6a862 100644
--- a/gcc/testsuite/g++.dg/pph/x4incomplete4321.cc
+++ b/gcc/testsuite/g++.dg/pph/x4incomplete4321.cc
@@ -1,6 +1,3 @@
-// { dg-xfail-if "ICE" { "*-*-*" } { "-fpph-map=pph.map" } }
-// { dg-bogus "a0incomplete2.hi:7:5: internal compiler error: in gimple_expand_cfg, at cfgexpand.c:4454" "" { xfail *-*-* } 0 }
-
 #include "x0incomplete3.h"
 #include "x0incomplete2.h"
 #include "x0incomplete1.h"
diff --git a/gcc/testsuite/g++.dg/pph/x4keyed.cc b/gcc/testsuite/g++.dg/pph/x4keyed.cc
index 94a72c3..f470785 100644
--- a/gcc/testsuite/g++.dg/pph/x4keyed.cc
+++ b/gcc/testsuite/g++.dg/pph/x4keyed.cc
@@ -1,6 +1,3 @@
-// { dg-xfail-if "ICE" { "*-*-*" } { "-fpph-map=pph.map" } }
-// { dg-bogus "x4keyed.cc:13:1: internal compiler error: in cgraph_analyze_functions, at cgraphunit.c:1210" "" { xfail *-*-* } 0 }
-
 #include "x0keyed1.h"
 #include "x0keyed2.h"
 
diff --git a/gcc/testsuite/g++.dg/pph/x4keyex.cc b/gcc/testsuite/g++.dg/pph/x4keyex.cc
index ecffabb..d56215f 100644
--- a/gcc/testsuite/g++.dg/pph/x4keyex.cc
+++ b/gcc/testsuite/g++.dg/pph/x4keyex.cc
@@ -1,23 +1,3 @@
-// { dg-xfail-if "ICE" { "*-*-*" } { "-fpph-map=pph.map" } }
-// { dg-bogus "x4keyex.cc:28:1: internal compiler error: in cgraph_analyze_functions, at cgraphunit.c:1210" "" { xfail *-*-* } 0 }
-
-// formerly was asm xokay 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 "x0keyed1.h"
 #include "x0keyed2.h"
 
diff --git a/gcc/testsuite/g++.dg/pph/x4keyno.cc b/gcc/testsuite/g++.dg/pph/x4keyno.cc
index 196cc873e..8de9bd1 100644
--- a/gcc/testsuite/g++.dg/pph/x4keyno.cc
+++ b/gcc/testsuite/g++.dg/pph/x4keyno.cc
@@ -1,8 +1,3 @@
-// { dg-xfail-if "ICE" { "*-*-*" } { "-fpph-map=pph.map" } }
-// { dg-bogus "x4keyno.cc:13:1: internal compiler error: in cgraph_analyze_functions, at cgraphunit.c:1210" "" { xfail *-*-* } 0 }
-
-// was asm xokay 32642
-
 #include "x0keyno1.h"
 #include "x0keyno2.h"
 
diff --git a/gcc/testsuite/g++.dg/pph/x6dynarray5.h b/gcc/testsuite/g++.dg/pph/x6dynarray5.h
index 130e989..05634c1 100644
--- a/gcc/testsuite/g++.dg/pph/x6dynarray5.h
+++ b/gcc/testsuite/g++.dg/pph/x6dynarray5.h
@@ -1,9 +1,11 @@
 // { dg-xfail-if "UNIMPL TRAIT MANGLING" { "*-*-*" } { "-fpph-map=pph.map" } }
+// { dg-bogus "bits/stl_uninitialized.h:66:12: sorry, unimplemented: mangling trait_expr" "" { xfail *-*-* } 0 }
+// { dg-bogus "bits/stl_uninitialized.h:124:12: sorry, unimplemented: mangling trait_expr" "" { xfail *-*-* } 0 }
 // { dg-bogus "bits/stl_uninitialized.h:178:12: sorry, unimplemented: mangling trait_expr" "" { xfail *-*-* } 0 }
 // { dg-bogus "bits/allocator.h:153:12: sorry, unimplemented: mangling trait_expr" "" { xfail *-*-* } 0 }
 // { dg-bogus "bits/stl_construct.h:98:12: sorry, unimplemented: mangling trait_expr" "" { xfail *-*-* } 0 }
 // { dg-bogus "bits/stl_tempbuf.h:183:12: sorry, unimplemented: mangling trait_expr" "" { xfail *-*-* } 0 }
-// { dg-bogus "bits/cpp_type_traits.h:87:12: internal compiler error: tree check: expected integer_cst, have const_decl in tree_int_cst_sgn, at tree.c:6567" "" { xfail *-*-* } 0 }
+// { dg-bogus "bits/cpp_type_traits.h:87:12: internal compiler error: tree check: expected integer_cst, have const_decl in tree_int_cst_sgn" "" { xfail *-*-* } 0 }
 
 #ifndef X6DYNARRAY5_H
 #define X6DYNARRAY5_H
diff --git a/gcc/testsuite/g++.dg/pph/x7dynarray6.cc b/gcc/testsuite/g++.dg/pph/x7dynarray6.cc
index d80111e..b32780e 100644
--- a/gcc/testsuite/g++.dg/pph/x7dynarray6.cc
+++ b/gcc/testsuite/g++.dg/pph/x7dynarray6.cc
@@ -1,5 +1,4 @@
 // { dg-xfail-if "BOGUS INTRINSIC RETURN" { "*-*-*" } { "-fpph-map=pph.map" } }
-// { dg-bogus "ext/atomicity.h:48:61: error: void value not ignored as it ought to be" "" { xfail *-*-* } 0 }
 
 #include <algorithm>
 #include <iostream>
diff --git a/gcc/testsuite/g++.dg/pph/x7rtti.cc b/gcc/testsuite/g++.dg/pph/x7rtti.cc
index 585176d..d7bc164 100644
--- a/gcc/testsuite/g++.dg/pph/x7rtti.cc
+++ b/gcc/testsuite/g++.dg/pph/x7rtti.cc
@@ -1,9 +1,8 @@
-// { dg-xfail-if "UNKNOWN MACRO AND BOGUS RTTI" { "*-*-*" } { "-fpph-map=pph.map" } }
-// { dg-bogus "x7rtti.cc:10:0: warning: .__STDC_IEC_559_COMPLEX__. redefined" "" { xfail *-*-* } 0 }
-// { dg-bogus "x7rtti.cc:10:0: warning: .__STDC_ISO_10646__. redefined" "" { xfail *-*-* } 0 }
-// { dg-bogus "x7rtti.cc:10:0: warning: .__STDC_IEC_559__. redefined" "" { xfail *-*-* } 0 }
-// { dg-bogus "x7rtti.cc:22:1: internal compiler error: in cgraph_analyze_functions, at cgraphunit.c:1210" "" { xfail *-*-* } 0 }
 // FIXME pph: This should be a { dg=do run } (with '=' replaced by '-')
+// { xfail-if "UNKNOWN MACRO AND BOGUS RTTI" { "*-*-*" } { "-fpph-map=pph.map" } }
+// { dg-bogus "warning: .__STDC_IEC_559_COMPLEX__. redefined" "" { xfail *-*-* } 0 }
+// { dg-bogus "warning: .__STDC_ISO_10646__. redefined" "" { xfail *-*-* } 0 }
+// { dg-bogus "warning: .__STDC_IEC_559__. redefined" "" { xfail *-*-* } 0 }
 
 #include "x5rtti1.h"
 #include "x5rtti2.h"
diff --git a/gcc/toplev.c b/gcc/toplev.c
index c145af4..e93be1b 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -562,7 +562,7 @@ compile_file (void)
   /* Compilation is now finished except for writing
      what's left of the symbol table output.  */
 
-  if (flag_syntax_only || flag_wpa)
+  if (flag_syntax_only || flag_wpa || flag_pph_gen)
     return;
 
   timevar_start (TV_PHASE_GENERATE);
