aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog26
-rw-r--r--gcc/Makefile.in9
-rw-r--r--gcc/flags.h4
-rw-r--r--gcc/flow.c6
-rw-r--r--gcc/gcov-io.h33
-rw-r--r--gcc/libgcov.c89
-rw-r--r--gcc/profile.c386
-rw-r--r--gcc/toplev.c6
8 files changed, 543 insertions, 16 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 07d016c..5c3911b 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,4 +1,28 @@
-2003-06-24 Zdenek Dvorak <rakdver@atrey.karlin.mff.cuni.cz>
+2003-06-26 Zdenek Dvorak <rakdver@atrey.karlin.mff.cuni.cz>
+
+ * value-prof.c: New.
+ * value-prof.h: New.
+ * Makefile.in (value-prof.o): New.
+ (LIBGCOV): Add _gcov_merge_single and _gcov_merge_delta
+ (profile.o): Add value-prof.h and tree.h dependency.
+ * flags.h (flag_profile_values): Declare.
+ * gcov-io.h (GCOV_COUNTERS, GCOV_COUNTER_NAMES, GCOV_MERGE_FUNCTIONS):
+ Add new counters.
+ (GCOV_COUNTER_V_INTERVAL, GCOV_COUNTER_V_POW2, GCOV_COUNTER_V_SINGLE,
+ GCOV_COUNTER_V_DELTA): New counter sections.
+ (__gcov_merge_single, __gcov_merge_delta): Declare.
+ * flow.c (mark_used_regs): Set subregs_of_mode only when the
+ structure is initialized.
+ * libgcov.c (__gcov_merge_single, __gcov_merge_delta): New functions.
+ * profile.c: Include value-prof.h and tree.h.
+ (gen_interval_profiler, gen_pow2_profiler, gen_one_value_profiler,
+ gen_const_delta_profiler, instrument_values): New static functions.
+ (get_exec_counts): Fix comment.
+ (branch_prob): Invoke instrument_values.
+ * toplev.c (flag_profile_values): New flag.
+ * doc/invoke.texi (-fprofile-values): Document.
+
+2003-06-26 Zdenek Dvorak <rakdver@atrey.karlin.mff.cuni.cz>
* Makefile.in (cfgrtl.o): Add expr.h dependency.
* cfgrtl.c: Include expr.h.
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 5029c52..c6c32ae 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -814,7 +814,7 @@ OBJS = alias.o bb-reorder.o bitmap.o builtins.o caller-save.o calls.o \
insn-extract.o insn-opinit.o insn-output.o insn-peep.o insn-recog.o \
integrate.o intl.o jump.o langhooks.o lcm.o lists.o local-alloc.o \
loop.o mbchar.o optabs.o options.o opts.o params.o predict.o \
- print-rtl.o print-tree.o \
+ print-rtl.o print-tree.o value-prof.o \
profile.o ra.o ra-build.o ra-colorize.o ra-debug.o ra-rewrite.o \
real.o recog.o reg-stack.o regclass.o regmove.o regrename.o \
reload.o reload1.o reorg.o resource.o rtl.o rtlanal.o rtl-error.o \
@@ -854,7 +854,7 @@ STAGESTUFF = *$(objext) insn-flags.h insn-config.h insn-codes.h \
LIB2FUNCS_ST = _eprintf __gcc_bcmp
# Defined in libgcov.c, included only in gcov library
-LIBGCOV = _gcov _gcov_merge_add
+LIBGCOV = _gcov _gcov_merge_add _gcov_merge_single _gcov_merge_delta
FPBIT_FUNCS = _pack_sf _unpack_sf _addsub_sf _mul_sf _div_sf \
_fpcmp_parts_sf _compare_sf _eq_sf _ne_sf _gt_sf _ge_sf \
@@ -1636,7 +1636,10 @@ conflict.o : conflict.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(OBSTACK_H)
$(HASHTAB_H) $(RTL_H) hard-reg-set.h $(BASIC_BLOCK_H)
profile.o : profile.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
$(TREE_H) flags.h output.h $(REGS_H) $(EXPR_H) function.h \
- toplev.h $(BASIC_BLOCK_H) $(COVERAGE_H)
+ toplev.h $(BASIC_BLOCK_H) $(COVERAGE_H) $(TREE_H) value-prof.h
+value-prof.o : value-prof.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
+ $(BASIC_BLOCK_H) hard-reg-set.h value-prof.h $(EXPR_H) output.h flags.h \
+ $(RECOG_H) insn-config.h $(OPTABS_H)
loop.o : loop.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) flags.h $(LOOP_H) \
insn-config.h $(REGS_H) hard-reg-set.h $(RECOG_H) $(EXPR_H) \
real.h $(PREDICT_H) $(BASIC_BLOCK_H) function.h cfgloop.h \
diff --git a/gcc/flags.h b/gcc/flags.h
index 6ef4ece..e7a3ea8 100644
--- a/gcc/flags.h
+++ b/gcc/flags.h
@@ -191,6 +191,10 @@ extern int profile_flag;
extern int profile_arc_flag;
+/* Nonzero if value profile should be measured. */
+
+extern int flag_profile_values;
+
/* Nonzero if generating info for gcov to calculate line test coverage. */
extern int flag_test_coverage;
diff --git a/gcc/flow.c b/gcc/flow.c
index 6949beb..b08a6e8 100644
--- a/gcc/flow.c
+++ b/gcc/flow.c
@@ -3842,7 +3842,8 @@ mark_used_regs (pbi, x, cond, insn)
case SUBREG:
#ifdef CANNOT_CHANGE_MODE_CLASS
- if (GET_CODE (SUBREG_REG (x)) == REG
+ if ((flags & PROP_REG_INFO)
+ && GET_CODE (SUBREG_REG (x)) == REG
&& REGNO (SUBREG_REG (x)) >= FIRST_PSEUDO_REGISTER)
bitmap_set_bit (&subregs_of_mode, REGNO (SUBREG_REG (x))
* MAX_MACHINE_MODE
@@ -3891,7 +3892,8 @@ mark_used_regs (pbi, x, cond, insn)
|| GET_CODE (testreg) == SUBREG)
{
#ifdef CANNOT_CHANGE_MODE_CLASS
- if (GET_CODE (testreg) == SUBREG
+ if ((flags & PROP_REG_INFO)
+ && GET_CODE (testreg) == SUBREG
&& GET_CODE (SUBREG_REG (testreg)) == REG
&& REGNO (SUBREG_REG (testreg)) >= FIRST_PSEUDO_REGISTER)
bitmap_set_bit (&subregs_of_mode, REGNO (SUBREG_REG (testreg))
diff --git a/gcc/gcov-io.h b/gcc/gcov-io.h
index 5565408..769fd8a 100644
--- a/gcc/gcov-io.h
+++ b/gcc/gcov-io.h
@@ -269,14 +269,24 @@ typedef HOST_WIDEST_INT gcov_type;
#define GCOV_COUNTER_ARCS 0 /* Arc transitions. */
#define GCOV_COUNTERS_SUMMABLE 1 /* Counters which can be
summaried. */
-#define GCOV_COUNTERS 1
-
-/* A list of human readable names of the counters */
-#define GCOV_COUNTER_NAMES {"arcs"}
-
-/* Names of merge functions for counters. */
-#define GCOV_MERGE_FUNCTIONS {"__gcov_merge_add"}
-
+#define GCOV_COUNTER_V_INTERVAL 1 /* Histogram of value inside an interval. */
+#define GCOV_COUNTER_V_POW2 2 /* Histogram of exact power2 logarithm
+ of a value. */
+#define GCOV_COUNTER_V_SINGLE 3 /* The most common value of expression. */
+#define GCOV_COUNTER_V_DELTA 4 /* The most common difference between
+ consecutive values of expression. */
+#define GCOV_COUNTERS 5
+
+ /* A list of human readable names of the counters */
+#define GCOV_COUNTER_NAMES {"arcs", "interval", "pow2", "single", "delta"}
+
+ /* Names of merge functions for counters. */
+#define GCOV_MERGE_FUNCTIONS {"__gcov_merge_add", \
+ "__gcov_merge_add", \
+ "__gcov_merge_add", \
+ "__gcov_merge_single", \
+ "__gcov_merge_delta"}
+
/* Convert a counter index to a tag. */
#define GCOV_TAG_FOR_COUNTER(COUNT) \
(GCOV_TAG_COUNTER_BASE + ((gcov_unsigned_t)(COUNT) << 17))
@@ -380,6 +390,13 @@ extern void __gcov_flush (void);
/* The merge function that just sums the counters. */
extern void __gcov_merge_add (gcov_type *, unsigned);
+
+/* The merge function to choose the most often value. */
+extern void __gcov_merge_single (gcov_type *, unsigned);
+
+/* The merge function to choose the most often difference between consecutive
+ values. */
+extern void __gcov_merge_delta (gcov_type *, unsigned);
#endif /* IN_LIBGCOV */
#if IN_LIBGCOV >= 0
diff --git a/gcc/libgcov.c b/gcc/libgcov.c
index ba54281..5396c39 100644
--- a/gcc/libgcov.c
+++ b/gcc/libgcov.c
@@ -63,6 +63,16 @@ void __gcov_merge_add (gcov_type *counters __attribute__ ((unused)),
unsigned n_counters __attribute__ ((unused))) {}
#endif
+#ifdef L_gcov_merge_single
+void __gcov_merge_single (gcov_type *counters __attribute__ ((unused)),
+ unsigned n_counters __attribute__ ((unused))) {}
+#endif
+
+#ifdef L_gcov_merge_delta
+void __gcov_merge_delta (gcov_type *counters __attribute__ ((unused)),
+ unsigned n_counters __attribute__ ((unused))) {}
+#endif
+
#else
#include <string.h>
@@ -466,4 +476,83 @@ __gcov_merge_add (gcov_type *counters, unsigned n_counters)
}
#endif /* L_gcov_merge_add */
+#ifdef L_gcov_merge_single
+/* 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 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;
+
+ if (n_counters % 3)
+ abort ();
+
+ n_measures = n_counters / 3;
+ for (i = 0; i < n_measures; i++, counters += 3)
+ {
+ value = gcov_read_counter ();
+ counter = gcov_read_counter ();
+ all = gcov_read_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 */
+
+#ifdef L_gcov_merge_delta
+/* The profile merging function for choosing the most common difference between
+ two consecutive evaluations of the 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 4-tuples where the members of the
+ tuple have meanings:
+ -- the last value of the measured entity
+ -- the stored candidate on the most common difference
+ -- counter
+ -- total number of evaluations of the value */
+void
+__gcov_merge_delta (gcov_type *counters, unsigned n_counters)
+{
+ unsigned i, n_measures;
+ gcov_type last, value, counter, all;
+
+ if (n_counters % 4)
+ abort ();
+
+ n_measures = n_counters / 4;
+ for (i = 0; i < n_measures; i++, counters += 4)
+ {
+ last = gcov_read_counter ();
+ value = gcov_read_counter ();
+ counter = gcov_read_counter ();
+ all = gcov_read_counter ();
+
+ if (counters[1] == value)
+ counters[2] += counter;
+ else if (counter > counters[2])
+ {
+ counters[1] = value;
+ counters[2] = counter - counters[2];
+ }
+ else
+ counters[2] -= counter;
+ counters[3] += all;
+ }
+}
+#endif /* L_gcov_merge_delta */
+
#endif /* inhibit_libc */
diff --git a/gcc/profile.c b/gcc/profile.c
index b2ae4a0..2140a0f 100644
--- a/gcc/profile.c
+++ b/gcc/profile.c
@@ -60,6 +60,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
#include "function.h"
#include "toplev.h"
#include "coverage.h"
+#include "value-prof.h"
+#include "tree.h"
/* Additional information about the edges we need. */
struct edge_info {
@@ -105,7 +107,12 @@ static int total_num_branches;
/* Forward declarations. */
static void find_spanning_tree PARAMS ((struct edge_list *));
static rtx gen_edge_profiler PARAMS ((int));
+static rtx gen_interval_profiler (struct histogram_value *, unsigned, unsigned);
+static rtx gen_pow2_profiler (struct histogram_value *, unsigned, unsigned);
+static rtx gen_one_value_profiler (struct histogram_value *, unsigned, unsigned);
+static rtx gen_const_delta_profiler (struct histogram_value *, unsigned, unsigned);
static unsigned instrument_edges PARAMS ((struct edge_list *));
+static void instrument_values (unsigned, struct histogram_value *);
static void compute_branch_probabilities PARAMS ((void));
static gcov_type * get_exec_counts PARAMS ((void));
static basic_block find_group PARAMS ((basic_block));
@@ -157,10 +164,73 @@ instrument_edges (el)
fprintf (rtl_dump_file, "%d edges instrumented\n", num_instr_edges);
return num_instr_edges;
}
+
+/* Add code to measure histograms list of VALUES of length N_VALUES. */
+static void
+instrument_values (unsigned n_values, struct histogram_value *values)
+{
+ rtx sequence;
+ unsigned i, t;
+ edge e;
+
+ /* Emit code to generate the histograms before the insns. */
+
+ for (i = 0; i < n_values; i++)
+ {
+ e = split_block (BLOCK_FOR_INSN (values[i].insn),
+ PREV_INSN (values[i].insn));
+ switch (values[i].type)
+ {
+ case HIST_TYPE_INTERVAL:
+ t = GCOV_COUNTER_V_INTERVAL;
+ break;
+
+ case HIST_TYPE_POW2:
+ t = GCOV_COUNTER_V_POW2;
+ break;
+
+ case HIST_TYPE_SINGLE_VALUE:
+ t = GCOV_COUNTER_V_SINGLE;
+ break;
+
+ case HIST_TYPE_CONST_DELTA:
+ t = GCOV_COUNTER_V_DELTA;
+ break;
+
+ default:
+ abort ();
+ }
+ if (!coverage_counter_alloc (t, values[i].n_counters))
+ continue;
+
+ switch (values[i].type)
+ {
+ case HIST_TYPE_INTERVAL:
+ sequence = gen_interval_profiler (values + i, t, 0);
+ break;
+
+ case HIST_TYPE_POW2:
+ sequence = gen_pow2_profiler (values + i, t, 0);
+ break;
+
+ case HIST_TYPE_SINGLE_VALUE:
+ sequence = gen_one_value_profiler (values + i, t, 0);
+ break;
+
+ case HIST_TYPE_CONST_DELTA:
+ sequence = gen_const_delta_profiler (values + i, t, 0);
+ break;
+
+ default:
+ abort ();
+ }
+
+ safe_insert_insn_on_edge (sequence, e);
+ }
+}
-/* Computes hybrid profile for all matching entries in da_file.
- Sets max_counter_in_program as a side effect. */
+/* Computes hybrid profile for all matching entries in da_file. */
static gcov_type *
get_exec_counts ()
@@ -553,6 +623,8 @@ branch_prob ()
unsigned num_edges, ignored_edges;
unsigned num_instrumented;
struct edge_list *el;
+ unsigned n_values = 0;
+ struct histogram_value *values = NULL;
total_num_times_called++;
@@ -804,6 +876,13 @@ branch_prob ()
EXIT_BLOCK_PTR->index = EXIT_BLOCK;
#undef BB_TO_GCOV_INDEX
+ if (flag_profile_values)
+ {
+ life_analysis (get_insns (), NULL, PROP_DEATH_NOTES);
+ find_values_to_profile (&n_values, &values);
+ allocate_reg_info (max_reg_num (), FALSE, FALSE);
+ }
+
if (flag_branch_probabilities)
compute_branch_probabilities ();
@@ -816,11 +895,16 @@ branch_prob ()
if (n_instrumented != num_instrumented)
abort ();
+ if (flag_profile_values)
+ instrument_values (n_values, values);
+
/* Commit changes done by instrumentation. */
commit_edge_insertions_watch_calls ();
allocate_reg_info (max_reg_num (), FALSE, FALSE);
}
+ if (flag_profile_values)
+ count_or_remove_death_notes (NULL, 1);
remove_fake_edges ();
free_aux_for_edges ();
/* Re-merge split basic blocks and the mess introduced by
@@ -1029,3 +1113,301 @@ gen_edge_profiler (edgeno)
end_sequence ();
return sequence;
}
+
+/* Output instructions as RTL to increment the interval histogram counter.
+ VALUE is the expression whose value is profiled. TAG is the tag of the
+ section for counters, BASE is offset of the counter position. */
+
+static rtx
+gen_interval_profiler (struct histogram_value *value,
+ unsigned tag, unsigned base)
+{
+ unsigned gcov_size = tree_low_cst (TYPE_SIZE (GCOV_TYPE_NODE), 1);
+ enum machine_mode mode = mode_for_size (gcov_size, MODE_INT, 0);
+ rtx mem_ref, tmp, tmp1, mr, val;
+ rtx sequence;
+ rtx more_label = gen_label_rtx ();
+ rtx less_label = gen_label_rtx ();
+ rtx end_of_code_label = gen_label_rtx ();
+ int per_counter = gcov_size / BITS_PER_UNIT;
+
+ start_sequence ();
+
+ if (value->seq)
+ emit_insn (value->seq);
+
+ mr = gen_reg_rtx (Pmode);
+
+ tmp = coverage_counter_ref (tag, base);
+ tmp = force_reg (Pmode, XEXP (tmp, 0));
+
+ val = expand_simple_binop (value->mode, MINUS,
+ copy_rtx (value->value),
+ GEN_INT (value->hdata.intvl.int_start),
+ NULL_RTX, 0, OPTAB_WIDEN);
+
+ if (value->hdata.intvl.may_be_more)
+ do_compare_rtx_and_jump (copy_rtx (val), GEN_INT (value->hdata.intvl.steps),
+ GE, 0, value->mode, NULL_RTX, NULL_RTX, more_label);
+ if (value->hdata.intvl.may_be_less)
+ do_compare_rtx_and_jump (copy_rtx (val), const0_rtx, LT, 0, value->mode,
+ NULL_RTX, NULL_RTX, less_label);
+
+ /* We are in range. */
+ tmp1 = expand_simple_binop (value->mode, MULT,
+ copy_rtx (val), GEN_INT (per_counter),
+ NULL_RTX, 0, OPTAB_WIDEN);
+ tmp1 = expand_simple_binop (Pmode, PLUS, copy_rtx (tmp), tmp1, mr,
+ 0, OPTAB_WIDEN);
+ if (tmp1 != mr)
+ emit_move_insn (copy_rtx (mr), tmp1);
+
+ if (value->hdata.intvl.may_be_more
+ || value->hdata.intvl.may_be_less)
+ {
+ emit_jump_insn (gen_jump (end_of_code_label));
+ emit_barrier ();
+ }
+
+ /* Above the interval. */
+ if (value->hdata.intvl.may_be_more)
+ {
+ emit_label (more_label);
+ tmp1 = expand_simple_binop (Pmode, PLUS, copy_rtx (tmp),
+ GEN_INT (per_counter * value->hdata.intvl.steps),
+ mr, 0, OPTAB_WIDEN);
+ if (tmp1 != mr)
+ emit_move_insn (copy_rtx (mr), tmp1);
+ if (value->hdata.intvl.may_be_less)
+ {
+ emit_jump_insn (gen_jump (end_of_code_label));
+ emit_barrier ();
+ }
+ }
+
+ /* Below the interval. */
+ if (value->hdata.intvl.may_be_less)
+ {
+ emit_label (less_label);
+ tmp1 = expand_simple_binop (Pmode, PLUS, copy_rtx (tmp),
+ GEN_INT (per_counter * (value->hdata.intvl.steps
+ + (value->hdata.intvl.may_be_more ? 1 : 0))),
+ mr, 0, OPTAB_WIDEN);
+ if (tmp1 != mr)
+ emit_move_insn (copy_rtx (mr), tmp1);
+ }
+
+ if (value->hdata.intvl.may_be_more
+ || value->hdata.intvl.may_be_less)
+ emit_label (end_of_code_label);
+
+ mem_ref = validize_mem (gen_rtx_MEM (mode, mr));
+
+ tmp = expand_simple_binop (mode, PLUS, copy_rtx (mem_ref), const1_rtx,
+ mem_ref, 0, OPTAB_WIDEN);
+
+ if (tmp != mem_ref)
+ emit_move_insn (copy_rtx (mem_ref), tmp);
+
+ sequence = get_insns ();
+ end_sequence ();
+ rebuild_jump_labels (sequence);
+ return sequence;
+}
+
+/* Output instructions as RTL to increment the power of two histogram counter.
+ VALUE is the expression whose value is profiled. TAG is the tag of the
+ section for counters, BASE is offset of the counter position. */
+
+static rtx
+gen_pow2_profiler (struct histogram_value *value,
+ unsigned tag, unsigned base)
+{
+ unsigned gcov_size = tree_low_cst (TYPE_SIZE (GCOV_TYPE_NODE), 1);
+ enum machine_mode mode = mode_for_size (gcov_size, MODE_INT, 0);
+ rtx mem_ref, tmp, mr, uval;
+ rtx sequence;
+ rtx end_of_code_label = gen_label_rtx ();
+ rtx loop_label = gen_label_rtx ();
+ int per_counter = gcov_size / BITS_PER_UNIT;
+
+ start_sequence ();
+
+ if (value->seq)
+ emit_insn (value->seq);
+
+ mr = gen_reg_rtx (Pmode);
+ tmp = coverage_counter_ref (tag, base);
+ tmp = force_reg (Pmode, XEXP (tmp, 0));
+ emit_move_insn (mr, tmp);
+
+ uval = gen_reg_rtx (value->mode);
+ emit_move_insn (uval, copy_rtx (value->value));
+
+ /* Check for non-power of 2. */
+ if (value->hdata.pow2.may_be_other)
+ {
+ do_compare_rtx_and_jump (copy_rtx (uval), const0_rtx, LE, 0, value->mode,
+ NULL_RTX, NULL_RTX, end_of_code_label);
+ tmp = expand_simple_binop (value->mode, PLUS, copy_rtx (uval),
+ constm1_rtx, NULL_RTX, 0, OPTAB_WIDEN);
+ tmp = expand_simple_binop (value->mode, AND, copy_rtx (uval), tmp,
+ NULL_RTX, 0, OPTAB_WIDEN);
+ do_compare_rtx_and_jump (tmp, const0_rtx, NE, 0, value->mode, NULL_RTX,
+ NULL_RTX, end_of_code_label);
+ }
+
+ /* Count log_2(value). */
+ emit_label (loop_label);
+
+ tmp = expand_simple_binop (Pmode, PLUS, copy_rtx (mr), GEN_INT (per_counter), mr, 0, OPTAB_WIDEN);
+ if (tmp != mr)
+ emit_move_insn (copy_rtx (mr), tmp);
+
+ tmp = expand_simple_binop (value->mode, ASHIFTRT, copy_rtx (uval), const1_rtx,
+ uval, 0, OPTAB_WIDEN);
+ if (tmp != uval)
+ emit_move_insn (copy_rtx (uval), tmp);
+
+ do_compare_rtx_and_jump (copy_rtx (uval), const0_rtx, NE, 0, value->mode,
+ NULL_RTX, NULL_RTX, loop_label);
+
+ /* Increase the counter. */
+ emit_label (end_of_code_label);
+
+ mem_ref = validize_mem (gen_rtx_MEM (mode, mr));
+
+ tmp = expand_simple_binop (mode, PLUS, copy_rtx (mem_ref), const1_rtx,
+ mem_ref, 0, OPTAB_WIDEN);
+
+ if (tmp != mem_ref)
+ emit_move_insn (copy_rtx (mem_ref), tmp);
+
+ sequence = get_insns ();
+ end_sequence ();
+ rebuild_jump_labels (sequence);
+ return sequence;
+}
+
+/* Output instructions as RTL for code to find the most common value.
+ VALUE is the expression whose value is profiled. TAG is the tag of the
+ section for counters, BASE is offset of the counter position. */
+
+static rtx
+gen_one_value_profiler (struct histogram_value *value,
+ unsigned tag, unsigned base)
+{
+ unsigned gcov_size = tree_low_cst (TYPE_SIZE (GCOV_TYPE_NODE), 1);
+ enum machine_mode mode = mode_for_size (gcov_size, MODE_INT, 0);
+ rtx stored_value_ref, counter_ref, all_ref, stored_value, counter, all;
+ rtx tmp, uval;
+ rtx sequence;
+ rtx same_label = gen_label_rtx ();
+ rtx zero_label = gen_label_rtx ();
+ rtx end_of_code_label = gen_label_rtx ();
+
+ start_sequence ();
+
+ if (value->seq)
+ emit_insn (value->seq);
+
+ stored_value_ref = coverage_counter_ref (tag, base);
+ counter_ref = coverage_counter_ref (tag, base + 1);
+ all_ref = coverage_counter_ref (tag, base + 2);
+ stored_value = validize_mem (stored_value_ref);
+ counter = validize_mem (counter_ref);
+ all = validize_mem (all_ref);
+
+ uval = gen_reg_rtx (mode);
+ convert_move (uval, copy_rtx (value->value), 0);
+
+ /* Check if the stored value matches. */
+ do_compare_rtx_and_jump (copy_rtx (uval), copy_rtx (stored_value), EQ,
+ 0, mode, NULL_RTX, NULL_RTX, same_label);
+
+ /* Does not match; check whether the counter is zero. */
+ do_compare_rtx_and_jump (copy_rtx (counter), const0_rtx, EQ, 0, mode,
+ NULL_RTX, NULL_RTX, zero_label);
+
+ /* The counter is not zero yet. */
+ tmp = expand_simple_binop (mode, PLUS, copy_rtx (counter), constm1_rtx,
+ counter, 0, OPTAB_WIDEN);
+
+ if (tmp != counter)
+ emit_move_insn (copy_rtx (counter), tmp);
+
+ emit_jump_insn (gen_jump (end_of_code_label));
+ emit_barrier ();
+
+ emit_label (zero_label);
+ /* Set new value. */
+ emit_move_insn (copy_rtx (stored_value), copy_rtx (uval));
+
+ emit_label (same_label);
+ /* Increase the counter. */
+ tmp = expand_simple_binop (mode, PLUS, copy_rtx (counter), const1_rtx,
+ counter, 0, OPTAB_WIDEN);
+
+ if (tmp != counter)
+ emit_move_insn (copy_rtx (counter), tmp);
+
+ emit_label (end_of_code_label);
+
+ /* Increase the counter of all executions; this seems redundant given
+ that ve have counts for edges in cfg, but it may happen that some
+ optimization will change the counts for the block (either because
+ it is unable to update them correctly, or because it will duplicate
+ the block or its part). */
+ tmp = expand_simple_binop (mode, PLUS, copy_rtx (all), const1_rtx,
+ all, 0, OPTAB_WIDEN);
+
+ if (tmp != all)
+ emit_move_insn (copy_rtx (all), tmp);
+ sequence = get_insns ();
+ end_sequence ();
+ rebuild_jump_labels (sequence);
+ return sequence;
+}
+
+/* Output instructions as RTL for code to find the most common value of
+ a difference between two evaluations of an expression.
+ VALUE is the expression whose value is profiled. TAG is the tag of the
+ section for counters, BASE is offset of the counter position. */
+
+static rtx
+gen_const_delta_profiler (struct histogram_value *value,
+ unsigned tag, unsigned base)
+{
+ struct histogram_value one_value_delta;
+ unsigned gcov_size = tree_low_cst (TYPE_SIZE (GCOV_TYPE_NODE), 1);
+ enum machine_mode mode = mode_for_size (gcov_size, MODE_INT, 0);
+ rtx stored_value_ref, stored_value, tmp, uval;
+ rtx sequence;
+
+ start_sequence ();
+
+ if (value->seq)
+ emit_insn (value->seq);
+
+ stored_value_ref = coverage_counter_ref (tag, base);
+ stored_value = validize_mem (stored_value_ref);
+
+ uval = gen_reg_rtx (mode);
+ convert_move (uval, copy_rtx (value->value), 0);
+ tmp = expand_simple_binop (mode, MINUS,
+ copy_rtx (uval), copy_rtx (stored_value),
+ NULL_RTX, 0, OPTAB_WIDEN);
+
+ one_value_delta.value = tmp;
+ one_value_delta.mode = mode;
+ one_value_delta.seq = NULL_RTX;
+ one_value_delta.insn = value->insn;
+ one_value_delta.type = HIST_TYPE_SINGLE_VALUE;
+ emit_insn (gen_one_value_profiler (&one_value_delta, tag, base + 1));
+
+ emit_move_insn (copy_rtx (stored_value), uval);
+ sequence = get_insns ();
+ end_sequence ();
+ rebuild_jump_labels (sequence);
+ return sequence;
+}
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 68fbb29..6d1dd44 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -410,6 +410,10 @@ int profile_flag = 0;
int profile_arc_flag = 0;
+/* Nonzero if value histograms should be measured. */
+
+int flag_profile_values = 0;
+
/* Nonzero if generating info for gcov to calculate line test coverage. */
int flag_test_coverage = 0;
@@ -1184,6 +1188,8 @@ static const lang_independent_options f_options[] =
N_("Create data files needed by gcov") },
{"branch-probabilities", &flag_dummy, 1,
N_("Use profiling information for branch probabilities") },
+ {"profile-values", &flag_profile_values, 1,
+ N_("Insert code to profile values of expressions") },
{"profile", &flag_dummy, 1,
N_("Enable basic program profiling code") },
{"reorder-blocks", &flag_dummy, 1,