This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[pph] Add streaming support using LTO streamer (issue4250071)


This patch adds the initial harness for using the AST streaming
facilities in LTO to use from pre-tokenized and pre-parsed header
support.

To avoid stepping on each other toes, I have set up the routines to
use FILE I/O for now.  I still need to fix the reader side before I
can switch everything to LTO streaming:

1- LTO does not support saving all kinds of AST.  I will need to
   either add callbacks or extend the current routines.  I think it
   may be better to add callbacks, but I'm open to suggestions here.
   In the end, PPH ought to be able to replace pre-compiled headers,
   so we will need to handle every kind of C++ AST.

2- The high-level streaming routines in cp/pph.c call pph_streamer_*
   functions to do their work.  Once I finish fixing the reader,
   I will just switch the functions in pph-streamer.[ch] to use
   LTO streams instead of FILE.  The routines in pph.c should not
   need to change.

Lawrence, the only routine we do not have in pph_streamer_* is a
printf equivalent.  Your code uses fprintf() for some things.  For now
you can access stream->file directly, but that will stop working when
I switch over.  Will you need to support a printf-like interface to
PPH streams?  Or are you using it for debugging only?

Tested on x86_64.  No new failures.  Committed to branch.


gcc/ChangeLog.pph
2011-03-09  Diego Novillo  <dnovillo@google.com>

	* lto-opts.c (input_data_block): Rename and move ...
	* lto-streamer-in.c (lto_input_data_block): ... here.
	* lto-streamer-in.c (lto_input_string): Rename from
	input_string.  Make extern.  Update all users.
	* lto-streamer-out.c (lto_output_string_with_length): Rename
	from output_string_with_length.  Make extern.  Update all
	users.
	(lto_output_string): Rename from output_string.  Make extern.
	Update all users.
	(lto_output_decl_state_streams): Make extern.
	(lto_output_decl_state_refs): Make extern.
	* lto-streamer.c (lto_get_common_nodes): #if0 assertions not
	valid inside the front end.
	* lto-streamer.h (lto_input_string): Declare.
	(lto_input_data_block): Declare.
	(lto_output_string): Declare.
	(lto_output_string_with_length): Declare.
	(lto_output_decl_state_streams): Declare.
	(lto_output_decl_state_refs): Declare.

gcc/cp/ChangeLog.pph

2011-03-09  Diego Novillo  <dnovillo@google.com>

	* Make-lang.in (CXX_AND_OBJCXX_OBJS): Add cp/pph-streamer.o
	(CXX_PPH_STREAMER_H): New.
	(cp/pph.o): Add dependency on CXX_PPH_STREAMER_H.
	(cp/pph-streamer.o): New.
	* pph-streamer.c: New file.
	* pph-streamer.h: New file.
	* pph.c: Update Copyright line.
	Include pph-streamer.h
	(pth_file_for): Change FILE * argument with pph_streamer *.
	Update all callers.
	(pth_write_uint): Remove.
	(pth_write_bytes): Remove.
	(pth_write_string): Remove.
	(pth_write_number): Re-implement using pph_streamer and 
	pph_output_* calls.
	(pth_save_token_value): Likewise.
	(pth_save_token): Likewise.
	(pth_write_header): Likewise.
	(pph_print_macro_defs_before): Likewise.
	(pph_print_macro_defs_after): Likewise.
	(pth_save_identifiers): Likewise.
	(pth_save_hunk): Likewise.
	(pth_save_include): Likewise.
	(pth_save_image): Likewise.
	(pth_read_uint): Remove.
	(pth_read_bytes): Remove.
	(pth_read_string): Remove.
	(pth_read_string_alloc): Remove.
	(pth_load_number): Re-implement using pph_streamer and
	pph_input_* calls.
	(pth_load_token_value): Likewise.
	(pth_load_identifiers): Likewise.
	(pth_load_hunk): Likewise.
	(pth_load_include): Likewise.
	(pth_load_include): Likewise.
	(pth_load_image): Likewise.
	(pth_have_valid_image_for): Likewise.
	(write_pph_format): Likewise.
	(write_pph_namespace): Likewise.
	(write_pph_print): Likewise.
	(write_pph_dump): Likewise.
	(write_pph_symbol): Likewise.
	(declvisitor): Likewise.
	(write_pph_namespace_1): Likewise.
	(write_pph_file_object): Likewise.
	(write_pph_file_summary): Likewise.
	(write_pph_file_dump): Likewise.
	(read_pph_file_object): Likewise.
	* pph.h: Update file comment.


gcc/testsuite/ChangeLog.pph:

	* lib/dg-pth.exp: New.
	* g++.dg/dg.exp: Call it.
	* g++.dg/pth: New directory.
	* g++.dg/pth/pth.exp: New.
	* g++.dg/pth/staticmbrvar.cc: New.
	* g++.dg/pth/typerefs.cc: New.
	* g++.dg/pth/tmplclass.cc: New.
	* g++.dg/pth/system-include.cc: New.
	* g++.dg/pth/variables.cc: New.
	* g++.dg/pth/trivial.cc: New.
	* g++.dg/pth/where.cc: New.
	* g++.dg/pth/template.cc: New.
	* g++.dg/pth/cflow.h: New.
	* g++.dg/pth/simple1.h: New.
	* g++.dg/pth/simple2.h: New.
	* g++.dg/pth/chained1.h: New.
	* g++.dg/pth/meth2.cc: New.
	* g++.dg/pth/chained2.h: New.
	* g++.dg/pth/mean.cc: New.
	* g++.dg/pth/special.cc: New.
	* g++.dg/pth/nontrivinit.cc: New.
	* g++.dg/pth/emptyclass.cc: New.
	* g++.dg/pth/simplecall.cc: New.
	* g++.dg/pth/autometh.cc: New.
	* g++.dg/pth/tmplfunc.cc: New.
	* g++.dg/pth/simple.cc: New.
	* g++.dg/pth/builtin1.h: New.
	* g++.dg/pth/builtin2.h: New.
	* g++.dg/pth/invoke.cc: New.
	* g++.dg/pth/chained.cc: New.
	* g++.dg/pth/usearray.cc: New.
	* g++.dg/pth/builtin3.h: New.
	* g++.dg/pth/globalref.cc: New.
	* g++.dg/pth/invoke.h: New.
	* g++.dg/pth/meth.cc: New.
	* g++.dg/pth/usearray.h: New.
	* g++.dg/pth/guarded1.h: New.
	* g++.dg/pth/guarded2.h: New.
	* g++.dg/pth/guarded3.h: New.
	* g++.dg/pth/funcstatic.cc: New.
	* g++.dg/pth/builtin.cc: New.
	* g++.dg/pth/hardlookup.cc: New.
	* g++.dg/pth/functions.cc: New.
	* g++.dg/pth/incmod.cc: New.
	* g++.dg/pth/cflow.cc: New.
	* g++.dg/pth/guarded.cc: New.
	* g++.dg/pth/incmod.h: New.
	* g++.dg/pth/paste.cc: New.
	* g++.dg/pth/incsame.cc: New.
	* g++.dg/pth/sys-types.cc: New.
	* g++.dg/pth/incsame.h: New.
	* g++.dg/pth/field.cc: New.
	* g++.dg/pth/classshort.cc: New.
	* g++.dg/pth/variable.cc: New.
	* g++.dg/pth/tmplsimple.cc: New.
	* g++.dg/pth/inif.h: New.

diff --git a/gcc/cp/Make-lang.in b/gcc/cp/Make-lang.in

--- a/gcc/cp/Make-lang.in
+++ b/gcc/cp/Make-lang.in
@@ -81,7 +81,7 @@ CXX_AND_OBJCXX_OBJS = cp/call.o cp/decl.o cp/expr.o cp/pt.o cp/typeck2.o \
  cp/typeck.o cp/cvt.o cp/except.o cp/friend.o cp/init.o cp/method.o \
  cp/search.o cp/semantics.o cp/tree.o cp/repo.o cp/dump.o cp/optimize.o \
  cp/mangle.o cp/cp-objcp-common.o cp/name-lookup.o cp/cxx-pretty-print.o \
- cp/cp-gimplify.o cp/pph.o tree-mudflap.o $(CXX_C_OBJS)
+ cp/cp-gimplify.o cp/pph.o cp/pph-streamer.o tree-mudflap.o $(CXX_C_OBJS)
 
 # Language-specific object files for C++.
 CXX_OBJS = cp/cp-lang.o c-family/stub-objc.o $(CXX_AND_OBJCXX_OBJS)
@@ -247,6 +247,7 @@ CXX_PARSER_H = tree.h c-family/c-pragma.h cp/parser.h
 CXX_PRETTY_PRINT_H = cp/cxx-pretty-print.h $(C_PRETTY_PRINT_H)
 CXX_PPH_H = $(srcdir)/../libcpp/include/line-map.h $(HASHTAB_H) \
 	$(CXX_PARSER_H) $(TIMEVAR_H) cp/pph.h 
+CXX_PPH_STREAMER_H = $(LTO_STREAMER_H) cp/pph-streamer.h $(TREE_H)
 
 cp/lex.o: cp/lex.c $(CXX_TREE_H) $(TM_H) $(FLAGS_H) \
   $(C_PRAGMA_H) output.h input.h cp/operators.def $(TM_P_H) \
@@ -331,7 +332,9 @@ cp/name-lookup.o: cp/name-lookup.c $(CONFIG_H) $(SYSTEM_H) coretypes.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
-cp/pph.c: cp/pph.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(CPPLIB_H) \
+cp/pph.o: cp/pph.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(CPPLIB_H) \
 	toplev.h $(TREE_H) $(CXX_TREE_H) $(TIMEVAR_H) pointer-set.h \
 	fixed-value.h $(TREE_PASS_H) $(TREE_INLINE_H) tree-pretty-print.h \
-	$(CXX_PARSER_H) $(CXX_PPH_H)
+	$(CXX_PARSER_H) $(CXX_PPH_H) $(CXX_PPH_STREAMER_H)
+cp/pph-streamer.o: cp/pph-streamer.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
+	$(TREE_H) $(LTO_STREAMER_H) $(CXX_PPH_STREAMER_H)
diff --git a/gcc/cp/pph-streamer.c b/gcc/cp/pph-streamer.c
new file mode 100644

--- /dev/null
+++ b/gcc/cp/pph-streamer.c
@@ -0,0 +1,157 @@
+/* Routines for streaming PPH data.
+   Copyright (C) 2011 Free Software Foundation, Inc.
+   Contributed by Diego Novillo <dnovillo@google.com>.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GCC is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "langhooks.h"
+#include "lto-streamer.h"
+#include "pph-streamer.h"
+
+#if !defined PPH_USE_FILE_IO
+static FILE *current_pph_file = NULL;
+#endif
+
+/* Read into memory the contents of the file in STREAM.  Initialize
+   internal tables and data structures needed to re-construct the
+   ASTs in the file.  */
+
+static void
+pph_file_read (pph_stream *stream)
+{
+#if defined PPH_USE_FILE_IO
+  stream->file = fopen (stream->name, "rb");
+#else
+  void *data;
+  struct stat st;
+  size_t bytes_read, data_size;
+
+  /* Read STREAM->NAME into the memory buffer DATA.  */
+  stat (stream->name, &st);
+  data_size = (size_t) st.st_size;
+  data = XCNEWVEC (char, data_size);
+  stream->file = fopen (stream->name, "rb");
+  gcc_assert (stream->file);
+  bytes_read = fread (data, 1, data_size, stream->file);
+  gcc_assert (bytes_read == data_size);
+  fclose (stream->file);
+
+  stream->ib = XCNEW (struct lto_input_block);
+  LTO_INIT_INPUT_BLOCK_PTR (stream->ib, (const char *) data, 0, data_size);
+  stream->data_in = lto_data_in_create (NULL, NULL, 0, NULL);
+#endif
+}
+
+
+/* Create a new PPH stream to be stored on the file called NAME.  If
+   TO_READ_P is true, the file is open for reading.  */
+
+pph_stream *
+pph_stream_open (const char *name, bool to_read_p)
+{
+  pph_stream *stream = XCNEW (pph_stream);
+  stream->name = xstrdup (name);
+  if (!to_read_p)
+    {
+      stream->file = fopen (name, "wb");
+      stream->out_state = lto_new_out_decl_state ();
+      lto_push_out_decl_state (stream->out_state);
+      stream->decl_state_stream = XCNEW (struct lto_output_stream);
+      stream->ob = create_output_block (LTO_section_decls);
+    }
+  else
+    pph_file_read (stream);
+
+  return stream;
+}
+
+
+#if !defined PPH_USE_FILE_IO
+/* Callback for lang_hooks.lto.begin_section.  Open file NAME.  */
+
+static void
+pph_stream_begin_section (const char *name ATTRIBUTE_UNUSED)
+{
+}
+
+
+/* Callback for lang_hooks.lto.append_data.  Write LEN bytes from DATA
+   into current_pph_file.  BLOCK is currently unused, but this hook is
+   required to free it.  */
+
+static void
+pph_stream_write (const void *data, size_t len, void *block)
+{
+  if (data)
+    fwrite (data, len, 1, current_pph_file);
+  free (block);
+}
+
+
+/* Callback for lang_hooks.lto.end_section.  */
+
+static void
+pph_stream_end_section (void)
+{
+}
+#endif
+
+
+/* Close PPH stream STREAM.  Write all the ASTs to disk and deallocate
+   all memory used by it.  */
+
+void
+pph_stream_close (pph_stream *stream)
+{
+#if defined PPH_USE_FILE_IO
+  fclose (stream->file);
+#else
+  gcc_assert (current_pph_file == NULL);
+  current_pph_file = stream->file;
+
+  /* Redirect the LTO basic I/O langhooks.  */
+  lang_hooks.lto.begin_section = pph_stream_begin_section;
+  lang_hooks.lto.append_data = pph_stream_write;
+  lang_hooks.lto.end_section = pph_stream_end_section;
+
+  /* Write the state buffers built by pph_stream_output() calls.  */
+  lto_begin_section (stream->name, false);
+
+  /* Make string 0 be a NULL string.  */
+  lto_output_1_stream (stream->ob->string_stream, 0);
+
+  /* Write out the physical representation for every AST in all the
+     streams in STREAM->OUT_STATE.  */
+  lto_output_decl_state_streams (stream->ob, stream->out_state);
+
+  /* Now write the vector of all AST references.  */
+  lto_output_decl_state_refs (stream->ob, stream->decl_state_stream,
+			      stream->out_state);
+
+  /* Finally, physically write all the streams.  */
+  lto_write_stream (stream->ob->main_stream);
+  lto_write_stream (stream->ob->string_stream);
+
+  lto_end_section ();
+  fclose (stream->file);
+  current_pph_file = NULL;
+#endif
+}
diff --git a/gcc/cp/pph-streamer.h b/gcc/cp/pph-streamer.h
new file mode 100644

--- /dev/null
+++ b/gcc/cp/pph-streamer.h
@@ -0,0 +1,198 @@
+/* Support routines and data structures for streaming PPH data.
+   Copyright (C) 2011 Free Software Foundation, Inc.
+   Contributed by Diego Novillo <dnovillo@google.com>.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GCC is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_CP_PPH_STREAMER_H
+#define GCC_CP_PPH_STREAMER_H
+
+#include "lto-streamer.h"
+#include "tree.h"
+
+/* FIXME pph - Workaround incomplete PPH streamer.  Use regular FILE I/O.  */
+#define PPH_USE_FILE_IO	1
+
+typedef struct pph_stream {
+  /* Path name of the PPH file.  */
+  const char *name;
+
+  /* FILE object associated with it.  */
+  FILE *file;
+
+  /* LTO output block to hold pickled ASTs and references.  This is
+      NULL when the file is opened for reading.  */
+  struct output_block *ob;
+  struct lto_out_decl_state *out_state;
+  struct lto_output_stream *decl_state_stream;
+
+  /* LTO input block to read ASTs and references from.  This is NULL
+      when the file is opened for writing.  */
+  struct lto_input_block *ib;
+
+  /* String tables and other descriptors used by the LTO reading
+      routines.  */
+  struct data_in *data_in;
+
+  /* Nonzero if the stream was open for writing.  */
+  unsigned int write_p : 1;
+} pph_stream;
+
+/* In pph-streamer.c.  */
+pph_stream *pph_stream_open (const char *, bool);
+void pph_stream_close (pph_stream *);
+
+
+/* Inline functions.  */
+
+/* Output AST T to STREAM.  */
+static inline void
+pph_output_tree (pph_stream *stream ATTRIBUTE_UNUSED, tree t ATTRIBUTE_UNUSED)
+{
+#if defined PPH_USE_FILE_IO
+  gcc_unreachable ();
+#else
+  lto_output_tree (stream->ob, t, true);
+#endif
+}
+
+/* Write a uint VALUE to STREAM.  */
+static inline void
+pph_output_uint (pph_stream *stream, unsigned int value)
+{
+#if defined PPH_USE_FILE_IO
+  fwrite (&value, 1, sizeof (value), stream->file);
+#else
+  lto_output_sleb128_stream (stream->ob->main_stream, value);
+#endif
+}
+
+/* Write N bytes from P to STREAM.  */
+static inline void
+pph_output_bytes (pph_stream *stream, const void *p, size_t n)
+{
+#if defined PPH_USE_FILE_IO
+  fwrite (p, 1, n, stream->file);
+#else
+  lto_output_data_stream (stream->ob->main_stream, p, n);
+#endif
+}
+
+/* Write string STR to STREAM.  */
+static inline void
+pph_output_string (pph_stream *stream, const char *str)
+{
+#if defined PPH_USE_FILE_IO
+  if (str == NULL)
+    pph_output_uint (stream, -1U);
+  else
+    {
+      unsigned length = strlen (str);
+      pph_output_uint (stream, length);
+      if (length > 0)
+	pph_output_bytes (stream, str, length);
+    }
+#else
+  lto_output_string (stream->ob, stream->ob->main_stream, str);
+#endif
+}
+
+/* Write string STR of length LEN to STREAM.  */
+static inline void
+pph_output_string_with_length (pph_stream *stream, const char *str,
+			       unsigned int len)
+{
+#if defined PPH_USE_FILE_IO
+  if (str == NULL)
+    pph_output_uint (stream, -1U);
+  else
+    {
+      pph_output_uint (stream, len);
+      if (len > 0)
+	pph_output_bytes (stream, str, len);
+    }
+#else
+  lto_output_string_with_length (stream->ob, stream->ob->main_stream, str, len);
+#endif
+}
+
+/* Read an unsigned HOST_WIDE_INT integer from STREAM.  */
+static inline unsigned
+pph_input_uint (pph_stream *stream)
+{
+#if defined PPH_USE_FILE_IO
+  unsigned num;
+  size_t received;
+  received = fread (&num, sizeof num, 1, stream->file);
+  gcc_assert (received == 1);
+  return num;
+#else
+  HOST_WIDE_INT unsigned n = lto_input_uleb128 (stream->ib);
+  gcc_assert (n == (unsigned) n);
+  return (unsigned) n;
+#endif
+}
+
+/* Read N bytes from STREAM into P.  The caller is responsible for 
+   allocating a sufficiently large buffer.  */
+static inline void
+pph_input_bytes (pph_stream *stream, void *p, size_t n)
+{
+#if defined PPH_USE_FILE_IO
+  size_t received = fread (p, 1, n, stream->file);
+  gcc_assert (received == n);
+#else
+  lto_input_data_block (stream->ib, p, n);
+#endif
+}
+
+/* Read and return a string of up to MAX characters from STREAM.
+   The caller is responsible for freeing the memory allocated
+   for the string.  */
+
+static inline const char *
+pph_input_string (pph_stream *stream)
+{
+#if defined PPH_USE_FILE_IO
+  char *buf = NULL;
+  unsigned len;
+  size_t received = fread (&len, sizeof len, 1, stream->file);
+  gcc_assert (received == 1);
+  if (len > 0 && len != -1U)
+    {
+      buf = XCNEWVEC (char, len + 1);
+      received = fread (buf, 1, len, stream->file);
+    }
+  return (const char *) buf;
+#else
+  return lto_input_string (stream->data_in, stream->ib);
+#endif
+}
+
+/* Load an AST from STREAM.  Return the corresponding tree.  */
+
+static inline tree
+pph_input_tree (pph_stream *stream ATTRIBUTE_UNUSED)
+{
+#if defined PPH_USE_FILE_IO
+  gcc_unreachable ();
+#else
+  return lto_input_tree (stream->ib, stream->data_in);
+#endif
+}
+
+#endif  /* GCC_CP_PPH_STREAMER_H  */
diff --git a/gcc/cp/pph.c b/gcc/cp/pph.c

--- a/gcc/cp/pph.c
+++ b/gcc/cp/pph.c
@@ -1,5 +1,5 @@
-/* Factored pre-tokenized header (PTH) support for C++
-   Copyright (C) 2010 Free Software Foundation, Inc.
+/* Factored pre-parsed header (PPH) support for C++
+   Copyright (C) 2010, 2011 Free Software Foundation, Inc.
    Contributed by Lawrence Crowl <crowl@google.com> and
    Diego Novillo <dnovillo@google.com>.
 
@@ -35,6 +35,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-pretty-print.h"
 #include "parser.h"
 #include "pph.h"
+#include "pph-streamer.h"
 
 /* Statistics collected for PTH/PPH.  */
 struct pth_stats_d pth_stats;
@@ -196,17 +197,18 @@ pth_name_for (const char *name)
 }
 
 
-/* Open an image file for path NAME with MODE.  */
+/* Open an image file for path NAME.  READ_P is true if the file should
+   be opened for reading.  */
 
-static FILE *
-pth_file_for (const char *name, const char *mode)
+static pph_stream *
+pth_file_for (const char *name, bool read_p)
 {
   char *s;
-  FILE *f;
+  pph_stream *f;
 
   s = pth_name_for (name);
-  f = fopen (s, mode);
-  if (f == NULL)
+  f = pph_stream_open (s, read_p);
+  if (f->file == NULL)
     fatal_error ("can%'t open token stream file %s: %m", s);
   free (s);
 
@@ -315,61 +317,17 @@ pth_get_index_from_type (tree type, unsigned *type_ix_p, unsigned *category_p)
 }
 
 
-/* Write a uint VALUE to the STREAM.  Return the number of bytes written.  */
+/* Save the number VAL to file F.  */
 
-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 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)
+static void
+pth_write_number (pph_stream *f, tree val)
 {
   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);
+  pph_output_uint (f, type_idx);
+  pph_output_uint (f, type_kind);
 
   if (type_kind == CPP_N_INTEGER)
     {
@@ -377,43 +335,38 @@ pth_write_number (tree val, FILE *f)
 
       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);
+      pph_output_bytes (f, v, 2 * sizeof (HOST_WIDE_INT));
     }
   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);
+      pph_output_bytes (f, &r, sizeof (REAL_VALUE_TYPE));
     }
   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);
+      pph_output_bytes (f, &fv, sizeof (FIXED_VALUE_TYPE));
     }
   else if (type_kind == CPP_N_IMAGINARY)
     {
-      pth_write_number (TREE_REALPART (val), f);
-      pth_write_number (TREE_IMAGPART (val), f);
+      pth_write_number (f, TREE_REALPART (val));
+      pth_write_number (f, TREE_IMAGPART (val));
     }
   else
     gcc_unreachable ();
-
-  return nbytes;
 }
 
 
-/* Save the tree associated with TOKEN to file F.  Return the number
-   of bytes written.  */
+/* Save the tree associated with TOKEN to file F.  */
 
-static size_t
-pth_save_token_value (cp_token *token, FILE *f)
+static void
+pth_save_token_value (pph_stream *f, cp_token *token)
 {
   const char *str;
-  size_t nbytes;
   unsigned len;
   tree val;
 
   val = token->u.value;
-  nbytes = 0;
   switch (token->type)
     {
       case CPP_TEMPLATE_ID:
@@ -424,7 +377,7 @@ pth_save_token_value (cp_token *token, FILE *f)
 	/* 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);
+	pph_output_string_with_length (f, str, len);
 	break;
 
       case CPP_KEYWORD:
@@ -437,7 +390,7 @@ pth_save_token_value (cp_token *token, FILE *f)
       case CPP_CHAR16:
       case CPP_CHAR32:
       case CPP_NUMBER:
-	nbytes += pth_write_number (val, f);
+	pth_write_number (f, val);
 	break;
 
       case CPP_STRING:
@@ -447,7 +400,7 @@ pth_save_token_value (cp_token *token, FILE *f)
 	/* FIXME pph.  Need to represent the type.  */
 	str = TREE_STRING_POINTER (val);
 	len = TREE_STRING_LENGTH (val);
-	nbytes += pth_write_string (str, len, f);
+	pph_output_string_with_length (f, str, len);
 	break;
 
       case CPP_PRAGMA:
@@ -456,38 +409,31 @@ pth_save_token_value (cp_token *token, FILE *f)
 
       default:
 	gcc_assert (token->u.value == NULL);
-	nbytes += pth_write_bytes (&token->u.value, sizeof (token->u.value), f);
+	pph_output_bytes (f, &token->u.value, sizeof (token->u.value));
     }
-
-  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)
+static void
+pth_save_token (cp_token *token, pph_stream *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;
+  pph_output_bytes (f, token, sizeof (cp_token) - sizeof (void *));
+  pth_save_token_value (f, token);
 }
 
 
 /* Write header information for IMAGE to STREAM.  */
 
 static void
-pth_write_header (pth_image *image, FILE *stream)
+pth_write_header (pth_image *image, pph_stream *stream)
 {
-  size_t nbytes;
   const char *id = pth_id_str ();
 
   if (!image->digest_computed_p)
@@ -496,10 +442,8 @@ pth_write_header (pth_image *image, FILE *stream)
       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 ());
+  pph_output_bytes (stream, id, strlen (id));
+  pph_output_bytes (stream, image->digest, DIGEST_LEN);
 }
 
 
@@ -507,7 +451,7 @@ pth_write_header (pth_image *image, FILE *stream)
    the STREAM. */
 
 static void
-pph_print_macro_defs_before (FILE *stream, cpp_idents_used *identifiers)
+pph_print_macro_defs_before (pph_stream *stream, cpp_idents_used *identifiers)
 {
   unsigned int idx;
 
@@ -518,9 +462,9 @@ pph_print_macro_defs_before (FILE *stream, cpp_idents_used *identifiers)
       const char *before = entry->before_str;
 
       if (before)
-          fprintf (stream, "#define %s%s\n", ident, before);
+          fprintf (stream->file, "#define %s%s\n", ident, before);
       else
-          fprintf (stream, "#undef %s\n", ident);
+          fprintf (stream->file, "#undef %s\n", ident);
     }
 }
 
@@ -529,7 +473,7 @@ pph_print_macro_defs_before (FILE *stream, cpp_idents_used *identifiers)
    the STREAM. */
 
 static void
-pph_print_macro_defs_after (FILE *stream, cpp_idents_used *identifiers)
+pph_print_macro_defs_after (pph_stream *stream, cpp_idents_used *identifiers)
 {
   unsigned int idx;
 
@@ -543,9 +487,9 @@ pph_print_macro_defs_after (FILE *stream, cpp_idents_used *identifiers)
       if (before != after)
         {
           if (after && (!before || strcmp (after, before) != 0))
-              fprintf (stream, "#define %s%s\n", ident, after);
+              fprintf (stream->file, "#define %s%s\n", ident, after);
           else if (before)
-              fprintf (stream, "#undef %s\n", ident);
+              fprintf (stream->file, "#undef %s\n", ident);
         }
     }
 }
@@ -844,33 +788,36 @@ pth_debug_state (void)
 /* Save the IDENTIFIERS to the STREAM.  */
 
 static void
-pth_save_identifiers (cpp_idents_used *identifiers, FILE *stream)
+pth_save_identifiers (cpp_idents_used *identifiers, pph_stream *stream)
 {
   unsigned int num_entries, id;
 
   num_entries = identifiers->num_entries;
-  pth_write_uint (identifiers->max_length, stream);
-  pth_write_uint (num_entries, stream);
+  pph_output_uint (stream, identifiers->max_length);
+  pph_output_uint (stream, num_entries);
 
   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);
+      pph_output_string_with_length (stream, entry->ident_str,
+				     entry->ident_len);
 
       gcc_assert (entry->before_len <= identifiers->max_length);
-      pth_write_string (entry->before_str, entry->before_len, stream);
+      pph_output_string_with_length (stream, entry->before_str,
+				     entry->before_len);
 
       gcc_assert (entry->after_len <= identifiers->max_length);
-      pth_write_string (entry->after_str, entry->after_len, stream);
+      pph_output_string_with_length (stream, entry->after_str,
+				     entry->after_len);
     }
 }
 
 /* Save the HUNK to the STREAM.  */
 
 static void
-pth_save_hunk (cp_token_hunk *hunk, FILE *stream)
+pth_save_hunk (cp_token_hunk *hunk, pph_stream *stream)
 {
   unsigned j;
   cp_token *token;
@@ -882,7 +829,7 @@ pth_save_hunk (cp_token_hunk *hunk, FILE *stream)
   pth_save_identifiers (&hunk->identifiers, stream);
 
   /* Write the number of tokens in HUNK.  */
-  pth_write_uint (VEC_length (cp_token, hunk->buffer), stream);
+  pph_output_uint (stream, VEC_length (cp_token, hunk->buffer));
 
   /* Write the tokens.  */
   for (j = 0; VEC_iterate (cp_token, hunk->buffer, j, token); j++)
@@ -893,16 +840,13 @@ pth_save_hunk (cp_token_hunk *hunk, FILE *stream)
 /* Save the #include directive INCLUDE to STREAM.  */
 
 static void
-pth_save_include (pth_include *include, FILE *stream)
+pth_save_include (pth_include *include, pph_stream *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);
+  pph_output_string (stream, include->image->fname);
+  pph_output_uint (stream, (unsigned int) include->itype);
+  pph_output_uint (stream, include->angle_brackets);
+  pph_output_string (stream, include->iname);
+  pph_output_string (stream, include->dname);
 }
 
 
@@ -911,50 +855,42 @@ pth_save_include (pth_include *include, FILE *stream)
 static void
 pth_save_image (pth_image *image)
 {
-  FILE *stream;
+  pph_stream *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");
+  stream = pth_file_for (image->fname, false);
 
-  /* 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 a header to recognize the file later.  */
+  pth_write_header (image, stream);
 
   /* Write the include-hunk (IH) sequencing vector.  */
   num = VEC_length (char, image->ih_sequence);
-  pth_write_uint (num, stream);
+  pph_output_uint (stream, num);
   if (num > 0)
-    pth_write_bytes (VEC_address (char, image->ih_sequence), num, stream);
+    pph_output_bytes (stream, VEC_address (char, image->ih_sequence), num);
   
   /* Write the number of #include commands.  */
-  pth_write_uint (VEC_length (pth_include_ptr, image->includes), stream);
+  pph_output_uint (stream, VEC_length (pth_include_ptr, image->includes));
 
   /* 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);
+  pph_output_uint (stream, VEC_length (cp_token_hunk_ptr, image->token_hunks));
 
   /* 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);
+  pph_stream_close (stream);
   image->save_p = false;
 
   if (flag_pth_debug >= 3)
@@ -988,104 +924,35 @@ pth_get_type_from_index (unsigned type_idx, unsigned type_kind)
 }
 
 
-/* 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 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)
+pth_load_number (pph_stream *f)
 {
   unsigned type_idx, type_kind;
   tree type, val;
 
-  pth_read_uint (&type_idx, f);
-  pth_read_uint (&type_kind, f);
+  type_idx = pph_input_uint (f);
+  type_kind = pph_input_uint (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);
+      pph_input_bytes (f, v, 2 * sizeof (HOST_WIDE_INT));
       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);
+      pph_input_bytes (f, &r, sizeof (REAL_VALUE_TYPE));
       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);
+      pph_input_bytes (f, &fv, sizeof (FIXED_VALUE_TYPE));
       val = build_fixed (type, fv);
     }
   else if (type_kind == CPP_N_IMAGINARY)
@@ -1104,9 +971,9 @@ pth_load_number (FILE *f)
 /* Load the tree value associated with TOKEN to file F.  */
 
 static void
-pth_load_token_value (cp_token *token, FILE *f)
+pth_load_token_value (cp_token *token, pph_stream *f)
 {
-  char *str;
+  const char *str;
 
   switch (token->type)
     {
@@ -1115,9 +982,9 @@ pth_load_token_value (cp_token *token, FILE *f)
 	break;
 
       case CPP_NAME:
-	str = pth_read_string_alloc (f);
+	str = pph_input_string (f);
 	token->u.value = get_identifier (str);
-	free (str);
+	free (CONST_CAST (char *, str));
 	break;
 
       case CPP_KEYWORD:
@@ -1136,9 +1003,9 @@ pth_load_token_value (cp_token *token, FILE *f)
       case CPP_WSTRING:
       case CPP_STRING16:
       case CPP_STRING32:
-	str = pth_read_string_alloc (f);
+	str = pph_input_string (f);
 	token->u.value = build_string (strlen (str), str);
-	free (str);
+	free (CONST_CAST (char *, str));
 	break;
 
       case CPP_PRAGMA:
@@ -1146,7 +1013,7 @@ pth_load_token_value (cp_token *token, FILE *f)
 	break;
 
       default:
-	pth_read_bytes (&token->u.value, sizeof (token->u.value), f);
+	pph_input_bytes (f, &token->u.value, sizeof (token->u.value));
 	gcc_assert (token->u.value == NULL);
     }
 }
@@ -1155,16 +1022,15 @@ pth_load_token_value (cp_token *token, FILE *f)
 /* Load the IDENTIFERS for a hunk from a STREAM.  */
 
 static void
-pth_load_identifiers (cpp_idents_used *identifiers, FILE *stream)
+pth_load_identifiers (cpp_idents_used *identifiers, pph_stream *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);
+  max_length = pph_input_uint (stream);
   identifiers->max_length = max_length;
-  pth_read_uint (&num_entries, stream);
+  num_entries = pph_input_uint (stream);
   identifiers->num_entries = num_entries;
   identifiers->entries = XCNEWVEC (cpp_ident_use, num_entries);
   identifiers->strings = XCNEW (struct obstack);
@@ -1176,42 +1042,58 @@ pth_load_identifiers (cpp_idents_used *identifiers, FILE *stream)
   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);
+      const char *s = pph_input_string (stream);
+      gcc_assert (s);
+      ident_len = strlen (s);
       identifiers->entries[j].ident_len = ident_len;
       identifiers->entries[j].ident_str =
-        (const char *) obstack_copy0 (identifiers->strings, buffer, ident_len);
+        (const char *) obstack_copy0 (identifiers->strings, s, ident_len);
+      free (CONST_CAST (char *, s));
 
-      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;
+      s = pph_input_string (stream);
+      if (s)
+	{
+	  before_len = strlen (s);
+	  identifiers->entries[j].before_len = before_len;
+	  identifiers->entries[j].before_str = (const char *)
+	      obstack_copy0 (identifiers->strings, s, before_len);
+	  free (CONST_CAST (char *, s));
+	}
       else
-        identifiers->entries[j].before_str = (const char *)
-            obstack_copy0 (identifiers->strings, buffer, before_len);
+	{
+	  /* The identifier table expects NULL entries to have
+	     a length of -1U.  */
+	  identifiers->entries[j].before_len = -1U;
+	  identifiers->entries[j].before_str = NULL;
+	}
 
-      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;
+      s = pph_input_string (stream);
+      if (s)
+	{
+	  after_len = strlen (s);
+	  identifiers->entries[j].after_len = after_len;
+	  identifiers->entries[j].after_str = (const char *)
+	      obstack_copy0 (identifiers->strings, s, after_len);
+	  free (CONST_CAST (char *, s));
+	}
       else
-        identifiers->entries[j].after_str = (const char *)
-            obstack_copy0 (identifiers->strings, buffer, after_len);
+	{
+	  /* The identifier table expects NULL entries to have
+	     a length of -1U.  */
+	  identifiers->entries[j].after_len = -1U;
+	  identifiers->entries[j].after_str = NULL;
+	}
     }
-
-  free (buffer);
 }
 
 
 /* Load a hunk into the IMAGE from a STREAM.  */
 
 static void
-pth_load_hunk (pth_image *image, FILE *stream)
+pth_load_hunk (pth_image *image, pph_stream *stream)
 {
   unsigned j, num_tokens;
   cp_token_hunk *hunk;
@@ -1222,7 +1104,7 @@ pth_load_hunk (pth_image *image, FILE *stream)
   pth_load_identifiers (&hunk->identifiers, stream);
 
   /* Read the number of tokens in HUNK. */
-  pth_read_uint (&num_tokens, stream);
+  num_tokens = pph_input_uint (stream);
 
   /* Read the tokens in the HUNK. */
   hunk->buffer = VEC_alloc (cp_token, gc, num_tokens);
@@ -1234,7 +1116,7 @@ pth_load_hunk (pth_image *image, FILE *stream)
          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);
+      pph_input_bytes (stream, token, sizeof (cp_token) - sizeof (void *));
 
       /* FIXME pph.  Use an arbitrary (but valid) location to avoid
          confusing the rest of the compiler for now.  */
@@ -1270,25 +1152,26 @@ pth_create_include (enum include_type itype, bool angle_brackets,
 
 static void
 pth_load_include (pth_state *state, pth_image *image, cpp_reader *reader,
-		  FILE *stream)
+		  pph_stream *stream)
 {
-  char *s;
+  const char *s;
   pth_include *include;
   unsigned tmp;
 
   include = pth_create_include (IT_INCLUDE, false, NULL);
 
-  s = pth_read_string_alloc (stream);
+  s = pph_input_string (stream);
   include->image = pth_image_lookup (state, s, reader);
+  free (CONST_CAST (char *, s));
 
-  pth_read_uint (&tmp, stream);
+  tmp = pph_input_uint (stream);
   include->itype = (enum include_type) tmp;
 
-  pth_read_uint (&tmp, stream);
+  tmp = pph_input_uint (stream);
   include->angle_brackets = (tmp != 0);
 
-  include->iname = pth_read_string_alloc (stream);
-  include->dname = pth_read_string_alloc (stream);
+  include->iname = pph_input_string (stream);
+  include->dname = pph_input_string (stream);
 
   VEC_safe_push (pth_include_ptr, gc, image->includes, include);
 }
@@ -1299,29 +1182,29 @@ pth_load_include (pth_state *state, pth_image *image, cpp_reader *reader,
 static void
 pth_load_image (pth_state *state, pth_image *image, cpp_reader *reader)
 {
-  FILE *stream;
+  pph_stream *stream;
   unsigned i, num;
 
   timevar_push (TV_PTH_LOAD);
 
-  stream = pth_file_for (image->fname, "r+b");
+  stream = pth_file_for (image->fname, true);
 
   /* 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);
+  fseek (stream->file, (long) pth_header_len (), SEEK_SET);
 
   /* Read the include-hunk (IH) sequencing vector.  */
-  pth_read_uint (&num, stream);
+  num = pph_input_uint (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);
+      pph_input_bytes (stream, VEC_address (char, image->ih_sequence), num);
     }
 
   /* Read the number path names of all the files #included by
      IMAGE->FNAME.  */
-  pth_read_uint (&num, stream);
+  num = pph_input_uint (stream);
   image->includes = VEC_alloc (pth_include_ptr, gc, num);
 
   /* Now read all the path names #included by IMAGE->FNAME.  */
@@ -1329,7 +1212,7 @@ pth_load_image (pth_state *state, pth_image *image, cpp_reader *reader)
     pth_load_include (state, image, reader, stream);
 
   /* Read how many token hunks are contained in this image.  */
-  pth_read_uint (&num, stream);
+  num = pph_input_uint (stream);
   image->token_hunks = VEC_alloc (cp_token_hunk_ptr, gc, num);
 
   PTH_STATS_INCR (hunks, num);
@@ -1338,7 +1221,7 @@ pth_load_image (pth_state *state, pth_image *image, cpp_reader *reader)
   for (i = 0; i < num; i++)
     pth_load_hunk (image, stream);
 
-  fclose (stream);
+  pph_stream_close (stream);
 
   /* Indicate that we have loaded this image from a file.  */
   image->loaded_p = true;
@@ -1362,7 +1245,7 @@ pth_load_image (pth_state *state, pth_image *image, cpp_reader *reader)
 static bool
 pth_have_valid_image_for (const char *fname, pth_image *image)
 {
-  FILE *f = NULL;
+  pph_stream *f = NULL;
   struct stat s;
   char *img_name, *id;
   const char *good_id;
@@ -1375,28 +1258,28 @@ pth_have_valid_image_for (const char *fname, pth_image *image)
     goto invalid_img;
 
   /* If the file exists, check if it has a valid signature.  */
-  f = fopen (img_name, "r");
+  f = pph_stream_open (img_name, true);
 
   good_id = pth_id_str ();
   id = XCNEWVEC (char, strlen (good_id) + 1);
-  pth_read_bytes (id, strlen (good_id), f);
+  pph_input_bytes (f, id, strlen (good_id));
   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);
+  pph_input_bytes (f, saved_digest, DIGEST_LEN);
   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);
+  pph_stream_close (f);
   return true;
 
 invalid_img:
   if (f)
-    fclose (f);
+    pph_stream_close (f);
 
   return false;
 }
@@ -1410,7 +1293,7 @@ pth_new_image (const char *fname)
   pth_image *image;
 
   image = ggc_alloc_cleared_pth_image ();
-  image->fname = fname;
+  image->fname = xstrdup (fname);
 
   return image;
 }
@@ -1949,35 +1832,35 @@ pth_file_change (cpp_reader *reader, const struct line_map *map)
 
 /* Write PPH output file.  */
 
-typedef void (*write_pph_format)(FILE *stream, tree decl, int flags);
+typedef void (*write_pph_format)(pph_stream *stream, tree decl, int flags);
 
 static void
-write_pph_namespace (FILE *stream, tree decl, write_pph_format fmt, int flags);
+write_pph_namespace (pph_stream *stream, tree decl, write_pph_format fmt, int flags);
 
 
 /* Write symbol to PPH output file like C.  */
 
 static void
-write_pph_print (FILE *stream, tree decl, int flags)
+write_pph_print (pph_stream *stream, tree decl, int flags)
 {
-  print_generic_decl (stream, decl, flags | TDF_VISDEF);
-  fprintf (stream, "\n");
+  print_generic_decl (stream->file, decl, flags | TDF_VISDEF);
+  fprintf (stream->file, "\n");
 }
 
 
 /* Write symbol to PPH output file as a dump.  */
 
 static void
-write_pph_dump (FILE *stream, tree decl, int flags)
+write_pph_dump (pph_stream *stream, tree decl, int flags)
 {
-  dump_node (decl, flags, stream);
+  dump_node (decl, flags, stream->file);
 }
 
 
 /* Write symbol to PPH output file.  */
 
 static void
-write_pph_symbol (FILE *stream, tree decl, write_pph_format fmt, int flags)
+write_pph_symbol (pph_stream *stream, tree decl, write_pph_format fmt, int flags)
 {
   if (TREE_CODE (decl) == NAMESPACE_DECL)
     write_pph_namespace (stream, decl, fmt, flags);
@@ -1988,10 +1871,10 @@ write_pph_symbol (FILE *stream, tree decl, write_pph_format fmt, int flags)
 
 /* Write namespace to PPH output file.  */
 
-typedef void (*declvisitor)(FILE *, tree, write_pph_format, int);
+typedef void (*declvisitor)(pph_stream *, tree, write_pph_format, int);
 
 static void
-write_pph_namespace_1 (declvisitor vtor, FILE *stream, tree decl,
+write_pph_namespace_1 (declvisitor vtor, pph_stream *stream, tree decl,
                        write_pph_format fmt, int flags)
 {
   tree prior = TREE_CHAIN (decl);
@@ -2001,7 +1884,7 @@ write_pph_namespace_1 (declvisitor vtor, FILE *stream, tree decl,
 }
 
 static void
-write_pph_namespace (FILE *stream, tree decl, write_pph_format fmt, int flags)
+write_pph_namespace (pph_stream *stream, tree decl, write_pph_format fmt, int flags)
 {
   struct cp_binding_level *level = NAMESPACE_LEVEL (decl);
   decl = level->namespaces;
@@ -2016,11 +1899,11 @@ write_pph_namespace (FILE *stream, tree decl, write_pph_format fmt, int flags)
 /* Write PPH output symbols and IDENTS_USED to STREAM as an object.  */
 
 static void
-write_pph_file_object (FILE *stream, cpp_idents_used *idents_used)
+write_pph_file_object (pph_stream *stream, cpp_idents_used *idents_used)
 { 
   int flags = 0;
   pth_save_identifiers (idents_used, stream);
-  fprintf (stream, "\n====\n");
+  fprintf (stream->file, "\n====\n");
   /* FIX pph: Wrong format for writing decls.  */
   write_pph_namespace (stream, global_namespace, write_pph_print, flags);
 }
@@ -2029,7 +1912,7 @@ write_pph_file_object (FILE *stream, cpp_idents_used *idents_used)
 /* Write PPH output symbols and IDENTS_USED to STREAM as a pretty summary.  */
 
 static void
-write_pph_file_summary (FILE *stream, cpp_idents_used *idents_used)
+write_pph_file_summary (pph_stream *stream, cpp_idents_used *idents_used)
 { 
   int flags = 0;
   pph_print_macro_defs_before (stream, idents_used);
@@ -2041,10 +1924,10 @@ write_pph_file_summary (FILE *stream, cpp_idents_used *idents_used)
 /* Write PPH output symbols and IDENTS_USED to STREAM as a textual dump.  */
 
 static void
-write_pph_file_dump (FILE *stream, cpp_idents_used *idents_used)
+write_pph_file_dump (pph_stream *stream, cpp_idents_used *idents_used)
 { 
   int flags = TDF_UID | TDF_LINENO;
-  pth_dump_identifiers (stream, idents_used);
+  pth_dump_identifiers (stream->file, idents_used);
   write_pph_namespace (stream, global_namespace, write_pph_dump, flags);
 }
 
@@ -2054,13 +1937,13 @@ write_pph_file_dump (FILE *stream, cpp_idents_used *idents_used)
 static void
 write_pph_file (void)
 {
-  FILE *stream;
+  pph_stream *stream;
   cpp_idents_used idents_used;
 
   if (flag_pph_debug >= 1)
     fprintf (pph_logfile, "PPH: Writing %s\n", pph_out_file);
 
-  stream = fopen (pph_out_file, "w");
+  stream = pph_stream_open (pph_out_file, false);
   if (!stream)
     fatal_error ("Cannot open PPH file for writing: %s: %m", pph_out_file);
 
@@ -2076,7 +1959,7 @@ write_pph_file (void)
     error ("unrecognized -fpph-fmt value: %d", flag_pph_fmt);
 
   /*FIX pph: double free or corruption: cpp_lt_idents_destroy (&idents_used); */
-  fclose (stream);
+  pph_stream_close (stream);
 }
 
 
@@ -2125,7 +2008,7 @@ report_validation_error (const char *filename,
 /* Read PPH FILENAME from STREAM as an object.  */
 
 static void
-read_pph_file_object (const char *filename, FILE *stream)
+read_pph_file_object (const char *filename, pph_stream *stream)
 {
   bool verified;
   cpp_ident_use *bad_use;
@@ -2152,19 +2035,19 @@ read_pph_file_object (const char *filename, FILE *stream)
 static void
 read_pph_file (const char *filename)
 {
-  FILE *stream;
+  pph_stream *stream;
 
   if (flag_pph_debug >= 1)
     fprintf (pph_logfile, "PPH: Reading %s\n", filename);
 
-  stream = fopen (filename, "r");
-  if (!stream)
+  stream = pph_stream_open (filename, true);
+  if (!stream->file)
     fatal_error ("Cannot open PPH file for reading: %s: %m", filename);
 
   if (flag_pph_fmt == 0)
     read_pph_file_object (filename, stream);
 
-  fclose (stream);
+  pph_stream_close (stream);
 }
 
 /* Record a #include or #include_next for PTH.  */
diff --git a/gcc/cp/pph.h b/gcc/cp/pph.h

--- a/gcc/cp/pph.h
+++ b/gcc/cp/pph.h
@@ -1,4 +1,4 @@
-/* Factored pre-tokenized header (PTH) support for C++
+/* Factored pre-parsed header (PPH) support for C++
    Copyright (C) 2010 Free Software Foundation, Inc.
    Contributed by Lawrence Crowl <crowl@google.com> and
    Diego Novillo <dnovillo@google.com>.
diff --git a/gcc/lto-opts.c b/gcc/lto-opts.c

--- a/gcc/lto-opts.c
+++ b/gcc/lto-opts.c
@@ -162,18 +162,6 @@ output_string_stream (struct lto_output_stream *stream, const char *string)
     output_data_stream (stream, &flag, sizeof (flag));
 }
 
-/* Read LENGTH bytes from STREAM to ADDR.  */
-
-static void
-input_data_block (struct lto_input_block *ib, void *addr, size_t length)
-{
-  size_t i;
-  unsigned char *const buffer = (unsigned char *const) addr;
-
-  for (i = 0; i < length; i++)
-    buffer[i] = lto_input_1_unsigned (ib);
-}
-
 /* Return a string from IB.  The string is allocated, and the caller is
    responsible for freeing it.  */
 
@@ -182,15 +170,15 @@ input_string_block (struct lto_input_block *ib)
 {
   bool flag;
 
-  input_data_block (ib, &flag, sizeof (flag));
+  lto_input_data_block (ib, &flag, sizeof (flag));
   if (flag)
     {
       size_t length;
       char *string;
 
-      input_data_block (ib, &length, sizeof (length));
+      lto_input_data_block (ib, &length, sizeof (length));
       string = (char *) xcalloc (1, length + 1);
-      input_data_block (ib, string, length);
+      lto_input_data_block (ib, string, length);
 
       return string;
     }
@@ -336,16 +324,16 @@ input_options (struct lto_input_block *ib)
 {
   size_t length, i;
 
-  input_data_block (ib, &length, sizeof (length));
+  lto_input_data_block (ib, &length, sizeof (length));
 
   for (i = 0; i < length; i++)
     {
       opt_t o;
 
-      input_data_block (ib, &o.type, sizeof (o.type));
-      input_data_block (ib, &o.code, sizeof (o.code));
+      lto_input_data_block (ib, &o.type, sizeof (o.type));
+      lto_input_data_block (ib, &o.code, sizeof (o.code));
       o.arg = input_string_block (ib);
-      input_data_block (ib, &o.value, sizeof (o.value));
+      lto_input_data_block (ib, &o.value, sizeof (o.value));
       VEC_safe_push (opt_t, heap, file_options, &o);
     }
 }
diff --git a/gcc/lto-streamer-in.c b/gcc/lto-streamer-in.c

--- a/gcc/lto-streamer-in.c
+++ b/gcc/lto-streamer-in.c
@@ -191,10 +191,24 @@ input_identifier (struct data_in *data_in, struct lto_input_block *ib)
   return get_identifier_with_length (ptr, len);
 }
 
+
+/* Read LENGTH bytes from STREAM to ADDR.  */
+
+void
+lto_input_data_block (struct lto_input_block *ib, void *addr, size_t length)
+{
+  size_t i;
+  unsigned char *const buffer = (unsigned char *const) addr;
+
+  for (i = 0; i < length; i++)
+    buffer[i] = lto_input_1_unsigned (ib);
+}
+
+
 /* Read a NULL terminated string from the string table in DATA_IN.  */
 
-static const char *
-input_string (struct data_in *data_in, struct lto_input_block *ib)
+const char *
+lto_input_string (struct data_in *data_in, struct lto_input_block *ib)
 {
   unsigned int len;
   const char *ptr;
@@ -275,7 +289,7 @@ lto_input_location (struct lto_input_block *ib, struct data_in *data_in)
 {
   expanded_location xloc;
 
-  xloc.file = input_string (data_in, ib);
+  xloc.file = lto_input_string (data_in, ib);
   if (xloc.file == NULL)
     return UNKNOWN_LOCATION;
 
@@ -2310,7 +2324,7 @@ lto_input_ts_translation_unit_decl_tree_pointers (struct lto_input_block *ib,
 						  struct data_in *data_in,
 						  tree expr)
 {
-  TRANSLATION_UNIT_LANGUAGE (expr) = xstrdup (input_string (data_in, ib));
+  TRANSLATION_UNIT_LANGUAGE (expr) = xstrdup (lto_input_string (data_in, ib));
   VEC_safe_push (tree, gc, all_translation_units, expr);
 }
 
@@ -2591,7 +2605,7 @@ lto_get_builtin_tree (struct lto_input_block *ib, struct data_in *data_in)
   else
     gcc_unreachable ();
 
-  asmname = input_string (data_in, ib);
+  asmname = lto_input_string (data_in, ib);
   if (asmname)
     set_builtin_user_assembler_name (result, asmname);
 
diff --git a/gcc/lto-streamer-out.c b/gcc/lto-streamer-out.c

--- a/gcc/lto-streamer-out.c
+++ b/gcc/lto-streamer-out.c
@@ -154,11 +154,11 @@ destroy_output_block (struct output_block *ob)
    table in OB. The string might or might not include a trailing '\0'.
    Then put the index onto the INDEX_STREAM.  */
 
-static void
-output_string_with_length (struct output_block *ob,
-			   struct lto_output_stream *index_stream,
-			   const char *s,
-			   unsigned int len)
+void
+lto_output_string_with_length (struct output_block *ob,
+			       struct lto_output_stream *index_stream,
+			       const char *s,
+			       unsigned int len)
 {
   struct string_slot **slot;
   struct string_slot s_slot;
@@ -200,15 +200,16 @@ output_string_with_length (struct output_block *ob,
 /* Output the '\0' terminated STRING to the string
    table in OB.  Then put the index onto the INDEX_STREAM.  */
 
-static void
-output_string (struct output_block *ob,
-	       struct lto_output_stream *index_stream,
-	       const char *string)
+void
+lto_output_string (struct output_block *ob,
+	           struct lto_output_stream *index_stream,
+	           const char *string)
 {
   if (string)
     {
       lto_output_uleb128_stream (index_stream, 0);
-      output_string_with_length (ob, index_stream, string, strlen (string) + 1);
+      lto_output_string_with_length (ob, index_stream, string,
+				     strlen (string) + 1);
     }
   else
     lto_output_uleb128_stream (index_stream, 1);
@@ -226,9 +227,9 @@ output_string_cst (struct output_block *ob,
   if (string)
     {
       lto_output_uleb128_stream (index_stream, 0);
-      output_string_with_length (ob, index_stream,
-				 TREE_STRING_POINTER (string),
-				 TREE_STRING_LENGTH (string));
+      lto_output_string_with_length (ob, index_stream,
+				     TREE_STRING_POINTER (string),
+				     TREE_STRING_LENGTH (string));
     }
   else
     lto_output_uleb128_stream (index_stream, 1);
@@ -246,9 +247,9 @@ output_identifier (struct output_block *ob,
   if (id)
     {
       lto_output_uleb128_stream (index_stream, 0);
-      output_string_with_length (ob, index_stream,
-				 IDENTIFIER_POINTER (id),
-				 IDENTIFIER_LENGTH (id));
+      lto_output_string_with_length (ob, index_stream,
+				     IDENTIFIER_POINTER (id),
+				     IDENTIFIER_LENGTH (id));
     }
   else
     lto_output_uleb128_stream (index_stream, 1);
@@ -611,13 +612,13 @@ lto_output_location (struct output_block *ob, location_t loc)
 
   if (loc == UNKNOWN_LOCATION)
     {
-      output_string (ob, ob->main_stream, NULL);
+      lto_output_string (ob, ob->main_stream, NULL);
       return;
     }
 
   xloc = expand_location (loc);
 
-  output_string (ob, ob->main_stream, xloc.file);
+  lto_output_string (ob, ob->main_stream, xloc.file);
   output_sleb128 (ob, xloc.line);
   output_sleb128 (ob, xloc.column);
   output_sleb128 (ob, xloc.sysp);
@@ -1155,7 +1156,7 @@ static void
 lto_output_ts_translation_unit_decl_tree_pointers (struct output_block *ob,
 						   tree expr)
 {
-  output_string (ob, ob->main_stream, TRANSLATION_UNIT_LANGUAGE (expr));
+  lto_output_string (ob, ob->main_stream, TRANSLATION_UNIT_LANGUAGE (expr));
 }
 
 /* Helper for lto_output_tree.  Write all pointer fields in EXPR to output
@@ -1320,12 +1321,12 @@ lto_output_builtin_tree (struct output_block *ob, tree expr, int ix)
 	 reader side from adding a second '*', we omit it here.  */
       const char *str = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (expr));
       if (strlen (str) > 1 && str[0] == '*')
-	output_string (ob, ob->main_stream, &str[1]);
+	lto_output_string (ob, ob->main_stream, &str[1]);
       else
-	output_string (ob, ob->main_stream, NULL);
+	lto_output_string (ob, ob->main_stream, NULL);
     }
   else
-    output_string (ob, ob->main_stream, NULL);
+    lto_output_string (ob, ob->main_stream, NULL);
 }
 
 
@@ -1745,7 +1746,7 @@ output_gimple_stmt (struct output_block *ob, gimple stmt)
       lto_output_uleb128_stream (ob->main_stream, gimple_asm_noutputs (stmt));
       lto_output_uleb128_stream (ob->main_stream, gimple_asm_nclobbers (stmt));
       lto_output_uleb128_stream (ob->main_stream, gimple_asm_nlabels (stmt));
-      output_string (ob, ob->main_stream, gimple_asm_string (stmt));
+      lto_output_string (ob, ob->main_stream, gimple_asm_string (stmt));
       /* Fallthru  */
 
     case GIMPLE_ASSIGN:
@@ -2342,7 +2343,7 @@ write_global_references (struct output_block *ob,
 /* Write all the streams in an lto_out_decl_state STATE using
    output block OB and output stream OUT_STREAM.  */
 
-static void
+void
 lto_output_decl_state_streams (struct output_block *ob,
 			       struct lto_out_decl_state *state)
 {
@@ -2356,7 +2357,7 @@ lto_output_decl_state_streams (struct output_block *ob,
 /* Write all the references in an lto_out_decl_state STATE using
    output block OB and output stream OUT_STREAM.  */
 
-static void
+void
 lto_output_decl_state_refs (struct output_block *ob,
 			    struct lto_output_stream *out_stream,
 			    struct lto_out_decl_state *state)
diff --git a/gcc/lto-streamer.c b/gcc/lto-streamer.c

--- a/gcc/lto-streamer.c
+++ b/gcc/lto-streamer.c
@@ -567,6 +567,10 @@ lto_get_common_nodes (void)
   else
     main_identifier_node = get_identifier ("main");
 
+  /* FIXME pph.  These assertions are never met while in the front end.
+     There should be a way of checking this only when we are in LTO
+     mode.  */
+#if 0
   gcc_assert (ptrdiff_type_node == integer_type_node);
 
   /* FIXME lto.  In the C++ front-end, fileptr_type_node is defined as a
@@ -577,6 +581,7 @@ lto_get_common_nodes (void)
      These should be assured in pass_ipa_free_lang_data.  */
   gcc_assert (fileptr_type_node == ptr_type_node);
   gcc_assert (TYPE_MAIN_VARIANT (fileptr_type_node) == ptr_type_node);
+#endif
 
   seen_nodes = pointer_set_create ();
 
diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h

--- a/gcc/lto-streamer.h
+++ b/gcc/lto-streamer.h
@@ -862,6 +862,9 @@ extern struct data_in *lto_data_in_create (struct lto_file_decl_data *,
 				    const char *, unsigned,
 				    VEC(ld_plugin_symbol_resolution_t,heap) *);
 extern void lto_data_in_delete (struct data_in *);
+extern const char *lto_input_string (struct data_in *,
+				     struct lto_input_block *);
+extern void lto_input_data_block (struct lto_input_block *, void *, size_t);
 
 
 /* In lto-streamer-out.c  */
@@ -870,6 +873,18 @@ extern struct output_block *create_output_block (enum lto_section_type);
 extern void destroy_output_block (struct output_block *);
 extern void lto_output_tree (struct output_block *, tree, bool);
 extern void produce_asm (struct output_block *ob, tree fn);
+extern void lto_output_string (struct output_block *,
+			       struct lto_output_stream *,
+			       const char *);
+extern void lto_output_string_with_length (struct output_block *,
+			                   struct lto_output_stream *,
+			                   const char *,
+			                   unsigned int);
+void lto_output_decl_state_streams (struct output_block *,
+				    struct lto_out_decl_state *);
+void lto_output_decl_state_refs (struct output_block *,
+			         struct lto_output_stream *,
+			         struct lto_out_decl_state *);
 
 
 /* In lto-cgraph.c  */
diff --git a/gcc/testsuite/g++.dg/dg.exp b/gcc/testsuite/g++.dg/dg.exp

--- a/gcc/testsuite/g++.dg/dg.exp
+++ b/gcc/testsuite/g++.dg/dg.exp
@@ -40,6 +40,7 @@ set tests [prune $tests $srcdir/$subdir/gcov/*]
 set tests [prune $tests $srcdir/$subdir/lto/*]
 set tests [prune $tests $srcdir/$subdir/pch/*]
 set tests [prune $tests $srcdir/$subdir/pph/*]
+set tests [prune $tests $srcdir/$subdir/pth/*]
 set tests [prune $tests $srcdir/$subdir/plugin/*]
 set tests [prune $tests $srcdir/$subdir/special/*]
 set tests [prune $tests $srcdir/$subdir/tls/*]
diff --git a/gcc/testsuite/g++.dg/pth/autometh.cc b/gcc/testsuite/g++.dg/pth/autometh.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/autometh.cc
@@ -0,0 +1,11 @@
+class base {
+    virtual int method() {
+        return 0;
+    }
+    int field;
+};
+void function() {
+    base var1;
+    base var2( var1 );
+    var1 = var2;
+}
diff --git a/gcc/testsuite/g++.dg/pth/builtin.cc b/gcc/testsuite/g++.dg/pth/builtin.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/builtin.cc
@@ -0,0 +1,3 @@
+#include "builtin1.h"
+#include "builtin2.h"
+#include "builtin3.h"
diff --git a/gcc/testsuite/g++.dg/pth/builtin1.h b/gcc/testsuite/g++.dg/pth/builtin1.h
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/builtin1.h
@@ -0,0 +1,2 @@
+#define QUOTE(arg) #arg
+#define VALUE(arg) QUOTE(arg)
diff --git a/gcc/testsuite/g++.dg/pth/builtin2.h b/gcc/testsuite/g++.dg/pth/builtin2.h
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/builtin2.h
@@ -0,0 +1,7 @@
+const char *date = VALUE(__DATE__);
+const char *time = VALUE(__TIME__);
+const char *file = VALUE(__FILE__);
+const char *line = VALUE(__LINE__);
+const char *vers = VALUE(__cplusplus);
+const char *optm = VALUE(__OPTIMIZE__);
+const char *func() { return __func__; }
diff --git a/gcc/testsuite/g++.dg/pth/builtin3.h b/gcc/testsuite/g++.dg/pth/builtin3.h
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/builtin3.h
@@ -0,0 +1,2 @@
+const char *refile = VALUE(__FILE__);
+const char *reline = VALUE(__LINE__);
diff --git a/gcc/testsuite/g++.dg/pth/cflow.cc b/gcc/testsuite/g++.dg/pth/cflow.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/cflow.cc
@@ -0,0 +1,6 @@
+#include "cflow.h"
+
+void foo (void)
+{
+  int x = var1 + var2 - (int) f1;
+}
diff --git a/gcc/testsuite/g++.dg/pth/cflow.h b/gcc/testsuite/g++.dg/pth/cflow.h
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/cflow.h
@@ -0,0 +1,13 @@
+#ifndef __CFLOW_H
+#define __CFLOW_H
+
+extern void foo (void);
+
+#define X	1
+#if defined X
+#include "inif.h"
+extern int var1;
+extern int var2;
+#endif
+#include "inif.h"
+#endif
diff --git a/gcc/testsuite/g++.dg/pth/chained.cc b/gcc/testsuite/g++.dg/pth/chained.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/chained.cc
@@ -0,0 +1,3 @@
+#include "chained1.h"
+#include "chained2.h"
+int x = TWO;
diff --git a/gcc/testsuite/g++.dg/pth/chained1.h b/gcc/testsuite/g++.dg/pth/chained1.h
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/chained1.h
@@ -0,0 +1 @@
+#define ONE 1
diff --git a/gcc/testsuite/g++.dg/pth/chained2.h b/gcc/testsuite/g++.dg/pth/chained2.h
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/chained2.h
@@ -0,0 +1 @@
+#define TWO ONE
diff --git a/gcc/testsuite/g++.dg/pth/classshort.cc b/gcc/testsuite/g++.dg/pth/classshort.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/classshort.cc
@@ -0,0 +1,19 @@
+typedef int type;
+type gbl = 1;
+struct B {
+    type fld;
+};
+struct D : B {
+    type method();
+    type another()
+    { return fld + mbr + gbl; }
+    type fld;
+    static type mbr;
+};
+type D::method()
+{ static int x = 2;
+  return fld + mbr + gbl; }
+type D::mbr = 4;
+typedef D D2;
+D2 var1;
+D2 var2 = var1;
diff --git a/gcc/testsuite/g++.dg/pth/empty.cc b/gcc/testsuite/g++.dg/pth/empty.cc
new file mode 100644

diff --git a/gcc/testsuite/g++.dg/pth/emptyclass.cc b/gcc/testsuite/g++.dg/pth/emptyclass.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/emptyclass.cc
@@ -0,0 +1,2 @@
+struct B {
+};
diff --git a/gcc/testsuite/g++.dg/pth/field.cc b/gcc/testsuite/g++.dg/pth/field.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/field.cc
@@ -0,0 +1,7 @@
+typedef int language;
+struct program {
+    language field;
+};
+struct client {
+    program field;
+};
diff --git a/gcc/testsuite/g++.dg/pth/funcstatic.cc b/gcc/testsuite/g++.dg/pth/funcstatic.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/funcstatic.cc
@@ -0,0 +1,3 @@
+int f() {
+    static int x = 3;
+}
diff --git a/gcc/testsuite/g++.dg/pth/functions.cc b/gcc/testsuite/g++.dg/pth/functions.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/functions.cc
@@ -0,0 +1,22 @@
+extern int extern_only(int);		//
+extern int extern_then_body(int);	//
+inline int extern_inline(int i)		// lazy body
+{ return extern_then_body(i); }
+int extern_then_body(int i)		// need body, merge head to body
+{ return extern_only( i ); }
+static int fwdref_static(int);
+int fwdref_static(int i)
+{ return extern_then_body( i ); }	// need body, merge head to body
+struct type {
+    int mbr_decl_only(int);
+    int mbr_decl_then_def(int);
+    inline int mbr_inl_then_def(int);
+    int mbr_decl_inline(int i)		// lazy body
+    { return mbr_decl_only( i ); }
+    virtual int mbr_virtual_inline()	// lazy body, but circular dependence
+    { return mbr_decl_only( 1 ); }
+};
+int type::mbr_decl_then_def(int i)	// need body
+{ return mbr_decl_inline( i ); }
+int type::mbr_inl_then_def(int i)	// lazy body
+{ return mbr_decl_then_def( i ); }
diff --git a/gcc/testsuite/g++.dg/pth/globalref.cc b/gcc/testsuite/g++.dg/pth/globalref.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/globalref.cc
@@ -0,0 +1,7 @@
+typedef int type;
+type x = 2;
+type y = x;
+type f() { return x; }
+const type n = 3;
+const type m = 4;
+type a[n+m];
diff --git a/gcc/testsuite/g++.dg/pth/guarded.cc b/gcc/testsuite/g++.dg/pth/guarded.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/guarded.cc
@@ -0,0 +1,2 @@
+#include "guarded2.h"
+#include "guarded3.h"
diff --git a/gcc/testsuite/g++.dg/pth/guarded1.h b/gcc/testsuite/g++.dg/pth/guarded1.h
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/guarded1.h
@@ -0,0 +1,6 @@
+#ifndef GUARDED1_H
+#define GUARDED1_H
+
+typedef int type;
+
+#endif
diff --git a/gcc/testsuite/g++.dg/pth/guarded2.h b/gcc/testsuite/g++.dg/pth/guarded2.h
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/guarded2.h
@@ -0,0 +1,8 @@
+#ifndef GUARDED2_H
+#define GUARDED2_H
+
+#include "guarded1.h"
+
+type variable2;
+
+#endif
diff --git a/gcc/testsuite/g++.dg/pth/guarded3.h b/gcc/testsuite/g++.dg/pth/guarded3.h
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/guarded3.h
@@ -0,0 +1,8 @@
+#ifndef GUARDED3_H
+#define GUARDED3_H
+
+#include "guarded1.h"
+
+type variable3;
+
+#endif
diff --git a/gcc/testsuite/g++.dg/pth/hardlookup.cc b/gcc/testsuite/g++.dg/pth/hardlookup.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/hardlookup.cc
@@ -0,0 +1,29 @@
+struct V { int a; static int b;};
+namespace N {
+   int V;
+   struct C { };
+   int operator + (int i, C c);
+   C O;
+   int I(int arg)
+   { return arg + V; }
+   struct V w;
+   int x = V::b;
+}
+
+int V;
+struct D { };
+D P;
+int operator + (int i, D d);
+int I(int arg)
+{ return arg + V; }
+
+int F() {
+   return I(N::V + N::O);
+}
+
+int G() {
+   return I(::V + P);
+}
+
+struct V w;
+int x = V::b;
diff --git a/gcc/testsuite/g++.dg/pth/incmod.cc b/gcc/testsuite/g++.dg/pth/incmod.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/incmod.cc
@@ -0,0 +1,9 @@
+struct T { int f; };
+#define NAME v
+#define VALUE 1
+#include "incmod.h"
+#undef NAME
+#define NAME w
+#undef VALUE
+#define VALUE 2
+#include "incmod.h"
diff --git a/gcc/testsuite/g++.dg/pth/incmod.h b/gcc/testsuite/g++.dg/pth/incmod.h
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/incmod.h
@@ -0,0 +1 @@
+struct T NAME = { VALUE };
diff --git a/gcc/testsuite/g++.dg/pth/incsame.cc b/gcc/testsuite/g++.dg/pth/incsame.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/incsame.cc
@@ -0,0 +1,5 @@
+struct T { int f; };
+struct T v
+#include "incsame.h"
+struct T w
+#include "incsame.h"
diff --git a/gcc/testsuite/g++.dg/pth/incsame.h b/gcc/testsuite/g++.dg/pth/incsame.h
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/incsame.h
@@ -0,0 +1 @@
+= { 1 };
diff --git a/gcc/testsuite/g++.dg/pth/inif.h b/gcc/testsuite/g++.dg/pth/inif.h
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/inif.h
@@ -0,0 +1 @@
+extern float f1;
diff --git a/gcc/testsuite/g++.dg/pth/invoke.cc b/gcc/testsuite/g++.dg/pth/invoke.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/invoke.cc
@@ -0,0 +1,6 @@
+#include "invoke.h"
+#define outer(major, minor) inner(major, minor)
+
+void outer(long one, short two) { }
+major(three);
+minor(four);
diff --git a/gcc/testsuite/g++.dg/pth/invoke.h b/gcc/testsuite/g++.dg/pth/invoke.h
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/invoke.h
@@ -0,0 +1,4 @@
+# define gnu_dev_major(dev) long dev
+# define gnu_dev_minor(dev) short dev
+# define major(dev) gnu_dev_major (dev)
+# define minor(dev) gnu_dev_minor (dev)
diff --git a/gcc/testsuite/g++.dg/pth/mean.cc b/gcc/testsuite/g++.dg/pth/mean.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/mean.cc
@@ -0,0 +1,163 @@
+#include <stdlib.h>	// { dg-error "fatal" "invalid hunk" { xfail *-*-* } }
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+static unsigned long long MAX_ITEMS = 10000;
+
+static int
+cmpdouble (const void *p1, const void *p2)
+{
+  double n1 = *((double *)p1);
+  double n2 = *((double *)p2);
+
+  if (n1 < n2)
+    return -1;
+  else if (n1 > n2)
+    return 1;
+  else
+    return 0;
+}
+
+
+double
+compute_median (int n, double vec[])
+{
+  qsort (vec, n, sizeof (double), cmpdouble);
+  
+  if (n % 2 == 0)
+    return ((vec[n / 2] + vec[n / 2 - 1]) / 2.0);
+  else
+    return vec[n / 2];
+}
+
+double
+compute_stddev (int n, double avg, double vec[])
+{
+  double sd, sum, s, x;
+  int i;
+
+  for (x = 0.0, sum = 0.0, i = 0; i < n; i++)
+    {
+      double d;
+
+      x = x + vec[i];
+      d = vec[i] - avg;
+      sum += d * d;
+    }
+
+  s = sum / n;
+  sd = sqrt (s);
+
+  return sd;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  double *vec;
+  double x, sum, product, inverse_sum, arithmetic, geometric, harmonic;
+  double min, max, median, variance, stddev;
+  int count;
+  int ret;
+
+  sum = 0.0;
+  product = 1.0;
+  inverse_sum = 0.0;
+  count = 0;
+  min = 0.0;
+  max = 0.0;
+
+  vec = (double *) malloc (MAX_ITEMS * sizeof (double));
+
+  while (1)
+    {
+      ret = scanf ("%lg", &x);
+      if (ret == EOF)
+	break;
+
+      if (count == 0)
+	min = max = x;
+
+      if (x < min)
+	min = x;
+
+      if (x > max)
+	max = x;
+
+      sum += x;
+      product *= x;
+      inverse_sum += 1.0 / x;
+      vec[count] = x;
+
+      count++;
+      if (count >= MAX_ITEMS)
+	{
+	  MAX_ITEMS *= 3;
+	  vec = (double *) realloc (vec, MAX_ITEMS * sizeof (double));
+	}
+    }
+
+  int do_min = (strstr (argv[0], "min") != NULL);
+  int do_max = (strstr (argv[0], "max") != NULL);
+  int do_avg = (strstr (argv[0], "avg") != NULL);
+  int do_geo = (strstr (argv[0], "geoavg") != NULL);
+  int do_harmonic = (strstr (argv[0], "harmonic") != NULL);
+  int do_median = (strstr (argv[0], "median") != NULL);
+  int do_variance = (strstr (argv[0], "variance") != NULL);
+  int do_stdev = (strstr (argv[0], "stdev") != NULL);
+  int do_all = (argc > 1 && strcmp (argv[1], "-a") == 0);
+
+  if (count > 0)
+    {
+      arithmetic = sum / count;
+      geometric = pow (product, (double) 1.0 / (double) count);
+      harmonic = count / inverse_sum;
+      median = compute_median (count, vec);
+      stddev = compute_stddev (count, arithmetic, vec);
+      variance = stddev * stddev;
+
+      if (do_all)
+	{
+	  printf ("# of items read --> %d\n", count);
+	  printf ("Min --------------> %lg\n", min);
+	  printf ("Max --------------> %lg\n", max);
+	  printf ("Arithmetic mean --> %lg\n", arithmetic);
+	  printf ("Geometric mean ---> %lg\n", geometric);
+	  printf ("Harmonic mean ----> %lg\n", harmonic);
+	  printf ("Median -----------> %lg\n", median);
+	  printf ("Variance ---------> %lg\n", variance);
+	  printf ("Standard dev -----> %lg\n", stddev);
+	}
+      else if (do_min)
+	printf ("%lg\n", min);
+      else if (do_max)
+	printf ("%lg\n", max);
+      else if (do_avg)
+	printf ("%lg\n", arithmetic);
+      else if (do_geo)
+	printf ("%lg\n", geometric);
+      else if (do_harmonic)
+	printf ("%lg\n", harmonic);
+      else if (do_median)
+	printf ("%lg\n", median);
+      else if (do_variance)
+	printf ("%lg\n", variance);
+      else if (do_stdev)
+	printf ("%lg\n", stddev);
+      else
+	{
+	  fprintf (stderr, "ERROR: Unknown value '%s' to compute\n", argv[0]);
+	  return 1;
+	}
+    }
+  else
+    {
+      fprintf (stderr, "ERROR: none of the input is positive\n");
+      return 1;
+    }
+
+  return 0;
+}
+// { dg-error "excess errors" "" { xfail *-*-* } }
diff --git a/gcc/testsuite/g++.dg/pth/meth.cc b/gcc/testsuite/g++.dg/pth/meth.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/meth.cc
@@ -0,0 +1,4 @@
+typedef int type;
+class base {
+    type fld;
+};
diff --git a/gcc/testsuite/g++.dg/pth/meth2.cc b/gcc/testsuite/g++.dg/pth/meth2.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/meth2.cc
@@ -0,0 +1,6 @@
+class one {
+};
+typedef one type;
+class two {
+    type fld;
+};
diff --git a/gcc/testsuite/g++.dg/pth/nontrivinit.cc b/gcc/testsuite/g++.dg/pth/nontrivinit.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/nontrivinit.cc
@@ -0,0 +1,2 @@
+int y = 0;
+int x = y+1;
diff --git a/gcc/testsuite/g++.dg/pth/paste.cc b/gcc/testsuite/g++.dg/pth/paste.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/paste.cc
@@ -0,0 +1,10 @@
+#define major	"beef"
+#define M(major)	foo##_##major##_(major)
+
+void foo_30_(int);
+void bar(void);
+
+void bar(void)
+{
+  M(30);
+}
diff --git a/gcc/testsuite/g++.dg/pth/pth.exp b/gcc/testsuite/g++.dg/pth/pth.exp
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/pth.exp
@@ -0,0 +1,38 @@
+# Copyright (C) 2011 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# GCC testsuite for preparsed header interaction.
+
+# Contributed by Diego Novillo <dnovillo@google.com>
+
+# Load support procs.
+load_lib "g++-dg.exp"
+load_lib dg-pth.exp
+
+# Initialize `dg'.
+dg-init
+
+set old_dg_do_what_default "${dg-do-what-default}"
+
+# Main loop.
+foreach test [lsort [glob -nocomplain $srcdir/$subdir/*.cc]] {
+    dg-pth $subdir $test [list "-fpth" ] ".pth"
+}
+
+set dg-do-what-default "$old_dg_do_what_default"
+
+# All done.
+dg-finish
diff --git a/gcc/testsuite/g++.dg/pth/simple.cc b/gcc/testsuite/g++.dg/pth/simple.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/simple.cc
@@ -0,0 +1,7 @@
+/* comment */
+#include "simple1.h"
+
+int main()
+{
+	return foo();
+}
diff --git a/gcc/testsuite/g++.dg/pth/simple1.h b/gcc/testsuite/g++.dg/pth/simple1.h
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/simple1.h
@@ -0,0 +1,11 @@
+#ifndef SIMPLE1_H
+#define SIMPLE1_H
+
+#include "simple2.h"
+
+inline int foo()
+{
+  assert(1);
+}
+
+#endif
diff --git a/gcc/testsuite/g++.dg/pth/simple2.h b/gcc/testsuite/g++.dg/pth/simple2.h
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/simple2.h
@@ -0,0 +1,7 @@
+#ifndef SIMPLE2_H
+#define SIMPLE2_H
+
+#define assert(x) yellow(x)
+extern void yellow(int);
+
+#endif
diff --git a/gcc/testsuite/g++.dg/pth/simplecall.cc b/gcc/testsuite/g++.dg/pth/simplecall.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/simplecall.cc
@@ -0,0 +1,10 @@
+int H( int a, int b) {
+}
+
+int I( int a, int b) {
+    return a+b;
+}
+
+int F( int c, int d) {
+    return H(c, d) + I(c, d);
+}
diff --git a/gcc/testsuite/g++.dg/pth/special.cc b/gcc/testsuite/g++.dg/pth/special.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/special.cc
@@ -0,0 +1,30 @@
+struct B {
+    B( int );
+    B( double );
+    B( const B& );
+    B& operator=( const B& );
+    B& operator=( int );
+    operator double();
+    ~B();
+};
+
+struct D {
+    D( const B& );
+};
+
+int F( D );
+int G( double );
+
+B b(1);
+//B q("hhh");
+
+int H() {
+    F(b);
+    B a(3.2);
+    B c = b;
+    B d(b);
+    c = b;
+    d = 4;
+    //G(double(""));
+    G(d);
+}
diff --git a/gcc/testsuite/g++.dg/pth/staticmbrvar.cc b/gcc/testsuite/g++.dg/pth/staticmbrvar.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/staticmbrvar.cc
@@ -0,0 +1,5 @@
+struct D {
+    int method() { return mbr; }
+    static int mbr;
+};
+int D::mbr = 4;
diff --git a/gcc/testsuite/g++.dg/pth/sys-types.cc b/gcc/testsuite/g++.dg/pth/sys-types.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/sys-types.cc
@@ -0,0 +1,2 @@
+#include <sys/types.h> 	// { dg-error "fatal" "invalid hunk" { xfail *-*-* } }
+// { dg-error "excess errors" "" { xfail *-*-* } }
diff --git a/gcc/testsuite/g++.dg/pth/system-include.cc b/gcc/testsuite/g++.dg/pth/system-include.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/system-include.cc
@@ -0,0 +1,3 @@
+#include <stdlib.h>	// { dg-error "fatal" "invalid hunk" { xfail *-*-* } }
+size_t X;
+// { dg-error "excess errors" "" { xfail *-*-* } }
diff --git a/gcc/testsuite/g++.dg/pth/template.cc b/gcc/testsuite/g++.dg/pth/template.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/template.cc
@@ -0,0 +1,24 @@
+namespace A {
+int x = 3;
+struct B;
+template< typename T >
+struct C {
+    T* b;
+    int method();
+    int another()
+    { return *b; }
+};
+template< typename T >
+int C< T >::method()
+{ return x; }
+} // namespace A
+int y = 4;
+struct D : A::C< int > {
+    int method();
+    int another()
+    { return *b; }
+};
+int D::method()
+{ return y; }
+int main()
+{ }
diff --git a/gcc/testsuite/g++.dg/pth/tmplclass.cc b/gcc/testsuite/g++.dg/pth/tmplclass.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/tmplclass.cc
@@ -0,0 +1,29 @@
+template< typename T >
+struct wrapper {
+  T value;
+  static T cache;
+};
+
+template< typename T >
+T wrapper<T>::cache = 3;
+
+template<>
+struct wrapper<char> {
+  int value;
+  static int cache;
+};
+
+int wrapper<char>::cache = 2;
+
+template
+struct wrapper<short>;
+
+template
+long wrapper<long>::cache;
+
+int main() {
+  wrapper<char> vc;
+  wrapper<short> vs;
+  wrapper<int> vi;
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/pth/tmplfunc.cc b/gcc/testsuite/g++.dg/pth/tmplfunc.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/tmplfunc.cc
@@ -0,0 +1,17 @@
+typedef int type;
+type val = 3;
+
+template< typename T >
+T identity(T arg)
+{ return arg + val; }
+
+template<>
+int identity< type >(type arg)
+{ return arg + val; }
+
+template
+short identity(short arg);
+
+int main() {
+  return identity( 'a' );
+}
diff --git a/gcc/testsuite/g++.dg/pth/tmplsimple.cc b/gcc/testsuite/g++.dg/pth/tmplsimple.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/tmplsimple.cc
@@ -0,0 +1,11 @@
+template< typename T >
+struct C {
+    int* b;
+    int method();
+    int another()
+    { return 1; }
+};
+template< typename T >
+int C< T >::method()
+{ return 1; }
+C<int> v;
diff --git a/gcc/testsuite/g++.dg/pth/trivial.cc b/gcc/testsuite/g++.dg/pth/trivial.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/trivial.cc
@@ -0,0 +1,3 @@
+int main()
+{
+}
diff --git a/gcc/testsuite/g++.dg/pth/typerefs.cc b/gcc/testsuite/g++.dg/pth/typerefs.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/typerefs.cc
@@ -0,0 +1,18 @@
+struct base {
+    int field;
+};
+base function();
+int base_from_var() {
+    base variable;
+}
+int base_from_func() {
+    function();
+}
+struct derived : base {
+    int method();
+};
+int derived::method() {
+    return field;
+}
+struct vderived : virtual base {
+};
diff --git a/gcc/testsuite/g++.dg/pth/usearray.cc b/gcc/testsuite/g++.dg/pth/usearray.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/usearray.cc
@@ -0,0 +1,8 @@
+#include "usearray.h"
+const int x = 3;
+const int y = 10;
+float array[x + y];
+float foo (int i)
+{
+  return array[i * 3];
+}
diff --git a/gcc/testsuite/g++.dg/pth/usearray.h b/gcc/testsuite/g++.dg/pth/usearray.h
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/usearray.h
@@ -0,0 +1 @@
+float foo(int);
diff --git a/gcc/testsuite/g++.dg/pth/variable.cc b/gcc/testsuite/g++.dg/pth/variable.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/variable.cc
@@ -0,0 +1,2 @@
+int one;
+int two;
diff --git a/gcc/testsuite/g++.dg/pth/variables.cc b/gcc/testsuite/g++.dg/pth/variables.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/variables.cc
@@ -0,0 +1,17 @@
+extern int gbl_init_extern;		// need body			pass
+extern int gbl_uninit_extern;		// head only			pass
+int gbl_tentative;			// need body			pass
+int gbl_initial = 1;			// need body			pass
+extern const int gbl_extern_const;	// head only			pass
+const float gbl_init_const = 1.5;	// need body			pass
+const int gbl_manifest = 2;		// lazy body	merge head	pass
+struct D {
+    static int mbr_init_plain;		// head only			pass
+    static int mbr_uninit_plain;	// head only			pass
+    static const int mbr_init_const;	// head only			pass
+    static const int mbr_uninit_const;	// head only			pass
+    static const int mbr_manifest = 3;	// lazy body	merge head	okay
+};
+int D::mbr_init_plain = 4;		// need body	merge body	pass
+int D::mbr_uninit_plain;		// need body	merge body	FAIL
+const int D::mbr_init_const = 5;	// need body	merge body	FAIL
diff --git a/gcc/testsuite/g++.dg/pth/where.cc b/gcc/testsuite/g++.dg/pth/where.cc
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/g++.dg/pth/where.cc
@@ -0,0 +1,21 @@
+namespace A {
+int x = 3;
+struct B;
+struct C {
+    B* b;
+    int method();
+    int another()
+    { return 1; }
+};
+int C::method()
+{ return 1; }
+} // namespace A
+struct D : A::C {
+    int method();
+    int another()
+    { return 1; }
+};
+int D::method()
+{ return 1; }
+int main()
+{ }
diff --git a/gcc/testsuite/lib/dg-pth.exp b/gcc/testsuite/lib/dg-pth.exp
new file mode 100644

--- /dev/null
+++ b/gcc/testsuite/lib/dg-pth.exp
@@ -0,0 +1,79 @@
+# Copyright (C) 2011 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# Contributed by Diego Novillo <dnovillo@google.com>
+
+load_lib copy-file.exp
+
+proc dg-flags-pth { subdir test otherflags options suffix } {
+    global runtests dg-do-what-default
+
+    # If we're only testing specific files and this isn't one of them, skip it.
+    if ![runtest_file_p $runtests $test] {
+       return
+    }
+    set nshort "$subdir/[file tail $test]"
+    set bname "[file rootname [file tail $nshort]]"
+
+    foreach flags $options {
+       verbose "Testing $nshort, $otherflags $flags" 1
+
+       set dg-do-what-default compile
+
+       set have_errs [llength [grep $test "{\[ \t\]\+dg-error\[ \t\]\+.*\[ \t\] \+}"]]
+       # Compile the file the first time to produce PTH images.
+       dg-test -keep-output $test "$otherflags $flags -I." ""
+
+       if { !$have_errs } {
+           if { [ file_on_host exists "$bname.s" ] } {
+               # Rename the .s file into .s-pth to compare it after the
+               # second build.
+               remote_upload host "$bname.s" "$bname.s-pth"
+               remote_download host "$bname.s-pth"
+
+               # Compile a second time to use the generated images.
+               dg-test -keep-output $test "$otherflags $flags -I." ""
+               remote_upload host "$bname.s"
+
+               # Compare the two assembly files.  They should be identical.
+               set tmp [ diff "$bname.s" "$bname.s-pth" ]
+               if { $tmp == 0 } {
+                   verbose -log "assembly file '$bname.s', '$bname.s-pth' comparison error"
+                   fail "$nshort $otherflags $flags assembly comparison"
+               } elseif { $tmp == 1 } {
+                   pass "$nshort $otherflags $flags assembly comparison"
+               } else {
+                   fail "$nshort $otherflags $flags assembly comparison"
+               }
+               file_on_host delete "$bname$suffix"
+               file_on_host delete "$bname.s"
+               file_on_host delete "$bname.s-pth"
+           } else {
+               verbose -log "assembly file '$bname.s' missing"
+               fail "$nshort $flags assembly comparison"
+           }
+       }
+
+       # Remove stale .pth files, if any.
+       foreach pth_file [glob -nocomplain *.pth] {
+           file_on_host delete $pth_file
+       }
+    }
+}
+
+proc dg-pth { subdir test options suffix } {
+  return [dg-flags-pth $subdir $test "" $options $suffix]
+}

--
This patch is available for review at http://codereview.appspot.com/4250071


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]