[pph] C++ parser changes for pth and pph instrumentation [2/3]

Diego Novillo dnovillo@google.com
Tue Nov 2 23:11:00 GMT 2010


Most of the changes in this patch are in cp/parser.c, but they
will need to be refactored to other files.

There are two main families of functions pth_* for the
pretoknized cache and pph_* for the parser cache.  Currently, the
pph_* functions are simply doing instrumentation and tracing that
we used as input to the emulator.

The pth_* implement the token cache.  Everything starts at
cp_lexer_new_main, it instantiates an empty token buffer and
starts the pre-processor.  It then tries to get the first file in
the translation unit from the token cache.

The cache stores  files included by each file and sequencing
information to know how the token hunks are interleaved with the
included files. The sequencing is represented as a string
describing the order in which elements from the two sets should
be consumed.

For example, given the following contents:
    
                #ifndef A_H_
                #define A_H_
                typedef int myType;
                #include "b.h"
                #define N M
                extern myType X2;
                #include "d.h"
                extern int X1;
                #endif  // A_H_
    
the image representing that file will contain 3 token hunks ('H')
and 2 included files ('I').  The sequence string for reproducing
it is 'HIHIH'.  The current implementation is heavy handed, it
uses a VEC of chars to represent the sequencing.  This should be
modified to use a more efficient representation.

Every token hunk in the file image is checked against its symbol
table.  If the hunk has a valid symbol table, the tokens in the
hunk are added to the token buffer and the symbols generated by
the hunk incorporated into libcpp's main symbol table.

When an invalid hunk is found, libcpp is told to start reading
the rest of that file from the byte offset at the last token hunk
that was successfully applied.

On the parser side, there are not many interesting bits yet.
Mostly, we instrument the parser to intercept declarations that
would go into the cache and print token plus dependency
information for each one.


2010-11-02  Lawrence Crowl  <crowl@google.com>
	    Diego Novillo  <dnovillo@google.com>

	* decl.c: Include langhooks.h
	(duplicate_decls_internal): Rename from duplicate_decls
	(duplicate_decls): New wrapper around
	duplicate_decls_internal.
	* Make-lang.in (cp/decl.o): Add dependency on langhooks.h
	(cp/parser.o): Add dependency on pointer-set.h,
	fixed-value.h, MD5_H, HASTHTAB_H, tree-pass.h,
	TREE_INLINE_H and tree-pretty-print.h.
	(cp/name-lookup.o): Add dependency on tree-pretty-print.h
	* rtti.c (create_pseudo_type_info): Mark TYPE_NAME of
	PSEUDO_TYPE as artificial.
	* cp-gimplify.c	(cp_genericize): Add FIXME note.
	* parser.c: Include timevar.h, pointer-set.h,
	fixed-value.h, cpplib.h, line-map.h, md5.h, hashtab.h,
	tree-pass.h, tree-inline.h and tree-pretty-print.h.
	(struct cp_token): Add field purged_p.
	Adjust all users of CPP_PURGED.
	(eof_token): Adjust.
	(struct cp_lexer): Convert buffer into a VEC.
	Remove field buffer_length.
	Adjust all users.
	(cp_token_cache_ptr): New typedef.
	(struct cp_token_ident_d): Declare.
	(cp_token_ident): New typedef.
	(struct cp_token_hunk): Declare.
	(cp_token_hunk_ptr): New typedef.
	(DIGEST_LEN): Define.
	(struct pth_image): Define.
	(pth_image_ptr): New typedef.
	(struct pth_include): Define.
	(pth_include_ptr): New typedef.
	(struct pth_state): Declare.
	(struct pth_stats_d): Declare.
	(pth_stats): Declare.
	(PTH_STATS_INCR): Define.
	(struct pph_stats_d): Declare.
	(pph_stats): Declare.
	(PPH_STATS_INCR): Define.
	(pph_decl_head_token_cache): Declare.
	(pph_decl_body_token_cache): Declare.
	(struct pph_decl_deps_d): Declare.
	(pph_decl_deps): Declare.
	(pph_tree_catcher): Declare.
	(pph_name_lookups): Declare.
	(pph_name_lookups_set): Declare.
	(pph_nl_token_map): Declare.
	(pph_logfile): Declare.
	(pph_print_trees_tokens): Declare.
	(cp_lexer_print_token): Always declare.
	(CPP_PURGED): Remove.  Replace all uses with
	token->purged_p.
	(N_CP_TTYPES): Adjust.
	(cp_lexer_dump_tokens): Move earlier in the file.
	(cp_lexer_debug_tokens): New.
	(pathnames_equal_p): New.
	(pph_debug_location): New.
	(pph_debug_loc_of_tree): New.
	(pth_image_dir_hash): New.
	(pth_image_dir_eq): New.
	(pth_get_state): New.
	(pth_id_str): New.
	(pth_header_len): New.
	(PTH_EXTENSION): Define.
	(pth_name_for): New.
	(pth_file_for): New.
	(pth_get_md5_digest): New.
	(pth_get_index_from_type): New.
	(pth_write_uint): New.
	(pth_write_sizet): New.
	(pth_write_bytes): New.
	(pth_write_string): New.
	(pth_write_number): New.
	(pth_save_token_value): New.
	(pth_save_token): New.
	(pth_write_header): New.
	(pth_dump_identifiers): New.
	(pth_debug_identifiers): New.
	(pth_dump_hunk): New.
	(pth_debug_hunk): New.
	(pth_dump_include): New.
	(pth_debug_include): New.
	(pth_dump_token_hunks_1): New.
	(pth_dump_token_hunks): New.
	(pth_debug_token_hunks): New.
	(pth_dump_image): New.
	(pth_debug_image): New.
	(pth_show_image_stats): New.
	(pth_dump_state): New.
	(pth_debug_state): New.
	(pth_save_identifiers): New.
	(pth_save_hunk): New.
	(pth_save_include): New.
	(pth_save_image): New.
	(pth_get_type_from_index): New.
	(pth_read_uint): New.
	(pth_read_sizet): New.
	(pth_read_bytes): New.
	(pth_read_string): New.
	(pth_read_string_alloc): New.
	(pth_load_number): New.
	(pth_load_token_value): New.
	(pth_load_identifiers): New.
	(pth_load_hunk): New.
	(pth_create_include): New.
	(pth_load_include): New.
	(pth_load_image): New.
	(pth_have_valid_image_for): New.
	(pth_new_image): New.
	(pth_image_lookup): New.
	(pth_append_hunk): New.
	(pth_hunk_is_valid_p): New.
	(pth_image_can_be_used): New.
	(cp_lexer_finished_p): New.
	(cp_lexer_get_tokens): New.
	(pth_get_dir_and_name): New.
	(pth_process_text_file): New.
	(pth_image_to_lexer): New.
	(pth_lexer_to_image): New.
	(pth_get_file_transition): New.
	(pth_leave_file): New.
	(pth_enter_file): New.
	(pth_file_change): New.
	(pth_include_handler): New.
	(pth_init): New.
	(pth_print_stats): New.
	(pth_finish): New.
	(cp_lexer_alloc): New.
	(cp_lexer_new_main): Rewrite to support -fpth.
	(cp_lexer_get_preprocessor_token): Flag error if -fpth
	is used together with PCH.
	(cp_lexer_consume_token): Call PPH_STATS_INCR.
	(cp_lexer_print_token): Remove "PURGED" string.
	Show CPP_NUMBER tokens.
	Do not abort if token->type is outside of TOKEN_NAMES.
	(pph_log_exposed): New.
	(pph_allocate_catcher_memory): New.
	(pph_free_catcher_memory): New.
	(pph_start_exposed): New.
	(pph_lookup_head_token_cache_for): New.
	(pph_lookup_body_token_cache_for): New.
	(pph_set_head_token_cache_for): New.
	(pph_set_body_token_cache_for): New.
	(pph_copy_decls_into_cache): New.
	(pph_copy_decls_outof_cache): New.
	(pph_stop_exposed): New.
	(cp_parser_declaration): Call pph_start_exposed and
	pph_stop_exposed for several cases.
	(cp_parser_elaborated_type_specifier): Add tracing code
	for -fpph-debug.
	(cp_parser_class_specifier): Add FIXME notes.
	(cp_parser_lookup_name_1): Rename from cp_parser_lookup_name.
	(cp_parser_lookup_name): Call it.
	Call pph_catch_name_lookup with the result.
	(pph_debug_tree): New.
	(pph_debug_type): New.
	(pph_tree_caught_p): New.
	(pph_catch_head_tokens_for): New.
	(pph_catch_body_tokens_for): New.
	(pph_lookup_dependencies_for): New.
	(pph_set_dependencies_for): New.
	(is_namespace): New.
	(pph_null_exposed): New.
	(pph_live_exposed): New.
	(pph_find_exposed_for): New.
	(pph_catch_dependencies_for): New.
	(pph_catch_tree): New.
	(pph_uncatch_tree): New.
	(pph_locate_name_lookups_in): New.
	(pph_print_copy_tokens): New.
	(pph_print_token_range): New.
	(pph_print_dependence): New.
	(pph_print_depend_template): New.
	(pph_print_depend_decl): New.
	(pph_print_depend_type): New.
	(pph_print_depend_type_type): New.
	(pph_print_depend_func_type): New.
	(pph_print_depend_var_type): New.
	(pph_get_decl_exposure): New.
	(pph_print_dependences): New.
	(pph_print_declaration_head): New.
	(pph_print_declaration_body): New.
	(pph_find_special_methods): New.
	(pph_implicit_class_cost): New.
	(pph_print_declaration): New.
	(pph_print_declarations): New.
	(pph_print_trees_tokens): New.
	(pph_catch_name_lookup): New.
	(pph_print_stats): New.
	(pph_init): New.
	(pph_finish): New.
	(c_parse_file): Call pph_init and pph_finish.
	* call.c (implicit_conversion): Call pph_catch_name_lookup.
	(build_new_op): Call pph_catch_name_lookup.
	(convert_like_real): Call pph_catch_name_lookup.
	(build_new_method_call): Call pph_catch_name_lookup.
	* cp-objcp-common.h (LANG_HOOKS_PPH_CATCH_TREE): Define.
	(LANG_HOOKS_PPH_UNCATCH_TREE): Define.
	* cp-tree.h (PPH_POP_TIMEVAR_AND_RETURN): Define.
	Replace every call to POP_TIMEVAR_AND_RETURN with
	PPH_POP_TIMEVAR_AND_RETURN.
	(pph_catch_tree): Declare.
	(pph_uncatch_tree): Declare.
	(pph_catch_name_lookup): Declare.
	* name-lookup.c: Include tree-pretty-print.h
	(add_decl_to_level): Add tracing for -fpph-debug.

Index: gcc/cp/decl.c
===================================================================
--- gcc/cp/decl.c	(revision 166136)
+++ gcc/cp/decl.c	(working copy)
@@ -53,6 +53,7 @@ along with GCC; see the file COPYING3.  
 #include "pointer-set.h"
 #include "splay-tree.h"
 #include "plugin.h"
+#include "langhooks.h"
 
 /* Possible cases of bad specifiers type used by bad_specifiers. */
 enum bad_spec_place {
@@ -1167,8 +1168,8 @@ validate_constexpr_redeclaration (tree o
 
    NEWDECL_IS_FRIEND is true if NEWDECL was declared as a friend.  */
 
-tree
-duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
+static tree
+duplicate_decls_internal (tree newdecl, tree olddecl, bool newdecl_is_friend)
 {
   unsigned olddecl_uid = DECL_UID (olddecl);
   int olddecl_friend = 0, types_match = 0, hidden_friend = 0;
@@ -2273,6 +2274,35 @@ duplicate_decls (tree newdecl, tree oldd
 
   return olddecl;
 }
+
+
+/* Wrapper for duplicate_decls_internal used by PPH support to
+   decide whether NEWDECL or OLDDECL should be removed from the
+   AST catching data structures.  This is necessary when NEWDECL
+   is a re-declaration of OLDDECL.  */
+
+tree
+duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
+{
+  tree gooddecl, baddecl;
+
+  gooddecl = duplicate_decls_internal (newdecl, olddecl, newdecl_is_friend);
+
+  /* If duplicate_decls_internal returns NULL, it means that NEWDECL
+     is not a re-declaration of OLDDECL, so nothing needs to be done
+     in that case.  */
+  if (gooddecl == NULL_TREE)
+    return NULL_TREE;
+
+  baddecl = (gooddecl == newdecl) ? olddecl : newdecl;
+  if (lang_hooks.pph_uncatch_tree)
+    lang_hooks.pph_uncatch_tree (baddecl);
+
+  if (lang_hooks.pph_catch_tree)
+    lang_hooks.pph_catch_tree (gooddecl);
+
+  return gooddecl;
+}
 

 /* Return zero if the declaration NEWDECL is valid
    when the declaration OLDDECL (assumed to be for the same name)
@@ -2531,7 +2561,7 @@ lookup_label (tree id)
     POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, decl);
 
   decl = make_label_decl (id, /*local_p=*/0);
-  POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, decl);
+  PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, decl);
 }
 
 /* Declare a local label named ID.  */
@@ -2832,7 +2862,7 @@ define_label (location_t location, tree 
   if (DECL_INITIAL (decl) != NULL_TREE)
     {
       error ("duplicate label %qD", decl);
-      POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
+      PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
     }
   else
     {
@@ -2851,7 +2881,7 @@ define_label (location_t location, tree 
       ent->uses = NULL;
     }
 
-  POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, decl);
+  PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, decl);
 }
 
 struct cp_switch
@@ -11009,7 +11039,7 @@ xref_tag (enum tag_types tag_code, tree 
 			       scope, template_header_p);
 
   if (t == error_mark_node)
-    POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
+    PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
 
   if (scope != ts_current && t && current_class_type
       && template_class_depth (current_class_type)
@@ -11064,12 +11094,13 @@ xref_tag (enum tag_types tag_code, tree 
       if (code == ENUMERAL_TYPE)
 	{
 	  error ("use of enum %q#D without previous declaration", name);
-	  POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
+	  PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
 	}
       else
 	{
 	  t = make_class_type (code);
 	  TYPE_CONTEXT (t) = context;
+          /* FIXME pph: creating incomplete class.  */
 	  t = pushtag (name, t, scope);
 	}
     }
@@ -11078,7 +11109,7 @@ xref_tag (enum tag_types tag_code, tree 
       if (template_header_p && MAYBE_CLASS_TYPE_P (t))
         {
 	  if (!redeclare_class_template (t, current_template_parms))
-            POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
+            PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
         }
       else if (!processing_template_decl
 	       && CLASS_TYPE_P (t)
@@ -11086,7 +11117,7 @@ xref_tag (enum tag_types tag_code, tree 
 	{
 	  error ("redeclaration of %qT as a non-template", t);
 	  error ("previous declaration %q+D", t);
-	  POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
+	  PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
 	}
 
       /* Make injected friend class visible.  */
@@ -11104,7 +11135,7 @@ xref_tag (enum tag_types tag_code, tree 
 	}
     }
 
-  POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, t);
+  PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, t);
 }
 
 tree
Index: gcc/cp/Make-lang.in
===================================================================
--- gcc/cp/Make-lang.in	(revision 166136)
+++ gcc/cp/Make-lang.in	(working copy)
@@ -255,7 +255,7 @@ cp/decl.o: cp/decl.c $(CXX_TREE_H) $(TM_
   output.h toplev.h $(HASHTAB_H) $(RTL_H) \
   cp/operators.def $(TM_P_H) $(TREE_INLINE_H) $(DIAGNOSTIC_H) $(C_PRAGMA_H) \
   debug.h gt-cp-decl.h $(TIMEVAR_H) $(TARGET_H) $(PLUGIN_H) \
-  intl.h tree-iterator.h $(SPLAY_TREE_H)
+  intl.h tree-iterator.h $(SPLAY_TREE_H) langhooks.h
 cp/decl2.o: cp/decl2.c $(CXX_TREE_H) $(TM_H) $(FLAGS_H) cp/decl.h \
   output.h toplev.h $(C_COMMON_H) gt-cp-decl2.h $(CGRAPH_H) \
   $(C_PRAGMA_H) $(TREE_DUMP_H) intl.h $(TARGET_H) $(GIMPLE_H) $(POINTER_SET_H) \
@@ -310,13 +310,15 @@ cp/optimize.o: cp/optimize.c $(CXX_TREE_
 cp/mangle.o: cp/mangle.c $(CXX_TREE_H) $(TM_H) toplev.h $(REAL_H) \
   gt-cp-mangle.h $(TARGET_H) $(TM_P_H) $(CGRAPH_H)
 cp/parser.o: cp/parser.c $(CXX_TREE_H) $(TM_H) $(DIAGNOSTIC_CORE_H) \
-  gt-cp-parser.h output.h $(TARGET_H) $(PLUGIN_H) intl.h
+  gt-cp-parser.h output.h $(TARGET_H) $(PLUGIN_H) intl.h \
+  pointer-set.h fixed-value.h $(MD5_H) $(HASHTAB_H) tree-pass.h \
+  $(TREE_INLINE_H) tree-pretty-print.h
 cp/cp-gimplify.o: cp/cp-gimplify.c $(CXX_TREE_H) toplev.h $(C_COMMON_H) \
 	$(TM_H) coretypes.h pointer-set.h tree-iterator.h
 
 cp/name-lookup.o: cp/name-lookup.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
 	$(TM_H) $(CXX_TREE_H) $(TIMEVAR_H) gt-cp-name-lookup.h toplev.h \
-	$(DIAGNOSTIC_CORE_H) $(FLAGS_H) debug.h
+	$(DIAGNOSTIC_CORE_H) $(FLAGS_H) debug.h tree-pretty-print.h
 
 cp/cxx-pretty-print.o: cp/cxx-pretty-print.c $(CXX_PRETTY_PRINT_H) \
   $(CONFIG_H) $(SYSTEM_H) $(TM_H) coretypes.h $(CXX_TREE_H) tree-pretty-print.h
Index: gcc/cp/rtti.c
===================================================================
--- gcc/cp/rtti.c	(revision 166136)
+++ gcc/cp/rtti.c	(working copy)
@@ -1215,6 +1215,7 @@ create_pseudo_type_info (int tk, const c
   /* Create the pseudo type.  */
   pseudo_type = make_class_type (RECORD_TYPE);
   finish_builtin_struct (pseudo_type, pseudo_name, fields, NULL_TREE);
+  DECL_ARTIFICIAL (TYPE_NAME (pseudo_type)) = 1; /* FIXME pph: Right? */
   CLASSTYPE_AS_BASE (pseudo_type) = pseudo_type;
 
   ti = VEC_index (tinfo_s, tinfo_descs, tk);
Index: gcc/cp/cp-gimplify.c
===================================================================
--- gcc/cp/cp-gimplify.c	(revision 166136)
+++ gcc/cp/cp-gimplify.c	(working copy)
@@ -963,6 +963,8 @@ cp_genericize (tree fndecl)
   pointer_set_destroy (wtd.p_set);
   VEC_free (tree, heap, wtd.bind_expr_stack);
 
+  /* FIXME pph: save function to hunk.  */
+
   /* Do everything else.  */
   c_genericize (fndecl);
 
Index: gcc/cp/pt.c
===================================================================
--- gcc/cp/pt.c	(revision 166136)
+++ gcc/cp/pt.c	(working copy)
@@ -6430,7 +6430,7 @@ lookup_template_class (tree d1,
     {
       if (complain & tf_error)
 	error ("%qT is not a template", d1);
-      POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
+      PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
     }
 
   if (TREE_CODE (templ) != TEMPLATE_DECL
@@ -6445,7 +6445,7 @@ lookup_template_class (tree d1,
 	  if (in_decl)
 	    error ("for template declaration %q+D", in_decl);
 	}
-      POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
+      PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
     }
 
   complain &= ~tf_user;
@@ -6495,10 +6495,10 @@ lookup_template_class (tree d1,
       if (arglist2 == error_mark_node
 	  || (!uses_template_parms (arglist2)
 	      && check_instantiated_args (templ, arglist2, complain)))
-	POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
+	PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
 
       parm = bind_template_template_parm (TREE_TYPE (templ), arglist2);
-      POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, parm);
+      PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, parm);
     }
   else
     {
@@ -6574,7 +6574,7 @@ lookup_template_class (tree d1,
 		{
 		  /* Restore the ARGLIST to its full size.  */
 		  TREE_VEC_LENGTH (arglist) = saved_depth;
-		  POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
+		  PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
 		}
 
 	      SET_TMPL_ARGS_LEVEL (bound_args, i, a);
@@ -6602,7 +6602,7 @@ lookup_template_class (tree d1,
 
       if (arglist == error_mark_node)
 	/* We were unable to bind the arguments.  */
-	POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
+	PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
 
       /* In the scope of a template class, explicit references to the
 	 template class refer to the type of the template, not any
@@ -6618,7 +6618,7 @@ lookup_template_class (tree d1,
 	  /* comp_template_args is expensive, check it last.  */
 	  && comp_template_args (TYPE_TI_ARGS (template_type),
 				 arglist))
-	POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, template_type);
+	PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, template_type);
 
       /* If we already have this specialization, return it.  */
       elt.tmpl = gen_tmpl;
@@ -6628,7 +6628,7 @@ lookup_template_class (tree d1,
 						  &elt, hash);
 
       if (entry)
-	POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, entry->spec);
+	PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, entry->spec);
 
       is_dependent_type = uses_template_parms (arglist);
 
@@ -6638,7 +6638,7 @@ lookup_template_class (tree d1,
 	  && check_instantiated_args (gen_tmpl,
 				      INNERMOST_TEMPLATE_ARGS (arglist),
 				      complain))
-	POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
+	PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
 
       if (!is_dependent_type
 	  && !PRIMARY_TEMPLATE_P (gen_tmpl)
@@ -6648,7 +6648,7 @@ lookup_template_class (tree d1,
 	  found = xref_tag_from_type (TREE_TYPE (gen_tmpl),
 				      DECL_NAME (gen_tmpl),
 				      /*tag_scope=*/ts_global);
-	  POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, found);
+	  PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, found);
 	}
 
       context = tsubst (DECL_CONTEXT (gen_tmpl), arglist,
@@ -6839,7 +6839,7 @@ lookup_template_class (tree d1,
       TREE_PUBLIC (type_decl) = 1;
       determine_visibility (type_decl);
 
-      POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, t);
+      PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, t);
     }
   timevar_pop (TV_NAME_LOOKUP);
 }
Index: gcc/cp/parser.c
===================================================================
--- gcc/cp/parser.c	(revision 166136)
+++ gcc/cp/parser.c	(working copy)
@@ -37,6 +37,16 @@ along with GCC; see the file COPYING3.  
 #include "cgraph.h"
 #include "c-family/c-common.h"
 #include "plugin.h"
+#include "timevar.h"
+#include "pointer-set.h"
+#include "fixed-value.h"
+#include "cpplib.h"
+#include "line-map.h"
+#include "md5.h"
+#include "hashtab.h"
+#include "tree-pass.h"
+#include "tree-inline.h"
+#include "tree-pretty-print.h"
 
 

 /* The lexer.  */
@@ -75,6 +85,10 @@ typedef struct GTY (()) cp_token {
      KEYWORD is RID_MAX) iff this name was looked up and found to be
      ambiguous.  An error has already been reported.  */
   BOOL_BITFIELD ambiguous_p : 1;
+  /* True for a token that has been purged.  If a token is purged,
+     it is no longer a valid token and it should be considered
+     deleted.  */
+  BOOL_BITFIELD purged_p : 1;
   /* The location at which this token was found.  */
   location_t location;
   /* The value associated with this token, if any.  */
@@ -86,6 +100,11 @@ typedef struct GTY (()) cp_token {
   } GTY((desc ("(%1.type == CPP_TEMPLATE_ID) || (%1.type == CPP_NESTED_NAME_SPECIFIER)"))) u;
 } cp_token;
 
+DEF_VEC_O (cp_token);
+DEF_VEC_ALLOC_O (cp_token,gc);
+DEF_VEC_ALLOC_O (cp_token,heap);
+
+
 /* We use a stack of token pointer for saving token sets.  */
 typedef struct cp_token *cp_token_position;
 DEF_VEC_P (cp_token_position);
@@ -93,7 +112,7 @@ DEF_VEC_ALLOC_P (cp_token_position,heap)
 
 static cp_token eof_token =
 {
-  CPP_EOF, RID_MAX, 0, PRAGMA_NONE, false, 0, 0, { NULL }
+  CPP_EOF, RID_MAX, 0, PRAGMA_NONE, false, false, false, 0, { NULL }
 };
 
 /* The cp_lexer structure represents the C++ lexer.  It is responsible
@@ -104,10 +123,7 @@ static cp_token eof_token =
 typedef struct GTY (()) cp_lexer {
   /* The memory allocated for the buffer.  NULL if this lexer does not
      own the token buffer.  */
-  cp_token * GTY ((length ("%h.buffer_length"))) buffer;
-  /* If the lexer owns the buffer, this is the number of tokens in the
-     buffer.  */
-  size_t buffer_length;
+  VEC(cp_token,gc) *buffer;
 
   /* A pointer just past the last available token.  The tokens
      in this lexer are [buffer, last_token).  */
@@ -134,6 +150,9 @@ typedef struct GTY (()) cp_lexer {
   bool in_pragma;
 } cp_lexer;
 
+DEF_VEC_O (cp_lexer);
+DEF_VEC_ALLOC_O (cp_lexer,heap);
+
 /* cp_token_cache is a range of tokens.  There is no need to represent
    allocate heap memory for it, since tokens are never removed from the
    lexer's array.  There is also no need for the GC to walk through
@@ -148,6 +167,302 @@ typedef struct GTY(()) cp_token_cache {
   cp_token * GTY ((skip)) last;
 } cp_token_cache;
 
+typedef cp_token_cache *cp_token_cache_ptr;
+DEF_VEC_P (cp_token_cache_ptr);
+DEF_VEC_ALLOC_P (cp_token_cache_ptr,gc);
+
+struct cp_token_ident_d
+{
+  unsigned int ident_len;
+  const char *ident_str;
+  unsigned int before_len;
+  const char *before_str;
+  unsigned int after_len;
+  const char *after_str;
+};
+
+typedef struct cp_token_ident_d cp_token_ident;
+
+/* A set of contiguous tokens within a single file.  */
+typedef struct GTY(()) cp_token_hunk
+{
+  /* Captured identifier and macro state.  */
+  cpp_idents_used identifiers;
+
+  /* The array of tokens.  */
+  VEC(cp_token,gc) *buffer;
+
+  /* Offset into the libcpp file buffer where this token hunk starts at.
+     If this token hunk is invalidated, the lexer is repositioned at
+     this offset into the file to start lexing again.  */
+  cpp_offset text_offset;
+
+  /* Length of this hunk in characters.  When the tokens in this hunk
+     are moved into the lexer buffer, the original character stream
+     is advanced this many characters to avoid future lexing from
+     seeing these tokens again.  */
+  size_t text_length;
+} cp_token_hunk;
+
+typedef struct cp_token_hunk *cp_token_hunk_ptr;
+
+DEF_VEC_P (cp_token_hunk_ptr);
+DEF_VEC_ALLOC_P (cp_token_hunk_ptr,gc);
+
+/* Number of bytes in an MD5 signature.  */
+#define DIGEST_LEN	16
+
+struct pth_image;
+typedef struct pth_image *pth_image_ptr;
+DEF_VEC_P (pth_image_ptr);
+DEF_VEC_ALLOC_P (pth_image_ptr,gc);
+
+DEF_VEC_ALLOC_I(char,gc);
+
+/* Description of a #include command.  This records the #include type
+   used in the original source and the image included.  It is used
+   to restore a file from its character stream in case that the
+   associated image is found to be tainted.  */
+
+typedef struct GTY(()) pth_include
+{
+  /* The image to include.  */
+  struct pth_image *image;
+
+  /* The #include command used: #include, #include_next or #import.  */
+  enum include_type itype;
+
+  /* Nonzero if this include used angle brackets.  */
+  unsigned int angle_brackets : 1;
+
+  /* Name used in the command used in this #include command.  */
+  const char * GTY((skip)) iname;
+
+  /* Directory where INAME can be found.  This is not necessarily
+     the same as lbasename (IMAGE->FNAME), but it is always true that
+     DNAME/INAME == IMAGE->FNAME.  */
+  const char * GTY((skip)) dname;
+} pth_include;
+
+typedef struct pth_include *pth_include_ptr;
+DEF_VEC_P (pth_include_ptr);
+DEF_VEC_ALLOC_P (pth_include_ptr,gc);
+
+/* An entry in the incremental cache.  Each entry represents
+   a single file in the translation unit.  Tokens are saved in a linked
+   list of lexers, separated by file change events in the pre-processor.
+   Every time the preprocessor signals a file change, a new lexer for
+   this entry is created.  */
+
+typedef struct GTY(()) pth_image
+{
+  /* Full path name.  */
+  const char * GTY((skip)) fname;
+
+  /* MD5 sum for the contents of the original path name.  */
+  unsigned char digest[DIGEST_LEN];
+
+  /* Vector of token hunks corresponding to this file, and this file
+     only. If this file includes other files, their tokens will not be
+     stored here.  Each entry in this vector is a hunk of tokens
+     delimited by file change events.  For instance, if a file is 
+     laid out like this:
+
+      [ t1 t2 ... tN ]
+      #include "foo.h"
+      [ p1 p2 ... pM ]
+
+     The field token_caches will have two entries.  One for the hunk
+     [t1, tN], the second one for the hunk [p1, pM].  */
+  VEC(cp_token_hunk_ptr,gc) *token_hunks;
+
+  /* Vector of files included by this file.  */
+  VEC(pth_include_ptr,gc) *includes;
+
+  /* Sequencing string that describes how the elements from
+     TOKEN_HUNKS and INCLUDES are interleaved.  Each character of this
+     string is either 'H' (hunk) or 'I' (include).  This is used in
+     pth_image_to_lexer to know in what order should the hunks
+     and include files be processed.
+     
+     FIXME pph, using a character string is heavy handed.  A bitmap
+     would suffice, but for now this is easier and the space consumed
+     should not be too significant.  */
+  VEC(char,gc) *ih_sequence;
+
+  /* True if this image needs to be flushed out to disk.  */
+  unsigned int save_p : 1;
+
+  /* True if this image has been loaded from an image file.  */
+  unsigned int loaded_p: 1;
+
+  /* True if this image has been incorporated into the current
+     compilation context.  FIXME pph, should we keep track of this
+     at the hunk level?  */
+  unsigned int used_p : 1;
+
+  /* True if we have computed the MD5 digest for FNAME.  */
+  unsigned int digest_computed_p : 1;
+
+  /* Index into the lexer buffer where the next token hunk to be
+     created should start.  This is managed by the file changing
+     logic in pth_file_change.  */
+  unsigned hunk_start_ix;
+
+  /* Offset into the libcpp character buffer where the next token
+     hunk will be read from.  */
+  cpp_offset hunk_text_offset;
+
+  /* libcpp buffer associated with IMAGE->FNAME.  */
+  cpp_buffer * GTY((skip)) buffer;
+} pth_image;
+
+
+/* PTH state.  This holds all the data needed to manage the PTH cache.  */
+typedef struct GTY(()) pth_state
+{
+  /* The cache of pre-tokenized content.  */
+  VEC(pth_image_ptr,gc) *cache;
+
+  /* Pointer map for speeding up cache lookups.  */
+  htab_t GTY((param_is (struct pth_image))) cache_dir;
+
+  /* The current cache image being modified.  */
+  pth_image *cur_image;
+
+  /* Attributes for the most recent #include command found by
+     pth_include_handler.  */
+  enum include_type new_itype;
+  bool new_angle_brackets;
+  const char *new_iname;
+
+  /* The lexer used to pre-process the file.  */
+  cp_lexer *lexer;
+
+  /* Previously registered file change callback.  */
+  void (*file_change_prev) (cpp_reader *, const struct line_map *);
+} pth_state;
+
+
+/* Statistics on PTH.  */
+
+struct pth_stats_d
+{
+  /* Number of files processed as a regular #include.  */
+  size_t included_files;
+
+  /* Number of valid images.  */
+  size_t valid_images;
+
+  /* Number of token hunks seen.  */
+  size_t hunks;
+
+  /* Number of valid hunks copied from images.  */
+  size_t valid_hunks;
+
+  /* Number of hunks that failed dependency checks.  */
+  size_t invalid_hunks;
+
+  /* Number of verified hunks.  */
+  size_t verified_hunks;
+
+  /* Number of verified identifiers.  */
+  size_t verified_identifiers;
+};
+
+static struct pth_stats_d pth_stats;
+
+#define PTH_STATS_INCR(CNT,N)			\
+  	do {					\
+	    if (flag_pth_stats)			\
+	      pth_stats.CNT += (N);		\
+	} while (0)
+
+
+/* Statistics on PPH.  */
+
+struct pph_stats_d
+{
+  /* Number of tokens parsed in this TU.  */
+  size_t parsed_tokens;
+
+  /* Number of tokens in the lexer buffer.  */
+  size_t lexed_tokens;
+
+  /* Number of declarations copied into the parser cache.  */
+  size_t cached_decls;
+
+  /* Number of declarations restored from the parser cache.  */
+  size_t restored_decls;
+
+  /* Number of references rebound when going in/out of the cache.  */
+  size_t cached_refs;
+
+  /* Number of name lookups done by the parser.  */
+  size_t name_lookups;
+
+  /* Number of decl lookups that were changed to something weird.  */
+  size_t bad_lookups;
+};
+
+static struct pph_stats_d pph_stats;
+  
+#define PPH_STATS_INCR(CNT,N)			\
+  	do {					\
+	    if (flag_pph_stats)			\
+	      pph_stats.CNT += (N);		\
+	} while (0)
+
+
+/* Cache of token ranges for head of symbol declarations.  For each
+   *_DECL tree intercepted during parsing, we store the vector of
+   tokens that make up the head of the declaration for the node.  */
+static struct pointer_map_t *pph_decl_head_token_cache = NULL;
+
+/* Cache of token ranges for body of symbol declarations.  For each
+   *_DECL tree intercepted during parsing, we store the vector of
+   tokens that make up the body of the declaration for the node.  */
+static struct pointer_map_t *pph_decl_body_token_cache = NULL;
+
+/* Maps for tracking decl dependencies.  For each *_DECL tree intercepted
+   during parsing, we store the trees on which the node depends for
+   its declaration.  Two maps are kept, one for the head of the declaration
+   and another for its body.  */
+struct pph_decl_deps_d
+{
+  /* Symbol dependencies on the declaration header.  */
+  struct pointer_map_t *header;
+
+  /* Symbol dependencies on the declaration body.  */
+  struct pointer_map_t *body;
+};
+
+/* Map of decl dependencies.  */
+static struct pph_decl_deps_d *pph_decl_deps = NULL;
+
+/* Tree catcher for the incremental compiler.  Whenever this array is
+   allocated, make_node_stat() will push certain trees into this array.  */
+static VEC(tree,heap) *pph_tree_catcher = NULL;
+
+/* Catcher for name lookups.  This stores every name lookup performed
+   on identifiers while we are catching ASTs in the parser.  */
+static VEC(tree,heap) *pph_name_lookups = NULL;
+
+/* Since identifiers may be looked up more than once during parsing,
+   this set prevents duplicate symbols from being placed in
+   pph_name_lookups.  */
+static struct pointer_set_t *pph_name_lookups_set = NULL;
+
+/* This map stores the token locations where a given symbol was looked
+   up.  When an identifier is looked up and resolved to symbol S, we
+   check where the current token pointer is and save it in a vector
+   associated with S.  */
+static struct pointer_map_t *pph_nl_token_map = NULL;
+
+/* Log file where PPH analysis is written to.  Controlled by
+   -fpph_logfile.  If this flag is not given, stdout is used.  */
+static FILE *pph_logfile = NULL;
+
 /* The various kinds of non integral constant we encounter. */
 typedef enum non_integral_constant {
   NIC_NONE,
@@ -272,6 +587,8 @@ typedef enum required_token {
 
 /* Prototypes.  */
 
+static void pph_print_trees_tokens (VEC(tree,heap) *, cp_token *, cp_token *);
+
 static cp_lexer *cp_lexer_new_main
   (void);
 static cp_lexer *cp_lexer_new_from_tokens
@@ -323,8 +640,9 @@ static void cp_lexer_stop_debugging
    (fputs, fprintf).  It will never be used, so all we need is a value
    of the right type that's guaranteed not to be NULL.  */
 #define cp_lexer_debug_stream stdout
-#define cp_lexer_print_token(str, tok) (void) 0
 #define cp_lexer_debugging_p(lexer) 0
+static void cp_lexer_print_token
+  (FILE *, cp_token *);
 #endif /* ENABLE_CHECKING */
 
 static cp_token_cache *cp_token_cache_new
@@ -353,13 +671,8 @@ static void cp_parser_initial_pragma
    cp_parser_nested_name_specifier_opt.  */
 #define CPP_NESTED_NAME_SPECIFIER ((enum cpp_ttype) (CPP_TEMPLATE_ID + 1))
 
-/* A token type for tokens that are not tokens at all; these are used
-   to represent slots in the array where there used to be a token
-   that has now been deleted.  */
-#define CPP_PURGED ((enum cpp_ttype) (CPP_NESTED_NAME_SPECIFIER + 1))
-
 /* The number of token types, including C++-specific ones.  */
-#define N_CP_TTYPES ((int) (CPP_PURGED + 1))
+#define N_CP_TTYPES ((int) (CPP_NESTED_NAME_SPECIFIER + 1))
 
 /* Variables.  */
 
@@ -372,23 +685,2221 @@ static FILE *cp_lexer_debug_stream;
    sizeof, typeof, or alignof.  */
 int cp_unevaluated_operand;
 
-/* Create a new main C++ lexer, the lexer that gets tokens from the
-   preprocessor.  */
+/* Dump up to NUM tokens in BUFFER to FILE.  If NUM is 0, dump all the
+   tokens.  */
+
+static void
+cp_lexer_dump_tokens (FILE *file, VEC(cp_token,gc) *buffer, unsigned num)
+{
+  unsigned i;
+  cp_token *token;
+
+  fprintf (file, "%u tokens\n", VEC_length (cp_token, buffer));
+
+  if (num == 0)
+    num = VEC_length (cp_token, buffer);
+
+  for (i = 0; VEC_iterate (cp_token, buffer, i, token) && i < num; i++)
+    {
+      cp_lexer_print_token (file, token);
+      switch (token->type)
+	{
+	  case CPP_SEMICOLON:
+	  case CPP_OPEN_BRACE:
+	  case CPP_CLOSE_BRACE:
+	  case CPP_EOF:
+	    fputc ('\n', file);
+	    break;
+
+	  default:
+	    fputc (' ', file);
+	}
+    }
+
+  if (i == num && i < VEC_length (cp_token, buffer))
+    {
+      fprintf (file, " ... ");
+      cp_lexer_print_token (file, VEC_index (cp_token, buffer,
+			    VEC_length (cp_token, buffer) - 1));
+    }
+
+  fprintf (file, "\n");
+}
+
+
+/* Dump all tokens in BUFFER to stderr.  */
+
+static void
+cp_lexer_debug_tokens (VEC(cp_token,gc) *buffer)
+{
+  cp_lexer_dump_tokens (stderr, buffer, 0);
+}
+
+/* Return true if path P1 and path P2 point to the same file.  */
+
+static inline bool
+pathnames_equal_p (const char *p1, const char *p2)
+{
+  return strcmp (lrealpath (p1), lrealpath (p2)) == 0;
+}
+
+/* Expand and print location LOC to FILE.  If FILE is NULL, pph_logfile
+   is used.  */
+
+static void
+pph_debug_location (FILE *file, location_t loc)
+{
+  expanded_location xloc;
+  file = (file) ? file : pph_logfile;
+  xloc = expand_location (loc);
+  fprintf (file, "%s:%d:%d", lrealpath (xloc.file), xloc.line, xloc.column);
+}
+
+
+/* Expand and print the location of tree T to FILE.  If FILE is NULL,
+   pph_logfile is used.  */
+
+static void
+pph_debug_loc_of_tree (FILE *file, tree t)
+{
+  pph_debug_location (file, DECL_SOURCE_LOCATION (t));
+}
+
+
+/* Hash and comparison functions for the directory of cached images.  */
+
+static hashval_t
+pth_image_dir_hash (const void *p)
+{
+  const char *s = lrealpath (((const pth_image *)p)->fname);
+  return htab_hash_string (s);
+}
+
+
+static int
+pth_image_dir_eq (const void *p1, const void *p2)
+{
+  const char *s1 = ((const pth_image *)p1)->fname;
+  const char *s2 = ((const pth_image *)p2)->fname;
+  return pathnames_equal_p (s1, s2);
+}
+
+
+
+static GTY(()) pth_state *pth_global_state = NULL;
+
+/* Return the global PTH state where the cache and its directory
+   are stored.  */
+
+static pth_state *
+pth_get_state (void)
+{
+  if (pth_global_state == NULL)
+    {
+      pth_global_state = ggc_alloc_cleared_pth_state ();
+      pth_global_state->cache_dir = htab_create_ggc (10, pth_image_dir_hash,
+				                     pth_image_dir_eq, NULL);
+    }
+
+  return pth_global_state;
+}
+
+
+/* Return an identification string for a PTH image.  */
+
+static const char *
+pth_id_str (void)
+{
+  /* FIXME pph - Build a better identification string.  */
+  return "PTH0x42";
+}
+
+
+/* Return the number of bytes taken by the header of a PTH image.  */
+
+static size_t
+pth_header_len (void)
+{
+  /* The header of a PTH image contains:
+	- An identification string (pth_id_str ())
+	- The MD5 digest for the source file associated with the image.
+   */
+  return strlen (pth_id_str ()) + DIGEST_LEN;
+}
+
+
+#define PTH_EXTENSION ".pth"
+
+/* Return a new string with the extension PTH_EXTENSION appended to NAME.  The
+   caller is responsible for freeing the returned string.  */
+
+static char *
+pth_name_for (const char *name)
+{
+  size_t i, len;
+  char *s;
+  
+  len = strlen (name) + sizeof (PTH_EXTENSION) + 1;
+  s = XCNEWVEC (char, len);
+  sprintf (s, "%s" PTH_EXTENSION, name);
+
+  /* Make the file name unique and store it in the current directory.  */
+  for (i = 0; i < len - sizeof (PTH_EXTENSION) - 1; i++)
+    if (!ISALNUM (s[i]))
+      s[i] = '_';
+
+  return s;
+}
+
+
+/* Open an image file for path NAME with MODE.  */
+
+static FILE *
+pth_file_for (const char *name, const char *mode)
+{
+  char *s;
+  FILE *f;
+
+  s = pth_name_for (name);
+  f = fopen (s, mode);
+  if (f == NULL)
+    fatal_error ("can%'t open token stream file %s: %m", s);
+  free (s);
+
+  return f;
+}
+
+
+/* Compute the MD5 digest for FNAME.  Store it in DIGEST.  */
+
+static void
+pth_get_md5_digest (const char *fname, unsigned char digest[DIGEST_LEN])
+{
+  if (flag_pth_md5)
+    {
+      FILE *f;
+
+      timevar_push (TV_PTH_MD5);
+
+      f = fopen (fname, "rb");
+      if (f == NULL)
+	fatal_error ("Cannot open %s for computing its digest: %m", fname);
+
+      md5_stream (f, digest);
+
+      fclose (f);
+
+      timevar_pop (TV_PTH_MD5);
+    }
+  else
+    {
+      struct stat buf;
+
+      if (stat (fname,&buf) != 0)
+	fatal_error ("Cannot stat %s: %m", fname);
+
+      memset (digest, 0, DIGEST_LEN);
+      memcpy (digest, &buf.st_mtime, sizeof (buf.st_mtime));
+    }
+}
+
+
+/* Compute an index value for TYPE suitable for restoring it later
+   from global_trees[] or integer_types.  The index is saved
+   in TYPE_IX_P and the number category (one of CPP_N_INTEGER,
+   CPP_N_FLOATING, etc) is saved in CATEGORY_P.  */
+
+static void
+pth_get_index_from_type (tree type, unsigned *type_ix_p, unsigned *category_p)
+{
+  void **val_p;
+  static struct pointer_map_t *type_cache = NULL;
+
+  /* For complex types we will just use the type of the components.  */
+  if (TREE_CODE (type) == COMPLEX_TYPE)
+    {
+      *type_ix_p = 0;
+      *category_p = CPP_N_IMAGINARY;
+      return;
+    }
+
+  if (type_cache == NULL)
+    type_cache = pointer_map_create ();
+
+  val_p = pointer_map_contains (type_cache, type);
+  if (val_p)
+    *type_ix_p = *((unsigned *) val_p);
+  else
+    {
+      if (CP_INTEGRAL_TYPE_P (type))
+	{
+	  unsigned i;
+	  for (i = itk_char; i < itk_none; i++)
+	    if (type == integer_types[i])
+	      {
+		*type_ix_p = (unsigned) i;
+		break;
+	      }
+
+	  gcc_assert (i != itk_none);
+	}
+      else if (FLOAT_TYPE_P (type) || FIXED_POINT_TYPE_P (type))
+	{
+	  unsigned i;
+
+	  for (i = TI_ERROR_MARK; i < TI_MAX; i++)
+	    if (global_trees[i] == type)
+	      {
+		*type_ix_p = (unsigned) i;
+		break;
+	      }
+
+	  gcc_assert (i != TI_MAX);
+	}
+      else
+	gcc_unreachable ();
+    }
+
+  if (CP_INTEGRAL_TYPE_P (type))
+    *category_p = CPP_N_INTEGER;
+  else if (FLOAT_TYPE_P (type))
+    *category_p = CPP_N_FLOATING;
+  else if (FIXED_POINT_TYPE_P (type))
+    *category_p = CPP_N_FRACT;
+  else
+    gcc_unreachable ();
+}
+
+
+/* Write a uint VALUE to the STREAM.  Return the number of bytes written.  */
+
+static inline size_t
+pth_write_uint (unsigned int value, FILE *stream)
+{
+  size_t sent = fwrite (&value, 1, sizeof (value), stream);
+  gcc_assert (sent == sizeof (value));
+  return sent;
+}
+
+
+/* Write a size_t VALUE to the STREAM.  Return the number of bytes written.  */
+
+static inline size_t
+pth_write_sizet (size_t value, FILE *stream)
+{
+  size_t sent = fwrite (&value, 1, sizeof (value), stream);
+  gcc_assert (sent == sizeof (value));
+  return sent;
+}
+
+
+/* Write N bytes from P to STREAM.  */
+
+static inline size_t
+pth_write_bytes (const void *p, size_t n, FILE *stream)
+{
+  size_t sent = fwrite (p, 1, n, stream);
+  gcc_assert (sent == n);
+  return sent;
+}
+
+
+/* Write string STR and its LENGTH to STREAM.  */
+
+static inline size_t
+pth_write_string (const char *str, unsigned int length, FILE *stream)
+{
+  size_t sent;
+
+  if (str == NULL)
+    sent = pth_write_uint (-1, stream);
+  else
+    {
+      sent = pth_write_uint (length, stream);
+      if (length > 0)
+	sent += pth_write_bytes (str, length, stream);
+    }
+
+  return sent;
+}
+
+
+/* Save the number VAL to file F.  Return the number of bytes written.  */
+
+static size_t
+pth_write_number (tree val, FILE *f)
+{
+  unsigned type_idx, type_kind;
+  size_t nbytes;
+
+  pth_get_index_from_type (TREE_TYPE (val), &type_idx, &type_kind);
+
+  nbytes = 0;
+  nbytes += pth_write_uint (type_idx, f);
+  nbytes += pth_write_uint (type_kind, f);
+
+  if (type_kind == CPP_N_INTEGER)
+    {
+      HOST_WIDE_INT v[2];
+
+      v[0] = TREE_INT_CST_LOW (val);
+      v[1] = TREE_INT_CST_HIGH (val);
+      nbytes += pth_write_bytes (v, 2 * sizeof (HOST_WIDE_INT), f);
+    }
+  else if (type_kind == CPP_N_FLOATING)
+    {
+      REAL_VALUE_TYPE r = TREE_REAL_CST (val);
+      nbytes += pth_write_bytes (&r, sizeof (REAL_VALUE_TYPE), f);
+    }
+  else if (type_kind == CPP_N_FRACT)
+    {
+      FIXED_VALUE_TYPE fv = TREE_FIXED_CST (val);
+      nbytes += pth_write_bytes (&fv, sizeof (FIXED_VALUE_TYPE), f);
+    }
+  else if (type_kind == CPP_N_IMAGINARY)
+    {
+      pth_write_number (TREE_REALPART (val), f);
+      pth_write_number (TREE_IMAGPART (val), f);
+    }
+  else
+    gcc_unreachable ();
+
+  return nbytes;
+}
+
+
+/* Save the tree associated with TOKEN to file F.  Return the number
+   of bytes written.  */
+
+static size_t
+pth_save_token_value (cp_token *token, FILE *f)
+{
+  const char *str;
+  size_t nbytes;
+  unsigned len;
+  tree val;
+
+  val = token->u.value;
+  nbytes = 0;
+  switch (token->type)
+    {
+      case CPP_TEMPLATE_ID:
+      case CPP_NESTED_NAME_SPECIFIER:
+	break;
+
+      case CPP_NAME:
+	/* FIXME pph.  Hash the strings and emit a string table.  */
+	str = IDENTIFIER_POINTER (val);
+	len = IDENTIFIER_LENGTH (val);
+	nbytes += pth_write_string (str, len, f);
+	break;
+
+      case CPP_KEYWORD:
+	/* Nothing to do.  We will reconstruct the keyword from
+	   ridpointers[token->keyword] at load time.  */
+	break;
+
+      case CPP_CHAR:
+      case CPP_WCHAR:
+      case CPP_CHAR16:
+      case CPP_CHAR32:
+      case CPP_NUMBER:
+	nbytes += pth_write_number (val, f);
+	break;
+
+      case CPP_STRING:
+      case CPP_WSTRING:
+      case CPP_STRING16:
+      case CPP_STRING32:
+	/* FIXME pph.  Need to represent the type.  */
+	str = TREE_STRING_POINTER (val);
+	len = TREE_STRING_LENGTH (val);
+	nbytes += pth_write_string (str, len, f);
+	break;
+
+      case CPP_PRAGMA:
+	/* Nothing to do.  Field pragma_kind has already been written.  */
+	break;
+
+      default:
+	gcc_assert (token->u.value == NULL);
+	nbytes += pth_write_bytes (&token->u.value, sizeof (token->u.value), f);
+    }
+
+  return nbytes;
+}
+
+
+/* Save TOKEN on file F.  Return the number of bytes written on F.  */
+
+static size_t
+pth_save_token (cp_token *token, FILE *f)
+{
+  size_t nbytes;
+
+  /* Do not write out the final field in TOKEN.  It contains
+     pointers that need to be pickled separately.
+
+     FIXME pph - Need to also emit the location_t table so we can
+     reconstruct it when reading the PTH state.  */
+  nbytes = pth_write_bytes (token, sizeof (cp_token) - sizeof (void *), f);
+  nbytes += pth_save_token_value (token, f);
+
+  return nbytes;
+}
+
+
+/* Write header information for IMAGE to STREAM.  */
+
+static void
+pth_write_header (pth_image *image, FILE *stream)
+{
+  size_t nbytes;
+  const char *id = pth_id_str ();
+
+  if (!image->digest_computed_p)
+    {
+      pth_get_md5_digest (image->fname, image->digest);
+      image->digest_computed_p = true;
+    }
+
+  nbytes = pth_write_bytes (id, strlen (id), stream);
+  nbytes += pth_write_bytes (image->digest, DIGEST_LEN, stream);
+
+  gcc_assert (nbytes == pth_header_len ());
+}
+
+/* Dump a table of IDENTIFIERS to the STREAM. */
+
+static void
+pth_dump_identifiers (FILE *stream, cpp_idents_used *identifiers)
+{
+  unsigned int idx, col = 1;
+
+  fprintf (stream, "%u identifiers up to %u chars\n",
+           identifiers->num_entries, identifiers->max_length);
+  for (idx = 0; idx < identifiers->num_entries; ++idx)
+    {
+      cpp_ident_use *ident = identifiers->entries + idx;
+
+      if (col + ident->ident_len >= 80)
+        {
+          fprintf (stream, "\n");
+          col = 1;
+        }
+      if (ident->before_str || ident->after_str)
+        {
+          if (col > 1)
+            fprintf (stream, "\n");
+          fprintf (stream, " %s = %s -> %s\n", ident->ident_str,
+                   ident->before_str, ident->after_str);
+          col = 1;
+        }
+      else
+        {
+          fprintf (stream, " %s", ident->ident_str);
+          col += ident->ident_len;
+        }
+    }
+  fprintf (stream, "\n");
+}
+
+/* Dump a debug log of the IDENTIFIERS. */
+
+void
+pth_debug_identifiers (cpp_idents_used *identifiers);
+
+void
+pth_debug_identifiers (cpp_idents_used *identifiers)
+{
+  pth_dump_identifiers (stderr, identifiers);
+}
+
+/* Dump a HUNK to the STREAM. */
+
+static void
+pth_dump_hunk (FILE *stream, cp_token_hunk *hunk)
+{
+  fprintf (stream, "Hunk location: { %lu, %lu, %lu }\n", hunk->text_offset.cur,
+	   hunk->text_offset.line_base, hunk->text_offset.next_line);
+  fprintf (stream, "Hunk length:   %lu characters\n", hunk->text_length);
+  pth_dump_identifiers (stream, &hunk->identifiers);
+  cp_lexer_dump_tokens (stream, hunk->buffer, 0);
+}
+
+/* Dump a debug log of the HUNK. */
+
+static void
+pth_debug_hunk (cp_token_hunk *hunk)
+{
+  pth_dump_hunk (stderr, hunk);
+}
+
+
+/* Dump #include command INCLUDE to FILE.  */
+
+static void
+pth_dump_include (FILE *f, pth_include *include)
+{
+  if (include == NULL)
+    return;
+
+  if (include->itype == IT_INCLUDE)
+    fprintf (f, "#include ");
+  else if (include->itype == IT_INCLUDE_NEXT)
+    fprintf (f, "#include_next ");
+  else if (include->itype == IT_IMPORT)
+    fprintf (f, "#import ");
+  else
+    fprintf (f, "#??? ");
+
+  fprintf (f, "%c%s%c",
+	   (include->angle_brackets) ? '<' : '"',
+	   include->iname,
+	   (include->angle_brackets) ? '>' : '"');
+
+  fprintf (f, " (found in %s)\n", include->dname);
+}
+
+
+/* Dump #include command INCLUDE to stderr.  */
+
+static void
+pth_debug_include (pth_include *include)
+{
+  pth_dump_include (stderr, include);
+}
+
+
+/* Recursive helper for pth_dump_token_hunks_1.  VISITED keeps track of
+   images that have already been dumped to avoid infinite recursion.  */
+
+static void
+pth_dump_token_hunks_1 (FILE *f, pth_image *image,
+		        struct pointer_set_t *visited)
+{
+  unsigned i, h_ix, i_ix;
+  char s;
+
+  if (pointer_set_insert (visited, image))
+    return;
+
+  fprintf (f, "LC_ENTER: %s {\n", image->fname);
+
+  for (i = 0, h_ix = 0, i_ix = 0;
+       VEC_iterate (char, image->ih_sequence, i, s);
+       i++)
+    {
+      if (s == 'H')
+	{
+	  cp_token_hunk *hunk;
+	  hunk = VEC_index (cp_token_hunk_ptr, image->token_hunks, h_ix++);
+	  pth_dump_hunk (f, hunk);
+	}
+      else if (s == 'I')
+	{
+	  pth_include *include;
+	  include = VEC_index (pth_include_ptr, image->includes, i_ix++);
+	  pth_dump_include (f, include);
+	  pth_dump_token_hunks_1 (f, include->image, visited);
+	}
+    }
+
+  fprintf (f, "LC_LEAVE: %s }\n", image->fname);
+}
+
+
+/* Dump all the tokens in IMAGE and the files included by it to F.  */
+
+static void
+pth_dump_token_hunks (FILE *f, pth_image *image)
+{
+  struct pointer_set_t *visited = pointer_set_create ();
+  pth_dump_token_hunks_1 (f, image, visited);
+  pointer_set_destroy (visited);
+}
+
+
+/* Dump all the tokens in IMAGE and the files included by it to stderr.  */
+
+static void
+pth_debug_token_hunks (pth_image *image)
+{
+  pth_dump_token_hunks (stderr, image);
+}
+
+
+/* Dump a debugging representation of IMAGE to F.  */
+
+static void
+pth_dump_image (FILE *f, pth_image *image)
+{
+  unsigned i;
+  cp_token_hunk *hunk;
+  pth_include *include;
+  char s;
+
+  if (image == NULL)
+    return;
+
+  fprintf (f, "Image for: %s\n", image->fname);
+
+  fprintf (f, "  MD5 digest: ");
+  if (image->digest_computed_p)
+    {
+      for (i = 0; i < DIGEST_LEN; i++)
+	fprintf (f, "%02x", image->digest[i]);
+      fprintf (f, "\n");
+    }
+  else
+    fprintf (f, "NOT COMPUTED\n");
+
+  fprintf (f, "  %u token hunks: { ",
+	   VEC_length (cp_token_hunk_ptr, image->token_hunks));
+  for (i = 0; VEC_iterate (cp_token_hunk_ptr, image->token_hunks, i, hunk); i++)
+    fprintf (f, "%u ", VEC_length (cp_token, hunk->buffer));
+  fprintf (f, "}\n");
+
+  fprintf (f, "  %u includes:\n",
+	   VEC_length (pth_include_ptr, image->includes));
+  for (i = 0; VEC_iterate (pth_include_ptr, image->includes, i, include); i++)
+    {
+      fprintf (f, "\t");
+      pth_dump_include (f, include);
+    }
+
+  fprintf (f, "  Include-Hunk (IH) sequence:          ");
+  for (i = 0; VEC_iterate (char, image->ih_sequence, i, s); i++)
+    fputc (s, f);
+  fputc ('\n', f);
+
+  if (image->loaded_p)
+    fprintf (f, "  Instantiated from image: %s\n", pth_name_for (image->fname));
+  else
+    {
+      fprintf (f, "  Instantiated from character stream: %s\n", image->fname);
+
+      if (image->save_p)
+	fprintf (f, "  Will be saved to image: %s\n",
+		 pth_name_for (image->fname));
+      else
+	fprintf (f, "  Will NOT be saved to an image (not include-guarded)\n");
+    }
+
+  if (image->used_p)
+    fprintf (f, "  Image already applied to current compilation context\n");
+
+  if (flag_pth_debug >= 4)
+    pth_dump_token_hunks (f, image);
+}
+
+
+/* Dump a debugging representation of IMAGE to stderr.  */
+
+static void
+pth_debug_image (pth_image *image)
+{
+  pth_dump_image (stderr, image);
+}
+
+
+/* Show statistics for PTH image IMAGE on FILE.  if FILE is NULL, use
+   pph_logfile.  */
+
+static void
+pth_show_image_stats (FILE *file, pth_image *image)
+{
+  unsigned i, num_tokens;
+  cp_token_hunk *hunk;
+
+  if (image == NULL)
+    return;
+
+  if (file == NULL)
+    file = pph_logfile;
+
+  num_tokens = 0;
+  for (i = 0; VEC_iterate (cp_token_hunk_ptr, image->token_hunks, i, hunk); i++)
+    num_tokens += VEC_length (cp_token, hunk->buffer);
+
+  fprintf (file, "%s: %u tokens, %u token hunks\n", image->fname, num_tokens,
+	   VEC_length (cp_token_hunk_ptr, image->token_hunks));
+}
+
+
+/* Dump the current PTH state to F.  */
+
+static void
+pth_dump_state (FILE *f)
+{
+  unsigned i;
+  pth_state *state;
+  pth_image *image;
+  
+  state = pth_get_state ();
+  fprintf (f, "Incremental compilation state\n\n");
+  fprintf (f, "%u file images\n", VEC_length (pth_image_ptr, state->cache));
+  for (i = 0; VEC_iterate (pth_image_ptr, state->cache, i, image); i++)
+    pth_dump_image (f, image);
+
+  fprintf (f, "\nCurrent image being processed: %s\n",
+	   (state->cur_image) ? state->cur_image->fname : "NONE");
+
+  if (state->lexer)
+    {
+      fprintf (f, "Tokens in main lexer:\n");
+      cp_lexer_dump_tokens (f, state->lexer->buffer, 0);
+    }
+}
+
+
+/* Dump the current PTH state to stderr.  */
+
+static void
+pth_debug_state (void)
+{
+  pth_dump_state (stderr);
+}
+
+
+/* Save the IDENTIFIERS to the STREAM.  */
+
+static void
+pth_save_identifiers (cpp_idents_used *identifiers, FILE *stream)
+{
+  unsigned int num_entries, id;
+
+  num_entries = identifiers->num_entries;
+  pth_write_uint (identifiers->max_length, stream);
+  pth_write_uint (num_entries, stream);
+
+  for ( id = 0; id < num_entries; ++id )
+    {
+      cpp_ident_use *entry = identifiers->entries + id;
+
+      gcc_assert (entry->ident_len <= identifiers->max_length);
+      pth_write_string (entry->ident_str, entry->ident_len, stream);
+
+      gcc_assert (entry->before_len <= identifiers->max_length);
+      pth_write_string (entry->before_str, entry->before_len, stream);
+
+      gcc_assert (entry->after_len <= identifiers->max_length);
+      pth_write_string (entry->after_str, entry->after_len, stream);
+    }
+}
+
+/* Save the HUNK to the STREAM.  */
+
+static void
+pth_save_hunk (cp_token_hunk *hunk, FILE *stream)
+{
+  unsigned j;
+  cp_token *token;
+
+  if (flag_pth_debug >= 5)
+    pth_debug_hunk (hunk);
+
+  /* Write out the identifiers used by HUNK.  */
+  pth_save_identifiers (&hunk->identifiers, stream);
+
+  /* Write the offset into the text file where this token starts.  */
+  pth_write_sizet (hunk->text_offset.cur, stream);
+  pth_write_sizet (hunk->text_offset.line_base, stream);
+  pth_write_sizet (hunk->text_offset.next_line, stream);
+
+  /* Write the length of the hunk.  */
+  pth_write_sizet (hunk->text_length, stream);
+
+  /* Write the number of tokens in HUNK.  */
+  pth_write_uint (VEC_length (cp_token, hunk->buffer), stream);
+
+  /* Write the tokens.  */
+  for (j = 0; VEC_iterate (cp_token, hunk->buffer, j, token); j++)
+    pth_save_token (token, stream);
+}
+
+
+/* Save the #include directive INCLUDE to STREAM.  */
+
+static void
+pth_save_include (pth_include *include, FILE *stream)
+{
+  pth_write_string (include->image->fname, strlen (include->image->fname),
+		    stream);
+  pth_write_uint ((unsigned int) include->itype, stream);
+  pth_write_uint (include->angle_brackets, stream);
+  pth_write_string (include->iname, strlen (include->iname), stream);
+  pth_write_string (include->dname,
+		    include->dname ? strlen (include->dname) : 0,
+		    stream);
+}
+
+
+/* Save the PTH image IMAGE to a file.  */
+
+static void
+pth_save_image (pth_image *image)
+{
+  FILE *stream;
+  cp_token_hunk *hunk;
+  unsigned i, num;
+  char *buf;
+  pth_include *include;
+
+  timevar_push (TV_PTH_SAVE);
+
+  /* Open the stream in append mode since we have already created
+     it in pth_new_image.  */
+  stream = pth_file_for (image->fname, "wb");
+
+  /* Write an invalid header first to avoid leaving a seemingly
+     valid file in case of failure.  */
+  buf = XCNEWVEC (char, pth_header_len ());
+  pth_write_bytes (buf, pth_header_len (), stream);
+
+  /* Write the include-hunk (IH) sequencing vector.  */
+  num = VEC_length (char, image->ih_sequence);
+  pth_write_uint (num, stream);
+  if (num > 0)
+    pth_write_bytes (VEC_address (char, image->ih_sequence), num, stream);
+  
+  /* Write the number of #include commands.  */
+  pth_write_uint (VEC_length (pth_include_ptr, image->includes), stream);
+
+  /* Write all the #include commands used by IMAGE.  */
+  for (i = 0; VEC_iterate (pth_include_ptr, image->includes, i, include); i++)
+    pth_save_include (include, stream);
+
+  /* Write the number of token caches in the cache.  */
+  pth_write_uint (VEC_length (cp_token_hunk_ptr, image->token_hunks), stream);
+
+  /* Write all the token hunks in image.  */
+  for (i = 0; VEC_iterate (cp_token_hunk_ptr, image->token_hunks, i, hunk); i++)
+    pth_save_hunk (hunk, stream);
+
+  /* Now write a valid header.  */
+  fseek (stream, 0, SEEK_SET);
+  pth_write_header (image, stream);
+
+  /* Clean up.  */
+  fclose (stream);
+  free (buf);
+  image->save_p = false;
+
+  if (flag_pth_debug >= 3)
+    {
+      fprintf (stderr, "\nSaved image for %s:\n", image->fname);
+      pth_debug_image (image);
+    }
+
+  timevar_pop (TV_PTH_SAVE);
+}
+
+
+/* Given a type index TYPE_IDX and TYPE_KIND specifying the kind of type,
+   return a type from integer_types or global_trees.  */
+
+static tree
+pth_get_type_from_index (unsigned type_idx, unsigned type_kind)
+{
+  if (type_kind == CPP_N_INTEGER)
+    return integer_types[type_idx];
+  else if (type_kind == CPP_N_FLOATING || type_kind == CPP_N_FRACT)
+    return global_trees[type_idx];
+  else if (type_kind == CPP_N_IMAGINARY)
+    {
+      /* We don't need a type for the complex number.  The type is
+	 associated with the real and imaginary parts.  */
+      return NULL_TREE;
+    }
+  else
+    gcc_unreachable ();
+}
+
+
+/* Read an unsigned int into *VAR_P.  */
+
+static void
+pth_read_uint (unsigned int *var_p, FILE *stream)
+{
+  size_t received = fread (var_p, sizeof *var_p, 1, stream);
+  gcc_assert (received == 1);
+}
+
+
+/* Read a size_t into *VAR_P.  */
+
+static void
+pth_read_sizet (size_t *var_p, FILE *stream)
+{
+  size_t received = fread (var_p, sizeof *var_p, 1, stream);
+  gcc_assert (received == 1);
+}
+
+
+/* Read N bytes into P from STREAM.  The caller is responsible
+   for allocating sufficient memory for P.  */
+
+static inline void
+pth_read_bytes (void *p, size_t n, FILE *stream)
+{
+  size_t received = fread (p, 1, n, stream);
+  gcc_assert (received == n);
+}
+
+
+/* Read a string of up to MAX characters from STREAM into BUFFER.
+   Return the actual string length read from STREAM.  The caller is
+   responsible for allocating sufficient memory for BUFFER.  */
+
+static unsigned int
+pth_read_string (char *buffer, unsigned int max, FILE *stream)
+{
+  unsigned int length;
+  size_t received;
+  
+  received = fread (&length, sizeof length, 1, stream);
+  gcc_assert (received == 1 && (length == -1U || length <= max));
+  if (length > 0 && length != -1U)
+    {
+      received = fread (buffer, 1, length, stream);
+      gcc_assert (received == length);
+    }
+
+  return length;
+}
+
+
+/* Read a string from STREAM allocating enough memory on the 
+   heap to hold it.
+
+   This function assumes that strings are represented as a length
+   followed by the string content.  A terminating '\0' is added
+   automatically.  */
+
+static inline char *
+pth_read_string_alloc (FILE *stream)
+{
+  char *s;
+  unsigned int len;
+
+  pth_read_uint (&len, stream);
+
+  /* By convention, NULL strings are represented with length -1U.  */
+  if (len == -1U)
+    return NULL;
+
+  s = XCNEWVEC (char, len + 1);
+  pth_read_bytes (s, len, stream);
+
+  return s;
+}
+
+
+/* Load a numeric value from file F.  Return the corresponding tree.  */
+
+static tree
+pth_load_number (FILE *f)
+{
+  unsigned type_idx, type_kind;
+  tree type, val;
+
+  pth_read_uint (&type_idx, f);
+  pth_read_uint (&type_kind, f);
+
+  type = pth_get_type_from_index (type_idx, type_kind);
+
+  if (type_kind == CPP_N_INTEGER)
+    {
+      HOST_WIDE_INT v[2];
+      pth_read_bytes (v, 2 * sizeof (HOST_WIDE_INT), f);
+      val = build_int_cst_wide (type, v[0], v[1]);
+    }
+  else if (type_kind == CPP_N_FLOATING)
+    {
+      REAL_VALUE_TYPE r;
+      pth_read_bytes (&r, sizeof (REAL_VALUE_TYPE), f);
+      val = build_real (type, r);
+    }
+  else if (type_kind == CPP_N_FRACT)
+    {
+      FIXED_VALUE_TYPE fv;
+      pth_read_bytes (&fv, sizeof (FIXED_VALUE_TYPE), f);
+      val = build_fixed (type, fv);
+    }
+  else if (type_kind == CPP_N_IMAGINARY)
+    {
+      tree r = pth_load_number (f);
+      tree i = pth_load_number (f);
+      val = build_complex (NULL_TREE, r, i);
+    }
+  else
+    gcc_unreachable ();
+
+  return val;
+}
+
+
+/* Load the tree value associated with TOKEN to file F.  */
+
+static void
+pth_load_token_value (cp_token *token, FILE *f)
+{
+  char *str;
+
+  switch (token->type)
+    {
+      case CPP_TEMPLATE_ID:
+      case CPP_NESTED_NAME_SPECIFIER:
+	break;
+
+      case CPP_NAME:
+	str = pth_read_string_alloc (f);
+	token->u.value = get_identifier (str);
+	free (str);
+	break;
+
+      case CPP_KEYWORD:
+	token->u.value = ridpointers[token->keyword];
+	break;
+
+      case CPP_CHAR:
+      case CPP_WCHAR:
+      case CPP_CHAR16:
+      case CPP_CHAR32:
+      case CPP_NUMBER:
+	token->u.value = pth_load_number (f);
+	break;
+
+      case CPP_STRING:
+      case CPP_WSTRING:
+      case CPP_STRING16:
+      case CPP_STRING32:
+	str = pth_read_string_alloc (f);
+	token->u.value = build_string (strlen (str), str);
+	free (str);
+	break;
+
+      case CPP_PRAGMA:
+	/* Nothing to do.  Field pragma_kind has already been loaded.  */
+	break;
+
+      default:
+	pth_read_bytes (&token->u.value, sizeof (token->u.value), f);
+	gcc_assert (token->u.value == NULL);
+    }
+}
+
+
+/* Need forward declaration for pth_image_lookup due to circular
+   dependency.  */
+static pth_image *pth_image_lookup (pth_state *, const char *, cpp_reader *);
+
+
+/* Load the IDENTIFERS for a hunk from a STREAM.  */
+
+static void
+pth_load_identifiers (cpp_idents_used *identifiers, FILE *stream)
+{
+  unsigned int j;
+  unsigned int max_length, num_entries;
+  char *buffer;
+  unsigned int ident_len, before_len, after_len;
+
+  pth_read_uint (&max_length, stream);
+  identifiers->max_length = max_length;
+  pth_read_uint (&num_entries, stream);
+  identifiers->num_entries = num_entries;
+  identifiers->entries = XCNEWVEC (cpp_ident_use, num_entries);
+  identifiers->strings = XCNEW (struct obstack);
+
+  /* Strings need no alignment.  */
+  _obstack_begin (identifiers->strings, 0, 0,
+                  (void *(*) (long)) xmalloc,
+                  (void (*) (void *)) free);
+  obstack_alignment_mask (identifiers->strings) = 0;
+  /* FIXME pph: We probably need to free all these things somewhere.  */
+
+  buffer = XCNEWVEC (char, max_length + 1);
+
+  /* Read the identifiers in HUNK. */
+  for (j = 0; j < num_entries; ++j)
+    {
+      ident_len = pth_read_string ( buffer, max_length, stream);
+      gcc_assert (ident_len > 0 && ident_len != -1U);
+      identifiers->entries[j].ident_len = ident_len;
+      identifiers->entries[j].ident_str =
+        (const char *) obstack_copy0 (identifiers->strings, buffer, ident_len);
+
+      before_len = pth_read_string ((char *) buffer, max_length, stream);
+      identifiers->entries[j].before_len = before_len;
+      if (before_len == -1U)
+        identifiers->entries[j].before_str = NULL;
+      else
+        identifiers->entries[j].before_str = (const char *)
+            obstack_copy0 (identifiers->strings, buffer, before_len);
+
+      after_len = pth_read_string ((char *) buffer, max_length, stream);
+      identifiers->entries[j].after_len = after_len;
+      if (after_len == -1U)
+        identifiers->entries[j].after_str = NULL;
+      else
+        identifiers->entries[j].after_str = (const char *)
+            obstack_copy0 (identifiers->strings, buffer, after_len);
+    }
+
+  free (buffer);
+}
+
+
+/* Load a hunk into the IMAGE from a STREAM.  */
+
+static void
+pth_load_hunk (pth_image *image, FILE *stream)
+{
+  unsigned j, num_tokens;
+  cp_token_hunk *hunk;
+  
+  hunk = ggc_alloc_cleared_cp_token_hunk ();
+
+  /* Setup the identifier list.  */
+  pth_load_identifiers (&hunk->identifiers, stream);
+
+  /* Read the offset into the text file where this token starts.  */
+  pth_read_sizet (&hunk->text_offset.cur, stream);
+  pth_read_sizet (&hunk->text_offset.line_base, stream);
+  pth_read_sizet (&hunk->text_offset.next_line, stream);
+
+  /* Read the text length of the hunk.  */
+  pth_read_sizet (&hunk->text_length, stream);
+
+  /* Read the number of tokens in HUNK. */
+  pth_read_uint (&num_tokens, stream);
+
+  /* Read the tokens in the HUNK. */
+  hunk->buffer = VEC_alloc (cp_token, gc, num_tokens);
+  for (j = 0; j < num_tokens; j++)
+    {
+      cp_token *token = VEC_quick_push (cp_token, hunk->buffer, NULL);
+
+      /* Do not read the whole structure, the token value has
+         dynamic size as it contains swizzled pointers.
+         FIXME pph, restructure to allow bulk reads of the whole
+         section.  */
+      pth_read_bytes (token, sizeof (cp_token) - sizeof (void *), stream);
+
+      /* FIXME pph.  Use an arbitrary (but valid) location to avoid
+         confusing the rest of the compiler for now.  */
+      token->location = input_location;
+
+      /* FIXME pph: verify that pth_load_token_value works with no tokens.  */
+      pth_load_token_value (token, stream);
+    }
+  gcc_assert (num_tokens == VEC_length (cp_token, hunk->buffer));
+
+  VEC_quick_push (cp_token_hunk_ptr, image->token_hunks, hunk);
+}
+
+
+/* Create a new empty #include directive for NAME.  ITYPE is one of
+   the supported include commands.  ANGLE_BRACKETS is true if the
+   include used '<>'.  */
+
+static pth_include *
+pth_create_include (enum include_type itype, bool angle_brackets,
+		    const char *name)
+{
+  pth_include *include = ggc_alloc_cleared_pth_include ();
+  include->itype = itype;
+  include->angle_brackets = angle_brackets;
+  include->iname = (name) ? xstrdup (name) : name;
+
+  return include;
+}
+
+
+/* Load an #include directive for IMAGE from STREAM.  */
+
+static void
+pth_load_include (pth_state *state, pth_image *image, cpp_reader *reader,
+		  FILE *stream)
+{
+  char *s;
+  pth_include *include;
+  unsigned tmp;
+
+  include = pth_create_include (IT_INCLUDE, false, NULL);
+
+  s = pth_read_string_alloc (stream);
+  include->image = pth_image_lookup (state, s, reader);
+
+  pth_read_uint (&tmp, stream);
+  include->itype = (enum include_type) tmp;
+
+  pth_read_uint (&tmp, stream);
+  include->angle_brackets = (tmp != 0);
+
+  include->iname = pth_read_string_alloc (stream);
+  include->dname = pth_read_string_alloc (stream);
+
+  VEC_safe_push (pth_include_ptr, gc, image->includes, include);
+}
+
+
+/* Load a PTH image for LEXER using the READER.  */
+
+static void
+pth_load_image (pth_state *state, pth_image *image, cpp_reader *reader)
+{
+  FILE *stream;
+  unsigned i, num;
+
+  timevar_push (TV_PTH_LOAD);
+
+  stream = pth_file_for (image->fname, "r+b");
+
+  /* Skip over the header, as we assume that it has already been
+     validated by pth_have_valid_image_for.  */
+  fseek (stream, (long) pth_header_len (), SEEK_SET);
+
+  /* Read the include-hunk (IH) sequencing vector.  */
+  pth_read_uint (&num, stream);
+  if (num > 0)
+    {
+      image->ih_sequence = VEC_alloc (char, gc, num);
+      VEC_safe_grow (char, gc, image->ih_sequence, num);
+      pth_read_bytes (VEC_address (char, image->ih_sequence), num, stream);
+    }
+
+  /* Read the number path names of all the files #included by
+     IMAGE->FNAME.  */
+  pth_read_uint (&num, stream);
+  image->includes = VEC_alloc (pth_include_ptr, gc, num);
+
+  /* Now read all the path names #included by IMAGE->FNAME.  */
+  for (i = 0; i < num; i++)
+    pth_load_include (state, image, reader, stream);
+
+  /* Read how many token hunks are contained in this image.  */
+  pth_read_uint (&num, stream);
+  image->token_hunks = VEC_alloc (cp_token_hunk_ptr, gc, num);
+
+  PTH_STATS_INCR (hunks, num);
+
+  /* Read all the token hunks.  */
+  for (i = 0; i < num; i++)
+    pth_load_hunk (image, stream);
+
+  fclose (stream);
+
+  /* Indicate that we have loaded this image from a file.  */
+  image->loaded_p = true;
+  image->save_p = false;
+  image->used_p = false;
+
+  if (flag_pth_debug >= 3)
+    {
+      fprintf (stderr, "\nLoaded image for %s:\n", image->fname);
+      pth_debug_image (image);
+    }
+
+  timevar_pop (TV_PTH_LOAD);
+}
+
+
+/* Return true if FNAME has a PTH image that can be used.  If an image
+   already exists, compute the MD5 digest for FNAME and store it
+   in IMAGE.  */
+
+static bool
+pth_have_valid_image_for (const char *fname, pth_image *image)
+{
+  FILE *f = NULL;
+  struct stat s;
+  char *img_name, *id;
+  const char *good_id;
+  char saved_digest[DIGEST_LEN];
+
+  image->digest_computed_p = false;
+
+  img_name = pth_name_for (fname);
+  if (stat (img_name, &s) != 0)
+    goto invalid_img;
+
+  /* If the file exists, check if it has a valid signature.  */
+  f = fopen (img_name, "r");
+
+  good_id = pth_id_str ();
+  id = XCNEWVEC (char, strlen (good_id) + 1);
+  pth_read_bytes (id, strlen (good_id), f);
+  if (strcmp (id, good_id) != 0)
+    goto invalid_img;
+
+  /* Now check if the MD5 digest stored in the image file matches the
+     digest for FNAME.  */
+  pth_read_bytes (saved_digest, DIGEST_LEN, f);
+  pth_get_md5_digest (fname, image->digest);
+  image->digest_computed_p = true;
+  if (memcmp (image->digest, saved_digest, DIGEST_LEN) != 0)
+    goto invalid_img;
+
+  fclose (f);
+  return true;
+
+invalid_img:
+  if (f)
+    fclose (f);
+
+  return false;
+}
+
+
+/* Create a new PTH cache object for file FNAME.  */
+
+static pth_image *
+pth_new_image (const char *fname)
+{
+  pth_image *image;
+
+  image = ggc_alloc_cleared_pth_image ();
+  image->fname = fname;
+
+  return image;
+}
+
+
+/* Return a cache image associated with file FNAME.  STATE holds
+   the PTH cache to use.  */
+
+static pth_image *
+pth_image_lookup (pth_state *state, const char *fname, cpp_reader *reader)
+{
+  void **slot;
+  pth_image *image, e;
+
+  if (fname == NULL)
+    return NULL;
+
+  e.fname = fname;
+  slot = htab_find_slot (state->cache_dir, &e, INSERT);
+  if (*slot == NULL)
+    {
+      /* Create a new image and store it in the cache.  */
+      image = pth_new_image (fname);
+      *slot = image;
+      VEC_safe_push (pth_image_ptr, gc, state->cache, image);
+
+      /* If a valid disk image already exists for FNAME, load it.
+	 Otherwise, mark the memory image for processing and saving.  */
+      if (pth_have_valid_image_for (fname, image))
+	pth_load_image (state, image, reader);
+      else
+	image->save_p = true;
+    }
+  else
+    image = *((pth_image **) slot);
+
+  return image;
+}
+
+
+/* Add all the tokens in HUNK to the end of LEXER->BUFFER.  IMAGE is
+   the memory image holding HUNK.  */
+
+static void
+pth_append_hunk (cp_lexer *lexer, pth_image *image, cp_token_hunk *hunk)
+{
+  cp_token *lexer_addr, *hunk_addr;
+  unsigned lexer_len, hunk_len;
+
+  PTH_STATS_INCR (valid_hunks, 1);
+
+  /* Apply all the identifiers used and defined by HUNK.  */
+  cpp_lt_replay (parse_in, &hunk->identifiers);
+
+  /* If this file has been read in memory (e.g., by being #included
+     from a tainted file), advance the text buffer pointer to the end
+     of the hunk to avoid reading it again.  */
+  if (image->buffer)
+    cpp_set_pos (image->buffer, hunk->text_offset);
+
+  hunk_len = VEC_length (cp_token, hunk->buffer);
+
+  /* Some hunks have no tokens and they are only useful for the
+     macros defined by them.  This is useful when one or more
+     image files are tainted and need to be restored from their
+     character stream.  */
+  if (hunk_len == 0)
+    return;
+
+  /* Determine the last location in LEXER->BUFFER before growing it.  */
+  lexer_len = VEC_length (cp_token, lexer->buffer);
+  VEC_safe_grow (cp_token, gc, lexer->buffer, lexer_len + hunk_len);
+
+  /* Bulk copy all the tokens in HUNK to the end of LEXER->BUFFER.  */
+  lexer_addr = VEC_address (cp_token, lexer->buffer);
+  hunk_addr = VEC_address (cp_token, hunk->buffer);
+  memcpy (&lexer_addr[lexer_len], hunk_addr, hunk_len * sizeof (cp_token));
+
+  if (flag_pth_debug >= 2)
+    {
+      fprintf (stderr, "\n=> ADDED TOKEN HUNK TO LEXER BUFFER");
+      if (flag_pth_debug >= 5)
+        pth_debug_hunk (hunk);
+    }
+}
+
+
+/* Return true if HUNK can be used in the current compilation
+   context of the cpp READER.  It must validate the identifier state.  */
+
+static bool
+pth_hunk_is_valid_p (pth_image *image, cp_token_hunk *hunk, cpp_reader *reader)
+{
+  bool verified;
+  cpp_ident_use *bad_use;
+  const char *cur_def;
+
+  timevar_push (TV_PTH_DEPENDENCY);
+  verified = cpp_lt_verify (reader, &hunk->identifiers, &bad_use, &cur_def);
+  if (!verified && flag_pth_debug >= 1)
+    {
+      fprintf (stderr, "PTH: %s failed verification: %s : <%s> -> <%s>\n",
+                         pth_name_for (image->fname), bad_use->ident_str,
+                         bad_use->before_str, cur_def);
+    }
+  PTH_STATS_INCR (verified_hunks, 1);
+  PTH_STATS_INCR (verified_identifiers, hunk->identifiers.num_entries);
+  timevar_pop (TV_PTH_DEPENDENCY);
+  return verified;
+}
+
+
+/* Return true if IMAGE can be used in the current compilation context.  */
+
+static bool
+pth_image_can_be_used (pth_image *image)
+{
+  return image && image->loaded_p && !image->used_p;
+}
+
+
+/* Return true if LEXER has a CPP_EOF at the end of the buffer.  */
+
+static inline bool
+cp_lexer_finished_p (cp_lexer *lexer)
+{
+  return !VEC_empty (cp_token, lexer->buffer)
+         && VEC_last (cp_token, lexer->buffer)->type == CPP_EOF;
+}
+
+
+/* Get tokens from the pre-processor character stream into LEXER.  */
+
+static void
+cp_lexer_get_tokens (cp_lexer *lexer)
+{
+  cp_token token;
+  bool done;
+
+  /* If we are using pretokenized headers, it's possible that by
+     the time we get called to read tokens, the token stream has
+     already been instantiated from images.
+
+     In that case, we need to check whether we have already
+     instantiated the whole translation unit.  If that's the case,
+     we will find a CPP_EOF token in the last position of
+     LEXER->BUFFER.  */
+  done = cp_lexer_finished_p (lexer);
+
+  /* Get the remaining tokens from the preprocessor.  */
+  while (!done)
+    {
+      cp_lexer_get_preprocessor_token (lexer, &token);
+      VEC_safe_push (cp_token, gc, lexer->buffer, &token);
+      done = (token.type == CPP_EOF);
+    }
+}
+
+
+/* Split NAME into its directory and file name components, storing them
+   in *DNAME_P and *FNAME_P.  After using it, *DNAME_P should be freed
+   by the caller.  */
+
+static void
+pth_get_dir_and_name (const char *name, char **dname_p, const char **fname_p)
+{
+  size_t len;
+
+  *fname_p = lbasename (name);
+  *dname_p = NULL;
+
+  len = *fname_p - name;
+  if (len > 0)
+    {
+      *dname_p = XNEWVEC (char, len + 1);
+      memcpy (*dname_p, name, len);
+      (*dname_p)[len] = '\0';
+      gcc_assert (IS_DIR_SEPARATOR ((*dname_p)[len - 1]));
+    }
+}
+
+
+/* Read tokens from the text stream in IMAGE->FNAME into LEXER.
+   If INCLUDE is not NULL, it describes how IMAGE->FNAME was
+   #included originally.  Otherwise, it is assumed to be an
+   include command done with the flag -include.
+
+   This is used when an image is found to be tainted and tokens
+   need to be read from the original character stream.  OFFSET
+   indicates how far into the character stream to start reading at.  */
+
+static void
+pth_process_text_file (cp_lexer *lexer, pth_image *image, pth_include *include,
+		       cpp_offset offset)
+{
+  bool prev_permissive, prev_inhibit_warnings;
+  bool pushed_p;
+  cpp_buffer *buffer;
+  lexer_state *state;
+
+  /* Emulate a #include directive on IMAGE->FNAME, if needed.  Note
+     that if we are already inside the CPP buffer for IMAGE->FNAME
+     we should not include it again, since this will cause another
+     call to pth_file_change which will again register IMAGE->FNAME as
+     an include for the parent file.  */
+  state = NULL;
+  if (image->buffer != cpp_get_buffer (parse_in))
+    {
+      if (include == NULL || include->itype == IT_INCLUDE_NEXT)
+	{
+	  char *dname;
+	  const char *fname;
+	  pth_get_dir_and_name (image->fname, &dname, &fname);
+	  pushed_p = cpp_push_include_type (parse_in, dname, fname, false,
+					    IT_INCLUDE);
+	  
+	  /* FIXME pph.  We are leaking DNAME here.  libcpp
+	     wants the directory name in permanent storage so we
+	     cannot free it, but we should put it in an obstack
+	     so it can be reclaimed at some point.  */
+	}
+      else
+	pushed_p = cpp_push_include_type (parse_in,
+					  include->dname,
+					  include->iname,
+					  include->angle_brackets,
+					  include->itype);
+
+      if (!pushed_p)
+	return;
+    }
+  else
+    {
+      /* Since we already have the buffer on top of the stack,
+	 reset the state of the lexer to avoid skipping over it.  */
+      state = cpp_reset_lexer_state (parse_in);
+    }
+
+  /* Inhibit libcpp error messages (ignore things like unmatched #endifs).
+     We need to do this because token hunks may span #if/#endif boundaries.
+     For instance,
+
+     	#if <COND>
+	  H1 [1]
+	#else
+	  H2 [2]
+	  #include "foo.h"
+	  H3 [3]
+	#endif
+	H1 or H3 [4]
+
+     In this example we have 3 hunks (H1, H2 and H3).  Depending on the
+     value of <COND> at image creation time, point [4] will belong to
+     hunk H1 (if <COND> was true) or hunk H3 (if <COND> was false).
+
+     When this image is loaded, if H3 cannot be applied because its
+     dependences fail to hold, then we will start pre-processing the
+     text for the file at the start of hunk H3.  This will find a
+     dangling #endif which triggers a libcpp error.  Since we know
+     that we are in the correct arm of the #if (after all H2 was
+     applied from the image), we can safely ignore the error.  */
+  prev_permissive = global_dc->permissive;
+  prev_inhibit_warnings = global_dc->dc_inhibit_warnings;
+  global_dc->dc_inhibit_warnings = true;
+  global_dc->permissive = true;
+
+  /* Position the reader at OFFSET and request to stop reading at
+     the end of it.  */
+  buffer = cpp_get_buffer (parse_in);
+  cpp_set_pos (buffer, offset);
+  cpp_return_at_eof (buffer, true);
+
+  /* Get tokens from IMAGE->FNAME.  */
+  cp_lexer_get_tokens (lexer);
+
+  /* Since we read this file separately, the very last token in LEXER
+     will now contain an EOF, which we do not need.  */
+  VEC_pop (cp_token, lexer->buffer);
+  cpp_return_at_eof (buffer, false);
+
+  /* Restore libcpp error/warnings.  */
+  global_dc->permissive = prev_permissive;
+  global_dc->dc_inhibit_warnings = prev_inhibit_warnings;
+
+  /* Restore the parser state, if necessary.  */
+  if (state)
+    cpp_restore_lexer_state (parse_in, state);
+}
+
+
+/* Populate LEXER->BUFFER with all the valid token hunks in
+   IMAGE.  If possible, try to load token hunks from files
+   included by IMAGE as well.
+
+   This means that we try to load the whole transitive closure
+   starting at IMAGE until we find the first unloadable file.
+   
+   Once we find the first unloadable token hunk, we skip the token
+   hunks from the character stream so that they don't need to be
+   pre-processed again.
+
+   If non-NULL, INCLUDE describes the #include command used to include
+   IMAGE->FNAME.  */
+
+static void
+pth_image_to_lexer (cp_lexer *lexer, pth_image *image, pth_include *include)
+{
+  unsigned i, h_ix, i_ix;
+  char s;
+
+  /* If we are trying to apply the same image more than once,
+     something is wrong.  We never create images for files that 
+     are included more than once, so we should never try to apply
+     the same image more than once.  */
+  gcc_assert (pth_image_can_be_used (image));
+
+  image->used_p = true;
+
+  if (flag_pth_debug >= 2)
+    {
+      fprintf (stderr, "\n<= INCORPORATING IMAGE %s INTO COMPILATION CONTEXT\n",
+	       image->fname);
+      if (flag_pth_debug >= 3)
+        pth_debug_include (include);
+    }
+
+  PTH_STATS_INCR (valid_images, 1);
+
+  for (i = 0, h_ix = 0, i_ix = 0;
+       VEC_iterate (char, image->ih_sequence, i, s);
+       i++)
+    {
+      if (s == 'H')
+	{
+	  cp_token_hunk *hunk;
+
+	  hunk = VEC_index (cp_token_hunk_ptr, image->token_hunks, h_ix++);
+	  if (pth_hunk_is_valid_p (image, hunk, parse_in))
+	    pth_append_hunk (lexer, image, hunk);
+	  else
+	    {
+	      PTH_STATS_INCR (invalid_hunks, 1);
+
+	      pth_process_text_file (lexer, image, NULL, hunk->text_offset);
+
+	      /* Since this hunk is invalid, assume that everything
+		 downstream from this hunk is also invalid.  FIXME pph,
+		 it may be possible to optimize this.  We should be
+		 able to pre-process from text exactly
+		 HUNK->TEXT_LENGTH characters instead of the whole
+		 file.  */
+	      break;
+	    }
+	}
+      else if (s == 'I')
+	{
+	  pth_include *incdir;
+	  pth_get_state ()->cur_image = image;
+	  incdir = VEC_index (pth_include_ptr, image->includes, i_ix++);
+	  if (pth_image_can_be_used (incdir->image))
+	    pth_image_to_lexer (lexer, incdir->image, incdir);
+	  else
+	    pth_process_text_file (lexer, incdir->image, incdir,
+				   cpp_buffer_start);
+	}
+      else
+	gcc_unreachable ();
+    }
+
+  /* If we just applied the tokens for the main input filename,
+     we need to append a CPP_EOF token, since that one is never
+     saved with the token hunks.  */
+  if (pathnames_equal_p (image->fname, main_input_filename))
+    VEC_safe_push (cp_token, gc, lexer->buffer, &eof_token);
+
+  /* If IMAGE has a libcpp buffer associated with it, it means that a
+     file that was being pre-processed from text has #included IMAGE
+     and the pre-processor has executed the file change logic.
+
+     In that case, the pre-processor will want to finish processing
+     IMAGE's text, and since we have just applied its tokens from
+     the image, the result will be duplicate tokens.  To prevent
+     this, we tell libcpp to skip over the whole text buffer
+     associated with IMAGE.  */
+  if (image->buffer)
+    cpp_set_pos (image->buffer, cpp_buffer_end);
+
+  return;
+}
+
+
+/* Create a token hunk for IMAGE from the token buffer in
+   LEXER->BUFFER.  The hunk will contain all the tokens starting at
+   IMAGE->HUNK_START_IX to the end of LEXER->BUFFER.
+
+   The new token hunk will be added to the end of IMAGE->TOKEN_HUNKS.  */
+
+static void
+pth_lexer_to_image (pth_image *image, cp_lexer *lexer, cpp_reader *reader)
+{
+  cp_token *lexer_addr, *hunk_addr;
+  cp_token_hunk *hunk;
+  unsigned num_tokens, start_ix, end_ix;
+  cpp_offset pos;
+
+  /* Create a new token hunk.  */
+  hunk = ggc_alloc_cleared_cp_token_hunk ();
+  VEC_safe_push (cp_token_hunk_ptr, gc, image->token_hunks, hunk);
+  VEC_safe_push (char, gc, image->ih_sequence, 'H');
+
+  /* The identifiers that may conflict with macros.  */
+  if (flag_pth_debug >= 2)
+    cpp_lt_statistics (reader);
+  hunk->identifiers = cpp_lt_capture (reader);
+
+  /* Remember the text offset where this hunk started and its length.  */
+  hunk->text_offset = image->hunk_text_offset;
+  pos = cpp_get_pos (image->buffer);
+  gcc_assert (pos.cur >= hunk->text_offset.cur);
+  hunk->text_length = pos.cur - hunk->text_offset.cur;
+
+  /* Compute the bounds for the new token hunk.  */
+  start_ix = image->hunk_start_ix;
+  end_ix = VEC_length (cp_token, lexer->buffer);
+  gcc_assert (end_ix >= start_ix);
+  num_tokens = end_ix - start_ix;
+
+  if (num_tokens > 0)
+    {
+      /* Copy tokens from LEXER->BUFFER into the new hunk.  */
+      hunk->buffer = VEC_alloc (cp_token, gc, num_tokens);
+      VEC_safe_grow (cp_token, gc, hunk->buffer, num_tokens);
+      lexer_addr = VEC_address (cp_token, lexer->buffer);
+      hunk_addr = VEC_address (cp_token, hunk->buffer);
+      memcpy (hunk_addr, &lexer_addr[start_ix], num_tokens * sizeof (cp_token));
+    }
+
+  if (flag_pth_debug >= 3)
+    {
+      fprintf (stderr, "\n=> SAVED HUNK TO IMAGE: %s\n", image->fname);
+      if (flag_pth_debug >= 5)
+        pth_debug_hunk (hunk);
+    }
+}
+
+
+/* Compute the effects of a file transition given the file change 
+   described by MAP.  On exit:
+
+    - *PREV_IMAGE_P will point to the image for the file that we just left,
+
+    - *NEW_IMAGE_P will point to the image for the file that we just
+      entered.  If *NEW_IMAGE_P is NULL, it means that we just left
+      the last file in the translation unit, so there isn't anything
+      else to be done.
+
+    - *REASON_P will have the LC_* reason for the change.  */
+
+static void
+pth_get_file_transition (const struct line_map *map, pth_image **prev_image_p,
+			 pth_image **new_image_p, enum lc_reason *reason_p)
+{
+  const char *fname;
+  pth_state *state;
+
+  /* MAP is NULL when we leave the main file in this translation unit.  */
+  if (map == NULL)
+    {
+      fname = NULL;
+      *reason_p = LC_LEAVE;
+    }
+  else
+    {
+      fname = map->to_file;
+      *reason_p = map->reason;
+    }
+
+  state = pth_get_state ();
+  *prev_image_p = state->cur_image;
+
+  /* FIXME pph.  Sanitize use of PARSE_IN.  Stick it in
+     pth_state together with lexer.  */
+  *new_image_p = pth_image_lookup (state, fname, parse_in);
+}
+
+
+/* Do bookkeeping actions required when the pre-processor is leaving
+   file IMAGE->FNAME.  READER is the cpp file reader object we
+   are using for lexing.  */
+
+static void
+pth_leave_file (cpp_reader *reader, pth_image *image)
+{
+  pth_state *state;
+
+  /* We are only interested in processing IMAGE if we have decided to
+     save its image.  */
+  if (!image->save_p)
+    return;
+
+  state = pth_get_state ();
+
+  /* If the image for the file we just finished is marked as
+     modified, create a new token hunk spanning from the token
+     that started the image to the current end of the lexer
+     buffer.  */
+  pth_lexer_to_image (image, state->lexer, reader);
+}
+
+
+/* Do bookkeeping actions required when the pre-processor is entering
+   file IMAGE->FNAME for reason REASON.  READER is the cpp file reader
+   object we are using for lexing.  INCLUDE is the #include command
+   used to enter IMAGE->FNAME.  It can be NULL in the case of the
+   top file in the translation unit.  */
+
+static void
+pth_enter_file (cpp_reader *reader, pth_image *image, pth_include *include,
+	        enum lc_reason reason)
+{
+  pth_state *state;
+
+  state = pth_get_state ();
+
+  /* Associate the current buffer with IMAGE.  */
+  image->buffer = cpp_get_buffer (reader);
+
+  /* Complete the current #include command with the directory
+     and image for the file that we just switched to.  */
+  if (include)
+    {
+      const char *dname = cpp_get_dir (cpp_get_file (image->buffer))->name;
+      include->image = image;
+      include->dname = (*dname) ? xstrdup (dname) : NULL;
+    }
+
+  /* If the file we are about to switch to has been loaded into an
+     image, try to get as many tokens as possible from the image
+     instead of the character stream.  */
+  if (pth_image_can_be_used (image))
+    pth_image_to_lexer (state->lexer, image, include);
+
+  /* If IMAGE does not need to be saved, we are done.  */
+  if (!image->save_p)
+    return;
+
+  /* Detect multiple inclusions of the same header file.  When a file
+     is included more than once, each inclusion will usually produce
+     different token hunks (e.g., <stddef.h> is typically included
+     from different places with "arguments" in the form of #defines
+     that determine what the caller wants stddef.h to provide.  See
+     <wchar.h> for an example).
+
+     This disrupts the validity of the image, as the hunks saved in it
+     no longer correspond to a single pre-processing of the file.  We
+     avoid this problem by tainting the image and forcing the file to
+     be always processed from its character stream.  */
+  if (reason == LC_ENTER && !VEC_empty (cp_token_hunk_ptr, image->token_hunks))
+    image->save_p = false;
+
+  /* The starting point for the next token hunk in the new
+     file image will be at the current last slot in
+     STATE->LEXER->BUFFER.  */
+  image->hunk_start_ix = VEC_length (cp_token, state->lexer->buffer);
+
+  /* The new hunk starts at the current offset in the current
+     libcpp buffer.  If this hunk is ever invalidated, this is
+     the offset at which to start pre-processing.  */
+  image->hunk_text_offset = cpp_get_pos (image->buffer);
+}
+
+
+/* Callback from the pre-processor when changing in or out of a file.
+   READER is the pre-processor state.  MAP is the line map for the 
+   file that we are changing to.  */
+
+static void
+pth_file_change (cpp_reader *reader, const struct line_map *map)
+{
+  enum lc_reason reason;
+  pth_state *state;
+  pth_image *prev_image, *new_image;
+
+  /* Call the previous file change handler, if it exists.  */
+  state = pth_get_state ();
+  if (state->file_change_prev)
+    state->file_change_prev (reader, map);
+
+  /* When processing pre-processed output, we will see names like
+     '<built-in>' and '<command-line>'.  Reject those.
+     ??? This rejects real path names that may start with '<', but
+         those should be rare.  */
+  if (map && map->to_file && map->to_file[0] == '<')
+    return;
+
+  /* Ignore LC_RENAME events.  They do not affect the actual image
+     that we are processing.  */
+  if (map && map->reason == LC_RENAME)
+    return;
+
+  timevar_push (TV_PTH_MANAGE);
+
+  /* Get images for the file involved in the transition.  */
+  pth_get_file_transition (map, &prev_image, &new_image, &reason);
+  gcc_assert (prev_image);
+
+  /* Ignore self-referential file change events.  These can happen
+     when mixing token images with text buffers.  */
+  if (prev_image == new_image)
+    {
+      timevar_pop (TV_PTH_MANAGE);
+      return;
+    }
+
+  /* Process the file we just left (get tokens from lexer buffer,
+     etc).  */
+  pth_leave_file (reader, prev_image);
+
+  /* Process the file we are about to enter (try to use its tokens if
+     the file is valid, etc).  */
+  if (new_image)
+    {
+      pth_include *include = NULL;
+      
+      if (reason == LC_ENTER)
+	include = pth_create_include (state->new_itype,
+				      state->new_angle_brackets,
+				      state->new_iname);
+
+      pth_enter_file (reader, new_image, include, reason);
+
+      /* If we are LC_ENTERing NEW_IMAGE, it means that PREV_IMAGE has
+	 #included NEW_IMAGE.  In that case, add NEW_IMAGE to the list
+	 of included files by PREV_IMAGE.  */
+      if (reason == LC_ENTER)
+	{
+	  PTH_STATS_INCR (included_files, 1);
+
+	  if (prev_image->save_p)
+	    {
+	      gcc_assert (include->image == new_image);
+	      VEC_safe_push (pth_include_ptr, gc, prev_image->includes,
+			     include);
+	      VEC_safe_push (char, gc, prev_image->ih_sequence, 'I');
+	    }
+	}
+    }
+
+  /* Update the current image.  */
+  state->cur_image = new_image;
+
+  timevar_pop (TV_PTH_MANAGE);
+}
+
+
+/* Record a #include or #include_next.  */
+
+static void
+pth_include_handler (cpp_reader *reader ATTRIBUTE_UNUSED,
+	             location_t loc ATTRIBUTE_UNUSED,
+	             const unsigned char *dname,
+	             const char *name,
+	             int angle_brackets,
+	             const cpp_token **tok_p ATTRIBUTE_UNUSED)
+{
+  pth_state *state;
+
+  state = pth_get_state ();
+
+  /* Remember the attributes for this #include command.  This is
+     used in pth_file_change to register a new include event for
+     the parent file.  */
+  if (strcmp ((const char *)dname, "include") == 0)
+    state->new_itype = IT_INCLUDE;
+  else if (strcmp ((const char *)dname, "include_next") == 0)
+    state->new_itype = IT_INCLUDE_NEXT;
+  else if (strcmp ((const char *)dname, "import") == 0)
+    state->new_itype = IT_IMPORT;
+  else
+    gcc_unreachable ();
+
+  state->new_angle_brackets = angle_brackets;
+  state->new_iname = name;
+}
+
+
+/* Initialize PTH support.  LEXER is the main lexer object used for
+   pre-processing.  */
+
+static void
+pth_init (cp_lexer *lexer)
+{
+  pth_state *state;
+  cpp_callbacks *cb;
+  cpp_lookaside *table;
+
+  timevar_push (TV_PTH_INIT);
+
+  gcc_assert (flag_pth);
+
+  table = cpp_lt_exchange (parse_in, cpp_lt_create (/*2 to the power*/15,
+			   flag_pth_debug));
+  gcc_assert (table == NULL);
+
+  memset (&pth_stats, 0, sizeof (pth_stats));
+
+  state = pth_get_state ();
+
+  /* If not using MD5 signatures, make sure that time stamps given
+     by stat() are smaller than DIGEST_LEN bytes.  FIXME pph, this is
+     slighly hacky.  */
+  if (!flag_pth_md5)
+    {
+      struct stat tmp;
+      gcc_assert (sizeof (tmp.st_mtime) < DIGEST_LEN);
+    }
+
+  /* Set an handler for file change events in libcpp.  */
+  cb = cpp_get_callbacks (parse_in);
+  state->file_change_prev = cb->file_change;
+  cb->file_change = pth_file_change;
+  cb->include = pth_include_handler;
+
+  state->lexer = lexer;
+
+  /* Make sure that we have not tried to get any tokens yet.  */
+  gcc_assert (VEC_empty (cp_token, lexer->buffer));
+
+  /* If we have a valid image for the main input file, populate as
+     many tokens from its transitive closure as possible.  */
+  state->cur_image = pth_image_lookup (state, main_input_filename, parse_in);
+  pth_enter_file (parse_in, state->cur_image, NULL, LC_ENTER);
+
+  timevar_pop (TV_PTH_INIT);
+}
+
+
+/* Show statistics on PTH on FILE.  If FILE is NULL, use pph_logfile.
+   LEXER is the lexer we just filled with tokens.  This is usually
+   the same as pth_get_state()->lexer, but it may be NULL if PTH is
+   not enabled (in cases where we just want stats on pre-processed
+   files).  */
+
+static void
+pth_print_stats (FILE *file, cp_lexer *lexer)
+{
+  unsigned i, num_tokens, total_tokens;
+  pth_state *state;
+  pth_image *image;
+  const char *prev_fname;
+  cp_token *token;
+
+  if (file == NULL)
+    file = pph_logfile;
+
+  fprintf (file, "\nPTH statistics\n\n");
+  fprintf (file, "#included files:       %lu\n", pth_stats.included_files);
+  fprintf (file, "Valid images:          %lu\n", pth_stats.valid_images);
+  fprintf (file, "Token hunks:           %lu\n", pth_stats.hunks);
+  fprintf (file, "Valid hunks:           %lu\n", pth_stats.valid_hunks);
+  fprintf (file, "Invalid hunks:         %lu\n", pth_stats.invalid_hunks);
+  fprintf (file, "Verified hunks:        %lu\n", pth_stats.verified_hunks);
+  fprintf (file, "Verified identifiers:  %lu\n", pth_stats.verified_identifiers);
+
+  state = pth_get_state ();
+  fprintf (file, "\n\nPTH image statistics (%u files)\n\n",
+	   VEC_length (pth_image_ptr, state->cache));
+  for (i = 0; VEC_iterate (pth_image_ptr, state->cache, i, image); i++)
+    pth_show_image_stats (file, image);
+
+  fprintf (file, "\nToken counts per file in #include order:\n");
+  num_tokens = total_tokens = 0;
+  prev_fname = NULL;
+  for (i = 0; VEC_iterate (cp_token, lexer->buffer, i, token); i++)
+    {
+      const char *fname = LOCATION_FILE (token->location);
+
+      if (prev_fname == NULL)
+	prev_fname = fname;
+
+      if ((fname
+	   && strcmp (fname, prev_fname) != 0)
+	  || i == VEC_length (cp_token, lexer->buffer) - 1)
+	{
+	  fprintf (file, "tokens: %u %s\n", num_tokens, lrealpath (prev_fname));
+	  prev_fname = fname;
+	  total_tokens += num_tokens;
+	  num_tokens = 0;
+	}
+
+      num_tokens++;
+    }
+
+  /* The main lexer buffer should have one more token: CPP_EOF.  */
+  if (total_tokens != VEC_length (cp_token, lexer->buffer) - 1)
+    fprintf (stderr, "*** WARNING: I counted %u, but there are %u\n",
+	     total_tokens, VEC_length (cp_token, lexer->buffer));
+
+  fprintf (file, "\n");
+}
+
+
+/* Save all the header images that have been marked modified from
+   the incremental state.  */
+
+static void
+pth_finish (void)
+{
+  /* If PPH is enabled, do not save PTH images to prevent analysis problems
+     due to lack of location information in PTH images.  FIXME pph:
+     Unneeded after we start saving proper location information.  */
+  if (flag_pph_debug >= 1)
+    {
+      if (flag_pph_debug > 1)
+	fprintf (stderr, "*** WARNING: Not saving PTH images because PPH "
+		 "is enabled\n");
+    }
+  else
+    {
+      pth_state *state;
+      pth_image *image;
+      size_t i;
+
+      state = pth_get_state ();
+      for (i = 0; VEC_iterate (pth_image_ptr, state->cache, i, image); i++)
+	if (image->save_p)
+	  pth_save_image (image);
+    }
+}
+
+
+/* Allocate memory for a new lexer object and return it.  */
 
 static cp_lexer *
-cp_lexer_new_main (void)
+cp_lexer_alloc (void)
 {
-  cp_token first_token;
   cp_lexer *lexer;
-  cp_token *pos;
-  size_t alloc;
-  size_t space;
-  cp_token *buffer;
-
-  /* It's possible that parsing the first pragma will load a PCH file,
-     which is a GC collection point.  So we have to do that before
-     allocating any memory.  */
-  cp_parser_initial_pragma (&first_token);
 
   c_common_no_more_pch ();
 
@@ -403,40 +2914,101 @@ cp_lexer_new_main (void)
 				   CP_SAVED_TOKEN_STACK);
 
   /* Create the buffer.  */
-  alloc = CP_LEXER_BUFFER_SIZE;
-  buffer = ggc_alloc_vec_cp_token (alloc);
+  lexer->buffer = VEC_alloc (cp_token, gc, CP_LEXER_BUFFER_SIZE);
 
-  /* Put the first token in the buffer.  */
-  space = alloc;
-  pos = buffer;
-  *pos = first_token;
+  return lexer;
+}
 
-  /* Get the remaining tokens from the preprocessor.  */
-  while (pos->type != CPP_EOF)
+
+/* Create a new main C++ lexer, the lexer that gets tokens from the
+   preprocessor.  */
+
+static cp_lexer *
+cp_lexer_new_main (void)
+{
+  cp_lexer *lexer;
+  cp_token token;
+
+  if (flag_pth)
     {
-      pos++;
-      if (!--space)
+      /* FIXME pph.  PTH is incompatible with PCH, so do not read ahead to
+	 the first token looking for a PCH pragma.  This convoluted
+	 initialization could be simplified if PCH was implemented in
+	 terms of the incremental compiler.  */
+      lexer = cp_lexer_alloc ();
+      pth_init (lexer);
+      cp_lexer_get_tokens (lexer);
+    }
+  else
+    {
+      /* FIXME pph.  Get rid of this duplicate and call cp_lexer_get_tokens
+	 all the time.  This is needed only because for PCH support we
+	 are forced to read one token in advance before starting the
+	 main lexer loop.  */
+
+      /* It's possible that parsing the first pragma will load a PCH file,
+	 which is a GC collection point.  So we have to do that before
+	 allocating any memory.  */
+      cp_parser_initial_pragma (&token);
+
+      lexer = cp_lexer_alloc ();
+
+      /* Put the first token in the buffer.  */
+      VEC_quick_push (cp_token, lexer->buffer, &token);
+
+      /* Get the remaining tokens from the preprocessor.  */
+      while (token.type != CPP_EOF)
 	{
-	  space = alloc;
-	  alloc *= 2;
-	  buffer = GGC_RESIZEVEC (cp_token, buffer, alloc);
-	  pos = buffer + space;
+	  cp_lexer_get_preprocessor_token (lexer, &token);
+	  VEC_safe_push (cp_token, gc, lexer->buffer, &token);
 	}
-      cp_lexer_get_preprocessor_token (lexer, pos);
     }
-  lexer->buffer = buffer;
-  lexer->buffer_length = alloc - space;
-  lexer->last_token = pos;
-  lexer->next_token = lexer->buffer_length ? buffer : &eof_token;
+
+  lexer->last_token = VEC_address (cp_token, lexer->buffer)
+                      + VEC_length (cp_token, lexer->buffer)
+		      - 1;
+  lexer->next_token = VEC_length (cp_token, lexer->buffer)
+		      ? VEC_address (cp_token, lexer->buffer)
+		      : &eof_token;
 
   /* Subsequent preprocessor diagnostics should use compiler
      diagnostic functions to get the compiler source location.  */
   done_lexing = true;
 
-  gcc_assert (lexer->next_token->type != CPP_PURGED);
+  gcc_assert (!lexer->next_token->purged_p);
+
+  if (flag_pth_debug >= 1)
+    {
+      if (flag_pth)
+	{
+	  if (flag_pth_debug >= 3)
+	    pth_debug_state ();
+
+          if (flag_pth_debug >= 4)
+            {
+	      fprintf (stderr, "Partitioned token stream\n");
+	      pth_debug_token_hunks (pth_image_lookup (pth_get_state (),
+		                                       main_input_filename,
+						       parse_in));
+            }
+	}
+
+      fprintf (stderr, "\n\nGlobal token stream\n");
+      cp_lexer_debug_tokens (lexer->buffer);
+    }
+
+  PPH_STATS_INCR (lexed_tokens, VEC_length (cp_token, lexer->buffer));
+
+  if (flag_pth_stats)
+    pth_print_stats (pph_logfile, lexer);
+
+  if (flag_pth)
+    pth_finish ();
+
   return lexer;
 }
 
+
 /* Create a new lexer whose token stream is primed with the tokens in
    CACHE.  When these tokens are exhausted, no new tokens will be read.  */
 
@@ -449,7 +3021,6 @@ cp_lexer_new_from_tokens (cp_token_cache
 
   /* We do not own the buffer.  */
   lexer->buffer = NULL;
-  lexer->buffer_length = 0;
   lexer->next_token = first == last ? &eof_token : first;
   lexer->last_token = last;
 
@@ -461,7 +3032,7 @@ cp_lexer_new_from_tokens (cp_token_cache
   lexer->debugging_p = false;
 #endif
 
-  gcc_assert (lexer->next_token->type != CPP_PURGED);
+  gcc_assert (!lexer->next_token->purged_p);
   return lexer;
 }
 
@@ -470,8 +3041,7 @@ cp_lexer_new_from_tokens (cp_token_cache
 static void
 cp_lexer_destroy (cp_lexer *lexer)
 {
-  if (lexer->buffer)
-    ggc_free (lexer->buffer);
+  VEC_free (cp_token, gc, lexer->buffer);
   VEC_free (cp_token_position, heap, lexer->saved_tokens);
   ggc_free (lexer);
 }
@@ -526,6 +3096,7 @@ cp_lexer_get_preprocessor_token (cp_lexe
 			lexer == NULL ? 0 : C_LEX_STRING_NO_JOIN);
   token->keyword = RID_MAX;
   token->pragma_kind = PRAGMA_NONE;
+  token->purged_p = false;
 
   /* On some systems, some header files are surrounded by an
      implicit extern "C" block.  Set a flag in the token if it
@@ -595,6 +3166,11 @@ cp_lexer_get_preprocessor_token (cp_lexe
       token->pragma_kind = ((enum pragma_kind)
 			    TREE_INT_CST_LOW (token->u.value));
       token->u.value = NULL_TREE;
+
+      /* PTH and PCH are not compatible.  Make sure we are not trying
+	 to use a PCH image.  */
+      if (flag_pth && token->pragma_kind == PRAGMA_GCC_PCH_PREPROCESS)
+	error ("PTH cannot be used with PCH");
     }
 }
 
@@ -737,7 +3313,7 @@ cp_lexer_peek_nth_token (cp_lexer* lexer
 	  break;
 	}
 
-      if (token->type != CPP_PURGED)
+      if (!token->purged_p)
 	--n;
     }
 
@@ -771,7 +3347,7 @@ cp_lexer_consume_token (cp_lexer* lexer)
 	}
 
     }
-  while (lexer->next_token->type == CPP_PURGED);
+  while (lexer->next_token->purged_p);
 
   cp_lexer_set_source_position_from_token (token);
 
@@ -783,6 +3359,8 @@ cp_lexer_consume_token (cp_lexer* lexer)
       putc ('\n', cp_lexer_debug_stream);
     }
 
+  PPH_STATS_INCR (parsed_tokens, 1);
+
   return token;
 }
 
@@ -796,10 +3374,7 @@ cp_lexer_purge_token (cp_lexer *lexer)
   cp_token *tok = lexer->next_token;
 
   gcc_assert (tok != &eof_token);
-  tok->type = CPP_PURGED;
-  tok->location = UNKNOWN_LOCATION;
-  tok->u.value = NULL_TREE;
-  tok->keyword = RID_MAX;
+  tok->purged_p = true;
 
   do
     {
@@ -810,7 +3385,7 @@ cp_lexer_purge_token (cp_lexer *lexer)
 	  break;
 	}
     }
-  while (tok->type == CPP_PURGED);
+  while (tok->purged_p);
   lexer->next_token = tok;
 }
 
@@ -829,12 +3404,7 @@ cp_lexer_purge_tokens_after (cp_lexer *l
   gcc_assert (tok < peek);
 
   for ( tok += 1; tok != peek; tok += 1)
-    {
-      tok->type = CPP_PURGED;
-      tok->location = UNKNOWN_LOCATION;
-      tok->u.value = NULL_TREE;
-      tok->keyword = RID_MAX;
-    }
+    tok->purged_p = true;
 }
 
 /* Begin saving tokens.  All tokens consumed after this point will be
@@ -878,8 +3448,6 @@ cp_lexer_rollback_tokens (cp_lexer* lexe
 
 /* Print a representation of the TOKEN on the STREAM.  */
 
-#ifdef ENABLE_CHECKING
-
 static void
 cp_lexer_print_token (FILE * stream, cp_token *token)
 {
@@ -895,15 +3463,9 @@ cp_lexer_print_token (FILE * stream, cp_
     /* C++ parser token types - see "Manifest constants", above.  */
     "KEYWORD",
     "TEMPLATE_ID",
-    "NESTED_NAME_SPECIFIER",
-    "PURGED"
+    "NESTED_NAME_SPECIFIER"
   };
 
-  /* If we have a name for the token, print it out.  Otherwise, we
-     simply give the numeric code.  */
-  gcc_assert (token->type < ARRAY_SIZE(token_names));
-  fputs (token_names[token->type], stream);
-
   /* For some tokens, print the associated data.  */
   switch (token->type)
     {
@@ -925,11 +3487,23 @@ cp_lexer_print_token (FILE * stream, cp_
       fprintf (stream, " \"%s\"", TREE_STRING_POINTER (token->u.value));
       break;
 
+    case CPP_NUMBER:
+      print_generic_expr (stream, token->u.value, 0);
+      break;
+
     default:
+      /* If we have a name for the token, print it out.  Otherwise, we
+	 simply give the numeric code.  */
+      if (token->type < ARRAY_SIZE(token_names))
+	fputs (token_names[token->type], stream);
+      else
+	fprintf (stream, "[%d]", token->type);
       break;
     }
 }
 
+#ifdef ENABLE_CHECKING
+
 /* Start emitting debugging information.  */
 
 static void
@@ -9235,6 +11809,309 @@ cp_parser_already_scoped_statement (cp_p
     }
 }
 
+static void
+pph_log_exposed (cp_parser *parser, const char *end)
+{
+  if (flag_pph_debug >= 2)
+    {
+      cp_token *pos = cp_lexer_token_position (parser->lexer, false);
+      fprintf (pph_logfile, "PPH: %s exposed declaration at ", end);
+      pph_debug_location (pph_logfile, pos->location);
+      fprintf (pph_logfile, "\n");
+    }
+}
+
+
+/* Allocate the various arrays, maps and sets used to collect ASTs and
+   their dependencies during parsing.  This memory is allocated and
+   freed for every grammar rule intercepted by pph_start_exposed() and
+   pph_stop_exposed().  */
+
+static void
+pph_allocate_catcher_memory (void)
+{
+  /* Note.  pph_tree_catcher *must* be instantiated to indicate that
+     we are going to be catching trees during parsing.  */
+  pph_tree_catcher = VEC_alloc (tree, heap, 5);
+  pph_decl_head_token_cache = pointer_map_create ();
+  pph_decl_body_token_cache = pointer_map_create ();
+  pph_decl_deps = XCNEW (struct pph_decl_deps_d);
+  pph_decl_deps->header = pointer_map_create ();
+  pph_decl_deps->body = pointer_map_create ();
+  pph_name_lookups = NULL;
+  pph_name_lookups_set = pointer_set_create ();
+  pph_nl_token_map = pointer_map_create ();
+}
+
+
+/* Free all the memory allocated by pph_allocate_catcher_memory.  */
+
+static void
+pph_free_catcher_memory (void)
+{
+  VEC_free (tree, heap, pph_tree_catcher);
+
+  pointer_map_destroy (pph_decl_head_token_cache);
+  pph_decl_head_token_cache = NULL;
+
+  pointer_map_destroy (pph_decl_body_token_cache);
+  pph_decl_body_token_cache = NULL;
+
+  pointer_map_destroy (pph_decl_deps->header);
+  pointer_map_destroy (pph_decl_deps->body);
+  free (pph_decl_deps);
+  pph_decl_deps = NULL;
+
+  VEC_free (tree, heap, pph_name_lookups);
+
+  pointer_set_destroy (pph_name_lookups_set);
+  pph_name_lookups_set = NULL;
+
+  pointer_map_destroy (pph_nl_token_map);
+  pph_nl_token_map = NULL;
+}
+
+
+/* Start collecting ASTs and dependencies.  */
+
+static cp_token *
+pph_start_exposed (cp_parser *parser)
+{
+  if (flag_pph_debug >= 1)
+    {
+      timevar_push (TV_PPH_MANAGE);
+
+      if (flag_pph_debug >= 4)
+        fprintf (pph_logfile, "\n--------------------------------------------------------------------------\n");
+      pph_log_exposed (parser, "start");
+      pph_allocate_catcher_memory ();
+      timevar_pop (TV_PPH_MANAGE);
+
+      return parser->lexer->next_token; /* the first token */
+    }
+  else
+    return NULL;
+}
+
+/* Return the token cache associated with tree node T.  */
+
+static VEC(cp_token, heap) *
+pph_lookup_head_token_cache_for (tree t)
+{
+  void **slot = pointer_map_contains (pph_decl_head_token_cache, t);
+  if (slot)
+    return ((VEC(cp_token, heap) *) *slot);
+
+  return NULL;
+}
+
+static VEC(cp_token, heap) *
+pph_lookup_body_token_cache_for (tree t)
+{
+  void **slot = pointer_map_contains (pph_decl_body_token_cache, t);
+  if (slot)
+    return ((VEC(cp_token, heap) *) *slot);
+
+  return NULL;
+}
+
+
+/* Set the token cache associated with tree node T to CACHE.  */
+
+static void
+pph_set_head_token_cache_for (tree t, VEC(cp_token, heap) *cache)
+{
+  void **slot = pointer_map_insert (pph_decl_head_token_cache, t);
+  *slot = (void *) cache;
+}
+
+static void
+pph_set_body_token_cache_for (tree t, VEC(cp_token, heap) *cache)
+{
+  void **slot = pointer_map_insert (pph_decl_body_token_cache, t);
+  *slot = (void *) cache;
+}
+
+/* Emulate the copying of declarations into the parser cache.  Deep
+   copy all the declarations in V.  */
+
+static void
+pph_copy_decls_into_cache (VEC(tree, heap) *v)
+{
+  unsigned i;
+  static tree t_copy, type_copy, t;
+  void **slot;
+  VEC(tree, heap) *ast_cache, *old_pph_tree_catcher, *old_pph_name_lookups;
+  static struct pointer_map_t *hunk_to_decls_map = NULL;
+  static htab_t cache_dir = NULL;
+  const char *fname;
+
+  timevar_push (TV_PPH_CACHE_IN);
+
+  if (hunk_to_decls_map == NULL)
+    hunk_to_decls_map = pointer_map_create ();
+
+  if (cache_dir == NULL)
+    cache_dir = htab_create (10, htab_hash_pointer, htab_eq_pointer, 0);
+
+  /* We will be copying trees, which will call into the tree catching
+     routines.  Prevent that.  */
+  old_pph_tree_catcher = pph_tree_catcher;
+  old_pph_name_lookups = pph_name_lookups;
+  pph_tree_catcher = pph_name_lookups = NULL;
+
+  /* Copy every declaration in V into the cache.  */
+  for (i = 0; VEC_iterate (tree, v, i, t); i++)
+    {
+      /* 1- Determine the token hunk H that owns T.  We first determine
+	 the PTH image and then which hunk inside that PTH image.  For
+	 choosing the hunk within the image, we simulate a second
+	 hash table lookup hashing the location to the token hunk.  */
+      pth_image *image, *hunk;
+      VEC(cp_token, heap) *head_tokens, *body_tokens;
+      cp_token *first;
+
+      head_tokens = pph_lookup_head_token_cache_for (t);
+      body_tokens = pph_lookup_body_token_cache_for (t);
+      if (VEC_empty (cp_token, head_tokens))
+	continue;
+      first = VEC_index (cp_token, head_tokens, 0);
+      fname = LOCATION_FILE (first->location);
+      slot = htab_find_slot (cache_dir, fname, INSERT);
+      if (*slot == NULL)
+	{
+	  image = XCNEW (pth_image);
+	  *slot = CONST_CAST (char *, fname);
+	}
+      else
+	image = *((pth_image **) slot);
+
+      /* For now, re-do the pth_image_lookup to simulate the lookup of
+	 the hunk within the image.  */
+      slot = htab_find_slot (cache_dir, fname, NO_INSERT);
+      hunk = *((pth_image **) slot);
+
+      /* Insert dummy uses for head_tokens, body_tokens and image.  */
+      if (i > VEC_length (tree, v))
+	{
+	  free (head_tokens);
+	  free (body_tokens);
+	  free (image);
+	}
+
+      /* 2- Find the tree cache associated with HUNK.  */
+      slot = pointer_map_insert (hunk_to_decls_map, hunk);
+      ast_cache = (VEC(tree, heap) *) *slot;
+
+      /* 3- Copy T and its type into the cache associated with HUNK.
+	 If T has a body (a FUNCTION_DECL), copy the body.  FIXME pph,
+	 copying ASTs will need new copying code to be implemented,
+	 the current routines do not handle everything that can be
+	 generated by the C++ FE.  */
+      t_copy = copy_decl (t);
+      if (!type_copy || TREE_TYPE (t))
+	type_copy = copy_type (TREE_TYPE (t));
+      if (TREE_CODE (t) == FUNCTION_DECL)
+	walk_tree (&DECL_SAVED_TREE (t_copy), copy_tree_r, (void *)1, NULL);
+      VEC_safe_push (tree, heap, ast_cache, t_copy);
+      *slot = (void *) ast_cache;
+    }
+
+  PPH_STATS_INCR (cached_decls, VEC_length (tree, v));
+
+  /* Restore tree and lookup catchers.  */
+  pph_tree_catcher = old_pph_tree_catcher;
+  pph_name_lookups = old_pph_name_lookups;
+
+  timevar_pop (TV_PPH_CACHE_IN);
+}
+
+
+/* Emulate the cache actions needed to get a declaration out of the
+   parser cache and instantiate it into the current compilation context.  */
+
+static void
+pph_copy_decls_outof_cache (VEC(tree, heap) *v)
+{
+  unsigned i;
+  tree t;
+  static VEC(tree, heap) *compilation_context = NULL;
+  VEC(tree, heap) *old_pph_tree_catcher, *old_pph_name_lookups;
+
+  /* Conceptually, this will be called with a token hunk that contains
+     all the declarations that we want to instantiate.  */
+  timevar_push (TV_PPH_CACHE_OUT);
+
+  /* We will be copying trees, which will call into the tree catching
+     routines.  Prevent that.  */
+  old_pph_tree_catcher = pph_tree_catcher;
+  old_pph_name_lookups = pph_name_lookups;
+  pph_tree_catcher = pph_name_lookups = NULL;
+
+  /* 1- Verify that the hunk is valid.  Traverse all the declarations
+     checking that none have been tainted.  */
+  for (i = 0; VEC_iterate (tree, v, i, t); i++)
+    {
+      /* If T is not valid, none of its users is valid.  */
+      if (1 || TREE_VISITED (t))
+	{
+	  unsigned j;
+	  tree r;
+	  for (j = 0; VEC_iterate (tree, v, j, r); j++)
+	    TREE_VISITED (r) = TREE_VISITED (r);
+	}
+    }
+
+  /* 2- Copy all the trees in the hunk to the current compilation context.  */
+  for (i = 0; VEC_iterate (tree, v, i, t); i++)
+    {
+      static tree t_copy, type_copy;
+      
+      t_copy = copy_decl (t);
+      if (!type_copy || TREE_TYPE (t))
+	type_copy = copy_type (TREE_TYPE (t));
+      if (TREE_CODE (t) == FUNCTION_DECL)
+	walk_tree (&DECL_SAVED_TREE (t_copy), copy_tree_r, (void *)1, NULL);
+
+      /* Emulate restoration into compilation context.  FIXME pph, this is
+	 missing the name lookups that may be required.  Estimate this
+	 separately from the number of name lookup operations and the
+	 time spent doing name lookups.  */
+      VEC_safe_push (tree, heap, compilation_context, t_copy);
+    }
+
+  PPH_STATS_INCR (restored_decls, VEC_length (tree, v));
+
+  /* Restore tree and lookup catchers.  */
+  pph_tree_catcher = old_pph_tree_catcher;
+  pph_name_lookups = old_pph_name_lookups;
+
+  timevar_pop (TV_PPH_CACHE_OUT);
+}
+
+static void
+pph_stop_exposed (cp_parser *parser, cp_token *first_token)
+{
+  if (flag_pph_debug >= 1 && !VEC_empty (tree, pph_tree_catcher))
+    {
+      cp_token *last_token;
+
+      timevar_push (TV_PPH_MANAGE);
+
+      last_token = parser->lexer->next_token;
+      pph_print_trees_tokens (pph_tree_catcher, first_token, last_token);
+      pph_copy_decls_into_cache (pph_tree_catcher);
+      pph_copy_decls_outof_cache (pph_tree_catcher);
+      PPH_STATS_INCR (cached_refs, VEC_length(tree, pph_name_lookups));
+      pph_free_catcher_memory ();
+      if (flag_pph_debug >= 4)
+        pph_log_exposed (parser, "stop");
+
+      timevar_pop (TV_PPH_MANAGE);
+    }
+}
+
+
 /* Declarations [gram.dcl.dcl] */
 
 /* Parse an optional declaration-sequence.
@@ -9358,18 +12235,34 @@ cp_parser_declaration (cp_parser* parser
       /* `template <>' indicates a template specialization.  */
       if (token2.type == CPP_LESS
 	  && cp_lexer_peek_nth_token (parser->lexer, 3)->type == CPP_GREATER)
-	cp_parser_explicit_specialization (parser);
+        {
+          cp_token *pph_first_token = pph_start_exposed (parser);
+	  cp_parser_explicit_specialization (parser);
+          pph_stop_exposed (parser, pph_first_token);
+        }
       /* `template <' indicates a template declaration.  */
       else if (token2.type == CPP_LESS)
-	cp_parser_template_declaration (parser, /*member_p=*/false);
+        {
+          cp_token *pph_first_token = pph_start_exposed (parser);
+	  cp_parser_template_declaration (parser, /*member_p=*/false);
+          pph_stop_exposed (parser, pph_first_token);
+        }
       /* Anything else must be an explicit instantiation.  */
       else
-	cp_parser_explicit_instantiation (parser);
+        {
+          cp_token *pph_first_token = pph_start_exposed (parser);
+	  cp_parser_explicit_instantiation (parser);
+          pph_stop_exposed (parser, pph_first_token);
+        }
     }
   /* If the next token is `export', then we have a template
      declaration.  */
   else if (token1.keyword == RID_EXPORT)
-    cp_parser_template_declaration (parser, /*member_p=*/false);
+    {
+      cp_token *pph_first_token = pph_start_exposed (parser);
+      cp_parser_template_declaration (parser, /*member_p=*/false);
+      pph_stop_exposed (parser, pph_first_token);
+    }
   /* If the next token is `extern', 'static' or 'inline' and the one
      after that is `template', we have a GNU extended explicit
      instantiation directive.  */
@@ -9378,7 +12271,11 @@ cp_parser_declaration (cp_parser* parser
 	       || token1.keyword == RID_STATIC
 	       || token1.keyword == RID_INLINE)
 	   && token2.keyword == RID_TEMPLATE)
-    cp_parser_explicit_instantiation (parser);
+    {
+      cp_token *pph_first_token = pph_start_exposed (parser);
+      cp_parser_explicit_instantiation (parser);
+      pph_stop_exposed (parser, pph_first_token);
+    }
   /* If the next token is `namespace', check for a named or unnamed
      namespace definition.  */
   else if (token1.keyword == RID_NAMESPACE
@@ -9404,8 +12301,12 @@ cp_parser_declaration (cp_parser* parser
   /* We must have either a block declaration or a function
      definition.  */
   else
-    /* Try to parse a block-declaration, or a function-definition.  */
-    cp_parser_block_declaration (parser, /*statement_p=*/false);
+    {
+      /* Try to parse a block-declaration, or a function-definition.  */
+      cp_token *pph_first_token = pph_start_exposed (parser);
+      cp_parser_block_declaration (parser, /*statement_p=*/false);
+      pph_stop_exposed (parser, pph_first_token);
+    }
 
   /* Free any declarators allocated.  */
   obstack_free (&declarator_obstack, p);
@@ -13205,6 +16106,9 @@ cp_parser_elaborated_type_specifier (cp_
 						    /*declarator=*/NULL))
 	    return error_mark_node;
 	  type = xref_tag (tag_type, identifier, ts, template_p);
+          if (flag_pph_debug >= 4)
+            fprintf (pph_logfile, "PPH: creating tag %p %s in hunk #\n",
+                     (void*)identifier, IDENTIFIER_POINTER (identifier));
 	}
     }
 
@@ -16748,6 +19652,8 @@ cp_parser_class_specifier (cp_parser* pa
       return error_mark_node;
     }
 
+  /* FIXME pph: Associate this struct definition with a token hunk.  */
+
   /* Process the base classes. If they're invalid, skip the 
      entire class body.  */
   if (!xref_basetypes (type, bases))
@@ -16878,6 +19784,7 @@ cp_parser_class_specifier (cp_parser* pa
   parser->in_unbraced_linkage_specification_p
     = saved_in_unbraced_linkage_specification_p;
 
+  /* FIXME pph: check to see if still in same token hunk.  */
   return type;
 }
 
@@ -18832,7 +21739,7 @@ cp_parser_label_declaration (cp_parser* 
    NULL_TREE otherwise.  */
 
 static tree
-cp_parser_lookup_name (cp_parser *parser, tree name,
+cp_parser_lookup_name_1 (cp_parser *parser, tree name,
 		       enum tag_types tag_type,
 		       bool is_template,
 		       bool is_namespace,
@@ -19075,6 +21982,27 @@ cp_parser_lookup_name (cp_parser *parser
   return decl;
 }
 
+
+/* Wrapper for cp_parser_lookup_name_1.  Call pph_catch_name_lookup
+   for every resolved NAME.  */
+
+static tree
+cp_parser_lookup_name (cp_parser *parser, tree name,
+		       enum tag_types tag_type,
+		       bool is_template,
+		       bool is_namespace,
+		       bool check_dependency,
+		       tree *ambiguous_decls,
+		       location_t name_location)
+{
+  tree t = cp_parser_lookup_name_1 (parser, name, tag_type, is_template,
+				    is_namespace, check_dependency,
+				    ambiguous_decls, name_location);
+  pph_catch_name_lookup (t);
+  return t;
+}
+
+
 /* Like cp_parser_lookup_name, but for use in the typical case where
    CHECK_ACCESS is TRUE, IS_TYPE is FALSE, IS_TEMPLATE is FALSE,
    IS_NAMESPACE is FALSE, and CHECK_DEPENDENCY is TRUE.  */
@@ -24720,6 +27648,1589 @@ cp_parser_omp_construct (cp_parser *pars
 
 static GTY (()) cp_parser *the_parser;
 
+/* PPH printing help.  */
+
+static void
+pph_debug_tree (tree t, bool body)
+{
+  if (t == NULL)
+    {
+      fprintf (pph_logfile, "nulldecl");
+      return;
+    }
+
+  if (!DECL_P (t))
+    {
+      fprintf (pph_logfile, "__%s__", tree_code_name[TREE_CODE (t)]);
+      return;
+    }
+
+  if (flag_pph_decls >= 2)
+    fprintf (pph_logfile, "%s ", tree_code_name[TREE_CODE (t)]);
+  fprintf (pph_logfile, "%d", (DECL_UID (t) << 1) + body);
+  if (flag_pph_decls >= 1)
+    fprintf (pph_logfile, " '%s'", get_name (t));
+  if (flag_pph_decls >= 3)
+    {
+      fprintf (pph_logfile, " ");
+      pph_debug_loc_of_tree (pph_logfile, t);
+      if (flag_pph_decls >= 4)
+        fprintf (pph_logfile, " @%p", (void *) t);
+    }
+}
+
+static void
+pph_debug_type (tree t, bool body)
+{
+  tree t_decl;
+  if (t == NULL)
+    {
+      fprintf (pph_logfile, "nulltype");
+      return;
+    }
+  t_decl = get_type_decl (t);
+  if (t_decl == NULL)
+    fprintf (pph_logfile, "nameless");
+  else
+    pph_debug_tree (t_decl, body);
+}
+
+/* Return true if tree T has been caught already.  */
+
+static bool
+pph_tree_caught_p (tree t)
+{
+  return (pph_lookup_head_token_cache_for (t) != NULL);
+}
+
+/* Collect the tokens needed for the head of DECL.
+   This assumes that the current token is positioned right after
+   the end of the declarator expression for DECL (i.e., it should
+   be called from grokdeclarator).  */
+
+static VEC(cp_token, heap) *
+pph_catch_head_tokens_for (tree t)
+{
+  cp_token *tok, *last, *first;
+  cp_lexer *lexer;
+  VEC(cp_token, heap) *tokens;
+
+  gcc_assert (t != error_mark_node);
+
+  tokens = pph_lookup_head_token_cache_for (t);
+  if (tokens)
+    {
+      fprintf (stderr, "*** ");
+      pph_debug_location (stderr, input_location);
+      fprintf (stderr, ": Tried to catch head tokens more than once for: ");
+      print_generic_stmt (stderr, t, 0);
+      gcc_unreachable ();
+    }
+
+  lexer = the_parser->lexer;
+
+  /* Look for the tokens backwards until the first brace or semicolon.  */
+  first = last = lexer->next_token;
+  for (tok = last - 1; tok >= VEC_address (cp_token, lexer->buffer); tok--)
+    {
+      if (tok->type == CPP_OPEN_BRACE
+	  || tok->type == CPP_CLOSE_BRACE
+	  || tok->type == CPP_SEMICOLON)
+	break;
+
+      first = tok;
+    }
+
+  /* Now include any trailing semicolon.  */
+  if (last->type == CPP_SEMICOLON)
+    last++;
+
+  /* Add all the tokens in [FIRST, LAST) to TOKENS.  */
+  for (tok = first; tok != last; tok++)
+    VEC_safe_push (cp_token, heap, tokens, tok);
+
+  pph_set_head_token_cache_for (t, tokens);
+
+  return tokens;
+}
+
+
+/* Collect the tokens needed for the body of DECL.
+   This assumes that the current token is positioned right after
+   the end of the declarator expression for DECL (i.e., it should
+   be called from grokdeclarator).  */
+
+static VEC(cp_token, heap) *
+pph_catch_body_tokens_for (tree t)
+{
+  cp_token *tok, *last, *first;
+  cp_lexer *lexer;
+  VEC(cp_token, heap) *tokens;
+
+  gcc_assert (t != error_mark_node);
+
+  tokens = pph_lookup_body_token_cache_for (t);
+  if (tokens)
+    {
+      fprintf (stderr, "*** ");
+      pph_debug_location (stderr, input_location);
+      fprintf (stderr, ": Tried to catch body tokens more than once for: ");
+      print_generic_stmt (stderr, t, 0);
+      gcc_unreachable ();
+    }
+
+  lexer = the_parser->lexer;
+
+  /* Look for the tokens forwards until the closing brace or semicolon.  */
+  first = last = lexer->next_token;
+  tok = first;
+  if (tok->type == CPP_EQ || tok->type == CPP_OPEN_PAREN)
+    {
+      /* Skip a variable-like definition.  Find the semicolon.  */
+      /* FIXME pph - This code changes with C++0x.  */
+      for (; tok <= VEC_last (cp_token, lexer->buffer); tok++)
+        if (tok->type == CPP_SEMICOLON)
+          break;
+      last = tok;
+    }
+  else if (tok->type == CPP_OPEN_BRACE || tok->type == CPP_COLON)
+    {
+      /* Skip a class-like or function-like definition.
+         Skip to a left brace, then skip to the matching right brace.  */
+      /* FIXME pph - This code changes with C++0x.  */
+      int nesting = 0;
+      for (; tok <= VEC_last (cp_token, lexer->buffer); tok++)
+        if (tok->type == CPP_OPEN_BRACE)
+          nesting++;
+        else if (tok->type == CPP_CLOSE_BRACE)
+          {
+            if ( nesting <= 1)
+              break;
+            else
+              nesting--;
+          }
+      last = tok;
+    }
+  else
+    return NULL; /* no body */
+
+  /* Add all the tokens in [FIRST, LAST) to TOKENS.  */
+  for (tok = first; tok <= last; tok++)
+    VEC_safe_push (cp_token, heap, tokens, tok);
+
+  pph_set_body_token_cache_for (t, tokens);
+
+  return tokens;
+}
+
+
+/* Return the dependencies for tree node T.  If HEADER_P is true, it
+   returns the dependencies for the header of T's declaration.
+   Otherwise, it returns dependencies for T's body.  */
+
+static VEC(tree,gc) *
+pph_lookup_dependencies_for (tree t, bool header_p)
+{
+  struct pointer_map_t *map;
+  void **slot;
+
+  map = (header_p) ? pph_decl_deps->header : pph_decl_deps->body;
+  slot = pointer_map_contains (map, t);
+  if (slot)
+    return ((VEC(tree,gc) *) *slot);
+
+  return NULL;
+}
+
+
+/* Set the dependencies for tree node T to DEPS.  If HEADER_P is true,
+   DEPS are the dependencies for T's header.  Otherwise, DEPS are the
+   dependencies for T's body.  */
+
+static void
+pph_set_dependencies_for (tree t, VEC(tree,gc) *deps, bool header_p)
+{
+  void **slot;
+  struct pointer_map_t *map;
+
+  map = (header_p) ? pph_decl_deps->header : pph_decl_deps->body;
+  slot = pointer_map_insert (map, t);
+  *slot = (void *) deps;
+}
+
+#define PPH_ARTIFICIAL(t) \
+(DECL_ARTIFICIAL (t) \
+&& !(TREE_CODE (t) == TYPE_DECL && DECL_IMPLICIT_TYPEDEF_P (t)))
+
+static bool
+is_namespace (tree container)
+{
+  enum tree_code code;
+
+  if (container == NULL)
+    return true;
+
+  code = TREE_CODE (container);
+  if (code == NAMESPACE_DECL)
+    return true;
+
+  return false;
+}
+
+/* Find the exposed declaration containing a symbol lookup.  */
+
+static tree
+pph_null_exposed (const char *reason)
+{
+  if (flag_pph_debug >= 3)
+    fprintf (pph_logfile, "%s\n", reason);
+  return NULL;
+}
+
+static tree
+pph_live_exposed (tree t, bool body)
+{
+  if (PPH_ARTIFICIAL (t))
+    return pph_null_exposed ("Artificial symbols are not exposed.");
+
+  if (flag_pph_debug >= 3)
+    {
+      if (t == NULL)
+        fprintf (pph_logfile, "(null)");
+      else
+        pph_debug_tree (t, body);
+      fprintf (pph_logfile, "\n");
+    }
+  return t;
+}
+
+static tree
+pph_find_exposed_for (tree t, bool *body)
+{
+  tree container;
+  enum tree_code code, t_code;
+  bool in_class;
+  *body = false; /* By default, we only depend on heads. */
+  for ( ; ; t = container, *body = true)
+    {
+      reclassify:
+      if (t == NULL)
+        {
+          PPH_STATS_INCR (bad_lookups, 1);
+          return pph_null_exposed ("NULLOID"); /* FIXME pph */
+        }
+      if (flag_pph_debug >= 3)
+        {
+          fprintf (pph_logfile, "      exposed for ");
+          pph_debug_tree (t, false);
+          fprintf (pph_logfile, " is ");
+        }
+      code = TREE_CODE (t);
+      switch (code)
+        {
+	  /* Types common to C and C++.  */
+	  case ARRAY_TYPE:
+	  case BOOLEAN_TYPE:
+	  case COMPLEX_TYPE:
+	  case ENUMERAL_TYPE:
+	  case FIXED_POINT_TYPE:
+	  case FUNCTION_TYPE:
+	  case INTEGER_TYPE:
+	  case LANG_TYPE:
+	  case METHOD_TYPE:
+	  case OFFSET_TYPE:
+	  case POINTER_TYPE:
+	  case QUAL_UNION_TYPE:
+	  case REAL_TYPE:
+	  case RECORD_TYPE:
+	  case REFERENCE_TYPE:
+	  case UNION_TYPE:
+	  case VECTOR_TYPE:
+	  case VOID_TYPE:
+	  /* C++-specific types.  */
+	  case BOUND_TEMPLATE_TEMPLATE_PARM:
+	  case TEMPLATE_TEMPLATE_PARM:
+	  case TEMPLATE_TYPE_PARM:
+	  case TYPENAME_TYPE:
+	  case TYPEOF_TYPE:
+	  case UNBOUND_CLASS_TEMPLATE:
+	  case TYPE_ARGUMENT_PACK:
+	  case TYPE_PACK_EXPANSION:
+	  case DECLTYPE_TYPE:
+            {
+              if (TYPE_NAME (t) == NULL)
+                return pph_null_exposed ("Anonymous Type");
+              else
+                t = TYPE_NAME (t);
+              /* FALLTHRU */
+            }
+
+          case TYPE_DECL:
+            {
+              container = DECL_CONTEXT (t);
+              in_class = container && CLASS_TYPE_P (container);
+              t_code = TREE_CODE (TREE_TYPE (t));
+              /* FIXME pph: Why DECL_TEMPLATE_TEMPLATE_PARM_P does not apply
+                 here?  It is a template template parameter, but the tree code
+                 is inconsistent.  */
+              if (DECL_TEMPLATE_PARM_P (t)
+                  || t_code == TEMPLATE_TEMPLATE_PARM
+                  || t_code == BOUND_TEMPLATE_TEMPLATE_PARM)
+                return pph_null_exposed ("TMPLPARM");
+
+              if (is_namespace (container))
+                return pph_live_exposed (t, *body);
+              break;
+            }
+
+          case VAR_DECL:
+            {
+              /* If var is lazy, depend on its body, not its head.  */
+              tree enclass = DECL_CONTEXT (t);
+              bool in_class = enclass && CLASS_TYPE_P (enclass);
+              bool defined = DECL_INITIAL (t) != NULL;
+                             /* FIXME pph: DECL_INITIALIZED_P (t)  */
+              if (defined && (in_class || !DECL_THIS_EXTERN (t))
+                  && DECL_INTEGRAL_CONSTANT_VAR_P (t))
+                *body = true;
+
+              container = DECL_CONTEXT (t);
+              in_class = container && CLASS_TYPE_P (container);
+              if (in_class && DECL_THIS_STATIC (t))
+                container = TYPE_CONTEXT (container);
+              if (is_namespace (container))
+                return pph_live_exposed (t, *body);
+	      break;
+            }
+
+          case FUNCTION_DECL:
+            {
+              /* If function is lazy, depend on body.  */
+              bool defined = DECL_INITIAL (t) != NULL;
+              if (defined && (DECL_DECLARED_INLINE_P (t)
+                              || DECL_USE_TEMPLATE (t) != 2))
+                *body = true;
+
+              container = DECL_CONTEXT (t);
+              in_class = container && CLASS_TYPE_P (container);
+              if (in_class)
+                container = TYPE_CONTEXT (container);
+              if (is_namespace (container))
+                return pph_live_exposed (t, *body);;
+              break;
+            }
+
+          case TEMPLATE_DECL:
+            {
+              int generic = DECL_USE_TEMPLATE (t);
+              if (generic != 2)
+                {
+                  t = DECL_TEMPLATE_RESULT (t);
+                  if (flag_pph_debug >= 3)
+                    fprintf (pph_logfile, "template redirected\n");
+                  goto reclassify;
+                }
+            }
+
+          case SCOPE_REF:
+            return pph_null_exposed ("SCOPE_REF"); /* FIXME pph */
+
+          case OVERLOAD:
+            return pph_null_exposed ("OVERLOAD"); /* FIXME pph */
+
+	  case BASELINK:
+	    container = BASELINK_BINFO (t);
+	    break;
+
+	  case TREE_BINFO:
+	    container = BINFO_TYPE (t);
+	    break;
+
+	  case TREE_LIST:
+	    t = TREE_VALUE (t);
+	    /* Fallthru  */
+
+          default:
+            {
+              if (t == NULL)
+                {
+                  PPH_STATS_INCR (bad_lookups, 1);
+                  return pph_null_exposed ("NULLOID"); /* FIXME pph */
+                }
+	      else if (!DECL_P (t))
+                {
+                  PPH_STATS_INCR (bad_lookups, 1);
+                  return pph_null_exposed ("BOZOID"); /* FIXME pph */
+                  /*FIXME pph:
+		  fatal_error ("Expecting a *_decl node.  Got %s",
+			       tree_code_name[TREE_CODE (t)]);
+                  */
+                }
+
+              container = DECL_CONTEXT (t);
+              in_class = container && CLASS_TYPE_P (container);
+              if (is_namespace (container))
+                return pph_null_exposed ("UNKNOWN");
+              break;
+            }
+        }
+      if (flag_pph_debug >= 3)
+        {
+          pph_debug_tree (container, *body);
+          fprintf (pph_logfile, "\n");
+        }
+    }
+}
+
+
+/* Collect the AST nodes that node T depends on.  HEADER_P is true if
+   we should collect ASTs from T's header.  Otherwise, we collect
+   ASTs from T's body.  */
+
+static VEC(tree,gc) *
+pph_catch_dependencies_for (tree t, bool header_p)
+{
+  VEC(cp_token, heap) *tokens;
+  unsigned i;
+  cp_token *tok;
+  VEC(tree,gc) *deps;
+
+  tokens = (header_p)
+	   ? pph_lookup_head_token_cache_for (t)
+	   : pph_lookup_body_token_cache_for (t);
+
+  if (tokens == NULL)
+    tokens = (header_p)
+	     ? pph_catch_head_tokens_for (t)
+	     : pph_catch_body_tokens_for (t);
+
+  deps = pph_lookup_dependencies_for (t, header_p);
+
+  for (i = 0; VEC_iterate (cp_token, tokens, i, tok); i++)
+    if (tok->type == CPP_NAME)
+      VEC_safe_push (tree, gc, deps, tok->u.value);
+
+  pph_set_dependencies_for (t, deps, header_p);
+
+  return deps;
+}
+
+
+/* Intercept tree node T by storing it in pph_tree_catcher and collecting
+   the tokens used in its instantiation.  */
+
+void
+pph_catch_tree (tree t)
+{
+  /* Only collect trees if the parser instantiated pph_tree_catcher
+     and we are currently parsing from the main lexer.  */
+  if (pph_tree_catcher && the_parser->lexer->buffer && !pph_tree_caught_p (t))
+    {
+      timevar_push (TV_PPH_MANAGE);
+
+      VEC_safe_push (tree, heap, pph_tree_catcher, t);
+      pph_catch_head_tokens_for (t);
+      pph_catch_body_tokens_for (t);
+      pph_catch_dependencies_for (t, true);
+      pph_catch_dependencies_for (t, false);
+
+      timevar_pop (TV_PPH_MANAGE);
+    }
+}
+
+
+/* Retract a caught tree.  */
+
+void
+pph_uncatch_tree (tree t)
+{
+  /* Only uncollect trees if the parser instantiated pph_tree_catcher
+     and we are currently parsing from the main lexer.  */
+  if (pph_tree_catcher && the_parser->lexer->buffer)
+    {
+      int i;
+      tree u;
+
+      timevar_push (TV_PPH_MANAGE);
+
+      /* Find the index; if present, remove it.  */
+      for (i = 0; VEC_iterate (tree, pph_tree_catcher, i, u); i++)
+        {
+          if (u == t)
+            {
+              VEC_ordered_remove (tree, pph_tree_catcher, i);
+              break;
+            }
+        }
+
+      timevar_pop (TV_PPH_MANAGE);
+    }
+}
+
+
+/* Given a set of tokens TOKENS, return the symbols from pph_name_lookups
+   that occur in TOKENS.  The returned vector is, then, the set of all
+   symbols that were resolved via name lookups during parsing.  This set
+   is a subset of all the CPP_NAME tokens in TOKENS.  */
+
+static void
+pph_locate_name_lookups_in (VEC(cp_token, heap) *tokens,
+                            VEC(tree,gc) **heads_found,
+                            VEC(tree,gc) **bodies_found)
+{
+  unsigned i;
+  tree t;
+  cp_token *first, *last;
+
+  *heads_found = NULL;
+  *bodies_found = NULL;
+
+  if (tokens == NULL || VEC_empty (cp_token, tokens))
+      return;
+
+  first = VEC_index (cp_token, tokens, 0);
+  last = VEC_last (cp_token, tokens);
+
+  for (i = 0; VEC_iterate (tree, pph_name_lookups, i, t); i++)
+    {
+      unsigned j;
+      cp_token *tok;
+      VEC(cp_token, heap) *lookup_locations;
+      void **slot;
+      bool pushed = false;
+
+      slot = pointer_map_contains (pph_nl_token_map, t);
+      gcc_assert (slot && *slot);
+      lookup_locations = (VEC(cp_token, heap) *) *slot;
+      for (j = 0; VEC_iterate (cp_token, lookup_locations, j, tok); j++)
+	{
+	  if (tok->location >= first->location
+	      && tok->location <= last->location)
+	    {
+              if (!pushed)
+                {
+                  bool body;
+                  tree exposed = pph_find_exposed_for (t, &body);
+                  if (exposed)
+                    {
+                      if (body)
+                        VEC_safe_push (tree, gc, *bodies_found, exposed);
+                      else
+                        VEC_safe_push (tree, gc, *heads_found, exposed);
+                      pushed = true;
+                    }
+                }
+
+              /* Avoid double-counting lookups by removing the lookup
+                 location after a class member declaration has found it.
+                 To make that work, we must remove all redundant entries.  */
+              if (flag_pph_debug >= 4)
+                {
+                  fprintf (pph_logfile, "      lookup in %p for ",
+                           (void*)lookup_locations);
+                  pph_debug_tree (t, false);
+                  fprintf (pph_logfile, " found at ");
+                  pph_debug_location (pph_logfile, tok->location);
+                  fprintf (pph_logfile, "\n");
+                  fprintf (pph_logfile, "        vector length from %d",
+                           VEC_length (cp_token, lookup_locations));
+                  
+                }
+              /* This code is slow, but VEC won't let me null entires.  */
+              VEC_ordered_remove (cp_token, lookup_locations, j);
+              /* We have just shifted down all later entries,
+                 and need to counteract the upcoming index increment.  */
+              j--;
+              if (flag_pph_debug >= 4)
+                {
+                  fprintf (pph_logfile, " to %d\n",
+                           VEC_length (cp_token, lookup_locations));
+                }
+	    }
+	}
+    }
+}
+
+
+/* Print all the trees in V and the tokens in the token range
+   [TOK1, TOK2).  */
+
+static VEC(cp_token, heap) *
+pph_print_copy_tokens (cp_token *tok1, cp_token *tok2)
+{
+  cp_token *tok;
+  VEC(cp_token, heap) *vtok;
+
+  /* If TOK2 is CPP_EOF, it will have the address of eof_token, which
+     will make the loop below go off the deep end.  Detect this and
+     make TOK2 the last token in the lexer buffer instead.  */
+  if (tok2 == &eof_token)
+    tok2 = VEC_last (cp_token, the_parser->lexer->buffer);
+
+  vtok = NULL;
+  for (tok = tok1; tok != tok2; tok++)
+    VEC_safe_push (cp_token, heap, vtok, tok);
+
+  return vtok;
+}
+
+static void
+pph_print_token_range (VEC(tree,heap) *v, VEC(cp_token, heap) *vtok)
+{
+  unsigned i;
+  tree t;
+
+  if (flag_pph_debug >= 4)
+    {
+      fprintf (pph_logfile, "PPH: hunk location ");
+      pph_debug_location (pph_logfile, VEC_index (cp_token, vtok, 0)->location);
+      fprintf (pph_logfile, " to ");
+      pph_debug_location (pph_logfile, VEC_last (cp_token, vtok)->location);
+      fprintf (pph_logfile, "\n");
+      fprintf (pph_logfile, "PPH: hunk tokens ");
+      cp_lexer_dump_tokens (stderr, (VEC(cp_token, gc) *)vtok, 0);
+      fprintf (pph_logfile, "PPH: hunk ASTs:\n");
+      for (i = 0; VEC_iterate (tree, v, i, t); i++)
+        {
+          pph_debug_tree (t, true);
+          /* FIXME pph: this may not be right; we may not care.  */
+          print_generic_stmt (stderr, t, 0);
+        }
+      fprintf (pph_logfile, "PPH: hunk decls:\n");
+    }
+}
+
+static void
+pph_print_dependence (bool user_body, bool used_body,
+                      tree t, tree d)
+{
+  static bool prior_user_body = false;
+  static bool prior_used_body = false;
+  static tree prior_t = NULL;
+  static tree prior_d = NULL;
+  if (flag_pph_debug >= 2)
+    {
+      fprintf (pph_logfile, "    pd_base ");
+      pph_debug_tree (t, user_body);
+      fprintf (pph_logfile, " on ");
+      pph_debug_tree (d, used_body);
+      fprintf (pph_logfile, "\n");
+    }
+  if (t && d && DECL_P (t) && DECL_P (d) && (t != d || user_body != used_body))
+    {
+      if (PPH_ARTIFICIAL (t))
+        {
+          /* Okay, find the real symbol this articial one belongs to.  */
+          d = pph_find_exposed_for (d, &used_body);
+          if (d == NULL)
+            return;
+          used_body = true;
+        }
+      if (user_body != prior_user_body
+          || used_body != prior_used_body
+          || t != prior_t
+          || d != prior_d)
+        {
+          fprintf (pph_logfile, "depend ");
+          pph_debug_tree (t, user_body);
+          fprintf (pph_logfile, " uses ");
+          pph_debug_tree (d, used_body);
+          fprintf (pph_logfile, "\n");
+          prior_user_body = user_body;
+          prior_used_body = used_body;
+          prior_t = t;
+          prior_d = d;
+        }
+    }
+}
+
+static void
+pph_print_depend_template (tree tmpl_info, tree t)
+{
+  tree tmpl_decl;
+  tree tmpl_ptrn;
+  if (flag_pph_debug >= 2)
+    {
+      fprintf (pph_logfile, "    pd_template ");
+      pph_debug_tree (t, true);
+      fprintf (pph_logfile, " %p", (void*)tmpl_info);
+      fprintf (pph_logfile, "\n");
+    }
+  tmpl_decl = TI_TEMPLATE (tmpl_info);
+  if (TREE_CODE (tmpl_decl) == OVERLOAD)
+    tmpl_decl = OVL_CURRENT (tmpl_decl);
+  tmpl_ptrn = DECL_TEMPLATE_RESULT (tmpl_decl);
+  if (tmpl_ptrn && t != tmpl_ptrn)
+    {
+      /* This is a template, but not the pattern.  */
+      pph_print_dependence (true, true, t, tmpl_ptrn);
+    }
+}
+
+/* Print the dependence of a head of declaration 
+   on the body of a type that the head uses directly.
+   If either of these is not exposed,
+   find the body of the exposed declaration that contains it.  */
+
+static void
+pph_print_depend_decl (tree user, tree used)
+{
+  if (flag_pph_debug >= 2)
+    {
+      fprintf (pph_logfile, "    pd_decl ");
+      pph_debug_tree (user, false);
+      fprintf (pph_logfile, " on ");
+      pph_debug_tree (used, false);
+      fprintf (pph_logfile, "\n");
+    }
+  if (user != NULL)
+    {
+      if (used != NULL)
+        {
+          bool body;
+          tree exp_for_user = pph_find_exposed_for (user, &body);
+          tree exp_for_used = pph_find_exposed_for (used, &body);
+          if (exp_for_user && exp_for_used && exp_for_user != exp_for_used)
+            pph_print_dependence (exp_for_user != user, true,
+                                  exp_for_user, exp_for_used);
+        }
+    }
+}
+
+static void
+pph_print_depend_type (tree decl, tree type)
+{
+  tree type_decl;
+  if (flag_pph_debug >= 2)
+    {
+      fprintf (pph_logfile, "    pd_type ");
+      pph_debug_tree (decl, false);
+      fprintf (pph_logfile, " on ");
+      pph_debug_type (type, false);
+      fprintf (pph_logfile, "\n");
+    }
+  if (type != NULL)
+    {
+      type_decl = get_type_decl (type);
+      pph_print_depend_decl (decl, type_decl);
+    }
+}
+
+static void
+pph_print_depend_type_type (tree t)
+{
+  tree t_type;
+  tree field;
+
+  if (flag_pph_debug >= 2)
+    {
+      fprintf (pph_logfile, "    depending on typedecl type ");
+      pph_debug_tree (t, false);
+      fprintf (pph_logfile, "\n");
+    }
+
+  t_type = TREE_TYPE (t);
+  field = TYPE_FIELDS (t_type); 
+  for (; field; field = TREE_CHAIN(field))
+    {
+      if (flag_pph_debug >= 2)
+        {
+          fprintf (pph_logfile, "    field ");
+          pph_debug_tree (field, false);
+        }
+      if (TREE_CODE (field) == FIELD_DECL)
+        {
+          tree f_type = TREE_TYPE (field);
+          if (flag_pph_debug >= 2)
+            {
+              fprintf (pph_logfile, " of type ");
+              pph_debug_type (f_type, false);
+              if (DECL_FIELD_IS_BASE (field))
+                fprintf (pph_logfile, " is a base field!!\n");
+              else
+                fprintf (pph_logfile, " is a plain field\n");
+            }
+          pph_print_depend_type (t, f_type);
+        }
+      else if (TREE_CODE (field) == TYPE_DECL)
+        {
+        tree f_type = TREE_TYPE (field);
+        if (flag_pph_debug >= 2)
+          {
+            fprintf (pph_logfile, " of type ");
+            pph_debug_type (f_type, false);
+            fprintf (pph_logfile, " is a type field\n");
+          }
+        pph_print_depend_type (t, f_type);
+        }
+    }
+
+  if (flag_pph_debug >= 2)
+    {
+      fprintf (pph_logfile, "    end of fields\n");
+    }
+}
+
+static void
+pph_print_depend_func_type (tree t)
+{
+  /* We must print a dependence of the head of the function
+     on the body of the types of its signature.  */
+
+  tree args;
+  tree func_type;
+
+  if (flag_pph_debug >= 2)
+    {
+      fprintf (pph_logfile, "    depending on function type ");
+      pph_debug_tree (t, false);
+      fprintf (pph_logfile, "\n");
+    }
+
+  func_type = TREE_TYPE (t);
+  pph_print_depend_type (t, TREE_TYPE (func_type)); /* return type */
+  for (args = TYPE_ARG_TYPES (func_type); args; args = TREE_CHAIN (args))
+    pph_print_depend_type (t, TREE_VALUE (args)); /* parameter */
+
+  if (DECL_VIRTUAL_P (t))
+    {
+      tree ctx_type = DECL_CONTEXT (t);
+      tree ctx_decl = get_type_decl (ctx_type);
+      /* Virtual functions depend on containing class's body.*/
+      pph_print_depend_type (t, ctx_type);
+      /* The virtual class's body also depends on the function
+         for construction of the vtable. */
+      pph_print_dependence (true, true, ctx_decl, t);
+    }
+}
+
+static void
+pph_print_depend_var_type (tree t)
+{
+  if (flag_pph_debug >= 2)
+    {
+      fprintf (pph_logfile, "    depending on var/field type ");
+      pph_debug_tree (t, false);
+      fprintf (pph_logfile, "\n");
+    }
+
+  pph_print_depend_type (t, TREE_TYPE (t));
+}
+
+enum decl_exposure { HIDDEN, EXPOSED, NEEDED };
+
+static enum decl_exposure
+pph_get_decl_exposure (tree t)
+{
+  tree container;
+  tree type;
+  tree tmpl_info;
+  bool defined = false;
+  bool inlined = false;
+  bool needed = false;
+  int generic = 0;
+  enum tree_code code = TREE_CODE (t);
+
+  if (flag_pph_debug >= 2)
+    {
+      fprintf (pph_logfile, "    get_exposure for ");
+      pph_debug_tree (t, false);
+      fprintf (pph_logfile, "\n");
+    }
+
+  /* For DECL_USE_TEMPLATE and CLASSTYPE_USE_TEMPLATE,
+      1=implicit instantiation
+      2=partial or explicit specialization, e.g.: 
+	  template <> int min<int> (int, int),
+      3=explicit instantiation, e.g.:
+	  template int min<int> (int, int);
+  */
+
+  if (code == TYPE_DECL)
+    {
+      pph_print_depend_type_type (t);
+
+      container = DECL_CONTEXT (t);
+      if (!is_namespace (container))
+        return HIDDEN;
+      type = TREE_TYPE (t);
+      defined = COMPLETE_TYPE_P (type);
+
+      /* FIXME pph: Why DECL_TEMPLATE_TEMPLATE_PARM_P does not apply
+         here?  It is a template template parameter, but the tree code
+         is inconsistent.  */
+      if (DECL_TEMPLATE_PARM_P (t)
+          || TREE_CODE (type) == TEMPLATE_TEMPLATE_PARM
+          || TREE_CODE (type) == BOUND_TEMPLATE_TEMPLATE_PARM)
+        return HIDDEN;
+      if (CLASS_TYPE_P (t))
+        {
+          tmpl_info = CLASSTYPE_TEMPLATE_INFO (type);
+          generic = CLASSTYPE_USE_TEMPLATE (type);
+        }
+      else
+        {
+          tmpl_info = NULL;
+          generic = 0;
+        }
+      if (generic == 1)
+        return HIDDEN;
+      if (tmpl_info != NULL)
+        {
+          pph_print_depend_template (tmpl_info, t);
+          needed = defined && generic == 3;
+        }
+      else
+        {
+          needed = false;
+        }
+    }
+  else if (code == VAR_DECL)
+    {
+      tree enclass = DECL_CONTEXT (t);
+      bool in_class = enclass && CLASS_TYPE_P (enclass);
+      /* If the VAR_DECL is in a class, it must be a static member.  */
+      container = enclass;
+      if (in_class)
+        container = TYPE_CONTEXT (enclass);
+
+      pph_print_depend_var_type (t);
+
+      if (!is_namespace (container))
+          return HIDDEN;
+      defined = DECL_INITIAL (t) != NULL /* FIXME pph: DECL_INITIALIZED_P (t)  */;
+      type = TREE_TYPE (t);
+      needed = !((!defined && (in_class || DECL_THIS_EXTERN (t)))
+                 || DECL_INTEGRAL_CONSTANT_VAR_P (t));
+      if (in_class)
+        {
+          tmpl_info = DECL_TEMPLATE_INFO (t);
+          generic = DECL_USE_TEMPLATE (t);
+          if (generic == 1)
+            return HIDDEN;
+        }
+      else
+        {
+          tmpl_info = NULL;
+          generic = 0;
+        }
+      if (tmpl_info != NULL)
+        {
+          pph_print_depend_template (tmpl_info, t);
+          needed = needed && generic == 3;
+        }
+    }
+  else if (code == FUNCTION_DECL)
+    {
+      if (flag_pph_debug >= 2)
+        {
+          fprintf (pph_logfile, "    depending on function ");
+          pph_debug_tree (t, false);
+          fprintf (pph_logfile, "\n");
+        }
+
+      container = DECL_CONTEXT (t);
+      if (container && CLASS_TYPE_P (container))
+          container = TYPE_CONTEXT (DECL_CONTEXT (t));
+
+      pph_print_depend_func_type (t);
+
+      if (!is_namespace (container))
+        return HIDDEN;
+      inlined = DECL_DECLARED_INLINE_P (t);
+      defined = DECL_INITIAL (t) != NULL;
+      tmpl_info = DECL_TEMPLATE_INFO (t);
+      generic = DECL_USE_TEMPLATE (t);
+      if (tmpl_info != NULL)
+        {
+          if (generic == 2)
+              needed = defined && !inlined;
+          else
+            {
+              pph_print_depend_template (tmpl_info, t);
+              needed = defined && !inlined && generic == 3;
+            }
+        }
+      else
+        {
+          needed = defined && !inlined;
+        }
+    }
+  else
+    {
+      gcc_assert (code < MAX_TREE_CODES);
+      return HIDDEN;
+    }
+
+  if (needed)
+    return NEEDED;
+  else
+    return EXPOSED;
+}
+
+static void
+pph_print_dependences (bool user_body, bool used_body,
+                        tree t, VEC(tree,gc) *deps)
+{
+  unsigned j;
+  tree d;
+  for (j = 0; VEC_iterate (tree, deps, j, d); j++)
+      pph_print_dependence (user_body, used_body, t, d);
+}
+
+/* Print the head of declaration T and its dependencies. N_HEAD_TOKENS
+   is the number of tokens taken by T's head.  N_HEAD_ITOKENS is the
+   number of invisible tokens.
+   
+   HEAD_TOKENS is the array of tokens in the head (note that the
+   length of this array may be different than N_HEAD_TOKENS, due to
+   adjustments made by the caller).
+
+   If CONTAINER is set, then T is a member of it.  */
+
+static void
+pph_print_declaration_head (tree t, bool artificial, tree container,
+                            unsigned n_head_tokens, unsigned n_head_invis,
+			    VEC(cp_token, heap) *head_tokens)
+{
+  VEC(tree,gc) *sym_head_deps, *sym_body_deps;
+  enum tree_code code = TREE_CODE (t);
+
+  fprintf (pph_logfile, "declaration ");
+  pph_debug_tree (t, false);
+
+  fprintf (pph_logfile, " htok %u,%u", n_head_tokens, n_head_invis);
+
+  /*FIXME pph: We want to get rid of most artificial tokens;
+    this is temporary to find them.  */
+  if (artificial)
+    fprintf (pph_logfile, " artificial");
+
+  if (container)
+    {
+      fprintf (pph_logfile, " mbrof ");
+      pph_debug_tree (get_type_decl (container), true);
+    }
+
+  fprintf (pph_logfile, "\n");
+
+  /* Template instances should depend on their pattern body.  */
+  if (artificial)
+    {
+      if (code == TYPE_DECL)
+        {
+          tree t_type = TREE_TYPE (t);
+          if (CLASS_TYPE_P (t_type))
+            {
+              tree tmpl_info = CLASSTYPE_TEMPLATE_INFO (t_type);
+              if (tmpl_info != NULL)
+                pph_print_depend_template (tmpl_info, t);
+            }
+        }
+      else if (code == VAR_DECL || code == FUNCTION_DECL)
+        {
+          tree tmpl_info;
+          tmpl_info = (DECL_LANG_SPECIFIC (t)) ? DECL_TEMPLATE_INFO (t) : NULL;
+          if (tmpl_info != NULL)
+            pph_print_depend_template (tmpl_info, t);
+        }
+    }
+  else
+    {
+      /* From the name dependencies, determine symbol dependencies
+	 by correlating the location of the looked-up symbols with
+	 the tokens in HEAD_TOKENS and BODY_TOKENS.  */
+      if (flag_pph_debug >= 2)
+        fprintf (pph_logfile, "  begin normal dependences\n");
+      pph_locate_name_lookups_in (head_tokens, &sym_head_deps, &sym_body_deps);
+      pph_print_dependences (false, false, t, sym_head_deps);
+      pph_print_dependences (true, false, t, sym_body_deps);
+      if (flag_pph_debug >= 2)
+        fprintf (pph_logfile, "  end normal dependences\n");
+    }
+}
+
+
+/* Print the head of declaration T and its dependencies. N_BODY_TOKENS
+   is the number of tokens taken by T's head.  N_BODY_ITOKENS is the
+   number of invisible tokens.
+
+   BODY_TOKENS is the array of tokens in the head (note that the
+   length of this array may be different than N_BODY_TOKENS, due to
+   adjustments made by the caller).
+
+   EXPOSURE indicates the exposure of T.
+
+   N_SUBTOKENS is the number of tokens that declarations inside T's
+   body have used up, those should be subtracted from the total number
+   of tokens in T to avoid double counting. */
+
+static void
+pph_print_declaration_body (tree t, bool artificial,
+                            enum decl_exposure exposure,
+                            unsigned n_body_tokens, unsigned n_body_invis,
+                            VEC(cp_token, heap) *body_tokens)
+{
+  VEC(tree,gc) *sym_head_deps, *sym_body_deps;
+  const char* msg;
+
+  fprintf (pph_logfile, "declaration ");
+  pph_debug_tree (t, true);
+
+  fprintf (pph_logfile, " btok %u,%u", n_body_tokens, n_body_invis);
+
+  /* FIXME pph: We want to get rid of most artificial tokens;
+     this is temporary to find them.  */
+  if (artificial)
+    msg = "artificial";
+  else if (exposure == NEEDED)
+    msg = "needed";
+  else
+    msg = "lazy";
+  fprintf (pph_logfile, " %s", msg);
+
+  fprintf (pph_logfile, "\n");
+
+  pph_print_dependence (true, false, t, t); /* body depends on its head */
+
+  if (flag_pph_debug >= 2)
+    fprintf (pph_logfile, "  begin normal dependences\n");
+  pph_locate_name_lookups_in (body_tokens, &sym_head_deps, &sym_body_deps);
+  pph_print_dependences (true, false, t, sym_head_deps);
+  pph_print_dependences (true, false, t, sym_body_deps);
+  if (flag_pph_debug >= 2)
+    fprintf (pph_logfile, "  end normal dependences\n");
+}
+
+
+/* Compute the implicit cost of a method F.  */
+
+static unsigned
+pph_find_special_methods (tree f)
+{
+  unsigned found = 0;
+  tree o;
+  if (TREE_CODE (f) == OVERLOAD)
+    {
+      for (o = f; o; o = OVL_NEXT (o))
+        found |= pph_find_special_methods (OVL_CURRENT (o));
+    }
+  else if (TREE_CODE (f) == TEMPLATE_DECL)
+    found |= pph_find_special_methods (DECL_TEMPLATE_RESULT (f));
+  else
+    {
+      gcc_assert (TREE_CODE (f) == FUNCTION_DECL);
+      if (DECL_ARTIFICIAL (f))
+        return found;
+      if (DECL_CONSTRUCTOR_P (f))
+        if (DECL_COPY_CONSTRUCTOR_P (f))
+          found |= (1<<2); /* copy constructor */
+        else
+          found |= (1<<1); /* default constructor */
+      else if (DECL_DESTRUCTOR_P (f))
+        found |= (1<<0); /* destructor */
+      else if (DECL_ASSIGNMENT_OPERATOR_P (f))
+        found |= (1<<3); /* copy assign op */
+    }
+  return found;
+}
+
+/* Compute the implicit cost of a class type T_TYPE.  */
+
+static unsigned
+pph_implicit_class_cost (tree t_type)
+{
+  VEC(tree,gc) *methods;
+  unsigned idx;
+  unsigned mbrs;
+  unsigned cost = 0;
+  unsigned found = 0;
+
+  /* Gather general statistics.  */
+  unsigned fields = fields_length (t_type); /* also direct bases */
+  unsigned vptr = (TYPE_POLYMORPHIC_P (t_type) ? 1 : 0);
+  unsigned slots = fields + vptr;
+  unsigned vbases = VEC_length (tree, CLASSTYPE_VBASECLASSES (t_type));
+  unsigned vtables = list_length (CLASSTYPE_VTABLES (t_type));
+
+  /* Assign cost of implicit special member variables.  */
+  /* These costs are somewhat arbitrary.  */
+  cost += 20 * (CLASSTYPE_TYPEINFO_VAR (t_type) != NULL); /* typeinfo */
+  cost += 4 * vbases * vtables; /* virtual tables */
+
+  /* Assign cost of implicit special member functions.  */
+  /* First find them.  */
+  methods = CLASSTYPE_METHOD_VEC (t_type);
+  if (methods != NULL)
+    for (idx = 0;  idx < VEC_length (tree, methods);  idx++)
+      {
+        tree ovld = VEC_index (tree, methods, idx);
+        if (ovld)
+          found |= pph_find_special_methods (ovld);
+      }
+  /* These costs are somewhat arbitrary.  */
+  /* FIXME pph: These queries seem to not work for templates.
+     We can accept the inaccuracy for now.  */
+  mbrs =  slots * 2 + vbases * 4;
+  if (!(found & (1<<2))) /* copy constructor */
+    {
+      if (TYPE_HAS_TRIVIAL_COPY_ASSIGN (t_type))
+        cost += 4;
+      else
+        cost += (8 + 2*mbrs) * (vbases > 0 ? 2 : 1);
+    }
+  if (!(found & (1<<1))) /* default constructor */
+    if (!TYPE_HAS_TRIVIAL_DFLT (t_type))
+      cost += 4 + mbrs;
+  if (!(found & (1<<0))) /* destructor */
+    if (!TYPE_HAS_TRIVIAL_DESTRUCTOR (t_type))
+      cost += (8 + 2*mbrs) * (vbases > 0 ? 2 : 1);
+  if (!(found & (1<<3))) /* copy assign op */
+    {
+      if (TYPE_HAS_TRIVIAL_COPY_ASSIGN (t_type))
+        cost += 4;
+      else
+        cost += (8 + 2*mbrs);
+    }
+
+  return cost;
+}
+
+
+/* Print declaration T with the given EXPOSURE.  If T has a body with
+   N tokens, subtract N_SUBTOKENS from it before printing them.  
+   This is used when printing class declarations.  The caller first
+   prints all the declarations inside the class, followed by the 
+   class declaration itself, to avoid double counting tokens in the class
+   body, they are subtracted from the total count.
+
+   PRIMARY is true when T is the very first declaration captured
+   during a pph_start_exposed/pph_stop_exposed region.  If T is a
+   member of a class, and it happens to be the first declaration
+   captured, it means that T is an out-of-line definition.
+   
+   If T is a member of a class, and PARENT is the TYPE_DECL for that
+   class, it means that we are printing the in-class declaration of T.
+   In that case, when we print the parent, we should subtract the
+   tokens attributed to T.  So, in these cases return the total
+   number of tokens printed in T's head and body.  Otherwise,
+   return 0.  */
+
+static unsigned
+pph_print_declaration (tree t, enum decl_exposure exposure,
+                       unsigned n_subtokens, bool primary, tree parent)
+{
+  tree container = NULL;
+  enum tree_code code = TREE_CODE (t);
+  bool is_member = false;
+  bool artificial = PPH_ARTIFICIAL (t);
+  bool print_head = true, print_body = true;
+
+  /* The cost of a declaration is proportional to the number of tokens.
+     Artificial symbols are not represented in the file, so they do
+     not have tokens.  We represent their cost as a number of
+     invisible tokens.  */
+  VEC(cp_token, heap) *head_tokens = pph_lookup_head_token_cache_for (t);
+  unsigned n_head_tokens = VEC_length (cp_token, head_tokens);
+  unsigned n_head_invis = 0;
+  VEC(cp_token, heap) *body_tokens = pph_lookup_body_token_cache_for (t);
+  unsigned n_body_tokens = VEC_length (cp_token, body_tokens);
+  unsigned n_body_invis = 0;
+  unsigned n_member_tokens = 0;
+
+  /* If this is a member of a class, count the number of tokens in the
+     member that overlap with the containing class. */
+  if (code == FUNCTION_DECL || code == VAR_DECL)
+    {
+      container = DECL_CONTEXT (t);
+      if (container && CLASS_TYPE_P (container))
+	{
+	  is_member = true;
+	  if (parent && container == TREE_TYPE (parent))
+	    n_member_tokens = n_head_tokens + n_body_tokens;
+	}
+    }
+
+  /* Now we need to adjust costs, and head/body printing.  */
+
+  if (code == VAR_DECL)
+    {
+      if (artificial)
+        /* Artificial static member variables get their token
+           counts from the calling expression, which isn't helpful.
+           Build the cost into the class instead.  All of which
+           means suppress this decl.  */
+        return 0;
+
+      if (DECL_NONTRIVIALLY_INITIALIZED_P (t)
+          && !DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (t))
+        n_body_invis += 10;
+
+      if (is_member)
+        {
+          if (primary) /* Must be an out-of-line declaration/definition.  */
+            {
+              /* Merge out-of-line member definitions into the body.
+                 This merge prevents two heads for the same symbol.  */
+              n_body_tokens += n_head_tokens;
+              n_body_invis += n_head_invis;
+              n_head_tokens = 0;
+              n_head_invis = 0;
+              print_head = false;
+              /* Out-of-line member variable declarations are definitions,
+                 and hence need to be generated.  */
+              exposure = NEEDED;
+            }
+          else /* Not primary; must be an in-line declaration/definition.  */
+            {
+              /* The only var bodies in the class are manifest constants;
+                 merge them into the head.  */
+              n_head_tokens += n_body_tokens;
+              n_head_invis += n_body_invis;
+              n_body_tokens = 0;
+              n_body_invis = 0;
+              print_body = false;
+	    }
+        }
+      else /* not is_member */
+        {
+          if (exposure == EXPOSED)
+            {
+              /* Merge manifest constants into the head.
+                 This code sweeps up extern declarations with no bodies,
+                 but that's okay.  */
+              n_head_tokens += n_body_tokens;
+              n_head_invis += n_body_invis;
+              n_body_tokens = 0;
+              n_body_invis = 0;
+              print_body = false;
+            }
+        }
+      if (exposure == NEEDED)
+        n_body_invis += 4; /* For emitting the actual variable declaration. */
+    }
+  else if (code == FUNCTION_DECL)
+    {
+      /* Pure function declarations get no body.  */
+      if (exposure == EXPOSED && !artificial
+          && n_body_tokens == 0 && n_body_invis == 0)
+        print_body = false;
+      if (artificial)
+        {
+          if (is_member)
+            {
+              /* Artificial special member functions get their token
+                 counts from the calling expression, which isn't helpful.
+                 Build the cost into the class instead.  All of which
+                 means suppress this decl.  */
+              return 0;
+            }
+        }
+    }
+  else if (code == TYPE_DECL)
+    {
+      tree t_type = TREE_TYPE (t);
+
+      if (artificial)
+        return 0;
+
+      if (DECL_IMPLICIT_TYPEDEF_P (t)
+	  && CLASS_TYPE_P (t_type)
+          && TYPE_LANG_SPECIFIC (t_type))
+        {
+          tree tmpl_info;
+          int generic;
+
+          n_body_invis += pph_implicit_class_cost (t_type);
+
+          tmpl_info = CLASSTYPE_TEMPLATE_INFO (t_type);
+          if (tmpl_info)
+            {
+              generic = CLASSTYPE_USE_TEMPLATE (t_type);
+              if (generic == 1)
+                {
+                  /* Implicit instantiations have no visibile tokens.  */
+                  n_head_invis += n_head_tokens;
+                  n_head_tokens = 0;
+                  n_body_invis += n_body_tokens;
+                  n_body_tokens = 0;
+                }
+              else if (generic == 3)
+                {
+                  /* Explicit instantiations have no bodies,
+                     but they are work.  This approximation is
+                     unjustified, but we are presuming that explicit
+                     instantiations are rare in application code. */
+                  n_body_invis += 15;
+                  exposure = NEEDED;
+                }
+              if (generic != 2)
+                {
+                  bool defined;
+                  defined = CLASS_TYPE_P (t_type) && COMPLETE_TYPE_P (t_type);
+                  if (defined)
+                    pph_print_depend_template (tmpl_info, t);
+                }
+            }
+          else if (primary)
+            {
+              /* Now subtract the sum of members from the body of the class.
+                 This prevents double counting when emitting the parent.
+                 For non-primary class symbols, this value will be zero.  */
+              gcc_assert (n_body_tokens >= n_subtokens);
+	      n_body_tokens -= n_subtokens;
+            }
+        }
+      else if (TREE_CODE (t_type) == ENUMERAL_TYPE)
+        {
+          /* No additional work for enum.  */
+        }
+      else
+        {
+          /* Not artificial, not a C++ class, not an enum;
+             so must be a pure typedef.  They have no body.  */
+          gcc_assert (n_body_tokens == 0 && n_body_invis == 0);
+          print_body = false;
+        }
+    }
+
+  if (print_head)
+    pph_print_declaration_head (t, artificial, (is_member) ? container : NULL,
+                                n_head_tokens, n_head_invis, head_tokens);
+
+  if (print_body)
+    pph_print_declaration_body (t, artificial, exposure,
+                                n_body_tokens, n_body_invis, body_tokens);
+
+  if (flag_pph_debug >= 4)
+    {
+      fprintf (pph_logfile, "    Declarator head tokens: ");
+      cp_lexer_debug_tokens ((VEC(cp_token, gc) *)head_tokens);
+      fprintf (pph_logfile, "    Declarator body tokens: ");
+      cp_lexer_debug_tokens ((VEC(cp_token, gc) *)body_tokens);
+      fprintf (pph_logfile, "\n");
+    }
+
+  return n_member_tokens;
+}
+
+static void
+pph_print_declarations (VEC(tree,heap) *v)
+{
+  unsigned i, j, n, first_ix;
+  tree t, first, parent;
+  enum decl_exposure exposure;
+  unsigned n_subtokens = 0;
+
+  if (VEC_empty (tree, v))
+    return;
+
+  /* If the first AST in V is a class/structure declaration, process
+     the sub-declarations first.  This will accumulate the tokens in
+     the sub-declarations, so that when we print the class itself
+     we don't double count the tokens in its body.  */
+
+  /* Skip over un-exposed declarations, like template parameters. */
+  n = VEC_length (tree, v);
+  for ( i = 0; VEC_iterate (tree, v, i, t); i++)
+    {
+      exposure = pph_get_decl_exposure (t);
+      if (exposure >= EXPOSED)
+        break;
+    }
+  if (i >= n)
+    return; /* No exposed decls. */
+
+  first = VEC_index (tree, v, i);
+  if (VEC_length (tree, v) > i+1
+      && TREE_CODE (first) == TYPE_DECL
+      && CLASS_TYPE_P (TREE_TYPE (first)))
+    {
+      parent = first;
+      first_ix = i+1;
+    }
+  else
+    {
+      parent = NULL;
+      first_ix = i;
+    }
+
+  for (j = first_ix; VEC_iterate (tree, v, j, t); j++)
+    {
+      exposure = pph_get_decl_exposure (t);
+      if (exposure >= EXPOSED)
+        n_subtokens += pph_print_declaration (t, exposure, 0U, i == j, parent);
+    }
+
+  /* If we didn't print the first declaration, print it now.  */
+  if (first_ix > i)
+    {
+      exposure = pph_get_decl_exposure (first);
+      if (exposure >= EXPOSED)
+	pph_print_declaration (first, exposure, n_subtokens, true, NULL);
+    }
+}
+
+static void
+pph_print_trees_tokens (VEC(tree,heap) *v, cp_token *tok1, cp_token *tok2)
+{
+  VEC(cp_token, heap) *vtok;
+
+  vtok = pph_print_copy_tokens (tok1, tok2);
+  if (vtok == NULL)
+    return;
+
+  pph_print_token_range (v, vtok);
+  pph_print_declarations (v);
+  fprintf (pph_logfile, "\n");
+}
+
+
+/* Intercept the result of a name lookup operation requested by the
+   parser while we are intercepting AST creation.  T is the result
+   of a name lookup done by the parser.  If this is the first time
+   we see it, store it in pph_name_lookups.  */
+
+void
+pph_catch_name_lookup (tree t)
+{
+  if (t == NULL_TREE || t == error_mark_node || pph_tree_catcher == NULL)
+    return;
+
+  timevar_push (TV_PPH_MANAGE);
+
+  PPH_STATS_INCR (name_lookups, 1);
+
+  if (the_parser->lexer)
+    {
+      /* If we are parsing, we are stopped one token past the identifier
+	 that we have just looked up.  Store the token where we have seen
+	 this identifier so that we can determine whether the identifier
+	 was accessed in a head or a body.
+
+	 Note that we do this for every instance we find for T, so that
+	 we can store all the locations where T was accessed from.  */
+      VEC(cp_token, heap) *tokens;
+      cp_token *tok;
+      void **slot;
+
+      slot = pointer_map_insert (pph_nl_token_map, t);
+      tokens = (VEC(cp_token, heap) *) *slot;
+      tok = the_parser->lexer->next_token - 1;
+      VEC_safe_push (cp_token, heap, tokens, tok);
+      *slot = tokens;
+    }
+
+  /* Make sure we do not store the same decl more than once.  */
+  if (pointer_set_insert (pph_name_lookups_set, t))
+    {
+      timevar_pop (TV_PPH_MANAGE);
+      return;
+    }
+
+  VEC_safe_push (tree, heap, pph_name_lookups, t);
+  timevar_pop (TV_PPH_MANAGE);
+}
 

 /* Special handling for the first token or line in the file.  The first
    thing in the file might be #pragma GCC pch_preprocess, which loads a
@@ -24898,6 +29409,58 @@ pragma_lex (tree *value)
   return ret;
 }
 
+
+/* Print statistics for the PPH cache.  */
+
+static void
+pph_print_stats (void)
+{
+  fprintf (stderr, "\nPPH cache statistics\n");
+  fprintf (stderr, "Number of tokens in the lexer:           %lu\n",
+	   pph_stats.lexed_tokens);
+  fprintf (stderr, "Number of tokens consumed by the parser: %lu\n",
+	   pph_stats.parsed_tokens);
+  fprintf (stderr, "Number of declarations cached in:        %lu\n",
+	   pph_stats.cached_decls);
+  fprintf (stderr, "Number of declarations restored:         %lu\n",
+	   pph_stats.restored_decls);
+  fprintf (stderr, "Number of cached decl references:        %lu\n",
+	   pph_stats.cached_refs);
+  fprintf (stderr, "Number of name lookups:                  %lu\n",
+	   pph_stats.name_lookups);
+  fprintf (stderr, "Number of bad lookups:                   %lu\n",
+	   pph_stats.bad_lookups);
+}
+
+
+/* Initialize PPH support.  */
+
+static void
+pph_init (void)
+{
+  if (flag_pph_logfile)
+    {
+      pph_logfile = fopen (flag_pph_logfile, "w");
+      if (!pph_logfile)
+	fatal_error ("Cannot create %s for writing: %m", flag_pph_logfile);
+    }
+  else
+    pph_logfile = stdout;
+}
+
+
+/* Finalize PPH support.  */
+
+static void
+pph_finish (void)
+{
+  if (flag_pph_stats)
+    pph_print_stats ();
+
+  if (flag_pph_logfile)
+    fclose (pph_logfile);
+}
+
 

 /* External interface.  */
 
@@ -24915,11 +29478,13 @@ c_parse_file (void)
     }
   already_called = true;
 
+  pph_init ();
   the_parser = cp_parser_new ();
   push_deferring_access_checks (flag_access_control
 				? dk_no_deferred : dk_no_check);
   cp_parser_translation_unit (the_parser);
   the_parser = NULL;
+  pph_finish ();
 }
 
 #include "gt-cp-parser.h"
Index: gcc/cp/call.c
===================================================================
--- gcc/cp/call.c	(revision 166136)
+++ gcc/cp/call.c	(working copy)
@@ -1503,7 +1503,10 @@ implicit_conversion (tree to, tree from,
 
       cand = build_user_type_conversion_1 (to, expr, convflags);
       if (cand)
-	conv = cand->second_conv;
+        {
+	  conv = cand->second_conv;
+          pph_catch_name_lookup (DECL_ORIGIN (cand->fn));
+        }
 
       /* We used to try to bind a reference to a temporary here, but that
 	 is now handled after the recursive call to this function at the end
@@ -4574,7 +4577,10 @@ build_new_op (enum tree_code code, int f
 	  if (resolve_args (arglist) == NULL)
 	    result = error_mark_node;
 	  else
-	    result = build_over_call (cand, LOOKUP_NORMAL, complain);
+            {
+              pph_catch_name_lookup (DECL_ORIGIN (cand->fn));
+	      result = build_over_call (cand, LOOKUP_NORMAL, complain);
+            }
 	}
       else
 	{
@@ -5115,6 +5121,7 @@ convert_like_real (conversion *convs, tr
 	for (i = 0; i < cand->num_convs; ++i)
 	  cand->convs[i]->user_conv_p = true;
 
+        pph_catch_name_lookup (DECL_ORIGIN (cand->fn));
 	expr = build_over_call (cand, LOOKUP_NORMAL, complain);
 
 	/* If this is a constructor or a function returning an aggr type,
@@ -6690,6 +6697,7 @@ build_new_method_call (tree instance, tr
 	      if (fn_p)
 		*fn_p = fn;
 	      /* Build the actual CALL_EXPR.  */
+              pph_catch_name_lookup (DECL_ORIGIN (cand->fn));
 	      call = build_over_call (cand, flags, complain);
 	      /* In an expression of the form `a->f()' where `f' turns
 		 out to be a static member function, `a' is
Index: gcc/cp/cp-objcp-common.h
===================================================================
--- gcc/cp/cp-objcp-common.h	(revision 166136)
+++ gcc/cp/cp-objcp-common.h	(working copy)
@@ -146,6 +146,10 @@ extern bool cp_function_decl_explicit_p 
 #define LANG_HOOKS_OMP_FINISH_CLAUSE cxx_omp_finish_clause
 #undef LANG_HOOKS_OMP_PRIVATIZE_BY_REFERENCE
 #define LANG_HOOKS_OMP_PRIVATIZE_BY_REFERENCE cxx_omp_privatize_by_reference
+#undef LANG_HOOKS_PPH_CATCH_TREE
+#define LANG_HOOKS_PPH_CATCH_TREE pph_catch_tree
+#undef LANG_HOOKS_PPH_UNCATCH_TREE
+#define LANG_HOOKS_PPH_UNCATCH_TREE pph_uncatch_tree
 
 #undef LANG_HOOKS_EH_USE_CXA_END_CLEANUP
 #define LANG_HOOKS_EH_USE_CXA_END_CLEANUP true
Index: gcc/cp/cp-tree.h
===================================================================
--- gcc/cp/cp-tree.h	(revision 166136)
+++ gcc/cp/cp-tree.h	(working copy)
@@ -5632,6 +5632,18 @@ extern tree cxx_omp_clause_dtor			(tree,
 extern void cxx_omp_finish_clause		(tree);
 extern bool cxx_omp_privatize_by_reference	(const_tree);
 
+/* In parser.c  */
+
+#define PPH_POP_TIMEVAR_AND_RETURN(TV, T)				\
+  do {									\
+    pph_catch_name_lookup (T);						\
+    POP_TIMEVAR_AND_RETURN(TV, T);					\
+  } while (0)
+
+extern void pph_catch_tree (tree);
+extern void pph_uncatch_tree (tree);
+extern void pph_catch_name_lookup (tree);
+
 /* -- end of C++ */
 
 #endif /* ! GCC_CP_TREE_H */
Index: gcc/cp/name-lookup.c
===================================================================
--- gcc/cp/name-lookup.c	(revision 166136)
+++ gcc/cp/name-lookup.c	(working copy)
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  
 #include "diagnostic-core.h"
 #include "debug.h"
 #include "c-family/c-pragma.h"
+#include "tree-pretty-print.h"
 
 /* The bindings for a particular name in a particular scope.  */
 
@@ -560,7 +561,15 @@ add_decl_to_level (tree decl, cxx_scope 
 	     && (TREE_STATIC (decl) || DECL_EXTERNAL (decl)))
 	    || (TREE_CODE (decl) == FUNCTION_DECL
 		&& (!TREE_PUBLIC (decl) || DECL_DECLARED_INLINE_P (decl))))
-	  VEC_safe_push (tree, gc, b->static_decls, decl);
+          {
+	    VEC_safe_push (tree, gc, b->static_decls, decl);
+            if (flag_pph_debug >= 3)
+              {
+                fprintf (stderr, "Adding %p to static_decls:\n", (void*)decl);
+                print_generic_expr (stderr, decl, 0);
+                fprintf (stderr, "\n");
+              }
+          }
     }
 }
 
@@ -1745,16 +1754,16 @@ identifier_type_value (tree id)
   timevar_push (TV_NAME_LOOKUP);
   /* There is no type with that name, anywhere.  */
   if (REAL_IDENTIFIER_TYPE_VALUE (id) == NULL_TREE)
-    POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, NULL_TREE);
+    PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, NULL_TREE);
   /* This is not the type marker, but the real thing.  */
   if (REAL_IDENTIFIER_TYPE_VALUE (id) != global_type_node)
-    POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, REAL_IDENTIFIER_TYPE_VALUE (id));
+    PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, REAL_IDENTIFIER_TYPE_VALUE (id));
   /* Have to search for it. It must be on the global level, now.
      Ask lookup_name not to return non-types.  */
   id = lookup_name_real (id, 2, 1, /*block_p=*/true, 0, LOOKUP_COMPLAIN);
   if (id)
-    POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, TREE_TYPE (id));
-  POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, NULL_TREE);
+    PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, TREE_TYPE (id));
+  PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, NULL_TREE);
 }
 
 /* Return the IDENTIFIER_GLOBAL_VALUE of T, for use in common code, since
@@ -2028,7 +2037,7 @@ pushdecl_with_scope (tree x, cxx_scope *
       current_binding_level = b;
     }
   current_function_decl = function_decl;
-  POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, x);
+  PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, x);
 }
 
 /* DECL is a FUNCTION_DECL for a non-member function, which may have
@@ -4482,7 +4491,7 @@ lookup_type_scope (tree name, tag_scope 
       while (b)
 	{
 	  if (iter->scope == b)
-	    POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, val);
+	    PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, val);
 
 	  if (b->kind == sk_cleanup || b->kind == sk_template_parms
 	      || b->kind == sk_function_parms)
@@ -4495,7 +4504,7 @@ lookup_type_scope (tree name, tag_scope 
 	}
     }
 
-  POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, NULL_TREE);
+  PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, NULL_TREE);
 }
 
 /* Similar to `lookup_name' but look only in the innermost non-class
@@ -4528,7 +4537,7 @@ lookup_name_innermost_nonclass_level (tr
 	  if (binding->scope == b
 	      && !(TREE_CODE (binding->value) == VAR_DECL
 		   && DECL_DEAD_FOR_LOCAL (binding->value)))
-	    POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, binding->value);
+	    PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, binding->value);
 
 	  if (b->kind == sk_cleanup)
 	    b = b->level_chain;
@@ -4537,7 +4546,7 @@ lookup_name_innermost_nonclass_level (tr
 	}
     }
 
-  POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, t);
+  PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, t);
 }
 
 /* Returns true iff DECL is a block-scope extern declaration of a function
@@ -5105,7 +5114,7 @@ lookup_arg_dependent (tree name, tree fn
   release_tree_vector (k.classes);
   release_tree_vector (k.namespaces);
     
-  POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, fns);
+  PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, fns);
 }
 
 /* Add namespace to using_directives. Return NULL_TREE if nothing was
@@ -5302,12 +5311,12 @@ pushtag (tree name, tree type, tag_scope
       decl = maybe_process_template_type_declaration
 	(type, scope == ts_within_enclosing_non_class, b);
       if (decl == error_mark_node)
-	POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, decl);
+	PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, decl);
 
       if (b->kind == sk_class)
 	{
 	  if (!TYPE_BEING_DEFINED (current_class_type))
-	    POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
+	    PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
 
 	  if (!PROCESSING_REAL_TEMPLATE_DECL_P ())
 	    /* Put this TYPE_DECL on the TYPE_FIELDS list for the
@@ -5322,7 +5331,7 @@ pushtag (tree name, tree type, tag_scope
 	{
 	  decl = pushdecl_with_scope (decl, b, /*is_friend=*/false);
 	  if (decl == error_mark_node)
-	    POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, decl);
+	    PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, decl);
 	}
 
       if (! in_class)
@@ -5361,7 +5370,7 @@ pushtag (tree name, tree type, tag_scope
   TREE_PUBLIC (decl) = 1;
   determine_visibility (decl);
 
-  POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, type);
+  PPH_POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, type);
 }
 

 /* Subroutines for reverting temporarily to top-level for instantiation



More information about the Gcc-patches mailing list