value-prof.c: New.
authorZdenek Dvorak <rakdver@atrey.karlin.mff.cuni.cz>
Thu, 26 Jun 2003 07:52:10 +0000 (09:52 +0200)
committerZdenek Dvorak <rakdver@gcc.gnu.org>
Thu, 26 Jun 2003 07:52:10 +0000 (07:52 +0000)
* 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.

From-SVN: r68519

gcc/ChangeLog
gcc/Makefile.in
gcc/flags.h
gcc/flow.c
gcc/gcov-io.h
gcc/libgcov.c
gcc/profile.c
gcc/toplev.c

index 07d016c..5c3911b 100644 (file)
@@ -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.
index 5029c52..c6c32ae 100644 (file)
@@ -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 \
index 6ef4ece..e7a3ea8 100644 (file)
@@ -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;
index 6949beb..b08a6e8 100644 (file)
@@ -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))
index 5565408..769fd8a 100644 (file)
@@ -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
index ba54281..5396c39 100644 (file)
@@ -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 */
index b2ae4a0..2140a0f 100644 (file)
@@ -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);
+    }
+}
 \f
 
-/* 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;
+}
index 68fbb29..6d1dd44 100644 (file)
@@ -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,