diff mbox

[coverage] Some restructuring

Message ID 4EDBBD6B.1090007@acm.org
State New
Headers show

Commit Message

Nathan Sidwell Dec. 4, 2011, 6:35 p.m. UTC
I've committed this patch to break apart the gcov finalization routines, I 
believe this will make it easier to fix the problem shown up by bug 51113 -- 
although this patch does not.  Notable changes:

* rename coverage_begin_output to coverage_begin_function for consistency with 
coverage_end_function

* Only call it once from the profile code -- I suspect in the past it was called 
from several locations where it wasn't possible to statically determine which 
would be the first call.  Now it's obvious.

* Move the opening of the notes file to the coverage initialization.  This does 
mean that we'll always create a notes file when generating coverage data, even 
if there are no functions in the resultant object file.  I don't think that's 
unsurprising -- and will stop gcov itself complaining about a missing notes file 
in this case.

* Replace the trailing array of the gcov_info object with a pointer to an array. 
  This doesn't require changes to libgcov.c's source but will of course change 
the resultant object file it compiles to and constitutes an ABI change.  This 
change allows the creation of the gcov_info data type without knowing the number 
of functions being instrumented.

tested on i686-pc-linux-gnu with profiledbootstrap.

nathan
2011-12-04  Nathan Sidwell  <nathan@acm.org>

	* gcov-io.h (struct gcov_info): Replace trailing array with
	pointer to array.
	* profile.c (branch_prob): Only call renamed
	coverage_begin_function once.
	* coverage.h (coverage_begin_output): Rename to ...
	(coverage_begin_function): ... here.
	* coverage.c (struct function_list): Rename to ...
	(struct coverage_data): ... this.  Update all uses.
	(gcov_info_var, gcov_fn_info_type, gcov_fn_info_ptr_type): New
	globals.
	(bbg_file_opened, bbg_function_announced): Remove.
	(get_coverage_counts): Adjust message.
	(coverage_begin_ouput): Rename to ...
	(coverage_begin_function): ... here.  Move file opening to
	coverage_init.  Adjust for being called only once.
	(coverage_end_function): Remove bbg file and inhibit further
	output here on error.
	(build_info_type): Adjust for change to pointer to array.
	(build_info): Receive array of function pointers and adjust.
	(create_coverage): Break into ...
	(coverage_obj_init, coverage_obj_fn, coverage_obj_finish):
	... these, and adjust.
	(coverage_init): Open the notes file here.  Tidy.
	(coverage_finish): Call coverage_obj_init etc.

Comments

Markus Trippelsdorf Dec. 7, 2011, 1:32 p.m. UTC | #1
On 2011.12.04 at 18:35 +0000, Nathan Sidwell wrote:
> I've committed this patch to break apart the gcov finalization routines, I 
> believe this will make it easier to fix the problem shown up by bug 51113 -- 
> although this patch does not.  Notable changes:
> 
> * rename coverage_begin_output to coverage_begin_function for consistency with 
> coverage_end_function
> 
> * Only call it once from the profile code -- I suspect in the past it was called 
> from several locations where it wasn't possible to statically determine which 
> would be the first call.  Now it's obvious.
> 
> * Move the opening of the notes file to the coverage initialization.  This does 
> mean that we'll always create a notes file when generating coverage data, even 
> if there are no functions in the resultant object file.  I don't think that's 
> unsurprising -- and will stop gcov itself complaining about a missing notes file 
> in this case.
> 
> * Replace the trailing array of the gcov_info object with a pointer to an array. 
>   This doesn't require changes to libgcov.c's source but will of course change 
> the resultant object file it compiles to and constitutes an ABI change.  This 
> change allows the creation of the gcov_info data type without knowing the number 
> of functions being instrumented.
> 
> tested on i686-pc-linux-gnu with profiledbootstrap.

Nathan,

this patch breaks profiled build of tramp3d:

 % c++ -w -Ofast -fprofile-generate -march=native tramp3d-v4.cpp
/tmp/ccMmeivA.o:tramp3d-v4.cpp:function Inform::flush(): error: undefined reference to '__gcov0__ZNKSt19basic_ostringstreamIcSt11char_traitsIcESaIcEE3strEv'
/tmp/ccMmeivA.o:tramp3d-v4.cpp:function Inform::flush(): error: undefined reference to '__gcov0__ZNKSt19basic_ostringstreamIcSt11char_traitsIcESaIcEE3strEv'
...

see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51449
diff mbox

Patch

Index: gcov-io.h
===================================================================
--- gcov-io.h	(revision 181984)
+++ gcov-io.h	(working copy)
@@ -448,8 +448,8 @@  struct gcov_info
 					  unused) */
   
   unsigned n_functions;		/* number of functions */
-  const struct gcov_fn_info *functions[0]; /* pointers to function
-					      information  */
+  const struct gcov_fn_info *const *functions; /* pointer to pointers
+					          to function information  */
 };
 
 /* Register a new object file module.  */
Index: profile.c
===================================================================
--- profile.c	(revision 181984)
+++ profile.c	(working copy)
@@ -1110,30 +1110,25 @@  branch_prob (void)
   lineno_checksum = coverage_compute_lineno_checksum ();
 
   /* Write the data from which gcov can reconstruct the basic block
-     graph.  */
+     graph and function line numbers  */
 
-  /* Basic block flags */
-  if (coverage_begin_output (lineno_checksum, cfg_checksum))
+  if (coverage_begin_function (lineno_checksum, cfg_checksum))
     {
       gcov_position_t offset;
 
+      /* Basic block flags */
       offset = gcov_write_tag (GCOV_TAG_BLOCKS);
       for (i = 0; i != (unsigned) (n_basic_blocks); i++)
 	gcov_write_unsigned (0);
       gcov_write_length (offset);
-    }
-
-   /* Keep all basic block indexes nonnegative in the gcov output.
-      Index 0 is used for entry block, last index is for exit block.
-      */
-  ENTRY_BLOCK_PTR->index = 1;
-  EXIT_BLOCK_PTR->index = last_basic_block;
 
-  /* Arcs */
-  if (coverage_begin_output (lineno_checksum, cfg_checksum))
-    {
-      gcov_position_t offset;
+      /* Keep all basic block indexes nonnegative in the gcov output.
+	 Index 0 is used for entry block, last index is for exit
+	 block.    */
+      ENTRY_BLOCK_PTR->index = 1;
+      EXIT_BLOCK_PTR->index = last_basic_block;
 
+      /* Arcs */
       FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, EXIT_BLOCK_PTR, next_bb)
 	{
 	  edge e;
@@ -1168,11 +1163,11 @@  branch_prob (void)
 
 	  gcov_write_length (offset);
 	}
-    }
 
-  /* Line numbers.  */
-  if (coverage_begin_output (lineno_checksum, cfg_checksum))
-    {
+      ENTRY_BLOCK_PTR->index = ENTRY_BLOCK;
+      EXIT_BLOCK_PTR->index = EXIT_BLOCK;
+
+      /* Line numbers.  */
       /* Initialize the output.  */
       output_location (NULL, 0, NULL, NULL);
 
@@ -1217,8 +1212,6 @@  branch_prob (void)
 	}
     }
 
-  ENTRY_BLOCK_PTR->index = ENTRY_BLOCK;
-  EXIT_BLOCK_PTR->index = EXIT_BLOCK;
 #undef BB_TO_GCOV_INDEX
 
   if (flag_profile_values)
Index: coverage.c
===================================================================
--- coverage.c	(revision 181984)
+++ coverage.c	(working copy)
@@ -54,9 +54,9 @@  along with GCC; see the file COPYING3.
 #include "gcov-io.h"
 #include "gcov-io.c"
 
-struct GTY((chain_next ("%h.next"))) 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 lineno_checksum;	 /* function lineno checksum */
   unsigned cfg_checksum;	 /* function cfg checksum */
@@ -78,8 +78,8 @@  typedef struct counts_entry
   struct gcov_ctr_summary summary;
 } counts_entry_t;
 
-static GTY(()) 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.  */
@@ -91,10 +91,14 @@  static GTY(()) tree fn_v_ctrs[GCOV_COUNT
 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 unsigned bbg_file_opened;
-static int bbg_function_announced;
 
 /* Name of the count data file.  */
 static char *da_file_name;
@@ -113,10 +117,13 @@  static void htab_counts_entry_del (void
 static void read_counts_file (void);
 static tree build_var (tree, tree, int);
 static void build_fn_info_type (tree, unsigned, tree);
-static tree build_fn_info (const struct function_list *, tree, tree);
-static void build_info_type (tree, unsigned, tree);
-static tree build_info (tree, tree, tree, unsigned);
-static void create_coverage (void);
+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) *);
 
 /* Return the type node for gcov_type.  */
 
@@ -374,7 +381,7 @@  get_coverage_counts (unsigned counter, u
     }
   else if (entry->lineno_checksum != lineno_checksum)
     {
-      warning (0, "source location for function %qE have changed,"
+      warning (0, "source locations for function %qE have changed,"
 	       " the profile data may be out of date",
 	       DECL_ASSEMBLER_NAME (current_function_decl));
     }
@@ -553,51 +560,32 @@  coverage_compute_cfg_checksum (void)
 }
 
 /* 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
-coverage_begin_output (unsigned lineno_checksum, unsigned cfg_checksum)
+coverage_begin_function (unsigned lineno_checksum, unsigned cfg_checksum)
 {
+  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 || !flag_test_coverage || flag_compare_debug)
+  if (no_coverage || !bbg_file_name)
     return 0;
 
-  if (!bbg_function_announced)
-    {
-      expanded_location xloc
-	= expand_location (DECL_SOURCE_LOCATION (current_function_decl));
-      unsigned long offset;
-
-      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;
-	}
-
+  xloc = expand_location (DECL_SOURCE_LOCATION (current_function_decl));
 
-      /* 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 (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);
 
-      bbg_function_announced = 1;
-    }
   return !gcov_is_error ();
 }
 
@@ -609,23 +597,22 @@  coverage_end_function (unsigned lineno_c
 {
   unsigned i;
 
-  if (bbg_file_opened > 1 && gcov_is_error ())
+  if (bbg_file_name && gcov_is_error ())
     {
       warning (0, "error writing %qs", bbg_file_name);
-      bbg_file_opened = -1;
+      unlink (bbg_file_name);
+      bbg_file_name = NULL;
     }
 
-  if (fn_ctr_mask)
+  /* 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 (fn_ctr_mask && !DECL_EXTERNAL (current_function_decl))
     {
-      struct function_list *item;
-
-      item = ggc_alloc_function_list ();
+      struct coverage_data *item = ggc_alloc_coverage_data ();
 
-      item->next = 0;
       item->ident = current_function_funcdef_no + 1;
       item->lineno_checksum = lineno_checksum;
       item->cfg_checksum = cfg_checksum;
-      item->fn_decl = current_function_decl;
       for (i = 0; i != GCOV_COUNTERS; i++)
 	{
 	  tree var = fn_v_ctrs[i];
@@ -640,20 +627,23 @@  coverage_end_function (unsigned lineno_c
 	      DECL_SIZE_UNIT (var) = TYPE_SIZE_UNIT (array_type);
 	      varpool_finalize_decl (var);
 	    }
+	}
+      item->fn_decl = current_function_decl;
+      item->next = 0;
+      *functions_tail = item;
+      functions_tail = &item->next;
+    }
+  
+  if (fn_ctr_mask)
+    {
+      for (i = 0; i != GCOV_COUNTERS; i++)
+	{
 	  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;
-      /* 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 (item->fn_decl))
-	{
-	  *functions_tail = item;
-	  functions_tail = &item->next;
-	}
     }
-  bbg_function_announced = 0;
 }
 
 /* Build a coverage variable of TYPE for function FN_DECL.  If COUNTER
@@ -737,12 +727,12 @@  build_fn_info_type (tree type, unsigned
   finish_builtin_struct (type, "__gcov_fn_info", fields, NULL_TREE);
 }
 
-/* Creates a CONSTRUCTOR for a gcov_fn_info. FUNCTION is
-   the function being processed and TYPE is the gcov_fn_info
-   RECORD_TYPE.  KEY is the object file key. */
+/* 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
-build_fn_info (const struct function_list *function, tree type, tree key)
+build_fn_info (const struct coverage_data *data, tree type, tree key)
 {
   tree fields = TYPE_FIELDS (type);
   tree ctr_type;
@@ -758,19 +748,19 @@  build_fn_info (const struct function_lis
   /* ident */
   CONSTRUCTOR_APPEND_ELT (v1, fields,
 			  build_int_cstu (get_gcov_unsigned_t (),
-					  function->ident));
+					  data->ident));
   fields = DECL_CHAIN (fields);
 
   /* lineno_checksum */
   CONSTRUCTOR_APPEND_ELT (v1, fields,
 			  build_int_cstu (get_gcov_unsigned_t (),
-					  function->lineno_checksum));
+					  data->lineno_checksum));
   fields = DECL_CHAIN (fields);
 
   /* cfg_checksum */
   CONSTRUCTOR_APPEND_ELT (v1, fields,
 			  build_int_cstu (get_gcov_unsigned_t (),
-					  function->cfg_checksum));
+					  data->cfg_checksum));
   fields = DECL_CHAIN (fields);
 
   /* counters */
@@ -779,7 +769,7 @@  build_fn_info (const struct function_lis
     if (prg_ctr_mask & (1 << ix))
       {
 	VEC(constructor_elt,gc) *ctr = NULL;
-	tree var = function->ctr_vars[ix];
+	tree var = data->ctr_vars[ix];
 	unsigned count = 0;
 
 	if (var)
@@ -804,17 +794,15 @@  build_fn_info (const struct function_lis
   return build_constructor (type, v1);
 }
 
-/* Creaste gcov_info_struct.  N_FUNCS is the number of functions in
-   the trailing array.  */
+/* 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 void
-build_info_type (tree type, unsigned n_funcs, tree fn_info_type)
+build_info_type (tree type, tree fn_info_ptr_type)
 {
   tree field, fields = NULL_TREE;
-  tree merge_fn_type, fn_info_array;
+  tree merge_fn_type;
 
-  gcc_assert (n_funcs);
-  
   /* Version ident */
   field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
 		      get_gcov_unsigned_t ());
@@ -860,33 +848,31 @@  build_info_type (tree type, unsigned n_f
   DECL_CHAIN (field) = fields;
   fields = field;
   
-  /* function_info pointer array */
-  fn_info_type = build_pointer_type
-    (build_qualified_type (fn_info_type, TYPE_QUAL_CONST));
-  fn_info_array = build_index_type (size_int (n_funcs));
-  fn_info_array = build_array_type (fn_info_type, fn_info_array);
+  /* 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_array);
+		      fn_info_ptr_type);
   DECL_CHAIN (field) = fields;
   fields = field;
 
   finish_builtin_struct (type, "__gcov_info", fields, NULL_TREE);
 }
 
-/* Creates the gcov_info initializer. 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
-build_info (tree info_type, tree fn_type, tree key_var, unsigned n_funcs)
+build_info (tree info_type, tree fn_ary)
 {
   tree info_fields = TYPE_FIELDS (info_type);
-  tree merge_fn_type, fn_info_ptr_type;
+  tree merge_fn_type, n_funcs;
   unsigned ix;
   tree filename_string;
   int da_file_name_len;
-  const struct function_list *fn;
   VEC(constructor_elt,gc) *v1 = NULL;
   VEC(constructor_elt,gc) *v2 = NULL;
-  VEC(constructor_elt,gc) *v3 = NULL;
 
   /* Version ident */
   CONSTRUCTOR_APPEND_ELT (v1, info_fields,
@@ -941,104 +927,135 @@  build_info (tree info_type, tree fn_type
   info_fields = DECL_CHAIN (info_fields);
 
   /* n_functions */
-  CONSTRUCTOR_APPEND_ELT (v1, info_fields,
-			  build_int_cstu (TREE_TYPE (info_fields), n_funcs));
+  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);
-  
-  /* Build the fn_info type and initializer.  */
-  fn_info_ptr_type = TREE_TYPE (TREE_TYPE (info_fields));
-  
-  for (fn = functions_head; fn; fn = fn->next)
-    {
-      tree init = build_fn_info (fn, fn_type, key_var);
-      tree var = build_var (fn->fn_decl, fn_type, -1);
 
-      DECL_INITIAL (var) = init;
-      varpool_finalize_decl (var);
-      
-      CONSTRUCTOR_APPEND_ELT (v3, NULL,
-			      build1 (ADDR_EXPR, fn_info_ptr_type, var));
-    }
+  /* functions */
   CONSTRUCTOR_APPEND_ELT (v1, info_fields,
-			  build_constructor (TREE_TYPE (info_fields), v3));
+			  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);
 }
 
-/* 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.  */
+/* 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.  */
 
-static void
-create_coverage (void)
+static bool
+coverage_obj_init (void)
 {
-  tree gcov_info, gcov_init, body, t;
-  tree gcov_info_type, gcov_fn_type;
-  unsigned n_counters = 0, n_functions  = 0;
-  struct function_list *fn;
-  struct function_list **fn_prev;
+  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];
 
   no_coverage = 1; /* Disable any further coverage.  */
 
   if (!prg_ctr_mask)
-    return;
+    return false;
 
   if (cgraph_dump_file)
     fprintf (cgraph_dump_file, "Using data file %s\n", da_file_name);
 
-  for (ix = 0; ix != GCOV_COUNTERS; ix++)
-    if ((1u << ix) & prg_ctr_mask)
-      n_counters++;
+  /* Prune functions.  */
   for (fn_prev = &functions_head; (fn = *fn_prev);)
     if (DECL_STRUCT_FUNCTION (fn->fn_decl))
-      {
-	n_functions++;
-	fn_prev = &fn->next;
-      }
+      fn_prev = &fn->next;
     else
       /* The function is not being emitted, remove from list.  */
       *fn_prev = fn->next;
+
+  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_type = lang_hooks.types.make_type (RECORD_TYPE);
-  build_fn_info_type (gcov_fn_type, n_counters, gcov_info_type);
-  build_info_type (gcov_info_type, n_functions, gcov_fn_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 = build_decl (BUILTINS_LOCATION,
-			  VAR_DECL, NULL_TREE, gcov_info_type);
-  TREE_STATIC (gcov_info) = 1;
+  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) = get_identifier (name_buf);
-  DECL_INITIAL (gcov_info) = build_info (gcov_info_type, gcov_fn_type,
-					 gcov_info, n_functions);
-
-  /* Build structure.  */
-  varpool_finalize_decl (gcov_info);
+  DECL_NAME (gcov_info_var) = get_identifier (name_buf);
 
   /* Build a decl for __gcov_init.  */
-  t = build_pointer_type (TREE_TYPE (gcov_info));
-  t = build_function_type_list (void_type_node, t, NULL);
-  t = build_decl (BUILTINS_LOCATION,
-		  FUNCTION_DECL, get_identifier ("__gcov_init"), t);
-  TREE_PUBLIC (t) = 1;
-  DECL_EXTERNAL (t) = 1;
-  DECL_ASSEMBLER_NAME (t);  /* Initialize assembler name so we can stream out. */
-  gcov_init = t;
+  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).  */
-  body = NULL;
-  t = build_fold_addr_expr (gcov_info);
-  t = build_call_expr (gcov_init, 1, t);
-  append_to_statement_list (t, &body);
+  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', body, DEFAULT_INIT_PRIORITY);
+  cgraph_build_static_cdtor ('I', ctor, DEFAULT_INIT_PRIORITY);
+
+  return true;
 }
-
+
+/* Generate the coverage function info for FN and DATA.  Append a
+   pointer to that object to CTOR and return the appended CTOR.  */
+
+static VEC(constructor_elt,gc) *
+coverage_obj_fn (VEC(constructor_elt,gc) *ctor, tree fn,
+		 struct coverage_data const *data)
+{
+  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;
+}
+
+/* Finalize the coverage data.  Generates the array of pointers to
+   function objects from CTOR.  Generate the gcov_info initializer.  */
+
+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);
+}
+
 /* Perform file-level initialization. Read in data file, generate name
    of graph file.  */
 
@@ -1046,33 +1063,45 @@  void
 coverage_init (const char *filename)
 {
   int len = strlen (filename);
-  /* + 1 for extra '/', in case prefix doesn't end with /.  */
-  int prefix_len;
+  int prefix_len = 0;
 
-  if (profile_data_prefix == 0 && !IS_ABSOLUTE_PATH(&filename[0]))
+  if (!profile_data_prefix && !IS_ABSOLUTE_PATH (filename))
     profile_data_prefix = getpwd ();
 
-  prefix_len = (profile_data_prefix) ? strlen (profile_data_prefix) + 1 : 0;
+  if (profile_data_prefix)
+    prefix_len = strlen (profile_data_prefix);
 
   /* Name of da file.  */
   da_file_name = XNEWVEC (char, len + strlen (GCOV_DATA_SUFFIX)
-			  + prefix_len + 1);
+			  + prefix_len + 2);
 
   if (profile_data_prefix)
     {
-      strcpy (da_file_name, profile_data_prefix);
-      da_file_name[prefix_len - 1] = '/';
-      da_file_name[prefix_len] = 0;
+      memcpy (da_file_name, profile_data_prefix, prefix_len);
+      da_file_name[prefix_len++] = '/';
     }
-  else
-    da_file_name[0] = 0;
-  strcat (da_file_name, filename);
-  strcat (da_file_name, GCOV_DATA_SUFFIX);
+  memcpy (da_file_name + prefix_len, filename, len);
+  strcpy (da_file_name + prefix_len + len, GCOV_DATA_SUFFIX);
 
   /* Name of bbg file.  */
-  bbg_file_name = XNEWVEC (char, 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);
+	}
+    }
 
   if (flag_branch_probabilities)
     read_counts_file ();
@@ -1084,17 +1113,22 @@  coverage_init (const char *filename)
 void
 coverage_finish (void)
 {
-  create_coverage ();
-  if (bbg_file_opened)
-    {
-      int error = gcov_close ();
+  if (bbg_file_name && gcov_close ())
+    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);
 
-      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);
+  if (coverage_obj_init ())
+    {
+      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);
     }
 }
 
Index: coverage.h
===================================================================
--- coverage.h	(revision 181984)
+++ coverage.h	(working copy)
@@ -26,13 +26,12 @@  along with GCC; see the file COPYING3.
 extern void coverage_init (const char *);
 extern void coverage_finish (void);
 
-/* Complete the coverage information for the current function. Once
-   per function.  */
-extern void coverage_end_function (unsigned, unsigned);
-
 /* Start outputting coverage information for the current
-   function. Repeatable per function.  */
-extern int coverage_begin_output (unsigned, unsigned);
+   function.  */
+extern int coverage_begin_function (unsigned, unsigned);
+
+/* Complete the coverage information for the current function.  */
+extern void coverage_end_function (unsigned, unsigned);
 
 /* Compute the control flow checksum for the current function.  */
 extern unsigned coverage_compute_cfg_checksum (void);