tree-optimization/97709 - set abnormal flag when vectorizing live lanes
[gcc.git] / libgcc / libgcov-driver.c
index cdebb7473262665e4c589f70b3db0e0d08b1acd7..e53e4dc392a0cd6c4d0d90ff79385c1d5bb9086f 100644 (file)
@@ -1,6 +1,6 @@
 /* Routines required for instrumenting a program.  */
 /* Compile this one with gcc.  */
-/* Copyright (C) 1989-2018 Free Software Foundation, Inc.
+/* Copyright (C) 1989-2020 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -53,6 +53,8 @@ static void gcov_error_exit (void);
 
 #include "gcov-io.c"
 
+#define GCOV_PROF_PREFIX "libgcov profiling error:%s:"
+
 struct gcov_fn_buffer
 {
   struct gcov_fn_buffer *next;
@@ -151,12 +153,33 @@ buffer_fn_data (const char *filename, const struct gcov_info *gi_ptr,
   return &fn_buffer->next;
 
 fail:
-  gcov_error ("profiling:%s:Function %u %s %u \n", filename, fn_ix,
+  gcov_error (GCOV_PROF_PREFIX "Function %u %s %u \n", filename, fn_ix,
               len ? "cannot allocate" : "counter mismatch", len ? len : ix);
 
   return (struct gcov_fn_buffer **)free_fn_data (gi_ptr, fn_buffer, ix);
 }
 
+/* Convert VERSION into a string description and return the it.
+   BUFFER is used for storage of the string.  The code should be
+   aligned wit gcov-iov.c.  */
+
+static char *
+gcov_version_string (char *buffer, char version[4])
+{
+  if (version[0] < 'A' || version[0] > 'Z'
+      || version[1] < '0' || version[1] > '9'
+      || version[2] < '0' || version[2] > '9')
+    sprintf (buffer, "(unknown)");
+  else
+    {
+      unsigned major = 10 * (version[0] - 'A') + (version[1] - '0');
+      unsigned minor = version[2] - '0';
+      sprintf (buffer, "%u.%u (%s)", major, minor,
+              version[3] == '*' ? "release" : "experimental");
+    }
+  return buffer;
+}
+
 /* Check if VERSION of the info block PTR matches libgcov one.
    Return 1 on success, or zero in case of versions mismatch.
    If FILENAME is not NULL, its value used for reporting purposes
@@ -169,12 +192,16 @@ gcov_version (struct gcov_info *ptr, gcov_unsigned_t version,
   if (version != GCOV_VERSION)
     {
       char v[4], e[4];
+      char version_string[128], expected_string[128];
 
       GCOV_UNSIGNED2STRING (v, version);
       GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
 
-      gcov_error ("profiling:%s:Version mismatch - expected %.4s got %.4s\n",
-                  filename? filename : ptr->filename, e, v);
+      gcov_error (GCOV_PROF_PREFIX "Version mismatch - expected %s (%.4s) "
+                 "got %s (%.4s)\n",
+                 filename? filename : ptr->filename,
+                 gcov_version_string (expected_string, e), e,
+                 gcov_version_string (version_string, v), v);
       return 0;
     }
   return 1;
@@ -209,7 +236,7 @@ merge_one_data (const char *filename,
   if (length != gi_ptr->stamp)
     {
       /* Read from a different compilation.  Overwrite the file.  */
-      gcov_error ("profiling:%s:overwriting an existing profile data "
+      gcov_error (GCOV_PROF_PREFIX "overwriting an existing profile data "
                  "with a different timestamp\n", filename);
       return 0;
     }
@@ -274,22 +301,27 @@ merge_one_data (const char *filename,
           if (!merge)
             continue;
 
-          tag = gcov_read_unsigned ();
-          length = gcov_read_unsigned ();
-          if (tag != GCOV_TAG_FOR_COUNTER (t_ix)
-              || length != GCOV_TAG_COUNTER_LENGTH (ci_ptr->num))
-            goto read_mismatch;
-          (*merge) (ci_ptr->values, ci_ptr->num);
-          ci_ptr++;
-        }
+         tag = gcov_read_unsigned ();
+         int read_length = (int)gcov_read_unsigned ();
+         length = abs (read_length);
+         if (tag != GCOV_TAG_FOR_COUNTER (t_ix)
+             || (length != GCOV_TAG_COUNTER_LENGTH (ci_ptr->num)
+                 && t_ix != GCOV_COUNTER_V_TOPN
+                 && t_ix != GCOV_COUNTER_V_INDIR))
+           goto read_mismatch;
+         /* Merging with all zero counters does not make sense.  */
+         if (read_length > 0)
+           (*merge) (ci_ptr->values, ci_ptr->num);
+         ci_ptr++;
+       }
       if ((error = gcov_is_error ()))
-        goto read_error;
+       goto read_error;
     }
 
   if (tag)
     {
     read_mismatch:;
-      gcov_error ("profiling:%s:Merge mismatch for %s %u\n",
+      gcov_error (GCOV_PROF_PREFIX "Merge mismatch for %s %u\n",
                   filename, f_ix >= 0 ? "function" : "summary",
                   f_ix < 0 ? -1 - f_ix : f_ix);
       return -1;
@@ -297,11 +329,42 @@ merge_one_data (const char *filename,
   return 0;
 
 read_error:
-  gcov_error ("profiling:%s:%s merging\n", filename,
+  gcov_error (GCOV_PROF_PREFIX "%s merging\n", filename,
               error < 0 ? "Overflow": "Error");
   return -1;
 }
 
+/* Store all TOP N counters where each has a dynamic length.  */
+
+static void
+write_top_counters (const struct gcov_ctr_info *ci_ptr,
+                   unsigned t_ix,
+                   gcov_unsigned_t n_counts)
+{
+  unsigned counters = n_counts / GCOV_TOPN_MEM_COUNTERS;
+  gcc_assert (n_counts % GCOV_TOPN_MEM_COUNTERS == 0);
+  unsigned pair_total = 0;
+  for (unsigned i = 0; i < counters; i++)
+    pair_total += ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 1];
+  unsigned disk_size = GCOV_TOPN_DISK_COUNTERS * counters + 2 * pair_total;
+  gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
+                        GCOV_TAG_COUNTER_LENGTH (disk_size));
+
+  for (unsigned i = 0; i < counters; i++)
+    {
+      gcov_type pair_count = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 1];
+      gcov_write_counter (ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i]);
+      gcov_write_counter (pair_count);
+      gcov_type start = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 2];
+      for (struct gcov_kvp *node = (struct gcov_kvp *)(intptr_t)start;
+          node != NULL; node = node->next)
+       {
+         gcov_write_counter (node->value);
+         gcov_write_counter (node->count);
+       }
+    }
+}
+
 /* Write counters in GI_PTR and the summary in PRG to a gcda file. In
    the case of appending to an existing file, SUMMARY_POS will be non-zero.
    We will write the file starting from SUMMAY_POS.  */
@@ -354,20 +417,40 @@ write_one_data (const struct gcov_info *gi_ptr,
       ci_ptr = gfi_ptr->ctrs;
       for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
         {
-          gcov_unsigned_t n_counts;
-          gcov_type *c_ptr;
+         gcov_position_t n_counts;
 
-          if (!gi_ptr->merge[t_ix])
-            continue;
+         if (!gi_ptr->merge[t_ix])
+           continue;
 
-          n_counts = ci_ptr->num;
-          gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
-                                 GCOV_TAG_COUNTER_LENGTH (n_counts));
-          c_ptr = ci_ptr->values;
-          while (n_counts--)
-            gcov_write_counter (*c_ptr++);
-          ci_ptr++;
-        }
+         n_counts = ci_ptr->num;
+
+         if (t_ix == GCOV_COUNTER_V_TOPN || t_ix == GCOV_COUNTER_V_INDIR)
+           write_top_counters (ci_ptr, t_ix, n_counts);
+         else
+           {
+             /* Do not stream when all counters are zero.  */
+             int all_zeros = 1;
+             for (unsigned i = 0; i < n_counts; i++)
+               if (ci_ptr->values[i] != 0)
+                 {
+                   all_zeros = 0;
+                   break;
+                 }
+
+             if (all_zeros)
+               gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
+                                      GCOV_TAG_COUNTER_LENGTH (-n_counts));
+             else
+               {
+                 gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
+                                        GCOV_TAG_COUNTER_LENGTH (n_counts));
+                 for (unsigned i = 0; i < n_counts; i++)
+                   gcov_write_counter (ci_ptr->values[i]);
+               }
+           }
+
+         ci_ptr++;
+       }
       if (buffered)
         fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS);
     }
@@ -375,97 +458,6 @@ write_one_data (const struct gcov_info *gi_ptr,
   gcov_write_unsigned (0);
 }
 
-/* Helper function for merging summary.  */
-
-static void
-merge_summary (int run_counted, struct gcov_summary *summary,
-             gcov_type run_max)
-{
-  if (!run_counted)
-    {
-      summary->runs++;
-      summary->sum_max += run_max;
-    }
-}
-
-/* Sort N entries in VALUE_ARRAY in descending order.
-   Each entry in VALUE_ARRAY has two values. The sorting
-   is based on the second value.  */
-
-GCOV_LINKAGE  void
-gcov_sort_n_vals (gcov_type *value_array, int n)
-{
-  int j, k;
-
-  for (j = 2; j < n; j += 2)
-    {
-      gcov_type cur_ent[2];
-
-      cur_ent[0] = value_array[j];
-      cur_ent[1] = value_array[j + 1];
-      k = j - 2;
-      while (k >= 0 && value_array[k + 1] < cur_ent[1])
-        {
-          value_array[k + 2] = value_array[k];
-          value_array[k + 3] = value_array[k+1];
-          k -= 2;
-        }
-      value_array[k + 2] = cur_ent[0];
-      value_array[k + 3] = cur_ent[1];
-    }
-}
-
-/* Sort the profile counters for all indirect call sites. Counters
-   for each call site are allocated in array COUNTERS.  */
-
-static void
-gcov_sort_icall_topn_counter (const struct gcov_ctr_info *counters)
-{
-  int i;
-  gcov_type *values;
-  int n = counters->num;
-
-  gcc_assert (!(n % GCOV_ICALL_TOPN_NCOUNTS));
-  values = counters->values;
-
-  for (i = 0; i < n; i += GCOV_ICALL_TOPN_NCOUNTS)
-    {
-      gcov_type *value_array = &values[i + 1];
-      gcov_sort_n_vals (value_array, GCOV_ICALL_TOPN_NCOUNTS - 1);
-    }
-}
-
-/* Sort topn indirect_call profile counters in GI_PTR.  */
-
-static void
-gcov_sort_topn_counter_arrays (const struct gcov_info *gi_ptr)
-{
-  unsigned int i;
-  int f_ix;
-  const struct gcov_fn_info *gfi_ptr;
-  const struct gcov_ctr_info *ci_ptr;
-
-  if (!gi_ptr->merge[GCOV_COUNTER_ICALL_TOPNV]) 
-    return;
-
-  for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; f_ix++)
-    {
-      gfi_ptr = gi_ptr->functions[f_ix];
-      ci_ptr = gfi_ptr->ctrs;
-      for (i = 0; i < GCOV_COUNTERS; i++)
-        {
-          if (!gi_ptr->merge[i])
-            continue;
-          if (i == GCOV_COUNTER_ICALL_TOPNV)
-            {
-              gcov_sort_icall_topn_counter (ci_ptr);
-              break;
-            }
-          ci_ptr++;
-        }
-    }
-}
-
 /* Dump the coverage counts for one gcov_info object. We merge with existing
    counts when possible, to avoid growing the .da files ad infinitum. We use
    this program's checksum to make sure we only accumulate whole program
@@ -475,16 +467,14 @@ gcov_sort_topn_counter_arrays (const struct gcov_info *gi_ptr)
 
 static void
 dump_one_gcov (struct gcov_info *gi_ptr, struct gcov_filename *gf,
-              unsigned run_counted, gcov_type run_max)
+              unsigned run_counted ATTRIBUTE_UNUSED,
+              gcov_type run_max ATTRIBUTE_UNUSED)
 {
   struct gcov_summary summary = {};
   int error;
   gcov_unsigned_t tag;
-
   fn_buffer = 0;
 
-  gcov_sort_topn_counter_arrays (gi_ptr);
-
   error = gcov_exit_open_gcda_file (gi_ptr, gf);
   if (error == -1)
     return;
@@ -495,7 +485,8 @@ dump_one_gcov (struct gcov_info *gi_ptr, struct gcov_filename *gf,
       /* Merge data from file.  */
       if (tag != GCOV_DATA_MAGIC)
         {
-          gcov_error ("profiling:%s:Not a gcov data file\n", gf->filename);
+         gcov_error (GCOV_PROF_PREFIX "Not a gcov data file\n",
+                     gf->filename);
           goto read_fatal;
         }
       error = merge_one_data (gf->filename, gi_ptr, &summary);
@@ -505,7 +496,15 @@ dump_one_gcov (struct gcov_info *gi_ptr, struct gcov_filename *gf,
 
   gcov_rewrite ();
 
-  merge_summary (run_counted, &summary, run_max);
+#if !IN_GCOV_TOOL
+  if (!run_counted)
+    {
+      summary.runs++;
+      summary.sum_max += run_max;
+    }
+#else
+  summary = gi_ptr->summary;
+#endif
 
   write_one_data (gi_ptr, &summary);
   /* fall through */
@@ -516,8 +515,8 @@ read_fatal:;
 
   if ((error = gcov_close ()))
     gcov_error (error  < 0 ?
-                "profiling:%s:Overflow writing\n" :
-                "profiling:%s:Error writing\n",
+               GCOV_PROF_PREFIX "Overflow writing\n" :
+               GCOV_PROF_PREFIX "Error writing\n",
                 gf->filename);
 }
 
@@ -589,6 +588,12 @@ struct gcov_root __gcov_root;
 struct gcov_master __gcov_master = 
   {GCOV_VERSION, 0};
 
+/* Pool of pre-allocated gcov_kvp strutures.  */
+struct gcov_kvp __gcov_kvp_pool[GCOV_PREALLOCATED_KVP];
+
+/* Index to first free gcov_kvp in the pool.  */
+unsigned __gcov_kvp_pool_index;
+
 void
 __gcov_exit (void)
 {