An inline 64-bit counter update is several instructions at every arc.
On an embedded target with a tight memory budget that bloats .text.
Emit a call to __gcov_increment_fn instead, so the update lives once
in libgcov and the text section stays smaller.
The libgcov definition is weak so a strong user definition overrides it.
That override can also implement any existing update method or their
combinations in runtime.

gcc/ChangeLog:

        * common.opt (fprofile-update=): Add function-call.
        * coretypes.h (enum profile_update): Add PROFILE_UPDATE_FUNCTION_CALL.
        * doc/invoke.texi (-fprofile-update): Document function-call.
        * gcc.cc (cc1_options): Do not replace an explicit function-call
        with prefer-atomic when -pthread is present.
        * tree-profile.cc (tree_gcov_inc_fn): New.
        (enum counter_update_method): Add COUNTER_UPDATE_FUNCTION_CALL.
        (gimple_init_gcov_profiler): Declare __gcov_increment_fn.
        (gen_counter_update): Call it.
        (tree_profiling): Select COUNTER_UPDATE_FUNCTION_CALL.

gcc/testsuite/ChangeLog:

        * gcc.dg/profile-update-function-call.c: New test.

libgcc/ChangeLog:

        * Makefile.in (LIBGCOV_PROFILER): Add _gcov_increment_fn.
        * libgcov-profiler.c (__gcov_increment_fn): New weak function.
        * libgcov.h (__gcov_increment_fn): Declare.
---
 gcc/common.opt                                |  5 +++-
 gcc/coretypes.h                               |  3 +-
 gcc/doc/invoke.texi                           | 13 +++++++-
 gcc/gcc.cc                                    |  3 +-
 .../gcc.dg/profile-update-function-call.c     | 19 ++++++++++++
 gcc/tree-profile.cc                           | 30 +++++++++++++++++--
 libgcc/Makefile.in                            |  3 +-
 libgcc/libgcov-profiler.c                     | 14 +++++++++
 libgcc/libgcov.h                              |  1 +
 9 files changed, 83 insertions(+), 8 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/profile-update-function-call.c

diff --git a/gcc/common.opt b/gcc/common.opt
index 69a55b8d40d..90766f38cd5 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -2680,7 +2680,7 @@ Enable correction of flow inconsistent profile data input.
 
 fprofile-update=
 Common Joined RejectNegative Enum(profile_update) Var(flag_profile_update) 
Init(PROFILE_UPDATE_SINGLE)
--fprofile-update=[single|atomic|prefer-atomic] Set the profile update method.
+-fprofile-update=[single|atomic|prefer-atomic|function-call]   Set the profile 
update method.
 
 fprofile-filter-files=
 Common Joined RejectNegative Var(flag_profile_filter_files)
@@ -2718,6 +2718,9 @@ Enum(profile_update) String(atomic) 
Value(PROFILE_UPDATE_ATOMIC)
 EnumValue
 Enum(profile_update) String(prefer-atomic) Value(PROFILE_UPDATE_PREFER_ATOMIC)
 
+EnumValue
+Enum(profile_update) String(function-call) Value(PROFILE_UPDATE_FUNCTION_CALL)
+
 fprofile-prefix-path=
 Common Joined RejectNegative Var(profile_prefix_path)
 Remove prefix from absolute path before mangling name for -fprofile-generate= 
and -fprofile-use=.
diff --git a/gcc/coretypes.h b/gcc/coretypes.h
index 5cc602ed7e5..874b528c663 100644
--- a/gcc/coretypes.h
+++ b/gcc/coretypes.h
@@ -239,7 +239,8 @@ enum offload_abi {
 enum profile_update {
   PROFILE_UPDATE_SINGLE,
   PROFILE_UPDATE_ATOMIC,
-  PROFILE_UPDATE_PREFER_ATOMIC
+  PROFILE_UPDATE_PREFER_ATOMIC,
+  PROFILE_UPDATE_FUNCTION_CALL
 };
 
 /* Type of profile reproducibility methods.  */
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 66d80f17e74..1478fcb09e4 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -17407,7 +17407,7 @@ See also @option{-ffile-prefix-map} and 
@option{-fcanon-prefix-map}.
 
 Alter the update method for an application instrumented for profile
 feedback based optimization.  The @var{method} argument should be one of
-@samp{single}, @samp{atomic} or @samp{prefer-atomic}.
+@samp{single}, @samp{atomic}, @samp{prefer-atomic} or @samp{function-call}.
 The first one is useful for single-threaded applications,
 while the second one prevents profile corruption by emitting thread-safe code.
 
@@ -17419,6 +17419,17 @@ when supported by a target, or to @samp{single} 
otherwise.  The GCC driver
 automatically selects @samp{prefer-atomic} when @option{-pthread}
 is present in the command line, otherwise the default method is @samp{single}.
 
+@samp{function-call} updates each counter with a call to
+@code{__gcov_increment_fn} instead of an inline increment.  The 64-bit
+add then lives once in @file{libgcov}, which keeps the text section
+smaller on targets where an inline 64-bit update is several instructions.
+Each site still passes the address of its counter.  @file{libgcov}
+provides a weak @code{__gcov_increment_fn}.  A strong definition of
+@code{gcov_type __gcov_increment_fn (gcov_type *)} overrides it.
+The compiler treats that call as @code{leaf} and @code{nothrow}, so the
+override must not throw and must not call back into the instrumented
+translation unit.
+
 If @samp{atomic} is selected, then the profile information is updated using
 atomic operations on a best-effort basis.  Ideally, the profile information is
 updated through atomic operations in hardware.  If the target platform does not
diff --git a/gcc/gcc.cc b/gcc/gcc.cc
index 973a3402330..7bf1de9cd05 100644
--- a/gcc/gcc.cc
+++ b/gcc/gcc.cc
@@ -1329,7 +1329,8 @@ static const char *cc1_options =
  %{coverage:-fprofile-arcs -ftest-coverage}\
  
%{fprofile-arcs|fcondition-coverage|fpath-coverage|fprofile-generate*|coverage:\
    %{!fprofile-update=single:\
-     %{pthread:-fprofile-update=prefer-atomic}}}";
+     %{!fprofile-update=function-call:\
+       %{pthread:-fprofile-update=prefer-atomic}}}}";
 
 static const char *asm_options =
 "%{-target-help:%:print-asm-header()} "
diff --git a/gcc/testsuite/gcc.dg/profile-update-function-call.c 
b/gcc/testsuite/gcc.dg/profile-update-function-call.c
new file mode 100644
index 00000000000..9cac6c1a36f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/profile-update-function-call.c
@@ -0,0 +1,19 @@
+/* { dg-require-effective-target global_constructor } */
+/* { dg-options "-O2 -fprofile-arcs -ftest-coverage 
-fprofile-update=function-call -fdump-tree-optimized" } */
+
+int
+foo (int x)
+{
+  if (x)
+    return 1;
+  return 0;
+}
+
+int
+main (void)
+{
+  return foo (1);
+}
+
+/* { dg-final { scan-tree-dump "__gcov_increment_fn" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "PROF_edge_counter" "optimized" } } */
diff --git a/gcc/tree-profile.cc b/gcc/tree-profile.cc
index 882a31a6806..955e3b74feb 100644
--- a/gcc/tree-profile.cc
+++ b/gcc/tree-profile.cc
@@ -68,6 +68,7 @@ static GTY(()) tree tree_topn_values_profiler_fn;
 static GTY(()) tree tree_indirect_call_profiler_fn;
 static GTY(()) tree tree_average_profiler_fn;
 static GTY(()) tree tree_ior_profiler_fn;
+static GTY(()) tree tree_gcov_inc_fn;
 static GTY(()) tree tree_time_profiler_counter;
 
 
@@ -100,12 +101,16 @@ static GTY(()) tree ic_tuple_callee_field;
 
    If the target does not support atomic operations in hardware, however,  it
    supports libatomic, then all updates are carried out by libatomic calls
-   (COUNTER_UPDATE_ATOMIC_BUILTIN).  */
+   (COUNTER_UPDATE_ATOMIC_BUILTIN).
+
+   -fprofile-update=function-call emits a call to __gcov_increment_fn instead
+   of an inline increment (COUNTER_UPDATE_FUNCTION_CALL).  */
 enum counter_update_method {
   COUNTER_UPDATE_SINGLE_THREAD,
   COUNTER_UPDATE_ATOMIC_BUILTIN,
   COUNTER_UPDATE_ATOMIC_SPLIT,
-  COUNTER_UPDATE_ATOMIC_PARTIAL
+  COUNTER_UPDATE_ATOMIC_PARTIAL,
+  COUNTER_UPDATE_FUNCTION_CALL
 };
 
 static counter_update_method counter_update = COUNTER_UPDATE_SINGLE_THREAD;
@@ -1415,6 +1420,16 @@ gimple_init_gcov_profiler (void)
        = tree_cons (get_identifier ("leaf"), NULL,
                     DECL_ATTRIBUTES (tree_ior_profiler_fn));
 
+      /* gcov_type (*) (gcov_type *)  */
+      tree inc_fn_type
+       = build_function_type_list (gcov_type_node, gcov_type_ptr, NULL_TREE);
+      tree_gcov_inc_fn = build_fn_decl ("__gcov_increment_fn", inc_fn_type);
+      /* User override must not throw or call back into this unit.  */
+      TREE_NOTHROW (tree_gcov_inc_fn) = 1;
+      DECL_ATTRIBUTES (tree_gcov_inc_fn)
+       = tree_cons (get_identifier ("leaf"), NULL,
+                    DECL_ATTRIBUTES (tree_gcov_inc_fn));
+
       /* LTO streamer needs assembler names.  Because we create these decls
         late, we need to initialize them by hand.  */
       DECL_ASSEMBLER_NAME (tree_interval_profiler_fn);
@@ -1423,6 +1438,7 @@ gimple_init_gcov_profiler (void)
       DECL_ASSEMBLER_NAME (tree_indirect_call_profiler_fn);
       DECL_ASSEMBLER_NAME (tree_average_profiler_fn);
       DECL_ASSEMBLER_NAME (tree_ior_profiler_fn);
+      DECL_ASSEMBLER_NAME (tree_gcov_inc_fn);
     }
 }
 
@@ -1463,7 +1479,13 @@ gen_counter_update (gimple_stmt_iterator *gsi, tree 
counter, tree result,
   tree one = build_int_cst (type, 1);
   tree relaxed = build_int_cst (integer_type_node, MEMMODEL_RELAXED);
 
-  if (counter_update == COUNTER_UPDATE_ATOMIC_BUILTIN
+  if (counter_update == COUNTER_UPDATE_FUNCTION_CALL)
+    {
+      /* __gcov_increment_fn (&counter);  */
+      gcall *call = gimple_build_call (tree_gcov_inc_fn, 1, addr);
+      gen_assign_counter_update (gsi, call, tree_gcov_inc_fn, result, name);
+    }
+  else if (counter_update == COUNTER_UPDATE_ATOMIC_BUILTIN
       || (result && counter_update == COUNTER_UPDATE_ATOMIC_SPLIT))
     {
       /* __atomic_fetch_add (&counter, 1, MEMMODEL_RELAXED); */
@@ -1974,6 +1996,8 @@ tree_profiling (void)
       else
        counter_update = COUNTER_UPDATE_ATOMIC_BUILTIN;
     }
+  else if (flag_profile_update == PROFILE_UPDATE_FUNCTION_CALL)
+    counter_update = COUNTER_UPDATE_FUNCTION_CALL;
 
   /* This is a small-ipa pass that gets called only once, from
      cgraphunit.cc:ipa_passes().  */
diff --git a/libgcc/Makefile.in b/libgcc/Makefile.in
index 59df657e282..8bdb45d52c5 100644
--- a/libgcc/Makefile.in
+++ b/libgcc/Makefile.in
@@ -918,7 +918,8 @@ LIBGCOV_PROFILER = _gcov_interval_profiler                  
        \
        _gcov_ior_profiler                                              \
        _gcov_ior_profiler_atomic                                       \
        _gcov_indirect_call_profiler_v4                                 \
-       _gcov_time_profiler
+       _gcov_time_profiler                                             \
+       _gcov_increment_fn
 LIBGCOV_INTERFACE = _gcov_dump _gcov_fork                              \
        _gcov_execl _gcov_execlp                                        \
        _gcov_execle _gcov_execv _gcov_execvp _gcov_execve _gcov_reset  \
diff --git a/libgcc/libgcov-profiler.c b/libgcc/libgcov-profiler.c
index 670fb9748e0..874547aefc1 100644
--- a/libgcc/libgcov-profiler.c
+++ b/libgcc/libgcov-profiler.c
@@ -233,5 +233,19 @@ __gcov_ior_profiler_atomic (gcov_type *counters, gcov_type 
value)
 }
 #endif
 
+#ifdef L_gcov_increment_fn
+/* Increment the counter at COUNTER by one and return the new value.
+   Used by -fprofile-update=function-call.  Weak so a strong user
+   definition overrides this copy.  That override must not throw and
+   must not call back into the instrumented translation unit.  */
+
+__attribute__ ((weak))
+gcov_type
+__gcov_increment_fn (gcov_type *counter)
+{
+  return ++*counter;
+}
+#endif
+
 
 #endif /* inhibit_libc */
diff --git a/libgcc/libgcov.h b/libgcc/libgcov.h
index 1643b4440cd..4107537de45 100644
--- a/libgcc/libgcov.h
+++ b/libgcc/libgcov.h
@@ -325,6 +325,7 @@ 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 gcov_type __gcov_increment_fn (gcov_type *);
 
 #ifndef inhibit_libc
 /* The wrappers around some library functions..  */
-- 
2.55.0

Reply via email to