OSDN Git Service

* libgcov.c (gcov_exit): Cleanup and fix.
[pf3gnuchains/gcc-fork.git] / gcc / profile.c
index 310277f..0994981 100644 (file)
@@ -60,14 +60,16 @@ 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 {
   unsigned int count_valid : 1;
-  
+
   /* Is on the spanning tree.  */
   unsigned int on_tree : 1;
-  
+
   /* Pretend this edge does not exist (it is abnormal and we've
      inserted a fake to compensate).  */
   unsigned int ignore : 1;
@@ -84,7 +86,7 @@ struct bb_info {
 #define EDGE_INFO(e)  ((struct edge_info *) (e)->aux)
 #define BB_INFO(b)  ((struct bb_info *) (b)->aux)
 
-/* Counter summary from the last set of coverage counts read. */
+/* Counter summary from the last set of coverage counts read.  */
 
 const struct gcov_ctr_summary *profile_info;
 
@@ -103,13 +105,22 @@ static int total_num_never_executed;
 static int total_num_branches;
 
 /* Forward declarations.  */
-static void find_spanning_tree PARAMS ((struct edge_list *));
-static rtx gen_edge_profiler PARAMS ((int));
-static unsigned instrument_edges PARAMS ((struct edge_list *));
-static void compute_branch_probabilities PARAMS ((void));
-static gcov_type * get_exec_counts PARAMS ((void));
-static basic_block find_group PARAMS ((basic_block));
-static void union_groups PARAMS ((basic_block, basic_block));
+static void find_spanning_tree (struct edge_list *);
+static rtx gen_edge_profiler (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 (struct edge_list *);
+static void instrument_values (unsigned, struct histogram_value *);
+static void compute_branch_probabilities (void);
+static void compute_value_histograms (unsigned, struct histogram_value *);
+static gcov_type * get_exec_counts (void);
+static basic_block find_group (basic_block);
+static void union_groups (basic_block, basic_block);
 
 \f
 /* Add edge instrumentation code to the entire insn chain.
@@ -118,13 +129,12 @@ static void union_groups PARAMS ((basic_block, basic_block));
    NUM_BLOCKS is the number of basic blocks found in F.  */
 
 static unsigned
-instrument_edges (el)
-     struct edge_list *el;
+instrument_edges (struct edge_list *el)
 {
   unsigned num_instr_edges = 0;
   int num_edges = NUM_EDGES (el);
   basic_block bb;
-  
+
   remove_fake_edges ();
 
   FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb)
@@ -134,11 +144,11 @@ instrument_edges (el)
       for (e = bb->succ; e; e = e->succ_next)
        {
          struct edge_info *inf = EDGE_INFO (e);
-         
+
          if (!inf->ignore && !inf->on_tree)
            {
              rtx edge_profile;
-             
+
              if (e->flags & EDGE_ABNORMAL)
                abort ();
              if (rtl_dump_file)
@@ -157,18 +167,81 @@ 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 ()
+get_exec_counts (void)
 {
   unsigned num_edges = 0;
   basic_block bb;
   gcov_type *counts;
-  
+
   /* Count the edges to be (possibly) instrumented.  */
   FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb)
     {
@@ -194,7 +267,7 @@ get_exec_counts ()
    Annotate them accordingly.  */
 
 static void
-compute_branch_probabilities ()
+compute_branch_probabilities (void)
 {
   basic_block bb;
   int i;
@@ -529,6 +602,63 @@ compute_branch_probabilities ()
   free_aux_for_blocks ();
 }
 
+/* Load value histograms for N_VALUES values whose description is stored
+   in VALUES array from .da file.  */
+static void
+compute_value_histograms (unsigned n_values, struct histogram_value *values)
+{
+  unsigned i, j, t, any;
+  unsigned n_histogram_counters[GCOV_N_VALUE_COUNTERS];
+  gcov_type *histogram_counts[GCOV_N_VALUE_COUNTERS];
+  gcov_type *act_count[GCOV_N_VALUE_COUNTERS];
+  gcov_type *aact_count;
+  for (t = 0; t < GCOV_N_VALUE_COUNTERS; t++)
+    n_histogram_counters[t] = 0;
+
+  for (i = 0; i < n_values; i++)
+    n_histogram_counters[(int) (values[i].type)] += values[i].n_counters;
+
+  any = 0;
+  for (t = 0; t < GCOV_N_VALUE_COUNTERS; t++)
+    {
+      if (!n_histogram_counters[t])
+       {
+         histogram_counts[t] = NULL;
+         continue;
+       }
+
+      histogram_counts[t] =
+       get_coverage_counts (COUNTER_FOR_HIST_TYPE (t),
+                            n_histogram_counters[t], NULL);
+      if (histogram_counts[t])
+       any = 1;
+      act_count[t] = histogram_counts[t];
+    }
+  if (!any)
+    return;
+
+  for (i = 0; i < n_values; i++)
+    {
+      rtx hist_list = NULL_RTX;
+      t = (int) (values[i].type);
+
+      aact_count = act_count[t];
+      act_count[t] += values[i].n_counters;
+      for (j = values[i].n_counters; j > 0; j--)
+       hist_list = alloc_EXPR_LIST (0, GEN_INT (aact_count[j - 1]), hist_list);
+      hist_list = alloc_EXPR_LIST (0, copy_rtx (values[i].value), hist_list);
+      hist_list = alloc_EXPR_LIST (0, GEN_INT (values[i].type), hist_list);
+      REG_NOTES (values[i].insn) =
+             alloc_EXPR_LIST (REG_VALUE_PROFILE, hist_list,
+                              REG_NOTES (values[i].insn));
+    }
+
+  for (t = 0; t < GCOV_N_VALUE_COUNTERS; t++)
+    if (histogram_counts[t])
+      free (histogram_counts[t]);
+}
+
 /* Instrument and/or analyze program behavior based on program flow graph.
    In either case, this function builds a flow graph for the function being
    compiled.  The flow graph is stored in BB_GRAPH.
@@ -546,13 +676,15 @@ compute_branch_probabilities ()
    Main entry point of this file.  */
 
 void
-branch_prob ()
+branch_prob (void)
 {
   basic_block bb;
   unsigned i;
   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++;
 
@@ -644,7 +776,7 @@ branch_prob ()
      as possible to minimize number of edge splits necessary.  */
 
   find_spanning_tree (el);
-  
+
   /* Fake edges that are not on the tree will not be instrumented, so
      mark them ignored.  */
   for (num_instrumented = i = 0; i < num_edges; i++)
@@ -682,7 +814,7 @@ branch_prob ()
   if (coverage_begin_output ())
     {
       gcov_position_t offset;
-      
+
       offset = gcov_write_tag (GCOV_TAG_BLOCKS);
       for (i = 0; i != (unsigned) (n_basic_blocks + 2); i++)
        gcov_write_unsigned (0);
@@ -695,7 +827,7 @@ branch_prob ()
   ENTRY_BLOCK_PTR->index = -1;
   EXIT_BLOCK_PTR->index = last_basic_block;
 #define BB_TO_GCOV_INDEX(bb)  ((bb)->index + 1)
-  
+
   /* Arcs */
   if (coverage_begin_output ())
     {
@@ -707,14 +839,14 @@ branch_prob ()
 
          offset = gcov_write_tag (GCOV_TAG_ARCS);
          gcov_write_unsigned (BB_TO_GCOV_INDEX (bb));
-         
+
          for (e = bb->succ; e; e = e->succ_next)
            {
              struct edge_info *i = EDGE_INFO (e);
              if (!i->ignore)
                {
                  unsigned flag_bits = 0;
-                 
+
                  if (i->on_tree)
                    flag_bits |= GCOV_ARC_ON_TREE;
                  if (e->flags & EDGE_FAKE)
@@ -730,20 +862,20 @@ branch_prob ()
          gcov_write_length (offset);
        }
     }
-  
-  /* Line numbers. */
+
+  /* Line numbers.  */
   if (coverage_begin_output ())
     {
       char const *prev_file_name = NULL;
       gcov_position_t offset;
-      
+
       FOR_EACH_BB (bb)
        {
          rtx insn = bb->head;
          int ignore_next_note = 0;
-         
+
          offset = 0;
-         
+
          /* We are looking for line number notes.  Search backward
             before basic block to find correct ones.  */
          insn = prev_nonnote_insn (insn);
@@ -751,7 +883,7 @@ branch_prob ()
            insn = get_insns ();
          else
            insn = NEXT_INSN (insn);
-         
+
          while (insn != bb->end)
            {
              if (GET_CODE (insn) == NOTE)
@@ -774,7 +906,7 @@ branch_prob ()
                          offset = gcov_write_tag (GCOV_TAG_LINES);
                          gcov_write_unsigned (BB_TO_GCOV_INDEX (bb));
                        }
-                     
+
                      /* If this is a new source file, then output the
                         file's name to the .bb file.  */
                      if (!prev_file_name
@@ -790,7 +922,7 @@ branch_prob ()
                }
              insn = NEXT_INSN (insn);
            }
-         
+
          if (offset)
            {
              /* A file of NULL indicates the end of run.  */
@@ -804,8 +936,19 @@ 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 ();
+    {
+      compute_branch_probabilities ();
+      if (flag_profile_values)
+       compute_value_histograms (n_values, values);
+    }
 
   /* For each edge not on the spanning tree, add counting code as rtl.  */
   if (profile_arc_flag
@@ -816,11 +959,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
@@ -836,8 +984,7 @@ branch_prob ()
    aux fields.  */
 
 static basic_block
-find_group (bb)
-     basic_block bb;
+find_group (basic_block bb)
 {
   basic_block group = bb, bb1;
 
@@ -855,8 +1002,7 @@ find_group (bb)
 }
 
 static void
-union_groups (bb1, bb2)
-     basic_block bb1, bb2;
+union_groups (basic_block bb1, basic_block bb2)
 {
   basic_block bb1g = find_group (bb1);
   basic_block bb2g = find_group (bb2);
@@ -877,8 +1023,7 @@ union_groups (bb1, bb2)
    are more expensive to instrument.  */
 
 static void
-find_spanning_tree (el)
-     struct edge_list *el;
+find_spanning_tree (struct edge_list *el)
 {
   int i;
   int num_edges = NUM_EDGES (el);
@@ -947,7 +1092,7 @@ find_spanning_tree (el)
 /* Perform file-level initialization for branch-prob processing.  */
 
 void
-init_branch_prob ()
+init_branch_prob (void)
 {
   int i;
 
@@ -968,7 +1113,7 @@ init_branch_prob ()
    is completed.  */
 
 void
-end_branch_prob ()
+end_branch_prob (void)
 {
   if (rtl_dump_file)
     {
@@ -1008,8 +1153,7 @@ end_branch_prob ()
 /* Output instructions as RTL to increment the edge execution count.  */
 
 static rtx
-gen_edge_profiler (edgeno)
-     int edgeno;
+gen_edge_profiler (int edgeno)
 {
   rtx ref = coverage_counter_ref (GCOV_COUNTER_ARCS, edgeno);
   rtx tmp;
@@ -1029,3 +1173,300 @@ 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;
+}