OSDN Git Service

2003-06-10 Andrew Haley <aph@redhat.com>
[pf3gnuchains/gcc-fork.git] / gcc / coverage.c
index 421b3b1..1b537c6 100644 (file)
@@ -50,7 +50,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 struct function_list
 {
   struct function_list *next;   /* next function */
-  const char *name;             /* function name */
+  unsigned ident;               /* function ident */
   unsigned checksum;            /* function checksum */
   unsigned n_ctrs[GCOV_COUNTERS];/* number of counters.  */
 };
@@ -59,7 +59,7 @@ struct function_list
 typedef struct counts_entry
 {
   /* We hash by  */
-  char *function_name;
+  unsigned ident;
   unsigned ctr;
   
   /* Store  */
@@ -74,14 +74,16 @@ typedef struct counts_entry
 
 static struct function_list *functions_head = 0;
 static struct function_list **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 prg_n_ctrs[GCOV_COUNTERS];
+static unsigned prg_n_ctrs[GCOV_COUNTERS]; /* Total counters allocated.  */
 
 /* Counter information for current function.  */
-static unsigned fn_ctr_mask;
-static unsigned fn_n_ctrs[GCOV_COUNTERS];
+static unsigned fn_ctr_mask; /* Mask of counters used. */
+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.  */
 static char *bbg_file_name;
@@ -97,6 +99,10 @@ 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;
+
 /* Forward declarations.  */
 static hashval_t htab_counts_entry_hash PARAMS ((const void *));
 static int htab_counts_entry_eq PARAMS ((const void *, const void *));
@@ -118,7 +124,7 @@ htab_counts_entry_hash (of)
 {
   const counts_entry_t *entry = of;
 
-  return htab_hash_string (entry->function_name) ^ entry->ctr;
+  return entry->ident * GCOV_COUNTERS + entry->ctr;
 }
 
 static int
@@ -129,8 +135,7 @@ htab_counts_entry_eq (of1, of2)
   const counts_entry_t *entry1 = of1;
   const counts_entry_t *entry2 = of2;
 
-  return !strcmp (entry1->function_name, entry2->function_name)
-    && entry1->ctr == entry2->ctr;
+  return entry1->ident == entry2->ident && entry1->ctr == entry2->ctr;
 }
 
 static void
@@ -139,7 +144,6 @@ htab_counts_entry_del (of)
 {
   counts_entry_t *entry = of;
 
-  free (entry->function_name);
   free (entry->counts);
   free (entry);
 }
@@ -149,11 +153,14 @@ htab_counts_entry_del (of)
 static void
 read_counts_file ()
 {
-  char *function_name_buffer = NULL;
-  unsigned version, ix, checksum = -1;
+  gcov_unsigned_t fn_ident = 0;
+  gcov_unsigned_t version, checksum = -1;
+  unsigned ix;
   counts_entry_t *summaried = NULL;
   unsigned seen_summary = 0;
-  
+  gcov_unsigned_t tag;
+  int error = 0;
+
   if (!gcov_open (da_file_name, 1))
     return;
   
@@ -166,7 +173,7 @@ read_counts_file ()
   else if ((version = gcov_read_unsigned ()) != GCOV_VERSION)
     {
       char v[4], e[4];
-      unsigned required = GCOV_VERSION;
+      gcov_unsigned_t required = GCOV_VERSION;
       
       for (ix = 4; ix--; required >>= 8, version >>= 8)
        {
@@ -182,20 +189,16 @@ read_counts_file ()
   counts_hash = htab_create (10,
                             htab_counts_entry_hash, htab_counts_entry_eq,
                             htab_counts_entry_del);
-  while (!gcov_is_eof ())
+  while ((tag = gcov_read_unsigned ()))
     {
-      unsigned tag, length;
-      unsigned long offset;
-      int error;
+      gcov_unsigned_t length;
+      gcov_position_t offset;
       
-      tag = gcov_read_unsigned ();
       length = gcov_read_unsigned ();
       offset = gcov_position ();
       if (tag == GCOV_TAG_FUNCTION)
        {
-         const char *string = gcov_read_string ();
-         free (function_name_buffer);
-         function_name_buffer = string ? xstrdup (string) : NULL;
+         fn_ident = gcov_read_unsigned ();
          checksum = gcov_read_unsigned ();
          if (seen_summary)
            {
@@ -231,13 +234,13 @@ read_counts_file ()
              entry->summary.sum_max += csum->sum_max;
            }
        }
-      else if (GCOV_TAG_IS_COUNTER (tag) && function_name_buffer)
+      else if (GCOV_TAG_IS_COUNTER (tag) && fn_ident)
        {
          counts_entry_t **slot, *entry, elt;
          unsigned n_counts = length / 8;
          unsigned ix;
 
-         elt.function_name = function_name_buffer;
+         elt.ident = fn_ident;
          elt.ctr = GCOV_COUNTER_FOR_TAG (tag);
 
          slot = (counts_entry_t **) htab_find_slot
@@ -246,7 +249,7 @@ read_counts_file ()
          if (!entry)
            {
              *slot = entry = xcalloc (1, sizeof (counts_entry_t));
-             entry->function_name = xstrdup (elt.function_name);
+             entry->ident = elt.ident;
              entry->ctr = elt.ctr;
              entry->checksum = checksum;
              entry->summary.num = n_counts;
@@ -255,33 +258,42 @@ read_counts_file ()
          else if (entry->checksum != checksum
                   || entry->summary.num != n_counts)
            {
-             warning ("profile mismatch for `%s'", function_name_buffer);
+             warning ("coverage mismatch for function %u", fn_ident);
              htab_delete (counts_hash);
              break;
            }
-         
-         /* 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.  */
-         if (!entry->chain || summaried != entry)
+         else if (elt.ctr >= GCOV_COUNTERS_SUMMABLE)
+           {
+             warning ("cannot merge separate %s counters for function %u",
+                      ctr_names[elt.ctr], fn_ident);
+             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))
            {
              entry->chain = summaried;
              summaried = entry;
            }
          for (ix = 0; ix != n_counts; ix++)
            entry->counts[ix] += gcov_read_counter ();
+       skip_merge:;
        }
-      gcov_seek (offset, length);
+      gcov_sync (offset, length);
       if ((error = gcov_is_error ()))
-       {
-         warning (error < 0 ? "`%s' has overflowed" : "`%s' is corrupted",
-                  da_file_name);
-         htab_delete (counts_hash);
-         break;
-       }
+       break;
     }
 
-  free (function_name_buffer);
+  if (!gcov_is_eof ())
+    {
+      warning (error < 0 ? "`%s' has overflowed" : "`%s' is corrupted",
+              da_file_name);
+      htab_delete (counts_hash);
+    }
+  
   gcov_close ();
 }
 
@@ -304,38 +316,42 @@ get_coverage_counts (unsigned counter, unsigned expected,
       return NULL;
     }
 
-  elt.function_name
-    = (char *) IDENTIFIER_POINTER
-    (DECL_ASSEMBLER_NAME (current_function_decl));
+  elt.ident = current_function_funcdef_no + 1;
   elt.ctr = counter;
   entry = htab_find (counts_hash, &elt);
   if (!entry)
     {
-      warning ("No profile for function '%s' found.", elt.function_name);
+      warning ("no coverage for function '%s' found.", IDENTIFIER_POINTER
+              (DECL_ASSEMBLER_NAME (current_function_decl)));
       return 0;
     }
   
   if (expected != entry->summary.num
       || compute_checksum () != entry->checksum)
     {
-      warning ("profile mismatch for `%s'", elt.function_name);
+      warning ("coverage mismatch for `%s'", IDENTIFIER_POINTER
+              (DECL_ASSEMBLER_NAME (current_function_decl)));
       return NULL;
     }
-
+  
   if (summary)
     *summary = &entry->summary;
 
   return entry->counts;
 }
 
-/* Generate a MEM rtl to access COUNTER NO .  */
+/* Allocate NUM counters of type COUNTER. Returns non-zero if the
+   allocation succeeded.  */
 
-rtx
-coverage_counter_ref (unsigned counter, unsigned no)
+int
+coverage_counter_alloc (unsigned counter, unsigned num)
 {
-  enum machine_mode mode = mode_for_size (GCOV_TYPE_SIZE, MODE_INT, 0);
-  rtx ref;
-
+  if (no_coverage)
+    return 0;
+  
+  if (!num)
+    return 1;
+  
   if (!ctr_labels[counter])
     {
       /* Generate and save a copy of this so it can be shared.  */
@@ -344,15 +360,25 @@ coverage_counter_ref (unsigned counter, unsigned no)
       ASM_GENERATE_INTERNAL_LABEL (buf, "LPBX", counter + 1);
       ctr_labels[counter] = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf));
     }
-  if (no + 1 > fn_n_ctrs[counter])
-    {
-      fn_n_ctrs[counter] = no + 1;
-      fn_ctr_mask |= 1 << counter;
-    }
+  fn_b_ctrs[counter] = fn_n_ctrs[counter];
+  fn_n_ctrs[counter] += num;
+  fn_ctr_mask |= 1 << counter;
+  return 1;
+}
+
+/* Generate a MEM rtl to access COUNTER NO.  */
+
+rtx
+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;
 
-  no += prg_n_ctrs[counter];
-  ref = plus_constant (ctr_labels[counter],
-                      GCOV_TYPE_SIZE / BITS_PER_UNIT * no);
+  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 ());
 
@@ -406,6 +432,9 @@ compute_checksum ()
 int
 coverage_begin_output ()
 {
+  if (no_coverage)
+    return 0;
+  
   if (!bbg_function_announced)
     {
       const char *file = DECL_SOURCE_FILE (current_function_decl);
@@ -426,9 +455,10 @@ coverage_begin_output ()
       
       /* 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_unsigned (compute_checksum ());
       gcov_write_string (file);
       gcov_write_unsigned (line);
       gcov_write_length (offset);
@@ -456,21 +486,19 @@ coverage_end_function ()
     {
       struct function_list *item;
       
-      /* ??? Probably should re-use the existing struct function.  */
       item = xmalloc (sizeof (struct function_list));
       
       *functions_tail = item;
       functions_tail = &item->next;
        
       item->next = 0;
-      item->name = xstrdup (IDENTIFIER_POINTER
-                           (DECL_ASSEMBLER_NAME (current_function_decl)));
+      item->ident = current_function_funcdef_no + 1;
       item->checksum = compute_checksum ();
       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] = 0;
+         fn_n_ctrs[i] = fn_b_ctrs[i] = 0;
        }
       prg_ctr_mask |= fn_ctr_mask;
       fn_ctr_mask = 0;
@@ -486,16 +514,13 @@ build_fn_info_type (counters)
 {
   tree type = (*lang_hooks.types.make_type) (RECORD_TYPE);
   tree field, fields;
-  tree string_type =
-         build_pointer_type (build_qualified_type (char_type_node,
-                                                   TYPE_QUAL_CONST));
   tree array_type;
   
-  /* name */
-  fields = build_decl (FIELD_DECL, NULL_TREE, string_type);
+  /* ident */
+  fields = build_decl (FIELD_DECL, NULL_TREE, unsigned_intSI_type_node);
 
   /* checksum */
-  field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
+  field = build_decl (FIELD_DECL, NULL_TREE, unsigned_intSI_type_node);
   TREE_CHAIN (field) = fields;
   fields = field;
 
@@ -523,26 +548,19 @@ build_fn_info_value (function, type)
 {
   tree value = NULL_TREE;
   tree fields = TYPE_FIELDS (type);
-  size_t name_len = strlen (function->name);
-  tree fname = build_string (name_len + 1, function->name);
-  tree string_type =
-         build_pointer_type (build_qualified_type (char_type_node,
-                                                   TYPE_QUAL_CONST));
   unsigned ix;
   tree array_value = NULL_TREE;
   
-  /* name */
-  TREE_TYPE (fname) =
-         build_array_type (char_type_node,
-                           build_index_type (build_int_2 (name_len, 0)));
+  /* ident */
   value = tree_cons (fields,
-                    build1 (ADDR_EXPR, string_type, fname),
+                    convert (unsigned_intSI_type_node,
+                             build_int_2 (function->ident, 0)),
                     value);
   fields = TREE_CHAIN (fields);
   
   /* checksum */
   value = tree_cons (fields,
-                    convert (unsigned_type_node,
+                    convert (unsigned_intSI_type_node,
                              build_int_2 (function->checksum, 0)),
                     value);
   fields = TREE_CHAIN (fields);
@@ -572,15 +590,26 @@ build_ctr_info_type ()
 {
   tree type = (*lang_hooks.types.make_type) (RECORD_TYPE);
   tree field, fields = NULL_TREE;
-  
+  tree gcov_ptr_type = build_pointer_type (GCOV_TYPE_NODE);
+  tree gcov_merge_fn_type;
+
   /* counters */
-  field = build_decl (FIELD_DECL, NULL_TREE, unsigned_type_node);
+  field = build_decl (FIELD_DECL, NULL_TREE, unsigned_intSI_type_node);
   TREE_CHAIN (field) = fields;
   fields = field;
 
   /* values */
+  field = build_decl (FIELD_DECL, NULL_TREE, gcov_ptr_type);
+  TREE_CHAIN (field) = fields;
+  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 (make_signed_type (GCOV_TYPE_SIZE)));
+                     build_pointer_type (gcov_merge_fn_type));
   TREE_CHAIN (field) = fields;
   fields = field;
 
@@ -600,10 +629,11 @@ build_ctr_info_value (counter, type)
 {
   tree value = NULL_TREE;
   tree fields = TYPE_FIELDS (type);
+  tree fn;
 
   /* counters */
   value = tree_cons (fields,
-                    convert (unsigned_type_node,
+                    convert (unsigned_intSI_type_node,
                              build_int_2 (prg_n_ctrs[counter], 0)),
                     value);
   fields = TREE_CHAIN (fields);
@@ -627,6 +657,18 @@ build_ctr_info_value (counter, type)
     }
   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));
   
@@ -662,10 +704,10 @@ build_gcov_info ()
   const_type = build_qualified_type (type, TYPE_QUAL_CONST);
   
   /* Version ident */
-  field = build_decl (FIELD_DECL, NULL_TREE, long_unsigned_type_node);
+  field = build_decl (FIELD_DECL, NULL_TREE, unsigned_intSI_type_node);
   TREE_CHAIN (field) = fields;
   fields = field;
-  value = tree_cons (field, convert (long_unsigned_type_node,
+  value = tree_cons (field, convert (unsigned_intSI_type_node,
                                     build_int_2 (GCOV_VERSION, 0)),
                     value);
   
@@ -775,8 +817,9 @@ create_coverage ()
   char *ctor_name;
   tree ctor;
   rtx gcov_info_address;
-  int save_flag_inline_functions = flag_inline_functions;
 
+  no_coverage = 1; /* Disable any further coverage.  */
+  
   if (!prg_ctr_mask)
     return;
   
@@ -806,6 +849,7 @@ create_coverage ()
   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);
@@ -813,29 +857,20 @@ create_coverage ()
   current_function_decl = ctor;
   DECL_INITIAL (ctor) = error_mark_node;
   make_decl_rtl (ctor, NULL);
-  init_function_start (ctor, input_filename, lineno);
+  init_function_start (ctor, input_filename, input_line);
   (*lang_hooks.decls.pushlevel) (0);
   expand_function_start (ctor, 0);
-  cfun->arc_profile = 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 (input_filename, lineno, 0);
+  expand_function_end (input_filename, input_line, 0);
   (*lang_hooks.decls.poplevel) (1, 0, 1);
 
-  /* Since ctor isn't in the list of globals, it would never be emitted
-     when it's considered to be 'safe' for inlining, so turn off
-     flag_inline_functions.  */
-  flag_inline_functions = 0;
-
   rest_of_compilation (ctor);
 
-  /* Reset flag_inline_functions to its original value.  */
-  flag_inline_functions = save_flag_inline_functions;
-
   if (! quiet_flag)
     fflush (asm_out_file);
   current_function_decl = NULL_TREE;
@@ -854,16 +889,17 @@ coverage_init (filename)
 {
   int len = strlen (filename);
 
+  /* 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);
   
-  read_counts_file ();
-
-  /* Open the bbg output file.  */
+  /* Name of bbg file.  */
   bbg_file_name = (char *) xmalloc (len + strlen (GCOV_GRAPH_SUFFIX) + 1);
   strcpy (bbg_file_name, filename);
   strcat (bbg_file_name, GCOV_GRAPH_SUFFIX);
+
+  read_counts_file ();
 }
 
 /* Performs file-level cleanup.  Close graph file, generate coverage