Implement N disk counters for single value and indirect call counters.

2019-06-10  Martin Liska  <mliska@suse.cz>

	* gcov-io.h (GCOV_DISK_SINGLE_VALUES): New.
	(GCOV_SINGLE_VALUE_COUNTERS): Likewise.
	* ipa-profile.c (ipa_profile_generate_summary):
	Use get_most_common_single_value.
	* tree-profile.c (gimple_init_gcov_profiler):
	Instrument with __gcov_one_value_profiler_v2
	and __gcov_indirect_call_profiler_v4.
	* value-prof.c (dump_histogram_value):
	Print all values for HIST_TYPE_SINGLE_VALUE.
	(stream_out_histogram_value): Update assert for
	N values.
	(stream_in_histogram_value): Set number of
	counters for HIST_TYPE_SINGLE_VALUE.
	(get_most_common_single_value): New.
	(gimple_divmod_fixed_value_transform):
	Use get_most_common_single_value.
	(gimple_ic_transform): Likewise.
	(gimple_stringops_transform): Likewise.
	(gimple_find_values_to_profile): Set number
	of counters for HIST_TYPE_SINGLE_VALUE.
	* value-prof.h (get_most_common_single_value):
	New.
2019-06-10  Martin Liska  <mliska@suse.cz>

	* Makefile.in: Add __gcov_one_value_profiler_v2,
	__gcov_one_value_profiler_v2_atomic and
	__gcov_indirect_call_profiler_v4.
	* libgcov-merge.c (__gcov_merge_single): Change
	function signature.
	(merge_single_value_set): New.
	* libgcov-profiler.c (__gcov_one_value_profiler_body):
	Update functionality.
	(__gcov_one_value_profiler): Remove.
	(__gcov_one_value_profiler_v2): ... this.
	(__gcov_one_value_profiler_atomic): Rename to ...
	(__gcov_one_value_profiler_v2_atomic): this.
	(__gcov_indirect_call_profiler_v3): Rename to ...
	(__gcov_indirect_call_profiler_v4): ... this.
	* libgcov.h (__gcov_one_value_profiler): Remove.
	(__gcov_one_value_profiler_atomic): Remove.
	(__gcov_one_value_profiler_v2_atomic): New.
	(__gcov_indirect_call_profiler_v3): Remove.
	(__gcov_one_value_profiler_v2): New.
	(__gcov_indirect_call_profiler_v4): New.
	(gcov_get_counter_ignore_scaling): New function.

From-SVN: r272106
This commit is contained in:
Martin Liska 2019-06-10 09:38:59 +02:00 committed by Martin Liska
parent b076a52602
commit 92d417175b
11 changed files with 249 additions and 116 deletions

View File

@ -1,3 +1,28 @@
2019-06-10 Martin Liska <mliska@suse.cz>
* gcov-io.h (GCOV_DISK_SINGLE_VALUES): New.
(GCOV_SINGLE_VALUE_COUNTERS): Likewise.
* ipa-profile.c (ipa_profile_generate_summary):
Use get_most_common_single_value.
* tree-profile.c (gimple_init_gcov_profiler):
Instrument with __gcov_one_value_profiler_v2
and __gcov_indirect_call_profiler_v4.
* value-prof.c (dump_histogram_value):
Print all values for HIST_TYPE_SINGLE_VALUE.
(stream_out_histogram_value): Update assert for
N values.
(stream_in_histogram_value): Set number of
counters for HIST_TYPE_SINGLE_VALUE.
(get_most_common_single_value): New.
(gimple_divmod_fixed_value_transform):
Use get_most_common_single_value.
(gimple_ic_transform): Likewise.
(gimple_stringops_transform): Likewise.
(gimple_find_values_to_profile): Set number
of counters for HIST_TYPE_SINGLE_VALUE.
* value-prof.h (get_most_common_single_value):
New.
2019-06-10 Martin Liska <mliska@suse.cz> 2019-06-10 Martin Liska <mliska@suse.cz>
* hash-map.h: Pass default value to hash_table ctor. * hash-map.h: Pass default value to hash_table ctor.

View File

@ -266,6 +266,13 @@ GCOV_COUNTERS
#define GCOV_N_VALUE_COUNTERS \ #define GCOV_N_VALUE_COUNTERS \
(GCOV_LAST_VALUE_COUNTER - GCOV_FIRST_VALUE_COUNTER + 1) (GCOV_LAST_VALUE_COUNTER - GCOV_FIRST_VALUE_COUNTER + 1)
/* Number of single value histogram values that live
on disk representation. */
#define GCOV_DISK_SINGLE_VALUES 4
/* Total number of single value counters. */
#define GCOV_SINGLE_VALUE_COUNTERS (2 * GCOV_DISK_SINGLE_VALUES + 1)
/* Convert a counter index to a tag. */ /* Convert a counter index to a tag. */
#define GCOV_TAG_FOR_COUNTER(COUNT) \ #define GCOV_TAG_FOR_COUNTER(COUNT) \
(GCOV_TAG_COUNTER_BASE + ((gcov_unsigned_t)(COUNT) << 17)) (GCOV_TAG_COUNTER_BASE + ((gcov_unsigned_t)(COUNT) << 17))

View File

@ -191,17 +191,17 @@ ipa_profile_generate_summary (void)
takes away bad histograms. */ takes away bad histograms. */
if (h) if (h)
{ {
/* counter 0 is target, counter 1 is number of execution we called target, gcov_type val, count, all;
counter 2 is total number of executions. */ if (get_most_common_single_value (NULL, "indirect call",
if (h->hvalue.counters[2]) h, &val, &count, &all))
{ {
struct cgraph_edge * e = node->get_edge (stmt); struct cgraph_edge * e = node->get_edge (stmt);
if (e && !e->indirect_unknown_callee) if (e && !e->indirect_unknown_callee)
continue; continue;
e->indirect_info->common_target_id
= h->hvalue.counters [0]; e->indirect_info->common_target_id = val;
e->indirect_info->common_target_probability e->indirect_info->common_target_probability
= GCOV_COMPUTE_SCALE (h->hvalue.counters [1], h->hvalue.counters [2]); = GCOV_COMPUTE_SCALE (count, all);
if (e->indirect_info->common_target_probability > REG_BR_PROB_BASE) if (e->indirect_info->common_target_probability > REG_BR_PROB_BASE)
{ {
if (dump_file) if (dump_file)

View File

@ -165,10 +165,10 @@ gimple_init_gcov_profiler (void)
= build_function_type_list (void_type_node, = build_function_type_list (void_type_node,
gcov_type_ptr, gcov_type_node, gcov_type_ptr, gcov_type_node,
NULL_TREE); NULL_TREE);
fn_name = concat ("__gcov_one_value_profiler", fn_suffix, NULL); fn_name = concat ("__gcov_one_value_profiler_v2", fn_suffix, NULL);
tree_one_value_profiler_fn = build_fn_decl (fn_name, tree_one_value_profiler_fn = build_fn_decl (fn_name,
one_value_profiler_fn_type); one_value_profiler_fn_type);
free (CONST_CAST (char *, fn_name));
TREE_NOTHROW (tree_one_value_profiler_fn) = 1; TREE_NOTHROW (tree_one_value_profiler_fn) = 1;
DECL_ATTRIBUTES (tree_one_value_profiler_fn) DECL_ATTRIBUTES (tree_one_value_profiler_fn)
= tree_cons (get_identifier ("leaf"), NULL, = tree_cons (get_identifier ("leaf"), NULL,
@ -182,7 +182,7 @@ gimple_init_gcov_profiler (void)
gcov_type_node, gcov_type_node,
ptr_type_node, ptr_type_node,
NULL_TREE); NULL_TREE);
profiler_fn_name = "__gcov_indirect_call_profiler_v3"; profiler_fn_name = "__gcov_indirect_call_profiler_v4";
tree_indirect_call_profiler_fn tree_indirect_call_profiler_fn
= build_fn_decl (profiler_fn_name, ic_profiler_fn_type); = build_fn_decl (profiler_fn_name, ic_profiler_fn_type);

View File

@ -259,15 +259,22 @@ dump_histogram_value (FILE *dump_file, histogram_value hist)
break; break;
case HIST_TYPE_SINGLE_VALUE: case HIST_TYPE_SINGLE_VALUE:
fprintf (dump_file, "Single value "); case HIST_TYPE_INDIR_CALL:
fprintf (dump_file,
(hist->type == HIST_TYPE_SINGLE_VALUE
? "Single value counter " : "Indirect call counter"));
if (hist->hvalue.counters) if (hist->hvalue.counters)
{ {
fprintf (dump_file, "value:%" PRId64 fprintf (dump_file, "all: %" PRId64 ", values: ",
" match:%" PRId64 (int64_t) hist->hvalue.counters[0]);
" wrong:%" PRId64, for (unsigned i = 0; i < GCOV_DISK_SINGLE_VALUES; i++)
(int64_t) hist->hvalue.counters[0], {
(int64_t) hist->hvalue.counters[1], fprintf (dump_file, "[%" PRId64 ":%" PRId64 "]",
(int64_t) hist->hvalue.counters[2]); (int64_t) hist->hvalue.counters[2 * i + 1],
(int64_t) hist->hvalue.counters[2 * i + 2]);
if (i != GCOV_DISK_SINGLE_VALUES - 1)
fprintf (dump_file, ", ");
}
} }
fprintf (dump_file, ".\n"); fprintf (dump_file, ".\n");
break; break;
@ -294,19 +301,6 @@ dump_histogram_value (FILE *dump_file, histogram_value hist)
fprintf (dump_file, ".\n"); fprintf (dump_file, ".\n");
break; break;
case HIST_TYPE_INDIR_CALL:
fprintf (dump_file, "Indirect call ");
if (hist->hvalue.counters)
{
fprintf (dump_file, "value:%" PRId64
" match:%" PRId64
" all:%" PRId64,
(int64_t) hist->hvalue.counters[0],
(int64_t) hist->hvalue.counters[1],
(int64_t) hist->hvalue.counters[2]);
}
fprintf (dump_file, ".\n");
break;
case HIST_TYPE_TIME_PROFILE: case HIST_TYPE_TIME_PROFILE:
fprintf (dump_file, "Time profile "); fprintf (dump_file, "Time profile ");
if (hist->hvalue.counters) if (hist->hvalue.counters)
@ -347,7 +341,7 @@ stream_out_histogram_value (struct output_block *ob, histogram_value hist)
/* When user uses an unsigned type with a big value, constant converted /* When user uses an unsigned type with a big value, constant converted
to gcov_type (a signed type) can be negative. */ to gcov_type (a signed type) can be negative. */
gcov_type value = hist->hvalue.counters[i]; gcov_type value = hist->hvalue.counters[i];
if (hist->type == HIST_TYPE_SINGLE_VALUE && i == 0) if (hist->type == HIST_TYPE_SINGLE_VALUE && (i > 0 && ((i - 1) % 2) == 0))
; ;
else else
gcc_assert (value >= 0); gcc_assert (value >= 0);
@ -392,7 +386,7 @@ stream_in_histogram_value (struct lto_input_block *ib, gimple *stmt)
case HIST_TYPE_SINGLE_VALUE: case HIST_TYPE_SINGLE_VALUE:
case HIST_TYPE_INDIR_CALL: case HIST_TYPE_INDIR_CALL:
ncounters = 3; ncounters = GCOV_SINGLE_VALUE_COUNTERS;
break; break;
case HIST_TYPE_IOR: case HIST_TYPE_IOR:
@ -729,6 +723,48 @@ gimple_divmod_fixed_value (gassign *stmt, tree value, profile_probability prob,
return tmp2; return tmp2;
} }
/* Return most common value of SINGLE_VALUE histogram. If
there's a unique value, return true and set VALUE and COUNT
arguments. */
bool
get_most_common_single_value (gimple *stmt, const char *counter_type,
histogram_value hist,
gcov_type *value, gcov_type *count,
gcov_type *all)
{
if (hist->hvalue.counters[2] == -1)
return false;
*count = 0;
*value = 0;
gcov_type read_all = hist->hvalue.counters[0];
for (unsigned i = 0; i < GCOV_DISK_SINGLE_VALUES; i++)
{
gcov_type v = hist->hvalue.counters[2 * i + 1];
gcov_type c = hist->hvalue.counters[2 * i + 2];
/* Indirect calls can't be vereified. */
if (stmt && check_counter (stmt, counter_type, &c, &read_all,
gimple_bb (stmt)->count))
return false;
*all = read_all;
if (c > *count)
{
*value = v;
*count = c;
}
else if (c == *count && v > *value)
*value = v;
}
return true;
}
/* Do transform 1) on INSN if applicable. */ /* Do transform 1) on INSN if applicable. */
static bool static bool
@ -758,23 +794,19 @@ gimple_divmod_fixed_value_transform (gimple_stmt_iterator *si)
if (!histogram) if (!histogram)
return false; return false;
if (!get_most_common_single_value (stmt, "divmod", histogram, &val, &count,
&all))
return false;
value = histogram->hvalue.value; value = histogram->hvalue.value;
val = histogram->hvalue.counters[0];
count = histogram->hvalue.counters[1];
all = histogram->hvalue.counters[2];
gimple_remove_histogram_value (cfun, stmt, histogram); gimple_remove_histogram_value (cfun, stmt, histogram);
/* We require that count is at least half of all; this means /* We require that count is at least half of all. */
that for the transformation to fire the value must be constant
at least 50% of time (and 75% gives the guarantee of usage). */
if (simple_cst_equal (gimple_assign_rhs2 (stmt), value) != 1 if (simple_cst_equal (gimple_assign_rhs2 (stmt), value) != 1
|| 2 * count < all || 2 * count < all
|| optimize_bb_for_size_p (gimple_bb (stmt))) || optimize_bb_for_size_p (gimple_bb (stmt)))
return false; return false;
if (check_counter (stmt, "value", &count, &all, gimple_bb (stmt)->count))
return false;
/* Compute probability of taking the optimal path. */ /* Compute probability of taking the optimal path. */
if (all > 0) if (all > 0)
prob = profile_probability::probability_in_gcov_type (count, all); prob = profile_probability::probability_in_gcov_type (count, all);
@ -1401,7 +1433,7 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
{ {
gcall *stmt; gcall *stmt;
histogram_value histogram; histogram_value histogram;
gcov_type val, count, all, bb_all; gcov_type val, count, all;
struct cgraph_node *direct_call; struct cgraph_node *direct_call;
stmt = dyn_cast <gcall *> (gsi_stmt (*gsi)); stmt = dyn_cast <gcall *> (gsi_stmt (*gsi));
@ -1418,21 +1450,9 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
if (!histogram) if (!histogram)
return false; return false;
val = histogram->hvalue.counters [0]; if (!get_most_common_single_value (NULL, "indirect call", histogram, &val,
count = histogram->hvalue.counters [1]; &count, &all))
all = histogram->hvalue.counters [2]; return false;
bb_all = gimple_bb (stmt)->count.ipa ().to_gcov_type ();
/* The order of CHECK_COUNTER calls is important -
since check_counter can correct the third parameter
and we want to make count <= all <= bb_all. */
if (check_counter (stmt, "ic", &all, &bb_all, gimple_bb (stmt)->count)
|| check_counter (stmt, "ic", &count, &all,
profile_count::from_gcov_type (all)))
{
gimple_remove_histogram_value (cfun, stmt, histogram);
return false;
}
if (4 * count <= 3 * all) if (4 * count <= 3 * all)
return false; return false;
@ -1644,19 +1664,19 @@ gimple_stringops_transform (gimple_stmt_iterator *gsi)
if (TREE_CODE (blck_size) == INTEGER_CST) if (TREE_CODE (blck_size) == INTEGER_CST)
return false; return false;
histogram = gimple_histogram_value_of_type (cfun, stmt, HIST_TYPE_SINGLE_VALUE); histogram = gimple_histogram_value_of_type (cfun, stmt,
HIST_TYPE_SINGLE_VALUE);
if (!histogram) if (!histogram)
return false; return false;
val = histogram->hvalue.counters[0]; if (!get_most_common_single_value (stmt, "stringops", histogram, &val,
count = histogram->hvalue.counters[1]; &count, &all))
all = histogram->hvalue.counters[2]; return false;
gimple_remove_histogram_value (cfun, stmt, histogram); gimple_remove_histogram_value (cfun, stmt, histogram);
/* We require that count is at least half of all; this means /* We require that count is at least half of all. */
that for the transformation to fire the value must be constant if (2 * count < all || optimize_bb_for_size_p (gimple_bb (stmt)))
at least 80% of time. */
if ((6 * count / 5) < all || optimize_bb_for_size_p (gimple_bb (stmt)))
return false; return false;
if (check_counter (stmt, "value", &count, &all, gimple_bb (stmt)->count)) if (check_counter (stmt, "value", &count, &all, gimple_bb (stmt)->count))
return false; return false;
@ -1928,11 +1948,11 @@ gimple_find_values_to_profile (histogram_values *values)
break; break;
case HIST_TYPE_SINGLE_VALUE: case HIST_TYPE_SINGLE_VALUE:
hist->n_counters = 3; hist->n_counters = GCOV_SINGLE_VALUE_COUNTERS;
break; break;
case HIST_TYPE_INDIR_CALL: case HIST_TYPE_INDIR_CALL:
hist->n_counters = 3; hist->n_counters = GCOV_SINGLE_VALUE_COUNTERS;
break; break;
case HIST_TYPE_TIME_PROFILE: case HIST_TYPE_TIME_PROFILE:

View File

@ -90,6 +90,10 @@ void free_histograms (function *);
void stringop_block_profile (gimple *, unsigned int *, HOST_WIDE_INT *); void stringop_block_profile (gimple *, unsigned int *, HOST_WIDE_INT *);
gcall *gimple_ic (gcall *, struct cgraph_node *, profile_probability); gcall *gimple_ic (gcall *, struct cgraph_node *, profile_probability);
bool check_ic_target (gcall *, struct cgraph_node *); bool check_ic_target (gcall *, struct cgraph_node *);
bool get_most_common_single_value (gimple *stmt, const char *counter_type,
histogram_value hist,
gcov_type *value, gcov_type *count,
gcov_type *all);
/* In tree-profile.c. */ /* In tree-profile.c. */

View File

@ -1,3 +1,27 @@
2019-06-10 Martin Liska <mliska@suse.cz>
* Makefile.in: Add __gcov_one_value_profiler_v2,
__gcov_one_value_profiler_v2_atomic and
__gcov_indirect_call_profiler_v4.
* libgcov-merge.c (__gcov_merge_single): Change
function signature.
(merge_single_value_set): New.
* libgcov-profiler.c (__gcov_one_value_profiler_body):
Update functionality.
(__gcov_one_value_profiler): Remove.
(__gcov_one_value_profiler_v2): ... this.
(__gcov_one_value_profiler_atomic): Rename to ...
(__gcov_one_value_profiler_v2_atomic): this.
(__gcov_indirect_call_profiler_v3): Rename to ...
(__gcov_indirect_call_profiler_v4): ... this.
* libgcov.h (__gcov_one_value_profiler): Remove.
(__gcov_one_value_profiler_atomic): Remove.
(__gcov_one_value_profiler_v2_atomic): New.
(__gcov_indirect_call_profiler_v3): Remove.
(__gcov_one_value_profiler_v2): New.
(__gcov_indirect_call_profiler_v4): New.
(gcov_get_counter_ignore_scaling): New function.
2019-06-07 Martin Liska <mliska@suse.cz> 2019-06-07 Martin Liska <mliska@suse.cz>
* Makefile.in: Remove usage of * Makefile.in: Remove usage of

View File

@ -893,13 +893,13 @@ LIBGCOV_PROFILER = _gcov_interval_profiler \
_gcov_interval_profiler_atomic \ _gcov_interval_profiler_atomic \
_gcov_pow2_profiler \ _gcov_pow2_profiler \
_gcov_pow2_profiler_atomic \ _gcov_pow2_profiler_atomic \
_gcov_one_value_profiler \ _gcov_one_value_profiler_v2 \
_gcov_one_value_profiler_atomic \ _gcov_one_value_profiler_v2_atomic \
_gcov_average_profiler \ _gcov_average_profiler \
_gcov_average_profiler_atomic \ _gcov_average_profiler_atomic \
_gcov_ior_profiler \ _gcov_ior_profiler \
_gcov_ior_profiler_atomic \ _gcov_ior_profiler_atomic \
_gcov_indirect_call_profiler_v3 \ _gcov_indirect_call_profiler_v4 \
_gcov_time_profiler _gcov_time_profiler
LIBGCOV_INTERFACE = _gcov_dump _gcov_flush _gcov_fork \ LIBGCOV_INTERFACE = _gcov_dump _gcov_flush _gcov_fork \
_gcov_execl _gcov_execlp \ _gcov_execl _gcov_execlp \

View File

@ -34,8 +34,9 @@ void __gcov_merge_add (gcov_type *counters __attribute__ ((unused)),
#endif #endif
#ifdef L_gcov_merge_single #ifdef L_gcov_merge_single
void __gcov_merge_single (gcov_type *counters __attribute__ ((unused)), void __gcov_merge_single (gcov_type *counters __attribute__ ((unused)))
unsigned n_counters __attribute__ ((unused))) {} {
}
#endif #endif
#else #else
@ -85,40 +86,72 @@ __gcov_merge_time_profile (gcov_type *counters, unsigned n_counters)
#endif /* L_gcov_merge_time_profile */ #endif /* L_gcov_merge_time_profile */
#ifdef L_gcov_merge_single #ifdef L_gcov_merge_single
static void
merge_single_value_set (gcov_type *counters)
{
unsigned j;
gcov_type value, counter;
/* First value is number of total executions of the profiler. */
gcov_type all = gcov_get_counter_ignore_scaling (-1);
counters[0] += all;
++counters;
for (unsigned i = 0; i < GCOV_DISK_SINGLE_VALUES; i++)
{
value = gcov_get_counter_target ();
counter = gcov_get_counter_ignore_scaling (-1);
if (counter == -1)
{
counters[1] = -1;
/* We can't return as we need to read all counters. */
continue;
}
else if (counter == 0 || counters[1] == -1)
{
/* We can't return as we need to read all counters. */
continue;
}
for (j = 0; j < GCOV_DISK_SINGLE_VALUES; j++)
{
if (counters[2 * j] == value)
{
counters[2 * j + 1] += counter;
break;
}
else if (counters[2 * j + 1] == 0)
{
counters[2 * j] = value;
counters[2 * j + 1] = counter;
break;
}
}
/* We haven't found a free slot for the value, mark overflow. */
if (j == GCOV_DISK_SINGLE_VALUES)
counters[1] = -1;
}
}
/* The profile merging function for choosing the most common value. /* The profile merging function for choosing the most common value.
It is given an array COUNTERS of N_COUNTERS old counters and it It is given an array COUNTERS of N_COUNTERS old counters and it
reads the same number of counters from the gcov file. The counters reads the same number of counters from the gcov file. The counters
are split into 3-tuples where the members of the tuple have are split into pairs where the members of the tuple have
meanings: meanings:
-- the stored candidate on the most common value of the measured entity -- the stored candidate on the most common value of the measured entity
-- counter -- counter
-- total number of evaluations of the value */ */
void void
__gcov_merge_single (gcov_type *counters, unsigned n_counters) __gcov_merge_single (gcov_type *counters, unsigned n_counters)
{ {
unsigned i, n_measures; gcc_assert (!(n_counters % GCOV_SINGLE_VALUE_COUNTERS));
gcov_type value, counter, all;
gcc_assert (!(n_counters % 3)); for (unsigned i = 0; i < (n_counters / GCOV_SINGLE_VALUE_COUNTERS); i++)
n_measures = n_counters / 3; merge_single_value_set (counters + (i * GCOV_SINGLE_VALUE_COUNTERS));
for (i = 0; i < n_measures; i++, counters += 3)
{
value = gcov_get_counter_target ();
counter = gcov_get_counter ();
all = gcov_get_counter ();
if (counters[0] == value)
counters[1] += counter;
else if (counter > counters[1])
{
counters[0] = value;
counters[1] = counter - counters[1];
}
else
counters[1] -= counter;
counters[2] += all;
}
} }
#endif /* L_gcov_merge_single */ #endif /* L_gcov_merge_single */

View File

@ -112,40 +112,37 @@ __gcov_pow2_profiler_atomic (gcov_type *counters, gcov_type value)
COUNTERS[1] is decremented. Otherwise COUNTERS[1] is set to one and COUNTERS[1] is decremented. Otherwise COUNTERS[1] is set to one and
VALUE is stored to COUNTERS[0]. This algorithm guarantees that if this VALUE is stored to COUNTERS[0]. This algorithm guarantees that if this
function is called more than 50% of the time with one value, this value function is called more than 50% of the time with one value, this value
will be in COUNTERS[0] in the end. will be in COUNTERS[0] in the end. */
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 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) int use_atomic)
{ {
if (value == counters[0]) if (value == counters[1])
counters[1]++; counters[2]++;
else if (counters[1] == 0) else if (counters[2] == 0)
{ {
counters[1] = 1; counters[2] = 1;
counters[0] = value; counters[1] = value;
} }
else else
counters[1]--; counters[2]--;
if (use_atomic) if (use_atomic)
__atomic_fetch_add (&counters[2], 1, __ATOMIC_RELAXED); __atomic_fetch_add (&counters[0], 1, __ATOMIC_RELAXED);
else else
counters[2]++; counters[0]++;
} }
#ifdef L_gcov_one_value_profiler #ifdef L_gcov_one_value_profiler_v2
void void
__gcov_one_value_profiler (gcov_type *counters, gcov_type value) __gcov_one_value_profiler_v2 (gcov_type *counters, gcov_type value)
{ {
__gcov_one_value_profiler_body (counters, value, 0); __gcov_one_value_profiler_body (counters, value, 0);
} }
#endif #endif
#if defined(L_gcov_one_value_profiler_atomic) && GCOV_SUPPORTS_ATOMIC #if defined(L_gcov_one_value_profiler_v2_atomic) && GCOV_SUPPORTS_ATOMIC
/* Update one value profilers (COUNTERS) for a given VALUE. /* Update one value profilers (COUNTERS) for a given VALUE.
@ -157,13 +154,13 @@ __gcov_one_value_profiler (gcov_type *counters, gcov_type value)
https://gcc.gnu.org/ml/gcc-patches/2016-08/msg00024.html. */ https://gcc.gnu.org/ml/gcc-patches/2016-08/msg00024.html. */
void void
__gcov_one_value_profiler_atomic (gcov_type *counters, gcov_type value) __gcov_one_value_profiler_v2_atomic (gcov_type *counters, gcov_type value)
{ {
__gcov_one_value_profiler_body (counters, value, 1); __gcov_one_value_profiler_body (counters, value, 1);
} }
#endif #endif
#ifdef L_gcov_indirect_call_profiler_v3 #ifdef L_gcov_indirect_call_profiler_v4
/* These two variables are used to actually track caller and callee. Keep /* These two variables are used to actually track caller and callee. Keep
them in TLS memory so races are not common (they are written to often). them in TLS memory so races are not common (they are written to often).
@ -185,7 +182,7 @@ struct indirect_call_tuple __gcov_indirect_call;
/* Tries to determine the most common value among its inputs. */ /* Tries to determine the most common value among its inputs. */
void void
__gcov_indirect_call_profiler_v3 (gcov_type value, void* cur_func) __gcov_indirect_call_profiler_v4 (gcov_type value, void* cur_func)
{ {
/* If the C++ virtual tables contain function descriptors then one /* If the C++ virtual tables contain function descriptors then one
function may have multiple descriptors and we need to dereference function may have multiple descriptors and we need to dereference

View File

@ -271,9 +271,9 @@ extern void __gcov_interval_profiler_atomic (gcov_type *, gcov_type, int,
unsigned); unsigned);
extern void __gcov_pow2_profiler (gcov_type *, gcov_type); extern void __gcov_pow2_profiler (gcov_type *, gcov_type);
extern void __gcov_pow2_profiler_atomic (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_v2 (gcov_type *, gcov_type);
extern void __gcov_one_value_profiler_atomic (gcov_type *, gcov_type); extern void __gcov_one_value_profiler_v2_atomic (gcov_type *, gcov_type);
extern void __gcov_indirect_call_profiler_v3 (gcov_type, void *); extern void __gcov_indirect_call_profiler_v4 (gcov_type, void *);
extern void __gcov_time_profiler (gcov_type *); extern void __gcov_time_profiler (gcov_type *);
extern void __gcov_time_profiler_atomic (gcov_type *); extern void __gcov_time_profiler_atomic (gcov_type *);
extern void __gcov_average_profiler (gcov_type *, gcov_type); extern void __gcov_average_profiler (gcov_type *, gcov_type);
@ -324,6 +324,29 @@ gcov_get_counter (void)
#endif #endif
} }
/* Similar function as gcov_get_counter(), but do not scale
when read value is equal to IGNORE_SCALING. */
static inline gcov_type
gcov_get_counter_ignore_scaling (gcov_type ignore_scaling)
{
#ifndef IN_GCOV_TOOL
/* This version is for reading count values in libgcov runtime:
we read from gcda files. */
return gcov_read_counter ();
#else
/* This version is for gcov-tool. We read the value from memory and
multiply it by the merge weight. */
gcov_type v = gcov_read_counter_mem ();
if (v != ignore_scaling)
v *= gcov_get_merge_weight ();
return v;
#endif
}
/* Similar function as gcov_get_counter(), but handles target address /* Similar function as gcov_get_counter(), but handles target address
counters. */ counters. */