+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>
* hash-map.h: Pass default value to hash_table ctor.
#define GCOV_N_VALUE_COUNTERS \
(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. */
#define GCOV_TAG_FOR_COUNTER(COUNT) \
(GCOV_TAG_COUNTER_BASE + ((gcov_unsigned_t)(COUNT) << 17))
takes away bad histograms. */
if (h)
{
- /* counter 0 is target, counter 1 is number of execution we called target,
- counter 2 is total number of executions. */
- if (h->hvalue.counters[2])
+ gcov_type val, count, all;
+ if (get_most_common_single_value (NULL, "indirect call",
+ h, &val, &count, &all))
{
struct cgraph_edge * e = node->get_edge (stmt);
if (e && !e->indirect_unknown_callee)
continue;
- e->indirect_info->common_target_id
- = h->hvalue.counters [0];
+
+ e->indirect_info->common_target_id = val;
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 (dump_file)
= build_function_type_list (void_type_node,
gcov_type_ptr, gcov_type_node,
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,
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,
gcov_type_node,
ptr_type_node,
NULL_TREE);
- profiler_fn_name = "__gcov_indirect_call_profiler_v3";
+ profiler_fn_name = "__gcov_indirect_call_profiler_v4";
tree_indirect_call_profiler_fn
= build_fn_decl (profiler_fn_name, ic_profiler_fn_type);
break;
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)
{
- fprintf (dump_file, "value:%" PRId64
- " match:%" PRId64
- " wrong:%" PRId64,
- (int64_t) hist->hvalue.counters[0],
- (int64_t) hist->hvalue.counters[1],
- (int64_t) hist->hvalue.counters[2]);
+ fprintf (dump_file, "all: %" PRId64 ", values: ",
+ (int64_t) hist->hvalue.counters[0]);
+ for (unsigned i = 0; i < GCOV_DISK_SINGLE_VALUES; i++)
+ {
+ fprintf (dump_file, "[%" PRId64 ":%" PRId64 "]",
+ (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");
break;
fprintf (dump_file, ".\n");
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:
fprintf (dump_file, "Time profile ");
if (hist->hvalue.counters)
/* When user uses an unsigned type with a big value, constant converted
to gcov_type (a signed type) can be negative. */
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
gcc_assert (value >= 0);
case HIST_TYPE_SINGLE_VALUE:
case HIST_TYPE_INDIR_CALL:
- ncounters = 3;
+ ncounters = GCOV_SINGLE_VALUE_COUNTERS;
break;
case HIST_TYPE_IOR:
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. */
static bool
if (!histogram)
return false;
+ if (!get_most_common_single_value (stmt, "divmod", histogram, &val, &count,
+ &all))
+ return false;
+
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);
- /* We require that count is at least half of all; this means
- that for the transformation to fire the value must be constant
- at least 50% of time (and 75% gives the guarantee of usage). */
+ /* We require that count is at least half of all. */
if (simple_cst_equal (gimple_assign_rhs2 (stmt), value) != 1
|| 2 * count < all
|| optimize_bb_for_size_p (gimple_bb (stmt)))
return false;
- if (check_counter (stmt, "value", &count, &all, gimple_bb (stmt)->count))
- return false;
-
/* Compute probability of taking the optimal path. */
if (all > 0)
prob = profile_probability::probability_in_gcov_type (count, all);
{
gcall *stmt;
histogram_value histogram;
- gcov_type val, count, all, bb_all;
+ gcov_type val, count, all;
struct cgraph_node *direct_call;
stmt = dyn_cast <gcall *> (gsi_stmt (*gsi));
if (!histogram)
return false;
- val = histogram->hvalue.counters [0];
- count = histogram->hvalue.counters [1];
- all = histogram->hvalue.counters [2];
-
- 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 (!get_most_common_single_value (NULL, "indirect call", histogram, &val,
+ &count, &all))
+ return false;
if (4 * count <= 3 * all)
return false;
if (TREE_CODE (blck_size) == INTEGER_CST)
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)
return false;
- val = histogram->hvalue.counters[0];
- count = histogram->hvalue.counters[1];
- all = histogram->hvalue.counters[2];
+ if (!get_most_common_single_value (stmt, "stringops", histogram, &val,
+ &count, &all))
+ return false;
+
gimple_remove_histogram_value (cfun, stmt, histogram);
- /* We require that count is at least half of all; this means
- that for the transformation to fire the value must be constant
- at least 80% of time. */
- if ((6 * count / 5) < all || optimize_bb_for_size_p (gimple_bb (stmt)))
+ /* We require that count is at least half of all. */
+ if (2 * count < all || optimize_bb_for_size_p (gimple_bb (stmt)))
return false;
if (check_counter (stmt, "value", &count, &all, gimple_bb (stmt)->count))
return false;
break;
case HIST_TYPE_SINGLE_VALUE:
- hist->n_counters = 3;
+ hist->n_counters = GCOV_SINGLE_VALUE_COUNTERS;
break;
- case HIST_TYPE_INDIR_CALL:
- hist->n_counters = 3;
+ case HIST_TYPE_INDIR_CALL:
+ hist->n_counters = GCOV_SINGLE_VALUE_COUNTERS;
break;
case HIST_TYPE_TIME_PROFILE:
void stringop_block_profile (gimple *, unsigned int *, HOST_WIDE_INT *);
gcall *gimple_ic (gcall *, struct cgraph_node *, profile_probability);
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. */
+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>
* Makefile.in: Remove usage of
_gcov_interval_profiler_atomic \
_gcov_pow2_profiler \
_gcov_pow2_profiler_atomic \
- _gcov_one_value_profiler \
- _gcov_one_value_profiler_atomic \
+ _gcov_one_value_profiler_v2 \
+ _gcov_one_value_profiler_v2_atomic \
_gcov_average_profiler \
_gcov_average_profiler_atomic \
_gcov_ior_profiler \
_gcov_ior_profiler_atomic \
- _gcov_indirect_call_profiler_v3 \
+ _gcov_indirect_call_profiler_v4 \
_gcov_time_profiler
LIBGCOV_INTERFACE = _gcov_dump _gcov_flush _gcov_fork \
_gcov_execl _gcov_execlp \
#endif
#ifdef L_gcov_merge_single
-void __gcov_merge_single (gcov_type *counters __attribute__ ((unused)),
- unsigned n_counters __attribute__ ((unused))) {}
+void __gcov_merge_single (gcov_type *counters __attribute__ ((unused)))
+{
+}
#endif
#else
#endif /* L_gcov_merge_time_profile */
#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.
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
- are split into 3-tuples where the members of the tuple have
+ are split into pairs where the members of the tuple have
meanings:
-- the stored candidate on the most common value of the measured entity
-- counter
- -- total number of evaluations of the value */
+ */
void
__gcov_merge_single (gcov_type *counters, unsigned n_counters)
{
- unsigned i, n_measures;
- gcov_type value, counter, all;
+ gcc_assert (!(n_counters % GCOV_SINGLE_VALUE_COUNTERS));
- gcc_assert (!(n_counters % 3));
- n_measures = n_counters / 3;
- 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;
- }
+ for (unsigned i = 0; i < (n_counters / GCOV_SINGLE_VALUE_COUNTERS); i++)
+ merge_single_value_set (counters + (i * GCOV_SINGLE_VALUE_COUNTERS));
}
#endif /* L_gcov_merge_single */
COUNTERS[1] is decremented. Otherwise COUNTERS[1] is set to one and
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
- 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. */
+ will be in COUNTERS[0] in the end. */
static inline void
__gcov_one_value_profiler_body (gcov_type *counters, gcov_type value,
int use_atomic)
{
- if (value == counters[0])
- counters[1]++;
- else if (counters[1] == 0)
+ if (value == counters[1])
+ counters[2]++;
+ else if (counters[2] == 0)
{
- counters[1] = 1;
- counters[0] = value;
+ counters[2] = 1;
+ counters[1] = value;
}
else
- counters[1]--;
+ counters[2]--;
if (use_atomic)
- __atomic_fetch_add (&counters[2], 1, __ATOMIC_RELAXED);
+ __atomic_fetch_add (&counters[0], 1, __ATOMIC_RELAXED);
else
- counters[2]++;
+ counters[0]++;
}
-#ifdef L_gcov_one_value_profiler
+#ifdef L_gcov_one_value_profiler_v2
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);
}
#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.
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_v2_atomic (gcov_type *counters, gcov_type value)
{
__gcov_one_value_profiler_body (counters, value, 1);
}
#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
them in TLS memory so races are not common (they are written to often).
/* Tries to determine the most common value among its inputs. */
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
function may have multiple descriptors and we need to dereference
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_v3 (gcov_type, void *);
+extern void __gcov_one_value_profiler_v2 (gcov_type *, gcov_type);
+extern void __gcov_one_value_profiler_v2_atomic (gcov_type *, gcov_type);
+extern void __gcov_indirect_call_profiler_v4 (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);
#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
counters. */