This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[pph] Add streaming support using LTO streamer (issue4250071)
- From: dnovillo at google dot com (Diego Novillo)
- To: gcc-patches at gcc dot gnu dot org, reply at codereview dot appspotmail dot com, crowl at google dot com
- Date: Wed, 9 Mar 2011 14:42:41 -0500 (EST)
- Subject: [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