OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / gcc / coverage.c
index 2523317..ce8b175 100644 (file)
@@ -1,6 +1,7 @@
 /* Read and write coverage files, and associated functionality.
    Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999,
 /* Read and write coverage files, and associated functionality.
    Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999,
-   2000, 2001, 2003  Free Software Foundation, Inc.
+   2000, 2001, 2003, 2004, 2005, 2007, 2008, 2009, 2010, 2011, 2012
+   Free Software Foundation, Inc.
    Contributed by James E. Wilson, UC Berkeley/Cygnus Support;
    based on some ideas from Dain Samples of UC Berkeley.
    Further mangling by Bob Manson, Cygnus Support.
    Contributed by James E. Wilson, UC Berkeley/Cygnus Support;
    based on some ideas from Dain Samples of UC Berkeley.
    Further mangling by Bob Manson, Cygnus Support.
@@ -10,7 +11,7 @@ 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
 
 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 2, or (at your option) any later
+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
 version.
 
 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
@@ -19,9 +20,8 @@ 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
 for more details.
 
 You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING.  If not, write to the Free
-Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA.  */
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
 
 
 #define GCOV_LINKAGE
 
 
 #define GCOV_LINKAGE
@@ -37,22 +37,32 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "regs.h"
 #include "expr.h"
 #include "function.h"
 #include "regs.h"
 #include "expr.h"
 #include "function.h"
+#include "basic-block.h"
 #include "toplev.h"
 #include "toplev.h"
+#include "tm_p.h"
 #include "ggc.h"
 #include "ggc.h"
-#include "target.h"
 #include "coverage.h"
 #include "coverage.h"
-#include "libfuncs.h"
 #include "langhooks.h"
 #include "hashtab.h"
 #include "langhooks.h"
 #include "hashtab.h"
+#include "tree-iterator.h"
+#include "cgraph.h"
+#include "tree-pass.h"
+#include "diagnostic-core.h"
+#include "intl.h"
+#include "filenames.h"
+#include "target.h"
 
 
+#include "gcov-io.h"
 #include "gcov-io.c"
 
 #include "gcov-io.c"
 
-struct function_list
+struct GTY((chain_next ("%h.next"))) coverage_data
 {
 {
-  struct function_list *next;   /* next function */
+  struct coverage_data *next;   /* next function */
   unsigned ident;               /* function ident */
   unsigned ident;               /* function ident */
-  unsigned checksum;            /* function checksum */
-  unsigned n_ctrs[GCOV_COUNTERS];/* number of counters.  */
+  unsigned lineno_checksum;     /* function lineno checksum */
+  unsigned cfg_checksum;        /* function cfg checksum */
+  tree fn_decl;                         /* the function decl */
+  tree ctr_vars[GCOV_COUNTERS];         /* counter variables.  */
 };
 
 /* Counts information for a function.  */
 };
 
 /* Counts information for a function.  */
@@ -63,32 +73,33 @@ typedef struct counts_entry
   unsigned ctr;
 
   /* Store  */
   unsigned ctr;
 
   /* Store  */
-  unsigned checksum;
+  unsigned lineno_checksum;
+  unsigned cfg_checksum;
   gcov_type *counts;
   struct gcov_ctr_summary summary;
   gcov_type *counts;
   struct gcov_ctr_summary summary;
-
-  /* Workspace */
-  struct counts_entry *chain;
-
 } counts_entry_t;
 
 } counts_entry_t;
 
-static struct function_list *functions_head = 0;
-static struct function_list **functions_tail = &functions_head;
+static GTY(()) struct coverage_data *functions_head = 0;
+static struct coverage_data **functions_tail = &functions_head;
 static unsigned no_coverage = 0;
 
 /* Cumulative counter information for whole program.  */
 static unsigned prg_ctr_mask; /* Mask of counter types generated.  */
 static unsigned no_coverage = 0;
 
 /* Cumulative counter information for whole program.  */
 static unsigned prg_ctr_mask; /* Mask of counter types generated.  */
-static unsigned prg_n_ctrs[GCOV_COUNTERS]; /* Total counters allocated.  */
 
 /* Counter information for current function.  */
 static unsigned fn_ctr_mask; /* Mask of counters used.  */
 
 /* Counter information for current function.  */
 static unsigned fn_ctr_mask; /* Mask of counters used.  */
+static GTY(()) tree fn_v_ctrs[GCOV_COUNTERS];   /* counter variables.  */
 static unsigned fn_n_ctrs[GCOV_COUNTERS]; /* Counters allocated.  */
 static unsigned fn_b_ctrs[GCOV_COUNTERS]; /* Allocation base.  */
 
 static unsigned fn_n_ctrs[GCOV_COUNTERS]; /* Counters allocated.  */
 static unsigned fn_b_ctrs[GCOV_COUNTERS]; /* Allocation base.  */
 
-/* Name of the output file for coverage output file.  */
+/* Coverage info VAR_DECL and function info type nodes.  */
+static GTY(()) tree gcov_info_var;
+static GTY(()) tree gcov_fn_info_type;
+static GTY(()) tree gcov_fn_info_ptr_type;
+
+/* Name of the output file for coverage output file.  If this is NULL
+   we're not writing to the notes file.  */
 static char *bbg_file_name;
 static char *bbg_file_name;
-static unsigned bbg_file_opened;
-static int bbg_function_announced;
 
 /* Name of the count data file.  */
 static char *da_file_name;
 
 /* Name of the count data file.  */
 static char *da_file_name;
@@ -96,9 +107,6 @@ static char *da_file_name;
 /* Hash table of count data.  */
 static htab_t counts_hash = NULL;
 
 /* Hash table of count data.  */
 static htab_t counts_hash = NULL;
 
-/* The names of the counter tables.  */
-static GTY(()) rtx ctr_labels[GCOV_COUNTERS];
-
 /* The names of merge functions for counters.  */
 static const char *const ctr_merge_functions[GCOV_COUNTERS] = GCOV_MERGE_FUNCTIONS;
 static const char *const ctr_names[GCOV_COUNTERS] = GCOV_COUNTER_NAMES;
 /* The names of merge functions for counters.  */
 static const char *const ctr_merge_functions[GCOV_COUNTERS] = GCOV_MERGE_FUNCTIONS;
 static const char *const ctr_names[GCOV_COUNTERS] = GCOV_COUNTER_NAMES;
@@ -108,20 +116,36 @@ static hashval_t htab_counts_entry_hash (const void *);
 static int htab_counts_entry_eq (const void *, const void *);
 static void htab_counts_entry_del (void *);
 static void read_counts_file (void);
 static int htab_counts_entry_eq (const void *, const void *);
 static void htab_counts_entry_del (void *);
 static void read_counts_file (void);
-static unsigned compute_checksum (void);
-static unsigned checksum_string (unsigned, const char *);
-static tree build_fn_info_type (unsigned);
-static tree build_fn_info_value (const struct function_list *, tree);
-static tree build_ctr_info_type (void);
-static tree build_ctr_info_value (unsigned, tree);
-static tree build_gcov_info (void);
-static void create_coverage (void);
+static tree build_var (tree, tree, int);
+static void build_fn_info_type (tree, unsigned, tree);
+static void build_info_type (tree, tree);
+static tree build_fn_info (const struct coverage_data *, tree, tree);
+static tree build_info (tree, tree);
+static bool coverage_obj_init (void);
+static VEC(constructor_elt,gc) *coverage_obj_fn
+(VEC(constructor_elt,gc) *, tree, struct coverage_data const *);
+static void coverage_obj_finish (VEC(constructor_elt,gc) *);
+\f
+/* Return the type node for gcov_type.  */
 
 
+tree
+get_gcov_type (void)
+{
+  return lang_hooks.types.type_for_size (GCOV_TYPE_SIZE, false);
+}
+
+/* Return the type node for gcov_unsigned_t.  */
+
+static tree
+get_gcov_unsigned_t (void)
+{
+  return lang_hooks.types.type_for_size (32, true);
+}
 \f
 static hashval_t
 htab_counts_entry_hash (const void *of)
 {
 \f
 static hashval_t
 htab_counts_entry_hash (const void *of)
 {
-  const counts_entry_t *entry = of;
+  const counts_entry_t *const entry = (const counts_entry_t *) of;
 
   return entry->ident * GCOV_COUNTERS + entry->ctr;
 }
 
   return entry->ident * GCOV_COUNTERS + entry->ctr;
 }
@@ -129,8 +153,8 @@ htab_counts_entry_hash (const void *of)
 static int
 htab_counts_entry_eq (const void *of1, const void *of2)
 {
 static int
 htab_counts_entry_eq (const void *of1, const void *of2)
 {
-  const counts_entry_t *entry1 = of1;
-  const counts_entry_t *entry2 = of2;
+  const counts_entry_t *const entry1 = (const counts_entry_t *) of1;
+  const counts_entry_t *const entry2 = (const counts_entry_t *) of2;
 
   return entry1->ident == entry2->ident && entry1->ctr == entry2->ctr;
 }
 
   return entry1->ident == entry2->ident && entry1->ctr == entry2->ctr;
 }
@@ -138,7 +162,7 @@ htab_counts_entry_eq (const void *of1, const void *of2)
 static void
 htab_counts_entry_del (void *of)
 {
 static void
 htab_counts_entry_del (void *of)
 {
-  counts_entry_t *entry = of;
+  counts_entry_t *const entry = (counts_entry_t *) of;
 
   free (entry->counts);
   free (entry);
 
   free (entry->counts);
   free (entry);
@@ -150,18 +174,19 @@ static void
 read_counts_file (void)
 {
   gcov_unsigned_t fn_ident = 0;
 read_counts_file (void)
 {
   gcov_unsigned_t fn_ident = 0;
-  gcov_unsigned_t checksum = -1;
-  counts_entry_t *summaried = NULL;
-  unsigned seen_summary = 0;
+  struct gcov_summary summary;
+  unsigned new_summary = 1;
   gcov_unsigned_t tag;
   gcov_unsigned_t tag;
-  int error = 0;
+  int is_error = 0;
+  unsigned lineno_checksum = 0;
+  unsigned cfg_checksum = 0;
 
   if (!gcov_open (da_file_name, 1))
     return;
 
   if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC))
     {
 
   if (!gcov_open (da_file_name, 1))
     return;
 
   if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC))
     {
-      warning ("`%s' is not a gcov data file", da_file_name);
+      warning (0, "%qs is not a gcov data file", da_file_name);
       gcov_close ();
       return;
     }
       gcov_close ();
       return;
     }
@@ -172,15 +197,15 @@ read_counts_file (void)
       GCOV_UNSIGNED2STRING (v, tag);
       GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
 
       GCOV_UNSIGNED2STRING (v, tag);
       GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
 
-      warning ("`%s' is version `%.4s', expected version `%.4s'",
-              da_file_name, v, e);
+      warning (0, "%qs is version %q.*s, expected version %q.*s",
+              da_file_name, 4, v, 4, e);
       gcov_close ();
       return;
     }
 
       gcov_close ();
       return;
     }
 
-  /* Read and discard the stamp. */
+  /* Read and discard the stamp.  */
   gcov_read_unsigned ();
   gcov_read_unsigned ();
-  
+
   counts_hash = htab_create (10,
                             htab_counts_entry_hash, htab_counts_entry_eq,
                             htab_counts_entry_del);
   counts_hash = htab_create (10,
                             htab_counts_entry_hash, htab_counts_entry_eq,
                             htab_counts_entry_del);
@@ -193,41 +218,34 @@ read_counts_file (void)
       offset = gcov_position ();
       if (tag == GCOV_TAG_FUNCTION)
        {
       offset = gcov_position ();
       if (tag == GCOV_TAG_FUNCTION)
        {
-         fn_ident = gcov_read_unsigned ();
-         checksum = gcov_read_unsigned ();
-         if (seen_summary)
+         if (length)
            {
            {
-             /* We have already seen a summary, this means that this
-                new function begins a new set of program runs. We
-                must unlink the summaried chain.  */
-             counts_entry_t *entry, *chain;
-
-             for (entry = summaried; entry; entry = chain)
-               {
-                 chain = entry->chain;
-                 entry->chain = NULL;
-               }
-             summaried = NULL;
-             seen_summary = 0;
+             fn_ident = gcov_read_unsigned ();
+             lineno_checksum = gcov_read_unsigned ();
+             cfg_checksum = gcov_read_unsigned ();
            }
            }
+         else
+           fn_ident = lineno_checksum = cfg_checksum = 0;
+         new_summary = 1;
        }
       else if (tag == GCOV_TAG_PROGRAM_SUMMARY)
        {
        }
       else if (tag == GCOV_TAG_PROGRAM_SUMMARY)
        {
-         counts_entry_t *entry;
-         struct gcov_summary summary;
+         struct gcov_summary sum;
+         unsigned ix;
 
 
-         gcov_read_summary (&summary);
-         seen_summary = 1;
-         for (entry = summaried; entry; entry = entry->chain)
-           {
-             struct gcov_ctr_summary *csum = &summary.ctrs[entry->ctr];
+         if (new_summary)
+           memset (&summary, 0, sizeof (summary));
 
 
-             entry->summary.runs += csum->runs;
-             entry->summary.sum_all += csum->sum_all;
-             if (entry->summary.run_max < csum->run_max)
-               entry->summary.run_max = csum->run_max;
-             entry->summary.sum_max += csum->sum_max;
+         gcov_read_summary (&sum);
+         for (ix = 0; ix != GCOV_COUNTERS_SUMMABLE; ix++)
+           {
+             summary.ctrs[ix].runs += sum.ctrs[ix].runs;
+             summary.ctrs[ix].sum_all += sum.ctrs[ix].sum_all;
+             if (summary.ctrs[ix].run_max < sum.ctrs[ix].run_max)
+               summary.ctrs[ix].run_max = sum.ctrs[ix].run_max;
+             summary.ctrs[ix].sum_max += sum.ctrs[ix].sum_max;
            }
            }
+         new_summary = 0;
        }
       else if (GCOV_TAG_IS_COUNTER (tag) && fn_ident)
        {
        }
       else if (GCOV_TAG_IS_COUNTER (tag) && fn_ident)
        {
@@ -243,50 +261,58 @@ read_counts_file (void)
          entry = *slot;
          if (!entry)
            {
          entry = *slot;
          if (!entry)
            {
-             *slot = entry = xcalloc (1, sizeof (counts_entry_t));
-             entry->ident = elt.ident;
+             *slot = entry = XCNEW (counts_entry_t);
+             entry->ident = fn_ident;
              entry->ctr = elt.ctr;
              entry->ctr = elt.ctr;
-             entry->checksum = checksum;
+             entry->lineno_checksum = lineno_checksum;
+             entry->cfg_checksum = cfg_checksum;
+             entry->summary = summary.ctrs[elt.ctr];
              entry->summary.num = n_counts;
              entry->summary.num = n_counts;
-             entry->counts = xcalloc (n_counts, sizeof (gcov_type));
+             entry->counts = XCNEWVEC (gcov_type, n_counts);
+           }
+         else if (entry->lineno_checksum != lineno_checksum
+                  || entry->cfg_checksum != cfg_checksum)
+           {
+             error ("Profile data for function %u is corrupted", fn_ident);
+             error ("checksum is (%x,%x) instead of (%x,%x)",
+                    entry->lineno_checksum, entry->cfg_checksum,
+                    lineno_checksum, cfg_checksum);
+             htab_delete (counts_hash);
+             break;
            }
            }
-         else if (entry->checksum != checksum
-                  || entry->summary.num != n_counts)
+         else if (entry->summary.num != n_counts)
            {
            {
-             warning ("coverage mismatch for function %u", fn_ident);
+             error ("Profile data for function %u is corrupted", fn_ident);
+             error ("number of counters is %d instead of %d", entry->summary.num, n_counts);
              htab_delete (counts_hash);
              break;
            }
          else if (elt.ctr >= GCOV_COUNTERS_SUMMABLE)
            {
              htab_delete (counts_hash);
              break;
            }
          else if (elt.ctr >= GCOV_COUNTERS_SUMMABLE)
            {
-             warning ("cannot merge separate %s counters for function %u",
-                      ctr_names[elt.ctr], fn_ident);
+             error ("cannot merge separate %s counters for function %u",
+                    ctr_names[elt.ctr], fn_ident);
              goto skip_merge;
            }
              goto skip_merge;
            }
-
-         if (elt.ctr < GCOV_COUNTERS_SUMMABLE
-             /* This should always be true for a just allocated entry,
-                and always false for an existing one. Check this way, in
-                case the gcov file is corrupt.  */
-             && (!entry->chain || summaried != entry))
+         else
            {
            {
-             entry->chain = summaried;
-             summaried = entry;
+             entry->summary.runs += summary.ctrs[elt.ctr].runs;
+             entry->summary.sum_all += summary.ctrs[elt.ctr].sum_all;
+             if (entry->summary.run_max < summary.ctrs[elt.ctr].run_max)
+               entry->summary.run_max = summary.ctrs[elt.ctr].run_max;
+             entry->summary.sum_max += summary.ctrs[elt.ctr].sum_max;
            }
          for (ix = 0; ix != n_counts; ix++)
            entry->counts[ix] += gcov_read_counter ();
        skip_merge:;
        }
       gcov_sync (offset, length);
            }
          for (ix = 0; ix != n_counts; ix++)
            entry->counts[ix] += gcov_read_counter ();
        skip_merge:;
        }
       gcov_sync (offset, length);
-      if ((error = gcov_is_error ()))
-       break;
-    }
-
-  if (!gcov_is_eof ())
-    {
-      warning (error < 0 ? "`%s' has overflowed" : "`%s' is corrupted",
-              da_file_name);
-      htab_delete (counts_hash);
+      if ((is_error = gcov_is_error ()))
+       {
+         error (is_error < 0 ? "%qs has overflowed" : "%qs is corrupted",
+                da_file_name);
+         htab_delete (counts_hash);
+         break;
+       }
     }
 
   gcov_close ();
     }
 
   gcov_close ();
@@ -296,6 +322,7 @@ read_counts_file (void)
 
 gcov_type *
 get_coverage_counts (unsigned counter, unsigned expected,
 
 gcov_type *
 get_coverage_counts (unsigned counter, unsigned expected,
+                     unsigned cfg_checksum, unsigned lineno_checksum,
                     const struct gcov_ctr_summary **summary)
 {
   counts_entry_t *entry, elt;
                     const struct gcov_ctr_summary **summary)
 {
   counts_entry_t *entry, elt;
@@ -306,28 +333,59 @@ get_coverage_counts (unsigned counter, unsigned expected,
       static int warned = 0;
 
       if (!warned++)
       static int warned = 0;
 
       if (!warned++)
-       warning ("file %s not found, execution counts assumed to be zero",
-                da_file_name);
+       inform (input_location, (flag_guess_branch_prob
+                ? "file %s not found, execution counts estimated"
+                : "file %s not found, execution counts assumed to be zero"),
+               da_file_name);
       return NULL;
     }
 
   elt.ident = current_function_funcdef_no + 1;
   elt.ctr = counter;
       return NULL;
     }
 
   elt.ident = current_function_funcdef_no + 1;
   elt.ctr = counter;
-  entry = htab_find (counts_hash, &elt);
-  if (!entry)
+  entry = (counts_entry_t *) htab_find (counts_hash, &elt);
+  if (!entry || !entry->summary.num)
+    /* The function was not emitted, or is weak and not chosen in the
+       final executable.  Silently fail, because there's nothing we
+       can do about it.  */
+    return NULL;
+  
+  if (entry->cfg_checksum != cfg_checksum
+      || entry->summary.num != expected)
     {
     {
-      warning ("no coverage for function '%s' found.", IDENTIFIER_POINTER
-              (DECL_ASSEMBLER_NAME (current_function_decl)));
-      return 0;
-    }
+      static int warned = 0;
+      bool warning_printed = false;
+      tree id = DECL_ASSEMBLER_NAME (current_function_decl);
+
+      warning_printed =
+       warning_at (input_location, OPT_Wcoverage_mismatch,
+                   "the control flow of function %qE does not match "
+                   "its profile data (counter %qs)", id, ctr_names[counter]);
+      if (warning_printed)
+       {
+        inform (input_location, "use -Wno-error=coverage-mismatch to tolerate "
+                "the mismatch but performance may drop if the function is hot");
+         
+         if (!seen_error ()
+             && !warned++)
+           {
+             inform (input_location, "coverage mismatch ignored");
+             inform (input_location, flag_guess_branch_prob
+                     ? G_("execution counts estimated")
+                     : G_("execution counts assumed to be zero"));
+             if (!flag_guess_branch_prob)
+               inform (input_location,
+                       "this can result in poorly optimized code");
+           }
+       }
 
 
-  if (expected != entry->summary.num
-      || compute_checksum () != entry->checksum)
-    {
-      warning ("coverage mismatch for `%s'", IDENTIFIER_POINTER
-              (DECL_ASSEMBLER_NAME (current_function_decl)));
       return NULL;
     }
       return NULL;
     }
+  else if (entry->lineno_checksum != lineno_checksum)
+    {
+      warning (0, "source locations for function %qE have changed,"
+              " the profile data may be out of date",
+              DECL_ASSEMBLER_NAME (current_function_decl));
+    }
 
   if (summary)
     *summary = &entry->summary;
 
   if (summary)
     *summary = &entry->summary;
@@ -347,120 +405,188 @@ coverage_counter_alloc (unsigned counter, unsigned num)
   if (!num)
     return 1;
 
   if (!num)
     return 1;
 
-  if (!ctr_labels[counter])
+  if (!fn_v_ctrs[counter])
     {
     {
-      /* Generate and save a copy of this so it can be shared.  */
-      char buf[20];
+      tree array_type = build_array_type (get_gcov_type (), NULL_TREE);
 
 
-      ASM_GENERATE_INTERNAL_LABEL (buf, "LPBX", counter + 1);
-      ctr_labels[counter] = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf));
+      fn_v_ctrs[counter]
+       = build_var (current_function_decl, array_type, counter);
     }
     }
+
   fn_b_ctrs[counter] = fn_n_ctrs[counter];
   fn_n_ctrs[counter] += num;
   fn_b_ctrs[counter] = fn_n_ctrs[counter];
   fn_n_ctrs[counter] += num;
+  
   fn_ctr_mask |= 1 << counter;
   return 1;
 }
 
   fn_ctr_mask |= 1 << counter;
   return 1;
 }
 
-/* Generate a MEM rtl to access COUNTER NO.  */
+/* Generate a tree to access COUNTER NO.  */
 
 
-rtx
-coverage_counter_ref (unsigned counter, unsigned no)
+tree
+tree_coverage_counter_ref (unsigned counter, unsigned no)
 {
 {
-  unsigned gcov_size = tree_low_cst (TYPE_SIZE (GCOV_TYPE_NODE), 1);
-  enum machine_mode mode = mode_for_size (gcov_size, MODE_INT, 0);
-  rtx ref;
-
-  if (no >= fn_n_ctrs[counter] - fn_b_ctrs[counter])
-    abort ();
-  no += prg_n_ctrs[counter] + fn_b_ctrs[counter];
-  ref = plus_constant (ctr_labels[counter], gcov_size / BITS_PER_UNIT * no);
-  ref = gen_rtx_MEM (mode, ref);
-  set_mem_alias_set (ref, new_alias_set ());
-
-  return ref;
+  tree gcov_type_node = get_gcov_type ();
+
+  gcc_assert (no < fn_n_ctrs[counter] - fn_b_ctrs[counter]);
+
+  no += fn_b_ctrs[counter];
+  
+  /* "no" here is an array index, scaled to bytes later.  */
+  return build4 (ARRAY_REF, gcov_type_node, fn_v_ctrs[counter],
+                build_int_cst (integer_type_node, no), NULL, NULL);
+}
+
+/* Generate a tree to access the address of COUNTER NO.  */
+
+tree
+tree_coverage_counter_addr (unsigned counter, unsigned no)
+{
+  tree gcov_type_node = get_gcov_type ();
+
+  gcc_assert (no < fn_n_ctrs[counter] - fn_b_ctrs[counter]);
+  no += fn_b_ctrs[counter];
+
+  /* "no" here is an array index, scaled to bytes later.  */
+  return build_fold_addr_expr (build4 (ARRAY_REF, gcov_type_node,
+                                      fn_v_ctrs[counter],
+                                      build_int_cst (integer_type_node, no),
+                                      NULL, NULL));
 }
 \f
 }
 \f
+
 /* Generate a checksum for a string.  CHKSUM is the current
    checksum.  */
 
 static unsigned
 /* Generate a checksum for a string.  CHKSUM is the current
    checksum.  */
 
 static unsigned
-checksum_string (unsigned chksum, const char *string)
+coverage_checksum_string (unsigned chksum, const char *string)
 {
 {
-  do
+  int i;
+  char *dup = NULL;
+
+  /* Look for everything that looks if it were produced by
+     get_file_function_name and zero out the second part
+     that may result from flag_random_seed.  This is not critical
+     as the checksums are used only for sanity checking.  */
+  for (i = 0; string[i]; i++)
     {
     {
-      unsigned value = *string << 24;
-      unsigned ix;
-
-      for (ix = 8; ix--; value <<= 1)
+      int offset = 0;
+      if (!strncmp (string + i, "_GLOBAL__N_", 11))
+      offset = 11;
+      if (!strncmp (string + i, "_GLOBAL__", 9))
+      offset = 9;
+
+      /* C++ namespaces do have scheme:
+         _GLOBAL__N_<filename>_<wrongmagicnumber>_<magicnumber>functionname
+       since filename might contain extra underscores there seems
+       to be no better chance then walk all possible offsets looking
+       for magicnumber.  */
+      if (offset)
        {
        {
-         unsigned feedback;
-
-         feedback = (value ^ chksum) & 0x80000000 ? 0x04c11db7 : 0;
-         chksum <<= 1;
-         chksum ^= feedback;
+         for (i = i + offset; string[i]; i++)
+           if (string[i]=='_')
+             {
+               int y;
+
+               for (y = 1; y < 9; y++)
+                 if (!(string[i + y] >= '0' && string[i + y] <= '9')
+                     && !(string[i + y] >= 'A' && string[i + y] <= 'F'))
+                   break;
+               if (y != 9 || string[i + 9] != '_')
+                 continue;
+               for (y = 10; y < 18; y++)
+                 if (!(string[i + y] >= '0' && string[i + y] <= '9')
+                     && !(string[i + y] >= 'A' && string[i + y] <= 'F'))
+                   break;
+               if (y != 18)
+                 continue;
+               if (!dup)
+                 string = dup = xstrdup (string);
+               for (y = 10; y < 18; y++)
+                 dup[i + y] = '0';
+             }
+         break;
        }
     }
        }
     }
-  while (*string++);
+
+  chksum = crc32_string (chksum, string);
+  free (dup);
 
   return chksum;
 }
 
 /* Compute checksum for the current function.  We generate a CRC32.  */
 
 
   return chksum;
 }
 
 /* Compute checksum for the current function.  We generate a CRC32.  */
 
-static unsigned
-compute_checksum (void)
+unsigned
+coverage_compute_lineno_checksum (void)
 {
 {
-  unsigned chksum = DECL_SOURCE_LINE (current_function_decl);
+  expanded_location xloc
+    = expand_location (DECL_SOURCE_LOCATION (current_function_decl));
+  unsigned chksum = xloc.line;
 
 
-  chksum = checksum_string (chksum, DECL_SOURCE_FILE (current_function_decl));
-  chksum = checksum_string
+  chksum = coverage_checksum_string (chksum, xloc.file);
+  chksum = coverage_checksum_string
     (chksum, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl)));
 
   return chksum;
 }
     (chksum, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl)));
 
   return chksum;
 }
+
+/* Compute cfg checksum for the current function.
+   The checksum is calculated carefully so that
+   source code changes that doesn't affect the control flow graph
+   won't change the checksum.
+   This is to make the profile data useable across source code change.
+   The downside of this is that the compiler may use potentially
+   wrong profile data - that the source code change has non-trivial impact
+   on the validity of profile data (e.g. the reversed condition)
+   but the compiler won't detect the change and use the wrong profile data.  */
+
+unsigned
+coverage_compute_cfg_checksum (void)
+{
+  basic_block bb;
+  unsigned chksum = n_basic_blocks;
+
+  FOR_EACH_BB (bb)
+    {
+      edge e;
+      edge_iterator ei;
+      chksum = crc32_byte (chksum, bb->index);
+      FOR_EACH_EDGE (e, ei, bb->succs)
+        {
+          chksum = crc32_byte (chksum, e->dest->index);
+        }
+    }
+
+  return chksum;
+}
 \f
 /* Begin output to the graph file for the current function.
 \f
 /* Begin output to the graph file for the current function.
-   Opens the output file, if not already done. Writes the
-   function header, if not already done. Returns nonzero if data
-   should be output.  */
+   Writes the function header. Returns nonzero if data should be output.  */
 
 int
 
 int
-coverage_begin_output (void)
+coverage_begin_function (unsigned lineno_checksum, unsigned cfg_checksum)
 {
 {
-  if (no_coverage)
+  expanded_location xloc;
+  unsigned long offset;
+
+  /* We don't need to output .gcno file unless we're under -ftest-coverage
+     (e.g. -fprofile-arcs/generate/use don't need .gcno to work). */
+  if (no_coverage || !bbg_file_name)
     return 0;
 
     return 0;
 
-  if (!bbg_function_announced)
-    {
-      const char *file = DECL_SOURCE_FILE (current_function_decl);
-      unsigned line = DECL_SOURCE_LINE (current_function_decl);
-      unsigned long offset;
+  xloc = expand_location (DECL_SOURCE_LOCATION (current_function_decl));
 
 
-      if (!bbg_file_opened)
-       {
-         if (!gcov_open (bbg_file_name, -1))
-           error ("cannot open %s", bbg_file_name);
-         else
-           {
-             gcov_write_unsigned (GCOV_NOTE_MAGIC);
-             gcov_write_unsigned (GCOV_VERSION);
-             gcov_write_unsigned (local_tick);
-           }
-         bbg_file_opened = 1;
-       }
+  /* Announce function */
+  offset = gcov_write_tag (GCOV_TAG_FUNCTION);
+  gcov_write_unsigned (current_function_funcdef_no + 1);
+  gcov_write_unsigned (lineno_checksum);
+  gcov_write_unsigned (cfg_checksum);
+  gcov_write_string (IDENTIFIER_POINTER
+                    (DECL_ASSEMBLER_NAME (current_function_decl)));
+  gcov_write_string (xloc.file);
+  gcov_write_unsigned (xloc.line);
+  gcov_write_length (offset);
 
 
-      /* Announce function */
-      offset = gcov_write_tag (GCOV_TAG_FUNCTION);
-      gcov_write_unsigned (current_function_funcdef_no + 1);
-      gcov_write_unsigned (compute_checksum ());
-      gcov_write_string (IDENTIFIER_POINTER
-                        (DECL_ASSEMBLER_NAME (current_function_decl)));
-      gcov_write_string (file);
-      gcov_write_unsigned (line);
-      gcov_write_length (offset);
-
-      bbg_function_announced = 1;
-    }
   return !gcov_is_error ();
 }
 
   return !gcov_is_error ();
 }
 
@@ -468,417 +594,484 @@ coverage_begin_output (void)
    error has occurred.  Save function coverage counts.  */
 
 void
    error has occurred.  Save function coverage counts.  */
 
 void
-coverage_end_function (void)
+coverage_end_function (unsigned lineno_checksum, unsigned cfg_checksum)
 {
   unsigned i;
 
 {
   unsigned i;
 
-  if (bbg_file_opened > 1 && gcov_is_error ())
+  if (bbg_file_name && gcov_is_error ())
     {
     {
-      warning ("error writing `%s'", bbg_file_name);
-      bbg_file_opened = -1;
+      warning (0, "error writing %qs", bbg_file_name);
+      unlink (bbg_file_name);
+      bbg_file_name = NULL;
     }
 
   if (fn_ctr_mask)
     {
     }
 
   if (fn_ctr_mask)
     {
-      struct function_list *item;
+      struct coverage_data *item = 0;
 
 
-      item = xmalloc (sizeof (struct function_list));
-
-      *functions_tail = item;
-      functions_tail = &item->next;
+      /* If the function is extern (i.e. extern inline), then we won't
+        be outputting it, so don't chain it onto the function
+        list.  */
+      if (!DECL_EXTERNAL (current_function_decl))
+       {
+         item = ggc_alloc_coverage_data ();
+         
+         item->ident = current_function_funcdef_no + 1;
+         item->lineno_checksum = lineno_checksum;
+         item->cfg_checksum = cfg_checksum;
+
+         item->fn_decl = current_function_decl;
+         item->next = 0;
+         *functions_tail = item;
+         functions_tail = &item->next;
+       }
 
 
-      item->next = 0;
-      item->ident = current_function_funcdef_no + 1;
-      item->checksum = compute_checksum ();
       for (i = 0; i != GCOV_COUNTERS; i++)
        {
       for (i = 0; i != GCOV_COUNTERS; i++)
        {
-         item->n_ctrs[i] = fn_n_ctrs[i];
-         prg_n_ctrs[i] += fn_n_ctrs[i];
-         fn_n_ctrs[i] = fn_b_ctrs[i] = 0;
+         tree var = fn_v_ctrs[i];
+
+         if (item)
+           item->ctr_vars[i] = var;
+         if (var)
+           {
+             tree array_type = build_index_type (size_int (fn_n_ctrs[i] - 1));
+             array_type = build_array_type (get_gcov_type (), array_type);
+             TREE_TYPE (var) = array_type;
+             DECL_SIZE (var) = TYPE_SIZE (array_type);
+             DECL_SIZE_UNIT (var) = TYPE_SIZE_UNIT (array_type);
+             varpool_finalize_decl (var);
+           }
+         
+         fn_b_ctrs[i] = fn_n_ctrs[i] = 0;
+         fn_v_ctrs[i] = NULL_TREE;
        }
       prg_ctr_mask |= fn_ctr_mask;
       fn_ctr_mask = 0;
     }
        }
       prg_ctr_mask |= fn_ctr_mask;
       fn_ctr_mask = 0;
     }
-  bbg_function_announced = 0;
 }
 
 }
 
-/* Creates the gcov_fn_info RECORD_TYPE.  */
+/* Build a coverage variable of TYPE for function FN_DECL.  If COUNTER
+   >= 0 it is a counter array, otherwise it is the function structure.  */
 
 static tree
 
 static tree
-build_fn_info_type (unsigned int counters)
+build_var (tree fn_decl, tree type, int counter)
+{
+  tree var = build_decl (BUILTINS_LOCATION, VAR_DECL, NULL_TREE, type);
+  const char *fn_name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (fn_decl));
+  char *buf;
+  size_t fn_name_len, len;
+
+  fn_name = targetm.strip_name_encoding (fn_name);
+  fn_name_len = strlen (fn_name);
+  buf = XALLOCAVEC (char, fn_name_len + 8 + sizeof (int) * 3);
+
+  if (counter < 0)
+    strcpy (buf, "__gcov__");
+  else
+    sprintf (buf, "__gcov%u_", counter);
+  len = strlen (buf);
+#ifndef NO_DOT_IN_LABEL
+  buf[len - 1] = '.';
+#elif !defined NO_DOLLAR_IN_LABEL
+  buf[len - 1] = '$';
+#endif
+  memcpy (buf + len, fn_name, fn_name_len + 1);
+  DECL_NAME (var) = get_identifier (buf);
+  TREE_STATIC (var) = 1;
+  TREE_ADDRESSABLE (var) = 1;
+  DECL_ALIGN (var) = TYPE_ALIGN (type);
+
+  return var;
+}
+
+/* Creates the gcov_fn_info RECORD_TYPE.  */
+
+static void
+build_fn_info_type (tree type, unsigned counters, tree gcov_info_type)
 {
 {
-  tree type = (*lang_hooks.types.make_type) (RECORD_TYPE);
+  tree ctr_info = lang_hooks.types.make_type (RECORD_TYPE);
   tree field, fields;
   tree array_type;
 
   tree field, fields;
   tree array_type;
 
+  gcc_assert (counters);
+  
+  /* ctr_info::num */
+  field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+                     get_gcov_unsigned_t ());
+  fields = field;
+  
+  /* ctr_info::values */
+  field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+                     build_pointer_type (get_gcov_type ()));
+  DECL_CHAIN (field) = fields;
+  fields = field;
+  
+  finish_builtin_struct (ctr_info, "__gcov_ctr_info", fields, NULL_TREE);
+
+  /* key */
+  field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+                     build_pointer_type (build_qualified_type
+                                         (gcov_info_type, TYPE_QUAL_CONST)));
+  fields = field;
+  
   /* ident */
   /* ident */
-  fields = build_decl (FIELD_DECL, NULL_TREE, unsigned_intSI_type_node);
+  field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+                     get_gcov_unsigned_t ());
+  DECL_CHAIN (field) = fields;
+  fields = field;
+  
+  /* lineno_checksum */
+  field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+                     get_gcov_unsigned_t ());
+  DECL_CHAIN (field) = fields;
+  fields = field;
 
 
-  /* checksum */
-  field = build_decl (FIELD_DECL, NULL_TREE, unsigned_intSI_type_node);
-  TREE_CHAIN (field) = fields;
+  /* cfg checksum */
+  field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+                     get_gcov_unsigned_t ());
+  DECL_CHAIN (field) = fields;
   fields = field;
 
   fields = field;
 
-  array_type = build_index_type (build_int_2 (counters - 1, 0));
-  array_type = build_array_type (unsigned_type_node, array_type);
+  array_type = build_index_type (size_int (counters - 1));
+  array_type = build_array_type (ctr_info, array_type);
 
   /* counters */
 
   /* counters */
-  field = build_decl (FIELD_DECL, NULL_TREE, array_type);
-  TREE_CHAIN (field) = fields;
+  field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE, array_type);
+  DECL_CHAIN (field) = fields;
   fields = field;
 
   finish_builtin_struct (type, "__gcov_fn_info", fields, NULL_TREE);
   fields = field;
 
   finish_builtin_struct (type, "__gcov_fn_info", fields, NULL_TREE);
-
-  return type;
 }
 
 }
 
-/* Creates a CONSTRUCTOR for a gcov_fn_info. FUNCTION is
-   the function being processed and TYPE is the gcov_fn_info
-   RECORD_TYPE.  */
+/* Returns a CONSTRUCTOR for a gcov_fn_info.  DATA is
+   the coverage data for the function and TYPE is the gcov_fn_info
+   RECORD_TYPE.  KEY is the object file key.  */
 
 static tree
 
 static tree
-build_fn_info_value (const struct function_list *function, tree type)
+build_fn_info (const struct coverage_data *data, tree type, tree key)
 {
 {
-  tree value = NULL_TREE;
   tree fields = TYPE_FIELDS (type);
   tree fields = TYPE_FIELDS (type);
+  tree ctr_type;
   unsigned ix;
   unsigned ix;
-  tree array_value = NULL_TREE;
+  VEC(constructor_elt,gc) *v1 = NULL;
+  VEC(constructor_elt,gc) *v2 = NULL;
 
 
+  /* key */
+  CONSTRUCTOR_APPEND_ELT (v1, fields,
+                         build1 (ADDR_EXPR, TREE_TYPE (fields), key));
+  fields = DECL_CHAIN (fields);
+  
   /* ident */
   /* ident */
-  value = tree_cons (fields,
-                    convert (unsigned_intSI_type_node,
-                             build_int_2 (function->ident, 0)),
-                    value);
-  fields = TREE_CHAIN (fields);
-
-  /* checksum */
-  value = tree_cons (fields,
-                    convert (unsigned_intSI_type_node,
-                             build_int_2 (function->checksum, 0)),
-                    value);
-  fields = TREE_CHAIN (fields);
+  CONSTRUCTOR_APPEND_ELT (v1, fields,
+                         build_int_cstu (get_gcov_unsigned_t (),
+                                         data->ident));
+  fields = DECL_CHAIN (fields);
+
+  /* lineno_checksum */
+  CONSTRUCTOR_APPEND_ELT (v1, fields,
+                         build_int_cstu (get_gcov_unsigned_t (),
+                                         data->lineno_checksum));
+  fields = DECL_CHAIN (fields);
+
+  /* cfg_checksum */
+  CONSTRUCTOR_APPEND_ELT (v1, fields,
+                         build_int_cstu (get_gcov_unsigned_t (),
+                                         data->cfg_checksum));
+  fields = DECL_CHAIN (fields);
 
   /* counters */
 
   /* counters */
+  ctr_type = TREE_TYPE (TREE_TYPE (fields));
   for (ix = 0; ix != GCOV_COUNTERS; ix++)
     if (prg_ctr_mask & (1 << ix))
       {
   for (ix = 0; ix != GCOV_COUNTERS; ix++)
     if (prg_ctr_mask & (1 << ix))
       {
-       tree counters = convert (unsigned_type_node,
-                                build_int_2 (function->n_ctrs[ix], 0));
-
-       array_value = tree_cons (NULL_TREE, counters, array_value);
+       VEC(constructor_elt,gc) *ctr = NULL;
+       tree var = data->ctr_vars[ix];
+       unsigned count = 0;
+
+       if (var)
+         count
+           = tree_low_cst (TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (var))), 0)
+           + 1;
+
+       CONSTRUCTOR_APPEND_ELT (ctr, TYPE_FIELDS (ctr_type),
+                               build_int_cstu (get_gcov_unsigned_t (),
+                                               count));
+
+       if (var)
+         CONSTRUCTOR_APPEND_ELT (ctr, DECL_CHAIN (TYPE_FIELDS (ctr_type)),
+                                 build_fold_addr_expr (var));
+       
+       CONSTRUCTOR_APPEND_ELT (v2, NULL, build_constructor (ctr_type, ctr));
       }
       }
+  
+  CONSTRUCTOR_APPEND_ELT (v1, fields,
+                         build_constructor (TREE_TYPE (fields), v2));
 
 
-  array_value = build_constructor (TREE_TYPE (fields), nreverse (array_value));
-  value = tree_cons (fields, array_value, value);
-
-  value = build_constructor (type, nreverse (value));
-
-  return value;
+  return build_constructor (type, v1);
 }
 
 }
 
-/* Creates the gcov_ctr_info RECORD_TYPE.  */
+/* Create gcov_info struct.  TYPE is the incomplete RECORD_TYPE to be
+   completed, and FN_INFO_PTR_TYPE is a pointer to the function info type.  */
 
 
-static tree
-build_ctr_info_type (void)
+static void
+build_info_type (tree type, tree fn_info_ptr_type)
 {
 {
-  tree type = (*lang_hooks.types.make_type) (RECORD_TYPE);
   tree field, fields = NULL_TREE;
   tree field, fields = NULL_TREE;
-  tree gcov_ptr_type = build_pointer_type (GCOV_TYPE_NODE);
-  tree gcov_merge_fn_type;
+  tree merge_fn_type;
 
 
-  /* counters */
-  field = build_decl (FIELD_DECL, NULL_TREE, unsigned_intSI_type_node);
-  TREE_CHAIN (field) = fields;
+  /* Version ident */
+  field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+                     get_gcov_unsigned_t ());
+  DECL_CHAIN (field) = fields;
   fields = field;
 
   fields = field;
 
-  /* values */
-  field = build_decl (FIELD_DECL, NULL_TREE, gcov_ptr_type);
-  TREE_CHAIN (field) = fields;
+  /* next pointer */
+  field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+                     build_pointer_type (build_qualified_type
+                                         (type, TYPE_QUAL_CONST)));
+  DECL_CHAIN (field) = fields;
   fields = field;
 
   fields = field;
 
-  /* merge */
-  gcov_merge_fn_type =
-    build_function_type_list (void_type_node,
-                             gcov_ptr_type, unsigned_type_node,
-                             NULL_TREE);
-  field = build_decl (FIELD_DECL, NULL_TREE,
-                     build_pointer_type (gcov_merge_fn_type));
-  TREE_CHAIN (field) = fields;
+  /* stamp */
+  field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+                     get_gcov_unsigned_t ());
+  DECL_CHAIN (field) = fields;
   fields = field;
 
   fields = field;
 
-  finish_builtin_struct (type, "__gcov_ctr_info", fields, NULL_TREE);
-
-  return type;
-}
-
-/* Creates a CONSTRUCTOR for a gcov_ctr_info. COUNTER is
-   the counter being processed and TYPE is the gcov_ctr_info
-   RECORD_TYPE.  */
-
-static tree
-build_ctr_info_value (unsigned int counter, tree type)
-{
-  tree value = NULL_TREE;
-  tree fields = TYPE_FIELDS (type);
-  tree fn;
-
-  /* counters */
-  value = tree_cons (fields,
-                    convert (unsigned_intSI_type_node,
-                             build_int_2 (prg_n_ctrs[counter], 0)),
-                    value);
-  fields = TREE_CHAIN (fields);
-
-  if (prg_n_ctrs[counter])
-    {
-      tree array_type, array;
-
-      array_type = build_index_type (build_int_2 (prg_n_ctrs[counter] - 1, 0));
-      array_type = build_array_type (TREE_TYPE (TREE_TYPE (fields)),
-                                    array_type);
+  /* Filename */
+  field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+                     build_pointer_type (build_qualified_type
+                                         (char_type_node, TYPE_QUAL_CONST)));
+  DECL_CHAIN (field) = fields;
+  fields = field;
 
 
-      array = build (VAR_DECL, array_type, NULL_TREE, NULL_TREE);
-      TREE_STATIC (array) = 1;
-      DECL_NAME (array) = get_identifier (XSTR (ctr_labels[counter], 0));
-      assemble_variable (array, 0, 0, 0);
+  /* merge fn array */
+  merge_fn_type
+    = build_function_type_list (void_type_node,
+                               build_pointer_type (get_gcov_type ()),
+                               get_gcov_unsigned_t (), NULL_TREE);
+  merge_fn_type
+    = build_array_type (build_pointer_type (merge_fn_type),
+                       build_index_type (size_int (GCOV_COUNTERS - 1)));
+  field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+                     merge_fn_type);
+  DECL_CHAIN (field) = fields;
+  fields = field;
+  
+  /* n_functions */
+  field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+                     get_gcov_unsigned_t ());
+  DECL_CHAIN (field) = fields;
+  fields = field;
+  
+  /* function_info pointer pointer */
+  fn_info_ptr_type = build_pointer_type
+    (build_qualified_type (fn_info_ptr_type, TYPE_QUAL_CONST));
+  field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
+                     fn_info_ptr_type);
+  DECL_CHAIN (field) = fields;
+  fields = field;
 
 
-      value = tree_cons (fields,
-                        build1 (ADDR_EXPR, TREE_TYPE (fields), array),
-                        value);
-    }
-  else
-    value = tree_cons (fields, null_pointer_node, value);
-  fields = TREE_CHAIN (fields);
-
-  fn = build_decl (FUNCTION_DECL,
-                  get_identifier (ctr_merge_functions[counter]),
-                  TREE_TYPE (TREE_TYPE (fields)));
-  DECL_EXTERNAL (fn) = 1;
-  TREE_PUBLIC (fn) = 1;
-  DECL_ARTIFICIAL (fn) = 1;
-  TREE_NOTHROW (fn) = 1;
-  value = tree_cons (fields,
-                    build1 (ADDR_EXPR, TREE_TYPE (fields), fn),
-                    value);
-
-  value = build_constructor (type, nreverse (value));
-
-  return value;
+  finish_builtin_struct (type, "__gcov_info", fields, NULL_TREE);
 }
 
 }
 
-/* Creates the gcov_info RECORD_TYPE and initializer for it. Returns a
-   CONSTRUCTOR.  */
+/* Returns a CONSTRUCTOR for the gcov_info object.  INFO_TYPE is the
+   gcov_info structure type, FN_ARY is the array of pointers to
+   function info objects.  */
 
 static tree
 
 static tree
-build_gcov_info (void)
+build_info (tree info_type, tree fn_ary)
 {
 {
-  unsigned n_ctr_types, ix;
-  tree type, const_type;
-  tree fn_info_type, fn_info_value = NULL_TREE;
-  tree fn_info_ptr_type;
-  tree ctr_info_type, ctr_info_ary_type, ctr_info_value = NULL_TREE;
-  tree field, fields = NULL_TREE;
-  tree value = NULL_TREE;
+  tree info_fields = TYPE_FIELDS (info_type);
+  tree merge_fn_type, n_funcs;
+  unsigned ix;
   tree filename_string;
   tree filename_string;
-  char *filename;
-  int filename_len;
-  unsigned n_fns;
-  const struct function_list *fn;
-  tree string_type;
-
-  /* Count the number of active counters.  */
-  for (n_ctr_types = 0, ix = 0; ix != GCOV_COUNTERS; ix++)
-    if (prg_ctr_mask & (1 << ix))
-      n_ctr_types++;
-
-  type = (*lang_hooks.types.make_type) (RECORD_TYPE);
-  const_type = build_qualified_type (type, TYPE_QUAL_CONST);
+  int da_file_name_len;
+  VEC(constructor_elt,gc) *v1 = NULL;
+  VEC(constructor_elt,gc) *v2 = NULL;
 
   /* Version ident */
 
   /* Version ident */
-  field = build_decl (FIELD_DECL, NULL_TREE, unsigned_intSI_type_node);
-  TREE_CHAIN (field) = fields;
-  fields = field;
-  value = tree_cons (field, convert (unsigned_intSI_type_node,
-                                    build_int_2 (GCOV_VERSION, 0)),
-                    value);
+  CONSTRUCTOR_APPEND_ELT (v1, info_fields,
+                         build_int_cstu (TREE_TYPE (info_fields),
+                                         GCOV_VERSION));
+  info_fields = DECL_CHAIN (info_fields);
 
   /* next -- NULL */
 
   /* next -- NULL */
-  field = build_decl (FIELD_DECL, NULL_TREE, build_pointer_type (const_type));
-  TREE_CHAIN (field) = fields;
-  fields = field;
-  value = tree_cons (field, null_pointer_node, value);
-
+  CONSTRUCTOR_APPEND_ELT (v1, info_fields, null_pointer_node);
+  info_fields = DECL_CHAIN (info_fields);
+  
   /* stamp */
   /* stamp */
-  field = build_decl (FIELD_DECL, NULL_TREE, unsigned_intSI_type_node);
-  TREE_CHAIN (field) = fields;
-  fields = field;
-  value = tree_cons (field, convert (unsigned_intSI_type_node,
-                                    build_int_2 (local_tick, 0)),
-                    value);
+  CONSTRUCTOR_APPEND_ELT (v1, info_fields,
+                         build_int_cstu (TREE_TYPE (info_fields),
+                                         local_tick));
+  info_fields = DECL_CHAIN (info_fields);
 
   /* Filename */
 
   /* Filename */
-  string_type = build_pointer_type (build_qualified_type (char_type_node,
-                                                   TYPE_QUAL_CONST));
-  field = build_decl (FIELD_DECL, NULL_TREE, string_type);
-  TREE_CHAIN (field) = fields;
-  fields = field;
-  filename = getpwd ();
-  filename = (filename && da_file_name[0] != '/'
-             ? concat (filename, "/", da_file_name, NULL)
-             : da_file_name);
-  filename_len = strlen (filename);
-  filename_string = build_string (filename_len + 1, filename);
-  if (filename != da_file_name)
-    free (filename);
-  TREE_TYPE (filename_string) =
-         build_array_type (char_type_node,
-                           build_index_type (build_int_2 (filename_len, 0)));
-  value = tree_cons (field, build1 (ADDR_EXPR, string_type, filename_string),
-                    value);
-
-  /* Build the fn_info type and initializer.  */
-  fn_info_type = build_fn_info_type (n_ctr_types);
-  fn_info_ptr_type = build_pointer_type (build_qualified_type
-                                        (fn_info_type, TYPE_QUAL_CONST));
-  for (fn = functions_head, n_fns = 0; fn; fn = fn->next, n_fns++)
-    fn_info_value = tree_cons (NULL_TREE,
-                              build_fn_info_value (fn, fn_info_type),
-                              fn_info_value);
-  if (n_fns)
+  da_file_name_len = strlen (da_file_name);
+  filename_string = build_string (da_file_name_len + 1, da_file_name);
+  TREE_TYPE (filename_string) = build_array_type
+    (char_type_node, build_index_type (size_int (da_file_name_len)));
+  CONSTRUCTOR_APPEND_ELT (v1, info_fields,
+                         build1 (ADDR_EXPR, TREE_TYPE (info_fields),
+                                 filename_string));
+  info_fields = DECL_CHAIN (info_fields);
+
+  /* merge fn array -- NULL slots indicate unmeasured counters */
+  merge_fn_type = TREE_TYPE (TREE_TYPE (info_fields));
+  for (ix = 0; ix != GCOV_COUNTERS; ix++)
     {
     {
-      tree array_type;
-
-      array_type = build_index_type (build_int_2 (n_fns - 1, 0));
-      array_type = build_array_type (fn_info_type, array_type);
+      tree ptr = null_pointer_node;
 
 
-      fn_info_value = build_constructor (array_type, nreverse (fn_info_value));
-      fn_info_value = build1 (ADDR_EXPR, fn_info_ptr_type, fn_info_value);
+      if ((1u << ix) & prg_ctr_mask)
+       {
+         tree merge_fn = build_decl (BUILTINS_LOCATION,
+                                     FUNCTION_DECL,
+                                     get_identifier (ctr_merge_functions[ix]),
+                                     TREE_TYPE (merge_fn_type));
+         DECL_EXTERNAL (merge_fn) = 1;
+         TREE_PUBLIC (merge_fn) = 1;
+         DECL_ARTIFICIAL (merge_fn) = 1;
+         TREE_NOTHROW (merge_fn) = 1;
+         /* Initialize assembler name so we can stream out. */
+         DECL_ASSEMBLER_NAME (merge_fn);
+         ptr = build1 (ADDR_EXPR, merge_fn_type, merge_fn);
+       }
+      CONSTRUCTOR_APPEND_ELT (v2, NULL, ptr);
     }
     }
-  else
-    fn_info_value = null_pointer_node;
+  CONSTRUCTOR_APPEND_ELT (v1, info_fields,
+                         build_constructor (TREE_TYPE (info_fields), v2));
+  info_fields = DECL_CHAIN (info_fields);
+
+  /* n_functions */
+  n_funcs = TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (fn_ary)));
+  n_funcs = fold_build2 (PLUS_EXPR, TREE_TYPE (info_fields),
+                        n_funcs, size_one_node);
+  CONSTRUCTOR_APPEND_ELT (v1, info_fields, n_funcs);
+  info_fields = DECL_CHAIN (info_fields);
+
+  /* functions */
+  CONSTRUCTOR_APPEND_ELT (v1, info_fields,
+                         build1 (ADDR_EXPR, TREE_TYPE (info_fields), fn_ary));
+  info_fields = DECL_CHAIN (info_fields);
+
+  gcc_assert (!info_fields);
+  return build_constructor (info_type, v1);
+}
 
 
-  /* number of functions */
-  field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
-  TREE_CHAIN (field) = fields;
-  fields = field;
-  value = tree_cons (field,
-                    convert (unsigned_type_node, build_int_2 (n_fns, 0)),
-                    value);
+/* Create the gcov_info types and object.  Generate the constructor
+   function to call __gcov_init.  Does not generate the initializer
+   for the object.  Returns TRUE if coverage data is being emitted.  */
 
 
-  /* fn_info table */
-  field = build_decl (FIELD_DECL, NULL_TREE, fn_info_ptr_type);
-  TREE_CHAIN (field) = fields;
-  fields = field;
-  value = tree_cons (field, fn_info_value, value);
+static bool
+coverage_obj_init (void)
+{
+  tree gcov_info_type, ctor, stmt, init_fn;
+  unsigned n_counters = 0;
+  unsigned ix;
+  struct coverage_data *fn;
+  struct coverage_data **fn_prev;
+  char name_buf[32];
 
 
-  /* counter_mask */
-  field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
-  TREE_CHAIN (field) = fields;
-  fields = field;
-  value = tree_cons (field,
-                    convert (unsigned_type_node,
-                             build_int_2 (prg_ctr_mask, 0)),
-                    value);
+  no_coverage = 1; /* Disable any further coverage.  */
 
 
-  /* counters */
-  ctr_info_type = build_ctr_info_type ();
-  ctr_info_ary_type = build_index_type (build_int_2 (n_ctr_types, 0));
-  ctr_info_ary_type = build_array_type (ctr_info_type, ctr_info_ary_type);
-  for (ix = 0; ix != GCOV_COUNTERS; ix++)
-    if (prg_ctr_mask & (1 << ix))
-      ctr_info_value = tree_cons (NULL_TREE,
-                                 build_ctr_info_value (ix, ctr_info_type),
-                                 ctr_info_value);
-  ctr_info_value = build_constructor (ctr_info_ary_type,
-                                     nreverse (ctr_info_value));
-
-  field = build_decl (FIELD_DECL, NULL_TREE, ctr_info_ary_type);
-  TREE_CHAIN (field) = fields;
-  fields = field;
-  value = tree_cons (field, ctr_info_value, value);
+  if (!prg_ctr_mask)
+    return false;
 
 
-  finish_builtin_struct (type, "__gcov_info", fields, NULL_TREE);
+  if (cgraph_dump_file)
+    fprintf (cgraph_dump_file, "Using data file %s\n", da_file_name);
 
 
-  value = build_constructor (type, nreverse (value));
+  /* Prune functions.  */
+  for (fn_prev = &functions_head; (fn = *fn_prev);)
+    if (DECL_STRUCT_FUNCTION (fn->fn_decl))
+      fn_prev = &fn->next;
+    else
+      /* The function is not being emitted, remove from list.  */
+      *fn_prev = fn->next;
 
 
-  return value;
+  for (ix = 0; ix != GCOV_COUNTERS; ix++)
+    if ((1u << ix) & prg_ctr_mask)
+      n_counters++;
+  
+  /* Build the info and fn_info types.  These are mutually recursive.  */
+  gcov_info_type = lang_hooks.types.make_type (RECORD_TYPE);
+  gcov_fn_info_type = lang_hooks.types.make_type (RECORD_TYPE);
+  gcov_fn_info_ptr_type = build_pointer_type
+    (build_qualified_type (gcov_fn_info_type, TYPE_QUAL_CONST));
+  build_fn_info_type (gcov_fn_info_type, n_counters, gcov_info_type);
+  build_info_type (gcov_info_type, gcov_fn_info_ptr_type);
+  
+  /* Build the gcov info var, this is referred to in its own
+     initializer.  */
+  gcov_info_var = build_decl (BUILTINS_LOCATION,
+                             VAR_DECL, NULL_TREE, gcov_info_type);
+  TREE_STATIC (gcov_info_var) = 1;
+  ASM_GENERATE_INTERNAL_LABEL (name_buf, "LPBX", 0);
+  DECL_NAME (gcov_info_var) = get_identifier (name_buf);
+
+  /* Build a decl for __gcov_init.  */
+  init_fn = build_pointer_type (gcov_info_type);
+  init_fn = build_function_type_list (void_type_node, init_fn, NULL);
+  init_fn = build_decl (BUILTINS_LOCATION, FUNCTION_DECL,
+                       get_identifier ("__gcov_init"), init_fn);
+  TREE_PUBLIC (init_fn) = 1;
+  DECL_EXTERNAL (init_fn) = 1;
+  DECL_ASSEMBLER_NAME (init_fn);
+
+  /* Generate a call to __gcov_init(&gcov_info).  */
+  ctor = NULL;
+  stmt = build_fold_addr_expr (gcov_info_var);
+  stmt = build_call_expr (init_fn, 1, stmt);
+  append_to_statement_list (stmt, &ctor);
+
+  /* Generate a constructor to run it.  */
+  cgraph_build_static_cdtor ('I', ctor, DEFAULT_INIT_PRIORITY);
+
+  return true;
 }
 
 }
 
-/* Write out the structure which libgcov uses to locate all the
-   counters.  The structures used here must match those defined in
-   gcov-io.h.  Write out the constructor to call __gcov_init.  */
+/* Generate the coverage function info for FN and DATA.  Append a
+   pointer to that object to CTOR and return the appended CTOR.  */
 
 
-static void
-create_coverage (void)
+static VEC(constructor_elt,gc) *
+coverage_obj_fn (VEC(constructor_elt,gc) *ctor, tree fn,
+                struct coverage_data const *data)
 {
 {
-  tree gcov_info, gcov_info_value;
-  char name[20];
-  char *ctor_name;
-  tree ctor;
-  rtx gcov_info_address;
-
-  no_coverage = 1; /* Disable any further coverage.  */
+  tree init = build_fn_info (data, gcov_fn_info_type, gcov_info_var);
+  tree var = build_var (fn, gcov_fn_info_type, -1);
+  
+  DECL_INITIAL (var) = init;
+  varpool_finalize_decl (var);
+      
+  CONSTRUCTOR_APPEND_ELT (ctor, NULL,
+                         build1 (ADDR_EXPR, gcov_fn_info_ptr_type, var));
+  return ctor;
+}
 
 
-  if (!prg_ctr_mask)
-    return;
+/* Finalize the coverage data.  Generates the array of pointers to
+   function objects from CTOR.  Generate the gcov_info initializer.  */
 
 
-  gcov_info_value = build_gcov_info ();
-
-  gcov_info = build (VAR_DECL, TREE_TYPE (gcov_info_value),
-                    NULL_TREE, NULL_TREE);
-  DECL_INITIAL (gcov_info) = gcov_info_value;
-
-  TREE_STATIC (gcov_info) = 1;
-  ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 0);
-  DECL_NAME (gcov_info) = get_identifier (name);
-
-  /* Build structure.  */
-  assemble_variable (gcov_info, 0, 0, 0);
-
-  /* Build the constructor function to invoke __gcov_init.  */
-  ctor_name = concat (IDENTIFIER_POINTER (get_file_function_name ('I')),
-                     "_GCOV", NULL);
-  ctor = build_decl (FUNCTION_DECL, get_identifier (ctor_name),
-                    build_function_type (void_type_node, NULL_TREE));
-  free (ctor_name);
-  DECL_EXTERNAL (ctor) = 0;
-
-  /* It can be a static function as long as collect2 does not have
-     to scan the object file to find its ctor/dtor routine.  */
-  TREE_PUBLIC (ctor) = ! targetm.have_ctors_dtors;
-  TREE_USED (ctor) = 1;
-  DECL_RESULT (ctor) = build_decl (RESULT_DECL, NULL_TREE, void_type_node);
-  DECL_UNINLINABLE (ctor) = 1;
-
-  ctor = (*lang_hooks.decls.pushdecl) (ctor);
-  rest_of_decl_compilation (ctor, 0, 1, 0);
-  announce_function (ctor);
-  current_function_decl = ctor;
-  DECL_INITIAL (ctor) = error_mark_node;
-  make_decl_rtl (ctor, NULL);
-  init_function_start (ctor);
-  (*lang_hooks.decls.pushlevel) (0);
-  expand_function_start (ctor, 0);
-
-  /* Actually generate the code to call __gcov_init.  */
-  gcov_info_address = force_reg (Pmode, XEXP (DECL_RTL (gcov_info), 0));
-  emit_library_call (gcov_init_libfunc, LCT_NORMAL, VOIDmode, 1,
-                    gcov_info_address, Pmode);
-
-  expand_function_end ();
-  (*lang_hooks.decls.poplevel) (1, 0, 1);
-
-  rest_of_compilation (ctor);
-
-  if (! quiet_flag)
-    fflush (asm_out_file);
-  current_function_decl = NULL_TREE;
-
-  if (targetm.have_ctors_dtors)
-    (* targetm.asm_out.constructor) (XEXP (DECL_RTL (ctor), 0),
-                                    DEFAULT_INIT_PRIORITY);
+static void
+coverage_obj_finish (VEC(constructor_elt,gc) *ctor)
+{
+  unsigned n_functions = VEC_length(constructor_elt, ctor);
+  tree fn_info_ary_type = build_array_type
+    (build_qualified_type (gcov_fn_info_ptr_type, TYPE_QUAL_CONST),
+     build_index_type (size_int (n_functions - 1)));
+  tree fn_info_ary = build_decl (BUILTINS_LOCATION, VAR_DECL, NULL_TREE,
+                                fn_info_ary_type);
+  char name_buf[32];
+
+  TREE_STATIC (fn_info_ary) = 1;
+  ASM_GENERATE_INTERNAL_LABEL (name_buf, "LPBX", 1);
+  DECL_NAME (fn_info_ary) = get_identifier (name_buf);
+  DECL_INITIAL (fn_info_ary) = build_constructor (fn_info_ary_type, ctor);
+  varpool_finalize_decl (fn_info_ary);
+  
+  DECL_INITIAL (gcov_info_var)
+    = build_info (TREE_TYPE (gcov_info_var), fn_info_ary);
+  varpool_finalize_decl (gcov_info_var);
 }
 }
-\f
+
 /* Perform file-level initialization. Read in data file, generate name
    of graph file.  */
 
 /* Perform file-level initialization. Read in data file, generate name
    of graph file.  */
 
@@ -886,18 +1079,48 @@ void
 coverage_init (const char *filename)
 {
   int len = strlen (filename);
 coverage_init (const char *filename)
 {
   int len = strlen (filename);
+  int prefix_len = 0;
+
+  if (!profile_data_prefix && !IS_ABSOLUTE_PATH (filename))
+    profile_data_prefix = getpwd ();
+
+  if (profile_data_prefix)
+    prefix_len = strlen (profile_data_prefix);
 
   /* Name of da file.  */
 
   /* Name of da file.  */
-  da_file_name = (char *) xmalloc (len + strlen (GCOV_DATA_SUFFIX) + 1);
-  strcpy (da_file_name, filename);
-  strcat (da_file_name, GCOV_DATA_SUFFIX);
+  da_file_name = XNEWVEC (char, len + strlen (GCOV_DATA_SUFFIX)
+                         + prefix_len + 2);
+
+  if (profile_data_prefix)
+    {
+      memcpy (da_file_name, profile_data_prefix, prefix_len);
+      da_file_name[prefix_len++] = '/';
+    }
+  memcpy (da_file_name + prefix_len, filename, len);
+  strcpy (da_file_name + prefix_len + len, GCOV_DATA_SUFFIX);
 
   /* Name of bbg file.  */
 
   /* Name of bbg file.  */
-  bbg_file_name = (char *) xmalloc (len + strlen (GCOV_NOTE_SUFFIX) + 1);
-  strcpy (bbg_file_name, filename);
-  strcat (bbg_file_name, GCOV_NOTE_SUFFIX);
+  if (flag_test_coverage && !flag_compare_debug)
+    {
+      bbg_file_name = XNEWVEC (char, len + strlen (GCOV_NOTE_SUFFIX) + 1);
+      memcpy (bbg_file_name, filename, len);
+      strcpy (bbg_file_name + len, GCOV_NOTE_SUFFIX);
+
+      if (!gcov_open (bbg_file_name, -1))
+       {
+         error ("cannot open %s", bbg_file_name);
+         bbg_file_name = NULL;
+       }
+      else
+       {
+         gcov_write_unsigned (GCOV_NOTE_MAGIC);
+         gcov_write_unsigned (GCOV_VERSION);
+         gcov_write_unsigned (local_tick);
+       }
+    }
 
 
-  read_counts_file ();
+  if (flag_branch_probabilities)
+    read_counts_file ();
 }
 
 /* Performs file-level cleanup.  Close graph file, generate coverage
 }
 
 /* Performs file-level cleanup.  Close graph file, generate coverage
@@ -906,17 +1129,22 @@ coverage_init (const char *filename)
 void
 coverage_finish (void)
 {
 void
 coverage_finish (void)
 {
-  create_coverage ();
-  if (bbg_file_opened)
+  if (bbg_file_name && gcov_close ())
+    unlink (bbg_file_name);
+  
+  if (!local_tick || local_tick == (unsigned)-1)
+    /* Only remove the da file, if we cannot stamp it.  If we can
+       stamp it, libgcov will DTRT.  */
+    unlink (da_file_name);
+
+  if (coverage_obj_init ())
     {
     {
-      int error = gcov_close ();
-
-      if (error)
-       unlink (bbg_file_name);
-      if (!local_tick)
-       /* Only remove the da file, if we cannot stamp it. If we can
-          stamp it, libgcov will DTRT.  */
-       unlink (da_file_name);
+      VEC(constructor_elt,gc) *fn_ctor = NULL;
+      struct coverage_data *fn;
+      
+      for (fn = functions_head; fn; fn = fn->next)
+       fn_ctor = coverage_obj_fn (fn_ctor, fn->fn_decl, fn);
+      coverage_obj_finish (fn_ctor);
     }
 }
 
     }
 }