diff gcc/value-prof.c @ 145:1830386684a0

gcc-9.2.0
author anatofuz
date Thu, 13 Feb 2020 11:34:05 +0900
parents 84e7813d76e9
children
line wrap: on
line diff
--- a/gcc/value-prof.c	Thu Oct 25 07:37:49 2018 +0900
+++ b/gcc/value-prof.c	Thu Feb 13 11:34:05 2020 +0900
@@ -1,5 +1,5 @@
 /* Transformations based on profile information for values.
-   Copyright (C) 2003-2018 Free Software Foundation, Inc.
+   Copyright (C) 2003-2020 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -42,7 +42,6 @@
 #include "gimple-pretty-print.h"
 #include "dumpfile.h"
 #include "builtins.h"
-#include "params.h"
 
 /* In this file value profile based optimizations are placed.  Currently the
    following optimizations are implemented (for more detailed descriptions
@@ -107,7 +106,7 @@
 static bool gimple_mod_pow2_value_transform (gimple_stmt_iterator *);
 static bool gimple_mod_subtract_transform (gimple_stmt_iterator *);
 static bool gimple_stringops_transform (gimple_stmt_iterator *);
-static bool gimple_ic_transform (gimple_stmt_iterator *);
+static void dump_ic_profile (gimple_stmt_iterator *gsi);
 
 /* Allocate histogram value.  */
 
@@ -228,111 +227,79 @@
   switch (hist->type)
     {
     case HIST_TYPE_INTERVAL:
-      fprintf (dump_file, "Interval counter range %d -- %d",
-	       hist->hdata.intvl.int_start,
-	       (hist->hdata.intvl.int_start
-	        + hist->hdata.intvl.steps - 1));
       if (hist->hvalue.counters)
 	{
-	   unsigned int i;
-	   fprintf (dump_file, " [");
-           for (i = 0; i < hist->hdata.intvl.steps; i++)
-	     fprintf (dump_file, " %d:%" PRId64,
-		      hist->hdata.intvl.int_start + i,
-		      (int64_t) hist->hvalue.counters[i]);
-	   fprintf (dump_file, " ] outside range:%" PRId64,
-		    (int64_t) hist->hvalue.counters[i]);
+	  fprintf (dump_file, "Interval counter range [%d,%d]: [",
+		   hist->hdata.intvl.int_start,
+		   (hist->hdata.intvl.int_start
+		    + hist->hdata.intvl.steps - 1));
+
+	  unsigned int i;
+	  for (i = 0; i < hist->hdata.intvl.steps; i++)
+	    {
+	      fprintf (dump_file, "%d:%" PRId64,
+		       hist->hdata.intvl.int_start + i,
+		       (int64_t) hist->hvalue.counters[i]);
+	      if (i != hist->hdata.intvl.steps - 1)
+		fprintf (dump_file, ", ");
+	    }
+	  fprintf (dump_file, "] outside range: %" PRId64 ".\n",
+		   (int64_t) hist->hvalue.counters[i]);
 	}
-      fprintf (dump_file, ".\n");
       break;
 
     case HIST_TYPE_POW2:
-      fprintf (dump_file, "Pow2 counter ");
       if (hist->hvalue.counters)
-	{
-	   fprintf (dump_file, "pow2:%" PRId64
-		    " nonpow2:%" PRId64,
-		    (int64_t) hist->hvalue.counters[1],
-		    (int64_t) hist->hvalue.counters[0]);
-	}
-      fprintf (dump_file, ".\n");
+	fprintf (dump_file, "Pow2 counter pow2:%" PRId64
+		 " nonpow2:%" PRId64 ".\n",
+		 (int64_t) hist->hvalue.counters[1],
+		 (int64_t) hist->hvalue.counters[0]);
       break;
 
-    case HIST_TYPE_SINGLE_VALUE:
-      fprintf (dump_file, "Single value ");
+    case HIST_TYPE_TOPN_VALUES:
+    case HIST_TYPE_INDIR_CALL:
       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,
+		   (hist->type == HIST_TYPE_TOPN_VALUES
+		    ? "Top N value counter" : "Indirect call counter"));
+	  if (hist->hvalue.counters)
+	    {
+	      fprintf (dump_file, " all: %" PRId64 ", values: ",
+		       (int64_t) hist->hvalue.counters[0]);
+	      for (unsigned i = 0; i < GCOV_TOPN_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_TOPN_VALUES - 1)
+		    fprintf (dump_file, ", ");
+		}
+	      fprintf (dump_file, ".\n");
+	    }
 	}
-      fprintf (dump_file, ".\n");
       break;
 
     case HIST_TYPE_AVERAGE:
-      fprintf (dump_file, "Average value ");
       if (hist->hvalue.counters)
-	{
-	   fprintf (dump_file, "sum:%" PRId64
-		    " times:%" PRId64,
-		    (int64_t) hist->hvalue.counters[0],
-		    (int64_t) hist->hvalue.counters[1]);
-	}
-      fprintf (dump_file, ".\n");
+	fprintf (dump_file, "Average value sum:%" PRId64
+		 " times:%" PRId64 ".\n",
+		 (int64_t) hist->hvalue.counters[0],
+		 (int64_t) hist->hvalue.counters[1]);
       break;
 
     case HIST_TYPE_IOR:
-      fprintf (dump_file, "IOR value ");
       if (hist->hvalue.counters)
-	{
-	   fprintf (dump_file, "ior:%" PRId64,
-		    (int64_t) hist->hvalue.counters[0]);
-	}
-      fprintf (dump_file, ".\n");
+	fprintf (dump_file, "IOR value ior:%" PRId64 ".\n",
+		 (int64_t) hist->hvalue.counters[0]);
       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)
-      {
-        fprintf (dump_file, "time:%" PRId64,
-                 (int64_t) hist->hvalue.counters[0]);
-      }
-      fprintf (dump_file, ".\n");
+	fprintf (dump_file, "Time profile time:%" PRId64 ".\n",
+		 (int64_t) hist->hvalue.counters[0]);
       break;
-    case HIST_TYPE_INDIR_CALL_TOPN:
-      fprintf (dump_file, "Indirect call topn ");
-      if (hist->hvalue.counters)
-	{
-           int i;
-
-           fprintf (dump_file, "accu:%" PRId64, hist->hvalue.counters[0]);
-           for (i = 1; i < (GCOV_ICALL_TOPN_VAL << 2); i += 2)
-             {
-               fprintf (dump_file, " target:%" PRId64 " value:%" PRId64,
-                       (int64_t) hist->hvalue.counters[i],
-                       (int64_t) hist->hvalue.counters[i+1]);
-             }
-        }
-      fprintf (dump_file, ".\n");
-      break;
-    case HIST_TYPE_MAX:
+    default:
       gcc_unreachable ();
    }
 }
@@ -363,7 +330,7 @@
       /* 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_TOPN_VALUES && i > 0)
 	;
       else
 	gcc_assert (value >= 0);
@@ -377,7 +344,7 @@
 /* Dump information about HIST to DUMP_FILE.  */
 
 void
-stream_in_histogram_value (struct lto_input_block *ib, gimple *stmt)
+stream_in_histogram_value (class lto_input_block *ib, gimple *stmt)
 {
   enum hist_type type;
   unsigned int ncounters = 0;
@@ -392,7 +359,7 @@
       bp = streamer_read_bitpack (ib);
       type = bp_unpack_enum (&bp, hist_type, HIST_TYPE_MAX);
       next = bp_unpack_value (&bp, 1);
-      new_val = gimple_alloc_histogram_value (cfun, type, stmt, NULL);
+      new_val = gimple_alloc_histogram_value (cfun, type, stmt);
       switch (type)
 	{
 	case HIST_TYPE_INTERVAL:
@@ -406,9 +373,9 @@
 	  ncounters = 2;
 	  break;
 
-	case HIST_TYPE_SINGLE_VALUE:
+	case HIST_TYPE_TOPN_VALUES:
 	case HIST_TYPE_INDIR_CALL:
-	  ncounters = 3;
+	  ncounters = GCOV_TOPN_VALUES_COUNTERS;
 	  break;
 
 	case HIST_TYPE_IOR:
@@ -416,14 +383,12 @@
 	  ncounters = 1;
 	  break;
 
-        case HIST_TYPE_INDIR_CALL_TOPN:
-          ncounters = (GCOV_ICALL_TOPN_VAL << 2) + 1;
-          break;
-
-	case HIST_TYPE_MAX:
+	default:
 	  gcc_unreachable ();
 	}
-      new_val->hvalue.counters = XNEWVAR (gcov_type, sizeof (*new_val->hvalue.counters) * ncounters);
+      new_val->hvalue.counters = XNEWVAR (gcov_type,
+					  sizeof (*new_val->hvalue.counters)
+					  * ncounters);
       new_val->n_counters = ncounters;
       for (i = 0; i < ncounters; i++)
 	new_val->hvalue.counters[i] = streamer_read_gcov_count (ib);
@@ -465,7 +430,7 @@
   histogram_value val;
   for (val = gimple_histogram_value (ofun, ostmt); val != NULL; val = val->hvalue.next)
     {
-      histogram_value new_val = gimple_alloc_histogram_value (fun, val->type, NULL, NULL);
+      histogram_value new_val = gimple_alloc_histogram_value (fun, val->type);
       memcpy (new_val, val, sizeof (*val));
       new_val->hvalue.stmt = stmt;
       new_val->hvalue.counters = XNEWVAR (gcov_type, sizeof (*new_val->hvalue.counters) * new_val->n_counters);
@@ -535,7 +500,7 @@
 	  {
 	    if (hist->hvalue.stmt != stmt)
 	      {
-		error ("Histogram value statement does not correspond to "
+		error ("histogram value statement does not correspond to "
 		       "the statement it is associated with");
 		debug_gimple_stmt (stmt);
 		dump_histogram_value (stderr, hist);
@@ -547,7 +512,7 @@
   if (VALUE_HISTOGRAMS (cfun))
     htab_traverse (VALUE_HISTOGRAMS (cfun), visit_hist, &visited_hists);
   if (error_found)
-    internal_error ("verify_histograms failed");
+    internal_error ("%qs failed", __func__);
 }
 
 /* Helper function for verify_histograms.  For each histogram reachable via htab
@@ -627,11 +592,6 @@
   gimple_stmt_iterator gsi;
   bool changed = false;
 
-  /* Autofdo does its own transformations for indirect calls,
-     and otherwise does not support value profiling.  */
-  if (flag_auto_profile)
-    return false;
-
   FOR_EACH_BB_FN (bb, cfun)
     {
       for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
@@ -658,8 +618,7 @@
 	  if (gimple_mod_subtract_transform (&gsi)
 	      || gimple_divmod_fixed_value_transform (&gsi)
 	      || gimple_mod_pow2_value_transform (&gsi)
-	      || gimple_stringops_transform (&gsi)
-	      || gimple_ic_transform (&gsi))
+	      || gimple_stringops_transform (&gsi))
 	    {
 	      stmt = gsi_stmt (gsi);
 	      changed = true;
@@ -670,6 +629,10 @@
 		  gsi = gsi_for_stmt (stmt);
 		}
 	    }
+
+	  /* The function never thansforms a GIMPLE statement.  */
+	  if (dump_enabled_p ())
+	    dump_ic_profile (&gsi);
         }
     }
 
@@ -754,6 +717,41 @@
   return tmp2;
 }
 
+/* Return the n-th value count of TOPN_VALUE histogram.  If
+   there's a value, return true and set VALUE and COUNT
+   arguments.  */
+
+bool
+get_nth_most_common_value (gimple *stmt, const char *counter_type,
+			   histogram_value hist, gcov_type *value,
+			   gcov_type *count, gcov_type *all, unsigned n)
+{
+  if (hist->hvalue.counters[2] == -1)
+    return false;
+
+  gcc_assert (n < GCOV_TOPN_VALUES);
+
+  *count = 0;
+  *value = 0;
+
+  gcov_type read_all = hist->hvalue.counters[0];
+
+  gcov_type v = hist->hvalue.counters[2 * n + 1];
+  gcov_type c = hist->hvalue.counters[2 * n + 2];
+
+  /* Indirect calls can't be verified.  */
+  if (stmt
+      && check_counter (stmt, counter_type, &c, &read_all,
+			gimple_bb (stmt)->count))
+    return false;
+
+  *all = read_all;
+
+  *value = v;
+  *count = c;
+  return true;
+}
+
 /* Do transform 1) on INSN if applicable.  */
 
 static bool
@@ -779,27 +777,23 @@
     return false;
 
   histogram = gimple_histogram_value_of_type (cfun, stmt,
-					      HIST_TYPE_SINGLE_VALUE);
+					      HIST_TYPE_TOPN_VALUES);
   if (!histogram)
     return false;
 
+  if (!get_nth_most_common_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);
@@ -819,12 +813,9 @@
     }
   result = gimple_divmod_fixed_value (stmt, tree_val, prob, count, all);
 
-  if (dump_file)
-    {
-      fprintf (dump_file, "Transformation done: div/mod by constant ");
-      print_generic_expr (dump_file, tree_val, TDF_SLIM);
-      fprintf (dump_file, "\n");
-    }
+  if (dump_enabled_p ())
+    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
+		     "Transformation done: div/mod by constant %T\n", tree_val);
 
   gimple_assign_set_rhs_from_tree (si, result);
   update_stmt (gsi_stmt (*si));
@@ -959,8 +950,9 @@
   if (check_counter (stmt, "pow2", &count, &all, gimple_bb (stmt)->count))
     return false;
 
-  if (dump_file)
-    fprintf (dump_file, "Transformation done: mod power of 2\n");
+  if (dump_enabled_p ())
+    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
+		     "Transformation done: mod power of 2\n");
 
   if (all > 0)
     prob = profile_probability::probability_in_gcov_type (count, all);
@@ -1118,7 +1110,6 @@
   count1 = histogram->hvalue.counters[0];
   count2 = histogram->hvalue.counters[1];
 
-  /* Compute probability of taking the optimal path.  */
   if (check_counter (stmt, "interval", &count1, &all, gimple_bb (stmt)->count))
     {
       gimple_remove_histogram_value (cfun, stmt, histogram);
@@ -1144,8 +1135,9 @@
     return false;
 
   gimple_remove_histogram_value (cfun, stmt, histogram);
-  if (dump_file)
-    fprintf (dump_file, "Transformation done: mod subtract\n");
+  if (dump_enabled_p ())
+    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
+		     "Transformation done: mod subtract\n");
 
   /* Compute probability of taking the optimal path(s).  */
   if (all > 0)
@@ -1193,40 +1185,43 @@
   cgraph_node_map = new hash_map<profile_id_hash, cgraph_node *>;
 
   FOR_EACH_DEFINED_FUNCTION (n)
-    if (n->has_gimple_body_p ())
+    if (n->has_gimple_body_p () || n->thunk.thunk_p)
       {
 	cgraph_node **val;
+	dump_user_location_t loc
+	  = dump_user_location_t::from_function_decl (n->decl);
 	if (local)
 	  {
 	    n->profile_id = coverage_compute_profile_id (n);
 	    while ((val = cgraph_node_map->get (n->profile_id))
 		   || !n->profile_id)
 	      {
-		if (dump_file)
-		  fprintf (dump_file, "Local profile-id %i conflict"
-			   " with nodes %s %s\n",
-			   n->profile_id,
-			   n->dump_name (),
-			   (*val)->dump_name ());
+		if (dump_enabled_p ())
+		  dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc,
+				   "Local profile-id %i conflict"
+				   " with nodes %s %s\n",
+				   n->profile_id,
+				   n->dump_name (),
+				   (*val)->dump_name ());
 		n->profile_id = (n->profile_id + 1) & 0x7fffffff;
 	      }
 	  }
 	else if (!n->profile_id)
 	  {
-	    if (dump_file)
-	      fprintf (dump_file,
-		       "Node %s has no profile-id"
-		       " (profile feedback missing?)\n",
-		       n->dump_name ());
+	    if (dump_enabled_p ())
+	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc,
+			       "Node %s has no profile-id"
+			       " (profile feedback missing?)\n",
+			       n->dump_name ());
 	    continue;
 	  }
 	else if ((val = cgraph_node_map->get (n->profile_id)))
 	  {
-	    if (dump_file)
-	      fprintf (dump_file,
-		       "Node %s has IP profile-id %i conflict. "
-		       "Giving up.\n",
-		       n->dump_name (), n->profile_id);
+	    if (dump_enabled_p ())
+	      dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc,
+			       "Node %s has IP profile-id %i conflict. "
+			       "Giving up.\n",
+			       n->dump_name (), n->profile_id);
 	    *val = NULL;
 	    continue;
 	  }
@@ -1254,25 +1249,6 @@
     return NULL;
 }
 
-/* Perform sanity check on the indirect call target. Due to race conditions,
-   false function target may be attributed to an indirect call site. If the
-   call expression type mismatches with the target function's type, expand_call
-   may ICE. Here we only do very minimal sanity check just to make compiler happy.
-   Returns true if TARGET is considered ok for call CALL_STMT.  */
-
-bool
-check_ic_target (gcall *call_stmt, struct cgraph_node *target)
-{
-   if (gimple_check_call_matching_types (call_stmt, target->decl, true))
-     return true;
-
-   if (dump_enabled_p ())
-     dump_printf_loc (MSG_MISSED_OPTIMIZATION, call_stmt,
-                      "Skipping target %s with mismatching types for icall\n",
-                      target->name ());
-   return false;
-}
-
 /* Do transformation
 
   if (actual_callee_address == address_of_most_common_function/method)
@@ -1415,97 +1391,58 @@
   return dcall_stmt;
 }
 
-/*
-  For every checked indirect/virtual call determine if most common pid of
-  function/class method has probability more than 50%. If yes modify code of
-  this call to:
- */
+/* Dump info about indirect call profile.  */
 
-static bool
-gimple_ic_transform (gimple_stmt_iterator *gsi)
+static void
+dump_ic_profile (gimple_stmt_iterator *gsi)
 {
   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 (!stmt)
-    return false;
+    return;
 
   if (gimple_call_fndecl (stmt) != NULL_TREE)
-    return false;
+    return;
 
   if (gimple_call_internal_p (stmt))
-    return false;
+    return;
 
   histogram = gimple_histogram_value_of_type (cfun, stmt, HIST_TYPE_INDIR_CALL);
   if (!histogram)
-    return false;
-
-  val = histogram->hvalue.counters [0];
-  count = histogram->hvalue.counters [1];
-  all = histogram->hvalue.counters [2];
+    return;
 
-  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;
-    }
+  count = 0;
+  all = histogram->hvalue.counters[0];
 
-  if (4 * count <= 3 * all)
-    return false;
-
-  direct_call = find_func_by_profile_id ((int)val);
-
-  if (direct_call == NULL)
+  for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
     {
-      if (val)
-	{
-	  if (dump_file)
-	    {
-	      fprintf (dump_file, "Indirect call -> direct call from other module");
-	      print_generic_expr (dump_file, gimple_call_fn (stmt), TDF_SLIM);
-	      fprintf (dump_file, "=> %i (will resolve only with LTO)\n", (int)val);
-	    }
-	}
-      return false;
-    }
+      if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
+				      &count, &all, j))
+	return;
+      if (!count)
+	continue;
+
+      direct_call = find_func_by_profile_id ((int) val);
 
-  if (!check_ic_target (stmt, direct_call))
-    {
-      if (dump_file)
-	{
-	  fprintf (dump_file, "Indirect call -> direct call ");
-	  print_generic_expr (dump_file, gimple_call_fn (stmt), TDF_SLIM);
-	  fprintf (dump_file, "=> ");
-	  print_generic_expr (dump_file, direct_call->decl, TDF_SLIM);
-	  fprintf (dump_file, " transformation skipped because of type mismatch");
-	  print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
-	}
-      gimple_remove_histogram_value (cfun, stmt, histogram);
-      return false;
+      if (direct_call == NULL)
+	dump_printf_loc (
+	  MSG_MISSED_OPTIMIZATION, stmt,
+	  "Indirect call -> direct call from other "
+	  "module %T=> %i (will resolve by ipa-profile only with LTO)\n",
+	  gimple_call_fn (stmt), (int) val);
+      else
+	dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
+			 "Indirect call -> direct call "
+			 "%T => %T (will resolve by ipa-profile)\n",
+			 gimple_call_fn (stmt), direct_call->decl);
+      dump_printf_loc (MSG_NOTE, stmt,
+		       "hist->count %" PRId64 " hist->all %" PRId64 "\n",
+		       count, all);
     }
-
-  if (dump_file)
-    {
-      fprintf (dump_file, "Indirect call -> direct call ");
-      print_generic_expr (dump_file, gimple_call_fn (stmt), TDF_SLIM);
-      fprintf (dump_file, "=> ");
-      print_generic_expr (dump_file, direct_call->decl, TDF_SLIM);
-      fprintf (dump_file, " transformation on insn postponned to ipa-profile");
-      print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
-      fprintf (dump_file, "hist->count %" PRId64
-	       " hist->all %" PRId64"\n", count, all);
-    }
-
-  return true;
 }
 
 /* Return true if the stringop CALL shall be profiled.  SIZE_ARG be
@@ -1669,19 +1606,19 @@
   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_TOPN_VALUES);
   if (!histogram)
     return false;
 
-  val = histogram->hvalue.counters[0];
-  count = histogram->hvalue.counters[1];
-  all = histogram->hvalue.counters[2];
+  if (!get_nth_most_common_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;
@@ -1731,10 +1668,10 @@
 	TYPE_PRECISION (get_gcov_type ()), false));
     }
 
-  if (dump_file)
-    fprintf (dump_file,
-	     "Transformation done: single value %i stringop for %s\n",
-	     (int)val, built_in_names[(int)fcode]);
+  if (dump_enabled_p ())
+    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
+		     "Transformation done: single value %i stringop for %s\n",
+		     (int)val, built_in_names[(int)fcode]);
 
   gimple_stringop_fixed_value (stmt, tree_val, prob, count, all);
 
@@ -1818,14 +1755,12 @@
       divisor = gimple_assign_rhs2 (stmt);
       op0 = gimple_assign_rhs1 (stmt);
 
-      values->reserve (3);
-
       if (TREE_CODE (divisor) == SSA_NAME)
 	/* Check for the case where the divisor is the same value most
 	   of the time.  */
-	values->quick_push (gimple_alloc_histogram_value (cfun,
-						      HIST_TYPE_SINGLE_VALUE,
-						      stmt, divisor));
+	values->safe_push (gimple_alloc_histogram_value (cfun,
+							 HIST_TYPE_TOPN_VALUES,
+							 stmt, divisor));
 
       /* For mod, check whether it is not often a noop (or replaceable by
 	 a few subtractions).  */
@@ -1835,16 +1770,15 @@
 	{
           tree val;
           /* Check for a special case where the divisor is power of 2.  */
-	  values->quick_push (gimple_alloc_histogram_value (cfun,
-		                                            HIST_TYPE_POW2,
-							    stmt, divisor));
-
+	  values->safe_push (gimple_alloc_histogram_value (cfun,
+							   HIST_TYPE_POW2,
+							   stmt, divisor));
 	  val = build2 (TRUNC_DIV_EXPR, type, op0, divisor);
 	  hist = gimple_alloc_histogram_value (cfun, HIST_TYPE_INTERVAL,
 					       stmt, val);
 	  hist->hdata.intvl.int_start = 0;
 	  hist->hdata.intvl.steps = 2;
-	  values->quick_push (hist);
+	  values->safe_push (hist);
 	}
       return;
 
@@ -1867,15 +1801,9 @@
     return;
 
   callee = gimple_call_fn (stmt);
-
-  values->reserve (3);
-
-  values->quick_push (gimple_alloc_histogram_value (
-                        cfun,
-                        PARAM_VALUE (PARAM_INDIR_CALL_TOPN_PROFILE) ?
-                          HIST_TYPE_INDIR_CALL_TOPN :
-                          HIST_TYPE_INDIR_CALL,
-			stmt, callee));
+  histogram_value v = gimple_alloc_histogram_value (cfun, HIST_TYPE_INDIR_CALL,
+						    stmt, callee);
+  values->safe_push (v);
 
   return;
 }
@@ -1907,7 +1835,7 @@
   if (TREE_CODE (blck_size) != INTEGER_CST)
     {
       values->safe_push (gimple_alloc_histogram_value (cfun,
-						       HIST_TYPE_SINGLE_VALUE,
+						       HIST_TYPE_TOPN_VALUES,
 						       stmt, blck_size));
       values->safe_push (gimple_alloc_histogram_value (cfun, HIST_TYPE_AVERAGE,
 						       stmt, blck_size));
@@ -1942,7 +1870,8 @@
     for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
       gimple_values_to_profile (gsi_stmt (gsi), values);
 
-  values->safe_push (gimple_alloc_histogram_value (cfun, HIST_TYPE_TIME_PROFILE, 0, 0));
+  values->safe_push (gimple_alloc_histogram_value (cfun,
+						   HIST_TYPE_TIME_PROFILE));
 
   FOR_EACH_VEC_ELT (*values, i, hist)
     {
@@ -1956,12 +1885,9 @@
 	  hist->n_counters = 2;
 	  break;
 
-	case HIST_TYPE_SINGLE_VALUE:
-	  hist->n_counters = 3;
-	  break;
-
- 	case HIST_TYPE_INDIR_CALL:
- 	  hist->n_counters = 3;
+	case HIST_TYPE_TOPN_VALUES:
+	case HIST_TYPE_INDIR_CALL:
+	  hist->n_counters = GCOV_TOPN_VALUES_COUNTERS;
 	  break;
 
         case HIST_TYPE_TIME_PROFILE:
@@ -1976,10 +1902,6 @@
 	  hist->n_counters = 1;
 	  break;
 
-        case HIST_TYPE_INDIR_CALL_TOPN:
-          hist->n_counters = GCOV_ICALL_TOPN_NCOUNTS;
-          break;
-
 	default:
 	  gcc_unreachable ();
 	}