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