Add new *_atomic counter update function
authorMartin Liska <mliska@suse.cz>
Wed, 10 Aug 2016 13:14:56 +0000 (15:14 +0200)
committerMartin Liska <marxin@gcc.gnu.org>
Wed, 10 Aug 2016 13:14:56 +0000 (13:14 +0000)
PR gcov-profile/58306
* Makefile.in: New functions (modules) are added.
* libgcov-profiler.c (__gcov_interval_profiler_atomic): New
function.
(__gcov_pow2_profiler_atomic): New function.
(__gcov_one_value_profiler_body): New argument is instroduced.
(__gcov_one_value_profiler): Call with the new argument.
(__gcov_one_value_profiler_atomic): Likewise.
(__gcov_indirect_call_profiler_v2): Likewise.
(__gcov_time_profiler_atomic): New function.
(__gcov_average_profiler_atomic): Likewise.
(__gcov_ior_profiler_atomic): Likewise.
* libgcov.h: Declare the aforementioned functions.
PR gcov-profile/58306
* gcc.dg/tree-prof/val-profiler-threads-1.c: New test.
PR gcov-profile/58306
* tree-profile.c (gimple_init_edge_profiler): Create conditionally
atomic variants of profile update functions.

From-SVN: r239324

gcc/ChangeLog
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/tree-prof/val-profiler-threads-1.c [new file with mode: 0644]
gcc/tree-profile.c
libgcc/ChangeLog
libgcc/Makefile.in
libgcc/libgcov-profiler.c
libgcc/libgcov.h

index cdf8b7740bd498c2352acbcddc683b5bd29603c3..7dbe48608f0493026b7dd8df01514e21cb246a65 100644 (file)
@@ -1,3 +1,9 @@
+2016-08-10  Martin Liska  <mliska@suse.cz>
+
+       PR gcov-profile/58306
+       * tree-profile.c (gimple_init_edge_profiler): Create conditionally
+       atomic variants of profile update functions.
+
 2016-08-10  Martin Liska  <mliska@suse.cz>
 
        Cherry picked (and modified) from google-4_7 branch
index 20bd72a0dbc0c13e0df56f5ba413abd440f1677d..c9f58fec41184c5d26c83644f97e02f58ed7ad59 100644 (file)
@@ -1,3 +1,8 @@
+2016-08-10  Martin Liska  <mliska@suse.cz>
+
+       PR gcov-profile/58306
+       * gcc.dg/tree-prof/val-profiler-threads-1.c: New test.
+
 2016-08-10  Martin Liska  <mliska@suse.cz>
 
        * g++.dg/gcov/gcov-threads-1.C: New test.
diff --git a/gcc/testsuite/gcc.dg/tree-prof/val-profiler-threads-1.c b/gcc/testsuite/gcc.dg/tree-prof/val-profiler-threads-1.c
new file mode 100644 (file)
index 0000000..e9b04a0
--- /dev/null
@@ -0,0 +1,41 @@
+/* { dg-options "-O0 -pthread -fprofile-update=atomic" } */
+#include <pthread.h>
+
+#define NUM_THREADS    8
+#define SIZE 1024
+#define ITERATIONS (1000 * 1000)
+
+char buffer[SIZE];
+char buffer2[SIZE];
+
+void *copy_memory(char *dst, char *src, unsigned size)
+{
+   for (unsigned i = 0; i < ITERATIONS; i++)
+   {
+     dst[size % 10] = src[size % 20];
+   }
+}
+
+void *foo(void *d)
+{
+  copy_memory (buffer, buffer2, SIZE);
+}
+
+int main(int argc, char *argv[])
+{
+   pthread_t threads[NUM_THREADS];
+   int rc;
+   long t;
+   for(t=0;t<NUM_THREADS;t++){
+     rc = pthread_create(&threads[t], NULL, foo, 0);
+     if (rc){
+        return 1;
+       }
+     }
+
+   int retval;
+   for(t=0;t<NUM_THREADS;t++)
+     pthread_join (threads[t], (void**)&retval);
+
+   return buffer[10];
+}
index 740f7ab7d5a56d06c195dfe93cba81ae0ebb3955..fdf0201f2e405ccc39c13b97da8a610511b94679 100644 (file)
@@ -128,9 +128,13 @@ gimple_init_edge_profiler (void)
   tree average_profiler_fn_type;
   tree time_profiler_fn_type;
   const char *profiler_fn_name;
+  const char *fn_name;
 
   if (!gcov_type_node)
     {
+      const char *fn_suffix
+       = flag_profile_update == PROFILE_UPDATE_ATOMIC ? "_atomic" : "";
+
       gcov_type_node = get_gcov_type ();
       gcov_type_ptr = build_pointer_type (gcov_type_node);
 
@@ -140,9 +144,10 @@ gimple_init_edge_profiler (void)
                                          gcov_type_ptr, gcov_type_node,
                                          integer_type_node,
                                          unsigned_type_node, NULL_TREE);
-      tree_interval_profiler_fn
-             = build_fn_decl ("__gcov_interval_profiler",
-                                    interval_profiler_fn_type);
+      fn_name = concat ("__gcov_interval_profiler", fn_suffix, NULL);
+      tree_interval_profiler_fn = build_fn_decl (fn_name,
+                                                interval_profiler_fn_type);
+      free (CONST_CAST (char *, fn_name));
       TREE_NOTHROW (tree_interval_profiler_fn) = 1;
       DECL_ATTRIBUTES (tree_interval_profiler_fn)
        = tree_cons (get_identifier ("leaf"), NULL,
@@ -153,8 +158,9 @@ gimple_init_edge_profiler (void)
              = build_function_type_list (void_type_node,
                                          gcov_type_ptr, gcov_type_node,
                                          NULL_TREE);
-      tree_pow2_profiler_fn = build_fn_decl ("__gcov_pow2_profiler",
-                                                  pow2_profiler_fn_type);
+      fn_name = concat ("__gcov_pow2_profiler", fn_suffix, NULL);
+      tree_pow2_profiler_fn = build_fn_decl (fn_name, pow2_profiler_fn_type);
+      free (CONST_CAST (char *, fn_name));
       TREE_NOTHROW (tree_pow2_profiler_fn) = 1;
       DECL_ATTRIBUTES (tree_pow2_profiler_fn)
        = tree_cons (get_identifier ("leaf"), NULL,
@@ -165,9 +171,10 @@ gimple_init_edge_profiler (void)
              = build_function_type_list (void_type_node,
                                          gcov_type_ptr, gcov_type_node,
                                          NULL_TREE);
-      tree_one_value_profiler_fn
-             = build_fn_decl ("__gcov_one_value_profiler",
-                                    one_value_profiler_fn_type);
+      fn_name = concat ("__gcov_one_value_profiler", fn_suffix, NULL);
+      tree_one_value_profiler_fn = build_fn_decl (fn_name,
+                                                 one_value_profiler_fn_type);
+      free (CONST_CAST (char *, fn_name));
       TREE_NOTHROW (tree_one_value_profiler_fn) = 1;
       DECL_ATTRIBUTES (tree_one_value_profiler_fn)
        = tree_cons (get_identifier ("leaf"), NULL,
@@ -197,9 +204,9 @@ gimple_init_edge_profiler (void)
       time_profiler_fn_type
               = build_function_type_list (void_type_node,
                                          gcov_type_ptr, NULL_TREE);
-      tree_time_profiler_fn
-             = build_fn_decl ("__gcov_time_profiler",
-                                    time_profiler_fn_type);
+      fn_name = concat ("__gcov_time_profiler", fn_suffix, NULL);
+      tree_time_profiler_fn = build_fn_decl (fn_name, time_profiler_fn_type);
+      free (CONST_CAST (char *, fn_name));
       TREE_NOTHROW (tree_time_profiler_fn) = 1;
       DECL_ATTRIBUTES (tree_time_profiler_fn)
        = tree_cons (get_identifier ("leaf"), NULL,
@@ -209,16 +216,17 @@ gimple_init_edge_profiler (void)
       average_profiler_fn_type
              = build_function_type_list (void_type_node,
                                          gcov_type_ptr, gcov_type_node, NULL_TREE);
-      tree_average_profiler_fn
-             = build_fn_decl ("__gcov_average_profiler",
-                                    average_profiler_fn_type);
+      fn_name = concat ("__gcov_average_profiler", fn_suffix, NULL);
+      tree_average_profiler_fn = build_fn_decl (fn_name,
+                                               average_profiler_fn_type);
+      free (CONST_CAST (char *, fn_name));
       TREE_NOTHROW (tree_average_profiler_fn) = 1;
       DECL_ATTRIBUTES (tree_average_profiler_fn)
        = tree_cons (get_identifier ("leaf"), NULL,
                     DECL_ATTRIBUTES (tree_average_profiler_fn));
-      tree_ior_profiler_fn
-             = build_fn_decl ("__gcov_ior_profiler",
-                                    average_profiler_fn_type);
+      fn_name = concat ("__gcov_ior_profiler", fn_suffix, NULL);
+      tree_ior_profiler_fn = build_fn_decl (fn_name, average_profiler_fn_type);
+      free (CONST_CAST (char *, fn_name));
       TREE_NOTHROW (tree_ior_profiler_fn) = 1;
       DECL_ATTRIBUTES (tree_ior_profiler_fn)
        = tree_cons (get_identifier ("leaf"), NULL,
index 866d14a17a3ae82b18c279c0a382115804bc1487..49186faa70ff9997656dd128a0619a1498cfbead 100644 (file)
@@ -1,3 +1,19 @@
+2016-08-10  Martin Liska  <mliska@suse.cz>
+
+       PR gcov-profile/58306
+       * Makefile.in: New functions (modules) are added.
+       * libgcov-profiler.c (__gcov_interval_profiler_atomic): New
+       function.
+       (__gcov_pow2_profiler_atomic): New function.
+       (__gcov_one_value_profiler_body): New argument is instroduced.
+       (__gcov_one_value_profiler): Call with the new argument.
+       (__gcov_one_value_profiler_atomic): Likewise.
+       (__gcov_indirect_call_profiler_v2): Likewise.
+       (__gcov_time_profiler_atomic): New function.
+       (__gcov_average_profiler_atomic): Likewise.
+       (__gcov_ior_profiler_atomic): Likewise.
+       * libgcov.h: Declare the aforementioned functions.
+
 2016-08-09  Martin Liska  <mliska@suse.cz>
 
        * libgcov-util.c: Fix typo and GNU coding style.
index efaf7f7bf1a260f29628219b9d25421804c2ea42..ba37c657baf3658e9d779886567e374b2f8ab275 100644 (file)
@@ -858,10 +858,18 @@ include $(iterator)
 
 LIBGCOV_MERGE = _gcov_merge_add _gcov_merge_single _gcov_merge_delta   \
        _gcov_merge_ior _gcov_merge_time_profile _gcov_merge_icall_topn
-LIBGCOV_PROFILER = _gcov_interval_profiler _gcov_pow2_profiler         \
+LIBGCOV_PROFILER = _gcov_interval_profiler                             \
+       _gcov_interval_profiler_atomic                                  \
+       _gcov_pow2_profiler                                             \
+       _gcov_pow2_profiler_atomic                                      \
        _gcov_one_value_profiler                                        \
-       _gcov_average_profiler _gcov_ior_profiler                       \
-       _gcov_indirect_call_profiler_v2 _gcov_time_profiler             \
+       _gcov_one_value_profiler_atomic                                 \
+       _gcov_average_profiler                                          \
+       _gcov_average_profiler_atomic                                   \
+       _gcov_ior_profiler                                              \
+       _gcov_ior_profiler_atomic                                       \
+       _gcov_indirect_call_profiler_v2                                 \
+       _gcov_time_profiler                                             \
        _gcov_indirect_call_topn_profiler
 LIBGCOV_INTERFACE = _gcov_dump _gcov_flush _gcov_fork                  \
        _gcov_execl _gcov_execlp                                        \
index a99d93bb412034f031e57cdc3952d240ba5fd702..70a821dc6257eb98e4aa89df0a9bbd5c4d4241c4 100644 (file)
@@ -46,6 +46,26 @@ __gcov_interval_profiler (gcov_type *counters, gcov_type value,
 }
 #endif
 
+#ifdef L_gcov_interval_profiler_atomic
+/* If VALUE is in interval <START, START + STEPS - 1>, then increases the
+   corresponding counter in COUNTERS.  If the VALUE is above or below
+   the interval, COUNTERS[STEPS] or COUNTERS[STEPS + 1] is increased
+   instead.  Function is thread-safe.  */
+
+void
+__gcov_interval_profiler_atomic (gcov_type *counters, gcov_type value,
+                                int start, unsigned steps)
+{
+  gcov_type delta = value - start;
+  if (delta < 0)
+    __atomic_fetch_add (&counters[steps + 1], 1, MEMMODEL_RELAXED);
+  else if (delta >= steps)
+    __atomic_fetch_add (&counters[steps], 1, MEMMODEL_RELAXED);
+  else
+    __atomic_fetch_add (&counters[delta], 1, MEMMODEL_RELAXED);
+}
+#endif
+
 #ifdef L_gcov_pow2_profiler
 /* If VALUE is a power of two, COUNTERS[1] is incremented.  Otherwise
    COUNTERS[0] is incremented.  */
@@ -60,6 +80,21 @@ __gcov_pow2_profiler (gcov_type *counters, gcov_type value)
 }
 #endif
 
+#ifdef L_gcov_pow2_profiler_atomic
+/* If VALUE is a power of two, COUNTERS[1] is incremented.  Otherwise
+   COUNTERS[0] is incremented.  Function is thread-safe.  */
+
+void
+__gcov_pow2_profiler_atomic (gcov_type *counters, gcov_type value)
+{
+  if (value == 0 || (value & (value - 1)))
+    __atomic_fetch_add (&counters[0], 1, MEMMODEL_RELAXED);
+  else
+    __atomic_fetch_add (&counters[1], 1, MEMMODEL_RELAXED);
+}
+#endif
+
+
 /* Tries to determine the most common value among its inputs.  Checks if the
    value stored in COUNTERS[0] matches VALUE.  If this is the case, COUNTERS[1]
    is incremented.  If this is not the case and COUNTERS[1] is not zero,
@@ -68,10 +103,12 @@ __gcov_pow2_profiler (gcov_type *counters, gcov_type value)
    function is called more than 50% of the time with one value, this value
    will be in COUNTERS[0] in the end.
 
-   In any case, COUNTERS[2] is incremented.  */
+   In any case, COUNTERS[2] is incremented.  If USE_ATOMIC is set to 1,
+   COUNTERS[2] is updated with an atomic instruction.  */
 
 static inline void
-__gcov_one_value_profiler_body (gcov_type *counters, gcov_type value)
+__gcov_one_value_profiler_body (gcov_type *counters, gcov_type value,
+                               int use_atomic)
 {
   if (value == counters[0])
     counters[1]++;
@@ -82,14 +119,36 @@ __gcov_one_value_profiler_body (gcov_type *counters, gcov_type value)
     }
   else
     counters[1]--;
-  counters[2]++;
+
+  if (use_atomic)
+    __atomic_fetch_add (&counters[2], 1, MEMMODEL_RELAXED);
+  else
+    counters[2]++;
 }
 
 #ifdef L_gcov_one_value_profiler
 void
 __gcov_one_value_profiler (gcov_type *counters, gcov_type value)
 {
-  __gcov_one_value_profiler_body (counters, value);
+  __gcov_one_value_profiler_body (counters, value, 0);
+}
+#endif
+
+#ifdef L_gcov_one_value_profiler_atomic
+
+/* Update one value profilers (COUNTERS) for a given VALUE.
+
+   CAVEAT: Following function is not thread-safe, only total number
+   of executions (COUNTERS[2]) is update with an atomic instruction.
+   Problem is that one cannot atomically update two counters
+   (COUNTERS[0] and COUNTERS[1]), for more information please read
+   following email thread:
+   https://gcc.gnu.org/ml/gcc-patches/2016-08/msg00024.html.  */
+
+void
+__gcov_one_value_profiler_atomic (gcov_type *counters, gcov_type value)
+{
+  __gcov_one_value_profiler_body (counters, value, 1);
 }
 #endif
 
@@ -265,7 +324,7 @@ __gcov_indirect_call_profiler_v2 (gcov_type value, void* cur_func)
   if (cur_func == __gcov_indirect_call_callee
       || (__LIBGCC_VTABLE_USES_DESCRIPTORS__ && __gcov_indirect_call_callee
           && *(void **) cur_func == *(void **) __gcov_indirect_call_callee))
-    __gcov_one_value_profiler_body (__gcov_indirect_call_counters, value);
+    __gcov_one_value_profiler_body (__gcov_indirect_call_counters, value, 0);
 }
 #endif
 
@@ -282,8 +341,19 @@ __gcov_time_profiler (gcov_type* counters)
   if (!counters[0])
     counters[0] = ++function_counter;
 }
+
+/* Sets corresponding COUNTERS if there is no value.
+   Function is thread-safe.  */
+
+void
+__gcov_time_profiler_atomic (gcov_type* counters)
+{
+  if (!counters[0])
+    counters[0] = __atomic_add_fetch (&function_counter, 1, MEMMODEL_RELAXED);
+}
 #endif
 
+
 #ifdef L_gcov_average_profiler
 /* Increase corresponding COUNTER by VALUE.  FIXME: Perhaps we want
    to saturate up.  */
@@ -296,6 +366,18 @@ __gcov_average_profiler (gcov_type *counters, gcov_type value)
 }
 #endif
 
+#ifdef L_gcov_average_profiler_atomic
+/* Increase corresponding COUNTER by VALUE.  FIXME: Perhaps we want
+   to saturate up.  Function is thread-safe.  */
+
+void
+__gcov_average_profiler_atomic (gcov_type *counters, gcov_type value)
+{
+  __atomic_fetch_add (&counters[0], value, MEMMODEL_RELAXED);
+  __atomic_fetch_add (&counters[1], 1, MEMMODEL_RELAXED);
+}
+#endif
+
 #ifdef L_gcov_ior_profiler
 /* Bitwise-OR VALUE into COUNTER.  */
 
@@ -306,4 +388,15 @@ __gcov_ior_profiler (gcov_type *counters, gcov_type value)
 }
 #endif
 
+#ifdef L_gcov_ior_profiler_atomic
+/* Bitwise-OR VALUE into COUNTER.  Function is thread-safe.  */
+
+void
+__gcov_ior_profiler_atomic (gcov_type *counters, gcov_type value)
+{
+  __atomic_fetch_or (&counters[0], value, MEMMODEL_RELAXED);
+}
+#endif
+
+
 #endif /* inhibit_libc */
index 80f13e267774ff630fec9c2ee7b9d48e74fdae1b..25147de2be739b9ced5596699fa7b2a9da84b4b7 100644 (file)
@@ -268,12 +268,19 @@ extern void __gcov_merge_icall_topn (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
 
 /* The profiler functions.  */
 extern void __gcov_interval_profiler (gcov_type *, gcov_type, int, unsigned);
+extern void __gcov_interval_profiler_atomic (gcov_type *, gcov_type, int,
+                                            unsigned);
 extern void __gcov_pow2_profiler (gcov_type *, gcov_type);
+extern void __gcov_pow2_profiler_atomic (gcov_type *, gcov_type);
 extern void __gcov_one_value_profiler (gcov_type *, gcov_type);
+extern void __gcov_one_value_profiler_atomic (gcov_type *, gcov_type);
 extern void __gcov_indirect_call_profiler_v2 (gcov_type, void *);
 extern void __gcov_time_profiler (gcov_type *);
+extern void __gcov_time_profiler_atomic (gcov_type *);
 extern void __gcov_average_profiler (gcov_type *, gcov_type);
+extern void __gcov_average_profiler_atomic (gcov_type *, gcov_type);
 extern void __gcov_ior_profiler (gcov_type *, gcov_type);
+extern void __gcov_ior_profiler_atomic (gcov_type *, gcov_type);
 extern void __gcov_indirect_call_topn_profiler (gcov_type, void *);
 extern void gcov_sort_n_vals (gcov_type *, int);